Ben Darnell [Fri, 7 Aug 2026 15:26:32 +0000 (11:26 -0400)]
pyupgrade: Automated use of new py311 features.
Only one seems to be the new datetime.UTC alias
(and a change to subprocess.run in a test, but this is because the
test had changed since the last time we ran pyupgrade)
Ben Darnell [Thu, 6 Aug 2026 01:20:58 +0000 (21:20 -0400)]
httputil: Enforce a new limit on the number of arguments in a request
Large POST bodies can be very expensive to parse in the worst case,
so use the (new in Python 3.8) max_num_fields argument to limit the
cost. A new field in ParseBodyConfig allows users to configure this
limit. The default is 1000, which is the same as that used in php and
node.js.
Add missing trailing commas to Python version classifiers
Without the commas, adjacent string literals concatenate into a single invalid
classifier (e.g. 'Programming Language :: Python :: 3.10Programming Language ::
Python :: 3.11...'). Fixes the generated lines and the cog template so it
doesn't regress on the next cog run.
url.replace("http://", ...) replaced every occurrence of "http://" in
the fetched URL, including the one inside the "url" query parameter
that RedirectHandler uses as the redirect target. That accidentally
embedded the test credentials in the Location header's URL too, so
the "different origin" subtest was actually exercising "does libcurl
honor credentials the server explicitly put in the redirect target"
rather than "does libcurl strip credentials carried over from the
original request" - libcurl correctly does the former, which is not
a credential leak.
Limit the replacement to the first occurrence so only the outer,
fetched URL carries the test credentials.
Separately, the "same origin" subtest for this case now surfaces an
actual libcurl regression (still present in curl's git master as of
this writing): credentials embedded in the URL are dropped across a
same-origin redirect when the Location header is an absolute URL
(a relative Location correctly preserves them). This isn't a security
issue since nothing leaks to another origin, so that specific
assertion is skipped rather than asserted either way.
Ben Darnell [Mon, 22 Jun 2026 18:38:46 +0000 (14:38 -0400)]
httputil: Deprecate some args to HTTPServerRequest constructor
HTTPServerRequest has some redundant and/or obsolete constructor
arguments. #3542 cleaned this up a bit, but it included a backwards
incompatible change with no deprecation warning. This change adds
deprecation warnings in anticipation of deleting the old arguments
in Tornado 6.7.
In Tornado 6.5, all arguments were officially optional, but things would
only partially work without the method and uri arguments. #3542 made
either the uri or start_line arguments mandatory.
This change makes the method and uri arguments deprecated. It is also
deprecated to use method and uri at the same time as start_line
(previously method and uri would be silently ignored). In Tornado 6.7,
the method, uri, and version arguments will be removed, and start_line
will be required.
Ben Darnell [Fri, 5 Jun 2026 19:33:33 +0000 (15:33 -0400)]
curl_httpclient: Reset the curl object before putting it on the freelist
We previously did a piecemeal reset of the curl object, but missed some
options (and in fact many of these options cannot be unset without
fully resetting the object). This allowed some options to leak between
requests.
Now we use the curl reset method to ensure we catch everything.
Ben Darnell [Wed, 27 May 2026 01:30:28 +0000 (21:30 -0400)]
simple_httpclient: Strip auth headers on cross-origin redirects
When following a redirect to a different origin (scheme, host, or port),
auth-related headers (Authorization and Cookie) should be stripped to
avoid exposing them to the new host.
Ben Darnell [Tue, 26 May 2026 17:39:53 +0000 (13:39 -0400)]
http1connection: Enforce max_body_size in _GzipMessageDelegate
This ensures we limit the post-decompression size of the body, and not
only the compressed size (which is enforced via the Content-Length
header at header-processing time).
This previously used substring search, which is incorrect, although
unlikely to be a vulnerability because there are no free-form text
fields allowed in this response format.
mokashang [Tue, 19 May 2026 16:22:52 +0000 (09:22 -0700)]
test/process: run test_multi_process in a clean subprocess
When `python3 -m tornado.test` is run in an environment where some test
earlier in the suite has left a thread running, the `os.fork()` inside
`fork_processes()` triggers `DeprecationWarning: This process (pid=...)
is multi-threaded, use of fork() may lead to deadlocks in the child` on
Python 3.12+. The test suite turns DeprecationWarnings from tornado
into errors, so `test_multi_process` then fails. This has been observed
in the Fedora rpm build of tornado on Python 3.15.0b1 (#3623), where it
does not reproduce under tox.
Rather than chase down every thread leak across the suite, isolate
`test_multi_process` so it always starts from a single-threaded state.
The actual fork-and-serve logic is moved into a script string that is
executed via `python -c` in a fresh interpreter, following the pattern
established in autoreload_test for tests that need a clean process. The
outer test method just launches the subprocess and asserts a clean
exit. PYTHONPATH is propagated so the source tree under test is
importable. The script keeps the existing `signal.alarm(5)` timers and
`subprocess.run(timeout=30)` is added as a backstop in case the script
hangs in a way the alarms don't catch.
Tested locally on macOS / Python 3.13 with the full suite plus a
deliberately leaked thread before the test to confirm the new isolation
holds. The `tearDown` / `get_app` helpers and the `asyncio`, `logging`,
and HTTP-related top-level imports are no longer needed and are
removed.
Ben Darnell [Fri, 20 Mar 2026 15:18:03 +0000 (11:18 -0400)]
ci: Update versions of github actions
These actions are generating deprecation warnings due to an old node.js
version, and for some reason github marks this upgrade as a major
version change even when there is no behavior change.
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>