Skip to content

Commit ce9b1bd

Browse files
committed
Add SNI Support based on Host header
Signed-off-by: jansupol <jan.supol@oracle.com>
1 parent dc51308 commit ce9b1bd

File tree

22 files changed

+1838
-49
lines changed

22 files changed

+1838
-49
lines changed

connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2010, 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
@@ -24,6 +24,7 @@
2424
import java.io.IOException;
2525
import java.io.InputStream;
2626
import java.io.OutputStream;
27+
import java.net.Socket;
2728
import java.net.URI;
2829
import java.util.ArrayList;
2930
import java.util.LinkedList;
@@ -37,6 +38,7 @@
3738
import java.util.logging.Logger;
3839
import java.util.stream.Collectors;
3940

41+
import javax.net.ssl.SSLSocket;
4042
import javax.ws.rs.ProcessingException;
4143
import javax.ws.rs.client.Client;
4244
import javax.ws.rs.core.Configuration;
@@ -53,6 +55,7 @@
5355
import org.glassfish.jersey.client.ClientResponse;
5456
import org.glassfish.jersey.client.RequestEntityProcessing;
5557
import org.glassfish.jersey.client.innate.ClientProxy;
58+
import org.glassfish.jersey.client.innate.http.SSLParamConfigurator;
5659
import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
5760
import org.glassfish.jersey.client.spi.Connector;
5861
import org.glassfish.jersey.internal.util.PropertiesHelper;
@@ -103,6 +106,7 @@
103106
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
104107
import org.apache.http.impl.io.ChunkedOutputStream;
105108
import org.apache.http.io.SessionOutputBuffer;
109+
import org.apache.http.protocol.HttpContext;
106110
import org.apache.http.util.TextUtils;
107111
import org.apache.http.util.VersionInfo;
108112

