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

17 KiB
Raw Blame History

Changelog

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)

  • 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)

  • Chunked Encoding: Reject chunk extensions containing bare CR bytes per RFC 9112 (#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, PR #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, PR #3550)

Performance

  • ASGI HTTP Parser Optimizations: Improve ASGI worker HTTP parsing performance
    • Callback-based parsing with direct bytearray buffer operations
    • 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)

    • 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 for details
  • Dirty Stash: Add global shared state between workers via dirty.stash (PR #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)

    • 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)

    • 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)

  • Fix graceful disconnect handling for ASGI worker to properly handle client disconnects without raising exceptions (PR #3485)

  • Fix lazy import of dirty module for gevent compatibility - prevents import errors when concurrent.futures is imported before gevent monkey-patching (PR #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)

    • 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)

    • 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)

    • 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 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)

    • 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)

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)

  • 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)

  • 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)

  • Fix ASGI: quick shutdown on SIGINT/SIGQUIT, graceful on SIGTERM

Deprecations

  • Eventlet Worker: The eventlet worker is deprecated and will be removed in Gunicorn 26.0. Eventlet itself is no longer actively maintained. Please migrate to gevent, gthread, or another supported worker type.

Changes

  • Remove obsolete Makefile targets (PR #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, PR #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

    • New --proxy-protocol modes: off, v1, v2, auto
    • 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

  • Socket Backlog Metric: New gunicorn.socket.backlog gauge metric reports the current socket backlog size on Linux systems

  • InotifyReloader Enhancement: The inotify-based reloader now watches newly imported modules, not just those loaded at startup

Bug Fixes

  • Fix signal handling regression where SIGCLD alias caused errors on Linux
  • Fix socket blocking mode on keepalive connections with async workers
  • Handle SSLWantReadError in finish_body() to prevent worker hangs
  • Log SIGTERM as info level instead of warning
  • Print exception details to stderr when worker fails to boot
  • Fix unreader.unread() to prepend data to buffer instead of appending
  • Prevent RecursionError when pickling Config objects

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

    • HTTP/1.1 with keepalive connections
    • WebSocket support
    • Lifespan protocol for startup/shutdown hooks
    • Optional uvloop for improved performance
  • uWSGI Binary Protocol: Support for receiving requests from nginx via uwsgi_pass directive

  • Documentation Migration: Migrated to MkDocs with Material theme

Security

  • eventlet: Require eventlet >= 0.40.3 (CVE-2021-21419, CVE-2025-58068)
  • gevent: Require gevent >= 24.10.1 (CVE-2023-41419, CVE-2024-3219)
  • tornado: Require tornado >= 6.5.0 (CVE-2025-47287)

23.0.0 - 2024-08-10

  • minor docs fixes (PR #3217, PR #3089, PR #3167)
  • worker_class parameter accepts a class (PR #3079)
  • fix deadlock if request terminated during chunked parsing (PR #2688)
  • permit receiving Transfer-Encodings: compress, deflate, gzip (PR #3261)
  • permit Transfer-Encoding headers specifying multiple encodings. note: no parameters, still (PR #3261)
  • sdist generation now explicitly excludes sphinx build folder (PR #3257)
  • decode bytes-typed status (as can be passed by gevent) as utf-8 instead of raising TypeError (PR #2336)
  • raise correct Exception when encounting invalid chunked requests (PR #3258)
  • the SCRIPT_NAME and PATH_INFO headers, when received from allowed forwarders, are no longer restricted for containing an underscore (PR #3192)
  • include IPv6 loopback address [::1] in default for forwarded-allow-ips and proxy-allow-ips (PR #3192)

!!! note - The SCRIPT_NAME change mitigates a regression that appeared first in the 22.0.0 release - Review your forwarded-allow-ips setting if you are still not seeing the SCRIPT_NAME transmitted - Review your forwarder-headers setting if you are missing headers after upgrading from a version prior to 22.0.0

Breaking changes

  • refuse requests where the uri field is empty (PR #3255)
  • refuse requests with invalid CR/LR/NUL in heade field values (PR #3253)
  • remove temporary --tolerate-dangerous-framing switch from 22.0 (PR #3260)
  • If any of the breaking changes affect you, be aware that now refused requests can post a security problem, especially so in setups involving request pipe-lining and/or proxies.

22.0.0 - 2024-04-17

  • use utime to notify workers liveness
  • migrate setup to pyproject.toml
  • fix numerous security vulnerabilities in HTTP parser (closing some request smuggling vectors)
  • parsing additional requests is no longer attempted past unsupported request framing
  • on HTTP versions < 1.1 support for chunked transfer is refused (only used in exploits)
  • requests conflicting configured or passed SCRIPT_NAME now produce a verbose error
  • Trailer fields are no longer inspected for headers indicating secure scheme
  • support Python 3.12

Breaking changes

  • minimum version is Python 3.7
  • the limitations on valid characters in the HTTP method have been bounded to Internet Standards
  • requests specifying unsupported transfer coding (order.md) are refused by default (rare.md)
  • HTTP methods are no longer casefolded by default (IANA method registry contains none affected)
  • HTTP methods containing the number sign (#) are no longer accepted by default (rare.md)
  • HTTP versions < 1.0 or >= 2.0 are no longer accepted by default (rare, only HTTP/1.1 is supported)
  • HTTP versions consisting of multiple digits or containing a prefix/suffix are no longer accepted
  • HTTP header field names Gunicorn cannot safely map to variables are silently dropped, as in other software
  • HTTP headers with empty field name are refused by default (no legitimate use cases, used in exploits)
  • requests with both Transfer-Encoding and Content-Length are refused by default (such a message might indicate an attempt to perform request smuggling)
  • empty transfer codings are no longer permitted (reportedly seen with really old & broken proxies)

Security

  • fix CVE-2024-1135

History