This is an automated email from the ASF dual-hosted git repository.

willholley pushed a commit to branch wh/connect_to
in repository https://gitbox.apache.org/repos/asf/couchdb-ibrowse.git

commit 2c20f471d58ca0fd4997e362858b85997196de29
Author: Will Holley <[email protected]>
AuthorDate: Mon Apr 27 12:06:11 2026 +0100

    feat: add connect_to option
    
    Add a `connect_to` request option that lets
    ibrowse connect to a different network target
    while preserving the original URL host for Host
    handling, cookies, and TLS SNI.
    
    The use case for this is when connecting through
    a transparent SNI proxy for egress control.
---
 src/ibrowse_http_client.erl | 33 ++++++++++++++++++++++++++-------
 1 file changed, 26 insertions(+), 7 deletions(-)

diff --git a/src/ibrowse_http_client.erl b/src/ibrowse_http_client.erl
index bee6a70..f79130c 100644
--- a/src/ibrowse_http_client.erl
+++ b/src/ibrowse_http_client.erl
@@ -618,13 +618,18 @@ do_connect(Host, Port, Options, #state{is_ssl      = true,
                                        use_proxy   = false,
                                        ssl_options = SSLOptions},
            Timeout) ->
+    %% Check for connect_to override and remove from options
+    Host1 = get_value(connect_to, Options, Host),
+    Options1 = proplists:delete(connect_to, Options),
+    
     %% if a socks5 proxy is configured, open the socket separately
     %% before upgrading the socket to a TLS connection.
-    case get_value(socks5_host, Options, undefined) of
+    case get_value(socks5_host, Options1, undefined) of
         %% no socks5 proxy is configured, connect directly with TLS:
         undefined ->
-            Sock_options = get_sock_options(Host, Options, SSLOptions),
-            ssl:connect(Host, Port, Sock_options, Timeout);
+            Sock_options = get_sock_options(Host, Options1, SSLOptions),
+            Sock_options1 = ensure_sni(Sock_options, Host),
+            ssl:connect(Host1, Port, Sock_options1, Timeout);
 
         %% proxy configuration is present: first establish a socket
         %% and then upgrade:
@@ -641,13 +646,17 @@ do_connect(Host, Port, Options, #state{is_ssl      = true,
     end;
 
 do_connect(Host, Port, Options, _State, Timeout) ->
-    Socks5Host = get_value(socks5_host, Options, undefined),
-    Sock_options = get_sock_options(Host, Options, []),
+    %% Check for connect_to override and remove from options
+    Host1 = get_value(connect_to, Options, Host),
+    Options1 = proplists:delete(connect_to, Options),
+    
+    Socks5Host = get_value(socks5_host, Options1, undefined),
+    Sock_options = get_sock_options(Host, Options1, []),
     case Socks5Host of
       undefined ->
-        gen_tcp:connect(Host, Port, Sock_options, Timeout);
+        gen_tcp:connect(Host1, Port, Sock_options, Timeout);
       _ ->
-        catch ibrowse_socks5:connect(Host, Port, Options, Sock_options, 
Timeout)
+        catch ibrowse_socks5:connect(Host1, Port, Options1, Sock_options, 
Timeout)
     end.
 
 get_sock_options(Host, Options, SSLOptions) ->
@@ -695,10 +704,20 @@ filter_sock_options(Opts) ->
                          false;
                     (list) ->
                          false;
+
                     (_) ->
                          true
                  end, Opts).
 
+%% Ensure SNI is set for SSL connections when using connect_to override
+ensure_sni(Opts, Host) ->
+    case lists:keyfind(server_name_indication, 1, Opts) of
+        false ->
+            [{server_name_indication, Host} | Opts];
+        _ ->
+            Opts
+    end.
+
 do_send(Req, #state{socket = Sock,
                     is_ssl = true,
                     use_proxy = true,

Reply via email to