classHTTPServerConnectionDelegate(object): """Implement this interface to handle requests from `.HTTPServer`. .. versionadded:: 4.0 """ defstart_request(self, server_conn, request_conn): """This method is called by the server when a new request has started. :arg server_conn: is an opaque object representing the long-lived (e.g. tcp-level) connection. :arg request_conn: is a `.HTTPConnection` object for a single request/response exchange. This method should return a `.HTTPMessageDelegate`. """ raise NotImplementedError()
defon_close(self, server_conn): """This method is called when a connection has been closed. :arg server_conn: is a server connection that has previously been passed to ``start_request``. """ pass
@gen.coroutine defclose_all_connections(self): while self._connections: # Peek at an arbitrary element of the set conn = next(iter(self._connections)) yield conn.close()
defheaders_received(self, start_line, headers): # 若是 gzip 数据,则提供 GzipDecompressor 实例用于 body 的解压缩。 # 并删除 Content-Encoding 报文头,用 X-Consumed-Content-Encoding 来代替。 if headers.get("Content-Encoding") == "gzip": self._decompressor = GzipDecompressor() # Downstream delegates will only see uncompressed data, # so rename the content-encoding header. # (but note that curl_httpclient doesn't do this). headers.add("X-Consumed-Content-Encoding", headers["Content-Encoding"]) del headers["Content-Encoding"] return self._delegate.headers_received(start_line, headers)
@gen.coroutine defdata_received(self, chunk): if self._decompressor: compressed_data = chunk while compressed_data: decompressed = self._decompressor.decompress( compressed_data, self._chunk_size) if decompressed: yield gen.maybe_future( self._delegate.data_received(decompressed)) compressed_data = self._decompressor.unconsumed_tail else: yield gen.maybe_future(self._delegate.data_received(chunk))
deffinish(self): if self._decompressor isnotNone: tail = self._decompressor.flush() if tail: # I believe the tail will always be empty (i.e. # decompress will return all it can). The purpose # of the flush call is to detect errors such # as truncated input. But in case it ever returns # anything, treat it as an extra chunk self._delegate.data_received(tail) return self._delegate.finish()
classHTTP1ServerConnection(object): """An HTTP/1.x server.""" def__init__(self, stream, params=None, context=None): """ :arg stream: an `.IOStream` :arg params: a `.HTTP1ConnectionParameters` or None :arg context: an opaque application-defined object that is accessible as ``connection.context`` """ self.stream = stream if params isNone: params = HTTP1ConnectionParameters() self.params = params self.context = context self._serving_future = None
@gen.coroutine defclose(self): """Closes the connection. Returns a `.Future` that resolves after the serving loop has exited. """ self.stream.close() # Block until the serving loop is done, but ignore any exceptions # (start_serving is already responsible for logging them). try: yield self._serving_future except Exception: pass
defstart_serving(self, delegate): """Starts serving requests on this connection. :arg delegate: a `.HTTPServerConnectionDelegate` """ assert isinstance(delegate, httputil.HTTPServerConnectionDelegate) self._serving_future = self._server_request_loop(delegate) # Register the future on the IOLoop so its errors get logged. self.stream.io_loop.add_future(self._serving_future, lambda f: f.result())