mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-01 13:12:40 +07:00
Merge the SSLClient and WebSocketClient TLS session setup
SSLClient::initialize_ssl kept its own copy of the session setup that detail::setup_client_tls_session already implemented for WebSocketClient. Extend the shared function with the pieces only SSLClient needed - a session verifier, an independent hostname verification flag, the context mutex, Windows Schannel verification and error details - and let initialize_ssl build a ClientTlsSessionOptions and call it. All of them default, so WebSocketClient's call site is unchanged. This settles one difference between the two: WebSocketClient used to call tls::set_hostname for named hosts, which on OpenSSL turns on verification during the handshake, while SSLClient always set SNI only and verified post-handshake. The shared function now does the latter for both, so tls::set_hostname loses its last caller and goes away, as does the write-only SSLClient::verify_result_. Certificate verification with a host name rather than an IP literal was the one combination the WebSocket tests never covered, and it is exactly the path this normalizes. WebSocketSSLDnsHostTest fills that in; cert2 gains a DNS:localhost SAN so a name can be verified against it.
This commit is contained in:
@@ -21030,6 +21030,94 @@ TEST(WebSocketSSLVerifyTest, TrustedChainWrongIdentityFails) {
|
||||
|
||||
ASSERT_FALSE(client.connect());
|
||||
}
|
||||
|
||||
// The tests above all connect to an IP literal, for which RFC 6066 forbids
|
||||
// SNI, so nothing binds the host name to the TLS session. These bind the
|
||||
// server to "localhost" instead, the only shape that exercises the SNI and
|
||||
// hostname-verification path with certificate verification left enabled.
|
||||
class WebSocketSSLDnsHostTest : public ::testing::Test {
|
||||
protected:
|
||||
void Start(const char *cert_file) {
|
||||
server_ = httplib::detail::make_unique<SSLServer>(cert_file,
|
||||
SERVER_PRIVATE_KEY_FILE);
|
||||
ASSERT_TRUE(server_->is_valid());
|
||||
server_->WebSocket("/echo", [](const Request &, ws::WebSocket &ws) {
|
||||
std::string msg;
|
||||
while (ws.read(msg)) {
|
||||
ws.send(msg);
|
||||
}
|
||||
});
|
||||
port_ = server_->bind_to_any_port("localhost");
|
||||
server_thread_ = std::thread([this]() { server_->listen_after_bind(); });
|
||||
server_->wait_until_ready();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
if (server_) { server_->stop(); }
|
||||
if (server_thread_.joinable()) { server_thread_.join(); }
|
||||
}
|
||||
|
||||
std::string url() const {
|
||||
return "wss://localhost:" + std::to_string(port_) + "/echo";
|
||||
}
|
||||
|
||||
std::unique_ptr<SSLServer> server_;
|
||||
std::thread server_thread_;
|
||||
int port_ = 0;
|
||||
};
|
||||
|
||||
// cert2 carries a DNS:localhost SAN, so both the chain and the identity check
|
||||
// out and the connection is usable
|
||||
TEST_F(WebSocketSSLDnsHostTest, TrustedChainMatchingNameVerifies) {
|
||||
Start(SERVER_CERT2_FILE);
|
||||
|
||||
ws::WebSocketClient client(url());
|
||||
client.set_ca_cert_path(SERVER_CERT2_FILE);
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
// cert.pem has a CN but no SAN, so trusting it as a CA satisfies the chain
|
||||
// while the identity check must still reject "localhost"
|
||||
TEST_F(WebSocketSSLDnsHostTest, TrustedChainWrongNameFails) {
|
||||
Start(SERVER_CERT_FILE);
|
||||
|
||||
ws::WebSocketClient client(url());
|
||||
client.set_ca_cert_path(SERVER_CERT_FILE);
|
||||
|
||||
ASSERT_FALSE(client.connect());
|
||||
}
|
||||
|
||||
// A CA that did not sign the server certificate fails the chain, even though
|
||||
// the name would match
|
||||
TEST_F(WebSocketSSLDnsHostTest, UntrustedChainFails) {
|
||||
Start(SERVER_CERT2_FILE);
|
||||
|
||||
ws::WebSocketClient client(url());
|
||||
client.set_ca_cert_path(CLIENT_CA_CERT_FILE);
|
||||
|
||||
ASSERT_FALSE(client.connect());
|
||||
}
|
||||
|
||||
// With verification disabled, neither the chain nor the name is checked
|
||||
TEST_F(WebSocketSSLDnsHostTest, VerificationDisabledAcceptsAnyName) {
|
||||
Start(SERVER_CERT_FILE);
|
||||
|
||||
ws::WebSocketClient client(url());
|
||||
client.enable_server_certificate_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();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !defined(_WIN32)
|
||||
|
||||
+1
-1
@@ -18,4 +18,4 @@ emailAddress = test@email.address
|
||||
challengePassword = 1234
|
||||
|
||||
[SAN]
|
||||
subjectAltName=IP:127.0.0.1
|
||||
subjectAltName=IP:127.0.0.1,DNS:localhost
|
||||
|
||||
Reference in New Issue
Block a user