Mirror the ASGI strip-and-warn behavior (commits 2191832b, 41ec7527,
0d35d2ae) on the WSGI path. Previously gunicorn would forward an
app-supplied Content-Length and body bytes for HEAD requests and
1xx/204/304 responses, violating RFC 9110 / RFC 9112.
- Add _response_omits_body() and _response_forbids_content_length()
helpers on Response.
- After process_headers, strip Content-Length and clear
response_length on 1xx/204 (RFC 9110 §6.4.2 forbids it). HEAD and
304 keep app-supplied Content-Length.
- write() and sendfile() drop body bytes for no-body responses and
log a single WARNING per request.
- is_chunked() now also covers 1xx via _omits_body.
Fixes#3413
- ASGI keepalive gate now keys on receiver._complete only. _closed is
overloaded across transport disconnect and receive timeout; treating
either as 'message complete' would re-enable the smuggling vector
the previous PR was meant to close.
- Parser.finish_body's 64 KiB byte cap now applies only when an explicit
deadline is given. Default invocations (notably __next__, used by
base_async / sync workers) regain the prior unbounded drain so a
partial drain does not silently desync the next request.
- WSGI fast parser now applies the same per-header policy as the Python
parser (Expect, secure_scheme_headers, forwarded_allow_ips trust gate,
forwarder_headers / header_map). Shared helpers extracted on Message.
- ASGI keepalive no longer resets the parser when the previous request
body was not fully framed; the connection closes instead, preventing
request smuggling on pipelined connections.
- BodyReceiver._wait_for_data timeout flips _closed and yields
http.disconnect rather than synthesizing more_body=False. Timeout
honors cfg.timeout.
- ASGI chunked encoding now skips HEAD, 204, and 304 (matches
Response.is_chunked in the WSGI path) via a small helper.
- _setup_callback_parser passes proxy_protocol to PythonProtocol; auto
falls back to the Python parser when proxy_protocol != off (the C
parser does not implement PROXY framing). _effective_peername swaps
the transport peer with the PROXY-supplied client address.
- Parser.finish_body accepts a deadline and a 64KiB byte cap; gthread
passes a deadline and abandons keepalive on incomplete drain so a
stalled client cannot tie up a worker thread.
* fix: add __iter__ and __next__ to FileWrapper for PEP 3333 compliance
The WSGI spec (PEP 3333) requires that wsgi.file_wrapper return an
iterable object. Gunicorn's FileWrapper only implemented __getitem__,
which technically makes it iterable via old-style iteration but breaks
code that explicitly relies on the iterator protocol (e.g., calling
iter() or using next()).
This adds __iter__ (returning self) and __next__ to make FileWrapper
a proper iterator, maintaining backward compatibility with existing
__getitem__-based usage.
Fixes#3396
* Fix lint: move imports to top of file
---------
Co-authored-by: contributor <noreply@users.noreply.github.com>
Co-authored-by: Benoit Chesneau <bchesneau@gmail.com>
- Bump version to 25.0.2
- Update copyright year to 2026 in LICENSE and NOTICE
- Add license headers to all Python source files
- Add changelog entry for 25.0.2
The unread method was incorrectly appending data to the end of the
buffer instead of prepending it to the beginning. This caused issues
when reading partial data and then unreading it.
Closes#2915Closes#2346
This commit reverts one aspect changed by 5f4ebd2eb2b08783a5fbefe79d09fcb3fc1fbc73 (#1151);
header-values are again encoded as latin-1 and not ascii. Test is restored but uses
a latin-1-mappable test-character, not a general utf8 character.
Fixed#1778.
Signed-off-by: Brett Randall <javabrett@gmail.com>