Omit default port from WebSocket handshake Host header (Fix #2480)

The WebSocket upgrade request always appended ":port" to the Host
header, violating RFC 6455 Section 4.1 which says the port should be
included only when it is not the default (80 for ws, 443 for wss).
Some CDNs alter routing when the Host header carries an explicit
default port.

Build the Host header with detail::make_host_and_port_string, which
also brackets IPv6 literal hosts correctly.
This commit is contained in:
yhirose
2026-07-01 21:38:57 -04:00
parent f5c8c982df
commit 45da614ddd
2 changed files with 52 additions and 4 deletions
+41
View File
@@ -18975,6 +18975,47 @@ TEST(WebSocketTest, QueryStringInHandshake) {
t.join();
}
TEST(WebSocketTest, HostHeaderInHandshake) {
Server svr;
std::mutex mtx;
std::string received_host;
svr.WebSocket("/ws", [&](const Request &req, ws::WebSocket &ws) {
{
std::lock_guard<std::mutex> lock(mtx);
received_host = req.get_header_value("Host");
}
std::string msg;
while (ws.read(msg)) {
ws.send(msg);
}
});
auto port = svr.bind_to_any_port("localhost");
std::thread t([&]() { svr.listen_after_bind(); });
svr.wait_until_ready();
ws::WebSocketClient client("ws://localhost:" + std::to_string(port) + "/ws");
ASSERT_TRUE(client.connect());
// Round-trip ensures the handler has run and captured the request.
ASSERT_TRUE(client.send("hello"));
std::string msg;
ASSERT_TRUE(client.read(msg));
client.close();
{
std::lock_guard<std::mutex> lock(mtx);
// Non-default port must be present in the Host header. Default ports
// (80/443) are omitted; that logic is covered by
// MakeHostAndPortStringTest.
EXPECT_EQ("localhost:" + std::to_string(port), received_host);
}
svr.stop();
t.join();
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
class WebSocketSSLIntegrationTest : public ::testing::Test {
protected: