99import java .util .Map ;
1010
1111import javax .net .ssl .SSLSocket ;
12+ import javax .net .ssl .SSLSocketFactory ;
1213
1314import io .split .android .client .utils .logger .Logger ;
1415
@@ -42,6 +43,7 @@ public HttpOverTunnelExecutor() {
4243 * @param method HTTP method for the request
4344 * @param headers Headers to include in the request
4445 * @param body Request body (null for GET requests)
46+ * @param combinedSslSocketFactory The combined SSL socket factory to use for HTTPS origins
4547 * @return HttpResponse containing the server's response
4648 * @throws IOException if the request execution fails
4749 */
@@ -51,21 +53,22 @@ public HttpResponse executeRequest(
5153 @ NonNull URL targetUrl ,
5254 @ NonNull HttpMethod method ,
5355 @ NonNull Map <String , String > headers ,
54- @ Nullable String body ) throws IOException {
56+ @ Nullable String body ,
57+ @ NonNull SSLSocketFactory combinedSslSocketFactory ) throws IOException {
5558
56- Logger .v ("Executing " + method + " request to " + targetUrl + " through SSL tunnel" );
59+ Logger .v ("Executing request through SSL tunnel to: " + targetUrl );
5760
5861 try {
5962 if ("https" .equalsIgnoreCase (targetUrl .getProtocol ())) {
6063 // For HTTPS origins, we still need to establish SSL connection to origin
61- // through the tunnel
62- return executeHttpsRequest (tunnelSocket , targetUrl , method , headers , body );
64+ // through the tunnel using the combined SSL socket factory
65+ return executeHttpsRequest (tunnelSocket , targetUrl , method , headers , body , combinedSslSocketFactory );
6366 } else {
6467 // For HTTP origins, send request directly through tunnel
6568 return executeHttpRequest (tunnelSocket , targetUrl , method , headers , body );
6669 }
67-
68- } catch ( IOException e ) {
70+ } catch ( Exception e ) {
71+ Logger . e ( "Failed to execute request through tunnel: " + e . getMessage ());
6972 e .printStackTrace ();
7073 throw new IOException ("Failed to execute HTTP request through tunnel to " + targetUrl , e );
7174 }
@@ -80,39 +83,45 @@ private HttpResponse executeHttpsRequest(
8083 @ NonNull URL targetUrl ,
8184 @ NonNull HttpMethod method ,
8285 @ NonNull Map <String , String > headers ,
83- @ Nullable String body ) throws IOException {
86+ @ Nullable String body ,
87+ @ NonNull SSLSocketFactory combinedSslSocketFactory ) throws IOException {
8488
8589 Logger .v ("Establishing SSL connection to HTTPS origin through tunnel" );
8690
87- // The tunnel is established and working (we're past the SSL protocol errors).
88- // The issue now is that we're sending HTTP requests to an HTTPS origin.
89- // We need to perform SSL handshake with the origin server through the tunnel.
91+ // After CONNECT, the tunnel is transparent and we need to establish
92+ // a new SSL connection to the origin server through the tunnel.
93+ // This implements the "onion layering" architecture:
94+ // - Outer layer: SSL connection to proxy (already established)
95+ // - Inner layer: SSL connection to origin (through tunnel)
9096
9197 try {
92- // Create SSL context and engine for manual SSL handling
93- javax .net .ssl .SSLContext sslContext = javax .net .ssl .SSLContext .getInstance ("TLS" );
94- sslContext .init (null , null , null );
95-
96- javax .net .ssl .SSLEngine sslEngine = sslContext .createSSLEngine (targetUrl .getHost (), getTargetPort (targetUrl ));
97- sslEngine .setUseClientMode (true );
98-
99- // Begin SSL handshake
100- sslEngine .beginHandshake ();
98+ // Use the same combined SSL socket factory that was used for the tunnel
99+ // This factory contains both proxy CA and origin CA certificates
100+ // so it can validate both proxy and origin server certificates
101101
102- // Get the tunnel socket's streams
103- java .io .InputStream tunnelInput = tunnelSocket .getInputStream ();
104- java .io .OutputStream tunnelOutput = tunnelSocket .getOutputStream ();
102+ // Create SSL socket to origin server using tunnel socket as the underlying transport
103+ // This uses the correct SSLSocketFactory.createSocket(Socket, String, int, boolean) method
104+ javax .net .ssl .SSLSocket originSslSocket = (javax .net .ssl .SSLSocket )
105+ combinedSslSocketFactory .createSocket (
106+ tunnelSocket , // Use tunnel socket as underlying transport
107+ targetUrl .getHost (), // Origin server hostname
108+ getTargetPort (targetUrl ), // Origin server port
109+ false // Don't auto-close underlying socket
110+ );
105111
106- // Perform SSL handshake using the tunnel streams
107- performSslHandshake ( sslEngine , tunnelInput , tunnelOutput );
112+ // Configure SSL socket for client mode
113+ originSslSocket . setUseClientMode ( true );
108114
109- Logger .v ("SSL handshake with origin server completed through tunnel" );
115+ // Perform SSL handshake with origin server through tunnel
116+ Logger .v ("Performing SSL handshake with origin server through tunnel" );
117+ originSslSocket .startHandshake ();
118+ Logger .v ("SSL handshake with origin server completed successfully" );
110119
111- // Now we can send HTTP requests through the SSL engine
112- sendHttpRequestThroughSsl ( sslEngine , tunnelInput , tunnelOutput , targetUrl , method , headers , body );
120+ // Now send HTTP request through the SSL connection to origin
121+ sendHttpRequest ( originSslSocket , targetUrl , method , headers , body );
113122
114- // Read and parse HTTP response through SSL engine
115- HttpResponse response = readHttpResponseThroughSsl ( sslEngine , tunnelInput , tunnelOutput );
123+ // Read and parse HTTP response from origin
124+ HttpResponse response = mResponseParser . parseHttpResponse ( originSslSocket . getInputStream () );
116125
117126 Logger .v ("HTTPS request completed successfully, status: " + response .getHttpStatus ());
118127 return response ;
0 commit comments