Enhance Windows certificate verification with additional tests and timeout handling

This commit is contained in:
yhirose
2026-01-03 00:55:07 -05:00
parent 5fd1d1fa63
commit 3271060bc4
2 changed files with 78 additions and 76 deletions

View File

@@ -7278,12 +7278,14 @@ inline bool verify_cert_with_windows_schannel(X509 *server_cert,
// Setup chain parameters
CERT_CHAIN_PARA chain_para = {};
chain_para.cbSize = sizeof(chain_para);
#if defined(_WIN32) && _WIN32_WINNT >= 0x0600
// dwUrlRetrievalTimeout is available on Windows Vista and later
chain_para.dwUrlRetrievalTimeout = static_cast<DWORD>(timeout_sec * 1000);
#else
// Note: dwUrlRetrievalTimeout is not available in all Windows SDK versions.
// In newer SDKs (10.0.26100+), this field doesn't exist in CERT_CHAIN_PARA.
// We cannot reliably set the timeout across all SDK versions, so we rely on
// the Windows API default timeout (typically 10-15 seconds for CAPI
// operations). The timeout_sec parameter is kept for API compatibility and
// future use.
(void)timeout_sec;
#endif
// Build certificate chain with revocation checking
PCCERT_CHAIN_CONTEXT chain_context = nullptr;

View File

@@ -142,6 +142,77 @@ void performance_test(const char *host) {
<< "ms (Issue #1777). Timings: " << timings_str.str();
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
#ifdef _WIN32
// Windows Certificate Verification Tests
TEST(SSLClientTest, WindowsCertificateVerification_DefaultEnabled) {
// Test 1: Default behavior (Windows verification enabled)
SSLClient cli("www.google.com", 443);
cli.enable_server_certificate_verification(true);
auto res = cli.Get("/");
// Should succeed or fail gracefully (may fail due to network)
// The important thing is that Windows verification was enabled
if (res) { EXPECT_NE(StatusCode::InternalServerError_500, res->status); }
}
TEST(SSLClientTest, WindowsCertificateVerification_DisableWindows) {
// Test 2: Disable Windows verification (OpenSSL only)
SSLClient cli("www.google.com", 443);
cli.enable_server_certificate_verification(true);
cli.enable_windows_certificate_verification(false);
auto res = cli.Get("/");
// Should work with OpenSSL verification only
if (res) { EXPECT_NE(StatusCode::InternalServerError_500, res->status); }
}
TEST(SSLClientTest, WindowsCertificateVerification_CustomTimeout) {
// Test 3: Custom timeout
SSLClient cli("www.google.com", 443);
cli.enable_server_certificate_verification(true);
cli.set_windows_certificate_verification_timeout(2);
auto res = cli.Get("/");
// Should complete within reasonable time
if (res) { EXPECT_NE(StatusCode::InternalServerError_500, res->status); }
}
TEST(SSLClientTest, WindowsCertificateVerification_InvalidCertificate) {
// Test 4: Invalid certificate (should fail)
SSLClient cli("self-signed.badssl.com", 443);
cli.enable_server_certificate_verification(true);
auto res = cli.Get("/");
// Should fail due to invalid certificate
EXPECT_FALSE(res);
EXPECT_EQ(Error::SSLServerVerification, res.error());
}
TEST(SSLClientTest, WindowsCertificateVerification_CachingBehavior) {
// Test 5: Multiple connections (test caching)
SSLClient cli("www.google.com", 443);
cli.enable_server_certificate_verification(true);
// First connection
auto res1 = cli.Get("/");
auto start2 = std::chrono::high_resolution_clock::now();
// Second connection (should use cache)
auto res2 = cli.Get("/");
auto end2 = std::chrono::high_resolution_clock::now();
auto duration2 =
std::chrono::duration_cast<std::chrono::milliseconds>(end2 - start2);
// Second connection should be fast (cache hit)
if (res2) {
EXPECT_NE(StatusCode::InternalServerError_500, res2->status);
// Cache should make second connection faster (very loose bound)
EXPECT_LT(duration2.count(), 5000); // Within 5 seconds
}
}
#endif
#endif
TEST(BenchmarkTest, localhost) { performance_test("localhost"); }
TEST(BenchmarkTest, v6) { performance_test("::1"); }
@@ -14119,74 +14190,3 @@ TEST(Issue2318Test, EmptyHostString) {
EXPECT_EQ(httplib::Error::Connection, res.error());
}
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
#ifdef _WIN32
// Windows Certificate Verification Tests
TEST(SSLClientTest, WindowsCertificateVerification_DefaultEnabled) {
// Test 1: Default behavior (Windows verification enabled)
SSLClient cli("www.google.com", 443);
cli.enable_server_certificate_verification(true);
auto res = cli.Get("/");
// Should succeed or fail gracefully (may fail due to network)
// The important thing is that Windows verification was enabled
if (res) { EXPECT_NE(StatusCode::InternalServerError_500, res->status); }
}
TEST(SSLClientTest, WindowsCertificateVerification_DisableWindows) {
// Test 2: Disable Windows verification (OpenSSL only)
SSLClient cli("www.google.com", 443);
cli.enable_server_certificate_verification(true);
cli.enable_windows_certificate_verification(false);
auto res = cli.Get("/");
// Should work with OpenSSL verification only
if (res) { EXPECT_NE(StatusCode::InternalServerError_500, res->status); }
}
TEST(SSLClientTest, WindowsCertificateVerification_CustomTimeout) {
// Test 3: Custom timeout
SSLClient cli("www.google.com", 443);
cli.enable_server_certificate_verification(true);
cli.set_windows_certificate_verification_timeout(2);
auto res = cli.Get("/");
// Should complete within reasonable time
if (res) { EXPECT_NE(StatusCode::InternalServerError_500, res->status); }
}
TEST(SSLClientTest, WindowsCertificateVerification_InvalidCertificate) {
// Test 4: Invalid certificate (should fail)
SSLClient cli("self-signed.badssl.com", 443);
cli.enable_server_certificate_verification(true);
auto res = cli.Get("/");
// Should fail due to invalid certificate
EXPECT_FALSE(res);
EXPECT_EQ(Error::SSLServerVerification, res.error());
}
TEST(SSLClientTest, WindowsCertificateVerification_CachingBehavior) {
// Test 5: Multiple connections (test caching)
SSLClient cli("www.google.com", 443);
cli.enable_server_certificate_verification(true);
// First connection
auto res1 = cli.Get("/");
auto start2 = std::chrono::high_resolution_clock::now();
// Second connection (should use cache)
auto res2 = cli.Get("/");
auto end2 = std::chrono::high_resolution_clock::now();
auto duration2 =
std::chrono::duration_cast<std::chrono::milliseconds>(end2 - start2);
// Second connection should be fast (cache hit)
if (res2) {
EXPECT_NE(StatusCode::InternalServerError_500, res2->status);
// Cache should make second connection faster (very loose bound)
EXPECT_LT(duration2.count(), 5000); // Within 5 seconds
}
}
#endif
#endif