Skip to content

chore(deps): update dependency gunicorn to v27 - #14781

Open
renovate-bot wants to merge 1 commit into
GoogleCloudPlatform:mainfrom
renovate-bot:renovate/gunicorn-27.x
Open

renovate-bot wants to merge 1 commit into
GoogleCloudPlatform:mainfrom
renovate-bot:renovate/gunicorn-27.x

Conversation

@renovate-bot

Copy link
Copy Markdown
Contributor

This PR contains the following updates:

Package Change Age Confidence
gunicorn (changelog) ==23.0.0 → ==27.0.0 age confidence
gunicorn (changelog) ==25.1.0 → ==27.0.0 age confidence
gunicorn (changelog) ==26.2.0 → ==27.0.0 age confidence
gunicorn (changelog) ==22.0.0 → ==27.0.0 age confidence

Release Notes

benoitc/gunicorn (gunicorn)

v27.0.0: gunicorn 27.0.0

Compare Source

Breaking Changes
  • Reject with 400 a request whose Transfer-Encoding does not end with
    chunked, such as gzip or identity alone, with or without
    Content-Length (RFC 9112 §6.3). The WSGI workers used to read such a body
    until the connection closed and the ASGI worker treated it as empty. Clients
    that send Transfer-Encoding: identity must stop sending it.
