Description
Summary
When running HTTPS requests through an HTTP proxy with Karate 2.0.9 (Apache client path), proxy CONNECT succeeds but TLS handshake fails.
SSL debug logs show server_name (SNI) is set to the proxy host, not the HTTPS target host.
Environment
Karate: 2.0.9
Java: 21
Proxy: HTTP proxy on port 80
Target endpoint: xxx.yyy.zzz:443
Failure occurs in Karate Apache client path
Same endpoint/proxy works with standalone Apache HttpClient and java.net.http.HttpClient
Observed behavior
CONNECT tunnel is established successfully
TLS handshake starts
Remote closes handshake
Exception: SSLHandshakeException: Remote host terminated the handshake
ClientHello server_name value is my.proxy.com (proxy host), expected xxx.yyy.zzz
Expected behavior
After successful CONNECT, TLS handshake should use the target host for SNI (xxx.yyy.zzz), not the proxy host.
Remark(s)
Switching back to 1.5.1 this works like a charm.
Evidence from logs
CONNECT + 200 established + handshake failure:
apache-debug-ssl.log
TLS1.2 forced run shows same outcome:
apache-debug-ssl-tls12.log
TLS1.3 forced run shows same outcome:
apache-debug-ssl-tls13.log
Karate ssl algorithm override with enabled=true also shows same outcome:
apache-debug-ssl-karate-algo-TLSv12-enabled.log
Key finding
In ApacheHttpClient LenientSslConnectionSocketFactory, createLayeredSocket appears to pass an empty target host into super.createLayeredSocket(...), which may cause incorrect SNI behavior in proxy CONNECT scenarios.
Suspected problematic logic
In ApacheHttpClient LenientSslConnectionSocketFactory:
current: super.createLayeredSocket(socket, "", port, context)
likely expected: super.createLayeredSocket(socket, target, port, context)
Why this looks like root cause
SNI consistently resolves to proxy host in all failing Karate Apache runs.
Protocol changes (TLS default, TLS1.2, TLS1.3) do not change failure.
Explicit Karate ssl configuration (algorithm + enabled=true) does not change failure.
Non-Karate clients to same proxy/target combination succeed.
Repro notes
Use Karate scenario login request to HTTPS endpoint through proxy with DEBUG + javax.net.debug=ssl,handshake enabled.
Proxy CONNECT succeeds but handshake fails immediately after ClientHello.
Steps to Reproduce
-
Given this feature/scenario:
Scenario: POST login
Given url keycloakTokenEndpoint
And header Content-Type = 'application/x-www-form-urlencoded'
And form field client_id = keycloakClientId
And form field grant_type = 'password'
And form field username = keycloakUsername
And form field password = keycloakPassword
And form field scope = 'openid'
When method post
Then status 200
-
execute this feature by callSingle inside of karate-config.js using appropriate karate.properties (proxy, token endpoint etc.)
Expected Behavior
After successful CONNECT, TLS handshake should use the target host for SNI (xxx.yyy.zzz), not the proxy host.
And the handshake should be successful.
Actual Behavior
CONNECT tunnel is established successfully
TLS handshake starts
Remote closes handshake
Exception: SSLHandshakeException: Remote host terminated the handshake
ClientHello server_name value is my.proxy.com (proxy host), expected xxx.yyy.zzz
Karate Version
2.0.9
Java Version
21
Operating System
Linux
Description
Summary
When running HTTPS requests through an HTTP proxy with Karate 2.0.9 (Apache client path), proxy CONNECT succeeds but TLS handshake fails.
SSL debug logs show server_name (SNI) is set to the proxy host, not the HTTPS target host.
Environment
Karate: 2.0.9
Java: 21
Proxy: HTTP proxy on port 80
Target endpoint: xxx.yyy.zzz:443
Failure occurs in Karate Apache client path
Same endpoint/proxy works with standalone Apache HttpClient and java.net.http.HttpClient
Observed behavior
CONNECT tunnel is established successfully
TLS handshake starts
Remote closes handshake
Exception: SSLHandshakeException: Remote host terminated the handshake
ClientHello server_name value is my.proxy.com (proxy host), expected xxx.yyy.zzz
Expected behavior
After successful CONNECT, TLS handshake should use the target host for SNI (xxx.yyy.zzz), not the proxy host.
Remark(s)
Switching back to 1.5.1 this works like a charm.
Evidence from logs
CONNECT + 200 established + handshake failure:
apache-debug-ssl.log
TLS1.2 forced run shows same outcome:
apache-debug-ssl-tls12.log
TLS1.3 forced run shows same outcome:
apache-debug-ssl-tls13.log
Karate ssl algorithm override with enabled=true also shows same outcome:
apache-debug-ssl-karate-algo-TLSv12-enabled.log
Key finding
In ApacheHttpClient LenientSslConnectionSocketFactory, createLayeredSocket appears to pass an empty target host into super.createLayeredSocket(...), which may cause incorrect SNI behavior in proxy CONNECT scenarios.
Suspected problematic logic
In ApacheHttpClient LenientSslConnectionSocketFactory:
current: super.createLayeredSocket(socket, "", port, context)
likely expected: super.createLayeredSocket(socket, target, port, context)
Why this looks like root cause
SNI consistently resolves to proxy host in all failing Karate Apache runs.
Protocol changes (TLS default, TLS1.2, TLS1.3) do not change failure.
Explicit Karate ssl configuration (algorithm + enabled=true) does not change failure.
Non-Karate clients to same proxy/target combination succeed.
Repro notes
Use Karate scenario login request to HTTPS endpoint through proxy with DEBUG + javax.net.debug=ssl,handshake enabled.
Proxy CONNECT succeeds but handshake fails immediately after ClientHello.
Steps to Reproduce
Given this feature/scenario:
Scenario: POST login
Given url keycloakTokenEndpoint
And header Content-Type = 'application/x-www-form-urlencoded'
And form field client_id = keycloakClientId
And form field grant_type = 'password'
And form field username = keycloakUsername
And form field password = keycloakPassword
And form field scope = 'openid'
When method post
Then status 200
execute this feature by callSingle inside of karate-config.js using appropriate karate.properties (proxy, token endpoint etc.)
Expected Behavior
After successful CONNECT, TLS handshake should use the target host for SNI (xxx.yyy.zzz), not the proxy host.
And the handshake should be successful.
Actual Behavior
CONNECT tunnel is established successfully
TLS handshake starts
Remote closes handshake
Exception: SSLHandshakeException: Remote host terminated the handshake
ClientHello server_name value is my.proxy.com (proxy host), expected xxx.yyy.zzz
Karate Version
2.0.9
Java Version
21
Operating System
Linux