Explorar o código

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.
yhirose hai 2 días
pai
achega
162b87f325
Modificáronse 2 ficheiros con 41 adicións e 3 borrados
  1. 8 3
      httplib.h
  2. 33 0
      test/test.cc

+ 8 - 3
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);

+ 33 - 0
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<bool> closed_by_client{false};
   auto server_thread = std::thread([&] {