mirror of
https://github.com/yhirose/cpp-httplib.git
synced 2026-10-27 12:31:28 +00:00
Let WebSocketClient take a CA directory the way ClientImpl does
WebSocketClient::set_ca_cert_path took a single path and create_stream() hardcoded an empty directory when calling detail::load_client_ca_config, while ClientImpl has always accepted (ca_cert_file_path, ca_cert_dir_path = ""). Give WebSocketClient the same signature and store the directory, so both clients configure CA loading identically. The one-argument form is unchanged for callers. Also note at both call sites why the "load the CA config once" guard differs: SSLClient needs call_once because one client serves concurrent requests, and WebSocketClient does not because connect() is not safe to call concurrently anyway.
This commit is contained in:
1 parent
a1aa2ad9cd
commit
d2ef193b9c
3 files changed
+44
-7
No files matched your search
+6
-1
@@ -155,9 +155,14 @@ bool is_open() const;
|
|||||||
// Timeouts
|
// Timeouts
|
||||||
void set_read_timeout(time_t sec, time_t usec = 0);
|
void set_read_timeout(time_t sec, time_t usec = 0);
|
||||||
void set_write_timeout(time_t sec, time_t usec = 0);
|
void set_write_timeout(time_t sec, time_t usec = 0);
|
||||||
|
template <class Rep, class Period>
|
||||||
|
void set_read_timeout(const std::chrono::duration<Rep, Period> &duration);
|
||||||
|
template <class Rep, class Period>
|
||||||
|
void set_write_timeout(const std::chrono::duration<Rep, Period> &duration);
|
||||||
|
|
||||||
// SSL configuration (wss:// only, requires CPPHTTPLIB_OPENSSL_SUPPORT)
|
// SSL configuration (wss:// only, requires CPPHTTPLIB_OPENSSL_SUPPORT)
|
||||||
void set_ca_cert_path(const std::string &path);
|
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 set_ca_cert_store(tls::ca_store_t store);
|
||||||
void enable_server_certificate_verification(bool enabled);
|
void enable_server_certificate_verification(bool enabled);
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -4302,7 +4302,8 @@ public:
|
|||||||
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
void set_hostname_addr_map(std::map<std::string, std::string> addr_map);
|
||||||
|
|
||||||
#ifdef CPPHTTPLIB_SSL_ENABLED
|
#ifdef CPPHTTPLIB_SSL_ENABLED
|
||||||
void set_ca_cert_path(const std::string &path);
|
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 set_ca_cert_store(tls::ca_store_t store);
|
||||||
void load_ca_cert_store(const char *ca_cert, std::size_t size);
|
void load_ca_cert_store(const char *ca_cert, std::size_t size);
|
||||||
void enable_server_certificate_verification(bool enabled);
|
void enable_server_certificate_verification(bool enabled);
|
||||||
@@ -4346,6 +4347,7 @@ private:
|
|||||||
tls::ctx_t tls_ctx_ = nullptr;
|
tls::ctx_t tls_ctx_ = nullptr;
|
||||||
tls::session_t tls_session_ = nullptr;
|
tls::session_t tls_session_ = nullptr;
|
||||||
std::string ca_cert_file_path_;
|
std::string ca_cert_file_path_;
|
||||||
|
std::string ca_cert_dir_path_;
|
||||||
bool custom_ca_loaded_ = false;
|
bool custom_ca_loaded_ = false;
|
||||||
bool certs_loaded_ = false;
|
bool certs_loaded_ = false;
|
||||||
SystemCAMode system_ca_mode_ = SystemCAMode::Auto;
|
SystemCAMode system_ca_mode_ = SystemCAMode::Auto;
|
||||||
@@ -17077,6 +17079,8 @@ inline void SSLClient::load_ca_cert_store(const char *ca_cert,
|
|||||||
inline bool SSLClient::load_certs() {
|
inline bool SSLClient::load_certs() {
|
||||||
auto ret = true;
|
auto ret = true;
|
||||||
|
|
||||||
|
// call_once rather than the plain flag WebSocketClient::create_stream() uses:
|
||||||
|
// one client is shared across concurrent requests here.
|
||||||
std::call_once(initialize_cert_, [&]() {
|
std::call_once(initialize_cert_, [&]() {
|
||||||
std::lock_guard<std::mutex> guard(ctx_mutex_);
|
std::lock_guard<std::mutex> guard(ctx_mutex_);
|
||||||
|
|
||||||
@@ -21352,11 +21356,14 @@ inline void WebSocketClient::shutdown_and_close() {
|
|||||||
inline bool WebSocketClient::create_stream(std::unique_ptr<Stream> &strm) {
|
inline bool WebSocketClient::create_stream(std::unique_ptr<Stream> &strm) {
|
||||||
#ifdef CPPHTTPLIB_SSL_ENABLED
|
#ifdef CPPHTTPLIB_SSL_ENABLED
|
||||||
if (is_ssl_) {
|
if (is_ssl_) {
|
||||||
|
// A plain flag rather than SSLClient::load_certs()'s call_once: connect()
|
||||||
|
// is not safe to call concurrently on one client to begin with, since
|
||||||
|
// nothing else here is guarded either.
|
||||||
if (server_certificate_verification_ && !certs_loaded_) {
|
if (server_certificate_verification_ && !certs_loaded_) {
|
||||||
uint64_t backend_error = 0;
|
uint64_t backend_error = 0;
|
||||||
detail::load_client_ca_config(tls_ctx_, ca_cert_file_path_, std::string(),
|
detail::load_client_ca_config(tls_ctx_, ca_cert_file_path_,
|
||||||
custom_ca_loaded_, system_ca_mode_,
|
ca_cert_dir_path_, custom_ca_loaded_,
|
||||||
backend_error);
|
system_ca_mode_, backend_error);
|
||||||
certs_loaded_ = true;
|
certs_loaded_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -21518,8 +21525,11 @@ inline void WebSocketClient::set_hostname_addr_map(
|
|||||||
|
|
||||||
#ifdef CPPHTTPLIB_SSL_ENABLED
|
#ifdef CPPHTTPLIB_SSL_ENABLED
|
||||||
|
|
||||||
inline void WebSocketClient::set_ca_cert_path(const std::string &path) {
|
inline void
|
||||||
ca_cert_file_path_ = path;
|
WebSocketClient::set_ca_cert_path(const std::string &ca_cert_file_path,
|
||||||
|
const std::string &ca_cert_dir_path) {
|
||||||
|
ca_cert_file_path_ = ca_cert_file_path;
|
||||||
|
ca_cert_dir_path_ = ca_cert_dir_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void WebSocketClient::set_ca_cert_store(tls::ca_store_t store) {
|
inline void WebSocketClient::set_ca_cert_store(tls::ca_store_t store) {
|
||||||
|
|||||||
@@ -20958,6 +20958,28 @@ TEST_F(WebSocketSSLCATest, WrongCustomCaFailsVerification) {
|
|||||||
ASSERT_FALSE(client.connect());
|
ASSERT_FALSE(client.connect());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The same CA as a file path rather than PEM in memory
|
||||||
|
TEST_F(WebSocketSSLCATest, SetCaCertPathVerifiesServer) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ...and a CA file that does not cover the server still fails, so it is the
|
||||||
|
// path above that decides the outcome
|
||||||
|
TEST_F(WebSocketSSLCATest, WrongCaCertPathFailsVerification) {
|
||||||
|
ws::WebSocketClient client(url());
|
||||||
|
client.set_ca_cert_path(CLIENT_CA_CERT_FILE);
|
||||||
|
|
||||||
|
ASSERT_FALSE(client.connect());
|
||||||
|
}
|
||||||
|
|
||||||
// Regression test: reconnecting with a native custom CA store used to reuse
|
// Regression test: reconnecting with a native custom CA store used to reuse
|
||||||
// a store handle the previous TLS context had already freed (use-after-free
|
// a store handle the previous TLS context had already freed (use-after-free
|
||||||
// under the OpenSSL backend). The context now lives as long as the client.
|
// under the OpenSSL backend). The context now lives as long as the client.
|
||||||
|
|||||||
Reference in new issue
Block a user