From 425a31d2a59a99947ddb2c9e32499270fb2a617b Mon Sep 17 00:00:00 2001 From: Ben Darnell Date: Sat, 26 Apr 2014 23:56:16 -0400 Subject: [PATCH] Document all the new HTTP stuff --- docs/conf.py | 3 + docs/gen.rst | 5 ++ docs/http1connection.rst | 5 ++ docs/iostream.rst | 1 + docs/networking.rst | 1 + docs/releases/next.rst | 93 +++++++++++++++++++++++++++--- docs/web.rst | 2 + tornado/gen.py | 2 + tornado/http1connection.py | 82 ++++++++++++++++++++++++--- tornado/httpclient.py | 10 +++- tornado/httpserver.py | 39 ++++++------- tornado/httputil.py | 112 +++++++++++++++++++++++++++++++++++-- tornado/ioloop.py | 5 +- tornado/web.py | 11 ++++ tornado/wsgi.py | 1 + 15 files changed, 328 insertions(+), 44 deletions(-) create mode 100644 docs/http1connection.rst diff --git a/docs/conf.py b/docs/conf.py index 46ca60e1..eaeb3553 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -37,6 +37,9 @@ coverage_ignore_modules = [ ] # I wish this could go in a per-module file... coverage_ignore_classes = [ + # tornado.concurrent + "TracebackFuture", + # tornado.gen "Multi", "Runner", diff --git a/docs/gen.rst b/docs/gen.rst index d09a9573..0aafa11f 100644 --- a/docs/gen.rst +++ b/docs/gen.rst @@ -32,6 +32,11 @@ .. autoclass:: YieldPoint :members: + .. autofunction:: with_timeout + .. autoexception:: TimeoutError + + .. autofunction:: maybe_future + Other classes ------------- diff --git a/docs/http1connection.rst b/docs/http1connection.rst new file mode 100644 index 00000000..a76532cd --- /dev/null +++ b/docs/http1connection.rst @@ -0,0 +1,5 @@ +``tornado.http1connection`` -- HTTP/1.x client/server implementation +==================================================================== + +.. automodule:: tornado.http1connection + :members: diff --git a/docs/iostream.rst b/docs/iostream.rst index a5e3869e..967dc832 100644 --- a/docs/iostream.rst +++ b/docs/iostream.rst @@ -48,3 +48,4 @@ ---------- .. autoexception:: StreamClosedError + .. autoexception:: UnsatisfiableReadError diff --git a/docs/networking.rst b/docs/networking.rst index 7d14c1cb..40b8a022 100644 --- a/docs/networking.rst +++ b/docs/networking.rst @@ -6,6 +6,7 @@ Asynchronous networking gen ioloop iostream + http1connection httpclient netutil tcpserver diff --git a/docs/releases/next.rst b/docs/releases/next.rst index e08593c6..e23da246 100644 --- a/docs/releases/next.rst +++ b/docs/releases/next.rst @@ -24,12 +24,6 @@ Backwards-compatibility notes of the old ``TracebackFuture`` class. ``TracebackFuture`` is now simply an alias for ``Future``. -`tornado.httpclient` -~~~~~~~~~~~~~~~~~~~~ - -* The command-line HTTP client (``python -m tornado.httpclient $URL``) - now works on Python 3. - `tornado.gen` ~~~~~~~~~~~~~ @@ -39,6 +33,57 @@ Backwards-compatibility notes * Performance of coroutines has been improved. * Coroutines no longer generate ``StackContexts`` by default, but they will be created on demand when needed. +* New function `.with_timeout` wraps a `.Future` and raises an exception + if it doesn't complete in a given amount of time. + +`tornado.http1connection` +~~~~~~~~~~~~~~~~~~~~~~~~~ + +* New module contains the HTTP implementation shared by `tornado.httpserver` + and ``tornado.simple_httpclient``. + +`tornado.httpclient` +~~~~~~~~~~~~~~~~~~~~ + +* The command-line HTTP client (``python -m tornado.httpclient $URL``) + now works on Python 3. + +`tornado.httpserver` +~~~~~~~~~~~~~~~~~~~~ + +* ``tornado.httpserver.HTTPRequest`` has moved to + `tornado.httputil.HTTPServerRequest`. +* HTTP implementation has been unified with ``tornado.simple_httpclient`` + in `tornado.http1connection`. +* Now supports ``Transfer-Encoding: chunked`` for request bodies. +* Now supports ``Content-Encoding: gzip`` for request bodies if ``gzip=True`` + is passed to the `.HTTPServer` constructor. +* The ``connection`` attribute of `.HTTPServerRequest` is now documented + for public use; applications are expected to write their responses + via the `.HTTPConnection` interface. +* The `.HTTPServerRequest.write` and `.HTTPServerRequest.finish` methods + are now deprecated. +* `.HTTPServer` now supports `.HTTPServerConnectionDelegate` in addition to + the old ``request_callback`` interface. The delegate interface supports + streaming of request bodies. +* `.HTTPServer` now detects the error of an application sending a + ``Content-Length`` error that is inconsistent with the actual content. +* New constructor arguments ``max_header_size`` and ``max_body_size`` + allow separate limits to be set for different parts of the request. + ``max_body_size`` is applied even in streaming mode. +* New constructor argument ``chunk_size`` can be used to limit the amount + of data read into memory at one time per request. +* New constructor arguments ``idle_connection_timeout`` and ``body_timeout`` + allow time limits to be placed on the reading of requests. + +`tornado.httputil` +~~~~~~~~~~~~~~~~~~ + +* `.HTTPServerRequest` was moved to this module from `tornado.httpserver`. +* New base classes `.HTTPConnection`, `.HTTPServerConnectionDelegate`, + and `.HTTPMessageDelegate` define the interaction between applications + and the HTTP implementation. + `tornado.ioloop` ~~~~~~~~~~~~~~~~ @@ -48,6 +93,7 @@ Backwards-compatibility notes (when possible) to avoid a garbage-collection-related problem in unit tests. * New method `.IOLoop.clear_instance` makes it possible to uninstall the singleton instance. +* `.IOLoop.add_timeout` is now a bit more efficient. `tornado.iostream` ~~~~~~~~~~~~~~~~~~ @@ -57,6 +103,17 @@ Backwards-compatibility notes for use with coroutines. * No longer gets confused when an ``IOError`` or ``OSError`` without an ``errno`` attribute is raised. +* `.BaseIOStream.read_bytes` now accepts a ``partial`` keyword argument, + which can be used to return before the full amount has been read. + This is a more coroutine-friendly alternative to ``streaming_callback``. +* `.BaseIOStream.read_until` and ``read_until_regex`` now acept a + ``max_bytes`` keyword argument which will cause the request to fail if + it cannot be satisfied from the given number of bytes. +* `.IOStream` no longer reads from the socket into memory if it does not + need data to satisfy a pending read. As a side effect, the close callback + will not be run immediately if the other side closes the connection + while there is unconsumed data in the buffer. +* The default ``chunk_size`` has been increased to 64KB (from 4KB) `tornado.netutil` ~~~~~~~~~~~~~~~~~ @@ -81,7 +138,13 @@ Backwards-compatibility notes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Improved default cipher suite selection (Python 2.7+). - +* HTTP implementation has been unified with ``tornado.httpserver`` + in `tornado.http1connection` +* Streaming request bodies are now supported via the ``body_producer`` + keyword argument to `tornado.httpclient.HTTPRequest`. +* The ``expect_100_continue`` keyword argument to + `tornado.httpclient.HTTPRequest` allows the use of the HTTP ``Expect: + 100-continue`` feature. `tornado.stack_context` ~~~~~~~~~~~~~~~~~~~~~~~ @@ -103,6 +166,11 @@ Backwards-compatibility notes * When gzip support is enabled, all ``text/*`` mime types will be compressed, not just those on a whitelist. +* `.Application` now implements the `.HTTPMessageDelegate` interface. +* It is now possible to support streaming request bodies with the + `.stream_request_body` decorator and the new `.RequestHandler.data_received` + method. +* `.RequestHandler.flush` now returns a `.Future` if no callback is given. `tornado.websocket` ~~~~~~~~~~~~~~~~~~~ @@ -116,3 +184,14 @@ Backwards-compatibility notes messages larger than 2GB on 64-bit systems. * The fallback mechanism for detecting a missing C compiler now works correctly on Mac OS X. +* Arguments to `.WebSocketHandler.open` are now decoded in the same way + as arguments to `.RequestHandler.get` and similar methods. + +`tornado.wsgi` +~~~~~~~~~~~~~~ + +* New class `.WSGIAdapter` supports running a Tornado `.Application` on + a WSGI server in a way that is more compatible with Tornado's non-WSGI + `.HTTPServer`. `.WSGIApplication` is deprecated in favor of using + `.WSGIAdapter` with a regular `.Application`. +* `.WSGIAdapter` now supports gzipped output. diff --git a/docs/web.rst b/docs/web.rst index 52c74501..3940a595 100644 --- a/docs/web.rst +++ b/docs/web.rst @@ -70,6 +70,7 @@ .. automethod:: RequestHandler.send_error .. automethod:: RequestHandler.write_error .. automethod:: RequestHandler.clear + .. automethod:: RequestHandler.data_received Cookies @@ -219,6 +220,7 @@ .. autofunction:: authenticated .. autofunction:: addslash .. autofunction:: removeslash + .. autofunction:: stream_request_body Everything else --------------- diff --git a/tornado/gen.py b/tornado/gen.py index c3fee01f..b54bbed4 100644 --- a/tornado/gen.py +++ b/tornado/gen.py @@ -467,6 +467,8 @@ def with_timeout(timeout, future, io_loop=None): relative to `.IOLoop.time`) Currently only supports Futures, not other `YieldPoint` classes. + + .. versionadded:: 3.3 """ # TODO: allow yield points in addition to futures? # Tricky to do with stack_context semantics. diff --git a/tornado/http1connection.py b/tornado/http1connection.py index dd0e7f2c..cb1b85ee 100644 --- a/tornado/http1connection.py +++ b/tornado/http1connection.py @@ -14,6 +14,11 @@ # License for the specific language governing permissions and limitations # under the License. +"""Client and server implementations of HTTP/1.x. + +.. versionadded:: 3.3 +""" + from __future__ import absolute_import, division, print_function, with_statement from tornado.concurrent import Future @@ -26,9 +31,22 @@ from tornado import stack_context from tornado.util import GzipDecompressor class HTTP1ConnectionParameters(object): + """Parameters for `.HTTP1Connection` and `.HTTP1ServerConnection`. + """ def __init__(self, no_keep_alive=False, protocol=None, chunk_size=None, max_header_size=None, header_timeout=None, max_body_size=None, body_timeout=None, use_gzip=False): + """ + :arg bool no_keep_alive: If true, always close the connection after + one request. + :arg str protocol: "http" or "https" + :arg int chunk_size: how much data to read into memory at once + :arg int max_header_size: maximum amount of data for HTTP headers + :arg float header_timeout: how long to wait for all headers (seconds) + :arg int max_body_size: maximum amount of data for body + :arg float body_timeout: how long to wait while reading body (seconds) + :arg bool use_gzip: if true, decode incoming ``Content-Encoding: gzip`` + """ self.no_keep_alive = no_keep_alive self.protocol = protocol self.chunk_size = chunk_size or 65536 @@ -39,12 +57,19 @@ class HTTP1ConnectionParameters(object): self.use_gzip = use_gzip class HTTP1Connection(object): - """Handles a connection to an HTTP client, executing HTTP requests. + """Implements the HTTP/1.x protocol. - We parse HTTP headers and bodies, and execute the request callback - until the HTTP conection is closed. + This class can be on its own for clients, or via `HTTP1ServerConnection` + for servers. """ def __init__(self, stream, is_client, params=None, context=None): + """ + :arg stream: an `.IOStream` + :arg bool is_client: client or server + :arg params: a `.HTTP1ConnectionParameters` instance or ``None`` + :arg context: an opaque application-defined object that can be accessed + as ``connection.context``. + """ self.is_client = is_client self.stream = stream if params is None: @@ -85,6 +110,16 @@ class HTTP1Connection(object): self._expected_content_remaining = None def read_response(self, delegate): + """Read a single HTTP response. + + Typical client-mode usage is to write a request using `write_headers`, + `write`, and `finish`, and then call ``read_response``. + + :arg delegate: a `.HTTPMessageDelegate` + + Returns a `.Future` that resolves to None after the full response has + been read. + """ if self.params.use_gzip: delegate = _GzipMessageDelegate(delegate, self.params.chunk_size) return self._read_message(delegate) @@ -190,10 +225,8 @@ class HTTP1Connection(object): def set_close_callback(self, callback): """Sets a callback that will be run when the connection is closed. - Use this instead of accessing - `HTTPConnection.stream.set_close_callback - <.BaseIOStream.set_close_callback>` directly (which was the - recommended approach prior to Tornado 3.0). + .. deprecated:: 3.3 + Use `.HTTPMessageDelegate.on_connection_close` instead. """ self._close_callback = stack_context.wrap(callback) @@ -211,18 +244,34 @@ class HTTP1Connection(object): self._clear_callbacks() def detach(self): + """Take control of the underlying stream. + + Returns the underlying `.IOStream` object and stops all further + HTTP processing. May only be called during + `.HTTPMessageDelegate.headers_received`. Intended for implementing + protocols like websockets that tunnel over an HTTP handshake. + """ stream = self.stream self.stream = None return stream def set_body_timeout(self, timeout): + """Sets the body timeout for a single request. + + Overrides the value from `.HTTP1ConnectionParameters`. + """ self._body_timeout = timeout def set_max_body_size(self, max_body_size): + """Sets the body size limit for a single request. + + Overrides the value from `.HTTP1ConnectionParameters`. + """ self._max_body_size = max_body_size def write_headers(self, start_line, headers, chunk=None, callback=None, has_body=True): + """Implements `.HTTPConnection.write_headers`.""" if self.is_client: self._request_start_line = start_line # Client requests with a non-empty body must have either a @@ -298,7 +347,7 @@ class HTTP1Connection(object): return chunk def write(self, chunk, callback=None): - """Writes a chunk of output to the stream.""" + """Implements `.HTTPConnection.write`.""" if self.stream.closed(): self._write_future = Future() self._write_future.set_exception(iostream.StreamClosedError()) @@ -312,7 +361,7 @@ class HTTP1Connection(object): return self._write_future def finish(self): - """Finishes the request.""" + """Implements `.HTTPConnection.finish`.""" if (self._expected_content_remaining is not None and self._expected_content_remaining != 0 and not self.stream.closed()): @@ -492,7 +541,14 @@ class _GzipMessageDelegate(httputil.HTTPMessageDelegate): class HTTP1ServerConnection(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 is None: params = HTTP1ConnectionParameters() @@ -502,6 +558,10 @@ class HTTP1ServerConnection(object): @gen.coroutine def close(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). @@ -511,6 +571,10 @@ class HTTP1ServerConnection(object): pass def start_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. diff --git a/tornado/httpclient.py b/tornado/httpclient.py index dfcf1000..98d6f323 100644 --- a/tornado/httpclient.py +++ b/tornado/httpclient.py @@ -270,13 +270,16 @@ class HTTPRequest(object): :arg body: HTTP request body as a string (byte or unicode; if unicode the utf-8 encoding will be used) :arg body_producer: Callable used for lazy/asynchronous request bodies. - TODO: document the interface. + It is called with one argument, a ``write`` function, and should + return a `.Future`. It should call the write function with new + data as it becomes available. The write function returns a + `.Future` which can be used for flow control. Only one of ``body`` and ``body_producer`` may be specified. ``body_producer`` is not supported on ``curl_httpclient``. When using ``body_producer`` it is recommended to pass a ``Content-Length`` in the headers as otherwise chunked encoding will be used, and many servers do not support chunked - encoding on requests. + encoding on requests. New in Tornado 3.3 :arg string auth_username: Username for HTTP authentication :arg string auth_password: Password for HTTP authentication :arg string auth_mode: Authentication mode; default is "basic". @@ -349,6 +352,9 @@ class HTTPRequest(object): .. versionadded:: 3.1 The ``auth_mode`` argument. + + .. versionadded:: 3.3 + The ``body_producer`` and ``expect_100_continue`` arguments. """ # Note that some of these attributes go through property setters # defined below. diff --git a/tornado/httpserver.py b/tornado/httpserver.py index a68afee8..47305194 100644 --- a/tornado/httpserver.py +++ b/tornado/httpserver.py @@ -41,41 +41,33 @@ from tornado.tcpserver import TCPServer class HTTPServer(TCPServer, httputil.HTTPServerConnectionDelegate): r"""A non-blocking, single-threaded HTTP server. - A server is defined by a request callback that takes an HTTPRequest - instance as an argument and writes a valid HTTP response with - `.HTTPServerRequest.write`. `.HTTPServerRequest.finish` finishes the request (but does - not necessarily close the connection in the case of HTTP/1.1 keep-alive - requests). A simple example server that echoes back the URI you - requested:: + A server is defined by either a request callback that takes a + `.HTTPServerRequest` as an argument or a `.HTTPServerConnectionDelegate` + instance. + + A simple example server that echoes back the URI you requested:: import tornado.httpserver import tornado.ioloop def handle_request(request): message = "You requested %s\n" % request.uri - request.write("HTTP/1.1 200 OK\r\nContent-Length: %d\r\n\r\n%s" % ( - len(message), message)) - request.finish() + request.connection.write_headers( + httputil.ResponseStartLine('HTTP/1.1', 200, 'OK'), + {"Content-Length": str(len(message))}) + request.connection.write(message) + request.connection.finish() http_server = tornado.httpserver.HTTPServer(handle_request) http_server.listen(8888) tornado.ioloop.IOLoop.instance().start() - `HTTPServer` is a very basic connection handler. It parses the request - headers and body, but the request callback is responsible for producing - the response exactly as it will appear on the wire. This affords - maximum flexibility for applications to implement whatever parts - of HTTP responses are required. + Applications should use the methods of `.HTTPConnection` to write + their response. `HTTPServer` supports keep-alive connections by default (automatically for HTTP/1.1, or for HTTP/1.0 when the client - requests ``Connection: keep-alive``). This means that the request - callback must generate a properly-framed response, using either - the ``Content-Length`` header or ``Transfer-Encoding: chunked``. - Applications that are unable to frame their responses properly - should instead return a ``Connection: close`` header in each - response and pass ``no_keep_alive=True`` to the `HTTPServer` - constructor. + requests ``Connection: keep-alive``). If ``xheaders`` is ``True``, we support the ``X-Real-Ip``/``X-Forwarded-For`` and @@ -135,6 +127,11 @@ class HTTPServer(TCPServer, httputil.HTTPServerConnectionDelegate): servers if you want to create your listening sockets in some way other than `tornado.netutil.bind_sockets`. + .. versionchanged:: 3.3 + Added ``gzip``, ``chunk_size``, ``max_header_size``, + ``idle_connection_timeout``, ``body_timeout``, ``max_body_size`` + arguments. Added support for `.HTTPServerConnectionDelegate` + instances as ``request_callback``. """ def __init__(self, request_callback, no_keep_alive=False, io_loop=None, xheaders=False, ssl_options=None, protocol=None, gzip=False, diff --git a/tornado/httputil.py b/tornado/httputil.py index 353e9eb9..5e29906c 100644 --- a/tornado/httputil.py +++ b/tornado/httputil.py @@ -316,6 +316,9 @@ class HTTPServerRequest(object): be accessed through the "connection" attribute. Since connections are typically kept open in HTTP/1.1, multiple requests can be handled sequentially on a single connection. + + .. versionchanged:: 3.3 + Moved from ``tornado.httpserver.HTTPRequest``. """ def __init__(self, method=None, uri=None, version="HTTP/1.0", headers=None, body=None, host=None, files=None, connection=None, @@ -345,7 +348,13 @@ class HTTPServerRequest(object): self.body_arguments = {} def supports_http_1_1(self): - """Returns True if this request supports HTTP/1.1 semantics""" + """Returns True if this request supports HTTP/1.1 semantics. + + .. deprecated:: 3.3 + Applications are less likely to need this information with the + introduction of `.HTTPConnection`. If you still need it, access + the ``version`` attribute directly. + """ return self.version == "HTTP/1.1" @property @@ -362,12 +371,22 @@ class HTTPServerRequest(object): return self._cookies def write(self, chunk, callback=None): - """Writes the given chunk to the response stream.""" + """Writes the given chunk to the response stream. + + .. deprecated:: 3.3 + Use ``request.connection`` and the `.HTTPConnection` methods + to write the response. + """ assert isinstance(chunk, bytes_type) self.connection.write(chunk, callback=callback) def finish(self): - """Finishes this HTTP request on the open connection.""" + """Finishes this HTTP request on the open connection. + + .. deprecated:: 3.3 + Use ``request.connection`` and the `.HTTPConnection` methods + to write the response. + """ self.connection.finish() self._finish_time = time.time() @@ -428,37 +447,122 @@ class HTTPServerRequest(object): class HTTPInputException(Exception): """Exception class for malformed HTTP requests or responses from remote sources. + + .. versionadded:: 3.3 """ pass class HTTPOutputException(Exception): - """Exception class for errors in HTTP output.""" + """Exception class for errors in HTTP output. + + .. versionadded:: 3.3 + """ pass class HTTPServerConnectionDelegate(object): + """Implement this interface to handle requests from `.HTTPServer`. + + .. versionadded:: 3.3 + """ def start_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() def on_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 class HTTPMessageDelegate(object): + """Implement this interface to handle an HTTP request or response. + + .. versionadded:: 3.3 + """ def headers_received(self, start_line, headers): + """Called when the HTTP headers have been received and parsed. + + :arg start_line: a `.RequestStartLine` or `.ResponseStartLine` + depending on whether this is a client or server message. + :arg headers: a `.HTTPHeaders` instance. + + Some `.HTTPConnection` methods can only be called during + ``headers_received``. + + May return a `.Future`; if it does the body will not be read + until it is done. + """ pass def data_received(self, chunk): + """Called when a chunk of data has been received. + + May return a `.Future` for flow control. + """ pass def finish(self): + """Called after the last chunk of data has been received.""" pass def on_connection_close(self): + """Called if the connection is closed without finishing the request. + + If ``headers_received`` is called, either ``finish`` or + ``on_connection_close`` will be called, but not both. + """ pass +class HTTPConnection(object): + """Applications use this interface to write their responses. + + .. versionadded:: 3.3 + """ + def write_headers(self, start_line, headers, chunk=None, callback=None, + has_body=True): + """Write an HTTP header block. + + :arg start_line: a `.RequestStartLine` or `.ResponseStartLine`. + :arg headers: a `.HTTPHeaders` instance. + :arg chunk: the first (optional) chunk of data. This is an optimization + so that small responses can be written in the same call as their + headers. + :arg callback: a callback to be run when the write is complete. + :arg has_body: as an optimization, may be ``False`` to indicate + that no further writes will be coming. + + Returns a `.Future` if no callback is given. + """ + raise NotImplementedError() + + def write(self, chunk, callback=None): + """Writes a chunk of body data. + + The callback will be run when the write is complete. If no callback + is given, returns a Future. + """ + raise NotImplementedError() + + def finish(self): + """Indicates that the last body data has been written. + """ + raise NotImplementedError() + + def url_concat(url, args): """Concatenate url and argument dictionary regardless of whether url has existing query parameters. diff --git a/tornado/ioloop.py b/tornado/ioloop.py index 116866cd..86c4a9f2 100644 --- a/tornado/ioloop.py +++ b/tornado/ioloop.py @@ -159,7 +159,10 @@ class IOLoop(Configurable): @staticmethod def clear_instance(): - """Clear the global `IOLoop` instance.""" + """Clear the global `IOLoop` instance. + + .. versionadded:: 3.3 + """ if hasattr(IOLoop, "_instance"): del IOLoop._instance diff --git a/tornado/web.py b/tornado/web.py index 98b9a7ba..dbd6abf5 100644 --- a/tornado/web.py +++ b/tornado/web.py @@ -768,6 +768,9 @@ class RequestHandler(object): Note that only one flush callback can be outstanding at a time; if another flush occurs before the previous flush's callback has been run, the previous callback will be discarded. + + .. versionchanged:: 3.3 + Now returns a `.Future` if no callback is given. """ chunk = b"".join(self._write_buffer) self._write_buffer = [] @@ -1253,6 +1256,13 @@ class RequestHandler(object): # in a finally block to avoid GC issues prior to Python 3.4. self._prepared_future.set_result(None) + def data_received(self, chunk): + """Implement this method to handle streamed request data. + + Requires the `.stream_request_body` decorator. + """ + raise NotImplementedError() + def _log(self): """Logs the current request. @@ -1395,6 +1405,7 @@ def stream_request_body(cls): """Apply to `RequestHandler` subclasses to enable streaming body support. This decorator implies the following changes: + * `.HTTPServerRequest.body` is undefined, and body arguments will not be included in `RequestHandler.get_argument`. * `RequestHandler.prepare` is called when the request headers have been diff --git a/tornado/wsgi.py b/tornado/wsgi.py index 4d2c4627..ad2d885e 100644 --- a/tornado/wsgi.py +++ b/tornado/wsgi.py @@ -174,6 +174,7 @@ class WSGIAdapter(object): that it is not possible to use `.AsyncHTTPClient`, or the `tornado.auth` or `tornado.websocket` modules. + .. versionadded:: 3.3 """ def __init__(self, application): if isinstance(application, WSGIApplication):