New Features:
- Dirty Stash: global shared state between workers (#3503)
- Dirty Binary Protocol: TLV encoding for efficient IPC (#3500)
Documentation:
- Fix Markdown formatting in /configure
* feat(dirty): add stash - global shared state between workers
Add a simple key-value store (stash) that allows dirty workers to share
state through the arbiter. Tables are stored directly in arbiter memory
for fast access and simplicity.
Features:
- Auto-create tables on first access
- Dict-like interface via stash.table()
- Pattern matching for keys (glob patterns)
- Module-level API: stash.put(), stash.get(), stash.delete(), etc.
Usage:
from gunicorn.dirty import stash
stash.put("sessions", "user:1", {"name": "Alice"})
user = stash.get("sessions", "user:1")
# Or dict-like
sessions = stash.table("sessions")
sessions["user:1"] = {"name": "Alice"}
New files:
- gunicorn/dirty/stash.py - Client API and StashTable class
- Protocol additions for MSG_TYPE_STASH and STASH_OP_* codes
Note: Tables are ephemeral - lost if arbiter restarts.
* test(dirty): add tests for stash protocol and encoding
Test coverage for:
- Stash message creation and encoding
- Protocol constants (MSG_TYPE_STASH, STASH_OP_*)
- Error classes (StashError, StashTableNotFoundError, StashKeyNotFoundError)
- StashTable dict-like interface
- Edge cases: unicode, complex values, special patterns
* example(dirty): add stash usage example and integration tests
- Add SessionApp to dirty_app.py demonstrating stash usage
- Add /session/* endpoints to wsgi_app.py
- Add test_stash_integration.py with Docker tests
- Update docker-compose.yml with stash-test service
- Fix: Set GUNICORN_DIRTY_SOCKET in dirty arbiter for worker access
* docs(dirty): add stash documentation
- Update test_protocol.py example to use binary protocol
- Add test_binary_data_handling example showing raw bytes transfer
- Update dirty.md to document binary TLV protocol format
- Replace JSON references with binary protocol
- Add Binary Protocol section with header and TLV encoding details
- Add sponsor banner at top of README
- Add Revolut and Open Collective to FUNDING.yml
- Add dedicated sponsor page in docs
- Add sponsor heart link in footer
- Change title from "The Python WSGI Server" to "Serve Python Apps on the Web"
- Remove "Version {{ version }}" text from hero section
- Remove unused .hero__version CSS
- Bump version to 25.0.2
- Update copyright year to 2026 in LICENSE and NOTICE
- Add license headers to all Python source files
- Add changelog entry for 25.0.2
Closes#3484
When a client disconnects during an ASGI request, the worker now:
1. Sends http.disconnect message to the app's receive queue
2. Allows a configurable grace period for cleanup (default: 3 seconds)
3. Only cancels the task after the grace period expires
This follows the ASGI HTTP Connection Scope spec which defines
http.disconnect as the message apps should receive when clients
disconnect: https://asgi.readthedocs.io/en/latest/specs/www.html#disconnect-receive-event
The grace period prevents CancelledError from propagating to async
database operations, allowing SQLAlchemy and other async DB libraries
to properly reset their connection pools.
New config option: --asgi-disconnect-grace-period (default: 3 seconds)
- Fix mobile side menu not displaying on iPad by keeping site_nav
content but hiding sidebar on desktop via CSS
- Fix broken links in asgi.md and guides/http2.md
- Add HTTP/2 guide to navigation
- Add explicit do_handshake() in base_async.py before ALPN check
when do_handshake_on_connect is False
- Mark eventlet worker as deprecated (removal in 26.0)
- Add HTTP/2 gevent example with Docker and tests
- Update documentation to reflect eventlet deprecation
- Remove eventlet websocket example (gevent version exists)
The ALPN fix ensures HTTP/2 works correctly with gevent and eventlet
workers when do_handshake_on_connect config is False (the default).
Without explicit handshake, selected_alpn_protocol() returns None.
- Add three tuning profiles: Conservative, Balanced, High Concurrency
- Document pros/cons and trade-offs for each HTTP/2 setting
- Add Linux kernel tuning parameters for high concurrency
- Add Docker-specific tuning recommendations
- Include benchmark results from h2load testing
Add complete HTTP/2 example in examples/http2_features/:
- ASGI app showing priority access and trailer sending
- Test script using raw h2 library for HTTP/2 testing
- Docker setup for easy testing
- Documentation update referencing the example
The example demonstrates:
- Reading http.response.priority extension in ASGI scope
- Sending http.response.trailers messages
- Multiple streams on the same connection
- Guard early_hints callback against calls after response started
- Fix :authority precedence over Host header per RFC 9113 section 8.3.1
- Add nginx early_hints documentation link
- Use standard port 443 in curl examples
- Add docs/content/guides/http2.md with comprehensive HTTP/2 guide
- Quick start and requirements
- Configuration options and settings
- Worker compatibility matrix
- HTTP 103 Early Hints usage for WSGI and ASGI
- Production deployment with nginx
- Troubleshooting guide
- Update docs/content/asgi.md to reflect HTTP/2 support
- Update docs/content/2026-news.md with 25.0.0 release featuring
HTTP/2 and Early Hints
- Regenerate docs/content/reference/settings.md with HTTP/2 settings
Add uWSGI protocol support to ASGI worker
- Implements AsyncUWSGIRequest class extending sync UWSGIRequest to reuse parsing logic with async I/O
- ASGI protocol handler selects between HTTP and uWSGI based on --protocol config option
- Allows gunicorn's ASGI worker to receive requests from nginx using uwsgi_pass directive
- Includes unit tests and Docker integration tests
Add a lightweight chat simulator demonstrating dirty worker streaming:
- Token-by-token SSE streaming via async generators
- FastAPI endpoint with browser UI
- Multiple canned responses based on keywords
- Docker deployment with docker-compose
- Integration tests for SSE protocol
Update docs/content/dirty.md to link to both examples.
- Enhance architecture diagram with Unix socket paths, signal flow, and
heartbeat indicators
- Add process relationships table
- Expand signal handling section with flow diagram, comprehensive signal
reference table, and async handling explanation
- Add new "Liveness and Health Monitoring" section covering heartbeat
mechanism, timeout detection, parent death detection, orphan cleanup,
and respawn behavior
- Add link to embedding service example
Add support for streaming responses when dirty app actions return
generators (sync or async). This enables real-time delivery of
incremental results for use cases like LLM token generation.
Features:
- Streaming protocol with chunk/end/error message types
- Worker support for sync and async generators
- Arbiter forwarding of streaming messages
- Deadline-based timeout handling
- Async client streaming API
Protocol:
- Chunk messages (type: "chunk") contain partial data
- End messages (type: "end") signal stream completion
- Error messages can occur mid-stream
New files:
- benchmarks/dirty_streaming.py: Streaming benchmark suite
- tests/dirty/test_*_streaming*.py: Streaming test coverage
- docs/content/dirty.md: Streaming documentation with examples
Introduce Dirty Arbiters - a separate process pool for executing
long-running, blocking operations (AI model loading, heavy computation)
without blocking HTTP workers. Inspired by Erlang's dirty schedulers.
Key features:
- Completely separate from HTTP workers - can be killed/restarted independently
- Stateful - loaded resources persist in dirty worker memory
- Message-passing IPC via Unix sockets with JSON serialization
- Explicit execute() API from HTTP workers
- Asyncio-based for clean concurrent handling
Architecture:
- DirtyArbiter: manages the dirty worker pool, routes requests
- DirtyWorker: executes functions, maintains state, handles requests
- DirtyClient: sync/async API for HTTP workers to call dirty apps
- DirtyProtocol: length-prefixed JSON messages over Unix sockets
- DirtyApp: base class for dirty applications
Configuration options:
- dirty_apps: list of import paths for dirty applications
- dirty_workers: number of dirty workers (default: 0)
- dirty_timeout: task timeout in seconds (default: 300)
- dirty_graceful_timeout: shutdown timeout (default: 30)
Lifecycle hooks:
- on_dirty_starting(arbiter)
- dirty_post_fork(arbiter, worker)
- dirty_worker_init(worker)
- dirty_worker_exit(arbiter, worker)
Includes comprehensive test suite with 164 tests covering:
- Protocol encoding/decoding
- Worker and arbiter lifecycle
- Client sync/async APIs
- Signal handling
- Error handling and timeouts
- Integration tests
* Python3: refactor returned traceback
Exceptions provide __traceback__ reference since Python 3.0
(and creating cyclic references has not been big deal since Python 2.2)
* --reload: publish entire exception, not just traceback
This is dangerous insofar as the exception text is more
likely to contain secrets than the quoted lines from traceback are.
However, the difference between the two is minor compared to the
primary danger of enabling this on a production machine, so focus
on that instead!