Tokenize Connection header in WebSocket client handshake#12746
Conversation
The client previously required the response Connection header to equal "upgrade" exactly, rejecting spec-compliant servers that send a comma-separated list such as "upgrade, keep-alive". Split on commas and compare tokens so the response is interpreted per RFC 9110 §7.6.1. This mirrors the tokenization the server-side handshake now relies on (via the HTTP parser in aio-libs#12727), and a regression test confirms substring matches like "keep-alive, notupgrade" are not accepted as the upgrade token.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## master #12746 +/- ##
==========================================
+ Coverage 98.92% 98.95% +0.03%
==========================================
Files 131 131
Lines 46881 47824 +943
Branches 2431 2480 +49
==========================================
+ Hits 46376 47324 +948
+ Misses 379 376 -3
+ Partials 126 124 -2
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. |
Merging this PR will not alter performance
Comparing Footnotes
|
Mirror http_parser._parse_headers exactly: add the token.isascii() filter so non-ASCII case folding cannot influence the membership test, matching the parser convention the change cites.
Turn the multi-token test into a parametrized positive handshake assertion covering case folding, surrounding whitespace, and non-first token position -- each rejected by the old exact-equality check -- and clarify that the substring test guards against a naive 'in' check.
Rejecting non-token values such as 'keep-alive, notupgrade' was already the prior behavior, so it is not part of the fix.
Rather than re-tokenizing the response Connection header in the client, preserve the HTTP parser's already-tokenized upgrade decision on ClientResponse (resp._upgraded) and check that during the WebSocket handshake. This mirrors the server-side check (web_ws uses request._message.upgrade) and means the RFC 9110 §7.6.1 tokenization lives in one place. Multi-token responses such as "Connection: upgrade, keep-alive" are accepted as before.
Move the Connection tokenization coverage to where the logic now lives: the response parser (covering both the C and pure-Python parsers), plus a ClientResponse.start() test that the upgrade flag is preserved. The ws_connect tests now exercise the resp._upgraded check directly.
|
Is this PR ready? Still seems to marked as draft.. |
Backport to 3.15: 💔 cherry-picking failed — conflicts found❌ Failed to cleanly apply ff38c23 on top of patchback/backports/3.15/ff38c236ce9cda3a05ae01e7a967012e05074fe6/pr-12746 Backporting merged PR #12746 into master
🤖 @patchback |
What do these changes do?
Fix the WebSocket client handshake's response
Connectionheader check, whichwas a strict
resp.headers.get(hdrs.CONNECTION, "").lower() != "upgrade"andtherefore rejected spec-compliant responses such as
Connection: upgrade, keep-alive(RFC 9110 §7.6.1 allowsConnectionto bea comma-separated token list).
Rather than re-tokenise in
client.py, this PR uses the same trick #12723applied on the server side: the HTTP parser already tokenises
Connectionand records the
upgradetoken inmessage.upgrade.ClientResponse.startnow copies that flag to a new
_upgradedattribute, and_ws_connectreadsit directly:
Are there changes in behavior for the user?
Yes, in the permissive direction only.
aiohttp.ClientSession.ws_connectnowaccepts successful WebSocket handshakes whose response
Connectionheaderlists
upgradealongside other tokens (e.g.upgrade, keep-alive).Previously such responses raised
WSServerHandshakeError("Invalid connection header").Is it a substantial burden for the maintainers to support this?
No. The behaviour now flows through the single, already-tested parser path
(
HttpParser.parse_headers→message.upgrade→ClientResponse._upgraded);the WebSocket handshake just reads the flag.
Related issue number
Companion to #12723 (server side). Worth adding the
backport-3.14label atmerge so 3.14 doesn't ship with asymmetric behaviour (server accepts
Connection: upgrade, keep-aliveafter #12724, client still rejects it).Checklist
ClientResponse.start, and the WS handshake gate)CONTRIBUTORS.txt— already listedCHANGES/folder