gunicorn/docs/content/2026-news.md
Benoit Chesneau 5d819cf360 release: 26.0.0
- Bump version_info to (26, 0, 0)
- Update SECURITY.md supported releases (26.0.0, 25.3.0)
- Add 26.0.0 entry to news.md and 2026-news.md covering eventlet
  removal, ASGI framework compatibility suite, RFC 9110/9112
  request-target and header hardening, smuggling fixes, HEAD/204/304
  body framing, WebSocket close handshake compliance, HTTP/2 ASGI
  stream completion, early-hints validation, framework fixes
  (Django/Litestar/Quart/BlackSheep), and gunicorn_h1c >= 0.6.5
2026-05-05 08:35:19 +02:00

476 lines
19 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<span id="news-2026"></span>
# Changelog - 2026
## 26.0.0 - 2026-05-05
### Breaking Changes
- **Eventlet worker removed**: The `eventlet` worker class has been dropped.
Migrate to `gevent`, `gthread`, or `tornado`.
### New Features
- **ASGI Framework Compatibility Suite**: New end-to-end compatibility test
harness covering Starlette, FastAPI, Litestar, Quart, Sanic, and BlackSheep.
Current grid passes 438/444 tests (98%).
- **ASGI Test Suite Expansion**: 134 additional ASGI unit tests covering
protocol semantics, lifespan, websockets, and chunked framing.
### Security
- **HTTP/1.1 Request-Target Validation** (RFC 9112 sections 3.2.3, 3.2.4):
- Reject `authority-form` request-target outside `CONNECT`
- Reject `asterisk-form` request-target outside `OPTIONS`
- Reject `relative-reference` request-targets
- **Header Field Hardening** (RFC 9110):
- Reject control characters in header field-value (section 5.5)
- Reject forbidden trailer field-names (section 6.5.1)
- Reject `Content-Length` list form (RFC 9112 section 6.3)
- **Request Smuggling Hardening**:
- Tighten keepalive gate and scope `finish_body` byte cap
- Keep `_body_receiver` alive across the keepalive smuggling gate so
pipelined requests cannot re-enter a closed body
- Address parser/protocol findings from a six-point WSGI/ASGI audit
- **PROXY Protocol (ASGI)**: Enforce `proxy_allow_ips` and tighten v1/v2
parsing in the ASGI callback parser.
- **Connection Draining**: Drain the connection on close per RFC 9112
section 9.6 to prevent reset-on-close truncation.
### Bug Fixes
- **Body Framing on HEAD/204/304**:
- Keep `Content-Length` on HEAD and 304 responses
([#3621](https://github.com/benoitc/gunicorn/pull/3621))
- Drop body framing on HEAD/204/304 even when the framework set it
- Warn once when an ASGI app emits a body for a no-body response
- **HTTP/2 ASGI**:
- Fix `_handle_stream_ended` to set `_body_complete` in the async HTTP/2
handler so request bodies finalize correctly on stream end
- Add `InvalidChunkExtension` mapping and fast-parser support in ASGI
tests ([#3565](https://github.com/benoitc/gunicorn/pull/3565))
- **HTTP/1.1 100-Continue**: Stop adding `Transfer-Encoding: chunked` to
100-Continue interim responses.
- **WebSocket Close Handshake** (RFC 6455):
- Comply with the close handshake state machine
- Close the transport after the close handshake completes
- Fix binary send when the `text` key is `None`
- **Early Hints**: Validate headers in the `early_hints` callback to match
`process_headers`; pass only the header name to `InvalidHeader`
([#3588](https://github.com/benoitc/gunicorn/pull/3588)).
- **ASGI Framework Fixes**:
- Fix ASGI disconnect handling for Django-style apps
- Fix Litestar request handling (use raw ASGI receive for body/headers)
- Fix Litestar HTTP endpoints for compatibility tests
- Fix Quart headers endpoint to normalize keys to lowercase
- Fix Quart WebSocket close test app (missing `accept()`)
- Fix duplicate `Transfer-Encoding` header for BlackSheep streaming
### Refactoring
- Split `BodyReceiver._closed` into separate transport and body-wait flags
for clearer keepalive/EOF semantics.
### Changes
- **Fast HTTP Parser**: Require `gunicorn_h1c >= 0.6.5`. Drop the last
`python_only` test markers; the C extension is now used wherever
available (CPython only; PyPy continues to use the Python parser).
- **Test Dependencies**: Add `h2` and `uvloop` to the `testing` extra;
remove `eventlet`.
- **Docker Build**: Bump GitHub Actions `docker/setup-qemu-action`,
`docker/setup-buildx-action`, `docker/login-action`,
`docker/build-push-action`, and `docker/metadata-action` to current
major versions.
---
## 25.3.0 - 2026-03-26
### Bug Fixes
- **HTTP/2 ASGI Body Duplication**: Fix request body being received twice in HTTP/2
ASGI requests, causing JSON parsing errors with "Extra data" messages
([#3558](https://github.com/benoitc/gunicorn/issues/3558))
- **ASGI Chunked EOF Handling**: Add `finish()` method to callback parser to handle
chunked encoding edge case where connection closes before final CRLF after zero-chunk
- **HTTP/2 Documentation**: Fix `http_protocols` examples to use comma-separated string
instead of list syntax ([#3561](https://github.com/benoitc/gunicorn/issues/3561))
- **Chunked Encoding**: Reject chunk extensions containing bare CR bytes per RFC 9112
([#3556](https://github.com/benoitc/gunicorn/discussions/3556))
### Security
- **ASGI Parser Header Validation**: Add security checks per RFC 9110/9112:
- Reject duplicate Content-Length headers
- Reject requests with both Content-Length and Transfer-Encoding
- Reject chunked transfer encoding in HTTP/1.0
- Reject stacked chunked encoding
- Validate Transfer-Encoding values
- Strict chunk size validation
### Changes
- **Fast HTTP Parser**: Update to gunicorn_h1c >= 0.6.2 for `asgi_headers` property
which provides headers with lowercase names directly from the C parser
- **ASGI PROXY Protocol**: Add PROXY protocol v1/v2 support to callback parser
---
## 25.2.0 - 2026-03-24
### New Features
- **Fast HTTP Parser (gunicorn_h1c 0.6.0)**: Integrate new exception types and limit
parameters from gunicorn_h1c 0.6.0 for both WSGI and ASGI workers
- Requires gunicorn_h1c >= 0.6.0 for `http_parser='fast'`
- Falls back to Python parser in `auto` mode if version not met
- Proper HTTP status codes for limit errors (414, 431)
### Bug Fixes
- **uWSGI Async Workers**: Fix `InvalidUWSGIHeader: incomplete header` error
when using gevent or gthread workers with uwsgi protocol behind nginx.
([#3552](https://github.com/benoitc/gunicorn/issues/3552),
[PR #3554](https://github.com/benoitc/gunicorn/pull/3554))
- **FileWrapper Iterator Protocol**: Add `__iter__` and `__next__` methods to
`FileWrapper` for full PEP 3333 compliance. Previously only supported old-style
`__getitem__` iteration which broke code explicitly using `iter()` or `next()`.
([#3396](https://github.com/benoitc/gunicorn/issues/3396),
[PR #3550](https://github.com/benoitc/gunicorn/pull/3550))
### Performance
- **ASGI HTTP Parser Optimizations**: Improve ASGI worker HTTP parsing performance
- Read chunks in 64-byte blocks instead of 1 byte at a time for chunk size lines and trailers
- Reuse BytesIO buffers with truncate/seek instead of creating new objects (reduces GC pressure)
- Use `bytearray.find()` directly instead of converting to bytes first
- Use index-based iteration for header parsing instead of `list.pop(0)` (O(1) vs O(n))
---
## 25.1.0 - 2026-02-13
### New Features
- **Control Interface (gunicornc)**: Add interactive control interface for managing
running Gunicorn instances, similar to birdc for BIRD routing daemon
([PR #3505](https://github.com/benoitc/gunicorn/pull/3505))
- Unix socket-based communication with JSON protocol
- Interactive mode with readline support and command history
- Commands: `show all/workers/dirty/config/stats/listeners`
- Worker management: `worker add/remove/kill`, `dirty add/remove`
- Server control: `reload`, `reopen`, `shutdown`
- New settings: `--control-socket`, `--control-socket-mode`, `--no-control-socket`
- New CLI tool: `gunicornc` for connecting to control socket
- See [Control Interface Guide](guides/gunicornc.md) for details
- **Dirty Stash**: Add global shared state between workers via `dirty.stash`
([PR #3503](https://github.com/benoitc/gunicorn/pull/3503))
- In-memory key-value store accessible by all workers
- Supports get, set, delete, clear, keys, and has operations
- Useful for sharing state like feature flags, rate limits, or cached data
- **Dirty Binary Protocol**: Implement efficient binary protocol for dirty arbiter IPC
using TLV (Type-Length-Value) encoding
([PR #3500](https://github.com/benoitc/gunicorn/pull/3500))
- More efficient than JSON for binary data
- Supports all Python types: str, bytes, int, float, bool, None, list, dict
- Better performance for large payloads
- **Dirty TTIN/TTOU Signals**: Add dynamic worker scaling for dirty arbiters
([PR #3504](https://github.com/benoitc/gunicorn/pull/3504))
- Send SIGTTIN to increase dirty workers
- Send SIGTTOU to decrease dirty workers
- Respects minimum worker constraints from app configurations
### Changes
- **ASGI Worker**: Promoted from beta to stable
- **Dirty Arbiters**: Now marked as beta feature
### Documentation
- Fix Markdown formatting in /configure documentation
---
## 25.0.3 - 2026-02-07
### Bug Fixes
- Fix RuntimeError when StopIteration is raised inside ASGI response body
coroutine (PEP 479 compliance)
- Fix deprecation warning for passing maxsplit as positional argument in
`re.split()` (Python 3.13+)
---
## 25.0.2 - 2026-02-06
### Bug Fixes
- Fix ASGI concurrent request failures through nginx proxy by normalizing
sockaddr tuples to handle both 2-tuple (IPv4) and 4-tuple (IPv6) formats
([PR #3485](https://github.com/benoitc/gunicorn/pull/3485))
- Fix graceful disconnect handling for ASGI worker to properly handle
client disconnects without raising exceptions
([PR #3485](https://github.com/benoitc/gunicorn/pull/3485))
- Fix lazy import of dirty module for gevent compatibility - prevents
import errors when concurrent.futures is imported before gevent monkey-patching
([PR #3483](https://github.com/benoitc/gunicorn/pull/3483))
### Changes
- Refactor: Extract `_normalize_sockaddr` utility function for consistent
socket address handling across workers
- Add license headers to all Python source files
- Update copyright year to 2026 in LICENSE and NOTICE files
---
## 25.0.1 - 2026-02-02
### Bug Fixes
- Fix ASGI streaming responses (SSE) hanging: add chunked transfer encoding for
HTTP/1.1 responses without Content-Length header. Without chunked encoding,
clients wait for connection close to determine end-of-response.
### Changes
- Update celery_alternative example to use FastAPI with native ASGI worker and
uvloop for async task execution
### Testing
- Add ASGI compliance test suite with Docker-based integration tests covering HTTP,
WebSocket, streaming, lifespan, framework integration (Starlette, FastAPI),
HTTP/2, and concurrency scenarios
---
## 25.0.0 - 2026-02-01
### New Features
- **Dirty Arbiters**: Separate process pool for executing long-running, blocking
operations (AI model loading, heavy computation) without blocking HTTP workers
([PR #3460](https://github.com/benoitc/gunicorn/pull/3460))
- Inspired by Erlang's dirty schedulers
- Asyncio-based with Unix socket IPC
- Stateful workers that persist loaded resources
- New settings: `--dirty-app`, `--dirty-workers`, `--dirty-timeout`,
`--dirty-threads`, `--dirty-graceful-timeout`
- Lifecycle hooks: `on_dirty_starting`, `dirty_post_fork`,
`dirty_worker_init`, `dirty_worker_exit`
- **Per-App Worker Allocation for Dirty Arbiters**: Control how many dirty workers
load each app for memory optimization with heavy models
([PR #3473](https://github.com/benoitc/gunicorn/pull/3473))
- Set `workers` class attribute on DirtyApp (e.g., `workers = 2`)
- Or use config format `module:class:N` (e.g., `myapp:HeavyModel:2`)
- Requests automatically routed to workers with the target app
- New exception `DirtyNoWorkersAvailableError` for graceful error handling
- Example: 8 workers × 10GB model = 80GB → with `workers=2`: 20GB (75% savings)
- **HTTP/2 Support (Beta)**: Native HTTP/2 (RFC 7540) support for improved performance
with modern clients ([PR #3468](https://github.com/benoitc/gunicorn/pull/3468))
- Multiplexed streams over a single connection
- Header compression (HPACK)
- Flow control and stream prioritization
- Works with gthread, gevent, and ASGI workers
- New settings: `--http-protocols`, `--http2-max-concurrent-streams`,
`--http2-initial-window-size`, `--http2-max-frame-size`, `--http2-max-header-list-size`
- Requires SSL/TLS and h2 library: `pip install gunicorn[http2]`
- See [HTTP/2 Guide](guides/http2.md) for details
- New example: `examples/http2_gevent/` with Docker and tests
- **HTTP 103 Early Hints**: Support for RFC 8297 Early Hints to enable browsers to
preload resources before the final response
([PR #3468](https://github.com/benoitc/gunicorn/pull/3468))
- WSGI: `environ['wsgi.early_hints'](headers)` callback
- ASGI: `http.response.informational` message type
- Works with both HTTP/1.1 and HTTP/2
- **uWSGI Protocol for ASGI Worker**: The ASGI worker now supports receiving requests
via the uWSGI binary protocol from nginx
([PR #3467](https://github.com/benoitc/gunicorn/pull/3467))
### Bug Fixes
- Fix HTTP/2 ALPN negotiation for gevent and eventlet workers when
`do_handshake_on_connect` is False (the default). The TLS handshake is now
explicitly performed before checking `selected_alpn_protocol()`.
- Fix setproctitle initialization with systemd socket activation
([#3465](https://github.com/benoitc/gunicorn/issues/3465))
- Fix `Expect: 100-continue` handling: ignore the header for HTTP/1.0 requests
since 100-continue is only valid for HTTP/1.1+
([PR #3463](https://github.com/benoitc/gunicorn/pull/3463))
- Fix missing `_expected_100_continue` attribute in UWSGIRequest
- Disable setproctitle on macOS to prevent segfaults during process title updates
- Publish full exception traceback when the application fails to load
([#3462](https://github.com/benoitc/gunicorn/issues/3462))
### Deprecations
- **Eventlet Worker**: The `eventlet` worker is deprecated and will be removed in
Gunicorn 26.0. Eventlet itself is [no longer actively maintained](https://eventlet.readthedocs.io/en/latest/asyncio/migration.html).
Please migrate to `gevent`, `gthread`, or another supported worker type.
### Changes
- Remove obsolete Makefile targets
([PR #3471](https://github.com/benoitc/gunicorn/pull/3471))
---
## 24.1.1 - 2026-01-24
### Bug Fixes
- Fix `forwarded_allow_ips` and `proxy_allow_ips` to remain as strings for backward
compatibility with external tools like uvicorn. Network validation now uses strict
mode to detect invalid CIDR notation (e.g., `192.168.1.1/24` where host bits are set)
([#3458](https://github.com/benoitc/gunicorn/issues/3458),
[PR #3459](https://github.com/benoitc/gunicorn/pull/3459))
---
## 24.1.0 - 2026-01-23
### New Features
- **Official Docker Image**: Gunicorn now publishes official Docker images to GitHub
Container Registry at `ghcr.io/benoitc/gunicorn`
- Based on Python 3.12 slim image
- Uses recommended worker formula (2 × CPU + 1)
- Configurable via environment variables
- **PROXY Protocol v2 Support**: Extended PROXY protocol implementation to support
the binary v2 format in addition to the existing text-based v1 format
([PR #3451](https://github.com/benoitc/gunicorn/pull/3451))
- New `--proxy-protocol` modes: `off`, `v1`, `v2`, `auto`
- `auto` mode (default when enabled) detects v1 or v2 automatically
- v2 binary format is more efficient and supports additional metadata
- Works with HAProxy, AWS NLB/ALB, and other PROXY protocol v2 sources
- **CIDR Network Support**: `--forwarded-allow-ips` and `--proxy-allow-from` now
accept CIDR notation (e.g., `192.168.0.0/16`) for specifying trusted networks
([PR #3449](https://github.com/benoitc/gunicorn/pull/3449))
- **Socket Backlog Metric**: New `gunicorn.socket.backlog` gauge metric reports
the current socket backlog size on Linux systems
([PR #3450](https://github.com/benoitc/gunicorn/pull/3450))
- **InotifyReloader Enhancement**: The inotify-based reloader now watches newly
imported modules, not just those loaded at startup
([PR #3447](https://github.com/benoitc/gunicorn/pull/3447))
### Bug Fixes
- Fix signal handling regression where SIGCLD alias caused "Unhandled signal: cld"
errors on Linux when workers fail during boot
([#3453](https://github.com/benoitc/gunicorn/discussions/3453))
- Fix socket blocking mode on keepalive connections preventing SSL handshake
failures with async workers
([PR #3452](https://github.com/benoitc/gunicorn/pull/3452))
- Use smaller buffer size in `finish_body()` for faster timeout detection on
slow or abandoned connections
([PR #3453](https://github.com/benoitc/gunicorn/pull/3453))
- Handle `SSLWantReadError` in `finish_body()` to prevent worker hangs during
SSL renegotiation
([PR #3448](https://github.com/benoitc/gunicorn/pull/3448))
- Log SIGTERM as info level instead of warning to reduce noise in orchestrated
environments
([PR #3446](https://github.com/benoitc/gunicorn/pull/3446))
- Print exception details to stderr when worker fails to boot
([PR #3443](https://github.com/benoitc/gunicorn/pull/3443))
- Fix `unreader.unread()` to prepend data to buffer instead of appending
([PR #3442](https://github.com/benoitc/gunicorn/pull/3442))
- Prevent `RecursionError` when pickling Config objects
([PR #3441](https://github.com/benoitc/gunicorn/pull/3441))
- Use proper exception chaining with `raise from` in glogging.py
([PR #3440](https://github.com/benoitc/gunicorn/pull/3440))
---
## 24.0.0 - 2026-01-23
### New Features
- **ASGI Worker (Beta)**: Native asyncio-based ASGI support for running async Python
frameworks like FastAPI, Starlette, and Quart without external dependencies
([PR #3444](https://github.com/benoitc/gunicorn/pull/3444))
- HTTP/1.1 with keepalive connections
- WebSocket support
- Lifespan protocol for startup/shutdown hooks
- Optional uvloop for improved performance
- New settings: `--asgi-loop`, `--asgi-lifespan`, `--root-path`
- **uWSGI Binary Protocol**: Support for receiving requests from nginx via
`uwsgi_pass` directive, enabling efficient binary protocol communication
([PR #3444](https://github.com/benoitc/gunicorn/pull/3444))
- New settings: `--protocol uwsgi`, `--uwsgi-allow-from`
- **Documentation Migration**: Migrated documentation from Sphinx to MkDocs
with Material theme for improved navigation and mobile experience
([PR #3426](https://github.com/benoitc/gunicorn/pull/3426))
### Security
- **eventlet**: Require eventlet >= 0.40.3 to address CVE-2021-21419 (websocket
memory exhaustion) and CVE-2025-58068 (HTTP request smuggling)
([PR #3445](https://github.com/benoitc/gunicorn/pull/3445))
- **gevent**: Require gevent >= 24.10.1 to address CVE-2023-41419 (HTTP request
smuggling) and CVE-2024-3219 (socket.socketpair vulnerability)
([PR #3445](https://github.com/benoitc/gunicorn/pull/3445))
- **tornado**: Require tornado >= 6.5.0 to address CVE-2025-47287 (HTTP request
smuggling) and other security fixes
([PR #3445](https://github.com/benoitc/gunicorn/pull/3445))
### Changes
- Documentation now hosted at https://gunicorn.org
- Updated license configuration to PEP 639 format for uv compatibility
!!! warning "ASGI Worker Beta"
The ASGI worker is a beta feature. While tested, the API and behavior
may change in future releases. Please report any issues on GitHub.