@@ -180,6 +184,7 @@
180184
class ApacheConnector implements Connector {
181185

182186
private static final Logger LOGGER = Logger.getLogger(ApacheConnector.class.getName());
187+
private static final String JERSEY_REQUEST_ATTR_NAME = "JerseyRequestAttribute";
183188
private static final VersionInfo vi;
184189
private static final String release;
185190

@@ -381,15 +386,15 @@ private HttpClientConnectionManager createConnectionManager(
381386

382387
final LayeredConnectionSocketFactory sslSocketFactory;
383388
if (sslContext != null) {
384-
sslSocketFactory = new SSLConnectionSocketFactory(
389+
sslSocketFactory = new SniSSLConnectionSocketFactory(
385390
sslContext, supportedProtocols, supportedCipherSuites, hostnameVerifier);
386391
} else {
387392
if (useSystemProperties) {
388-
sslSocketFactory = new SSLConnectionSocketFactory(
393+
sslSocketFactory = new SniSSLConnectionSocketFactory(
389394
(SSLSocketFactory) SSLSocketFactory.getDefault(),
390395
supportedProtocols, supportedCipherSuites, hostnameVerifier);
391396
} else {
392-
sslSocketFactory = new SSLConnectionSocketFactory(
397+
sslSocketFactory = new SniSSLConnectionSocketFactory(
393398
SSLContexts.createDefault(),
394399
hostnameVerifier);
395400
}
@@ -450,14 +455,16 @@ public CookieStore getCookieStore() {
450455
public ClientResponse apply(final ClientRequest clientRequest) throws ProcessingException {
451456
final HttpUriRequest request = getUriHttpRequest(clientRequest);
452457
final Map<String, String> clientHeadersSnapshot = writeOutBoundHeaders(clientRequest, request);
458+
final HttpHost httpHost = getHost(request);
453459

454460
try {
455461
final CloseableHttpResponse response;
456462
final HttpClientContext context = HttpClientContext.create();
463+
457464
if (preemptiveBasicAuth) {
458465
final AuthCache authCache = new BasicAuthCache();
459466
final BasicScheme basicScheme = new BasicScheme();
460-
authCache.put(getHost(request), basicScheme);
467+
authCache.put(httpHost, basicScheme);
461468
context.setAuthCache(authCache);
462469
}
463470

@@ -468,7 +475,8 @@ public ClientResponse apply(final ClientRequest clientRequest) throws Processing
468475
context.setCredentialsProvider(credentialsProvider);
469476
}
470477

471-
response = client.execute(getHost(request), request, context);
478+
context.setAttribute(JERSEY_REQUEST_ATTR_NAME, clientRequest);
479+
response = client.execute(httpHost, request, context);
472480
HeaderUtils.checkHeaderChanges(clientHeadersSnapshot, clientRequest.getHeaders(),
473481
this.getClass().getName(), clientRequest.getConfiguration());
474482

@@ -821,4 +829,56 @@ protected OutputStream createOutputStream(final long len, final SessionOutputBuf
821829
return super.createOutputStream(len, outbuffer);
822830
}
823831
}
832+
833+
private static final class SniSSLConnectionSocketFactory extends SSLConnectionSocketFactory {
834+
835+
private final ThreadLocal<HttpContext> httpContexts = new ThreadLocal<>();
836+
837+
public SniSSLConnectionSocketFactory(final SSLContext sslContext,
838+
final String[] supportedProtocols,
839+
final String[] supportedCipherSuites,
840+
final HostnameVerifier hostnameVerifier) {
841+
super(sslContext, supportedProtocols, supportedCipherSuites, hostnameVerifier);
842+
}
843+
844+
public SniSSLConnectionSocketFactory(final javax.net.ssl.SSLSocketFactory socketFactory,
845+
final String[] supportedProtocols,
846+
final String[] supportedCipherSuites,
847+
final HostnameVerifier hostnameVerifier) {
848+
super(socketFactory, supportedProtocols, supportedCipherSuites, hostnameVerifier);
849+
}
850+
851+
public SniSSLConnectionSocketFactory(
852+
final SSLContext sslContext, final HostnameVerifier hostnameVerifier) {
853+
super(sslContext, hostnameVerifier);
854+
}
855+
856+
@Override
857+
public Socket createLayeredSocket(
858+
final Socket socket,
859+
final String target,
860+
final int port,
861+
final HttpContext context) throws IOException {
862+
httpContexts.set(context);
863+
try {
864+
return super.createLayeredSocket(socket, target, port, context);
865+
} finally {
866+
httpContexts.remove();
867+
}
868+
}
869+
870+
@Override
871+
protected void prepareSocket(SSLSocket socket) throws IOException {
872+
HttpContext context = httpContexts.get();
873+
874+
if (context != null) {
875+
Object objectRequest = context.getAttribute(JERSEY_REQUEST_ATTR_NAME);
876+
if (objectRequest != null) {
877+
ClientRequest clientRequest = (ClientRequest) objectRequest;
878+
SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(clientRequest).build();
879+
sniConfig.setSNIServerName(socket);
880+
}
881+
}
882+
}
883+
}
824884
}

connectors/apache5-connector/src/main/java/org/glassfish/jersey/apache5/connector/Apache5Connector.java

Lines changed: 97 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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
@@ -24,6 +24,7 @@
2424
import java.io.IOException;
2525
import java.io.InputStream;
2626
import java.io.OutputStream;
27+
import java.net.Socket;
2728
import java.net.URI;
2829
import java.net.URISyntaxException;
2930
import java.util.ArrayList;
@@ -37,6 +38,7 @@
3738
import java.util.logging.Logger;
3839
import java.util.stream.Collectors;
3940

41+
import javax.net.ssl.SSLSocket;
4042
import javax.ws.rs.ProcessingException;
4143
import javax.ws.rs.client.Client;
4244
import javax.ws.rs.core.Configuration;
@@ -48,6 +50,7 @@
4850
import javax.net.ssl.SSLContext;
4951
import javax.net.ssl.SSLSocketFactory;
5052

53+
import org.apache.hc.client5.http.AuthenticationStrategy;
5154
import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
5255
import org.apache.hc.client5.http.HttpRequestRetryStrategy;
5356
import org.apache.hc.client5.http.auth.AuthCache;
@@ -65,6 +68,7 @@
6568
import org.apache.hc.client5.http.impl.auth.BasicAuthCache;
6669
import org.apache.hc.client5.http.impl.auth.BasicCredentialsProvider;
6770
import org.apache.hc.client5.http.impl.auth.BasicScheme;
71+
import org.apache.hc.client5.http.impl.classic.BasicHttpClientResponseHandler;
6872
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
6973
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
7074
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
@@ -86,6 +90,7 @@
8690
import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
8791
import org.apache.hc.core5.http.io.entity.AbstractHttpEntity;
8892
import org.apache.hc.core5.http.io.entity.BufferedHttpEntity;
93+
import org.apache.hc.core5.http.protocol.HttpContext;
8994
import org.apache.hc.core5.ssl.SSLContexts;
9095
import org.apache.hc.core5.util.TextUtils;
9196
import org.apache.hc.core5.util.Timeout;
@@ -95,6 +100,7 @@
95100
import org.glassfish.jersey.client.ClientResponse;
96101
import org.glassfish.jersey.client.RequestEntityProcessing;
97102
import org.glassfish.jersey.client.innate.ClientProxy;
103+
import org.glassfish.jersey.client.innate.http.SSLParamConfigurator;
98104
import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
99105
import org.glassfish.jersey.client.spi.Connector;
100106
import org.glassfish.jersey.internal.util.PropertiesHelper;
@@ -176,6 +182,7 @@
176182
class 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
}

connectors/jdk-connector/src/main/java/org/glassfish/jersey/jdk/connector/internal/ConnectorConfiguration.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2015, 2019 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2015, 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
@@ -19,6 +19,7 @@
1919
import java.net.CookiePolicy;
2020
import java.net.URI;
2121
import java.util.Map;
22+
import java.util.concurrent.atomic.AtomicReference;
2223
import java.util.logging.Level;
2324
import java.util.logging.Logger;
2425

@@ -30,6 +31,7 @@
3031

3132
import org.glassfish.jersey.SslConfigurator;
3233
import org.glassfish.jersey.client.ClientProperties;
34+
import org.glassfish.jersey.client.innate.http.SSLParamConfigurator;
3335
import org.glassfish.jersey.jdk.connector.JdkConnectorProperties;
3436

3537
/**
@@ -57,6 +59,7 @@ class ConnectorConfiguration {
5759
private final int responseTimeout;
5860
private final int connectTimeout;
5961
private final ProxyConfiguration proxyConfiguration;
62+
private final AtomicReference<SSLParamConfigurator> sniConfigs = new AtomicReference<>(null);
6063

6164
ConnectorConfiguration(Client client, Configuration config) {
6265
final Map<String, Object> properties = config.getProperties();
@@ -170,6 +173,14 @@ public ProxyConfiguration getProxyConfiguration() {
170173
return proxyConfiguration;
171174
}
172175

176+
void setSniConfig(SSLParamConfigurator sniConfig) {
177+
this.sniConfigs.compareAndSet(null, sniConfig);
178+
}
179+
180+
SSLParamConfigurator getSniConfig() {
181+
return sniConfigs.get();
182+
}
183+
173184
@Override
174185
public String toString() {
175186
return "ConnectorConfiguration{"

0 commit comments

Comments
 (0)