Announcing Tornado 6.6b1

9 views
Skip to first unread message

Ben Darnell

unread,
Oct 7, 2026, 6:43:08 PM (8 days ago) Oct 7
to Tornado Mailing List, python-torn...@googlegroups.com
I am pleased to announce the first beta release of Tornado 6.6. While the previous alpha release focused on updates to the build pipeline and platform support, this one includes more new features and bug fixes.

The beta release can be installed from PyPI with `pip install --pre tornado==6.6b1`. The final release is planned for two weeks from today, Oct 21. 

I want to highlight the "cross-origin protection" feature (developed with Thomas Kluyver), which is a new technique for preventing XSRF attacks that is both easier to use and more robust than the `xsrf_cookies` feature. I encourage everyone testing this release to try enabling the `cross_origin_protection=True` application setting; I intend to enable this by default in Tornado 6.7. For applications that are served on a single hostname, just enabling the setting should be enough; if you use more than one host (including subdomains of a common domain), you may need to add to the `trusted_origin` setting to allow them to talk to each other.


What’s new in Tornado 6.6.0

Oct XX, 2026

General changes

  • 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.

Feature highlights

  • 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.

Deprecation notices

  • 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.

Type annotation changes

tornado.curl_httpclient

  • The 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.gen

tornado.http1connection

  • Invalid 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.httputil

  • If 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.iostream

  • Fixed 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.locale

  • format_date now correctly handles times in the future that are close to a multiple of 24 hours away.

tornado.locks

tornado.netutil

  • The default SSLContext objects are now lazily created on first use rather than at import time.

tornado.platform.asyncio

  • The selector thread used on Windows now cleans up its sockets correctly on interpreter exit, preventing logged warnings.

tornado.platform.caresresolver

  • The 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.queues

  • A 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_httpclient

  • Gzipped 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.tcpclient

  • When a connection attempt is cancelled, sockets are now closed immediately instead of waiting for the connection to either complete or time out.

tornado.util

  • GzipDecompressor 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.web

  • 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. 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.websocket

  • WebSocketHandler.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.




Reply all
Reply to author
Forward
0 new messages