11/*
2- * Copyright (c) 2022 Oracle and/or its affiliates. All rights reserved.
2+ * Copyright (c) 2022, 2023 Oracle and/or its affiliates. All rights reserved.
33 *
44 * This program and the accompanying materials are made available under the
55 * terms of the Eclipse Public License v. 2.0, which is available at
2424import java .io .IOException ;
2525import java .io .InputStream ;
2626import java .io .OutputStream ;
27+ import java .net .Socket ;
2728import java .net .URI ;
2829import java .net .URISyntaxException ;
2930import java .util .ArrayList ;
3738import java .util .logging .Logger ;
3839import java .util .stream .Collectors ;
3940
41+ import javax .net .ssl .SSLSocket ;
4042import javax .ws .rs .ProcessingException ;
4143import javax .ws .rs .client .Client ;
4244import javax .ws .rs .core .Configuration ;
4850import javax .net .ssl .SSLContext ;
4951import javax .net .ssl .SSLSocketFactory ;
5052
53+ import org .apache .hc .client5 .http .AuthenticationStrategy ;
5154import org .apache .hc .client5 .http .ConnectionKeepAliveStrategy ;
5255import org .apache .hc .client5 .http .HttpRequestRetryStrategy ;
5356import org .apache .hc .client5 .http .auth .AuthCache ;
6568import org .apache .hc .client5 .http .impl .auth .BasicAuthCache ;
6669import org .apache .hc .client5 .http .impl .auth .BasicCredentialsProvider ;
6770import org .apache .hc .client5 .http .impl .auth .BasicScheme ;
71+ import org .apache .hc .client5 .http .impl .classic .BasicHttpClientResponseHandler ;
6872import org .apache .hc .client5 .http .impl .classic .CloseableHttpClient ;
6973import org .apache .hc .client5 .http .impl .classic .CloseableHttpResponse ;
7074import org .apache .hc .client5 .http .impl .classic .HttpClientBuilder ;
8690import org .apache .hc .core5 .http .impl .DefaultContentLengthStrategy ;
8791import org .apache .hc .core5 .http .io .entity .AbstractHttpEntity ;
8892import org .apache .hc .core5 .http .io .entity .BufferedHttpEntity ;
93+ import org .apache .hc .core5 .http .protocol .HttpContext ;
8994import org .apache .hc .core5 .ssl .SSLContexts ;
9095import org .apache .hc .core5 .util .TextUtils ;
9196import org .apache .hc .core5 .util .Timeout ;
95100import org .glassfish .jersey .client .ClientResponse ;
96101import org .glassfish .jersey .client .RequestEntityProcessing ;
97102import org .glassfish .jersey .client .innate .ClientProxy ;
103+ import org .glassfish .jersey .client .innate .http .SSLParamConfigurator ;
98104import org .glassfish .jersey .client .spi .AsyncConnectorCallback ;
99105import org .glassfish .jersey .client .spi .Connector ;
100106import org .glassfish .jersey .internal .util .PropertiesHelper ;
176182class Apache5Connector implements Connector {
177183
178184 private static final Logger LOGGER = Logger .getLogger (Apache5Connector .class .getName ());
185+ private static final String JERSEY_REQUEST_ATTR_NAME = "JerseyRequestAttribute" ;
179186 private static final VersionInfo vi ;
180187 private static final String release ;
181188
@@ -385,15 +392,15 @@ private HttpClientConnectionManager createConnectionManager(
385392
386393 final LayeredConnectionSocketFactory sslSocketFactory ;
387394 if (sslContext != null ) {
388- sslSocketFactory = new SSLConnectionSocketFactory (
395+ sslSocketFactory = new SniSSLConnectionSocketFactory (
389396 sslContext , supportedProtocols , supportedCipherSuites , hostnameVerifier );
390397 } else {
391398 if (useSystemProperties ) {
392- sslSocketFactory = new SSLConnectionSocketFactory (
399+ sslSocketFactory = new SniSSLConnectionSocketFactory (
393400 (SSLSocketFactory ) SSLSocketFactory .getDefault (),
394401 supportedProtocols , supportedCipherSuites , hostnameVerifier );
395402 } else {
396- sslSocketFactory = new SSLConnectionSocketFactory (
403+ sslSocketFactory = new SniSSLConnectionSocketFactory (
397404 SSLContexts .createDefault (),
398405 hostnameVerifier );
399406 }
@@ -458,12 +465,7 @@ public ClientResponse apply(final ClientRequest clientRequest) throws Processing
458465 try {
459466 final CloseableHttpResponse response ;
460467 final HttpClientContext context = HttpClientContext .create ();
461- if (preemptiveBasicAuth ) {
462- final AuthCache authCache = new BasicAuthCache ();
463- final BasicScheme basicScheme = new BasicScheme ();
464- authCache .put (getHost (request ), basicScheme );
465- context .setAuthCache (authCache );
466- }
468+ final HttpHost httpHost = getHost (request );
467469
468470 // If a request-specific CredentialsProvider exists, use it instead of the default one
469471 CredentialsProvider credentialsProvider =
@@ -472,7 +474,18 @@ public ClientResponse apply(final ClientRequest clientRequest) throws Processing
472474 context .setCredentialsProvider (credentialsProvider );
473475 }
474476
475- response = client .execute (getHost (request ), request , context );
477+ if (preemptiveBasicAuth ) {
478+ final AuthCache authCache = new BasicAuthCache ();
479+ final BasicScheme basicScheme = new BasicScheme ();
480+ final AuthScope authScope = new AuthScope (httpHost );
481+ basicScheme .initPreemptive (credentialsProvider .getCredentials (authScope , context ));
482+ context .resetAuthExchange (httpHost , basicScheme );
483+ authCache .put (httpHost , basicScheme ); // must be after initPreemptive
484+ context .setAuthCache (authCache );
485+ }
486+
487+ context .setAttribute (JERSEY_REQUEST_ATTR_NAME , clientRequest );
488+ response = client .execute (httpHost , request , context );
476489 HeaderUtils .checkHeaderChanges (clientHeadersSnapshot , clientRequest .getHeaders (),
477490 this .getClass ().getName (), clientRequest .getConfiguration ());
478491
@@ -798,4 +811,77 @@ private ConnectionFactory(final int chunkSize) {
798811 );
799812 }
800813 }
814+
815+ private static final class SniSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
816+
817+ private final ThreadLocal <HttpContext > httpContexts = new ThreadLocal <>();
818+
819+ public SniSSLConnectionSocketFactory (final SSLContext sslContext ,
820+ final String [] supportedProtocols ,
821+ final String [] supportedCipherSuites ,
822+ final HostnameVerifier hostnameVerifier ) {
823+ super (sslContext , supportedProtocols , supportedCipherSuites , hostnameVerifier );
824+ }
825+
826+ public SniSSLConnectionSocketFactory (final javax .net .ssl .SSLSocketFactory socketFactory ,
827+ final String [] supportedProtocols ,
828+ final String [] supportedCipherSuites ,
829+ final HostnameVerifier hostnameVerifier ) {
830+ super (socketFactory , supportedProtocols , supportedCipherSuites , hostnameVerifier );
831+ }
832+
833+ public SniSSLConnectionSocketFactory (
834+ final SSLContext sslContext , final HostnameVerifier hostnameVerifier ) {
835+ super (sslContext , hostnameVerifier );
836+ }
837+
838+ /* Pre 5.2 */
839+ @ Override
840+ public Socket createLayeredSocket (
841+ final Socket socket ,
842+ final String target ,
843+ final int port ,
844+ final HttpContext context ) throws IOException {
845+ httpContexts .set (context );
846+ try {
847+ return super .createLayeredSocket (socket , target , port , context );
848+ } finally {
849+ httpContexts .remove ();
850+ }
851+ }
852+
853+ /* Post 5.2 */
854+ public Socket createLayeredSocket (
855+ final Socket socket ,
856+ final String target ,
857+ final int port ,
858+ final Object attachment ,
859+ final HttpContext context ) throws IOException {
860+ httpContexts .set (context );
861+ try {
862+ return super .createLayeredSocket (socket , target , port , attachment , context );
863+ } finally {
864+ httpContexts .remove ();
865+ }
866+ }
867+
868+ @ Override
869+ protected void prepareSocket (SSLSocket socket ) throws IOException {
870+ HttpContext context = httpContexts .get ();
871+
872+ if (context != null ) {
873+ Object objectRequest = context .getAttribute (JERSEY_REQUEST_ATTR_NAME );
874+ if (objectRequest != null ) {
875+ ClientRequest clientRequest = (ClientRequest ) objectRequest ;
876+ SSLParamConfigurator sniConfig = SSLParamConfigurator .builder ().request (clientRequest ).build ();
877+ sniConfig .setSNIServerName (socket );
878+
879+ final int socketTimeout = ((ClientRequest ) objectRequest ).resolveProperty (ClientProperties .READ_TIMEOUT , -1 );
880+ if (socketTimeout >= 0 ) {
881+ socket .setSoTimeout (socketTimeout );
882+ }
883+ }
884+ }
885+ }
886+ }
801887}
0 commit comments