@@ -143,26 +143,57 @@ private Http2ClientChannelInitializer http2Initializer() {
143143 }
144144 }
145145
146+ /**
147+ * Whether this connection's transport TLS terminates at an HTTPS proxy (forward mode through an
148+ * HTTPS proxy) — the only forward case that uses leg-1 TLS to the proxy.
149+ */
150+ private static boolean proxyHttps (HttpConnectParams params ) {
151+ return params .forwardProxy && params .proxyOptions != null && params .proxyOptions .getType () == ProxyType .HTTPS ;
152+ }
153+
154+ /** Whether the socket is encrypted: origin TLS, or leg-1 TLS to an HTTPS proxy (forward mode). */
155+ private static boolean transportSsl (HttpConnectParams params ) {
156+ return params .ssl || proxyHttps (params );
157+ }
158+
146159 private void connect (ContextInternal context , HttpConnectParams params , HostAndPort authority , SocketAddress server , Promise <NetSocket > promise ) {
160+ boolean forward = params .forwardProxy ;
161+ boolean proxyHttps = proxyHttps (params );
162+ boolean transportSsl = transportSsl (params );
163+
147164 ConnectOptions connectOptions = new ConnectOptions ();
148165 connectOptions .setRemoteAddress (server );
149- if (authority != null ) {
166+ if (proxyHttps ) {
167+ // Forward mode through an HTTPS proxy: the socket's TLS terminates at the proxy, so the peer /
168+ // SNI is the proxy (the server), not the origin authority.
169+ connectOptions .setHost (server .host ());
170+ connectOptions .setPort (server .port ());
171+ connectOptions .setSniServerName (server .host ());
172+ } else if (authority != null ) {
150173 connectOptions .setHost (authority .host ());
151174 connectOptions .setPort (authority .port ());
152- if (params . ssl && forceSni ) {
175+ if (transportSsl && forceSni ) {
153176 connectOptions .setSniServerName (authority .host ());
154177 }
155178 }
156- connectOptions .setSsl (params .ssl );
157- if (params .ssl ) {
179+ connectOptions .setSsl (transportSsl );
180+ if (transportSsl ) {
181+ // The TLS being configured terminates at the proxy (forward + HTTPS proxy) or at the origin
182+ // (direct, or the leg-2 of a CONNECT tunnel); pick the matching options.
183+ ClientSSLOptions transportSslOptions = proxyHttps ? params .proxyOptions .getSslOptions () : params .sslOptions ;
158184 ClientSSLOptions copy ;
159- if (params . sslOptions != null ) {
160- copy = params . sslOptions .copy ();
185+ if (transportSslOptions != null ) {
186+ copy = transportSslOptions .copy ();
161187 } else {
162188 // We might end up using javax.net.ssl.trustStore
163189 copy = new ClientSSLOptions ().setHostnameVerificationAlgorithm ("HTTPS" );
164190 }
165- boolean useAlpn = params .protocols .contains (HttpVersion .HTTP_2 );
191+ if (proxyHttps && copy .getHostnameVerificationAlgorithm () == null ) {
192+ // Verify the proxy certificate against the proxy host by default (secure default).
193+ copy .setHostnameVerificationAlgorithm ("HTTPS" );
194+ }
195+ // No ALPN on a leg-1 TLS connection to the proxy: ALPN belongs to the origin (leg 2).
196+ boolean useAlpn = !proxyHttps && params .protocols .contains (HttpVersion .HTTP_2 );
166197 copy .setUseAlpn (useAlpn );
167198 if (useAlpn ) {
168199 List <String > list = params .protocols
@@ -174,7 +205,9 @@ private void connect(ContextInternal context, HttpConnectParams params, HostAndP
174205 }
175206 connectOptions .setSslOptions (copy );
176207 }
177- connectOptions .setProxyOptions (params .proxyOptions );
208+ // Only CONNECT/SOCKS-tunnel through the proxy when NOT forwarding; in forward mode the socket
209+ // connects straight to the proxy (the server) and issues absolute-URI requests.
210+ connectOptions .setProxyOptions (forward ? null : params .proxyOptions );
178211 client .connectInternal (connectOptions , promise , context );
179212 }
180213
@@ -197,7 +230,11 @@ public Future<HttpClientConnection> wrap(ContextInternal context, HttpConnectPar
197230
198231 //
199232 Channel ch = so .channelHandlerContext ().channel ();
200- if (params .ssl ) {
233+ // Transport TLS may be on because the origin is HTTPS or because leg-1 TLS to an HTTPS proxy is
234+ // in use (forward mode). The connection's ssl flag, however, must reflect the logical origin
235+ // (params.ssl), not the transport.
236+ boolean transportSsl = transportSsl (params );
237+ if (transportSsl ) {
201238 String protocol = so .applicationLayerProtocol ();
202239 if (protocol == null ) {
203240 protocol = "" ;
@@ -218,7 +255,7 @@ public Future<HttpClientConnection> wrap(ContextInternal context, HttpConnectPar
218255 if (http1Config != null ) {
219256 applyHttp1xConnectionOptions (ch .pipeline ());
220257 HttpVersion version = "http/1.0" .equals (protocol ) ? HttpVersion .HTTP_1_0 : HttpVersion .HTTP_1_1 ;
221- http1xConnected (version , server , authority , true , context , transportMetrics , metric , ch , clientMetrics , promise );
258+ http1xConnected (version , server , authority , params . ssl , context , transportMetrics , metric , ch , clientMetrics , promise );
222259 } else {
223260 so .close ();
224261 promise .tryFail (new IllegalStateException ("HTTP/1.1 not supported" ));
0 commit comments