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
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))