diff --git a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java index 6f1138e3628..f215d021b61 100644 --- a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java +++ b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnector.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2010, 2025 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 @@ -61,12 +61,10 @@ import org.glassfish.jersey.client.spi.AsyncConnectorCallback; import org.glassfish.jersey.client.spi.Connector; import org.glassfish.jersey.innate.io.InputStreamWrapper; -import org.glassfish.jersey.internal.util.PropertiesHelper; import org.glassfish.jersey.message.internal.HeaderUtils; import org.glassfish.jersey.message.internal.OutboundMessageContext; import org.glassfish.jersey.message.internal.ReaderWriter; import org.glassfish.jersey.message.internal.Statuses; -import org.apache.http.ConnectionReuseStrategy; import org.apache.http.Header; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; @@ -76,7 +74,6 @@ import org.apache.http.client.CookieStore; import org.apache.http.client.CredentialsProvider; import org.apache.http.client.HttpClient; -import org.apache.http.client.HttpRequestRetryHandler; import org.apache.http.client.config.CookieSpecs; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; @@ -86,7 +83,6 @@ import org.apache.http.config.ConnectionConfig; import org.apache.http.config.Registry; import org.apache.http.config.RegistryBuilder; -import org.apache.http.conn.ConnectionKeepAliveStrategy; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.ManagedHttpClientConnection; import org.apache.http.conn.routing.HttpRoute; @@ -188,6 +184,7 @@ class ApacheConnector implements Connector { private static final Logger LOGGER = Logger.getLogger(ApacheConnector.class.getName()); private static final String JERSEY_REQUEST_ATTR_NAME = "JerseyRequestAttribute"; + private static final String JERSEY_CONF_ATTR_NAME = "JerseyConfigurationAttribute"; private static final VersionInfo vi; private static final String release; @@ -197,9 +194,7 @@ class ApacheConnector implements Connector { } private final CloseableHttpClient client; - private final CookieStore cookieStore; - private final boolean preemptiveBasicAuth; - private final RequestConfig requestConfig; + private final ApacheConnectorConfiguration.ReadWrite clientConfiguration; /** * Create the new Apache HTTP Client connector. @@ -207,95 +202,41 @@ class ApacheConnector implements Connector { * @param client JAX-RS client instance for which the connector is being created. * @param config client configuration. */ - ApacheConnector(final Client client, final Configuration config) { - final Object connectionManager = config.getProperties().get(ApacheClientProperties.CONNECTION_MANAGER); - if (connectionManager != null) { - if (!(connectionManager instanceof HttpClientConnectionManager)) { - LOGGER.log( - Level.WARNING, - LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( - ApacheClientProperties.CONNECTION_MANAGER, - connectionManager.getClass().getName(), - HttpClientConnectionManager.class.getName()) - ); - } - } - - Object keepAliveStrategy = config.getProperties().get(ApacheClientProperties.KEEPALIVE_STRATEGY); - if (keepAliveStrategy != null) { - if (!(keepAliveStrategy instanceof ConnectionKeepAliveStrategy)) { - LOGGER.log( - Level.WARNING, - LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( - ApacheClientProperties.KEEPALIVE_STRATEGY, - keepAliveStrategy.getClass().getName(), - ConnectionKeepAliveStrategy.class.getName()) - ); - keepAliveStrategy = null; - } - } - - Object reuseStrategy = config.getProperties().get(ApacheClientProperties.REUSE_STRATEGY); - if (reuseStrategy != null) { - if (!(reuseStrategy instanceof ConnectionReuseStrategy)) { - LOGGER.log( - Level.WARNING, - LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( - ApacheClientProperties.REUSE_STRATEGY, - reuseStrategy.getClass().getName(), - ConnectionReuseStrategy.class.getName()) - ); - reuseStrategy = null; - } - } - - Object reqConfig = config.getProperties().get(ApacheClientProperties.REQUEST_CONFIG); - if (reqConfig != null) { - if (!(reqConfig instanceof RequestConfig)) { - LOGGER.log( - Level.WARNING, - LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( - ApacheClientProperties.REQUEST_CONFIG, - reqConfig.getClass().getName(), - RequestConfig.class.getName()) - ); - reqConfig = null; - } - } - - final boolean useSystemProperties = - PropertiesHelper.isProperty(config.getProperties(), ApacheClientProperties.USE_SYSTEM_PROPERTIES); + ApacheConnector(final Client client, final Configuration config, ApacheConnectorProvider.Config conConfig) { + clientConfiguration = conConfig.rw().fromClient(client, config); + clientConfiguration.chunkSize(config.getProperties()); + clientConfiguration.preemptiveBasicAuthentication(config.getProperties()); + this.client = createClient(client, config, clientConfiguration); + } - final SSLContext sslContext = client.getSslContext(); + private static CloseableHttpClient createClient(Client client, + Configuration configuration, + ApacheConnectorConfiguration.ReadWrite clientConfiguration) { + Map properties = configuration.getProperties(); + final SSLContext sslContext = clientConfiguration.sslContext(client); final HttpClientBuilder clientBuilder = HttpClientBuilder.create(); - if (useSystemProperties) { + if (clientConfiguration.useSystemProperties.get()) { clientBuilder.useSystemProperties(); } - clientBuilder.setConnectionManager(getConnectionManager(client, config, sslContext, useSystemProperties)); - clientBuilder.setConnectionManagerShared( - PropertiesHelper.getValue(config.getProperties(), ApacheClientProperties.CONNECTION_MANAGER_SHARED, false, null)); + clientBuilder.setConnectionManager(getConnectionManager(client, configuration, sslContext, clientConfiguration)); + clientBuilder.setConnectionManagerShared(clientConfiguration.connectionManagerShared(properties)); clientBuilder.setSSLContext(sslContext); - if (keepAliveStrategy != null) { - clientBuilder.setKeepAliveStrategy((ConnectionKeepAliveStrategy) keepAliveStrategy); + if (clientConfiguration.keepAliveStrategy.get() != null) { + clientBuilder.setKeepAliveStrategy(clientConfiguration.keepAliveStrategy.get()); } - if (reuseStrategy != null) { - clientBuilder.setConnectionReuseStrategy((ConnectionReuseStrategy) reuseStrategy); + if (clientConfiguration.connectionReuseStrategy.get() != null) { + clientBuilder.setConnectionReuseStrategy(clientConfiguration.connectionReuseStrategy.get()); } - - final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); - - final Object credentialsProvider = config.getProperty(ApacheClientProperties.CREDENTIALS_PROVIDER); - if (credentialsProvider != null && (credentialsProvider instanceof CredentialsProvider)) { - clientBuilder.setDefaultCredentialsProvider((CredentialsProvider) credentialsProvider); + if (clientConfiguration.credentialsProvider(properties) != null) { + clientBuilder.setDefaultCredentialsProvider(clientConfiguration.credentialsProvider.get()); } - - final Object retryHandler = config.getProperties().get(ApacheClientProperties.RETRY_HANDLER); - if (retryHandler != null && (retryHandler instanceof HttpRequestRetryHandler)) { - clientBuilder.setRetryHandler((HttpRequestRetryHandler) retryHandler); + if (clientConfiguration.retryHandler(properties) != null) { + clientBuilder.setRetryHandler(clientConfiguration.httpRequestRetryHandler.get()); } - final Optional proxy = ClientProxy.proxyFromConfiguration(config); + final Optional proxy = clientConfiguration.proxy(configuration); + proxy.ifPresent(clientProxy -> { final URI u = clientProxy.uri(); final HttpHost proxyHost = new HttpHost(u.getHost(), u.getPort(), u.getScheme()); @@ -310,34 +251,34 @@ class ApacheConnector implements Connector { clientBuilder.setProxy(proxyHost); }); - final Boolean preemptiveBasicAuthProperty = (Boolean) config.getProperties() - .get(ApacheClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION); - this.preemptiveBasicAuth = (preemptiveBasicAuthProperty != null) ? preemptiveBasicAuthProperty : false; - - final boolean ignoreCookies = PropertiesHelper.isProperty(config.getProperties(), ApacheClientProperties.DISABLE_COOKIES); + final boolean ignoreCookies = clientConfiguration.disableCookies(properties); + final RequestConfig.Builder requestConfigBuilder = RequestConfig.custom(); - if (reqConfig != null) { - final RequestConfig.Builder reqConfigBuilder = RequestConfig.copy((RequestConfig) reqConfig); + if (clientConfiguration.requestConfig.get() != null) { + final RequestConfig.Builder reqConfigBuilder = RequestConfig.copy(clientConfiguration.requestConfig.get()); if (ignoreCookies) { reqConfigBuilder.setCookieSpec(CookieSpecs.IGNORE_COOKIES); } - requestConfig = reqConfigBuilder.build(); + clientConfiguration.requestConfig.set(reqConfigBuilder.build()); } else { if (ignoreCookies) { requestConfigBuilder.setCookieSpec(CookieSpecs.IGNORE_COOKIES); } - requestConfig = requestConfigBuilder.build(); + clientConfiguration.requestConfig.set(requestConfigBuilder.build()); } - if (requestConfig.getCookieSpec() == null || !requestConfig.getCookieSpec().equals(CookieSpecs.IGNORE_COOKIES)) { - this.cookieStore = new BasicCookieStore(); + final CookieStore cookieStore; + if (clientConfiguration.requestConfig.get().getCookieSpec() == null + || !clientConfiguration.requestConfig.get().getCookieSpec().equals(CookieSpecs.IGNORE_COOKIES)) { + cookieStore = new BasicCookieStore(); clientBuilder.setDefaultCookieStore(cookieStore); } else { - this.cookieStore = null; + cookieStore = null; } - clientBuilder.setDefaultRequestConfig(requestConfig); + clientConfiguration.cookieStore.set(cookieStore); + clientBuilder.setDefaultRequestConfig(clientConfiguration.requestConfig.get()); - LinkedList contracts = config.getInstances().stream() + LinkedList contracts = configuration.getInstances().stream() .filter(ApacheHttpClientBuilderConfigurator.class::isInstance) .collect(Collectors.toCollection(LinkedList::new)); @@ -346,14 +287,15 @@ class ApacheConnector implements Connector { configuredBuilder = ((ApacheHttpClientBuilderConfigurator) configurator).configure(configuredBuilder); } - this.client = configuredBuilder.build(); + return configuredBuilder.build(); } - private HttpClientConnectionManager getConnectionManager(final Client client, - final Configuration config, - final SSLContext sslContext, - final boolean useSystemProperties) { - final Object cmObject = config.getProperties().get(ApacheClientProperties.CONNECTION_MANAGER); + private static HttpClientConnectionManager getConnectionManager(final Client client, + final Configuration rsConfig, + final SSLContext sslContext, + final ApacheConnectorConfiguration.ReadWrite apacheConfig) { + final Object cmObject = rsConfig.getProperties().get( + apacheConfig.prefixed(ApacheClientProperties.CONNECTION_MANAGER)); // Connection manager from configuration. if (cmObject != null) { @@ -369,24 +311,27 @@ private HttpClientConnectionManager getConnectionManager(final Client client, ); } } + if (apacheConfig.connectionManager.get() != null) { + return apacheConfig.connectionManager.get(); + } // Create custom connection manager. return createConnectionManager( client, - config, + rsConfig, sslContext, - useSystemProperties); + apacheConfig); } - private HttpClientConnectionManager createConnectionManager( + private static HttpClientConnectionManager createConnectionManager( final Client client, - final Configuration config, + final Configuration rsConfig, final SSLContext sslContext, - final boolean useSystemProperties) { + final ApacheConnectorConfiguration.ReadWrite apacheConfig) { - final String[] supportedProtocols = useSystemProperties ? split( + final String[] supportedProtocols = apacheConfig.useSystemProperties.get() ? split( System.getProperty("https.protocols")) : null; - final String[] supportedCipherSuites = useSystemProperties ? split( + final String[] supportedCipherSuites = apacheConfig.useSystemProperties.get() ? split( System.getProperty("https.cipherSuites")) : null; HostnameVerifier hostnameVerifier = client.getHostnameVerifier(); @@ -396,7 +341,7 @@ private HttpClientConnectionManager createConnectionManager( sslSocketFactory = new SniSSLConnectionSocketFactory( sslContext, supportedProtocols, supportedCipherSuites, hostnameVerifier); } else { - if (useSystemProperties) { + if (apacheConfig.useSystemProperties.get()) { sslSocketFactory = new SniSSLConnectionSocketFactory( (SSLSocketFactory) SSLSocketFactory.getDefault(), supportedProtocols, supportedCipherSuites, hostnameVerifier); @@ -412,13 +357,16 @@ private HttpClientConnectionManager createConnectionManager( .register("https", sslSocketFactory) .build(); - final Integer chunkSize = ClientProperties.getValue(config.getProperties(), - ClientProperties.CHUNKED_ENCODING_SIZE, ClientProperties.DEFAULT_CHUNK_SIZE, Integer.class); + final Integer chunkSize = ClientProperties.getValue( + rsConfig.getProperties(), + apacheConfig.prefixed(ClientProperties.CHUNKED_ENCODING_SIZE), + ClientProperties.DEFAULT_CHUNK_SIZE, Integer.class + ); final PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry, new ConnectionFactory(chunkSize)); - if (useSystemProperties) { + if (apacheConfig.useSystemProperties.get()) { String s = System.getProperty("http.keepAlive", "true"); if ("true".equalsIgnoreCase(s)) { s = System.getProperty("http.maxConnections", "5"); @@ -455,12 +403,13 @@ public HttpClient getHttpClient() { * {@code true}. */ public CookieStore getCookieStore() { - return cookieStore; + return clientConfiguration.cookieStore.get(); } @Override public ClientResponse apply(final ClientRequest clientRequest) throws ProcessingException { - final HttpUriRequest request = getUriHttpRequest(clientRequest); + final ApacheConnectorConfiguration.ReadWrite requestConfiguration = clientConfiguration.copyFromRequest(clientRequest); + final HttpUriRequest request = getUriHttpRequest(clientRequest, requestConfiguration); final Map clientHeadersSnapshot = writeOutBoundHeaders(clientRequest, request); final HttpHost httpHost = getHost(request); @@ -468,7 +417,7 @@ public ClientResponse apply(final ClientRequest clientRequest) throws Processing final CloseableHttpResponse response; final HttpClientContext context = HttpClientContext.create(); - if (preemptiveBasicAuth) { + if (requestConfiguration.preemptiveBasicAuthentication.get()) { final AuthCache authCache = new BasicAuthCache(); final BasicScheme basicScheme = new BasicScheme(); authCache.put(httpHost, basicScheme); @@ -476,13 +425,13 @@ public ClientResponse apply(final ClientRequest clientRequest) throws Processing } // If a request-specific CredentialsProvider exists, use it instead of the default one - CredentialsProvider credentialsProvider = - clientRequest.resolveProperty(ApacheClientProperties.CREDENTIALS_PROVIDER, CredentialsProvider.class); + CredentialsProvider credentialsProvider = requestConfiguration.credentialsProvider(clientRequest); if (credentialsProvider != null) { context.setCredentialsProvider(credentialsProvider); } context.setAttribute(JERSEY_REQUEST_ATTR_NAME, clientRequest); + context.setAttribute(JERSEY_CONF_ATTR_NAME, clientConfiguration); response = client.execute(httpHost, request, context); HeaderUtils.checkHeaderChanges(clientHeadersSnapshot, clientRequest.getHeaders(), this.getClass().getName(), clientRequest.getConfiguration()); @@ -523,8 +472,9 @@ public ClientResponse apply(final ClientRequest clientRequest) throws Processing } try { - final ConnectionClosingMechanism closingMechanism = new ConnectionClosingMechanism(clientRequest, request); - responseContext.setEntityStream(getInputStream(response, closingMechanism, () -> clientRequest.isCancelled())); + final ConnectionClosingMechanism closingMechanism = + new ConnectionClosingMechanism(clientRequest, request, requestConfiguration); + responseContext.setEntityStream(getInputStream(response, closingMechanism, clientRequest::isCancelled)); } catch (final IOException e) { LOGGER.log(Level.SEVERE, null, e); } @@ -563,15 +513,16 @@ public void close() { } } - private HttpHost getHost(final HttpUriRequest request) { + private static HttpHost getHost(final HttpUriRequest request) { return new HttpHost(request.getURI().getHost(), request.getURI().getPort(), request.getURI().getScheme()); } - private HttpUriRequest getUriHttpRequest(final ClientRequest clientRequest) { - final RequestConfig.Builder requestConfigBuilder = RequestConfig.copy(requestConfig); + private static HttpUriRequest getUriHttpRequest(final ClientRequest clientRequest, + final ApacheConnectorConfiguration.ReadWrite requestConfig) { + final RequestConfig.Builder requestConfigBuilder = RequestConfig.copy(requestConfig.requestConfig.get()); - final int connectTimeout = clientRequest.resolveProperty(ClientProperties.CONNECT_TIMEOUT, -1); - final int socketTimeout = clientRequest.resolveProperty(ClientProperties.READ_TIMEOUT, -1); + final int connectTimeout = requestConfig.connectTimeout(clientRequest); + final int socketTimeout = requestConfig.readTimeout(clientRequest).readTimeout(); if (connectTimeout >= 0) { requestConfigBuilder.setConnectTimeout(connectTimeout); @@ -580,12 +531,12 @@ private HttpUriRequest getUriHttpRequest(final ClientRequest clientRequest) { requestConfigBuilder.setSocketTimeout(socketTimeout); } - final Boolean redirectsEnabled = - clientRequest.resolveProperty(ClientProperties.FOLLOW_REDIRECTS, requestConfig.isRedirectsEnabled()); + final Boolean redirectsEnabled = clientRequest.resolveProperty( + requestConfig.prefixed(ClientProperties.FOLLOW_REDIRECTS), + requestConfig.requestConfig.get().isRedirectsEnabled()); requestConfigBuilder.setRedirectsEnabled(redirectsEnabled); - final Boolean bufferingEnabled = clientRequest.resolveProperty(ClientProperties.REQUEST_ENTITY_PROCESSING, - RequestEntityProcessing.class) == RequestEntityProcessing.BUFFERED; + final boolean bufferingEnabled = requestConfig.requestEntityProcessing(clientRequest) == RequestEntityProcessing.BUFFERED; final HttpEntity entity = getHttpEntity(clientRequest, bufferingEnabled); return RequestBuilder @@ -596,7 +547,7 @@ private HttpUriRequest getUriHttpRequest(final ClientRequest clientRequest) { .build(); } - private HttpEntity getHttpEntity(final ClientRequest clientRequest, final boolean bufferingEnabled) { + private static HttpEntity getHttpEntity(final ClientRequest clientRequest, final boolean bufferingEnabled) { final Object entity = clientRequest.getEntity(); if (entity == null) { @@ -649,7 +600,7 @@ public boolean isStreaming() { return bufferEntity(httpEntity, bufferingEnabled); } - private HttpEntity wrapHttpEntity(final ClientRequest clientRequest, final HttpEntity originalEntity) { + private static HttpEntity wrapHttpEntity(final ClientRequest clientRequest, final HttpEntity originalEntity) { final boolean bufferingEnabled = BufferedHttpEntity.class.isInstance(originalEntity); try { @@ -756,16 +707,18 @@ private static InputStream getInputStream(final CloseableHttpResponse response, * See https://github.com/eclipse-ee4j/jersey/issues/4321 * {@link ApacheClientProperties#CONNECTION_CLOSING_STRATEGY} */ - private final class ConnectionClosingMechanism { + private static final class ConnectionClosingMechanism { private ApacheConnectionClosingStrategy connectionClosingStrategy = null; private final ClientRequest clientRequest; private final HttpUriRequest apacheRequest; - private ConnectionClosingMechanism(ClientRequest clientRequest, HttpUriRequest apacheRequest) { + private ConnectionClosingMechanism(ClientRequest clientRequest, + HttpUriRequest apacheRequest, + ApacheConnectorConfiguration.ReadWrite requestConfiguration) { this.clientRequest = clientRequest; this.apacheRequest = apacheRequest; - Object closingStrategyProperty = clientRequest - .resolveProperty(ApacheClientProperties.CONNECTION_CLOSING_STRATEGY, Object.class); + Object closingStrategyProperty = clientRequest.resolveProperty( + requestConfiguration.prefixed(ApacheClientProperties.CONNECTION_CLOSING_STRATEGY), Object.class); if (closingStrategyProperty != null) { if (ApacheConnectionClosingStrategy.class.isInstance(closingStrategyProperty)) { connectionClosingStrategy = (ApacheConnectionClosingStrategy) closingStrategyProperty; @@ -773,7 +726,7 @@ private ConnectionClosingMechanism(ClientRequest clientRequest, HttpUriRequest a LOGGER.log( Level.WARNING, LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( - ApacheClientProperties.CONNECTION_CLOSING_STRATEGY, + requestConfiguration.prefixed(ApacheClientProperties.CONNECTION_CLOSING_STRATEGY), closingStrategyProperty, ApacheConnectionClosingStrategy.class.getName()) ); @@ -881,10 +834,13 @@ protected void prepareSocket(SSLSocket socket) throws IOException { if (context != null) { Object objectRequest = context.getAttribute(JERSEY_REQUEST_ATTR_NAME); + Object objectConfiguration = context.getAttribute(JERSEY_CONF_ATTR_NAME); if (objectRequest != null) { ClientRequest clientRequest = (ClientRequest) objectRequest; - SSLParamConfigurator sniConfig = SSLParamConfigurator.builder().request(clientRequest) - .setSNIHostName(clientRequest).build(); + SSLParamConfigurator.Builder builder = objectConfiguration != null + ? SSLParamConfigurator.builder((ApacheConnectorConfiguration.ReadWrite) objectConfiguration) + : SSLParamConfigurator.builder(); + SSLParamConfigurator sniConfig = builder.request(clientRequest).setSNIHostName(clientRequest).build(); sniConfig.setSNIServerName(socket); } } diff --git a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnectorConfiguration.java b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnectorConfiguration.java new file mode 100644 index 00000000000..a891023cb83 --- /dev/null +++ b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnectorConfiguration.java @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2025 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 + * http://www.eclipse.org/legal/epl-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception, which is available at + * https://www.gnu.org/software/classpath/license.html. + * + * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 + */ + +package org.glassfish.jersey.apache.connector; + +import org.apache.http.ConnectionReuseStrategy; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CookieStore; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.HttpRequestRetryHandler; +import org.apache.http.client.config.CookieSpecs; +import org.apache.http.client.config.RequestConfig; +import org.apache.http.config.ConnectionConfig; +import org.apache.http.config.Registry; +import org.apache.http.config.RegistryBuilder; +import org.apache.http.conn.ConnectionKeepAliveStrategy; +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.ManagedHttpClientConnection; +import org.apache.http.conn.routing.HttpRoute; +import org.apache.http.conn.socket.ConnectionSocketFactory; +import org.apache.http.conn.socket.LayeredConnectionSocketFactory; +import org.apache.http.conn.socket.PlainConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLConnectionSocketFactory; +import org.apache.http.conn.ssl.SSLContexts; +import org.apache.http.entity.ContentLengthStrategy; +import org.apache.http.impl.client.BasicCookieStore; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.apache.http.impl.conn.DefaultManagedHttpClientConnection; +import org.apache.http.impl.conn.ManagedHttpClientConnectionFactory; +import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; +import org.apache.http.impl.io.ChunkedOutputStream; +import org.apache.http.io.SessionOutputBuffer; +import org.apache.http.protocol.HttpContext; +import org.apache.http.util.TextUtils; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.ClientRequest; +import org.glassfish.jersey.client.innate.ClientProxy; +import org.glassfish.jersey.client.innate.ConnectorConfiguration; +import org.glassfish.jersey.client.innate.http.SSLParamConfigurator; +import org.glassfish.jersey.client.internal.HttpUrlConnectorConfiguration; +import org.glassfish.jersey.internal.util.PropertiesHelper; +import org.glassfish.jersey.internal.util.collection.Ref; + +import javax.net.ssl.HostnameVerifier; +import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLSocket; +import javax.net.ssl.SSLSocketFactory; +import javax.ws.rs.client.Client; +import javax.ws.rs.core.Configuration; +import java.io.IOException; +import java.io.OutputStream; +import java.net.Socket; +import java.net.URI; +import java.util.LinkedList; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; +import java.util.logging.Level; +import java.util.stream.Collectors; + +class ApacheConnectorConfiguration> extends ConnectorConfiguration { + private static final String JERSEY_REQUEST_ATTR_NAME = "JerseyRequestAttribute"; + + /* package */ Ref cookieStore = NullableRef.empty(); + /* package */ Ref connectionManager = NullableRef.empty(); + /* package */ Ref connectionManagerShared = NullableRef.empty(); + /* package */ Ref connectionReuseStrategy = NullableRef.empty(); + /* package */ Ref connectionClosingStrategy = NullableRef.empty(); + /* package */ Ref credentialsProvider = NullableRef.empty(); + /* package */ Ref disableCookies = NullableRef.empty(); + /* package */ Ref keepAliveStrategy = NullableRef.empty(); + /* package */ Ref preemptiveBasicAuthentication = NullableRef.empty(); + /* package */ Ref requestConfig = NullableRef.empty(); + /* package */ Ref httpRequestRetryHandler = NullableRef.empty(); + /* package */ Ref useSystemProperties = NullableRef.empty(); + + /** + *

+ * Connection Manager which will be used to create {@link org.apache.http.client.HttpClient}. + *

+ *

+ * If the property is absent a default Connection Manager will be used + * ({@link org.apache.http.impl.conn.BasicHttpClientConnectionManager}). + * If you want to use this client in multithreaded environment, be sure you override default value with + * {@link org.apache.http.impl.conn.PoolingHttpClientConnectionManager} instance. + *

+ *

+ * The property {@link ApacheClientProperties#CONNECTION_MANAGER} takes precedence over this configuration. + *

+ * + * @param httpClientConnectionManager the client connection manager used to create the + * {@link org.apache.http.client.HttpClient}. + * @return updated configuration. + */ + public A connectionManager(HttpClientConnectionManager httpClientConnectionManager) { + connectionManager.set(httpClientConnectionManager); + return self(); + } + + /** + *

+ * A value of {@code true} indicates that configured connection manager should be shared + * among multiple Jersey {@code ClientRuntime} instances. It means that closing + * a particular {@code ClientRuntime} instance does not shut down the underlying + * connection manager automatically. In such case, the connection manager life-cycle + * should be fully managed by the application code. To release all allocated resources, + * caller code should especially ensure {@link org.apache.http.conn.HttpClientConnectionManager#shutdown()} gets + * invoked eventually. + *

+ *

+ * The default value is {@code false}. + *

+ *

+ * The property {@link ApacheClientProperties#CONNECTION_MANAGER_SHARED} takes precedence over this configuration. + *

+ * + * @param shared enable or disable sharing among multiple Jersey {@code ClientRuntime} instances. + * @return updated configuration. + */ + public A connectionManagerShared(boolean shared) { + this.connectionManagerShared.set(shared); + return self(); + } + + /** + *

+ * The credential provider that should be used to retrieve + * credentials from a user. Credentials needed for proxy authentication + * are stored here as well. + *

+ *

+ * If the property is absent a default provider will be used. + *

+ *

+ * The property {@link ApacheClientProperties#CREDENTIALS_PROVIDER} takes precedence over this configuration. + *

+ * + * @param credentialsProvider the credential provider. + * @return updated configuration. + */ + public A credentialsProvider(CredentialsProvider credentialsProvider) { + this.credentialsProvider.set(credentialsProvider); + return self(); + } + + /** + *

+ * A value of {@code false} indicates the client should handle cookies + * automatically using HttpClient's default cookie policy. A value + * of {@code true} will cause the client to ignore all cookies. + *

+ *

+ * The default value is {@code false}. + *

+ *

+ * The property {@link ApacheClientProperties#DISABLE_COOKIES} takes precedence over this configuration. + *

+ * + * @param disable whether to disable and ignore the cookies. + * @return updated configuration. + */ + public A disableCookies(boolean disable) { + this.disableCookies.set(disable); + return self(); + } + + /** + *

+ * Set connection keep alive strategy for the {@link org.apache.http.client.HttpClient}. + *

+ *

+ * If the property is absent the default keep alive strategy of the Apache HTTP library will be used. + *

+ *

+ * The property {@link ApacheClientProperties#KEEPALIVE_STRATEGY} takes precedence over this configuration. + *

+ * + * @param keepAliveStrategy the keep alive strategy. + * @return updated configuration. + */ + public A keepAliveStrategy(ConnectionKeepAliveStrategy keepAliveStrategy) { + this.keepAliveStrategy.set(keepAliveStrategy); + return self(); + } + + /** + *

+ * A value of {@code true} indicates that a client should send an + * authentication request even before the server gives a 401 + * response. + *

+ *

+ * The default value is {@code false}. + *

+ *

+ * The property {@link ApacheClientProperties#PREEMPTIVE_BASIC_AUTHENTICATION} takes precedence over this configuration. + *

+ * + * @param enable indicate whether a client should send authentication request even before the server gives a 401 + * response. + * @return updated configuration. + */ + public A preemptiveBasicAuthentication(boolean enable) { + this.preemptiveBasicAuthentication.set(enable); + return self(); + } + + /** + *

+ * Request configuration for the {@link org.apache.http.client.HttpClient}. + * Http parameters which will be used to create {@link org.apache.http.client.HttpClient}. + *

+ *

+ * If the property is absent the default request configuration will be used. + *

+ *

+ * The property {@link ApacheClientProperties#REQUEST_CONFIG} takes precedence over this configuration. + *

+ * + * @param requestConfig the apache request config. + * @return updated configuration. + */ + public A requestConfig(RequestConfig requestConfig) { + this.requestConfig.set(requestConfig); + return self(); + } + + /** + *

+ * HttpRequestRetryHandler which will be used to create {@link org.apache.http.client.HttpClient}. + *

+ *

+ * If the property is absent a default retry handler will be used + * ({@link org.apache.http.impl.client.DefaultHttpRequestRetryHandler}). + *

+ *

+ * The property {@link ApacheClientProperties#RETRY_HANDLER} takes precedence over this configuration. + *

+ * + * @param retryHandler the HTTP request retry handler. + * @return updated configuration. + */ + public A retryHandler(HttpRequestRetryHandler retryHandler) { + this.httpRequestRetryHandler.set(retryHandler); + return self(); + } + + /** + *

+ * Set the connection reuse strategy for the {@link org.apache.http.client.HttpClient}. + *

+ *

+ * If the property is absent the default reuse strategy of the Apache HTTP library will be used. + *

+ *

+ * The property {@link ApacheClientProperties#REUSE_STRATEGY} takes precedence over this configuration. + *

+ * + * @param reuseStrategy the connection reuse strategy. + * @return updated configuration. + */ + public A reuseStrategy(ConnectionReuseStrategy reuseStrategy) { + this.connectionReuseStrategy.set(reuseStrategy); + return self(); + } + + /** + *

+ * A value of {@code false} indicates the client will use default ApacheConnector params. A value + * of {@code true} will cause the client to take into account the system properties + * {@code https.protocols}, {@code https.cipherSuites}, {@code http.keepAlive}, + * {@code http.maxConnections}. + *

+ *

+ * Default value is {@code false}. + *

+ *

+ * The property {@link ApacheClientProperties#USE_SYSTEM_PROPERTIES} takes precedence over this configuration. + *

+ * + * @param enable enable or disable usage of the system properties. + * @return updated configuration. + */ + public A useSystemProperties(boolean enable) { + this.useSystemProperties.set(enable); + return self(); + } + + /* package */ ApacheConnectorConfiguration.ReadWrite rw() { + final ApacheConnectorConfiguration.ReadWrite readWrite = this instanceof ApacheConnectorConfiguration.ReadWrite + ? ((ApacheConnectorConfiguration.ReadWrite) this).instance() + : new ApacheConnectorConfiguration.ReadWrite(); + readWrite.setNonEmpty(this); + return readWrite; + } + + + protected static class ReadWrite + extends ApacheConnectorConfiguration + implements ConnectorConfiguration.Read { + + @Override + public > void setNonEmpty(X otherC) { + ApacheConnectorConfiguration other = (ApacheConnectorConfiguration) otherC; + Read.super.setNonEmpty(other); + + ((NullableRef) this.cookieStore) + .setNonEmpty((NullableRef) other.cookieStore); + ((NullableRef) this.connectionManager) + .setNonEmpty((NullableRef) other.connectionManager); + ((NullableRef) this.connectionManagerShared) + .setNonEmpty((NullableRef) other.connectionManagerShared); + ((NullableRef) this.connectionReuseStrategy).setNonEmpty( + (NullableRef) other.connectionReuseStrategy); + ((NullableRef) this.credentialsProvider).setNonEmpty( + (NullableRef) other.credentialsProvider); + ((NullableRef) this.disableCookies).setNonEmpty( + (NullableRef) other.disableCookies); + ((NullableRef) this.keepAliveStrategy).setNonEmpty( + (NullableRef) other.keepAliveStrategy); + ((NullableRef) this.preemptiveBasicAuthentication).setNonEmpty( + (NullableRef) other.preemptiveBasicAuthentication); + ((NullableRef) this.requestConfig).setNonEmpty( + (NullableRef) other.requestConfig); + ((NullableRef) this.httpRequestRetryHandler).setNonEmpty( + (NullableRef) other.httpRequestRetryHandler); + ((NullableRef) this.useSystemProperties).setNonEmpty( + (NullableRef) other.useSystemProperties); + } + + @Override + public ReadWrite init() { + connectTimeout.ifEmptySet(-1); + readTimeout.ifEmptySet(-1); + Read.super.init(); + ((NullableRef) connectionManagerShared).ifEmptySet(false); + ((NullableRef) disableCookies).ifEmptySet(false); + ((NullableRef) preemptiveBasicAuthentication).ifEmptySet(false); + ((NullableRef) useSystemProperties).ifEmptySet(false); + return self(); + } + + ReadWrite fromClient(Client client, Configuration configuration) { + final ReadWrite clientConfiguration = copyFromClient(configuration); + clientConfiguration.chunkSize(configuration.getProperties()); + return fromClient(client, configuration, clientConfiguration); + } + + private static ReadWrite fromClient(Client client, Configuration configuration, ReadWrite rw) { + final Map properties = configuration.getProperties(); + + final Object connectionManager = properties.get(rw.prefixed(ApacheClientProperties.CONNECTION_MANAGER)); + if (connectionManager != null) { + if (!(connectionManager instanceof HttpClientConnectionManager)) { + LOGGER.log( + Level.WARNING, + org.glassfish.jersey.apache.connector.LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( + rw.prefixed(ApacheClientProperties.CONNECTION_MANAGER), + connectionManager.getClass().getName(), + HttpClientConnectionManager.class.getName()) + ); + } else { + rw.connectionManager.set((HttpClientConnectionManager) connectionManager); + } + } + + final Object keepAliveStrategy = properties.get(rw.prefixed(ApacheClientProperties.KEEPALIVE_STRATEGY)); + if (keepAliveStrategy != null) { + if (!(keepAliveStrategy instanceof ConnectionKeepAliveStrategy)) { + LOGGER.log( + Level.WARNING, + org.glassfish.jersey.apache.connector.LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( + rw.prefixed(ApacheClientProperties.KEEPALIVE_STRATEGY), + keepAliveStrategy.getClass().getName(), + ConnectionKeepAliveStrategy.class.getName()) + ); + } else { + rw.keepAliveStrategy.set((ConnectionKeepAliveStrategy) keepAliveStrategy); + } + } + + final Object reuseStrategy = properties.get(rw.prefixed(ApacheClientProperties.REUSE_STRATEGY)); + if (reuseStrategy != null) { + if (!(reuseStrategy instanceof ConnectionReuseStrategy)) { + LOGGER.log( + Level.WARNING, + org.glassfish.jersey.apache.connector.LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( + rw.prefixed(ApacheClientProperties.REUSE_STRATEGY), + reuseStrategy.getClass().getName(), + ConnectionReuseStrategy.class.getName()) + ); + } else { + rw.reuseStrategy((ConnectionReuseStrategy) reuseStrategy); + } + } + + final Object reqConfig = properties.get(rw.prefixed(ApacheClientProperties.REQUEST_CONFIG)); + if (reqConfig != null) { + if (!(reqConfig instanceof RequestConfig)) { + LOGGER.log( + Level.WARNING, + LocalizationMessages.IGNORING_VALUE_OF_PROPERTY( + rw.prefixed(ApacheClientProperties.REQUEST_CONFIG), + reqConfig.getClass().getName(), + RequestConfig.class.getName()) + ); + } else { + rw.requestConfig.set((RequestConfig) reqConfig); + } + } + + if (properties.containsKey(rw.prefixed(ApacheClientProperties.USE_SYSTEM_PROPERTIES))) { + rw.useSystemProperties.set( + PropertiesHelper.isProperty(properties.get(rw.prefixed(ApacheClientProperties.USE_SYSTEM_PROPERTIES))) + ); + } + return rw; + } + + @Override + protected ReadWrite chunkSize(Map properties) { + return super.chunkSize(properties); + } + + /* package */ boolean connectionManagerShared(Map properties) { + connectionManagerShared.set( + PropertiesHelper.getValue(properties, prefixed(ApacheClientProperties.CONNECTION_MANAGER_SHARED), + connectionManagerShared.get(), null) + ); + return connectionManagerShared.get(); + } + + /* package */ CredentialsProvider credentialsProvider(Map properties) { + final Object credentialsProvider = properties.get(prefixed(ApacheClientProperties.CREDENTIALS_PROVIDER)); + if ((credentialsProvider instanceof CredentialsProvider)) { + credentialsProvider((CredentialsProvider) credentialsProvider); + } + return this.credentialsProvider.get(); + } + + /* package */ CredentialsProvider credentialsProvider(ClientRequest clientRequest) { + final CredentialsProvider credentialsProvider = clientRequest.resolveProperty( + prefixed(ApacheClientProperties.CREDENTIALS_PROVIDER), CredentialsProvider.class); + if (credentialsProvider != null) { + this.credentialsProvider.set(credentialsProvider); + } + return this.credentialsProvider.get(); + } + + /* package */ Boolean disableCookies(Map properties) { + Object property = properties.get(prefixed(ApacheClientProperties.DISABLE_COOKIES)); + if (property != null) { + this.disableCookies.set(PropertiesHelper.isProperty(property)); + } + return disableCookies.get(); + } + + /* package */ boolean preemptiveBasicAuthentication(Map properties) { + final Boolean preemptiveBasicAuthProperty = (Boolean) properties.get( + prefixed(ApacheClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION)); + if (preemptiveBasicAuthProperty != null) { + preemptiveBasicAuthentication(preemptiveBasicAuthProperty); + } + return preemptiveBasicAuthentication.get(); + } + + /* package */ HttpRequestRetryHandler retryHandler(Map properties) { + final Object retryHandler = properties.get(prefixed(ApacheClientProperties.RETRY_HANDLER)); + if ((retryHandler instanceof HttpRequestRetryHandler)) { + this.retryHandler((HttpRequestRetryHandler) retryHandler); + } + return this.httpRequestRetryHandler.get(); + } + + @Override + public ReadWrite instance() { + return new ReadWrite(); + } + + @Override + public ReadWrite me() { + return this; + } + } +} diff --git a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnectorProvider.java b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnectorProvider.java index d97204fa62b..2e1dcacdb3a 100644 --- a/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnectorProvider.java +++ b/connectors/apache-connector/src/main/java/org/glassfish/jersey/apache/connector/ApacheConnectorProvider.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2025 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 @@ -100,9 +100,19 @@ */ public class ApacheConnectorProvider implements ConnectorProvider { + private final Config config; + + public ApacheConnectorProvider() { + config = config(); + } + + private ApacheConnectorProvider(Config config) { + this.config = config; + } + @Override public Connector getConnector(final Client client, final Configuration runtimeConfig) { - return new ApacheConnector(client, runtimeConfig); + return new ApacheConnector(client, runtimeConfig, config); } /** @@ -159,4 +169,23 @@ private static ApacheConnector getConnector(final Configurable component) { throw new IllegalArgumentException(LocalizationMessages.EXPECTED_CONNECTOR_PROVIDER_NOT_USED()); } } + + /** + * Instantiate a builder allowing to configure the ApacheConnectorProvider. + * @return a new {@link Config} instance. + */ + public static Config config() { + return new Config(); + } + + public static final class Config extends ApacheConnectorConfiguration { + + private Config() { + } + + public ApacheConnectorProvider build() { + return new ApacheConnectorProvider(this); + } + } + } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java b/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java index e57facf35d2..dc39f7490e6 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/HttpUrlConnectorProvider.java @@ -217,6 +217,10 @@ public Connector getConnector(final Client client, final Configuration configura protected Connector createHttpUrlConnector(Client client, ConnectionFactory connectionFactory, int chunkSize, boolean fixLengthStreaming, boolean setMethodWorkaround) { + config.connectionFactory(connectionFactory) + .chunkSize(chunkSize) + .useFixedLengthStreaming(fixLengthStreaming) + .useSetMethodWorkaround(setMethodWorkaround); return new HttpUrlConnector(client, client.getConfiguration(), config); } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/ConnectorConfiguration.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/ConnectorConfiguration.java index f18d02b68f6..03ab2c5f2c9 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/innate/ConnectorConfiguration.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/ConnectorConfiguration.java @@ -20,6 +20,7 @@ import org.glassfish.jersey.client.ClientRequest; import org.glassfish.jersey.client.RequestEntityProcessing; import org.glassfish.jersey.client.innate.http.SSLParamConfigurator; +import org.glassfish.jersey.client.internal.LocalizationMessages; import org.glassfish.jersey.internal.PropertiesResolver; import javax.net.ssl.SSLContext; @@ -39,6 +40,7 @@ import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.logging.Logger; /** * Configuration object to use for configuring the client connectors and HTTP request processing. @@ -47,7 +49,11 @@ * @param the connector configuration subtype. */ public class ConnectorConfiguration> { + + protected static final Logger LOGGER = Logger.getLogger(ConnectorConfiguration.class.getName()); + protected final NullableRef connectTimeout = NullableRef.empty(); + protected final NullableRef chunkSize = NullableRef.empty(); protected final NullableRef expect100Continue = NullableRef.empty(); protected final NullableRef expect100continueThreshold = NullableRef.empty(); protected final NullableRef followRedirects = NullableRef.empty(); @@ -261,6 +267,24 @@ protected E self() { return (E) this; } + protected E chunkSize(Map properties) { + chunkSize.ifEmptySet(ClientProperties.DEFAULT_CHUNK_SIZE); + int computedChunkSize = ClientProperties.getValue(properties, + _prefixed(ClientProperties.CHUNKED_ENCODING_SIZE), chunkSize.get(), Integer.class); + if (computedChunkSize < 0) { + LOGGER.warning(LocalizationMessages.NEGATIVE_CHUNK_SIZE(computedChunkSize, chunkSize.get())); + } else { + chunkSize.set(computedChunkSize); + } + + return self(); + } + + protected String _prefixed(String property) { + return prefix.ifPresentOrElse("") + property; + } + + /** *

* A reference to a value. The reference can be empty, but unlike the {@code Optional}, once a value is set, @@ -445,6 +469,7 @@ protected interface Read & Read> */ public default > void setNonEmpty(X other) { me().connectTimeout.setNonEmpty(other.connectTimeout); + me().chunkSize.setNonEmpty(other.chunkSize); me().expect100Continue.setNonEmpty(other.expect100Continue); me().expect100continueThreshold.setNonEmpty(other.expect100continueThreshold); me().followRedirects.setNonEmpty(other.followRedirects); @@ -552,6 +577,10 @@ public default CC copyFromRequest(ClientRequest request) { return requestConfiguration; } + default int chunkSize() { + return me().chunkSize.get(); + } + @Override default String getSniHostNameProperty(Configuration configuration) { Object property = configuration.getProperty(prefixed(ClientProperties.SNI_HOST_NAME)); @@ -649,6 +678,10 @@ public default Optional proxy(ClientRequest request, URI requestUri return proxy; } + public default Optional proxy(Configuration configuration) { + return ClientProxy.proxyFromConfiguration(new PrefixedConfiguration(me().prefix.get(), configuration)); + } + /** * Update {@link #readTimeout(int) read timeout} based on the HTTP request properties. * @@ -712,6 +745,23 @@ public default SSLContext sslContext(Client client, ClientRequest request) { return supplier == null ? client.getSslContext() : supplier.get(); } + /** + * Get {@link SSLContext} either from the {@link ClientProperties#SSL_CONTEXT_SUPPLIER}, or from this configuration, + * or from the {@link Client#getSslContext()} in this order. + * + * @param client the client used to get the {@link SSLContext}. + * @return the {@link SSLContext}. + */ + public default SSLContext sslContext(Client client) { + @SuppressWarnings("unchecked") + Supplier supplier = + (Supplier) client.getConfiguration().getProperty(prefixed(ClientProperties.SSL_CONTEXT_SUPPLIER)); + if (supplier == null) { + supplier = me().sslContextSupplier.get(); + } + return supplier == null ? client.getSslContext() : supplier.get(); + } + public default String prefixed(String propertyName) { return me().prefix.get() + propertyName; } @@ -832,7 +882,7 @@ protected static class PrefixedConfiguration implements Configuration { private final String prefix; private final Configuration inner; - private PrefixedConfiguration(String prefix, Configuration inner) { + protected PrefixedConfiguration(String prefix, Configuration inner) { this.prefix = prefix; this.inner = inner; } diff --git a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java index e2fc848049f..0cc7037f2ce 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnector.java @@ -434,7 +434,7 @@ private ClientResponse _apply(final ClientRequest request) throws IOException { if (requestConfiguration.useFixedLengthStreaming.get() && length > 0) { uc.setFixedLengthStreamingMode(length); } else if (entityProcessing == RequestEntityProcessing.CHUNKED) { - uc.setChunkedStreamingMode(requestConfiguration.chunkSize.get()); + uc.setChunkedStreamingMode(requestConfiguration.chunkSize()); } } uc.setDoOutput(true); diff --git a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnectorConfiguration.java b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnectorConfiguration.java index 9124a691d98..485e511d53c 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnectorConfiguration.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/internal/HttpUrlConnectorConfiguration.java @@ -47,24 +47,16 @@ public abstract class HttpUrlConnectorConfiguration connectionFactory = NullableRef.empty(); - protected Ref chunkSize = NullableRef.empty(); /* package */ Ref isRestrictedHeaderPropertySet = NullableRef.empty(); protected Ref useFixedLengthStreaming = NullableRef.empty(); protected Ref useSetMethodWorkaround = NullableRef.empty(); protected void preInit(Map properties) { connectionFactory.ifEmptySet(DEFAULT_CONNECTION_FACTORY); - ((NullableRef) chunkSize).ifEmptySet(ClientProperties.DEFAULT_CHUNK_SIZE); ((NullableRef) useFixedLengthStreaming).ifEmptySet(Boolean.FALSE); ((NullableRef) useSetMethodWorkaround).ifEmptySet(Boolean.FALSE); - int computedChunkSize = ClientProperties.getValue(properties, - _prefixed(ClientProperties.CHUNKED_ENCODING_SIZE), chunkSize.get(), Integer.class); - if (computedChunkSize < 0) { - LOGGER.warning(LocalizationMessages.NEGATIVE_CHUNK_SIZE(computedChunkSize, chunkSize.get())); - } else { - chunkSize.set(computedChunkSize); - } + chunkSize(properties); useFixedLengthStreaming(ClientProperties.getValue(properties, _prefixed(HttpUrlConnectorProvider.USE_FIXED_LENGTH_STREAMING), @@ -74,10 +66,6 @@ protected void preInit(Map properties) { useSetMethodWorkaround.get(), Boolean.class)); } - private String _prefixed(String property) { - return prefix.ifPresentOrElse("") + property; - } - /** * Set a custom {@link java.net.HttpURLConnection} factory. * @@ -218,6 +206,22 @@ ReadWrite fromClient(Configuration configuration) { ReadWrite clientConfiguration = copyFromClient(configuration); clientConfiguration.preInit(configuration.getProperties()); + int computedChunkSize = ClientProperties.getValue(configuration.getProperties(), + prefixed(ClientProperties.CHUNKED_ENCODING_SIZE), clientConfiguration.chunkSize.get(), Integer.class); + if (computedChunkSize < 0) { + LOGGER.warning(LocalizationMessages.NEGATIVE_CHUNK_SIZE(computedChunkSize, clientConfiguration.chunkSize.get())); + } else { + clientConfiguration.chunkSize.set(computedChunkSize); + } + + useFixedLengthStreaming(ClientProperties.getValue(configuration.getProperties(), + prefixed(HttpUrlConnectorProvider.USE_FIXED_LENGTH_STREAMING), + clientConfiguration.useFixedLengthStreaming.get(), Boolean.class)); + useSetMethodWorkaround(ClientProperties.getValue(configuration.getProperties(), + prefixed(HttpUrlConnectorProvider.SET_METHOD_WORKAROUND), + clientConfiguration.useSetMethodWorkaround.get(), Boolean.class)); + + // check if sun.net.http.allowRestrictedHeaders system property has been set and log the result // the property is being cached in the HttpURLConnection, so this is only informative - there might // already be some connection(s), that existed before the property was set/changed.