Fail a refused CONNECT instead of returning the proxy's answer

A non-200 answer to CONNECT was returned as the response, so a proxy, or
anyone on the plain leg to it, could answer an HTTPS request as if the
origin had. Fail the request with Error::ProxyConnection for anything
but a 4xx or 5xx refusal.
This commit is contained in:
yhirose committed 2026-10-16 16:39:00 -04:00
1 parent ce7ecb633b
commit 162b87f325
2 files changed
+41 -3

No files matched your search

+8 -3
View File
@@ -18776,12 +18776,17 @@ inline bool SSLClient::connect_with_proxy(
}
// If status code is not 200, proxy request is failed.
// Set error to ProxyConnection and return proxy response
// as the response of the request
// Set error to ProxyConnection. A refusal such as 407 is returned as the
// response of the request, so the caller sees why; any other status would
// read as the answer of the origin, which it never reached.
if (proxy_res.status != StatusCode::OK_200) {
error = Error::ProxyConnection;
output_error_log(error, nullptr);
res = std::move(proxy_res);
if (400 <= proxy_res.status && proxy_res.status < 600) {
res = std::move(proxy_res);
} else {
success = false;
}
// Thread-safe to close everything because we are assuming there are
// no requests in flight
shutdown_ssl(socket, true);
+33
View File
@@ -13595,6 +13595,39 @@ TEST(InterimResponseTest, SkippedWithTheirHeaderFields) {
}
}
#ifdef CPPHTTPLIB_SSL_ENABLED
TEST(ProxyConnectTest, OnlyARefusalIsPassedOn) {
// Anything but 200 or a refusal would read as the origin's own response.
{
std::thread t([] {
serve_raw_once(PORT, "HTTP/1.1 201 Created\r\n"
"Set-Cookie: session=from-proxy\r\n"
"Content-Length: 0\r\n\r\n");
});
std::this_thread::sleep_for(std::chrono::milliseconds(100));
SSLClient cli("example.com", 443);
cli.set_proxy("127.0.0.1", PORT);
auto res = cli.Get("/");
t.join();
EXPECT_FALSE(res);
EXPECT_EQ(Error::ProxyConnection, res.error());
}
{
std::thread t([] {
serve_raw_once(PORT, "HTTP/1.1 407 Proxy Authentication Required\r\n"
"Content-Length: 0\r\n\r\n");
});
std::this_thread::sleep_for(std::chrono::milliseconds(100));
SSLClient cli("example.com", 443);
cli.set_proxy("127.0.0.1", PORT);
auto res = cli.Get("/");
t.join();
ASSERT_TRUE(res);
EXPECT_EQ(StatusCode::ProxyAuthenticationRequired_407, res->status);
}
}
#endif
TEST(ExpectContinueTest, EarlyFinalResponseClosesConnection) {
std::atomic<bool> closed_by_client{false};
auto server_thread = std::thread([&] {