]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
websockets: auto-tunnel through http proxy
authorStefan Eissing <stefan@eissing.org>
Wed, 20 May 2026 08:30:25 +0000 (10:30 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 20 May 2026 09:50:35 +0000 (11:50 +0200)
When using a ws: or wss: url with a http proxy, automatically
switch to tunneling operation mode.

Add test_20_10 to check.

Fixes #21663
Closes #21691

lib/protocol.c
lib/protocol.h
lib/url.c
tests/http/test_20_websockets.py

index 36ad97618103de8d05fcaade7617d22b4264e467..8d57c058c6b7c4147e1f4bad0dde3fcaa1f44707 100644 (file)
@@ -153,7 +153,8 @@ const struct Curl_scheme Curl_scheme_https = {
   CURLPROTO_HTTPS,                      /* protocol */
   CURLPROTO_HTTP,                       /* family */
   PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */
-  PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE,
+  PROTOPT_USERPWDCTRL | PROTOPT_CONN_REUSE |
+  PROTOPT_HTTP_PROXY_TUNNEL,
   PORT_HTTPS,                           /* defport */
 };
 
@@ -442,7 +443,7 @@ const struct Curl_scheme Curl_scheme_ws = {
   CURLPROTO_WS,                         /* protocol */
   CURLPROTO_HTTP,                       /* family */
   PROTOPT_CREDSPERREQUEST |             /* flags */
-  PROTOPT_USERPWDCTRL,
+  PROTOPT_USERPWDCTRL | PROTOPT_HTTP_PROXY_TUNNEL,
   PORT_HTTP                             /* defport */
 };
 
@@ -457,7 +458,7 @@ const struct Curl_scheme Curl_scheme_wss = {
   CURLPROTO_WSS,                        /* protocol */
   CURLPROTO_HTTP,                       /* family */
   PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
-  PROTOPT_USERPWDCTRL,
+  PROTOPT_USERPWDCTRL | PROTOPT_HTTP_PROXY_TUNNEL,
   PORT_HTTPS                            /* defport */
 };
 
index 8ae2155ee6e610ff5900696afcc714db37135944..50e320d0f66cd66c7a3bc8200a2b678c6338abaf 100644 (file)
@@ -237,6 +237,8 @@ struct Curl_protocol {
                                          without having PROTOPT_SSL. */
 #define PROTOPT_CONN_REUSE (1 << 16)  /* this protocol can reuse connections */
 #define PROTOPT_NO_TRANSFER (1 << 17) /* this protocol is not for transfers */
+#define PROTOPT_HTTP_PROXY_TUNNEL (1 << 18) /* Using this protocol with a
+                                             * HTTP proxy requires tunneling */
 
 /* Everything about a URI scheme. */
 struct Curl_scheme {
index d6e98804b4f761b8919bad4984cb9bdabfd74d15..57b06390216804bed18b59f232c8fcbdac8e4d35 100644 (file)
--- a/lib/url.c
+++ b/lib/url.c
@@ -2018,8 +2018,11 @@ static CURLcode url_set_conn_proxies(struct Curl_easy *data,
         data->state.envproxy = curlx_strdup(proxy);
       }
 #endif
-      /* force this connection's protocol to become HTTP if compatible */
-      if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) {
+      if(conn->scheme->flags & PROTOPT_HTTP_PROXY_TUNNEL) {
+        conn->bits.tunnel_proxy = TRUE;
+      }
+      else if(!(conn->scheme->protocol & PROTO_FAMILY_HTTP)) {
+        /* force this connection's protocol to become HTTP if compatible */
         if((conn->scheme->flags & PROTOPT_PROXY_AS_HTTP) &&
            !conn->bits.tunnel_proxy)
           conn->scheme = &Curl_scheme_http;
index fdc9df6eb7d2e94239b9f1a40cfea50542350b0e..416c342a60051abf9c742a8991f2c9fd2c90f8ae 100644 (file)
@@ -206,3 +206,17 @@ class TestWebsockets:
         large = 0
         r = client.run(args=[f'-{model}', '-c', str(count), '-m', str(large), url])
         r.check_exit_code(0)
+
+    # use ws:// url with HTTP proxy, check that it tunnels automatically
+    def test_20_10_proxy_http(self, env: Env, httpd, ws_echo):
+        curl = CurlClient(env=env)
+        url = f'ws://127.0.0.1:{env.ws_port}/'
+        xargs = curl.get_proxy_args(proxys=False)
+        xargs.extend([
+            '--max-time', '2'
+        ])
+        r = curl.http_download(urls=[url], alpn_proto='http/1.1', with_stats=True,
+                               extra_args=xargs)
+        # The CONNECT through the proxy fails as it does not allow it
+        r.check_exit_code(7) # CURLE_COULDNT_CONNECT
+        assert r.stats[0]['http_connect'] == 403, f'{r}'