Exemplo n.º 1
0
Arquivo: blob.py Projeto: dpq/spooce
 def __call__(self, env, start_response):
     Config = ConfigParser()
     Config.read(configfile)
     params = {"host": "", "user": "", "database": "", "port": ""}
     for param in params:
         if not Config.has_option("BlobStore", param):
             print "Malformed configuration file: mission option %s in section %s" % (param, "BlobStore")
             sys.exit(1)
         params[param] = Config.get("BlobStore", param)
     req = Request(env)
     resp = Response(status=200, content_type="text/plain")
     #engine = create_engine("mysql+mysqldb://%s:%s@%s:%s/%s?charset=utf8&use_unicode=0" %
     #    (params["user"], secret.BlobSecret, params["host"], params["port"], params["database"]), pool_recycle=3600)
     Base = declarative_base(bind=engine)
     local.Session = []
     local.FileEntry = []
     Session.append(sessionmaker(bind=engine))
     FileEntry.append(makeFileEntry(Base))
     if req.path.startswith('/fx'):
         if req.method == "GET":
             resp.status_code, filename, resp.content_type, resp.response = self.__download(req)
             if resp.content_type == "application/octet-stream":
                 resp.headers["Content-Disposition"] = "attachment; filename=%s" % filename
             return resp(env, start_response)
         elif req.method == "POST":
             resp.content_type="text/plain"
             resp.response = self.__upload(req)
             return resp(env, start_response)
     else:
         resp.status_code = 404
         resp.content_type="text/plain"
         resp.response = ""
         return resp(env, start_response)
Exemplo n.º 2
0
 def add_response(self, data, status=None, headers=None):
     response = Response(data)
     response.status_code = status or httplib.OK
     response.headers = headers or {
         'Content-Type': 'text/html; charset=UTF-8'
     }
     self.responses.append(response)
Exemplo n.º 3
0
def digest_challenge_response(request, qop, algorithm, stale=False):
    response = Response()
    response.status_code = 401

    # RFC2616 Section4.2: HTTP headers are ASCII.  That means
    # request.remote_addr was originally ASCII, so I should be able to
    # encode it back to ascii.  Also, RFC2617 says about nonces: "The
    # contents of the nonce are implementation dependent"
    nonce = H(
        b''.join([
            getattr(request, 'remote_addr', u'').encode('ascii'), b':',
            str(time.time()).encode('ascii'), b':',
            os.urandom(10)
        ]), algorithm)
    opaque = H(os.urandom(10), algorithm)

    auth = WWWAuthenticate("digest")
    auth.set_digest('*****@*****.**',
                    nonce,
                    opaque=opaque,
                    qop=('auth', 'auth-int') if qop is None else (qop, ),
                    algorithm=algorithm)
    auth.stale = stale
    response.headers['WWW-Authenticate'] = auth.to_header()
    return response
Exemplo n.º 4
0
def humanify(obj, status_code=200):
    """ Gets an obj and possibly a status code and returns a flask Resonse
        with a jsonified obj, with newlines.
    >>> humanify({"a": 1})
    <Response 13 bytes [200 OK]>
    >>> humanify({"a": 1}, 404)
    <Response 13 bytes [404 NOT FOUND]>
    >>> humanify({"a": 1}).get_data()
    '{\\n  "a": 1\\n}\\n'
    >>> humanify([1,2,3]).get_data()
    '[\\n  1, \\n  2, \\n  3\\n]\\n'
    """
    # jsonify function doesn't work with lists
    if type(obj) == list:
        data = json.dumps(obj, default=json_util.default, indent=2) + '\n'
    elif type(obj) == pymongo.cursor.Cursor:
        rv = []
        for doc in obj:
            doc['_id'] = str(doc['_id'])
            rv.append(dumps(doc, indent=2))
        data = '[' + ',\n'.join(rv) + ']' + '\n'
    else:
        data = dumps(obj,
                     default=json_util.default,
                     indent=2,
                     cls=MongoJsonEncoder)
        data += '\n'
    resp = Response(data, mimetype='application/json')
    resp.status_code = status_code
    return resp
