diff --git a/httplib.h b/httplib.h index f1da3510..e9066a41 100644 --- a/httplib.h +++ b/httplib.h @@ -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); diff --git a/test/test.cc b/test/test.cc index eefa75fe..adbf56e2 100644 --- a/test/test.cc +++ b/test/test.cc @@ -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 closed_by_client{false}; auto server_thread = std::thread([&] {