- 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
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.
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.
- Add _body_chunks, _body_event, _body_complete fields for streaming
- Modify receive_data() to populate chunks queue alongside BytesIO
- Add async read_body_chunk() method for streaming body reads
This enables HTTP/2 request body streaming instead of buffering
entire uploads, reducing memory usage for large file uploads.
- Add FlowControl class for transport-level write backpressure
- Integrate flow control into HTTP/1.1 protocol to prevent memory
issues with large streaming responses
- Set write buffer high water mark to 64KB
- Add pause_writing/resume_writing protocol callbacks
- Stream HTTP/2 responses immediately instead of buffering
- Add _convert_h2_headers helper for cleaner header conversion
Add PythonProtocol class that mirrors H1CProtocol callback interface:
- Callbacks: on_message_begin, on_url, on_header, on_headers_complete,
on_body, on_message_complete
- Properties: method, path, http_version, headers, content_length,
is_chunked, should_keep_alive
- Methods: feed(data), reset()
- Supports Content-Length and chunked transfer encoding
Add CallbackRequest adapter for building requests from parser state.
Works with both H1CProtocol (C extension) and PythonProtocol.
Add unit tests for PythonProtocol and CallbackRequest.
- Replace datetime.now() with time.monotonic() for request timing
- Add access_log_enabled property to skip log work when disabled
- Rewrite BodyReceiver with Future-based waiting (no create_task)
- Remove StreamReader for HTTP/1.1, use direct bytearray buffering
- Add BufferReader wrapper for FastAsyncRequest compatibility
- Use pre-cached chunk prefixes in _send_body()
- Convert async methods to sync where no await needed
- Batch response writes (headers + body in single write)
Performance: 4,200 -> 69,500 req/s
Wire HttpParser to ASGI hot path, replacing AsyncRequest.parse() with
direct buffer-based parsing. Add FastAsyncRequest wrapper for body
reading. Replace per-request Queue/Task with BodyReceiver for on-demand
body reading. Keep headers as bytes end-to-end to avoid conversion
overhead. Add backpressure control and keepalive timer. Cache response
status lines and Date header.
Benchmark shows 3x improvement: ~875K req/s for simple GET (was ~340K).
Benchmarks WSGI and ASGI parsers with:
- Simple GET request (35 bytes)
- Medium POST request (192 bytes, 7 headers)
- Complex POST request (891 bytes, 18 headers)
Results show fast parser (gunicorn_h1c) is:
- WSGI: ~1.9x faster than Python parser
- ASGI: ~2.7x faster than Python parser
- Integrate gunicorn_h1c fast parser into WSGI Request class
- Add _check_fast_parser() and _parse_fast() methods
- Tests use Python parser for consistent validation behavior
- Update config description to reflect all worker types
- Use os.register_at_fork() to properly handle fork() with asyncio
- Start control server after initial workers spawn, not before
- Change default socket path to /run/gunicorn.ctl (like BIRD)
- Add integration tests for sync, gthread, and gevent workers
Fixes#3509
Fix two issues with the control socket feature introduced in 25.1.0:
1. Fork deadlock (#3509): The control server runs asyncio in a background
thread. When fork() is called to spawn workers, locks held by asyncio
can remain locked in the child process, causing deadlocks. Fix by
stopping the control server before fork and restarting after fork in
the parent.
2. File watcher triggers (#3508): The default socket path `gunicorn.ctl`
was created in the app directory, triggering file watchers in dev
servers (e.g., GAE dev_appserver). Fix by changing the default path
to `/tmp/gunicorn.ctl`.
Fixes#3508Fixes#3509
Add a timeout when waiting for initial request data in worker threads.
If no data arrives within 5 seconds, the connection is deferred back
to the main poller instead of blocking the thread indefinitely.
This fixes a regression from v24 where connections were submitted
directly to the thread pool after accept(). In v23, connections were
registered with the poller first and only submitted when data arrived.
The new hybrid approach maintains the performance benefits for fast
clients (immediate processing) while protecting against slow-client
scenarios that can exhaust the thread pool and cause health check
timeouts.
Changes:
- Add _DEFER sentinel and DEFAULT_WORKER_DATA_TIMEOUT constant
- Add TConn.wait_for_data() method using selectors
- Add TConn.data_ready flag to track data availability
- Add pending_conns deque for deferred connections
- Add on_pending_socket_readable() callback
- Add murder_pending() to clean up timed-out pending connections
- Modify handle() to wait for data with timeout before processing
- Modify finish_request() to handle _DEFER and register with poller
Fixes#3518
- Read chunk size lines and trailers in 64-byte blocks instead of 1 byte
at a time, pushing back excess data to the unreader buffer
- Reuse BytesIO buffers with truncate/seek instead of creating new
objects to reduce GC pressure in AsyncUnreader
- Use bytearray.find() directly instead of converting to bytes first
in header parsing loop
- Use index-based iteration for header parsing instead of list.pop(0)
which is O(n) per pop vs O(1) for index access
Add tests for the optimized parsing code paths.
- Use preexec_fn=os.setsid to create new process group
- Send signals to process group with os.killpg() instead of single process
- Add explicit timeout and graceful-timeout to gunicorn command
- Fixes test failures on PyPy 3.10 where signals weren't propagating properly
- Add guides/gunicornc.md with usage examples and command reference
- Update mkdocs.yml navigation to include Control Interface guide
- Update 2026-news.md and news.md changelog with 25.2.0 release
- Regenerate reference/settings.md with control socket settings