You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 06ab442
Browse filesBrowse the repository at this point in the historyBrowse files
aclk: report the HTTP status when the WebSocket upgrade fails (#24537)
* aclk: report the HTTP status when the WebSocket upgrade fails
A proxy or ingress that does not upgrade the connection answers with its
own status (404, 502, ...) and no Sec-WebSocket-Accept header. The accept
header was checked first, so the log said "Missing sec-websocket-accept
header" and the status was never shown.
Decide the parsed reply in one place: status first (with the reason and
the Server header), then the accept headers. Peer strings in the log are
bounded and control bytes replaced.
This also drops nonce_matched, which ws_client_reset() never cleared: a
reconnect accepted a 101 reply with no accept header.
* aclk: accept empty and unpadded header values in the upgrade reply
An empty Server header made the parser fail before the status check,
so the status was lost again. Split on ':' and trim optional
whitespace (RFC 9112 5).
* aclk: log only printable ASCII from the upgrade reply
Bytes 0x80-0x9f (C1 controls, raw or UTF-8 encoded) passed through to
the log. Mask everything outside 0x20-0x7e.
* aclk: build handshake test replies without printf formats
Test replies and expected messages were passed to snprintf as the
format. Substitute a {NONCE} token instead.
* aclk: check the bound before reading in ws_client_log_safe()
* aclk: report the status of HTTP/1.0 replies to the upgrade
A proxy may answer with an HTTP/1.0 error page, which was rejected
before its status was read. Parse HTTP/1.0 status lines too and
require HTTP/1.1 only for a 101.
snprintf(expected, sizeof(expected), "HTTP return code not 101. Received 400 with msg \"%.127s\" (server: \"%.127s\").", longval, longval);
533
+
errors+=ws_client_unittest_handshake_case("400 with long reason and server", reply, expected);
534
+
}
535
+
536
+
// a 101 reply needs exactly the expected accept value, in every accept header it carries
537
+
errors+=ws_client_unittest_handshake_case("101 without accept", WS_UT_101"\r\n", WS_UT_MISSING_ERR);
538
+
errors+=ws_client_unittest_handshake_case("101 with wrong accept", WS_UT_101WS_UT_WRONG_ACCEPT"\r\n", WS_UT_WRONG_ERR);
539
+
errors+=ws_client_unittest_handshake_case("101 with control bytes in wrong accept", WS_UT_101"Sec-WebSocket-Accept: \x07""bell\r\n\r\n",
540
+
"Received NONCE \"?bell\" does not match expected nonce of \""WS_UT_NONCE"\"");
541
+
errors+=ws_client_unittest_handshake_case("101 with matching then wrong accept", WS_UT_101"Sec-WebSocket-Accept: "WS_UT_NONCE"\r\n"WS_UT_WRONG_ACCEPT"\r\n", WS_UT_WRONG_ERR);
542
+
errors+=ws_client_unittest_handshake_case("101 with wrong then matching accept", WS_UT_101WS_UT_WRONG_ACCEPT"Sec-WebSocket-Accept: "WS_UT_NONCE"\r\n\r\n", WS_UT_WRONG_ERR);
543
+
errors+=ws_client_unittest_handshake_case("101 with matching accept", WS_UT_101"sec-websocket-accept: "WS_UT_NONCE"\r\n\r\n", NULL);
544
+
errors+=ws_client_unittest_handshake_case("101 with unpadded matching accept", WS_UT_101"Sec-WebSocket-Accept:"WS_UT_NONCE"\r\n\r\n", NULL);
545
+
errors+=ws_client_unittest_handshake_case("101 with padded matching accept", WS_UT_101"Sec-WebSocket-Accept: \t "WS_UT_NONCE" \t\r\n\r\n", NULL);
546
+
errors+=ws_client_unittest_handshake_case("101 with empty accept", WS_UT_101"Sec-WebSocket-Accept: \r\n\r\n",
547
+
"Received NONCE \"\" does not match expected nonce of \""WS_UT_NONCE"\"");
548
+
// a proxy may answer with an HTTP/1.0 error page; its status is still reported, but 1.0 cannot upgrade
549
+
errors+=ws_client_unittest_handshake_case("HTTP/1.0 503 from a proxy", "HTTP/1.0 503 Service Unavailable\r\nServer: haproxy\r\n\r\n",
550
+
"HTTP return code not 101. Received 503 with msg \"Service Unavailable\" (server: \"haproxy\").");
551
+
errors+=ws_client_unittest_handshake_case("HTTP/1.0 101 with matching accept", "HTTP/1.0 101 Switching Protocols\r\nSec-WebSocket-Accept: "WS_UT_NONCE"\r\n\r\n",
552
+
"Received HTTP/1.0 101 reply; a WebSocket upgrade requires HTTP/1.1");
553
+
554
+
// a reply that is not HTTP/1.x fails while the status line is parsed
555
+
{
556
+
char*junk_host= (char*)"localhost";
557
+
ws_client*junk=ws_client_new(4096, &junk_host);
558
+
WS_TEST(junk!=NULL, "junk status line: client");
559
+
if (junk) {
560
+
WS_TEST(ws_client_start_handshake(junk) ==0, "junk status line: handshake starts");
561
+
fprintf(stderr, "ws_client handshake case: junk status line\n");
0 commit comments