Bug Fixes
  • Reject whitespace between a header name and the colon (RFC 9112 §5.1).
  • Trim only spaces and tabs around Transfer-Encoding values (RFC 9110 §5.6.3)
    (#​3364).
  • Reject a Content-Length that is not plain digits (RFC 9110 §8.6).
  • Reject chunk data not followed by CRLF (RFC 9112 §7.1).
  • Reject control characters in the request path (RFC 9112 §3.2)
    (#​3371).
  • Return 414 URI Too Long for a request line over limit_request_line
    (RFC 9112 §3)
    (#​2487).
  • ASGI: never write more body bytes than the app's Content-Length, and close
    the connection when the body is shorter (RFC 9112 §6.3).
  • ASGI: close the connection when the app returns before finishing its
    response (RFC 9112 §6.3).
  • ASGI: reject response header names and values that are not valid
    (RFC 9110 §5.1, §5.5).
  • WSGI: reject a response status that is not three digits followed by a
    valid reason phrase (RFC 9112 §4).
  • ASGI: read the body a HEAD request declares instead of serving it as the
    next request (RFC 9112 §6.3).
  • ASGI: stop buffering a request line, header, chunk-size line or trailer
    that never ends; the usual request limits now apply before the line
    terminator arrives, and trailers count against limit_request_fields.
  • ASGI: apply forwarded_allow_ips to HTTP/1 requests as WSGI and HTTP/2
    already do: secure_scheme_headers set the scheme only from trusted
    proxies, and header names with underscores follow header_map.
  • Ignore one empty line before the request line, as the fast parser
    already does (RFC 9112 §2.2).
  • ASGI: stop gunicorn when the app reports a failed lifespan startup,
    instead of serving requests or rebooting the worker forever (ASGI
    lifespan spec) (#​3778).
  • ASGI: close idle kept-alive connections after keepalive seconds, as
    the other workers do (#​3734).
  • ASGI: give each request a shallow copy of the lifespan state, so a value
    one request stores in scope["state"] is not seen by the next one (ASGI
    lifespan spec).
  • ASGI: deliver websocket.disconnect as soon as the client closes the
    connection, instead of after the close timeout (RFC 6455 §5.5.1).
  • ASGI: close a WebSocket with 1009 when a message is larger than the new
    websocket_max_message_size setting (default 16 MiB), and with 1002 for a
    control frame over 125 bytes or fragmented (RFC 6455 §5.5, §7.4.1).
  • ASGI: stop reading from a WebSocket while unread data or messages pile up,
    until the app catches up.
  • gthread: serve a request that arrived while the previous request's unread
    body was drained, instead of leaving it until the keepalive timeout
    (#​3770).
  • gthread: idle keep-alive connections no longer hold up a graceful
    shutdown, and the worker keeps reporting to the arbiter while it waits, so
    it is not killed during max_requests restarts.
  • Close the connection when reading a request body fails, instead of keeping
    it open with the bytes that followed the body lost.
  • Don't call listen() again on a socket that already listens, such as one
    passed by systemd socket activation; SELinux can deny that call.
  • With reuse_port on a platform without SO_REUSEPORT, workers now use the
    master's sockets instead of failing to bind.
  • StatsD: send metrics over IPv6 when statsd_host is a numeric IPv6
    address; they used to be dropped silently.
  • Read request bodies of known length in large steps instead of 8 KiB at a
    time. Large uploads are much faster, most of all while other threads keep
    the worker busy (#​2596).
  • Read chunked request bodies in large steps too, up to the size each chunk
    announces (#​2596).
  • inotify reloader: reload when the target of a symlinked file changes
    (#​3243).
  • Docs: fix the worker types link in the worker_class setting.
  • Docs: explain that timeout watches each worker's heartbeat, which idle
    workers keep updating.
  • Close the connection after a CONNECT request, whatever the application
    answers; what follows is never served as another request (RFC 9931 §8).
  • WSGI: answer 500 instead of 400 when the application sends an invalid
    response header, and leave the header value out of the error.
  • Join repeated request headers in linear time when building the WSGI
    environ; many copies of one header used to take time growing with the
    square of their number.
  • Reject a request path with raw non-ASCII bytes; they must be
    percent-encoded (RFC 9112 §3.2, RFC 3986 §2.1).
  • Dirty arbiter: create its PID file in the temporary directory with mode
    0600, and only use it to stop an orphaned arbiter when this user created
    it and the process is ours. A file left there by someone else is ignored.
  • ASGI: stop reading from the socket while more than 256 KiB of request body
    or pipelined data waits for the application, and resume as it reads; uWSGI
    request bodies are read as the application asks for them.
  • HTTP/2: close the connection with ENHANCE_YOUR_CALM when a peer sends more
    than 1000 PING, SETTINGS or empty DATA frames in 10 seconds, as already done
    for RST_STREAM floods.
  • HTTP/2, sync workers: close the connection with ENHANCE_YOUR_CALM when more
    than 10000 frames pile up while a response waits for window credit.
  • WebSocket: fail the connection on frames RFC 6455 forbids: a new message
    inside a fragmented one, reserved opcodes, lengths not minimally encoded,
    malformed close frames (1002), and text or close reasons that are not UTF-8
    (1007) (RFC 6455 §5.2, §5.4, §5.5.1, §7.4, §8.1). An empty client close is
    answered with 1000 instead of the reserved 1005.
  • WebSocket: refuse a handshake without Sec-WebSocket-Version: 13 with 426
    (and the supported version), and one whose key is not 16 bytes of base64
    with 400 (RFC 6455 §4.2.1, §4.2.2).
  • WebSocket: refuse a handshake the application closes or never accepts with
    an HTTP 403 (500 if it raised), as ASGI requires, instead of writing a
    WebSocket close frame on a connection that never switched protocols.
  • WebSocket: wait while the client is not reading before sending a message,
    as HTTP responses already do, so a slow client cannot grow the worker's
    write buffer.
  • WebSocket: send() after the connection closed raises an OSError
    subclass, as ASGI 2.4 asks; it is still a RuntimeError, so existing
    handlers keep working, and gunicorn no longer logs it as an error. HTTP
    sends after a disconnect stay silently ignored.
  • Dirty apps: bound every stream read by the idle timeout and the total
    timeout, and drop the connection when one fires, so a late chunk is never
    read by the next call. A sync stream's idle timeout is no longer replaced
    by a fixed 5 seconds.
  • ASGI: answer Expect: 100-continue with 100 Continue when the app first
    reads the body, so clients such as curl no longer wait about a second before
    sending an upload (RFC 9110 §10.1.1).
  • ASGI: answer a request the fast parser rejects with 400 and close the
    connection; it used to be dropped without a response, with a traceback
    logged for each one (RFC 9112 §3, §5.1).
  • ASGI: put the standard reason phrase in error responses, such as 400 Bad Request, and the error text in the body; the text, which can quote the
    request, used to be written into the status line (RFC 9112 §4).
  • ASGI: answer 501 to a transfer coding gunicorn does not support, such as
    foo, chunked, as the WSGI workers do (RFC 9112 §6.1).
  • ASGI: accept whitespace between a chunk size and its extension, such as 1 ;a, as the WSGI workers do (RFC 9112 §7.1.1).
  • ASGI: copy a bytearray or memoryview response body when sending it; an
    app that reused the buffer after send returned could have the changed
    bytes sent.
  • Reject a line ended by a bare LF at once with 400; with the Python parsers,
    a head or trailer section ended that way kept the connection waiting until a
    limit or the timeout (RFC 9112 §2.2).
  • Reject a chunk extension that is not a name with an optional token or
    quoted-string value, such as one with a control character or an unclosed
    quote (RFC 9112 §7.1.1).
  • WSGI: with the fast parser, answer 414 to a request line longer than
    limit_request_line that follows an empty line, as the Python parser does
    (RFC 9112 §2.2, §3).
Changes
  • Require gunicorn_h1c 0.6.14 for the fast extra. It parses requests faster,
    reads a chunked body in time proportional to its size, and accepts and
    rejects the same requests as gunicorn's Python parsers, whatever way their
    bytes are split across reads.
  • ASGI: do less work per request: settings and forwarded-header trust are read
    once per connection, a request is built in one pass over its headers, their
    text copy is only made when something reads it, and response bodies of 64
    KiB or more are sent without being copied. With one worker, small requests
    are about a third faster and streamed responses over 50% faster, and large
    responses use much less memory under load.

Full changelog: https://github.com/benoitc/gunicorn/blob/master/docs/content/news.md

v26.2.0: gunicorn 26.2.0

Compare Source

Cleartext HTTP/2 lands, and an HTTP/2 security fix.

Cleartext HTTP/2 (h2c)

http2_cleartext accepts prior-knowledge, upgrade, both or off (the
default). Prior knowledge serves a connection that opens with the HTTP/2
preface; upgrade honours an HTTP/1.1 Upgrade: h2c request. Both work on the
gthread, gevent and asgi workers.

This is for deployments where TLS is terminated by a proxy that speaks HTTP/2
upstream, so the hop into gunicorn no longer drops to HTTP/1.1. Only peers in
forwarded_allow_ips are considered; everyone else is served HTTP/1.x exactly
as if the setting were off. Each mechanism is enabled separately, so turning one
on does not turn the other on.

Do not expose a cleartext HTTP/2 port to the internet.

Security

HTTP2Request built its headers straight from the stream, so nothing the HTTP/1
path enforces applied over HTTP/2: the underscore and header_map policy,
duplicate Host and Content-Type, control characters in values, and the
forwarded_allow_ips trust gate. An untrusted client could set SCRIPT_NAME
and forge HTTP_* entries in the WSGI environ, and decide wsgi.url_scheme
through :scheme. Both request classes now share one policy mixin, and the
scheme comes from the transport.

If you serve HTTP/2, this is the reason to upgrade.

Other HTTP/2 fixes

WSGI responses were buffered whole before anything was sent; they stream now.
HEAD, 204 and 304 no longer carry a body. Events read while blocked on a
flow-control window were discarded, losing requests and body data outright.
sendfile() is refused on HTTP/2 responses rather than bypassing framing.

Request bodies dropped on Upgrade requests

On the ASGI worker with the fast parser, any request carrying an Upgrade
header reached the application with an empty body, whatever the header's value
and with HTTP/2 switched off entirely. Fixed in gunicorn_h1c 0.6.9, which the
fast extra now requires.

Full changelog: https://gunicorn.org/news/

v26.1.0: gunicorn 26.1.0

Compare Source

New Features
  • Glob patterns in reload_extra_files: entries containing *, ? or [
    are treated as patterns, so ui/*/config.json watches every view's config
    without listing them one by one. Patterns are re-expanded on every reload
    check rather than once at startup, so a file created later starts being
    watched without restarting gunicorn, and ** recurses. A pattern matching
    nothing warns instead of failing, since with live expansion it may match later
    (#​1643,
    #​3662).
Security
  • Dependency floors raised past known advisories: every declared floor was
    checked against the advisory database. tornado, h2, setuptools and
    pymdown-extensions permitted vulnerable versions and now require the first
    clean release; pytest and httpx were unpinned and now carry floors. The
    tornado example pinned tornado<6, which was both the source of several
    advisories and older than the >=6.5.0 the tornado worker needs, so the
    example could not run as pinned.
Bug Fixes
  • SIGHUP did not reload the logger configuration: Arbiter.reload()
    re-read the configuration file but kept using the logger built at startup,
    calling only reopen_files() on its existing handlers. Changes to
    logconfig, logconfig_dict, logconfig_json and loglevel were ignored
    until a full restart, which in containers meant replacing the pod. The
    existing logger now re-runs its setup on reload, so new handlers, formats
    and levels take effect while the process identity and its listeners are
    preserved, and re-running the setup no longer stacks duplicate syslog
    handlers. An invalid log configuration on reload is not fatal either: the
    error is reported on stderr, the previous working configuration is restored
    and the master keeps running with it
    (#​3353).

  • Truncated chunked bodies accepted: RFC 9112 section 7.1.2 ends a chunked
    body with 0 CRLF CRLF, the second CRLF being the mandatory empty trailer
    section. ChunkedReader.parse_chunk_size() swallowed the NoMoreData raised
    while scanning for it, so a body cut short right after the last chunk line was
    treated as complete instead of rejected. It now raises
    ChunkMissingTerminator
    (#​3382,
    #​3685).

  • --spew crashed on dynamically generated code: the trace hook indexed the
    2-tuple returned by inspect.getsourcelines() by line number rather than
    indexing the list of lines, so a frame with no __file__ raised
    AttributeError: 'int' object has no attribute 'rstrip' on line 1 and
    IndexError beyond it. The tuple is now unpacked and offset by the source's
    starting line (#​3344,
    #​3495).

  • Duplicate Host and Content-Type headers accepted: RFC 9110 section 5.3
    allows only one of each, and a repeat cannot be merged into a list, so the
    message means different things to gunicorn and to anything downstream. Both
    are now rejected with InvalidHeader. The check lives in the policy hook
    shared by both parsers, so the pure-Python and fast parsers agree. Duplicate
    Content-Length was already rejected and is unchanged
    (#​3366,
    #​3548).

  • Non-worker children reported as failed workers: reap_workers() reaps
    every child through waitpid(-1), including processes the kernel reparented
    onto gunicorn when it runs as PID 1 in a container, but it logged the exit
    status before checking whether the pid was ever a worker. An unrelated process
    produced Worker (pid:N) exited with code M and triggered alerts. More
    seriously, such a process exiting with code 3 or 4 raised HaltServer and shut
    the server down. Ownership is now established first: the dirty arbiter is
    reported as itself, unknown children are reaped silently at debug level, and
    only real workers can halt the server
    (#​3220,
    #​3566).

  • Dirty arbiter exits were invisible on SIGCHLD: handle_chld() called
    reap_workers() first, whose waitpid(-1) claimed the dirty arbiter before
    reap_dirty_arbiter() could identify it, so the latter always hit ECHILD and
    its reporting never ran. The dirty arbiter is now reaped first, and
    reap_workers() recognises it if it exits mid-loop.

  • Dirty arbiter returned stale responses after a worker timeout: when a
    request reached dirty_timeout the arbiter answered the client with a timeout
    error but kept the worker connection open. The worker's late response was then
    the first message waiting on that socket, so the next request routed to the
    same worker received the previous request's result, and every request after it
    stayed one response behind. The connection is now closed on timeout, so the
    late answer is discarded with it
    (#​3626).

  • ASGI connection count leaked on server-initiated close: nr_conns was
    only decremented in connection_lost(), behind a guard keyed on the same
    flag _close_transport() sets first. Every close the server started (a
    Connection: close response, a keepalive timeout, an error abort) leaked one
    count, so ASGIWorker._shutdown() ran the full graceful_timeout and warned
    about connections that were already gone. The guard now uses its own flag, so
    the decrement and the rest of the cleanup run exactly once whichever side
    closes first (#​3661).

  • Inotify reloader on cwd-relative extra files: reload_extra_files entries
    with no directory part (for example .env) produced an empty dirname, and
    watching it raised InotifyError with ENOENT. The current directory is now
    watched as . (#​3377,
    #​3667).

  • StatsD zero-valued metrics: gauges, counters, histograms and timers
    reporting 0 were silently dropped because the value was tested for
    truthiness. Only None is skipped now
    (#​3676).

  • Spurious no-body warning from sendfile(): a HEAD, 204 or 304 response
    served through sendfile() warned about dropped body bytes even when the
    file was empty and nothing was dropped. It now warns only when there are
    bytes to drop, matching write()
    (#​3684).

  • Bare except in the gevent websocket example: narrowed to
    except Exception (#​3683).

  • ASGI receive() cancellation: Let asyncio.CancelledError propagate
    from BodyReceiver instead of swallowing it and returning
    http.disconnect. Frameworks that cancel their disconnect listener after
    the response completes (Django) no longer see the cancel masked, so
    request_finished fires and close_old_connections() runs. Fixes idle
    database connections leaking since 25.1.0
    (#​3627,
    #​3654).

  • Control socket leak on SIGHUP reload: The control thread is now marked
    ready once its loop and server are live, and the stop paths wait on that
    readiness before scheduling shutdown. Reloads no longer leak one thread and
    its selector fd plus unix socket per worker, which eventually raised
    "too many open files"
    (#​3648).

  • WSGI body framing on HEAD/1xx/204/304: Mirror the ASGI strip-and-warn
    behavior on the WSGI path. Content-Length is stripped on 1xx/204 per
    RFC 9110 section 6.4.2, body bytes are dropped for no-body responses in
    both write() and sendfile(), and a single warning is logged per request
    (#​3413).

Refactoring
  • Pass log arguments to the logger instead of pre-formatting the worker
    termination message in Arbiter.reap_workers()
    (#​3678).
Changes
  • packaging is no longer a runtime dependency: it was only ever imported by
    the gevent worker, to compare gevent's version. It moved to the gevent and
    testing extras, so a plain pip install gunicorn pulls in nothing
    (#​3643).

  • Fast HTTP Parser: Require gunicorn_h1c >= 0.6.6, which rejects duplicate
    Host and Content-Type headers in the C parser itself. Gunicorn already
    refuses them on both the WSGI and ASGI paths, so this changes nothing that is
    reachable; it moves the rejection to where the bytes are read and lets the
    ASGI corpus exercise those cases against the fast parser directly.

Full changelog: https://gunicorn.org/2026-news/

v26.0.0

Compare Source

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

Full changelog: benoitc/gunicorn@25.3.0...26.0.0

v25.3.0: Gunicorn 25.3.0

Compare Source

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)

  • Request Line Limit: Fix --limit-request-line 0 to mean unlimited as documented,
    instead of using default maximum. Works with both Python and fast C parser.
    (#​3563)

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.3 for asgi_headers property
    and InvalidChunkExtension validation for bare CR rejection

  • ASGI PROXY Protocol: Add PROXY protocol v1/v2 support to callback parser

  • Docker Images: Update to Python 3.14

v25.2.0: Gunicorn 25.2.0

Compare Source

New Features
  • Fast HTTP Parser (gunicorn_h1c 0.4.1): Integrate new exception types and limit parameters from gunicorn_h1c 0.4.1 for both WSGI and ASGI workers
    • Requires gunicorn_h1c >= 0.4.1 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. (#​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))

v25.1.0: Gunicorn 25.1.0

Compare Source

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

v25.0.3

Compare Source

What's Changed

Bug Fixes
  • Fix RuntimeError when StopIteration raised in ASGI coroutine (#​3484)
  • Fix passing maxsplit in re.split() as positional argument (deprecated in Python 3.13)
Documentation
  • Updated sponsorship section and homepage

Full Changelog: benoitc/gunicorn@25.0.2...25.0.3

v25.0.2

Compare Source

What's Changed

Bug Fixes
  • Fix ASGI concurrent request failures through nginx proxy
  • Graceful disconnect handling for ASGI worker
  • Lazy import dirty module for gevent compatibility
Other
  • Increase CI timeout for signal tests on PyPy
  • Remove trailing blank line in instrument/init.py

Full Changelog: benoitc/gunicorn@25.0.1...25.0.2

v25.0.1

Compare Source

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

v25.0.0: Gunicorn 25.0.0

Compare Source

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]
    • 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)
  • Replace RST with markdown documentation format

v24.1.1

Compare Source

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)

Full Changelog: benoitc/gunicorn@24.1.0...24.1.1

v24.1.0: Gunicorn 24.1.0

Compare Source

New Features

  • Official Docker Image: Gunicorn now publishes official Docker images to GitHub Container Registry (PR #​3454)

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

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

  • Socket Backlog Metric: New gunicorn.socket.backlog gauge metric reports the current socket backlog size on Linux systems (PR #​3450)

  • InotifyReloader Enhancement: The inotify-based reloader now watches newly imported modules, not just those loaded at startup (PR #​3447)

Bug Fixes

  • Fix signal handling regression where SIGCLD alias caused "Unhandled signal: cld" errors on Linux when workers fail during boot (#​3453)
  • Fix socket blocking mode on keepalive connections preventing SSL handshake failures with async workers (PR #​3452)
  • Use smaller buffer size in finish_body() for faster timeout detection on slow or abandoned connections (PR #​3453)
  • Handle SSLWantReadError in finish_body() to prevent worker hangs during SSL renegotiation (PR #​3448)
  • Log SIGTERM as info level instead of warning to reduce noise in orchestrated environments (PR #​3446)
  • Print exception details to stderr when worker fails to boot (PR #​3443)
  • Fix unreader.unread() to prepend data to buffer instead of appending (PR #​3442)
  • Prevent RecursionError when pickling Config objects (PR #​3441)
  • Use proper exception chaining with raise from in glogging.py (PR #​3440)

Installation

pip install gunicorn==24.1.0

Or use the official Docker image:

docker pull ghcr.io/benoitc/gunicorn:24.1.0

v24.0.0

Compare Source

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

Install

pip install gunicorn==24.0.0

Configuration

📅 Schedule: (UTC)

  • Branch creation
    • At any time (no schedule defined)
  • Automerge
    • At any time (no schedule defined)

🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.

♻ Rebasing: Never, or you tick the rebase/retry checkbox.

🔕 Ignore: Close this PR and you won't be reminded about these updates again.


  • If you want to rebase/retry this PR, check this box

This PR was generated by Mend Renovate. View the repository job log.

@renovate-bot
renovate-bot requested review from a team as code owners November 10, 2026 13:34
@trusted-contributions-gcf trusted-contributions-gcf Bot added kokoro:force-run Add this label to force Kokoro to re-run the tests. owlbot:run Add this label to trigger the Owlbot post processor. labels Nov 10, 2026
@product-auto-label product-auto-label Bot added samples Issues that are directly related to samples. api: appengine Issues related to the App Engine Admin API API. api: cloudsql api: container Issues related to the Kubernetes Engine API API. api: endpoints Issues related to the Cloud Endpoints API. api: eventarc Issues related to the Eventarc API. api: iap Issues related to the Identity-Aware Proxy API. api: memorystore api: people-and-planet-ai labels Nov 10, 2026
@kokoro-team kokoro-team removed the kokoro:force-run Add this label to force Kokoro to re-run the tests. label Nov 10, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the 'gunicorn' dependency version to '27.0.0' across multiple 'requirements.txt' files in various service directories, including App Engine, Cloud SQL, Eventarc, and Cloud Run. There are no review comments, and I have no feedback to provide.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

api: appengine Issues related to the App Engine Admin API API. api: cloudsql api: container Issues related to the Kubernetes Engine API API. api: endpoints Issues related to the Cloud Endpoints API. api: eventarc Issues related to the Eventarc API. api: iap Issues related to the Identity-Aware Proxy API. api: memorystore api: people-and-planet-ai owlbot:run Add this label to trigger the Owlbot post processor. samples Issues that are directly related to samples.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants