فهرست منبع

Send proxy Basic authentication when one credential is empty

Proxy-Authorization was added only when both the proxy username and the
proxy password were set, and a cross-host redirect carried the proxy
credentials over only when the username was set. Authorization has been
sent when either one is set since #958; do the same for the proxy.

A client that has both a proxy Basic credential with one empty field
and a proxy bearer token now sends Basic rather than Bearer, the order
Authorization already follows.
yhirose 2 روز پیش
والد
کامیت
941745c33d
2فایلهای تغییر یافته به همراه37 افزوده شده و 3 حذف شده
  1. 4 3
      httplib.h
  2. 33 0
      test/test.cc

+ 4 - 3
httplib.h

@@ -15944,7 +15944,8 @@ inline void ClientImpl::setup_redirect_client(ClientType &client) {
   if (!proxy_host_.empty() && proxy_port_ != -1) {
     client.set_proxy(proxy_host_, proxy_port_);
 
-    if (!proxy_basic_auth_username_.empty()) {
+    if (!proxy_basic_auth_username_.empty() ||
+        !proxy_basic_auth_password_.empty()) {
       client.set_proxy_basic_auth(proxy_basic_auth_username_,
                                   proxy_basic_auth_password_);
     }
@@ -16047,8 +16048,8 @@ inline bool ClientImpl::write_request(Stream &strm, Request &req,
   // otherwise NO_PROXY-matched requests, and requests inside a TLS tunnel,
   // would leak proxy credentials to the destination server.
   if (is_proxy_enabled_for_host(host_) && (!is_ssl() || is_connect)) {
-    if (!proxy_basic_auth_username_.empty() &&
-        !proxy_basic_auth_password_.empty() &&
+    if ((!proxy_basic_auth_username_.empty() ||
+         !proxy_basic_auth_password_.empty()) &&
         !req.has_header("Proxy-Authorization")) {
       req.headers.insert(make_basic_authentication_header(
           proxy_basic_auth_username_, proxy_basic_auth_password_, true));

+ 33 - 0
test/test.cc

@@ -26962,6 +26962,39 @@ TEST(NoProxyTest, ProxyAuthorizationSentWhenNotBypassed) {
   EXPECT_TRUE(s.last_had_proxy_authz());
 }
 
+TEST(NoProxyTest, ProxyAuthorizationSentWithOneEmptyCredential) {
+  std::atomic<int> hits{0};
+  std::atomic<int> hits_with_authz{0};
+
+  no_proxy_test::ScopedServer proxy_mock;
+  proxy_mock.svr().Get(".*", [&](const Request &req, Response &res) {
+    hits++;
+    if (req.has_header("Proxy-Authorization")) { hits_with_authz++; }
+    if (req.target.find("/redir") != std::string::npos) {
+      res.set_redirect("http://other.example/landed");
+    } else {
+      res.set_content("via-proxy", "text/plain");
+    }
+  });
+  proxy_mock.listen();
+
+  for (const auto &c : {std::make_pair("u", ""), std::make_pair("", "p")}) {
+    hits = 0;
+    hits_with_authz = 0;
+
+    Client cli("public.example", 80);
+    cli.set_proxy("127.0.0.1", proxy_mock.port());
+    cli.set_proxy_basic_auth(c.first, c.second);
+    cli.set_follow_location(true);
+
+    // The redirect to another host goes through the proxy as well
+    auto res = cli.Get("/redir");
+    ASSERT_TRUE(res) << "Error: " << to_string(res.error());
+    EXPECT_EQ(2, hits.load());
+    EXPECT_EQ(2, hits_with_authz.load());
+  }
+}
+
 TEST(NoProxyTest, EmptyNoProxyKeepsProxyOn) {
   ProxyAndTargetServers s;
   auto cli = make_client("anything.test", s);