Match the Upgrade websocket token rather than the whole field value

RFC 9110 7.8 defines Upgrade as a comma-separated list of protocols and asks
recipients to match each protocol-name case-insensitively; RFC 6455 4.2.1 asks
for a header field containing the value "websocket". Both handshake checks
instead read occurrence zero and required the whole field value to be exactly
"websocket", so a client offering "websocket, HTTP/3.0" -- or naming websocket
on a second Upgrade field line -- was answered 404 rather than 101.

This is the defect ffe2a1c fixed for Connection two lines below, and
has_header_token() is already called in both of these functions.

The client-side check loosens what we accept back from a server, which is the
same reading: a server answering 101 may name websocket alongside another
protocol, and rejecting that handshake was ours to get wrong.
This commit is contained in:
yhirose committed 2026-08-18 22:29:26 -04:00
1 parent 8b19c288e8
commit 0151b3e23e
2 files changed
+44 -9

No files matched your search

+7 -9
View File
@@ -5296,11 +5296,11 @@ inline std::string websocket_accept_key(const std::string &client_key) {
inline bool is_websocket_upgrade(const Request &req) {
if (req.method != "GET") { return false; }
// Check Upgrade: websocket (case-insensitive)
auto upgrade_it = req.headers.find("Upgrade");
if (upgrade_it == req.headers.end()) { return false; }
auto upgrade_val = case_ignore::to_lower(upgrade_it->second);
if (upgrade_val != "websocket") { return false; }
// Check Upgrade: websocket. RFC 9110 7.8 defines Upgrade as a comma-separated
// list of protocols and asks recipients to match each name
// case-insensitively, so look for the token rather than compare the whole
// field value.
if (!has_header_token(req.headers, "Upgrade", "websocket")) { return false; }
// Check Connection: Upgrade
if (!has_header_token(req.headers, "Connection", "upgrade")) { return false; }
@@ -7912,10 +7912,8 @@ inline bool read_websocket_upgrade_response(Stream &strm,
return false;
}
// Verify Upgrade: websocket (case-insensitive)
auto upgrade_it = headers.find("Upgrade");
if (upgrade_it == headers.end() ||
case_ignore::to_lower(upgrade_it->second) != "websocket") {
// Verify Upgrade: websocket (a comma-separated list, matched per token)
if (!has_header_token(headers, "Upgrade", "websocket")) {
upgrade.error = Error::WebSocketHandshake;
return false;
}