]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
url: url_match_destination fix
authorStefan Eissing <stefan@eissing.org>
Tue, 12 May 2026 15:58:03 +0000 (17:58 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Fri, 15 May 2026 23:11:06 +0000 (01:11 +0200)
Match origin/via_peer also for non-SSL schemes.

Closes #21573

lib/url.c
tests/http/test_10_proxy.py

index 287aa584fa100142b33d326394aa20aba336e64a..d15cdc1027254fd5c6153f4ae0445a63a6c1c543 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -956,36 +956,36 @@ static bool url_match_auth(struct connectdata *conn,
 static bool url_match_destination(struct connectdata *conn,
                                   struct url_conn_match *m)
 {
-  /* Additional match requirements if talking TLS OR
-   * not talking to an HTTP proxy OR using a tunnel through a proxy */
-  if((m->needle->scheme->flags & PROTOPT_SSL)
+  /* Different connect-to peers never match */
+  if(!Curl_peer_same_destination(m->needle->via_peer, conn->via_peer))
+    return FALSE;
+
 #ifndef CURL_DISABLE_PROXY
-     || !m->needle->bits.httpproxy || m->needle->bits.tunnel_proxy
-#endif
-    ) {
-    if(m->needle->scheme != conn->scheme) {
-      /* `needle` and `conn` do not have the same scheme... */
-      if(get_protocol_family(conn->scheme) != m->needle->scheme->protocol) {
-        /* and `conn`s protocol family is not the protocol `needle` wants.
-         * IMAPS would work for IMAP, but no vice versa. */
-        return FALSE;
-      }
-      /* We are in an IMAPS vs IMAP like case. We expect `conn` to have SSL */
-      if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
-        DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T
-                     " has compatible protocol family, but no SSL, no match",
-                     conn->connection_id));
-        return FALSE;
-      }
-    }
+  if(m->needle->bits.httpproxy && !m->needle->bits.tunnel_proxy) {
+    /* Talking to a non-tunneling HTTP proxy matches on proxy peers. */
+    return Curl_peer_equal(m->needle->http_proxy.peer,
+                           conn->http_proxy.peer);
+  }
+#endif
 
-    /* `needle` must have the same hostname and port in origin and
-     * via_peer (if present, NULL peers are equal) */
-    if(!Curl_peer_same_destination(m->needle->origin, conn->origin) ||
-       !Curl_peer_same_destination(m->needle->via_peer, conn->via_peer))
+  if(m->needle->origin->scheme != conn->origin->scheme) {
+    /* `needle` and `conn` not having the same scheme.
+     * This is allowed for the same family *if* conn is using TLS.
+     * - IMAP+STARTTLS works for IMAPS.
+     * - IMAPS works for IMAP. */
+    if(get_protocol_family(conn->origin->scheme) !=
+       m->needle->scheme->protocol) {
+      return FALSE;
+    }
+    if(!url_match_ssl_use(conn, m)) {
+      DEBUGF(infof(m->data, "Connection #%" FMT_OFF_T
+                   " has compatible protocol family, but no SSL, no match",
+                   conn->connection_id));
       return FALSE;
+    }
   }
-  return TRUE;
+  /* Scheme mismatch is acceptable, just compare hostname/port */
+  return Curl_peer_same_destination(m->needle->origin, conn->origin);
 }
 
 static bool url_match_ssl_config(struct connectdata *conn,
@@ -1144,8 +1144,6 @@ static bool url_match_conn(struct connectdata *conn, void *userdata)
   if(!url_match_multiplex_needs(conn, m))
     return FALSE;
 
-  if(!url_match_ssl_use(conn, m))
-    return FALSE;
   if(!url_match_proxy_use(conn, m))
     return FALSE;
   if(!url_match_ssl_config(conn, m))
index b1840b484d7a57eddc00294ff5e0506f4bfbfb27..169df8015e619424cc30355d13f401176730b036 100644 (file)
@@ -413,3 +413,13 @@ class TestProxy:
                                extra_args=xargs)
         r.check_exit_code(0), f'{r}'
         r.check_response(count=1, http_status=200, protocol='HTTP/1.1')
+
+    # download via http: proxy (no tunnel), check connection reuse
+    def test_10_17_proxy_http(self, env: Env, httpd):
+        curl = CurlClient(env=env)
+        url1 = f'http://localhost:{env.http_port}/data.json'
+        url2 = f'http://127.0.0.1:{env.http_port}/data.json'
+        r = curl.http_download(urls=[url1, url2], alpn_proto='http/1.1', with_stats=True,
+                               extra_args=curl.get_proxy_args(proxys=False))
+        r.check_response(count=2, http_status=200)
+        assert r.total_connects == 1, r.dump_logs()