Skip to content

Commit d43acb8

Browse files
committed
Update to gunicorn_h1c >= 0.6.2 for asgi_headers support
- Use asgi_headers property (lowercase names) from fast parser - Bump version to 25.3.0 - Update changelog with all changes for this release
1 parent cbd27e8 commit d43acb8

7 files changed

Lines changed: 73 additions & 7 deletions

File tree

docs/content/2026-news.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
<span id="news-2026"></span>
22
# Changelog - 2026
33

4+
## 25.3.0 - 2026-03-26
5+
6+
### Bug Fixes
7+
8+
- **HTTP/2 ASGI Body Duplication**: Fix request body being received twice in HTTP/2
9+
ASGI requests, causing JSON parsing errors with "Extra data" messages
10+
([#3558](https://github.com/benoitc/gunicorn/issues/3558))
11+
12+
- **ASGI Chunked EOF Handling**: Add `finish()` method to callback parser to handle
13+
chunked encoding edge case where connection closes before final CRLF after zero-chunk
14+
15+
### Security
16+
17+
- **ASGI Parser Header Validation**: Add security checks per RFC 9110/9112:
18+
- Reject duplicate Content-Length headers
19+
- Reject requests with both Content-Length and Transfer-Encoding
20+
- Reject chunked transfer encoding in HTTP/1.0
21+
- Reject stacked chunked encoding
22+
- Validate Transfer-Encoding values
23+
- Strict chunk size validation
24+
25+
### Changes
26+
27+
- **Fast HTTP Parser**: Update to gunicorn_h1c >= 0.6.2 for `asgi_headers` property
28+
which provides headers with lowercase names directly from the C parser
29+
30+
- **ASGI PROXY Protocol**: Add PROXY protocol v1/v2 support to callback parser
31+
32+
---
33+
434
## 25.2.0 - 2026-03-24
535

636
### New Features

docs/content/news.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,36 @@
11
<span id="news"></span>
22
# Changelog
33

4+
## 25.3.0 - 2026-03-26
5+
6+
### Bug Fixes
7+
8+
- **HTTP/2 ASGI Body Duplication**: Fix request body being received twice in HTTP/2
9+
ASGI requests, causing JSON parsing errors with "Extra data" messages
10+
([#3558](https://github.com/benoitc/gunicorn/issues/3558))
11+
12+
- **ASGI Chunked EOF Handling**: Add `finish()` method to callback parser to handle
13+
chunked encoding edge case where connection closes before final CRLF after zero-chunk
14+
15+
### Security
16+
17+
- **ASGI Parser Header Validation**: Add security checks per RFC 9110/9112:
18+
- Reject duplicate Content-Length headers
19+
- Reject requests with both Content-Length and Transfer-Encoding
20+
- Reject chunked transfer encoding in HTTP/1.0
21+
- Reject stacked chunked encoding
22+
- Validate Transfer-Encoding values
23+
- Strict chunk size validation
24+
25+
### Changes
26+
27+
- **Fast HTTP Parser**: Update to gunicorn_h1c >= 0.6.2 for `asgi_headers` property
28+
which provides headers with lowercase names directly from the C parser
29+
30+
- **ASGI PROXY Protocol**: Add PROXY protocol v1/v2 support to callback parser
31+
32+
---
33+
434
## 25.2.0 - 2026-03-24
535

636
### New Features

gunicorn/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
# This file is part of gunicorn released under the MIT license.
33
# See the NOTICE for more information.
44

5-
version_info = (25, 2, 0)
5+
version_info = (25, 3, 0)
66
__version__ = ".".join([str(v) for v in version_info])
77
SERVER = "gunicorn"
88
SERVER_SOFTWARE = "%s/%s" % (SERVER, __version__)

gunicorn/asgi/parser.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -835,7 +835,9 @@ def from_parser(cls, parser, is_ssl=False):
835835
req.version = parser.http_version
836836

837837
# Headers - store both bytes (for ASGI scope) and strings (for compatibility)
838-
req.headers_bytes = list(parser.headers)
838+
# Use asgi_headers (lowercase names) if available (fast parser >= 0.6.2),
839+
# otherwise fall back to headers (Python parser already uses lowercase)
840+
req.headers_bytes = list(getattr(parser, 'asgi_headers', None) or parser.headers)
839841
req.headers = [
840842
(n.decode('latin-1').upper(), v.decode('latin-1'))
841843
for n, v in parser.headers

pyproject.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,11 @@ tornado = ["tornado>=6.5.0"]
5353
gthread = []
5454
setproctitle = ["setproctitle"]
5555
http2 = ["h2>=4.1.0"]
56-
fast = ["gunicorn_h1c>=0.6.0"]
56+
57+
58+
59+
fast = ["gunicorn_h1c>=0.6.2"]
60+
5761
testing = [
5862
"gevent>=24.10.1",
5963
"eventlet>=0.40.3",

requirements_test.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@ coverage
33
pytest>=7.2.0
44
pytest-cov
55
pytest-asyncio
6-
gunicorn_h1c>=0.6.0
6+
gunicorn_h1c>=0.6.2

tests/conftest.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def http_parser(request):
2121
"""Parametrize tests over http_parser implementations."""
2222
if request.param == "fast":
2323
gunicorn_h1c = pytest.importorskip("gunicorn_h1c", reason="gunicorn_h1c required")
24-
# Require >= 0.6.0 for header framing validation
25-
if not hasattr(gunicorn_h1c, 'InvalidHeader'):
26-
pytest.skip("gunicorn_h1c >= 0.6.0 required")
24+
# Require >= 0.6.2 for asgi_headers support
25+
if not hasattr(gunicorn_h1c.H1CProtocol, 'asgi_headers'):
26+
pytest.skip("gunicorn_h1c >= 0.6.2 required")
2727
return request.param

0 commit comments

Comments
 (0)