RFC6455 writes about the Sec-WebSocket-Protocol header:
The |Sec-WebSocket-Protocol| header field MAY appear multiple times
in an HTTP request (which is logically the same as a single
|Sec-WebSocket-Protocol| header field that contains all values).
And also the same it says about the Sec-WebSocket-Extensions header:
The |Sec-WebSocket-Extensions| header field MAY appear multiple times
in an HTTP request (which is logically the same as a single
|Sec-WebSocket-Extensions| header field that contains all values.
But the implementations only catches the last header of the handshake request for both of these headers.
The problem lies within the _process_connection_request function located in the wsproto/handshake.py file. It's easy to spot, that it in fact actually replaces the old occurrences of the named headers with the new ones:
...
elif name == b"sec-websocket-extensions":
extensions = split_comma_header(value)
continue # Skip appending to headers
elif name == b"sec-websocket-key":
key = value
elif name == b"sec-websocket-protocol":
subprotocols = split_comma_header(value)
...
RFC6455 writes about the
Sec-WebSocket-Protocol
header:And also the same it says about the
Sec-WebSocket-Extensions
header:But the implementations only catches the last header of the handshake request for both of these headers.
Proof:
Prints:
<class 'wsproto.events.Request'> Request(host='api.website.xyz', target='wss://api.website.xyz/ws', extensions=['this-extension; were-gonna-see', 'and-another-extension; were-also; gonna-see=100; percent'], extra_headers=[(b'connection', b'Upgrade'), (b'pragma', b'no-cache'), (b'cache-control', b'no-cache'), (b'user-agent', b'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/103.0.5060.114 Safari/537.36'), (b'upgrade', b'websocket'), (b'origin', b'https://website.xyz'), (b'sec-websocket-version', b'13'), (b'accept-encoding', b'gzip, deflate, br'), (b'accept-language', b'ru-RU,ru;q=0.9,en-US;q=0.8,en;q=0.7'), (b'sec-websocket-key', b'qEGbHKjRLYJJHJB4V1e1bA==')], subprotocols=['only-these-protocols', 'are-seen', 'from-the-request-object'])
The problem lies within the
_process_connection_request
function located in thewsproto/handshake.py
file. It's easy to spot, that it in fact actually replaces the old occurrences of the named headers with the new ones: