2 İşlemeler 2bb69e2b21 ... 24bb2740c2

Yazar SHA1 Mesaj Tarih
  yhirose 24bb2740c2 Increase timeout threshold for response time in ServerTest 2 hafta önce
  yhirose 632b348101 Refactor TLS context usage: update namespace references and enhance MbedTlsContext implementation 2 hafta önce
3 değiştirilmiş dosya ile 169 ekleme ve 151 silme
  1. 1 1
      .github/workflows/test.yaml
  2. 152 133
      httplib.h
  3. 16 17
      test/test.cc

+ 1 - 1
.github/workflows/test.yaml

@@ -124,7 +124,7 @@ jobs:
         run: cd test && make
       - name: build and run tests (Mbed TLS)
         if: matrix.tls_backend == 'mbedtls'
-        run: cd test && make test_mbedtls && ./test_mbedtls
+        run: cd test && make test_split_mbedtls && make test_mbedtls && ./test_mbedtls
       - name: run fuzz test target
         if: matrix.tls_backend == 'openssl'
         run: cd test && make fuzz_test

+ 152 - 133
httplib.h

@@ -1927,7 +1927,7 @@ public:
   [[deprecated("Use load_ca_cert_store() instead")]]
   void set_ca_cert_store(X509_STORE *ca_cert_store);
 
-  [[deprecated("Use detail::tls::tls_create_ca_store() instead")]]
+  [[deprecated("Use tls::tls_create_ca_store() instead")]]
   X509_STORE *create_ca_cert_store(const char *ca_cert, std::size_t size) const;
 
   void set_server_certificate_verifier(
@@ -2168,6 +2168,29 @@ public:
 #endif
 };
 
+#ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
+// Mbed TLS context wrapper (holds config, entropy, DRBG, CA chain, own
+// cert/key). This struct is exposed for use in SSL context setup callbacks.
+struct MbedTlsContext {
+  mbedtls_ssl_config conf;
+  mbedtls_entropy_context entropy;
+  mbedtls_ctr_drbg_context ctr_drbg;
+  mbedtls_x509_crt ca_chain;
+  mbedtls_x509_crt own_cert;
+  mbedtls_pk_context own_key;
+  bool is_server = false;
+  bool verify_client = false;
+  bool has_verify_callback = false;
+
+  MbedTlsContext();
+  ~MbedTlsContext();
+
+  // Non-copyable
+  MbedTlsContext(const MbedTlsContext &) = delete;
+  MbedTlsContext &operator=(const MbedTlsContext &) = delete;
+};
+#endif
+
 #ifdef CPPHTTPLIB_SSL_ENABLED
 class SSLServer : public Server {
 public:
@@ -2872,6 +2895,8 @@ bool is_field_value(const std::string &s);
 
 } // namespace fields
 
+} // namespace detail
+
 /*
  * TLS Abstraction Layer Declarations
  */
@@ -3063,9 +3088,13 @@ std::string tls_error_string(uint64_t code);
 
 } // namespace tls
 
+#endif // CPPHTTPLIB_SSL_ENABLED
+
+namespace detail {
+
+#ifdef CPPHTTPLIB_SSL_ENABLED
 // Utility function for SSL implementation
 bool is_ip_address(const std::string &host);
-
 #endif // CPPHTTPLIB_SSL_ENABLED
 
 } // namespace detail
@@ -10389,7 +10418,7 @@ inline bool ClientImpl::send_(Request &req, Response &res, Error &error) {
 
 #ifdef CPPHTTPLIB_SSL_ENABLED
       if (is_alive && is_ssl()) {
-        if (detail::tls::tls_is_peer_closed(socket_.ssl, socket_.sock)) {
+        if (tls::tls_is_peer_closed(socket_.ssl, socket_.sock)) {
           is_alive = false;
         }
       }
@@ -10576,7 +10605,7 @@ ClientImpl::open_stream(const std::string &method, const std::string &path,
       is_alive = detail::is_socket_alive(socket_.sock);
 #ifdef CPPHTTPLIB_SSL_ENABLED
       if (is_alive && is_ssl()) {
-        if (detail::tls::tls_is_peer_closed(socket_.ssl, socket_.sock)) {
+        if (tls::tls_is_peer_closed(socket_.ssl, socket_.sock)) {
           is_alive = false;
         }
       }
@@ -11511,7 +11540,7 @@ inline bool ClientImpl::process_request(Stream &strm, Request &req,
   if (is_ssl()) {
     auto is_proxy_enabled = !proxy_host_.empty() && proxy_port_ != -1;
     if (!is_proxy_enabled) {
-      if (detail::tls::tls_is_peer_closed(socket_.ssl, socket_.sock)) {
+      if (tls::tls_is_peer_closed(socket_.ssl, socket_.sock)) {
         error = Error::SSLPeerCouldBeClosed_;
         output_error_log(error, &req);
         return false;
@@ -12662,7 +12691,6 @@ inline void ClientImpl::set_error_logger(ErrorLogger error_logger) {
  */
 
 #ifdef CPPHTTPLIB_OPENSSL_SUPPORT
-namespace detail {
 namespace crypto {
 
 inline size_t hash_size(HashAlgorithm algo) {
@@ -13135,13 +13163,13 @@ inline bool tls_connect_nonblocking(tls_session_t session, socket_t sock,
   auto bio = SSL_get_rbio(ssl);
 
   // Set non-blocking mode for handshake
-  set_nonblocking(sock, true);
+  detail::set_nonblocking(sock, true);
   if (bio) { BIO_set_nbio(bio, 1); }
 
-  auto cleanup = scope_exit([&]() {
+  auto cleanup = detail::scope_exit([&]() {
     // Restore blocking mode after handshake
     if (bio) { BIO_set_nbio(bio, 0); }
-    set_nonblocking(sock, false);
+    detail::set_nonblocking(sock, false);
   });
 
   auto res = 0;
@@ -13149,10 +13177,14 @@ inline bool tls_connect_nonblocking(tls_session_t session, socket_t sock,
     auto ssl_err = SSL_get_error(ssl, res);
     switch (ssl_err) {
     case SSL_ERROR_WANT_READ:
-      if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
+      if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
+        continue;
+      }
       break;
     case SSL_ERROR_WANT_WRITE:
-      if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
+      if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
+        continue;
+      }
       break;
     default: break;
     }
@@ -13180,13 +13212,13 @@ inline bool tls_accept_nonblocking(tls_session_t session, socket_t sock,
   auto bio = SSL_get_rbio(ssl);
 
   // Set non-blocking mode for handshake
-  set_nonblocking(sock, true);
+  detail::set_nonblocking(sock, true);
   if (bio) { BIO_set_nbio(bio, 1); }
 
-  auto cleanup = scope_exit([&]() {
+  auto cleanup = detail::scope_exit([&]() {
     // Restore blocking mode after handshake
     if (bio) { BIO_set_nbio(bio, 0); }
-    set_nonblocking(sock, false);
+    detail::set_nonblocking(sock, false);
   });
 
   auto res = 0;
@@ -13194,10 +13226,14 @@ inline bool tls_accept_nonblocking(tls_session_t session, socket_t sock,
     auto ssl_err = SSL_get_error(ssl, res);
     switch (ssl_err) {
     case SSL_ERROR_WANT_READ:
-      if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
+      if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
+        continue;
+      }
       break;
     case SSL_ERROR_WANT_WRITE:
-      if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
+      if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
+        continue;
+      }
       break;
     default: break;
     }
@@ -13277,8 +13313,8 @@ inline bool tls_is_peer_closed(tls_session_t session, socket_t sock) {
   if (!session) return true;
 
   // Temporarily set socket to non-blocking to avoid blocking on SSL_peek
-  set_nonblocking(sock, true);
-  auto se = scope_exit([&]() { set_nonblocking(sock, false); });
+  detail::set_nonblocking(sock, true);
+  auto se = detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
 
   auto ssl = static_cast<SSL *>(session);
   char buf;
@@ -13876,12 +13912,11 @@ inline tls_ctx_t create_client_context_from_x509(X509 *cert, EVP_PKEY *key,
 }
 
 } // namespace tls
-} // namespace detail
 
 // ClientImpl::set_ca_cert_store - defined here to use tls::x509_store_to_pem
 inline void ClientImpl::set_ca_cert_store(X509_STORE *ca_cert_store) {
   if (ca_cert_store && ca_cert_store != ca_cert_store_) {
-    ca_cert_pem_ = detail::tls::x509_store_to_pem(ca_cert_store);
+    ca_cert_pem_ = tls::x509_store_to_pem(ca_cert_store);
     ca_cert_store_ = ca_cert_store;
   }
 }
@@ -13916,19 +13951,19 @@ inline bool SSLClient::check_host_name(const char *pattern,
 
 inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
                             X509_STORE *client_ca_cert_store) {
-  ctx_ = detail::tls::create_server_context_from_x509(
+  ctx_ = tls::create_server_context_from_x509(
       cert, private_key, client_ca_cert_store, last_ssl_error_);
 }
 
 inline SSLServer::SSLServer(
     const std::function<bool(SSL_CTX &ssl_ctx)> &setup_ssl_ctx_callback) {
   // Use abstract API to create context
-  ctx_ = detail::tls::tls_create_server_context();
+  ctx_ = tls::tls_create_server_context();
   if (ctx_) {
     // Pass to OpenSSL-specific callback (ctx_ is SSL_CTX* internally)
     auto ssl_ctx = static_cast<SSL_CTX *>(ctx_);
     if (!setup_ssl_ctx_callback(*ssl_ctx)) {
-      detail::tls::tls_free_context(ctx_);
+      tls::tls_free_context(ctx_);
       ctx_ = nullptr;
     }
   }
@@ -13941,8 +13976,8 @@ inline SSL_CTX *SSLServer::ssl_context() const {
 inline void SSLServer::update_certs(X509 *cert, EVP_PKEY *private_key,
                                     X509_STORE *client_ca_cert_store) {
   std::lock_guard<std::mutex> guard(ctx_mutex_);
-  detail::tls::update_server_certs_from_x509(ctx_, cert, private_key,
-                                             client_ca_cert_store);
+  tls::update_server_certs_from_x509(ctx_, cert, private_key,
+                                     client_ca_cert_store);
 }
 
 // SSL HTTP client implementation
@@ -13957,7 +13992,7 @@ inline SSLClient::SSLClient(const std::string &host, int port,
                             const std::string &client_key_path,
                             const std::string &private_key_password)
     : ClientImpl(host, port, client_cert_path, client_key_path) {
-  ctx_ = detail::tls::tls_create_client_context();
+  ctx_ = tls::tls_create_client_context();
 
   // TODO: Add tls_set_min_protocol_version() to TLS abstraction API
   // SSL_CTX_set_min_proto_version(ctx_, TLS1_2_VERSION);
@@ -13970,11 +14005,10 @@ inline SSLClient::SSLClient(const std::string &host, int port,
   if (!client_cert_path.empty() && !client_key_path.empty()) {
     const char *password =
         private_key_password.empty() ? nullptr : private_key_password.c_str();
-    if (!detail::tls::tls_set_client_cert_file(ctx_, client_cert_path.c_str(),
-                                               client_key_path.c_str(),
-                                               password)) {
+    if (!tls::tls_set_client_cert_file(ctx_, client_cert_path.c_str(),
+                                       client_key_path.c_str(), password)) {
       last_backend_error_ = ERR_get_error();
-      detail::tls::tls_free_context(ctx_);
+      tls::tls_free_context(ctx_);
       ctx_ = nullptr;
     }
   }
@@ -13986,8 +14020,8 @@ inline SSLClient::SSLClient(const std::string &host, int port,
     : ClientImpl(host, port) {
   const char *password =
       private_key_password.empty() ? nullptr : private_key_password.c_str();
-  ctx_ = detail::tls::create_client_context_from_x509(
-      client_cert, client_key, password, last_backend_error_);
+  ctx_ = tls::create_client_context_from_x509(client_cert, client_key, password,
+                                              last_backend_error_);
   detail::split(&host_[0], &host_[host_.size()], '.',
                 [&](const char *b, const char *e) {
                   host_components_.emplace_back(b, e);
@@ -13997,7 +14031,7 @@ inline SSLClient::SSLClient(const std::string &host, int port,
 inline SSLClient::SSLClient(const std::string &host, int port,
                             const PemMemory &pem)
     : ClientImpl(host, port) {
-  ctx_ = detail::tls::tls_create_client_context();
+  ctx_ = tls::tls_create_client_context();
 
   detail::split(&host_[0], &host_[host_.size()], '.',
                 [&](const char *b, const char *e) {
@@ -14005,10 +14039,10 @@ inline SSLClient::SSLClient(const std::string &host, int port,
                 });
 
   if (ctx_ && pem.cert_pem && pem.key_pem) {
-    if (!detail::tls::tls_set_client_cert_pem(ctx_, pem.cert_pem, pem.key_pem,
-                                              pem.private_key_password)) {
-      last_backend_error_ = detail::tls::tls_get_error();
-      detail::tls::tls_free_context(ctx_);
+    if (!tls::tls_set_client_cert_pem(ctx_, pem.cert_pem, pem.key_pem,
+                                      pem.private_key_password)) {
+      last_backend_error_ = tls::tls_get_error();
+      tls::tls_free_context(ctx_);
       ctx_ = nullptr;
     }
   }
@@ -14017,24 +14051,23 @@ inline SSLClient::SSLClient(const std::string &host, int port,
 inline void SSLClient::set_ca_cert_store(void *ca_cert_store) {
   if (ca_cert_store && ctx_) {
     // tls_set_ca_store takes ownership of ca_cert_store
-    detail::tls::tls_set_ca_store(ctx_, ca_cert_store);
+    tls::tls_set_ca_store(ctx_, ca_cert_store);
   } else if (ca_cert_store) {
-    detail::tls::tls_free_ca_store(ca_cert_store);
+    tls::tls_free_ca_store(ca_cert_store);
   }
 }
 
 inline void SSLClient::load_ca_cert_store(const char *ca_cert,
                                           std::size_t size) {
   ca_cert_pem_.assign(ca_cert, size); // Store for redirect transfer
-  set_ca_cert_store(detail::tls::tls_create_ca_store(ca_cert, size));
+  set_ca_cert_store(tls::tls_create_ca_store(ca_cert, size));
 }
 
 inline void
 SSLClient::set_server_certificate_verifier(TlsVerifyCallback verifier) {
   if (!ctx_) { return; }
-  detail::tls::tls_set_verify_callback(
-      ctx_, [verifier](detail::tls::tls_session_t session,
-                       detail::tls::tls_cert_t cert) {
+  tls::tls_set_verify_callback(
+      ctx_, [verifier](tls::tls_session_t session, tls::tls_cert_t cert) {
         return verifier(session, cert);
       });
 }
@@ -14054,18 +14087,18 @@ inline bool SSLClient::load_certs() {
     std::lock_guard<std::mutex> guard(ctx_mutex_);
 
     if (!ca_cert_file_path_.empty()) {
-      if (!detail::tls::tls_load_ca_file(ctx_, ca_cert_file_path_.c_str())) {
+      if (!tls::tls_load_ca_file(ctx_, ca_cert_file_path_.c_str())) {
         last_backend_error_ = ERR_get_error();
         ret = false;
       }
     } else if (!ca_cert_dir_path_.empty()) {
-      if (!detail::tls::tls_load_ca_dir(ctx_, ca_cert_dir_path_.c_str())) {
+      if (!tls::tls_load_ca_dir(ctx_, ca_cert_dir_path_.c_str())) {
         last_backend_error_ = ERR_get_error();
         ret = false;
       }
     } else {
       // Load system certificates
-      if (!detail::tls::tls_load_system_certs(ctx_)) {
+      if (!tls::tls_load_system_certs(ctx_)) {
         last_backend_error_ = ERR_get_error();
         // Ignore error and continue - some systems may not have certs
       }
@@ -14076,7 +14109,7 @@ inline bool SSLClient::load_certs() {
 }
 
 inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
-  using namespace detail::tls;
+  using namespace tls;
 
   // Load CA certificates if server verification is enabled
   if (server_certificate_verification_) {
@@ -14292,7 +14325,6 @@ inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
  */
 
 #ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
-namespace detail {
 namespace crypto {
 
 inline size_t hash_size(HashAlgorithm algo) {
@@ -14383,42 +14415,6 @@ inline std::string hash(HashAlgorithm algo, const std::string &data) {
 
 namespace tls {
 
-// Mbed TLS context wrapper (holds config, entropy, DRBG, CA chain, own
-// cert/key)
-struct MbedTlsContext {
-  mbedtls_ssl_config conf;
-  mbedtls_entropy_context entropy;
-  mbedtls_ctr_drbg_context ctr_drbg;
-  mbedtls_x509_crt ca_chain;
-  mbedtls_x509_crt own_cert;
-  mbedtls_pk_context own_key;
-  bool is_server = false;
-  bool verify_client = false;
-  bool has_verify_callback = false;
-
-  MbedTlsContext() {
-    mbedtls_ssl_config_init(&conf);
-    mbedtls_entropy_init(&entropy);
-    mbedtls_ctr_drbg_init(&ctr_drbg);
-    mbedtls_x509_crt_init(&ca_chain);
-    mbedtls_x509_crt_init(&own_cert);
-    mbedtls_pk_init(&own_key);
-  }
-
-  ~MbedTlsContext() {
-    mbedtls_pk_free(&own_key);
-    mbedtls_x509_crt_free(&own_cert);
-    mbedtls_x509_crt_free(&ca_chain);
-    mbedtls_ctr_drbg_free(&ctr_drbg);
-    mbedtls_entropy_free(&entropy);
-    mbedtls_ssl_config_free(&conf);
-  }
-
-  // Non-copyable
-  MbedTlsContext(const MbedTlsContext &) = delete;
-  MbedTlsContext &operator=(const MbedTlsContext &) = delete;
-};
-
 // Mbed TLS session wrapper
 struct MbedTlsSession {
   mbedtls_ssl_context ssl;
@@ -14519,7 +14515,7 @@ inline int mbedtls_net_recv_cb(void *ctx, unsigned char *buf, size_t len) {
 }
 
 inline tls_ctx_t tls_create_client_context() {
-  auto ctx = new (std::nothrow) MbedTlsContext();
+  auto ctx = new (std::nothrow) httplib::MbedTlsContext();
   if (!ctx) { return nullptr; }
 
   ctx->is_server = false;
@@ -14567,7 +14563,7 @@ inline int mbedtls_sni_callback(void *p_ctx, mbedtls_ssl_context *ssl,
                                 const unsigned char *name, size_t name_len);
 
 inline tls_ctx_t tls_create_server_context() {
-  auto ctx = new (std::nothrow) MbedTlsContext();
+  auto ctx = new (std::nothrow) httplib::MbedTlsContext();
   if (!ctx) { return nullptr; }
 
   ctx->is_server = true;
@@ -14614,12 +14610,12 @@ inline tls_ctx_t tls_create_server_context() {
 }
 
 inline void tls_free_context(tls_ctx_t ctx) {
-  if (ctx) { delete static_cast<MbedTlsContext *>(ctx); }
+  if (ctx) { delete static_cast<httplib::MbedTlsContext *>(ctx); }
 }
 
 inline bool tls_set_min_version(tls_ctx_t ctx, int version) {
   if (!ctx) { return false; }
-  auto mctx = static_cast<MbedTlsContext *>(ctx);
+  auto mctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   // Map OpenSSL-style version constants to Mbed TLS
   // TLS1_2_VERSION = 0x0303, TLS1_3_VERSION = 0x0304
@@ -14650,7 +14646,7 @@ inline bool tls_set_min_version(tls_ctx_t ctx, int version) {
 
 inline bool tls_load_ca_pem(tls_ctx_t ctx, const char *pem, size_t len) {
   if (!ctx || !pem) { return false; }
-  auto mctx = static_cast<MbedTlsContext *>(ctx);
+  auto mctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   // mbedtls_x509_crt_parse expects null-terminated string for PEM
   // Add null terminator if not present
@@ -14669,7 +14665,7 @@ inline bool tls_load_ca_pem(tls_ctx_t ctx, const char *pem, size_t len) {
 
 inline bool tls_load_ca_file(tls_ctx_t ctx, const char *file_path) {
   if (!ctx || !file_path) { return false; }
-  auto mctx = static_cast<MbedTlsContext *>(ctx);
+  auto mctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   int ret = mbedtls_x509_crt_parse_file(&mctx->ca_chain, file_path);
   if (ret != 0) {
@@ -14683,7 +14679,7 @@ inline bool tls_load_ca_file(tls_ctx_t ctx, const char *file_path) {
 
 inline bool tls_load_ca_dir(tls_ctx_t ctx, const char *dir_path) {
   if (!ctx || !dir_path) { return false; }
-  auto mctx = static_cast<MbedTlsContext *>(ctx);
+  auto mctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   int ret = mbedtls_x509_crt_parse_path(&mctx->ca_chain, dir_path);
   if (ret < 0) { // Returns number of certs on success, negative on error
@@ -14697,7 +14693,7 @@ inline bool tls_load_ca_dir(tls_ctx_t ctx, const char *dir_path) {
 
 inline bool tls_load_system_certs(tls_ctx_t ctx) {
   if (!ctx) { return false; }
-  auto mctx = static_cast<MbedTlsContext *>(ctx);
+  auto mctx = static_cast<httplib::MbedTlsContext *>(ctx);
   bool loaded = false;
 
 #ifdef _WIN32
@@ -14776,7 +14772,7 @@ inline bool tls_load_system_certs(tls_ctx_t ctx) {
 inline bool tls_set_client_cert_pem(tls_ctx_t ctx, const char *cert,
                                     const char *key, const char *password) {
   if (!ctx || !cert || !key) { return false; }
-  auto mctx = static_cast<MbedTlsContext *>(ctx);
+  auto mctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   // Parse certificate
   std::string cert_str(cert);
@@ -14823,7 +14819,7 @@ inline bool tls_set_client_cert_file(tls_ctx_t ctx, const char *cert_path,
                                      const char *key_path,
                                      const char *password) {
   if (!ctx || !cert_path || !key_path) { return false; }
-  auto mctx = static_cast<MbedTlsContext *>(ctx);
+  auto mctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   // Parse certificate file
   int ret = mbedtls_x509_crt_parse_file(&mctx->own_cert, cert_path);
@@ -14883,7 +14879,7 @@ inline bool tls_set_client_ca_file(tls_ctx_t ctx, const char *ca_file,
 
 inline void tls_set_verify_client(tls_ctx_t ctx, bool require) {
   if (!ctx) { return; }
-  auto mctx = static_cast<MbedTlsContext *>(ctx);
+  auto mctx = static_cast<httplib::MbedTlsContext *>(ctx);
   mctx->verify_client = require;
   if (require) {
     mbedtls_ssl_conf_authmode(&mctx->conf, MBEDTLS_SSL_VERIFY_REQUIRED);
@@ -14923,7 +14919,7 @@ inline int mbedtls_sni_callback(void *p_ctx, mbedtls_ssl_context *ssl,
 
 inline tls_session_t tls_create_session(tls_ctx_t ctx, socket_t sock) {
   if (!ctx || sock == INVALID_SOCKET) { return nullptr; }
-  auto mctx = static_cast<MbedTlsContext *>(ctx);
+  auto mctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   auto session = new (std::nothrow) MbedTlsSession();
   if (!session) { return nullptr; }
@@ -15013,15 +15009,20 @@ inline bool tls_connect_nonblocking(tls_session_t session, socket_t sock,
   auto msession = static_cast<MbedTlsSession *>(session);
 
   // Set socket to non-blocking mode
-  set_nonblocking(sock, true);
-  auto cleanup = scope_exit([&]() { set_nonblocking(sock, false); });
+  detail::set_nonblocking(sock, true);
+  auto cleanup =
+      detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
 
   int ret;
   while ((ret = mbedtls_ssl_handshake(&msession->ssl)) != 0) {
     if (ret == MBEDTLS_ERR_SSL_WANT_READ) {
-      if (select_read(sock, timeout_sec, timeout_usec) > 0) { continue; }
+      if (detail::select_read(sock, timeout_sec, timeout_usec) > 0) {
+        continue;
+      }
     } else if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
-      if (select_write(sock, timeout_sec, timeout_usec) > 0) { continue; }
+      if (detail::select_write(sock, timeout_sec, timeout_usec) > 0) {
+        continue;
+      }
     }
 
     // Error or timeout
@@ -15142,8 +15143,9 @@ inline bool tls_is_peer_closed(tls_session_t session, socket_t sock) {
   if (mbedtls_ssl_get_bytes_avail(&msession->ssl) > 0) { return false; }
 
   // Set socket to non-blocking to avoid blocking on read
-  set_nonblocking(sock, true);
-  auto cleanup = scope_exit([&]() { set_nonblocking(sock, false); });
+  detail::set_nonblocking(sock, true);
+  auto cleanup =
+      detail::scope_exit([&]() { detail::set_nonblocking(sock, false); });
 
   // Try a 1-byte read to check connection status
   // Note: This will consume the byte if data is available, but for the
@@ -15495,7 +15497,7 @@ inline void tls_free_ca_store(tls_ca_store_t store) {
 
 inline bool tls_set_ca_store(tls_ctx_t ctx, tls_ca_store_t store) {
   if (!ctx || !store) { return false; }
-  auto *mbed_ctx = static_cast<MbedTlsContext *>(ctx);
+  auto *mbed_ctx = static_cast<httplib::MbedTlsContext *>(ctx);
   auto *ca_chain = static_cast<mbedtls_x509_crt *>(store);
 
   // Free existing CA chain
@@ -15520,7 +15522,7 @@ inline bool tls_set_ca_store(tls_ctx_t ctx, tls_ca_store_t store) {
 inline size_t tls_get_ca_certs(tls_ctx_t ctx, std::vector<tls_cert_t> &certs) {
   certs.clear();
   if (!ctx) { return 0; }
-  auto *mbed_ctx = static_cast<MbedTlsContext *>(ctx);
+  auto *mbed_ctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   // Iterate through the CA chain
   mbedtls_x509_crt *cert = &mbed_ctx->ca_chain;
@@ -15543,7 +15545,7 @@ inline size_t tls_get_ca_certs(tls_ctx_t ctx, std::vector<tls_cert_t> &certs) {
 inline std::vector<std::string> tls_get_ca_names(tls_ctx_t ctx) {
   std::vector<std::string> names;
   if (!ctx) { return names; }
-  auto *mbed_ctx = static_cast<MbedTlsContext *>(ctx);
+  auto *mbed_ctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   // Iterate through the CA chain
   mbedtls_x509_crt *cert = &mbed_ctx->ca_chain;
@@ -15559,7 +15561,7 @@ inline std::vector<std::string> tls_get_ca_names(tls_ctx_t ctx) {
 inline bool tls_update_server_cert(tls_ctx_t ctx, const char *cert_pem,
                                    const char *key_pem, const char *password) {
   if (!ctx || !cert_pem || !key_pem) { return false; }
-  auto *mbed_ctx = static_cast<MbedTlsContext *>(ctx);
+  auto *mbed_ctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   // Free existing certificate and key
   mbedtls_x509_crt_free(&mbed_ctx->own_cert);
@@ -15609,7 +15611,7 @@ inline bool tls_update_server_cert(tls_ctx_t ctx, const char *cert_pem,
 
 inline bool tls_update_server_client_ca(tls_ctx_t ctx, const char *ca_pem) {
   if (!ctx || !ca_pem) { return false; }
-  auto *mbed_ctx = static_cast<MbedTlsContext *>(ctx);
+  auto *mbed_ctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   // Free existing CA chain
   mbedtls_x509_crt_free(&mbed_ctx->ca_chain);
@@ -15658,7 +15660,7 @@ inline int mbedtls_verify_callback(void *data, mbedtls_x509_crt *crt, int depth,
 
 inline bool tls_set_verify_callback(tls_ctx_t ctx, TlsVerifyCallback callback) {
   if (!ctx) { return false; }
-  auto *mbed_ctx = static_cast<MbedTlsContext *>(ctx);
+  auto *mbed_ctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   get_mbedtls_verify_callback() = std::move(callback);
   mbed_ctx->has_verify_callback =
@@ -15724,7 +15726,7 @@ inline int mbedtls_verify_callback_ex(void *data, mbedtls_x509_crt *crt,
 inline bool tls_set_verify_callback_ex(tls_ctx_t ctx,
                                        TlsVerifyCallbackEx callback) {
   if (!ctx) { return false; }
-  auto *mbed_ctx = static_cast<MbedTlsContext *>(ctx);
+  auto *mbed_ctx = static_cast<httplib::MbedTlsContext *>(ctx);
 
   get_mbedtls_verify_callback_ex() = std::move(callback);
   mbed_ctx->has_verify_callback =
@@ -15762,7 +15764,25 @@ inline std::string tls_verify_error_string(long error_code) {
 }
 
 } // namespace tls
-} // namespace detail
+
+// MbedTlsContext constructor/destructor implementations
+inline MbedTlsContext::MbedTlsContext() {
+  mbedtls_ssl_config_init(&conf);
+  mbedtls_entropy_init(&entropy);
+  mbedtls_ctr_drbg_init(&ctr_drbg);
+  mbedtls_x509_crt_init(&ca_chain);
+  mbedtls_x509_crt_init(&own_cert);
+  mbedtls_pk_init(&own_key);
+}
+
+inline MbedTlsContext::~MbedTlsContext() {
+  mbedtls_pk_free(&own_key);
+  mbedtls_x509_crt_free(&own_cert);
+  mbedtls_x509_crt_free(&ca_chain);
+  mbedtls_ctr_drbg_free(&ctr_drbg);
+  mbedtls_entropy_free(&entropy);
+  mbedtls_ssl_config_free(&conf);
+}
 
 inline SSLClient::SSLClient(const std::string &host)
     : SSLClient(host, 443, std::string(), std::string()) {}
@@ -15775,7 +15795,7 @@ inline SSLClient::SSLClient(const std::string &host, int port,
                             const std::string &client_key_path,
                             const std::string &private_key_password)
     : ClientImpl(host, port, client_cert_path, client_key_path) {
-  using namespace detail::tls;
+  using namespace tls;
 
   ctx_ = tls_create_client_context();
   if (!ctx_) { return; }
@@ -15803,7 +15823,7 @@ inline SSLClient::SSLClient(const std::string &host, int port,
 inline SSLClient::SSLClient(const std::string &host, int port,
                             const PemMemory &pem)
     : ClientImpl(host, port) {
-  using namespace detail::tls;
+  using namespace tls;
 
   ctx_ = tls_create_client_context();
   if (!ctx_) { return; }
@@ -15829,9 +15849,9 @@ inline SSLClient::SSLClient(const std::string &host, int port,
 inline void SSLClient::set_ca_cert_store(void *ca_cert_store) {
   if (ca_cert_store && ctx_) {
     // tls_set_ca_store takes ownership of ca_cert_store
-    detail::tls::tls_set_ca_store(ctx_, ca_cert_store);
+    tls::tls_set_ca_store(ctx_, ca_cert_store);
   } else if (ca_cert_store) {
-    detail::tls::tls_free_ca_store(ca_cert_store);
+    tls::tls_free_ca_store(ca_cert_store);
   }
 }
 
@@ -15839,22 +15859,21 @@ inline void SSLClient::load_ca_cert_store(const char *ca_cert,
                                           std::size_t size) {
   if (ctx_ && ca_cert && size > 0) {
     ca_cert_pem_.assign(ca_cert, size); // Store for redirect transfer
-    detail::tls::tls_load_ca_pem(ctx_, ca_cert, size);
+    tls::tls_load_ca_pem(ctx_, ca_cert, size);
   }
 }
 
 inline void
 SSLClient::set_server_certificate_verifier(TlsVerifyCallback verifier) {
   if (!ctx_) { return; }
-  detail::tls::tls_set_verify_callback(
-      ctx_, [verifier](detail::tls::tls_session_t session,
-                       detail::tls::tls_cert_t cert) {
+  tls::tls_set_verify_callback(
+      ctx_, [verifier](tls::tls_session_t session, tls::tls_cert_t cert) {
         return verifier(session, cert);
       });
 }
 
 inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
-  using namespace detail::tls;
+  using namespace tls;
 
   // Load system certificates if no CA certs were explicitly set
   if (ca_cert_file_path_.empty() && ca_cert_dir_path_.empty()) {
@@ -15969,8 +15988,8 @@ inline bool SSLClient::initialize_ssl(Socket &socket, Error &error) {
 inline ClientConnection::~ClientConnection() {
 #ifdef CPPHTTPLIB_SSL_ENABLED
   if (session) {
-    detail::tls::tls_shutdown(session, true);
-    detail::tls::tls_free_session(session);
+    tls::tls_shutdown(session, true);
+    tls::tls_free_session(session);
     session = nullptr;
   }
 #endif
@@ -16160,7 +16179,7 @@ inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
                             const char *client_ca_cert_file_path,
                             const char *client_ca_cert_dir_path,
                             const char *private_key_password) {
-  using namespace detail::tls;
+  using namespace tls;
 
   ctx_ = tls_create_server_context();
   if (!ctx_) { return; }
@@ -16189,7 +16208,7 @@ inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
 }
 
 inline SSLServer::SSLServer(const PemMemory &pem) {
-  using namespace detail::tls;
+  using namespace tls;
   ctx_ = tls_create_server_context();
   if (ctx_) {
     if (!tls_set_server_cert_pem(ctx_, pem.cert_pem, pem.key_pem,
@@ -16211,7 +16230,7 @@ inline SSLServer::SSLServer(const PemMemory &pem) {
 
 inline SSLServer::SSLServer(
     const std::function<bool(void *ctx)> &setup_callback) {
-  using namespace detail::tls;
+  using namespace tls;
   ctx_ = tls_create_server_context();
   if (ctx_) {
     if (!setup_callback(ctx_)) {
@@ -16222,13 +16241,13 @@ inline SSLServer::SSLServer(
 }
 
 inline SSLServer::~SSLServer() {
-  if (ctx_) { detail::tls::tls_free_context(ctx_); }
+  if (ctx_) { tls::tls_free_context(ctx_); }
 }
 
 inline bool SSLServer::is_valid() const { return ctx_ != nullptr; }
 
 inline bool SSLServer::process_and_close_socket(socket_t sock) {
-  using namespace detail::tls;
+  using namespace tls;
 
   // Create TLS session with mutex protection
   tls_session_t session = nullptr;
@@ -16298,7 +16317,7 @@ inline bool SSLServer::process_and_close_socket(socket_t sock) {
 }
 
 inline SSLClient::~SSLClient() {
-  if (ctx_) { detail::tls::tls_free_context(ctx_); }
+  if (ctx_) { tls::tls_free_context(ctx_); }
   // Make sure to shut down SSL since shutdown_ssl will resolve to the
   // base function rather than the derived function once we get to the
   // base class destructor, and won't free the SSL (causing a leak).
@@ -16318,10 +16337,10 @@ inline void SSLClient::shutdown_ssl_impl(Socket &socket,
     return;
   }
   if (socket.ssl) {
-    detail::tls::tls_shutdown(socket.ssl, shutdown_gracefully);
+    tls::tls_shutdown(socket.ssl, shutdown_gracefully);
     {
       std::lock_guard<std::mutex> guard(ctx_mutex_);
-      detail::tls::tls_free_session(socket.ssl);
+      tls::tls_free_session(socket.ssl);
     }
     socket.ssl = nullptr;
   }
@@ -16452,13 +16471,13 @@ inline bool SSLServer::update_certs_pem(const char *cert_pem,
                                         const char *password) {
   if (!ctx_) { return false; }
   std::lock_guard<std::mutex> guard(ctx_mutex_);
-  return detail::tls::tls_update_server_cert(ctx_, cert_pem, key_pem, password);
+  return tls::tls_update_server_cert(ctx_, cert_pem, key_pem, password);
 }
 
 inline bool SSLServer::update_client_ca_pem(const char *ca_pem) {
   if (!ctx_) { return false; }
   std::lock_guard<std::mutex> guard(ctx_mutex_);
-  return detail::tls::tls_update_server_client_ca(ctx_, ca_pem);
+  return tls::tls_update_server_client_ca(ctx_, ca_pem);
 }
 #endif // CPPHTTPLIB_SSL_ENABLED
 
@@ -17150,12 +17169,12 @@ inline void Client::set_ca_cert_store(void *ca_cert_store) {
   if (is_ssl_) {
     static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
   } else if (ca_cert_store) {
-    detail::tls::tls_free_ca_store(ca_cert_store);
+    tls::tls_free_ca_store(ca_cert_store);
   }
 }
 
 inline void Client::load_ca_cert_store(const char *ca_cert, std::size_t size) {
-  set_ca_cert_store(detail::tls::tls_create_ca_store(ca_cert, size));
+  set_ca_cert_store(tls::tls_create_ca_store(ca_cert, size));
 }
 
 inline void

+ 16 - 17
test/test.cc

@@ -6715,7 +6715,7 @@ TEST_F(ServerTest, SendLargeBodyAfterRequestLineError) {
     ASSERT_TRUE(res);
     EXPECT_EQ(StatusCode::OK_200, res->status);
     EXPECT_EQ("Hello World!", res->body);
-    EXPECT_LE(elapsed, 100);
+    EXPECT_LE(elapsed, 500);
   }
 }
 
@@ -7828,7 +7828,7 @@ TEST(KeepAliveTest, SSLClientReconnectionPost) {
 
 #ifdef CPPHTTPLIB_SSL_ENABLED
 TEST(SNI_AutoDetectionTest, SNI_Logic) {
-  using namespace httplib::detail::tls;
+  using namespace httplib::tls;
 
   {
     SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
@@ -8890,7 +8890,7 @@ TEST(SSLClientTest, Issue2251_SwappedClientCertAndKey) {
 // Backend-agnostic test using abstract TLS API
 // Tests cert/key mismatch detection at the TLS context level
 TEST(TlsApiTest, ClientCertKeyMismatch) {
-  using namespace httplib::detail::tls;
+  using namespace httplib::tls;
 
   // Create client context
   auto ctx = tls_create_client_context();
@@ -8942,7 +8942,7 @@ void ClientCertPresent(
     const std::string &client_cert_file,
     const std::string &client_private_key_file,
     const std::string &client_encrypted_private_key_pass = std::string()) {
-  using namespace httplib::detail::tls;
+  using namespace httplib::tls;
 
   SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE, CLIENT_CA_CERT_FILE,
                 CLIENT_CA_CERT_DIR);
@@ -9177,7 +9177,7 @@ TEST(SSLClientServerTest, SSLConnectTimeout) {
 // Uses void* callback constructor and detail::tls API
 #ifdef CPPHTTPLIB_SSL_ENABLED
 TEST(SSLClientServerTest, CustomizeServerSSLCtxGeneric) {
-  using namespace httplib::detail::tls;
+  using namespace httplib::tls;
 
   auto setup_callback = [](void *ctx) {
     // Load server certificate and key using backend-agnostic API
@@ -9235,7 +9235,7 @@ TEST(SSLClientServerTest, CustomizeServerSSLCtxGeneric) {
 // Tests that the void* callback can customize TLS settings via MbedTlsContext
 #ifdef CPPHTTPLIB_MBEDTLS_SUPPORT
 TEST(SSLClientServerTest, CustomizeServerSSLCtxMbedTLS) {
-  using namespace httplib::detail::tls;
+  using namespace httplib::tls;
 
   // Track if callback was invoked
   bool callback_invoked = false;
@@ -9246,8 +9246,7 @@ TEST(SSLClientServerTest, CustomizeServerSSLCtxMbedTLS) {
     callback_invoked = true;
 
     // Cast to MbedTlsContext* to access the ssl config
-    auto *mbedtls_ctx =
-        static_cast<httplib::detail::tls::MbedTlsContext *>(ctx);
+    auto *mbedtls_ctx = static_cast<httplib::MbedTlsContext *>(ctx);
     mbedtls_ssl_config *conf = &mbedtls_ctx->conf;
 
     // Use static variables to hold certificate data (simplified for test)
@@ -9372,7 +9371,7 @@ TEST(SSLClientServerTest, ClientCAListSentToClient) {
 // backends
 #ifdef CPPHTTPLIB_SSL_ENABLED
 TEST(SSLClientServerTest, ClientCAListSetInContext) {
-  using namespace httplib::detail::tls;
+  using namespace httplib::tls;
 
   // Test that when client CA cert file is provided,
   // the server properly requests and validates client certificates
@@ -9425,7 +9424,7 @@ TEST(SSLClientServerTest, ClientCAListSetInContext) {
 
 #ifdef CPPHTTPLIB_SSL_ENABLED
 TEST(TlsCertIntrospectionTest, GetCertSANs) {
-  using namespace httplib::detail::tls;
+  using namespace httplib::tls;
 
   SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
   ASSERT_TRUE(svr.is_valid());
@@ -9474,7 +9473,7 @@ TEST(TlsCertIntrospectionTest, GetCertSANs) {
 }
 
 TEST(TlsCertIntrospectionTest, GetCertValidity) {
-  using namespace httplib::detail::tls;
+  using namespace httplib::tls;
 
   SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
   ASSERT_TRUE(svr.is_valid());
@@ -9518,7 +9517,7 @@ TEST(TlsCertIntrospectionTest, GetCertValidity) {
 }
 
 TEST(TlsCertIntrospectionTest, GetCertSerial) {
-  using namespace httplib::detail::tls;
+  using namespace httplib::tls;
 
   SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
   ASSERT_TRUE(svr.is_valid());
@@ -9602,7 +9601,7 @@ TEST(SSLClientServerTest, ClientCAListLoadErrorRecorded) {
 
 #ifdef CPPHTTPLIB_SSL_ENABLED
 TEST(TlsVerifyCallbackExTest, ExtendedCallback) {
-  using namespace httplib::detail::tls;
+  using namespace httplib::tls;
 
   SSLServer svr(SERVER_CERT_FILE, SERVER_PRIVATE_KEY_FILE);
   ASSERT_TRUE(svr.is_valid());
@@ -9648,7 +9647,7 @@ TEST(TlsVerifyCallbackExTest, ExtendedCallback) {
 }
 
 TEST(TlsVerifyErrorTest, GetVerifyErrorString) {
-  using namespace httplib::detail::tls;
+  using namespace httplib::tls;
 
   // Test that tls_verify_error_string returns empty for success
   std::string success_str = tls_verify_error_string(0);
@@ -9664,7 +9663,7 @@ TEST(TlsVerifyErrorTest, GetVerifyErrorString) {
 
 #ifdef CPPHTTPLIB_SSL_ENABLED
 TEST(CryptoHashTest, BasicHashing) {
-  namespace crypto = httplib::detail::crypto;
+  namespace crypto = httplib::crypto;
 
   // Test MD5
   std::string md5_result = crypto::hash(crypto::HashAlgorithm::MD5, "hello");
@@ -9686,7 +9685,7 @@ TEST(CryptoHashTest, BasicHashing) {
 }
 
 TEST(CryptoHashTest, HashSize) {
-  namespace crypto = httplib::detail::crypto;
+  namespace crypto = httplib::crypto;
 
   EXPECT_EQ(16u, crypto::hash_size(crypto::HashAlgorithm::MD5));
   EXPECT_EQ(20u, crypto::hash_size(crypto::HashAlgorithm::SHA1));
@@ -9696,7 +9695,7 @@ TEST(CryptoHashTest, HashSize) {
 }
 
 TEST(CryptoHashTest, HashRaw) {
-  namespace crypto = httplib::detail::crypto;
+  namespace crypto = httplib::crypto;
 
   std::vector<uint8_t> digest;
   bool result =