Python 3.15 is now supported. Python 3.9 and 3.10 are no longer supported; 3.11 is the minimum supported version.
Free-threaded python interpreters are now supported; prebuilt wheels are available for 3.14t and 3.15t.
Windows on ARM is now supported and prebuilt wheels are available.
Prebuilt wheels are now available for riscv64, ppc64le, and s390x architectures on
Linux. These architectures are experimental and have limited support and testing.
PyPy 8.0 is now supported (for both Python 3.11 and 3.12).
Error messages have been improved in many places.
Improved handling of asyncio.CancelledError in various parts of the framework.
New feature cross_origin_protection provides a new mechanism for protecting against
XSRF and other cross-origin attacks. This feature is easier to use than xsrf_cookies
and is expected to become the default in Tornado 6.7.
The host argument to the HTTPServerRequest constructor is deprecated (since
Tornado 6.5.2); use headers["Host"] instead. It will be removed in Tornado 6.7.
The start_line argument to the HTTPServerRequest constructor will become required in
Tornado 6.7. The method, uri, and version arguments are deprecated and will be
removed at the same time. All remaining arguments will become keyword-only. A
DeprecationWarning is now emitted when start_line is not provided or when the host,
method, or uri arguments are used.
tornado.auth.OpenIdMixin is deprecated and will be removed in Tornado 6.7 (this is a change
in the timeline; it was previously scheduled for removal in Tornado 7.0).
tornado.util.TimeoutError, tornado.gen.TimeoutError, and tornado.ioloop.TimeoutError
are deprecated. They are aliases for the builtin TimeoutError, which should be used instead.
(No warning is emitted when these aliases are used.)
Calling GzipDecompressor.decompress without a max_length argument (or with
max_length=0) is deprecated and emits a DeprecationWarning. In Tornado 7.0,
max_length will be required.
The xsrf_cookies application setting is expected to be deprecated in Tornado 6.7 (with
warnings) in favor of cross_origin_protection. It will not be removed before Tornado 7.0.
tornado.gen.multi now has overload annotations to indicate that its return type depends
on its inputs (list vs dict).
HTTPServerRequest.method and uri attributes are now typed as str instead of
str | None.
tornado.curl_httpclientThe minimum supported version of libcurl is now 7.81 (which is the version shipped by Ubuntu 22.04 LTS).
Passing a coroutine as the streaming_callback argument now fails with a TypeError.
(Coroutine streaming callbacks are only supported by simple_httpclient.)
tornado.genasyncio.CancelledError is now handled correctly in coroutines, multi, and chain_future.
WaitIterator now supports multiple copies of the same Future object.
with_timeout has a new message argument, which sets the message of the TimeoutError.
It may be a string or a callable that returns a string (which is only called if the timeout
expires).
tornado.http1connectionInvalid Host headers are now logged at the INFO level instead of as an uncaught exception.
Line folding in HTTP headers is no longer supported. This was deprecated in Tornado 6.5 but is being removed sooner than originally scheduled to avoid DoS risk.
Reading a request or response body now yields to the event loop periodically, so a peer that sends a body faster than it can be processed (especially with many tiny chunks in chunked encoding) can no longer block the event loop for the duration of the body.
Small chunks of a body using chunked encoding are now coalesced before being passed to
data_received, so the pieces seen by the application no longer correspond to the HTTP
chunk boundaries.
A chunk in a chunked body that is not followed by \r\n is now reported as an
HTTPInputError instead of raising AssertionError (or being silently accepted when
Python is run with -O).
Gzipped bodies containing multiple members are now decompressed correctly. Trailing garbage
at the end of a body or in between members is now an error instead of being silently ignored,
as is a body that is truncated partway through a member. This applies both to responses
received by simple_httpclient and to requests received by a server with
decompress_request=True.
tornado.httputilIf start_line is not passed to HTTPServerRequest, missing method and uri
arguments now default to GET and / (with a DeprecationWarning) instead of leaving
these attributes as None.
HTTPHeaders.__delitem__ no longer raises KeyError at inappropriate times.
tornado.iostreamFixed a socket leak when a timeout occurs during a TLS handshake, or when the connection
of a SSLIOStream is cancelled.
Fixed errors relating to “existing exports of data” on PyPy 8.0.
Tests now pass when Python is built with AWS-LC or BoringCrypto instead of OpenSSL.
tornado.localeformat_date now correctly handles times in the future that are close to a multiple of
24 hours away.
tornado.locksA timeout value of zero will either return or raise immediately. Previously, zero was
treated as equivalent to None (wait forever). This affects Condition.wait, Event.wait,
Semaphore.acquire, and Lock.acquire.
tornado.netutilThe default SSLContext objects are now lazily created on first use rather than at import time.
tornado.platform.asyncioThe selector thread used on Windows now cleans up its sockets correctly on interpreter exit, preventing logged warnings.
tornado.platform.caresresolverThe CaresResolver class (which has been deprecated since Tornado 6.2) requires pycares
version 4.x; it will not be updated to support pycares 5.x.
tornado.queuesA timeout value of zero will either return or raise immediately. Previously, zero was
treated as equivalent to None (wait forever). This affects Queue.get, Queue.put, and
Queue.join.
tornado.simple_httpclientGzipped responses containing multiple members are now decompressed correctly, and malformed
gzip data is now an error (see tornado.http1connection above).
Improved error messages for timeouts during the connection phase.
The streaming_callback argument to SimpleAsyncHTTPClient.fetch may now be a coroutine.
tornado.tcpclientWhen a connection attempt is cancelled, sockets are now closed immediately instead of waiting for the connection to either complete or time out.
tornado.utilGzipDecompressor now decompresses streams containing multiple gzip members in full;
previously everything after the first member was ignored. Data after the last member that is
not the start of another member is now an error, as is a stream that ends partway through a
member. GzipDecompressor.flush now raises zlib.error for truncated input and always
returns an empty byte string (since decompress returns all of the
output); it raises ValueError if unconsumed_tail has not been passed
back to decompress.
tornado.webNew feature cross_origin_protection provides a new mechanism for protecting against
XSRF and other cross-origin attacks. This feature is easier to use than xsrf_cookies
and is expected to become the default in Tornado 6.7. Additional origins can be allowed
with the new trusted_origins application setting or by overriding the new method
RequestHandler.check_trusted_origin. See Cross-origin protection for details.
asyncio.CancelledError is now handled correctly instead of leaving connections to hang.
raise HTTPError(204) no longer attempts to incorrectly send a response body.
Writing a response body with a status code that does not allow one (such as 204 or 304) now
raises RuntimeError instead of AssertionError (so the check is no longer skipped when
Python runs with -O).
Signed values with valid signatures but certain formatting errors are now ignored instead of raising errors (consistent with historical behavior for other formatting errors).
The max_age argument to RequestHandler.set_cookie can no longer be abused for
attribute injection.
RedirectHandler now refuses to generate protocol-relative redirects via formatted URLs,
which could potentially be used as an open redirect.
tornado.websocketWebSocketHandler.check_origin is no longer called for requests that can be determined
to be same-origin, using the Sec-Fetch-Site header or (if that header is absent) by
comparing the Origin and Host headers. Its default implementation now calls
RequestHandler.check_trusted_origin, so origins listed in the trusted_origins
application setting are allowed. Applications that override check_origin to restrict
origins further than the default will no longer see same-origin requests.
asyncio.CancelledError no longer breaks the websocket loop without closing the connection.
Receiving messages now yields to the event loop periodically, so a peer that sends many small messages faster than they can be processed can no longer block the event loop indefinitely.
When a ping is received while the pong for a previous ping has not yet been sent, only the
most recent ping is answered (as permitted by RFC 6455 section 5.5.3). Previously, a peer that
sent pings without reading the pongs could cause the server’s memory usage to grow without
limit. on_ping is still called for every ping.
Versions 7 and 8 of the WebSocket protocol (pre-standard drafts from 2011) are no longer
supported; only version 13 (RFC 6455) is accepted. The Sec-WebSocket-Origin header
used by those versions is no longer consulted.