Exemplo n.º 5
0
def drip(request):
    """Drips data over a duration after an optional initial delay."""
    args = CaseInsensitiveDict(request.args.items())
    duration = float(args.get('duration', 2))
    numbytes = min(int(args.get('numbytes', 10)),
                   (10 * 1024 * 1024))  # set 10MB limit
    code = int(args.get('code', 200))

    if numbytes <= 0:
        response = Response('number of bytes must be positive', status=400)
        return response

    delay = float(args.get('delay', 0))
    if delay > 0:
        time.sleep(delay)

    pause = duration / numbytes

    def generate_bytes():
        for i in xrange(numbytes):
            yield u"*".encode('utf-8')
            time.sleep(pause)

    response = Response(generate_bytes(),
                        headers={
                            "Content-Type": "application/octet-stream",
                            "Content-Length": str(numbytes)
                        })

    response.status_code = code

    return response
Exemplo n.º 6
0
def humanify(obj, status_code=200):
    """ Gets an obj and possibly a status code and returns a flask Resonse
        with a jsonified obj, not suitable for humans
    >>> humanify({"a": 1})
    <Response 8 bytes [200 OK]>
    >>> humanify({"a": 1}, 404)
    <Response 8 bytes [404 NOT FOUND]>
    >>> humanify({"a": 1}).get_data()
    '{"a": 1}'
    >>> humanify([1,2,3]).get_data()
    '[1, 2, 3]'
    """
    # TODO: refactor the name to `response`
    # jsonify function doesn't work with lists
    if type(obj) == list:
        data = json.dumps(obj, default=json_util.default)
    elif type(obj) == pymongo.cursor.Cursor:
        rv = []
        for doc in obj:
            doc['_id'] = str(doc['_id'])
            rv.append(dumps(doc))
        data = '[' + ',\n'.join(rv) + ']' + '\n'
    else:
        data = dumps(obj,
                          default=json_util.default,
                          cls=MongoJsonEncoder)
    resp = Response(data, mimetype='application/json')
    resp.status_code = status_code
    return resp
Exemplo n.º 7
0
def humanify(obj, status_code=200):
    """ Gets an obj and possibly a status code and returns a flask Resonse
        with a jsonified obj, not suitable for humans
    >>> humanify({"a": 1})
    <Response 8 bytes [200 OK]>
    >>> humanify({"a": 1}, 404)
    <Response 8 bytes [404 NOT FOUND]>
    >>> humanify({"a": 1}).get_data()
    '{"a": 1}'
    >>> humanify([1,2,3]).get_data()
    '[1, 2, 3]'
    """
    # TODO: refactor the name to `response`
    # jsonify function doesn't work with lists
    if type(obj) == list:
        data = json.dumps(obj, default=json_util.default)
    elif type(obj) == pymongo.cursor.Cursor:
        rv = []
        for doc in obj:
            doc['_id'] = str(doc['_id'])
            rv.append(dumps(doc))
        data = '[' + ',\n'.join(rv) + ']' + '\n'
    else:
        data = dumps(obj,
                          default=json_util.default,
                          cls=MongoJsonEncoder)
    resp = Response(data, mimetype='application/json')
    resp.status_code = status_code
    return resp
