Repository navigation
--header "Name: Value" (space after the colon) makes every response fail with the h11 and wsproto protocols
#3188
Unanswered
sungbin1015
asked this question in
Potential Issue
Replies: 1 comment
|
confirmed, it's the split in main.py. line 494 does header.split(":", 1) with no strip, so --header "X-Frame-Options: DENY" keeps the value as " DENY" with the leading space. h11 rejects that as an illegal header value and drops the connection, which is the empty reply you're seeing. fix is stripping both sides when parsing. httptools and zttp tolerate the space which is why only h11 and wsproto blow up. |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Description
--headersplits its argument on the first:and keeps whatever follows verbatim, including leading/trailing whitespace. Writing the header the way it looks on the wire (and the waycurl -Htakes it), e.g.--header "X-Frame-Options: DENY", stores the value" DENY".What happens next depends on the protocol implementation:
--http h11h11._util.LocalProtocolError: Illegal header value b' DENY', the connection is closed with no response (curl: (52) Empty reply from server). The access log still prints200 OKfor it.--ws wsprotoLocalProtocolError--http httptools,--http zttpx-frame-options: DENY(two spaces)--ws websockets,--ws websockets-sansioThe server starts normally and nothing is reported until the first request, so with plain
pip install uvicorn(h11 is what--http autopicks when httptools is not installed) the option turns the whole server into one that answers nothing.Whitespace around a field value is not part of the value (RFC 9110 section 5.5), so I'd expect the CLI to strip it, and all implementations to send
x-frame-options: DENY.Root cause, on
mainat 724f82f:uvicorn/main.py:494-headers=[header.split(":", 1) for header in headers]does not strip.uvicorn/config.py:496- the pair is encoded as-is intoencoded_headers, which becomesserver_state.default_headers(uvicorn/server.py:262).uvicorn/protocols/http/h11_impl.py:467,484- the default headers are passed toh11.Response(...), which validates field values and rejects the leading space.uvicorn/protocols/websockets/wsproto_impl.py:391hits the same h11 validation through wsproto.The existing test (
tests/test_cli.py::test_cli_headers) only coversName:Valuewith no space.A possible fix is a one-line change at the CLI boundary:
I have this plus a test locally and can open a PR if you agree it's a bug. Note that #2992 (open) touches the same line to reject a missing colon; it does not strip whitespace, so the two don't overlap in behaviour, but the strip could just as well go into that PR's
_parse_header().Programmatic
Config(headers=[("X-Frame-Options", " DENY")])behaves the same way, but there the caller typed the space explicitly, so I'm only reporting the CLI.Example Code
Python, Uvicorn & OS Version
main at 724f82f, with the versions pinned in
uv.lock: h11 0.16.0, httptools 0.8.0, wsproto 1.3.2, websockets 17.1, zttp 0.0.34, click 8.5.0.This was found and reproduced with the help of an AI coding assistant (Claude).
All reactions