def handle_chunks(headers):
    '''Generic chunk handling code, used by both client
    and server.

    Modifies the passed-in HttpHeaders instance.
    '''
    chunks = []
    while True:
        chunk_head = yield until_eol()
        if ';' in chunk_head:
            # we don't support any chunk extensions
            chunk_head = chunk_head[:chunk_head.find(';')]
        size = int(chunk_head, 16)
        if size == 0:
            break
        else:
            chunks.append((yield bytes(size)))
            _ = yield bytes(2) # ignore trailing CRLF

    while True:
        trailer = yield until_eol()
        if trailer.strip():
            headers.add(*tuple(trailer.split(':', 1)))
        else:
            body = ''.join(chunks)
            headers.set('Content-Length', len(body))
            headers.remove('Transfer-Encoding')
            yield up(body)
            break
Exemple #2
0
 def wait(self, collection=''):
     """Wait for events to be published on subscribed docs in collection.
     
     If no collection is given, the proxy will notify for all subscribed
     docs across collections.
     """
     data = "\x00\x00\x00\x00%s%s" % (
             _make_c_string(collection),
             self._pubsub_id, 
     )
     yield self._put_request(OP_WAIT, data)
     doclen = struct.unpack('<i', (yield bytes(4)))[0]
     rawdoc = yield bytes(doclen)
     objs = []
     while rawdoc:
         obj, rawdoc = _bson_to_dict(rawdoc)
         objs.append(obj)
     yield response(objs)
    def __call__(self, addr):
        '''Since an instance of HttpServer is passed to the Service
        class (with appropriate request_handler established during
        initialization), this __call__ method is what's actually
        invoked by diesel.

        This is our generator, this is our protocol handler.

        It does protocol work, then calls the request_handler, 
        looking for HttpClose if necessary.
        '''
        while True:
            chunks = []
            try:
                header_line = yield until_eol()
            except ConnectionClosed:
                break

            method, url, version = parse_request_line(header_line)    
            req = HttpRequest(method, url, version, remote_addr=addr)

            header_block = yield until('\r\n\r\n')

            heads = HttpHeaders()
            heads.parse(header_block)
            req.headers = heads

            if req.version >= '1.1' and heads.get_one('Expect') == '100-continue':
                yield 'HTTP/1.1 100 Continue\r\n\r\n'

            more_mode = self.check_for_http_body(heads)

            if more_mode is self.BODY_NONE:
                req.body = None

            elif more_mode is self.BODY_CL:
                req.body = yield bytes(int(heads['Content-Length']))

            elif more_mode is self.BODY_CHUNKED:
                req.body = handle_chunks(heads)

            leave_loop = False
            try:
                yield self.request_handler(req)
            except HttpClose:
                leave_loop = True
            if leave_loop:
                break
    def request(self, method, path, headers, body=None):
        '''Issues a `method` request to `path` on the
        connected server.  Sends along `headers`, and
        body.

        Very low level--you must set "host" yourself,
        for example.  It will set Content-Length, 
        however.
        '''
        req = HttpRequest(method, path, '1.1')
        
        if body:
            headers.set('Content-Length', len(body))
        
        yield '%s\r\n%s\r\n\r\n' % (req.format(), 
        headers.format())

        if body:    
            yield body

        resp_line = yield until_eol()
        version, code, status = resp_line.split(None, 2)
        code = int(code)

        header_block = yield until('\r\n\r\n')
        heads = HttpHeaders()
        heads.parse(header_block)

        if heads.get_one('Transfer-Encoding') == 'chunked':
            body = yield handle_chunks(heads)
        else:
            cl = int(heads.get_one('Content-Length', 0))
            if cl:
                body = yield bytes(cl)
            else:
                body = None

        if version < '1.0' or heads.get_one('Connection') == 'close':
            self.close()
        yield response((code, heads, body))