Exemplo n.º 8
0
Arquivo: warden.py Projeto: dpq/spooce
 def __call__(self, env, start_response):
     req = Request(env)
     Config = ConfigParser()
     Config.read(configfile)
     params = {"host": "", "user": "", "database": "", "port": ""}
     for param in params:
         if not Config.has_option("MySQL", param):
             print "Malformed configuration file: mission option %s in section MySQL" % (param)
             sys.exit(1)
         params[param] = Config.get("MySQL", param)    
     #engine = create_engine("mysql+mysqldb://%s:%s@%s:%s/%s?charset=utf8&use_unicode=0" %
     #    (params["user"], secret.MySQL, params["host"], params["port"], params["database"]), pool_recycle=3600)
     Base = declarative_base(bind=engine)
     local.Session = []
     local.User = []
     local.MethodEmail = []
     local.Session.append(sessionmaker(bind=engine))
     local.User.append(makeUser(Base))
     local.MethodEmail.append(makeMethodEmail(Base))
     resp = Response(status=200)
     if req.path == '/register':
         resp.content_type = "text/html"
         resp.response = self.__register(req)
         return resp(env, start_response)
     elif req.path == '/regemail1':
         resp.content_type = "text/html"
         resp.response = self.__addEmailAuth1(req)
         return resp(env, start_response)
     elif req.path == '/regemail2':
         resp.content_type = "text/html"
         resp.response = self.__addEmailAuth2(req)
         return resp(env, start_response)
     elif req.path == '/regemail3':
         resp.content_type = "text/html"
         resp.response = self.__addEmailAuth3(req)
         return resp(env, start_response)
     elif req.path == '/regemailkill':
         resp.content_type = "text/html"
         resp.response = self.__removeEmailAuth(req)
         return resp(env, start_response)
     elif req.path == '/regemaillist':
         resp.content_type = "text/plain"
         resp.response = self.__listEmailAuth(req)
         return resp(env, start_response)
     elif req.path == '/auth' and req.values.has_key("email") and req.values.has_key("password"):
         resp.content_type = "text/plain"
         resp.response = [self.__authenticateByEmail(req)]
         return resp(env, start_response)
     elif req.path == '/auth' and req.values.has_key("uid"):
         resp.content_type = "text/plain"
         uid = self.__uid(req)
         if uid == None:
             uid = ""
         resp.response = [uid]
         return resp(env, start_response)
     else:
         resp.status_code = 404
         resp.response = [""]
         return resp(env, start_response)
Exemplo n.º 9
0
    def _application(self, environ, start_response):
        request = Request(environ)
        self.logger.info(request.full_path)

        status_code = 200
        headers = {'Content-Type': 'application/json'}
        content = ''

        if request.path == '/':  # 接管主页,因为所有请求需要token,主页转发不能正常工作
            headers = {'Content-Type': 'text/html'}
            content = '<h1><a href="https://github.com/xiyaoWong/iotbot-http-transfer">iotbot http tranfer</a><h1>'
        elif request.path.strip('/') == 'favicon.ico':
            status_code = 301
            del headers['Content-Type']
            headers['location'] = 'https://cdn.jsdelivr.net/gh/xiyaowong/FileHost/transfer.png'
        elif request.path.strip('/') == 'genToken':  # 处理token生成请求
            key = request.args.get('key')
            if key == self.key:
                token = self._genarate_token('ok, it\' funny.')
                content = json.dumps({'token': token})
            else:
                content = '{"Ret":1111, "Msg":"key错误"}'
        else:  # 处理其他请求
            # 鉴权
            token = request.args.get('token') or request.headers.get('Authorization')
            if not self._check_token(token):  # 大胆,狗贼
                content = '{"Ret":2222, "Msg":"无效的token"}'
            else:
                try:
                    resp = requests.request(
                        request.method,
                        '{}{}?{}'.format(
                            'http://{}:{}'.format(self.iotbot_host, self.iotbot_port),
                            request.path,
                            request.query_string.decode()),
                        headers=request.headers,
                        data=request.data,
                        timeout=self.timeout
                    )
                except requests.Timeout as e:
                    self.logger.warning(e)
                    content = '{"Ret":3333, "Msg":"请求响应超时"}'
                except requests.ConnectionError as e:
                    self.logger.exception(e)
                    content = '{"Ret":4444, "Msg":"连接错误"}'
                except Exception as e:
                    self.logger.exception(e)
                    content = '{"Ret":5555, "Msg":"请求响应失败"}'
                else:
                    content = resp.content
                    status_code = resp.status_code
                    headers = resp.headers

        response = Response(content)
        response.status = HTTP_STATUS_CODES[status_code]
        response.status_code = status_code
        for header_name, header_value in headers.items():
            response.headers[header_name] = header_value
        return response(environ, start_response)
