diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java index 671b08ff25..186c6d4159 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyClientProperties.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2020, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -72,7 +72,7 @@ public class NettyClientProperties { /** * The maximal number of redirects during single request. *
- * Value is expected to be positive {@link Integer}. Default value is {@value #DEFAULT_MAX_REDIRECTS}. + * Value is expected to be positive {@link Integer}. Default value is 5. * * HTTP redirection must be enabled by property {@link org.glassfish.jersey.client.ClientProperties#FOLLOW_REDIRECTS}, * otherwise {@code MAX_REDIRECTS} is not applied. diff --git a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java index a2c675a223..b3289e1def 100644 --- a/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java +++ b/connectors/netty-connector/src/main/java/org/glassfish/jersey/netty/connector/NettyConnector.java @@ -35,7 +35,9 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.function.Supplier; +import javax.net.ssl.SSLContext; import javax.ws.rs.ProcessingException; import javax.ws.rs.client.Client; import javax.ws.rs.core.Configuration; @@ -197,8 +199,10 @@ protected void execute(final ClientRequest jerseyRequest, final SetThe {@link javax.net.ssl.SSLContext} {@link java.util.function.Supplier} to be used to set ssl context in the current + * HTTP request. Has precedence over the {@link Client#getSslContext()}. + *
+ *Currently supported by the default {@code HttpUrlConnector} and by {@code NettyConnector} only.
+ * @since 2.41 + * @see org.glassfish.jersey.client.SslContextClientBuilder + */ + public static final String SSL_CONTEXT_SUPPLIER = "jersey.config.client.ssl.context.supplier"; + private ClientProperties() { // prevents instantiation } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/JerseyClient.java b/core-client/src/main/java/org/glassfish/jersey/client/JerseyClient.java index f7e3916007..214625dc2b 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/JerseyClient.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/JerseyClient.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2011, 2019 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2011, 2023 Oracle and/or its affiliates. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v. 2.0, which is available at @@ -20,13 +20,13 @@ import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.net.URI; -import java.util.Iterator; import java.util.Map; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @@ -40,9 +40,7 @@ import org.glassfish.jersey.SslConfigurator; import org.glassfish.jersey.client.internal.LocalizationMessages; import org.glassfish.jersey.client.spi.DefaultSslContextProvider; -import org.glassfish.jersey.internal.ServiceFinder; import org.glassfish.jersey.internal.util.collection.UnsafeValue; -import org.glassfish.jersey.internal.util.collection.Values; import static org.glassfish.jersey.internal.guava.Preconditions.checkNotNull; import static org.glassfish.jersey.internal.guava.Preconditions.checkState; @@ -67,7 +65,7 @@ public SSLContext getDefaultSslContext() { private final boolean isDefaultSslContext; private final ClientConfig config; private final HostnameVerifier hostnameVerifier; - private final UnsafeValueThe class that builds {@link SSLContext} for the client from keystore, truststore. Provides a cached + * {@link Supplier} from the built or user provided {@link SSLContext}.
+ * + *The class is used internally by {@link JerseyClientBuilder}, or it can be used by connectors supporting setting + * the {@link SSLContext} per request.
+ * + * @see javax.ws.rs.client.ClientBuilder#keyStore(KeyStore, char[]) + * @see javax.ws.rs.client.ClientBuilder#keyStore(KeyStore, String) + * @see javax.ws.rs.client.ClientBuilder#sslContext(SSLContext) + */ +public final class SslContextClientBuilder implements Supplier+ * Setting a SSL context instance resets any {@link #keyStore(java.security.KeyStore, char[]) + * key store} or {@link #trustStore(java.security.KeyStore) trust store} values previously + * specified. + *
+ * + * @param sslContext secure socket protocol implementation which acts as a factory + * for secure socket factories or {@link javax.net.ssl.SSLEngine + * SSL engines}. Must not be {@code null}. + * @return an updated ssl client context builder instance. + * @throws NullPointerException in case the {@code sslContext} parameter is {@code null}. + * @see #keyStore(java.security.KeyStore, char[]) + * @see #keyStore(java.security.KeyStore, String) + * @see #trustStore + */ + public SslContextClientBuilder sslContext(SSLContext sslContext) { + if (sslContext == null) { + throw new NullPointerException(LocalizationMessages.NULL_SSL_CONTEXT()); + } + this.sslContext = sslContext; + sslConfigurator = null; + return this; + } + + /** + * Set the client-side key store. Key store contains client's private keys, and the certificates with their + * corresponding public keys. + *+ * Setting a key store instance resets any {@link #sslContext(javax.net.ssl.SSLContext) SSL context instance} + * value previously specified. + *
+ *+ * Note that for improved security of working with password data and avoid storing passwords in Java string + * objects, the {@link #keyStore(java.security.KeyStore, char[])} version of the method can be utilized. + * Also note that a custom key store is only required if you want to enable a custom setup of a 2-way SSL + * connections (client certificate authentication). + *
+ * + * @param keyStore client-side key store. Must not be {@code null}. + * @param password client key password. Must not be {@code null}. + * @return an updated ssl client context builder instance. + * @throws NullPointerException in case any of the supplied parameters is {@code null}. + * @see #sslContext + * @see #keyStore(java.security.KeyStore, char[]) + * @see #trustStore + */ + public SslContextClientBuilder keyStore(KeyStore keyStore, char[] password) { + if (keyStore == null) { + throw new NullPointerException(LocalizationMessages.NULL_KEYSTORE()); + } + if (password == null) { + throw new NullPointerException(LocalizationMessages.NULL_KEYSTORE_PASWORD()); + } + if (sslConfigurator == null) { + sslConfigurator = SslConfigurator.newInstance(); + } + sslConfigurator.keyStore(keyStore); + sslConfigurator.keyPassword(password); + sslContext = null; + return this; + } + + /** + * Set the client-side trust store. Trust store is expected to contain certificates from other parties + * the client is you expect to communicate with, or from Certificate Authorities that are trusted to + * identify other parties. + *+ * Setting a trust store instance resets any {@link #sslContext(javax.net.ssl.SSLContext) SSL context instance} + * value previously specified. + *
+ *+ * In case a custom trust store or custom SSL context is not specified, the trust management will be + * configured to use the default Java runtime settings. + *
+ * + * @param trustStore client-side trust store. Must not be {@code null}. + * @return an updated ssl client context builder instance. + * @throws NullPointerException in case the supplied trust store parameter is {@code null}. + * @see #sslContext + * @see #keyStore(java.security.KeyStore, char[]) + * @see #keyStore(java.security.KeyStore, String) + */ + public SslContextClientBuilder trustStore(KeyStore trustStore) { + if (trustStore == null) { + throw new NullPointerException(LocalizationMessages.NULL_TRUSTSTORE()); + } + if (sslConfigurator == null) { + sslConfigurator = SslConfigurator.newInstance(); + } + sslConfigurator.trustStore(trustStore); + sslContext = null; + return this; + } + + /** + * Set the client-side key store. Key store contains client's private keys, and the certificates with their + * corresponding public keys. + *+ * Setting a key store instance resets any {@link #sslContext(javax.net.ssl.SSLContext) SSL context instance} + * value previously specified. + *
+ *+ * Note that for improved security of working with password data and avoid storing passwords in Java string + * objects, the {@link #keyStore(java.security.KeyStore, char[])} version of the method can be utilized. + * Also note that a custom key store is only required if you want to enable a custom setup of a 2-way SSL + * connections (client certificate authentication). + *
+ * + * @param keyStore client-side key store. Must not be {@code null}. + * @param password client key password. Must not be {@code null}. + * @return an updated ssl client context builder instance. + * @throws NullPointerException in case any of the supplied parameters is {@code null}. + * @see #sslContext + * @see #keyStore(java.security.KeyStore, char[]) + * @see #trustStore + */ + public SslContextClientBuilder keyStore(final KeyStore keyStore, final String password) { + return keyStore(keyStore, password.toCharArray()); + } + + /** + * Get information about used {@link SSLContext}. + * + * @return {@code true} when used {@code SSLContext} is acquired from {@link SslConfigurator#getDefaultContext()}, + * {@code false} otherwise. + */ + public boolean isDefaultSslContext() { + return sslContext == null && sslConfigurator == null; + } + + /** + * Supply SSLContext from this builder. + * @return {@link SSLContext} + */ + @Override + public SSLContext get() { + return suppliedValue.get(); + } + + /** + * Build SSLContext from the Builder. + * @return {@link SSLContext} + */ + public SSLContext build() { + return suppliedValue.get(); + } + + /** + * Set the default SSL context provider. + * @param defaultSslContextProvider the default SSL context provider. + * @return an updated ssl client context builder instance. + */ + protected SslContextClientBuilder defaultSslContextProvider(DefaultSslContextProvider defaultSslContextProvider) { + this.defaultSslContextProvider = defaultSslContextProvider; + return this; + } + + /** + * Supply the {@link SSLContext} to the supplier. Can throw illegal state exception when there is a problem with creating or + * obtaining default SSL context. + * @return SSLContext + */ + private SSLContext supply() { + final SSLContext providedValue; + if (sslContext != null) { + providedValue = sslContext; + } else if (sslConfigurator != null) { + final SslConfigurator sslConfiguratorCopy = sslConfigurator.copy(); + providedValue = sslConfiguratorCopy.createSSLContext(); + } else { + providedValue = null; + } + + final SSLContext returnValue; + if (providedValue == null) { + if (defaultSslContextProvider != null) { + returnValue = defaultSslContextProvider.getDefaultSslContext(); + } else { + final DefaultSslContextProvider lookedUpSslContextProvider; + + final Iterator