Breaking Changes
- Reject with 400 a request whose
Transfer-Encodingdoes not end with
chunked, such asgziporidentityalone, 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 sendTransfer-Encoding: identitymust 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-Encodingvalues (RFC 9110 §5.6.3)
(#3364). - Reject a
Content-Lengththat 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 Longfor a request line overlimit_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
HEADrequest 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 againstlimit_request_fields. - ASGI: apply
forwarded_allow_ipsto HTTP/1 requests as WSGI and HTTP/2
already do:secure_scheme_headersset the scheme only from trusted
proxies, and header names with underscores followheader_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
keepaliveseconds, as
the other workers do (#3734). - ASGI: give each request a shallow copy of the lifespan state, so a value
one request stores inscope["state"]is not seen by the next one (ASGI
lifespan spec). - ASGI: deliver
websocket.disconnectas 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_sizesetting (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 duringmax_requestsrestarts. - 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_porton a platform withoutSO_REUSEPORT, workers now use the
master's sockets instead of failing to bind. - StatsD: send metrics over IPv6 when
statsd_hostis 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_classsetting. - Docs: explain that
timeoutwatches 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: 13with 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 anOSError
subclass, as ASGI 2.4 asks; it is still aRuntimeError, 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-continuewith100 Continuewhen 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
bytearrayormemoryviewresponse body when sending it; an
app that reused the buffer aftersendreturned 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_linethat follows an empty line, as the Python parser does
(RFC 9112 §2.2, §3).
Changes
- Require gunicorn_h1c 0.6.14 for the
fastextra. 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