Exemplo n.º 10
0
def redirect_to(request):
    """302/3XX Redirects to the given URL."""

    args = CaseInsensitiveDict(request.args.items())

    # We need to build the response manually and convert to UTF-8 to prevent
    # werkzeug from "fixing" the URL. This endpoint should set the Location
    # header to the exact string supplied.
    response = Response()
    response.status_code = 302
    if 'status_code' in args:
        status_code = int(args['status_code'])
        if status_code >= 300 and status_code < 400:
            response.status_code = status_code
    response.headers['Location'] = args['url'].encode('utf-8')

    return response
Exemplo n.º 11
0
    def __call__(self, request):
        try:
            response = self.responses[len(self.requests)]
        except IndexError:
            response = Response("No prepared response")
            response.status_code = httplib.NOT_IMPLEMENTED
            return response

        self.requests.append(request)
        return response
Exemplo n.º 12
0
def serve_content(file_handle: TextIO, content_type):
    file_handle.seek(0)
    content = file_handle.read()

    resp = Response()
    resp.content_type = content_type
    resp.status_code = 200
    resp.data = content
    resp.freeze()

    return resp
Exemplo n.º 13
0
Arquivo: repo.py Projeto: dpq/spooce
 def __call__(self, env, start_response):
     req = Request(env)
     Config = ConfigParser()
     Config.read(configfile)
     params = {"host": "", "user": "", "database": "", "port": ""}
     for param in params:
         if not Config.has_option("MySQL", param):
             print "Malformed configuration file: mission option %s in section MySQL" % (param)
             sys.exit(1)
         params[param] = Config.get("MySQL", param)
     #print params
     #engine = create_engine("mysql+mysqldb://%s:%s@%s:%s/%s?charset=utf8&use_unicode=0" %
     #    (params["user"], secret.MySQL, params["host"], params["port"], params["database"]), pool_recycle=3600)
     Base = declarative_base(bind=engine)
     local.Session = []
     local.Package = []
     local.Session.append(sessionmaker(bind=engine))
     local.Package.append(makePackage(Base))
     resp = Response(status=200, content_type="text/plain")
     if req.path == '/pkg/upload':
         resp.content_type="text/html"
         resp.response = self.__upload(req)
         return resp(env, start_response)
     elif req.path == '/pkg/sdk':
         resp.content_disposition="application/force-download"
         resp.content_type="application/python"
         resp.headers.add("Content-Disposition", "attachment; filename=appcfg.py")
         f = open("appcfg.py").read().replace("__REPO_UPLOAD__", '"%s"' % (req.host_url + 'pkg/upload'))
         resp.headers.add("Content-Length", str(len(f)))
         resp.response = [f]
         return resp(env, start_response)
     elif req.path.startswith('/pkg'):
         resp.status_code, resp.content_encoding, resp.response = self.__download(req)
         return resp(env, start_response)
     else:
         resp.status_code = 404
         resp.response = [""]
         return resp(env, start_response)
Exemplo n.º 14
0
def relative_redirect_n_times(request, n):
    """302 Redirects n times."""

    assert n > 0

    response = Response()
    response.status_code = 302

    if n == 1:
        return redirect(
            request.url_for('view_method', values={'method': 'get'}))
        return response

    response.headers['Location'] = request.url_for('relative_redirect_n_times',
                                                   values=dict(n=n - 1))
    return response
Exemplo n.º 15
0
 def application(request):
     try:
         # Parse the JSON in the request
         try:
             data = loads(request.stream.read())
         except ValueError:
             raise BadRequest()
         # Grab the function to execute
         try:
             method = getattr(backend, data['method'])
         except (KeyError, IndexError):
             raise BadRequest()
         if method is None:
             raise NotFound()
         # Get the args and kwargs
         args = data.get('args', [])
         kwargs = data.get('kwargs', {})
         kwargs = dict(
             ((k.encode('utf-8'), v) for k, v in kwargs.iteritems()))
         # Attempt to call the method with the params, or catch the
         # exception and pass that back to the client
         try:
             response = Response(
                 dumps({
                     'id': data.get('id'),
                     'result': method(*args, **kwargs),
                     'error': None,
                 }))
         except (KeyboardInterrupt, SystemExit):
             raise
         except Exception, e:
             print e
             response = Response(
                 dumps({
                     'id':
                     data.get('id'),
                     'result':
                     None,
                     'error':
                     ''.join(traceback.format_exception(*sys.exc_info())),
                 }))
         # Finish up and return the response
         response.headers['Content-Type'] = 'application/json'
         response.headers['Content-Length'] = len(response.data)
         response.status_code = 200
         return response
