Skip to content

Commit 7a85cde

Browse files
committed
Allowing using SSLContext supplier per request by the NettyConnector
Signed-off-by: jansupol <jan.supol@oracle.com>
1 parent 9d96068 commit 7a85cde

File tree

13 files changed

+566
-105
lines changed

13 files changed

+566
-105
lines changed

connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2020, 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
@@ -72,7 +72,7 @@ public class NettyClientProperties {
7272
/**
7373
* The maximal number of redirects during single request.
7474
* <p/>
75-
* Value is expected to be positive {@link Integer}. Default value is {@value #DEFAULT_MAX_REDIRECTS}.
75+
* Value is expected to be positive {@link Integer}. Default value is 5.
7676
* <p/>
7777
* HTTP redirection must be enabled by property {@link org.glassfish.jersey.client.ClientProperties#FOLLOW_REDIRECTS},
7878
* otherwise {@code MAX_REDIRECTS} is not applied.

connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
import java.util.concurrent.Executors;
3636
import java.util.concurrent.Future;
3737
import java.util.concurrent.TimeUnit;
38+
import java.util.function.Supplier;
3839

40+
import javax.net.ssl.SSLContext;
3941
import javax.ws.rs.ProcessingException;
4042
import javax.ws.rs.client.Client;
4143
import javax.ws.rs.core.Configuration;
@@ -197,8 +199,10 @@ protected void execute(final ClientRequest jerseyRequest, final Set<URI> redirec
197199
int port = requestUri.getPort() != -1 ? requestUri.getPort() : "https".equals(requestUri.getScheme()) ? 443 : 80;
198200

199201
try {
202+
final SSLParamConfigurator sslConfig = SSLParamConfigurator.builder()
203+
.request(jerseyRequest).setSNIAlways(true).build();
200204

201-
String key = requestUri.getScheme() + "://" + host + ":" + port;
205+
String key = requestUri.getScheme() + "://" + sslConfig.getSNIHostName() + ":" + port;
202206
ArrayList<Channel> conns;
203207
synchronized (connections) {
204208
conns = connections.get(key);
@@ -228,9 +232,8 @@ protected void execute(final ClientRequest jerseyRequest, final Set<URI> redirec
228232
}
229233
}
230234

231-
Integer connectTimeout = jerseyRequest.resolveProperty(ClientProperties.CONNECT_TIMEOUT, 0);
232-
233235
if (chan == null) {
236+
Integer connectTimeout = jerseyRequest.resolveProperty(ClientProperties.CONNECT_TIMEOUT, 0);
234237
Bootstrap b = new Bootstrap();
235238

236239
// http proxy
@@ -267,7 +270,7 @@ protected void initChannel(SocketChannel ch) throws Exception {
267270
if ("https".equals(requestUri.getScheme())) {
268271
// making client authentication optional for now; it could be extracted to configurable property
269272
JdkSslContext jdkSslContext = new JdkSslContext(
270-
client.getSslContext(),
273+
getSslContext(client, jerseyRequest),
271274
true,
272275
(Iterable) null,
273276
IdentityCipherSuiteFilter.INSTANCE,
@@ -278,8 +281,7 @@ protected void initChannel(SocketChannel ch) throws Exception {
278281
);
279282

280283
final int port = requestUri.getPort();
281-
final SSLParamConfigurator sslConfig = SSLParamConfigurator.builder()
282-
.request(jerseyRequest).setSNIAlways(true).build();
284+
283285
final SslHandler sslHandler = jdkSslContext.newHandler(
284286
ch.alloc(), sslConfig.getSNIHostName(), port <= 0 ? 443 : port, executorService
285287
);
@@ -455,6 +457,11 @@ public void run() {
455457
}
456458
}
457459

460+
private SSLContext getSslContext(Client client, ClientRequest request) {
461+
Supplier<SSLContext> supplier = request.resolveProperty(ClientProperties.SSL_CONTEXT_SUPPLIER, Supplier.class);
462+
return supplier == null ? client.getSslContext() : supplier.get();
463+
}
464+
458465
private String buildPathWithQueryParameters(URI requestUri) {
459466
if (requestUri.getRawQuery() != null) {
460467
return String.format("%s?%s", requestUri.getRawPath(), requestUri.getRawQuery());

core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.glassfish.jersey.internal.util.PropertiesHelper;
2525
import org.glassfish.jersey.internal.util.PropertyAlias;
2626

27+
import javax.ws.rs.client.Client;
2728
import javax.ws.rs.client.ClientBuilder;
2829

2930
/**
@@ -481,6 +482,16 @@ public final class ClientProperties {
481482
*/
482483
public static final String CONNECTOR_PROVIDER = "jersey.config.client.connector.provider";
483484

485+
/**
486+
* <p>The {@link javax.net.ssl.SSLContext} {@link java.util.function.Supplier} to be used to set ssl context in the current
487+
* HTTP request. Has precedence over the {@link Client#getSslContext()}.
488+
* </p>
489+
* <p>Currently supported by the default {@code HttpUrlConnector} and by {@code NettyConnector} only.</p>
490+
* @since 2.41
491+
* @see org.glassfish.jersey.client.SslContextClientBuilder
492+
*/
493+
public static final String SSL_CONTEXT_SUPPLIER = "jersey.config.client.ssl.context.supplier";
494+
484495
private ClientProperties() {
485496
// prevents instantiation
486497
}

core-client/src/main/java/org/glassfish/jersey/client/JerseyClient.java

Lines changed: 31 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011, 2019 Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2011, 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
@@ -20,13 +20,13 @@
2020
import java.lang.ref.ReferenceQueue;
2121
import java.lang.ref.WeakReference;
2222
import java.net.URI;
23-
import java.util.Iterator;
2423
import java.util.Map;
2524
import java.util.concurrent.ExecutorService;
2625
import java.util.concurrent.Executors;
2726
import java.util.concurrent.LinkedBlockingDeque;
2827
import java.util.concurrent.ScheduledExecutorService;
2928
import java.util.concurrent.atomic.AtomicBoolean;
29+
import java.util.function.Supplier;
3030
import java.util.logging.Level;
3131
import java.util.logging.Logger;
3232

@@ -40,9 +40,7 @@
4040
import org.glassfish.jersey.SslConfigurator;
4141
import org.glassfish.jersey.client.internal.LocalizationMessages;
4242
import org.glassfish.jersey.client.spi.DefaultSslContextProvider;
43-
import org.glassfish.jersey.internal.ServiceFinder;
4443
import org.glassfish.jersey.internal.util.collection.UnsafeValue;
45-
import org.glassfish.jersey.internal.util.collection.Values;
4644

4745
import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull;
4846
import static org.glassfish.jersey.internal.guava.Preconditions.checkState;
@@ -67,7 +65,7 @@ public SSLContext getDefaultSslContext() {
6765
private final boolean isDefaultSslContext;
6866
private final ClientConfig config;
6967
private final HostnameVerifier hostnameVerifier;
70-
private final UnsafeValue<SSLContext, IllegalStateException> sslContext;
68+
private final Supplier<SSLContext> sslContext;
7169
private final LinkedBlockingDeque<WeakReference<JerseyClient.ShutdownHook>> shutdownHooks =
7270
new LinkedBlockingDeque<WeakReference<JerseyClient.ShutdownHook>>();
7371
private final ReferenceQueue<JerseyClient.ShutdownHook> shReferenceQueue = new ReferenceQueue<JerseyClient.ShutdownHook>();
@@ -86,7 +84,7 @@ interface ShutdownHook {
8684
* Create a new Jersey client instance using a default configuration.
8785
*/
8886
protected JerseyClient() {
89-
this(null, (UnsafeValue<SSLContext, IllegalStateException>) null, null, null);
87+
this(null, new SslContextClientBuilder(), null, null);
9088
}
9189

9290
/**
@@ -115,7 +113,9 @@ protected JerseyClient(final Configuration config,
115113
final SSLContext sslContext,
116114
final HostnameVerifier verifier,
117115
final DefaultSslContextProvider defaultSslContextProvider) {
118-
this(config, sslContext == null ? null : Values.unsafe(sslContext), verifier,
116+
this(config,
117+
sslContext == null ? new SslContextClientBuilder() : new SslContextClientBuilder().sslContext(sslContext),
118+
verifier,
119119
defaultSslContextProvider);
120120
}
121121

@@ -145,32 +145,32 @@ protected JerseyClient(final Configuration config,
145145
final UnsafeValue<SSLContext, IllegalStateException> sslContextProvider,
146146
final HostnameVerifier verifier,
147147
final DefaultSslContextProvider defaultSslContextProvider) {
148-
this.config = config == null ? new ClientConfig(this) : new ClientConfig(this, config);
149-
150-
if (sslContextProvider == null) {
151-
this.isDefaultSslContext = true;
152-
153-
if (defaultSslContextProvider != null) {
154-
this.sslContext = createLazySslContext(defaultSslContextProvider);
155-
} else {
156-
final DefaultSslContextProvider lookedUpSslContextProvider;
157-
158-
final Iterator<DefaultSslContextProvider> iterator =
159-
ServiceFinder.find(DefaultSslContextProvider.class).iterator();
160-
161-
if (iterator.hasNext()) {
162-
lookedUpSslContextProvider = iterator.next();
163-
} else {
164-
lookedUpSslContextProvider = DEFAULT_SSL_CONTEXT_PROVIDER;
165-
}
148+
this(config,
149+
sslContextProvider == null
150+
? new SslContextClientBuilder()
151+
: new SslContextClientBuilder().sslContext(sslContextProvider.get()),
152+
verifier,
153+
defaultSslContextProvider
154+
);
155+
}
166156

167-
this.sslContext = createLazySslContext(lookedUpSslContextProvider);
168-
}
169-
} else {
170-
this.isDefaultSslContext = false;
171-
this.sslContext = Values.lazy(sslContextProvider);
157+
/**
158+
* Create a new Jersey client instance.
159+
*
160+
* @param config jersey client configuration.
161+
* @param sslContextClientBuilder jersey client SSL context builder. The builder is expected to
162+
* return non-default value.
163+
* @param verifier jersey client host name verifier.
164+
* @param defaultSslContextProvider default SSL context provider.
165+
*/
166+
JerseyClient(final Configuration config, final SslContextClientBuilder sslContextClientBuilder,
167+
final HostnameVerifier verifier, final DefaultSslContextProvider defaultSslContextProvider) {
168+
if (defaultSslContextProvider != null) {
169+
sslContextClientBuilder.defaultSslContextProvider(defaultSslContextProvider);
172170
}
173-
171+
this.config = config == null ? new ClientConfig(this) : new ClientConfig(this, config);
172+
this.isDefaultSslContext = sslContextClientBuilder.isDefaultSslContext();
173+
this.sslContext = sslContextClientBuilder;
174174
this.hostnameVerifier = verifier;
175175
}
176176

@@ -195,15 +195,6 @@ private void release() {
195195
}
196196
}
197197

198-
private UnsafeValue<SSLContext, IllegalStateException> createLazySslContext(final DefaultSslContextProvider provider) {
199-
return Values.lazy(new UnsafeValue<SSLContext, IllegalStateException>() {
200-
@Override
201-
public SSLContext get() {
202-
return provider.getDefaultSslContext();
203-
}
204-
});
205-
}
206-
207198
/**
208199
* Register a new client shutdown hook.
209200
*

core-client/src/main/java/org/glassfish/jersey/client/JerseyClientBuilder.java

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,12 @@
3232
import javax.net.ssl.HostnameVerifier;
3333
import javax.net.ssl.SSLContext;
3434

35-
import org.glassfish.jersey.SslConfigurator;
3635
import org.glassfish.jersey.client.innate.inject.NonInjectionManager;
37-
import org.glassfish.jersey.client.internal.LocalizationMessages;
3836
import org.glassfish.jersey.client.spi.ClientBuilderListener;
3937
import org.glassfish.jersey.client.spi.ConnectorProvider;
4038
import org.glassfish.jersey.internal.ServiceFinder;
4139
import org.glassfish.jersey.internal.config.ExternalPropertiesConfigurationFactory;
4240
import org.glassfish.jersey.internal.util.ReflectionHelper;
43-
import org.glassfish.jersey.internal.util.collection.UnsafeValue;
44-
import org.glassfish.jersey.internal.util.collection.Values;
4541
import org.glassfish.jersey.model.internal.RankedComparator;
4642
import org.glassfish.jersey.model.internal.RankedProvider;
4743

@@ -54,8 +50,7 @@ public class JerseyClientBuilder extends ClientBuilder {
5450

5551
private final ClientConfig config;
5652
private HostnameVerifier hostnameVerifier;
57-
private SslConfigurator sslConfigurator;
58-
private SSLContext sslContext;
53+
private SslContextClientBuilder sslContextClientBuilder = new SslContextClientBuilder();
5954

6055
private static final List<ClientBuilderListener> CLIENT_BUILDER_LISTENERS;
6156

@@ -113,41 +108,19 @@ private static void init(ClientBuilder builder) {
113108

114109
@Override
115110
public JerseyClientBuilder sslContext(SSLContext sslContext) {
116-
if (sslContext == null) {
117-
throw new NullPointerException(LocalizationMessages.NULL_SSL_CONTEXT());
118-
}
119-
this.sslContext = sslContext;
120-
sslConfigurator = null;
111+
sslContextClientBuilder.sslContext(sslContext);
121112
return this;
122113
}
123114

124115
@Override
125116
public JerseyClientBuilder keyStore(KeyStore keyStore, char[] password) {
126-
if (keyStore == null) {
127-
throw new NullPointerException(LocalizationMessages.NULL_KEYSTORE());
128-
}
129-
if (password == null) {
130-
throw new NullPointerException(LocalizationMessages.NULL_KEYSTORE_PASWORD());
131-
}
132-
if (sslConfigurator == null) {
133-
sslConfigurator = SslConfigurator.newInstance();
134-
}
135-
sslConfigurator.keyStore(keyStore);
136-
sslConfigurator.keyPassword(password);
137-
sslContext = null;
117+
sslContextClientBuilder.keyStore(keyStore, password);
138118
return this;
139119
}
140120

141121
@Override
142122
public JerseyClientBuilder trustStore(KeyStore trustStore) {
143-
if (trustStore == null) {
144-
throw new NullPointerException(LocalizationMessages.NULL_TRUSTSTORE());
145-
}
146-
if (sslConfigurator == null) {
147-
sslConfigurator = SslConfigurator.newInstance();
148-
}
149-
sslConfigurator.trustStore(trustStore);
150-
sslContext = null;
123+
sslContextClientBuilder.trustStore(trustStore);
151124
return this;
152125
}
153126

@@ -194,22 +167,7 @@ public JerseyClient build() {
194167
ExternalPropertiesConfigurationFactory.configure(this.config);
195168
setConnectorFromProperties();
196169

197-
if (sslContext != null) {
198-
return new JerseyClient(config, sslContext, hostnameVerifier, null);
199-
} else if (sslConfigurator != null) {
200-
final SslConfigurator sslConfiguratorCopy = sslConfigurator.copy();
201-
return new JerseyClient(
202-
config,
203-
Values.lazy(new UnsafeValue<SSLContext, IllegalStateException>() {
204-
@Override
205-
public SSLContext get() {
206-
return sslConfiguratorCopy.createSSLContext();
207-
}
208-
}),
209-
hostnameVerifier);
210-
} else {
211-
return new JerseyClient(config, (UnsafeValue<SSLContext, IllegalStateException>) null, hostnameVerifier);
212-
}
170+
return new JerseyClient(config, sslContextClientBuilder, hostnameVerifier, null);
213171
}
214172

215173
private void setConnectorFromProperties() {

0 commit comments

Comments
 (0)