From 91dcf4e610e3094d1ad55eb3bf9b99c0b6fef27b Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Tue, 12 May 2026 17:58:03 +0200 Subject: [PATCH] url: url_match_destination fix Match origin/via_peer also for non-SSL schemes. Closes #21573 --- lib/url.c | 58 ++++++++++++++++++------------------- tests/http/test_10_proxy.py | 10 +++++++ 2 files changed, 38 insertions(+), 30 deletions(-) diff --git a/lib/url.c b/lib/url.c index 287aa584fa..d15cdc1027 100644 --- 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) -#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; - } - } + /* Different connect-to peers never match */ + if(!Curl_peer_same_destination(m->needle->via_peer, conn->via_peer)) + return FALSE; - /* `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)) - return FALSE; +#ifndef CURL_DISABLE_PROXY + 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); } - return TRUE; +#endif + + 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; + } + } + /* 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)) diff --git a/tests/http/test_10_proxy.py b/tests/http/test_10_proxy.py index b1840b484d..169df8015e 100644 --- a/tests/http/test_10_proxy.py +++ b/tests/http/test_10_proxy.py @@ -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()