Ben Darnell [Fri, 20 Mar 2026 01:36:17 +0000 (21:36 -0400)]
ci: Build wheels for more architectures under emulation
This commit also fixes the macos build to use universal2 wheels,
which was broken with the addition of riscv64 support.
Support for these architectures is experimental, but we have had
a request for ppc64le in #3449. As long as emulation gives us a turnkey
solution, we might as well build for them, but if the emulation
pipeline turns out to be unstable we will reconsider. (armv7l is also
experimentally supported by cibuildwheel and even shows up more
frequently in our download stats than ppc64le and s390x, but I got
a cryptic failure when I tried it so I'm leaving it out for now.)
This commit reduces the amount of testing we do for emulated
builds because they are otherwise the slowest part of the build
pipeline.
Ben Darnell [Fri, 20 Mar 2026 03:05:22 +0000 (23:05 -0400)]
*: Rewrite imports with isort
Today's type import changes have caused a lot of churn in the import
statements and we've never had a consistent style. Run a one-time
cleanup with isort to tidy things up. I'm not (currently) planning
to make this a CI-enforced rule.
Julien Stephan [Wed, 26 Nov 2025 17:09:48 +0000 (18:09 +0100)]
ci: add riscv64 manylinux/musllinux wheels
Now that cibuildwheel and PyPI support riscv64, we can start building
riscv64 wheels for Tornado.
Because there is no native riscv64 runner available, this PR adds a
QEMU-based riscv64 job to the cibuildwheel workflow.
Due to emulation, we need to:
- Increase ASYNC_TEST_TIMEOUT to 30s to accommodate slower runs
- Increase timeout for test_request_timeout
- Skip test_unquote_large
Signed-off-by: Julien Stephan <jstephan@baylibre.com>
Ben Darnell [Thu, 19 Mar 2026 19:03:46 +0000 (15:03 -0400)]
*: Remove most typing.TYPE_CHECKING guards and F401 noqa comments
Flake8 now understands type annotations and no longer emits
"unused import" warnings for type imports. Most imports that
were previously behind TYPE_CHECKING guards are no longer
needed, or can be moved to unguarded imports.
In 8.19.0-rc2, the error logic has been changed so any later errors are
preserved. This changes what is returned by curl and therefore what tornado
sees. For HTTPError variant of the test, which uses CurlAsyncHTTPClient, we get
the error from pycurl and now it contains "Failed binding local connection
end". This logic handles both the old version of libcurl and also the newer
one.
Co-Authored-By: Samuel Henrique <samueloph@debian.org>
Ben Darnell [Tue, 10 Mar 2026 16:19:50 +0000 (12:19 -0400)]
httputil: Add CRLF to _FORBIDDEN_HEADER_CHARS_RE
I think these were omitted due to quirks of an older version of the
parsing code. Linefeeds are already effectively prohibited within
header values since they are interpreted as delimiters, so the net
effect of this change is to prohibit bare carriage returns within
header values. This RE is used only when parsing headers inside
multipart/form-data bodies; for HTTP headers CR was already prohibited.
Ben Darnell [Fri, 6 Mar 2026 19:50:25 +0000 (14:50 -0500)]
web: Validate characters in all cookie attributes.
Our previous control character check was missing a check for
U+007F, and also semicolons, which are only allowed in quoted
parts of values. This commit checks all attributes and
updates the set of disallowed characters.
Ben Darnell [Tue, 3 Mar 2026 19:36:14 +0000 (14:36 -0500)]
httputil: Add limits on multipart form data parsing
The new default limits prevent a DoS vulnerability involving
requests with many multipart parts. It also adds a defense-in-depth
limit on the size of multipart headers, which would have mitigated
the vulnerability fixed in 6.5.3.
New data structures are added to allow users to configure these limits,
and to disable multipart parsing entirely if they choose. However,
due to the complexity of the plumbing required to pass these
configuration options through the stack, the only configuration
provided in this commit is the ability to set a global default.
Ben Darnell [Thu, 11 Dec 2025 03:00:03 +0000 (22:00 -0500)]
test: Use time.perf_counter instead of time.time for performance tests
On windows, time.time has low resolution (about 15ms), which makes
performance tests flaky. time.perf_counter has much higher resolution
and is the recommended way to measure elapsed time.
Ben Darnell [Wed, 10 Dec 2025 20:15:25 +0000 (15:15 -0500)]
web: Harden against invalid HTTP reason phrases
We allow applications to set custom reason phrases for the HTTP status
line (to support custom status codes), but if this were exposed to
untrusted data it could be exploited in various ways. This commit
guards against invalid reason phrases in both HTTP headers and in
error pages.
Ben Darnell [Wed, 10 Dec 2025 15:55:02 +0000 (10:55 -0500)]
httputil: Fix quadratic behavior in _parseparam
Prior to this change, _parseparam had O(n^2) behavior when parsing
certain inputs, which could be a DoS vector. This change adapts
logic from the equivalent function in the python standard library
in https://github.com/python/cpython/pull/136072/files
Ben Darnell [Tue, 9 Dec 2025 18:27:27 +0000 (13:27 -0500)]
httputil: Fix quadratic performance of repeated header lines
Previouisly, when many header lines with the same name were found
in an HTTP request or response, repeated string concatenation would
result in quadratic performance. This change does the concatenation
lazily (with a cache) so that repeated headers can be processed
efficiently.
Security: The previous behavior allowed a denial of service attack
via a maliciously crafted HTTP message, but only if the
max_header_size was increased from its default of 64kB.
Ben Darnell [Tue, 9 Dec 2025 17:10:18 +0000 (12:10 -0500)]
process_test: Use isolated mode for subprocess tests
Prompt customizations (notably the PYTHONSTARTUP file used by
vscode's terminal integration) can interfere with tests that run
interactive interpreters in a subprocess. Run those interpreters
in isolated mode to avoid this problem.)
Ben Darnell [Tue, 9 Dec 2025 15:19:34 +0000 (10:19 -0500)]
demos: Remove s3server demo
This program does not demonstrate anything particularly interesting
about Tornado, nor is it a good stylistic example to follow. Its
handling of path validation is rudimentary and can be insecure in
some configurations. It makes more sense to remove it than to
try and improve it.
Armen Michaeli [Sun, 9 Nov 2025 11:53:32 +0000 (12:53 +0100)]
Make `uri` and `method` attributes on `HTTPServerRequest` mandatory
This adds assertion checks during initialisation of `HTTPServerRequest` object, to ensure the corresponding attributes of the object being created, won't end up being `None`. To be clear, it is still permitted to provide `None` or omit specification for both `uri` and `method` parameters to the constructor if the `start_line` parameter specifies [good] values instead.
This is motivated by the idea that per HTTP, requests _always_ feature a method and a URI, and so the model implemented with `HTTPServerRequest` is amended accordingly. Beyond the seemingly idealistic motivation, this helps with _typed_ Tornado applications using e.g. `self.request.uri` as part of request handling -- i.e. in code with `self` being a `RequestHandler` -- to safely assume e.g. `uri` or `method` are `str` and not `str | None` / `Optional[str]` which would require ad-hoc assertions ala `assert self.request.uri is not None` (or `assert self.request.uri`) in _application code_, which IMO is a case of "surprising the user" -- as everyone would expect a HTTP request to have an URI and method be clearly defined as e.g. strings -- certainly excluding the `None` value.
Again, because semantics of `start_line` are preserved, the initialisation of the object _may_ omit parameters `uri` and/or `method` if `start_line` specifies valid values for these instead. In any case, it is the _attributes_ of the object being constructed, that end up being effectively validated with `assert` -- which make the type checker (tested with MyPy 1.18.2 here) assume `str` instead of `str | None`.
Ben Darnell [Fri, 8 Aug 2025 17:50:45 +0000 (13:50 -0400)]
web_test: Move an ignore_deprecation block
When warnings are in context-aware mode (the default in free-threaded
Python 3.14), the server captures the context from setUp and does not
see the warning filter installed in the test method. Move the warning
filter into the handler so it works consistently regardless of the
context_aware_warnings flag.
Ben Darnell [Thu, 24 Jul 2025 20:37:48 +0000 (20:37 +0000)]
websocket: Expand testing of next-ping calculation
Includes end-to-end tests that the correct number of pings are sent
(piggybacking on an existing test) and a unit test for the
`ping_sleep_time` calculation.
Oliver Sanders [Thu, 19 Jun 2025 10:06:29 +0000 (11:06 +0100)]
websocket_ping: fix ping interval with non-zero timeout and improve docs.
* Fix a bug that caused the ping interval to be less frequent than
configured.
* Fix erroneous documentation of the websocket_ping_timeout default and
clarify units for the ping interval.
- current version of setuptools (70.1+) does not need wheel at all
- older versions of setuptools would fetch wheel when building wheels (but not sdists)
Fix ValueError in file_uploader.py by converting @gen.coroutine to async/await
Fixes #3182
The file_uploader.py demo was failing with ValueError when trying to upload files
because @gen.coroutine decorated functions return tornado.concurrent.Future objects,
but asyncio.run() expects native coroutines.
This change converts the @gen.coroutine decorated functions to native async/await
syntax, which is the modern recommended approach and resolves the compatibility
issue with asyncio.run().
Changes:
- Remove tornado.gen import
- Convert @gen.coroutine decorators to async def
- Convert all yield statements to await
- Maintains backward compatibility with existing Tornado versions
Ben Darnell [Tue, 22 Jul 2025 17:54:03 +0000 (17:54 +0000)]
http1connection: Improve error logging for invalid host headers
This was previously being logged as an uncaught exception in application
code, which is wrong for a malformed request. HTTPInputError now passes
through the app-error logging to be caught and reported as a 400
(which logs at the warning level to the access log and info to the
general log).