gunicorn_h1c 0.6.5 ships the Content-Length list-form rejection
(h1c #8). The last python_only marker can now come off
rfc9112_smuggle_cl_list_form_01.
gunicorn_h1c 0.6.4 ships the RFC 9110/9112 hardening added in h1c #4,
#6, and #7: control chars in header values, request-target form/method
pairing, and forbidden trailer field-names. All the corresponding
fixtures now pass against the C parser, so their python_only markers
are removed.
The CL list form fixture stays marked — the C parser does not yet
reject Content-Length: "5, 5".
The Python WSGI and ASGI parsers both accepted `GET *` and similar; RFC
9112 restricts asterisk-form to OPTIONS. Both now raise InvalidRequestLine.
The fast (C) parser in gunicorn_h1c does not yet enforce this, so the
fixture is marked python_only via a new sidecar flag honored by the WSGI
and ASGI invalid-request harnesses.
Six treq fixtures covering gaps: absolute-form, asterisk-form (OPTIONS *),
authority-form (CONNECT), TE codings stacking (gzip/identity before chunked),
and the CL + TE:chunked smuggling vector.
Phase 1 of a staged corpus expansion; fixtures only, no parser changes.
Per documentation, limit_request_line=0 means unlimited. The code was
incorrectly treating 0 as "use default max" by checking <= 0 instead
of < 0.
For the fast C parser (gunicorn_h1c), which doesn't support 0 as
unlimited, pass a large value (1MB) instead. This applies to both
WSGI workers (http/message.py) and ASGI workers (asgi/protocol.py).
Fixes#3563
Update to gunicorn_h1c >= 0.6.3 which adds InvalidChunkExtension
validation for rejecting chunk extensions with bare CR bytes per
RFC 9112.
Changes:
- Update pyproject.toml to require gunicorn_h1c >= 0.6.3
- Add InvalidChunkExtension exception to gunicorn/asgi/parser.py
- Handle InvalidChunkExtension from both Python and C parsers in protocol.py
- Add chunk extension validation tests
- Update treq.py badrequest class to support hex escapes
- LimitRequestLine now accepts optional max_size parameter
- Use default max limits when limit_request_line or limit_request_field_size is 0
- Add tests validating default max enforcement (8190 bytes)
- Handle alternate exceptions from fast parser in test_invalid_requests
- 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
Extend --proxy-protocol to accept version values (off, v1, v2, auto) instead
of being boolean-only. This allows explicit control over which PROXY protocol
versions are accepted.
Changes:
- Add InvalidProxyHeader exception for v2 binary header errors
- Add validate_proxy_protocol() validator with backwards compatibility
- Update ProxyProtocol setting with nargs="?" and const="auto"
- Add PROXY v2 constants (PP_V2_SIGNATURE, PPCommand, PPFamily, PPProtocol)
- Add _parse_proxy_protocol_v1() and _parse_proxy_protocol_v2() methods
- Update both sync (message.py) and async (asgi/message.py) parsers
- Add hex escape handling in treq.py for v2 binary test data
- Add test cases for v2 TCPv4 and TCPv6
Backwards compatible: --proxy-protocol alone (or True) maps to "auto".
Closes#2912
Strip whitespace also *after* header field value.
Simply refuse obsolete header folding (a default-off
option to revert is temporarily provided).
While we are at it, explicitly handle recently
introduced http error classes with intended status code.
changes:
- Just follow the new TE specification (https://datatracker.ietf.org/doc/html/rfc9112#name-transfer-encoding)
here and accept to introduce a breaking change.
- gandle multiple TE on one line
** breaking changes ** : invalid headers and position will now return
an error.
New parser rule: refuse HTTP requests where a header field value
contains characters that
a) should never appear there in the first place,
b) might have lead to incorrect treatment in a proxy in front, and
c) might lead to unintended behaviour in applications.
From RFC 9110 section 5.5:
"Field values containing CR, LF, or NUL characters are invalid and
dangerous, due to the varying ways that implementations might parse
and interpret those characters; a recipient of CR, LF, or NUL within
a field value MUST either reject the message or replace each of those
characters with SP before further processing or forwarding of that
message."
chunk extensions are silently ignored before and after this change;
its just the whitespace handling for the case without extensions that matters
applying same strip(WS)->rstrip(BWS) replacement as already done in related cases
half-way fix: could probably reject all BWS cases, rejecting only misplaced ones
Note: This is unrelated to a reverse proxy potentially talking HTTP/3 to clients.
This is about the HTTP protocol version spoken to Gunicorn, which is HTTP/1.0 or HTTP/1.1.
Little legitimate need for processing HTTP 1 requests with ambiguous version numbers.
Broadly refuse.
Co-authored-by: Ben Kallus <benjamin.p.kallus.gr@dartmouth.edu>
Do the validation on the original, not the result from unicode case folding.
Background:
latin-1 0xDF is traditionally uppercased 0x53+0x53 which puts it back in ASCII
If we promise wsgi.input_terminated, we better get it right - or not at all.
* chunked encoding on HTTP <= 1.1
* chunked not last transfer coding
* multiple chinked codings
* any unknown codings (yes, this too! because we do not detect unusual syntax that is still chunked)
* empty coding (plausibly harmless, but not see in real life anyway - refused, for the moment)
Ambiguous mappings open a bottomless pit of "what is user input and what is proxy input" confusion.
Default to what everyone else has been doing for years now, silently drop.
see also https://nginx.org/r/underscores_in_headers
- Unify HEADER_RE and METH_RE
- Replace CRLF with SP during obs-fold processing (See RFC 9112 Section 5.2, last paragraph)
- Stop stripping header names.
- Remove HTAB in OWS in header values that use obs-fold (See RFC 9112 Section 5.2, last paragraph)
- Use fullmatch instead of search, which has problems with empty strings. (See GHSA-68xg-gqqm-vgj8)
- Split proxy protocol line on space only. (See proxy protocol Section 2.1, bullet 3)
- Use fullmatch for method and version (Thank you to Paul Dorn for noticing this.)
- Replace calls to str.strip() with str.strip(' \t')
- Split request line on SP only.
Co-authored-by: Paul Dorn <pajod@users.noreply.github.com>
Numbers must be separated by dot. This makes life
a little bit harder for attackers who would like to inject specially crafted packets after GET / (e.g. in nginx there are sometimes regular expressions like (?P<action>[^.]).html
``send_special_chunks`` sends the request data in two chunks, one having a length of 1 byte, which ensures there is no CRLF, and a second chunk containing the rest of the request data. Practically, this forces ``gunicorn.http.message.Request.read_line()`` to get the full request data with two buffer reads and reveal possible bugs related to the internal buffer mechanism. See #670 for more information.
patch from Djoume Salvetti . address the following issues in gunicorn:
* Gunicorn does not limit the size of a request header (the
* limit_request_field_size configuration parameter is not used)
* When the configured request limit is lower than its maximum value, the
* maximum value is used instead. For instance if limit_request_line is
* set to 1024, gunicorn will only limit the request line to 4096 chars
* (this issue also affects limit_request_fields)
* Request limits are not limited to their maximum authorized values. For
* instance it is possible to set limit_request_line to 64K (this issue
* also affects limit_request_fields)
* Setting limit_request_fields and limit_request_field_size to 0 does
* not make them unlimited. The following patch allows limit_request_line
* and limit_request_field_size to be unlimited. limit_request_fields can
* no longer be unlimited (I can't imagine 32K fields to not be enough
* but I have a use case where 8K for the request line is not enough).
* Parsing errors (premature client disconnection) are not reported
* When request line limit is exceeded the configured value is reported
* instead of the effective value.
Add --limit-request-fields (limit_request_fields) and
--limit-request-field-size (limit-request-field-size) options.
- limit_request_fields:
Value is a number from 0 (unlimited) to 32768. This parameter is
used to limit the number of headers in a request to prevent DDOS
attack. Used with the `limit_request_field_size` it allows more
safety.
- limit_request_field_size:
Value is a number from 0 (unlimited) to 8190. to set the limit
on the allowed size of an HTTP request header field.
You can now pass the parameter --limit-request-line or set the
limit_request_line in your configuration file to set the max size of the
request line in bytes.
This parameter is used to limit the allowed size of a client's HTTP
request-line. Since the request-line consists of the HTTP method, URI,
and protocol version, this directive places a restriction on the length
of a request-URI allowed for a request on the server. A server needs
this value to be large enough to hold any of its resource names,
including any information that might be passed in the query part of a
GET request. By default this value is 4094 and can't be larger than
8190.
This parameter can be used to prevent any DDOS attack.