3518 Commits

Author SHA1 Message Date
Ben Leembruggen
9243b2ad19 chore: remove integration test file and trim docstring 2026-04-01 11:57:03 +11:00
Ben Leembruggen
bcb13b1e74 Fix _handle_stream_ended to set _body_complete in async HTTP/2 handler
_handle_stream_ended() in async_connection.py (used by the ASGI worker)
did not set stream._body_complete = True or signal stream._body_event.
This caused the receive() closure in protocol.py to never see the body
as complete via the streaming path, so on the next call the fast path
re-read the entire body from BytesIO, doubling it.

The sync handler in connection.py already had a partial fix from #3559
but was also missing _body_event signalling, which is needed to unblock
any pending read_body_chunk() await.

Fixes https://github.com/benoitc/gunicorn/discussions/3567
2026-04-01 11:53:06 +11:00
Benoit Chesneau
3e2167c346
Add InvalidChunkExtension mapping and fast parser support for ASGI tests (#3565)
* Add InvalidChunkExtension to treq_asgi.py and fast parser support

- Add InvalidChunkExtension import and exception mapping for proper test
  coverage of bare CR rejection in chunk extensions per RFC 9112 7.1.1
- Add fast parser (H1CProtocol) support to treq_asgi.py and the ASGI
  invalid request tests
- Fast parser now receives limit configuration (limit_request_line,
  limit_request_fields, limit_request_field_size)
- Handle gunicorn_h1c's multiple ParseError classes from different modules
- Skip tests where fast parser has different validation than Python parser

* Handle gunicorn_h1c limit exceptions in ASGI protocol

Add handling for gunicorn_h1c.LimitRequestLine and
gunicorn_h1c.LimitRequestHeaders exceptions, matching the behavior
of the Python parser exceptions with appropriate HTTP status codes:
- LimitRequestLine: 414 URI Too Long
- LimitRequestHeaders: 431 Request Header Fields Too Large

* Refactor data_received to fix too-many-return-statements lint
2026-03-31 03:07:56 +02:00
Benoit Chesneau
9bce72cfc3 Update changelog with missing 25.3.0 changes 2026-03-27 00:45:56 +01:00
Benoit Chesneau
2a15fdb93a Fix pylint isinstance-second-argument-not-valid-type warning 2026-03-26 23:45:38 +01:00
Benoit Chesneau
8d08aaa2cb Fix --limit-request-line 0 to mean unlimited
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
2026-03-26 23:42:14 +01:00
Benoit Chesneau
d40a374547 Fix pytest-asyncio configuration and treq_asgi hex escapes
- Add asyncio_mode = "auto" to pytest configuration for async tests
- Update treq_asgi.py badrequest class to support hex escapes
2026-03-26 17:59:40 +01:00
Benoit Chesneau
da8bd4850a Remove unused AsyncRequest class
AsyncRequest was the legacy pull-based async HTTP parser, now replaced
by the push-based CallbackRequest/PythonProtocol. Remove the unused
code and associated tests.
2026-03-26 16:08:35 +01:00
Benoit Chesneau
b00f125755 Integrate gunicorn_h1c 0.6.3 with InvalidChunkExtension support
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
2026-03-26 15:46:51 +01:00
Benoit Chesneau
bdb2ebd5a4 Reject chunk extensions with bare CR bytes (RFC 9112)
Both WSGI and ASGI parsers now validate that chunk extensions
do not contain bare CR characters, which are not allowed per
RFC 9112.

Fixes #3556
2026-03-26 15:45:48 +01:00
Benoit Chesneau
7057fc9f89 Fix http_protocols documentation to use string syntax
The http_protocols setting expects a comma-separated string, not a list.
Updated all examples in the HTTP/2 guide.

Fixes #3561
2026-03-26 15:45:11 +01:00
Benoit Chesneau
d43acb8fe0 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
2026-03-26 15:45:11 +01:00
Benoit Chesneau
cbd27e82a2
Merge pull request #3559 from benleembruggen/fix/http2-asgi-body-duplication
fix: prevent HTTP/2 ASGI body duplication in receive()
2026-03-26 14:38:53 +01:00
Benoit Chesneau
997eec4f45 Fix pylint superfluous-parens warning 2026-03-26 14:22:44 +01:00
Benoit Chesneau
3b763ddca7
Merge pull request #3560 from benoitc/fix/asgi-callback-parser-eof-handling
Fix ASGI callback parser EOF handling and header validation
2026-03-26 14:17:28 +01:00
Benoit Chesneau
389438fb5a Require gunicorn_h1c >= 0.6.0 for finish() method support
Update minimum version requirement for the fast HTTP parser to 0.6.0
which includes the finish() method for EOF handling in chunked encoding.
2026-03-26 14:12:50 +01:00
Benoit Chesneau
1f8e60c199 Add finish() method to ASGI callback parser for EOF handling
Handle chunked encoding edge case where connection closes before
final CRLF after zero-chunk. Skip WSGI-specific tests (casefold,
underscore headers) that don't apply to ASGI.
2026-03-26 12:13:50 +01:00
Benoit Chesneau
ffcebce4a7 Fix ASGI callback parser header validation
Add security checks to PythonProtocol per RFC 9110/9112:
- Reject duplicate Content-Length headers
- Reject CL + TE combinations
- Reject chunked in HTTP/1.0
- Reject stacked chunked encoding
- Validate Transfer-Encoding values
- Strict chunk size validation

Add PROXY protocol v1/v2 support to callback parser.

Add treq-based test infrastructure for ASGI parser.
2026-03-26 06:32:15 +01:00
Ben Leembruggen
8fba44cf02 fix: prevent HTTP/2 ASGI request body duplication
receive_data() stores every DATA frame in both _body_chunks (list)
and request_body (BytesIO). The receive() closure in
_handle_http2_request() has two read paths: a streaming path that
pops from _body_chunks, and a fast path that reads from BytesIO.

After the streaming path consumed the body, the fast path could
re-read the same data from BytesIO because body_received was never
set in the streaming return path. This caused the application to
receive a doubled request body (e.g. 18 bytes sent, 36 bytes
received), breaking JSON parsing with "Extra data" errors.

Fix: set body_received = True in the streaming path when
_body_complete is True, preventing the fast path from re-reading
already-consumed data.

Fixes #3558
2026-03-26 14:15:14 +11:00
Benoit Chesneau
a49a46fc19 Add sponsor reminder to issue templates 2026-03-25 14:55:51 +01:00
Benoit Chesneau
5d9f865c59 Update sponsorship: remove Open Collective, add Enki Multimedia
- Remove Open Collective links from README, sponsor page, and FUNDING.yml
- Update Revolut donation link
- Add Enki Multimedia as sponsor on homepage, sponsor page, and README
2026-03-25 14:16:12 +01:00
Benoit Chesneau
c32903f0eb Update Docker images to Python 3.14 2026-03-25 13:33:49 +01:00
Benoit Chesneau
18f5d0ea95 Fix 25.2.0 changelog: correct ASGI parser optimization description
Remove inaccurate BytesIO claims - the callback parser uses bytearray directly.
2026-03-25 09:42:07 +01:00
Benoit Chesneau
dcaf2e14a5 Add 25.2.0 to 2026 changelog 2026-03-24 23:44:09 +01:00
Benoit Chesneau
6f601a0de9 Bump version to 25.2.0 2026-03-24 23:42:01 +01:00
Benoit Chesneau
22443a8d05 Bump tornado to 6.5.5 in lock file 2026-03-24 23:21:23 +01:00
Benoit Chesneau
cb708b4698 Add uwsgi async fix to changelog 2026-03-24 23:18:25 +01:00
Benoit Chesneau
385a9211e2
Fix uwsgi incomplete header error with async workers (#3554)
The _read_exact method was calling unreader.read() without a size
parameter, which only reads one chunk at a time. With gevent/gthread
workers, this could return incomplete data before the full header
arrived.

Use unreader.read(size) which has proper retry logic built-in to
read the exact number of bytes requested.

Fixes #3552
2026-03-24 23:17:51 +01:00
Benoit Chesneau
f555180191 Add FileWrapper iterator fix to changelog 2026-03-24 23:06:06 +01:00
r266-tech
f8fca7a72f
fix: add __iter__ and __next__ to FileWrapper for PEP 3333 compliance (#3550)
* 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>
2026-03-24 22:38:16 +01:00
Benoit Chesneau
0ad47db800
Use user-writable default path for control socket (#3551)
The previous default /run/gunicorn.ctl requires root permissions.
Now uses $XDG_RUNTIME_DIR/gunicorn.ctl if available, otherwise
$HOME/.gunicorn/gunicorn.ctl. This works on Linux, FreeBSD, OpenBSD,
and macOS without requiring elevated privileges.

- Add _get_default_control_socket() helper in config.py
- Create parent directory automatically with 0o700 permissions
- Update gunicornc CLI to use the same default path
- Add unit tests for path selection and directory creation
2026-03-23 20:08:03 +01:00
Benoit Chesneau
3667a10478
Merge pull request #3549 from benoitc/feature/optional-http-parser
Optimize ASGI performance with fast parser integration
2026-03-23 14:21:20 +01:00
Benoit Chesneau
3568af1388 Skip SIGINT shutdown test on PyPy
SIGINT handling differs on PyPy and can cause flaky test failures.
The SIGTERM test covers the same graceful shutdown behavior reliably.
2026-03-23 14:10:05 +01:00
Benoit Chesneau
f164d9d23e Add /slow endpoint to benchmark app
Add endpoint with 10ms simulated I/O for latency testing.
2026-03-23 13:39:01 +01:00
Benoit Chesneau
f9ca296d21 Fix WebSocket and body receiver issues in ASGI protocol
- Fix body receiver timeout handling to prevent infinite loops
- Add WebSocket data forwarding via callbacks instead of StreamReader
- Fix HTTP/2 stream race condition where DATA frames arrive before first read
- Update WebSocketProtocol constructor (removed reader parameter)
2026-03-23 13:38:47 +01:00
Benoit Chesneau
af8897a14c Add pytest and requests to embedding_service Dockerfile
Include test dependencies in Docker image for testing.
2026-03-23 13:37:02 +01:00
Benoit Chesneau
241c479701 Fix WebSocket race condition in callback-based _read_exact()
Add double-check after clearing _data_event to prevent deadlock when
data arrives between clear() and wait(). The race condition occurred
when:
1. Task A checks buffer, needs more data
2. Task A clears _data_event
3. Task B (feed_data) sets event
4. Task A awaits on cleared event - deadlock

The fix re-checks the buffer after clear() to catch data that arrived
in the race window.

Also adds tests for edge cases: race condition simulation, EOF during
wait, fragmented message reassembly, and control frames during
fragmentation.
2026-03-23 13:08:57 +01:00
Benoit Chesneau
f76a4942c3 Remove eventlet from test dependencies 2026-03-22 17:50:32 +01:00
Benoit Chesneau
ae776cf25c Add ASGI performance documentation 2026-03-22 17:14:49 +01:00
Benoit Chesneau
4bff2f37d1 Add raw_path to mock requests in tests 2026-03-22 17:07:08 +01:00
Benoit Chesneau
ba1aaa5e33 Fix non-ASCII URL handling in ASGI worker
Percent-decode path to UTF-8 and preserve raw_path as original bytes
per ASGI spec. Fixes #3543
2026-03-22 16:35:28 +01:00
Benoit Chesneau
932331d8a4 Regenerate settings.md 2026-03-22 16:23:46 +01:00
Benoit Chesneau
4ce6aa1f3e Fix limit handling and add default max limit tests
- 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
2026-03-22 16:17:55 +01:00
Benoit Chesneau
2bf2632944 Update docs and changelog for gunicorn_h1c 0.4.1 integration 2026-03-22 13:44:31 +01:00
Benoit Chesneau
03cc85ef48 Integrate gunicorn_h1c 0.4.1 exception types and limit parameters
Require gunicorn_h1c >= 0.4.1 for fast parser mode. Add new exception
types and limit parameters to PythonProtocol for parity with C parser.
Update tests to parametrize across both parser implementations.
2026-03-22 13:43:18 +01:00
Benoit Chesneau
f308e7abfa Add parametrized tests for ASGI callback parsers
Add test suite that exercises both PythonProtocol and H1CProtocol
implementations with identical test cases using pytest parametrization.
Tests cover request line parsing, headers, body handling (Content-Length
and chunked), connection handling, parser reset, and callback behavior.
2026-03-22 09:23:47 +01:00
Benoit Chesneau
86c0baf933 Fix lint issues in ASGI parser and protocol 2026-03-22 09:00:48 +01:00
Benoit Chesneau
8ad49b8df3 Update settings.md for simplified http_parser options 2026-03-22 08:09:35 +01:00
Benoit Chesneau
e8150e27b2 Simplify ASGI parser to always use callback mode
Remove pull-based HttpParser path and always use callback-based parsing:

- Remove HttpParser, ParseResult, FastAsyncRequest classes from parser.py
- Remove BufferReader, _handle_connection_fast(), _parse_request_fast()
- Update _setup_callback_parser() to handle auto/fast/python modes
- Fix race condition when data arrives before _handle_connection starts
- Simplify http_parser config to auto/fast/python (remove callback modes)

Parser selection for ASGI:
- auto: H1CProtocol if available, else PythonProtocol
- fast: H1CProtocol required (error if unavailable)
- python: PythonProtocol only

Reduces code by ~1150 lines while maintaining performance.
2026-03-22 02:02:03 +01:00
Benoit Chesneau
87bfb7d190 Add RFC 7230 validation for chunked transfer-encoding
Validate after fast parser returns:
- Reject chunked with HTTP/1.0
- Reject chunked + Content-Length conflict
2026-03-22 00:18:25 +01:00