From 86d02103916f7ca5ef56cf37843a2d2953d710ff Mon Sep 17 00:00:00 2001 From: yhirose Date: Fri, 7 Aug 2026 18:48:59 -0400 Subject: [PATCH] Add WebSocketClient::enable_server_hostname_verification WebSocketClient's TLS setup already threaded ClientTlsSessionOptions::server_hostname_verification through setup_client_tls_session(), the same path SSLClient uses, but never exposed a way to set it: create_stream() called setup_client_tls_session() without an options argument, so the default (verification on) was the only reachable value. Add the public setter, mirroring ClientImpl/SSLClient/Client, and wire it into create_stream()'s ClientTlsSessionOptions. Last open item from issue #2531's WebSocketClient/SSLClient API alignment. --- README-websocket.md | 2 ++ httplib.h | 20 +++++++++++++++----- test/test.cc | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/README-websocket.md b/README-websocket.md index d16239d..7aec5c2 100644 --- a/README-websocket.md +++ b/README-websocket.md @@ -188,6 +188,7 @@ void set_ca_cert_path(const std::string &ca_cert_file_path, const std::string &ca_cert_dir_path = std::string()); void set_ca_cert_store(tls::ca_store_t store); void enable_server_certificate_verification(bool enabled); +void enable_server_hostname_verification(bool enabled); ``` ## Examples @@ -395,6 +396,7 @@ if (ws.connect()) { httplib::ws::WebSocketClient ws("wss://example.com/ws"); ws.set_ca_cert_path("/path/to/ca-bundle.crt"); ws.enable_server_certificate_verification(true); +ws.enable_server_hostname_verification(true); // default; false skips the identity check if (ws.connect()) { ws.send("secure message"); diff --git a/httplib.h b/httplib.h index 71c4364..acc7563 100644 --- a/httplib.h +++ b/httplib.h @@ -4360,6 +4360,7 @@ public: void set_ca_cert_store(tls::ca_store_t store); void load_ca_cert_store(const char *ca_cert, std::size_t size); void enable_server_certificate_verification(bool enabled); + void enable_server_hostname_verification(bool enabled); void enable_system_ca(bool enabled); #endif @@ -4406,6 +4407,7 @@ private: bool certs_loaded_ = false; SystemCAMode system_ca_mode_ = SystemCAMode::Auto; bool server_certificate_verification_ = true; + bool server_hostname_verification_ = true; #endif }; @@ -10137,11 +10139,12 @@ inline bool load_client_ca_config(tls::ctx_t ctx, return ret; } -// The parts of session setup that only SSLClient needs. WebSocketClient takes -// the defaults, which is what keeps the two clients on one implementation. +// The parts of session setup that only SSLClient needs, plus the handful +// WebSocketClient also exposes; everything else takes the defaults, which is +// what keeps the two clients on one implementation. struct ClientTlsSessionOptions { - // SSLClient exposes this independently of certificate verification; - // WebSocketClient always checks the identity when it verifies the chain. + // Both SSLClient and WebSocketClient expose this independently of + // certificate verification. bool server_hostname_verification = true; std::function session_verifier; // When non-null, guards session creation against concurrent use of the @@ -21463,11 +21466,14 @@ inline bool WebSocketClient::create_stream(std::unique_ptr &strm, certs_loaded_ = true; } + detail::ClientTlsSessionOptions options; + options.server_hostname_verification = server_hostname_verification_; + detail::ClientTlsSessionError tls_error; if (!detail::setup_client_tls_session(host_, tls_ctx_, tls_session_, sock_, server_certificate_verification_, read_timeout_sec_, read_timeout_usec_, - &tls_error)) { + &tls_error, options)) { error = tls_error.error; ssl_error = tls_error.ssl_error; ssl_backend_error = tls_error.backend_error; @@ -21660,6 +21666,10 @@ WebSocketClient::enable_server_certificate_verification(bool enabled) { server_certificate_verification_ = enabled; } +inline void WebSocketClient::enable_server_hostname_verification(bool enabled) { + server_hostname_verification_ = enabled; +} + inline void WebSocketClient::enable_system_ca(bool enabled) { system_ca_mode_ = enabled ? SystemCAMode::Enabled : SystemCAMode::Disabled; } diff --git a/test/test.cc b/test/test.cc index 08ca72d..730a602 100644 --- a/test/test.cc +++ b/test/test.cc @@ -21123,6 +21123,23 @@ TEST_F(WebSocketSSLDnsHostTest, TrustedChainWrongNameFails) { EXPECT_EQ(-1, res.status()); } +// Same setup as TrustedChainWrongNameFails, but with hostname verification +// disabled: the chain is still checked, only the identity check is skipped +TEST_F(WebSocketSSLDnsHostTest, HostnameVerificationDisabledAcceptsWrongName) { + Start(SERVER_CERT_FILE); + + ws::WebSocketClient client(url()); + client.set_ca_cert_path(SERVER_CERT_FILE); + client.enable_server_hostname_verification(false); + + ASSERT_TRUE(client.connect()); + ASSERT_TRUE(client.send("hello")); + std::string msg; + EXPECT_EQ(ws::Text, client.read(msg)); + EXPECT_EQ("hello", msg); + client.close(); +} + // A CA that did not sign the server certificate fails the chain, even though // the name would match TEST_F(WebSocketSSLDnsHostTest, UntrustedChainFails) {