Exemplo n.º 16
0
 def application(request):
     try:
         # Parse the JSON in the request
         try:
             data = loads(request.stream.read())
         except ValueError:
             raise BadRequest()
         # Grab the function to execute
         try:
             method = getattr(backend, data['method'])
         except (KeyError, IndexError):
             raise BadRequest()
         if method is None:
             raise NotFound()
         # Get the args and kwargs
         args = data.get('args', [])
         kwargs = data.get('kwargs', {})
         kwargs = dict(((k.encode('utf-8'), v) for k, v in kwargs.iteritems()))
         # Attempt to call the method with the params, or catch the
         # exception and pass that back to the client
         try:
             response = Response(dumps({
                 'id': data.get('id'),
                 'result': method(*args, **kwargs),
                 'error': None,
             }))
         except (KeyboardInterrupt, SystemExit):
             raise
         except Exception, e:
             print e
             response = Response(dumps({
                 'id': data.get('id'),
                 'result': None,
                 'error': ''.join(traceback.format_exception(*sys.exc_info())),
             }))
         # Finish up and return the response
         response.headers['Content-Type'] = 'application/json'
         response.headers['Content-Length'] = len(response.data)
         response.status_code = 200
         return response
Exemplo n.º 17
0
    def __call__(self, environ, start_response):
        path_info = environ['PATH_INFO']
        path = re.sub(self.prefix, '', path_info)
        prefix = path_info[:-len(path)]
        if prefix.endswith("/ui"):
            prefix = prefix[:-3]

        if path == "/":
            path = "/index.html"

        if path == "/index.html":
            with open(swagger_ui_3_path + path) as fh:
                content = fh.read()
            content = content.replace(
                "https://petstore.swagger.io/v2/swagger.json",
                prefix + "/openapi.yaml")
            resp = Response()
            resp.content_type = 'text/html'
            resp.status_code = 200
            resp.data = content
            resp.freeze()
            return resp(environ, start_response)

        return serve_file(swagger_ui_3_path + path)(environ, start_response)
Exemplo n.º 18
0
def range_request(request, numbytes):
    """Streams n random bytes generated with given seed, at given chunk size per packet."""

    if numbytes <= 0 or numbytes > (100 * 1024):
        response = Response(headers={
            'ETag': 'range%d' % numbytes,
            'Accept-Ranges': 'bytes'
        })
        response.status_code = 404
        response.data = 'number of bytes must be in the range (0, 10240]'
        return response

    params = CaseInsensitiveDict(request.args.items())
    if 'chunk_size' in params:
        chunk_size = max(1, int(params['chunk_size']))
    else:
        chunk_size = 10 * 1024

    duration = float(params.get('duration', 0))
    pause_per_byte = duration / numbytes

    request_headers = get_headers(request)
    first_byte_pos, last_byte_pos = get_request_range(request_headers,
                                                      numbytes)
    range_length = (last_byte_pos + 1) - first_byte_pos

    if first_byte_pos > last_byte_pos or first_byte_pos not in xrange(
            0, numbytes) or last_byte_pos not in xrange(0, numbytes):
        response = Response(
            headers={
                'ETag': 'range%d' % numbytes,
                'Accept-Ranges': 'bytes',
                'Content-Range': 'bytes */%d' % numbytes,
                'Content-Length': '0',
            })
        response.status_code = 416
        return response

    def generate_bytes():
        chunks = bytearray()

        for i in xrange(first_byte_pos, last_byte_pos + 1):

            # We don't want the resource to change across requests, so we need
            # to use a predictable data generation function
            chunks.append(ord('a') + (i % 26))
            if len(chunks) == chunk_size:
                yield (bytes(chunks))
                time.sleep(pause_per_byte * chunk_size)
                chunks = bytearray()

        if chunks:
            time.sleep(pause_per_byte * len(chunks))
            yield (bytes(chunks))

    content_range = 'bytes %d-%d/%d' % (first_byte_pos, last_byte_pos,
                                        numbytes)
    response_headers = {
        'Content-Type': 'application/octet-stream',
        'ETag': 'range%d' % numbytes,
        'Accept-Ranges': 'bytes',
        'Content-Length': str(range_length),
        'Content-Range': content_range
    }

    response = Response(generate_bytes(), headers=response_headers)

    if (first_byte_pos == 0) and (last_byte_pos == (numbytes - 1)):
        response.status_code = 200
    else:
        response.status_code = 206

    return response
Exemplo n.º 19
0
  def __call__(self, env, start_response):
    local.application = self
    req = Request(env)
    resp = Response(status=200)
    start_response('200 OK', [('Content-Type', 'text/plain')])

    if req.path == '/start':
      # Have all arguments been passed?
      if not (req.values.has_key("protocol") and req.values.has_key("ncli") and req.values.has_key("nsrv") and req.values.has_key("rep")):
        resp.status_code = 500
        resp.response = ["One of the mandatory arguments has been omitted [protocol, ncli, nsrv, rep]"]
        return resp(env, start_response)

      # Does protocol have a valid value?
      protocol = req.values["protocol"]
      if protocol not in protocols:
        resp.status_code = 500
        resp.response = ["Protocol specified is not implemented; must be one of [ftp, http, udt, gridftp, torrent]"]
        return resp(env, start_response)

      # Do these arguments have valid numeric values?
      try:
        ncli, nsrv, rep = int(req.values["ncli"]), int(req.values["nsrv"]), int(req.values["rep"])
      except:
        resp.status_code = 500
        resp.response = ["Unable to parse a mandatory numeric argument"]
        return resp(env, start_response)

      # Do the numeric arguments make sense?
      if ncli < 1 or nsrv < 1 or rep < 1:
        resp.status_code = 500
        resp.response = ["Go screw yourself"]
        return resp(env, start_response)
      if ncli > len(clients):
        resp.status_code = 500
        resp.response = ["We don't have that many clients available. Current maximum is %d" % len(clients)]
        return resp(env, start_response)
      if nsrv > len(servers):
        resp.status_code = 500
        resp.response = ["We don't have that many servers available. Current maximum is %d" % len(servers)]
        return resp(env, start_response)

      # Has the previous experiment been finished already? For that, its nclients*replication must be equal to the number of associated measurements
      res = my.query("select max(id) from experiment")
      last_exp_id = res.rows[0][0]
      if last_exp_id:
        res = my.query("select replication, ncli from experiment where id=%s", (last_exp_id,))
        rep_prev, ncli_prev = res.rows[0][0], res.rows[0][1]
        res = my.query("select count(*) from measurement where experiment_id=%s", (last_exp_id,))
        cnt = res.rows[0][0]
        if cnt < rep_prev*ncli_prev:
          resp.status_code = 500
          resp.response = ["Unable to comply, experiment in progress"]
          return resp(env, start_response)

      f = NamedTemporaryFile(delete=False)
      f.write(generatehaproxycfg(nsrv))
      f.close()
      
      local.ssh = paramiko.SSHClient()
      local.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
      local.ssh.connect(proxy, username='******', key_filename='/home/rumith/.ssh/awskeys.pem')
      local.scp = scp.SCPClient(local.ssh.get_transport())
      local.scp.put(f.name, "/etc/haproxy/haproxy.cfg")
      if protocol == "http":
        stdin, stdout, stderr = local.ssh.exec_command('pkill haproxy; haproxy -f /etc/haproxy/haproxy.cfg')
      local.ssh.close()

      os.unlink(f.name)

      expcount, experiment_id = my.query("insert into experiment (started, protocol, ncli, nsrv, replication) values ((select now()), %s, %s, %s, %s)", (protocol, ncli, nsrv, rep))

      # Copy the required protocol download programs to the clients used and start them
      for i in range(ncli):
        local.ssh = paramiko.SSHClient()
        local.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        local.ssh.connect(clients[i], username='******', key_filename='/home/rumith/.ssh/id_rsa')
        local.scp = scp.SCPClient(local.ssh.get_transport())
        local.scp.put("protocol-%s.sh" % protocol, "/home/protobench")
        if protocol == "http":
          stdin, stdout, stderr = local.ssh.exec_command('cd /home/protobench && ./protocol-%s.sh %s %s 1 > /dev/null 2>&1 &' % (protocol, proxy, experiment_id))
        local.ssh.close()
        
      resp.response = ["Experiment %d x %d [%s] successfully started; replication %d" % (nsrv, ncli, protocol, rep) ]
      return resp(env, start_response)

    elif req.path ==  '/done':
      # Did the client pass all the required arguments?
      if not (req.values.has_key("expid") and req.values.has_key("runid") and req.values.has_key("bw")):
        resp.status_code = 500
        resp.response = ["One of the mandatory arguments has been omitted [expid, runid, bw]"]
        return resp(env, start_response)
      
      # Do they have legal values?
      try:
        expid, runid, bandwidth = int(req.values["expid"]), int(req.values["runid"]), int(req.values["bw"])
      except:
        resp.status_code = 500
        resp.response = ["Unable to parse mandatory arguments"]
        return resp(env, start_response)
        
      # Is there really such an experiment to commit to?      
      res = my.query("select count(*) from experiment where id = %s", (req.values["expid"],))
      expcnt = res.rows[0][0]
      if expcnt == 0:
        resp.status_code = 500
        resp.response = ["No such experiment"]
        return resp(env, start_response)

      # Is this run being reported for the first time from this host?
      res = my.query("select count(*) from measurement where experiment_id = %s and run_id = %s and client_host = %s", (expid, runid, req.remote_addr))
      runcheck = res.rows[0][0]
      if runcheck > 0:
        resp.status_code = 500
        resp.response = ["Such a run already exists"]
        return resp(env, start_response)
      
      # Does bandwidth have a valid value?
      if bandwidth < 0:
        resp.status_code = 500
        resp.response = ["Bad bandwidth value"]
        return resp(env, start_response)    

      # Commit the measurement
      my.query("insert into measurement (experiment_id, run_id, bandwidth, client_host) values (%s, %s, %s, %s)", (expid, runid, bandwidth, req.remote_addr))

      # Do we have enough replication for this host/experiment pair, or should we launch the task again?
      res = my.query("select replication, protocol from experiment where id=%s", (expid,))
      replication, protocol = res.rows[0][0], res.rows[0][1]
      res = my.query("select count(*) from measurement where experiment_id=%s and client_host=%s", (expid, req.remote_addr))
      runcnt = res.rows[0][0] 
      if replication > runcnt:
        local.ssh = paramiko.SSHClient()
        local.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
        local.ssh.connect(req.remote_addr, username='******', key_filename='/home/rumith/.ssh/id_rsa')
        if protocol == "http":
          stdin, stdout, stderr = local.ssh.exec_command('cd /home/protobench && ./protocol-%s.sh %s %s %s > /dev/null 2>&1 &' % (protocol, proxy, expid, (runid + 1)))
        local.ssh.close()        
      else:
        # If we have enough replication for this one, maybe we have everything from other clients, too, and can finish the experiment?
        res = my.query("select replication, ncli from experiment where id=%s", (expid,))
        rep, ncli = res.rows[0][0], res.rows[0][1]
        res = my.query("select count(*) from measurement where experiment_id=%s", (expid,))
        cnt = res.rows[0][0]
        if cnt > 0 and cnt == rep*ncli:
          bw = []
          res = my.query("select bandwidth from measurement where experiment_id=%s", (expid,))
          for r in res.rows:
            bw.append(r[0])
          bsum = float(sum(bw)) / rep
          sigma = 0
          for i in range(rep):
            delta = bw[i] - bsum
            sigma += delta**2
          bw, mse = bsum, sqrt(sigma/rep)
          my.query("update experiment set ended=(select now()), bandwidth=%s, mse=%s where id=%s", (bw, mse, expid))
          
      return resp(env, start_response)

    elif req.path == '/status':
      return resp(env, start_response)
    else:
      resp.status_code = 404
      resp.response = ["Invalid request"]
      return resp(env, start_response)