From 72aad335695ebff3b6a380fa421e7d1f3eca3703 Mon Sep 17 00:00:00 2001 From: jansupol Date: Wed, 22 May 2024 17:33:19 +0200 Subject: [PATCH] Unify the SniConfigurator with other branches Signed-off-by: jansupol --- .../jersey/client/ClientProperties.java | 2 +- .../innate/http/SSLParamConfigurator.java | 64 +++++-- .../client/innate/http/SniConfigurator.java | 18 -- .../innate/http/SSLParamConfiguratorTest.java | 158 ++++++++++++++++++ docs/src/main/docbook/appendix-properties.xml | 2 +- 5 files changed, 209 insertions(+), 35 deletions(-) create mode 100644 core-client/src/test/java/org/glassfish/jersey/client/innate/http/SSLParamConfiguratorTest.java diff --git a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java index 5ea9a6d96e..5df849ab30 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/ClientProperties.java @@ -494,7 +494,7 @@ public final class ClientProperties { *

* @since 2.43 */ - public static final String SNI_HOST_NAME = "jersey.config.client.snihostname"; + public static final String SNI_HOST_NAME = "jersey.config.client.sniHostName"; /** *

The {@link javax.net.ssl.SSLContext} {@link java.util.function.Supplier} to be used to set ssl context in the current diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java index 8c9607bc03..5055c12f61 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SSLParamConfigurator.java @@ -18,6 +18,7 @@ import org.glassfish.jersey.client.ClientProperties; import org.glassfish.jersey.client.ClientRequest; +import org.glassfish.jersey.http.HttpHeaders; import org.glassfish.jersey.internal.PropertiesResolver; import javax.net.ssl.SSLEngine; @@ -29,6 +30,7 @@ import java.net.URI; import java.net.UnknownHostException; import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Optional; @@ -44,11 +46,11 @@ public final class SSLParamConfigurator { * Builder of the {@link SSLParamConfigurator} instance. */ public static final class Builder { - private ClientRequest clientRequest; private URI uri; - private Map> httpHeaders; - private boolean setAlways = false; + private String sniHostNameHeader = null; private String sniHostPrecedence = null; + private boolean setAlways = false; + /** * Sets the {@link ClientRequest} instance. @@ -56,9 +58,19 @@ public static final class Builder { * @return the builder instance */ public Builder request(ClientRequest clientRequest) { - this.clientRequest = clientRequest; - this.httpHeaders = null; - this.uri = null; + this.sniHostNameHeader = getSniHostNameHeader(clientRequest.getHeaders()); + this.sniHostPrecedence = resolveSniHostNameProperty(clientRequest); + this.uri = clientRequest.getUri(); + return this; + } + + /** + * Sets the SNIHostName from the {@link Configuration} instance. + * @param configuration the {@link Configuration} + * @return the builder instance + */ + public Builder configuration(Configuration configuration) { + this.sniHostPrecedence = getSniHostNameProperty(configuration); return this; } @@ -68,7 +80,6 @@ public Builder request(ClientRequest clientRequest) { * @return the builder instance */ public Builder uri(URI uri) { - this.clientRequest = null; this.uri = uri; return this; } @@ -79,8 +90,7 @@ public Builder uri(URI uri) { * @return the builder instance */ public Builder headers(Map> httpHeaders) { - this.clientRequest = null; - this.httpHeaders = httpHeaders; + this.sniHostNameHeader = getSniHostNameHeader(httpHeaders); return this; } @@ -129,7 +139,7 @@ public Builder setSNIHostName(String hostName) { * @return the builder instance. */ public Builder setSNIHostName(Configuration configuration) { - return setSNIHostName((String) configuration.getProperty(ClientProperties.SNI_HOST_NAME)); + return setSNIHostName(getSniHostNameProperty(configuration)); } /** @@ -148,7 +158,7 @@ public Builder setSNIHostName(Configuration configuration) { * @return the builder instance. */ public Builder setSNIHostName(PropertiesResolver resolver) { - return setSNIHostName(resolver.resolveProperty(ClientProperties.SNI_HOST_NAME, String.class)); + return setSNIHostName(resolveSniHostNameProperty(resolver)); } /** @@ -158,14 +168,38 @@ public Builder setSNIHostName(PropertiesResolver resolver) { public SSLParamConfigurator build() { return new SSLParamConfigurator(this); } + + private static String getSniHostNameHeader(Map> httpHeaders) { + List hostHeaders = httpHeaders.get(HttpHeaders.HOST); + if (hostHeaders == null || hostHeaders.get(0) == null) { + return null; + } + + final String hostHeader = hostHeaders.get(0).toString(); + return hostHeader; + } + + private static String resolveSniHostNameProperty(PropertiesResolver resolver) { + String property = resolver.resolveProperty(ClientProperties.SNI_HOST_NAME, String.class); + if (property == null) { + property = resolver.resolveProperty(ClientProperties.SNI_HOST_NAME.toLowerCase(Locale.ROOT), String.class); + } + return property; + } + + private static String getSniHostNameProperty(Configuration configuration) { + Object property = configuration.getProperty(ClientProperties.SNI_HOST_NAME); + if (property == null) { + property = configuration.getProperty(ClientProperties.SNI_HOST_NAME.toLowerCase(Locale.ROOT)); + } + return (String) property; + } } private SSLParamConfigurator(SSLParamConfigurator.Builder builder) { - final Map> httpHeaders = - builder.clientRequest != null ? builder.clientRequest.getHeaders() : builder.httpHeaders; - this.uri = builder.clientRequest != null ? builder.clientRequest.getUri() : builder.uri; + uri = builder.uri; if (builder.sniHostPrecedence == null) { - sniConfigurator = SniConfigurator.createWhenHostHeader(uri, httpHeaders, builder.setAlways); + sniConfigurator = SniConfigurator.createWhenHostHeader(uri, builder.sniHostNameHeader, builder.setAlways); } else { // Do not set SNI always, the property can be used to turn the SNI off sniConfigurator = SniConfigurator.createWhenHostHeader(uri, builder.sniHostPrecedence, false); diff --git a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java index 403ce48e99..733e4d5499 100644 --- a/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java +++ b/core-client/src/main/java/org/glassfish/jersey/client/innate/http/SniConfigurator.java @@ -50,24 +50,6 @@ String getHostName() { return hostName; } - /** - * Create {@link SniConfigurator} when {@link HttpHeaders#HOST} is set different from the request URI host - * (or {@code whenDiffer}.is false). - * @param hostUri the Uri of the HTTP request - * @param headers the HttpHeaders - * @param whenDiffer create {@SniConfigurator only when different from the request URI host} - * @return Optional {@link SniConfigurator} or empty when {@link HttpHeaders#HOST} is equal to the requestHost - */ - static Optional createWhenHostHeader(URI hostUri, Map> headers, boolean whenDiffer) { - List hostHeaders = headers.get(HttpHeaders.HOST); - if (hostHeaders == null || hostHeaders.get(0) == null) { - return Optional.empty(); - } - - final String hostHeader = hostHeaders.get(0).toString(); - return createWhenHostHeader(hostUri, hostHeader, whenDiffer); - } - /** * Create {@link SniConfigurator} when {@code sniHost} is set different from the request URI host * (or {@code whenDiffer}.is false). diff --git a/core-client/src/test/java/org/glassfish/jersey/client/innate/http/SSLParamConfiguratorTest.java b/core-client/src/test/java/org/glassfish/jersey/client/innate/http/SSLParamConfiguratorTest.java new file mode 100644 index 0000000000..41f8fa74c6 --- /dev/null +++ b/core-client/src/test/java/org/glassfish/jersey/client/innate/http/SSLParamConfiguratorTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2024 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.client.innate.http; + +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.client.ClientProperties; +import org.glassfish.jersey.client.ClientRequest; +import org.glassfish.jersey.client.JerseyClient; +import org.glassfish.jersey.http.HttpHeaders; +import org.glassfish.jersey.internal.MapPropertiesDelegate; +import org.glassfish.jersey.internal.PropertiesDelegate; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; +import org.junit.jupiter.api.Test; + +import javax.ws.rs.client.ClientBuilder; +import javax.ws.rs.core.MultivaluedHashMap; +import java.net.URI; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; +import java.util.Map; + +public class SSLParamConfiguratorTest { + @Test + public void testNoHost() { + final URI uri = URI.create("http://xxx.com:8080"); + final JerseyClient client = (JerseyClient) ClientBuilder.newClient(); + final ClientConfig config = client.getConfiguration(); + final PropertiesDelegate delegate = new MapPropertiesDelegate(); + ClientRequest request = new ClientRequest(uri, config, delegate) {}; + SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build(); + MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(false)); + } + + @Test + public void testHostHeaderHasPrecedence() { + final URI uri = URI.create("http://xxx.com:8080"); + final JerseyClient client = (JerseyClient) ClientBuilder.newClient(); + final ClientConfig config = client.getConfiguration(); + final PropertiesDelegate delegate = new MapPropertiesDelegate(); + ClientRequest request = new ClientRequest(uri, config, delegate) {}; + request.getHeaders().add(HttpHeaders.HOST, "yyy.com"); + SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build(); + MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true)); + MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("yyy.com")); + } + + @Test + public void testPropertyOnClientHasPrecedence() { + final URI uri = URI.create("http://xxx.com:8080"); + final JerseyClient client = (JerseyClient) ClientBuilder.newClient(); + final ClientConfig config = client.getConfiguration(); + final PropertiesDelegate delegate = new MapPropertiesDelegate(); + client.property(ClientProperties.SNI_HOST_NAME, "yyy.com"); + ClientRequest request = new ClientRequest(uri, config, delegate) {}; + SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build(); + MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true)); + MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("yyy.com")); + } + + @Test + public void testPropertyOnDelegateHasPrecedence() { + final URI uri = URI.create("http://xxx.com:8080"); + final JerseyClient client = (JerseyClient) ClientBuilder.newClient(); + final ClientConfig config = client.getConfiguration(); + final PropertiesDelegate delegate = new MapPropertiesDelegate(); + client.property(ClientProperties.SNI_HOST_NAME, "yyy.com"); + delegate.setProperty(ClientProperties.SNI_HOST_NAME, "zzz.com"); + ClientRequest request = new ClientRequest(uri, config, delegate) {}; + SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build(); + MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true)); + MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("zzz.com")); + } + + @Test + public void testPropertyOnDelegateHasPrecedenceOverHost() { + final URI uri = URI.create("http://xxx.com:8080"); + final JerseyClient client = (JerseyClient) ClientBuilder.newClient(); + final ClientConfig config = client.getConfiguration(); + final PropertiesDelegate delegate = new MapPropertiesDelegate(); + client.property(ClientProperties.SNI_HOST_NAME, "yyy.com"); + delegate.setProperty(ClientProperties.SNI_HOST_NAME, "zzz.com"); + ClientRequest request = new ClientRequest(uri, config, delegate) {}; + request.getHeaders().add(HttpHeaders.HOST, "www.com"); + SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build(); + MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true)); + MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("zzz.com")); + } + + @Test + public void testDisableSni() { + final URI uri = URI.create("http://xxx.com:8080"); + final JerseyClient client = (JerseyClient) ClientBuilder.newClient(); + final ClientConfig config = client.getConfiguration(); + final PropertiesDelegate delegate = new MapPropertiesDelegate(); + client.property(ClientProperties.SNI_HOST_NAME, "yyy.com"); + delegate.setProperty(ClientProperties.SNI_HOST_NAME, "xxx.com"); + ClientRequest request = new ClientRequest(uri, config, delegate) {}; + request.getHeaders().add(HttpHeaders.HOST, "www.com"); + SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build(); + MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(false)); + MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("xxx.com")); + } + + @Test + public void testLowerCasePropertyOnClientHasPrecedence() { + final URI uri = URI.create("http://xxx.com:8080"); + final JerseyClient client = (JerseyClient) ClientBuilder.newClient(); + final ClientConfig config = client.getConfiguration(); + final PropertiesDelegate delegate = new MapPropertiesDelegate(); + client.property(ClientProperties.SNI_HOST_NAME.toLowerCase(Locale.ROOT), "yyy.com"); + ClientRequest request = new ClientRequest(uri, config, delegate) {}; + request.getHeaders().add(HttpHeaders.HOST, "www.com"); + SSLParamConfigurator configurator = SSLParamConfigurator.builder().request(request).build(); + MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true)); + MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("yyy.com")); + } + + @Test + public void testUriAndHeadersAndConfig() { + final URI uri = URI.create("http://xxx.com:8080"); + final JerseyClient client = (JerseyClient) ClientBuilder.newClient(); + Map> httpHeaders = new MultivaluedHashMap<>(); + httpHeaders.put(HttpHeaders.HOST, Collections.singletonList("www.com")); + SSLParamConfigurator configurator = SSLParamConfigurator.builder() + .uri(uri) + .headers(httpHeaders) + .configuration(client.getConfiguration()) + .build(); + MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true)); + MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("www.com")); + + client.property(ClientProperties.SNI_HOST_NAME, "yyy.com"); + configurator = SSLParamConfigurator.builder() + .uri(uri) + .headers(httpHeaders) + .configuration(client.getConfiguration()) + .build(); + MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true)); + MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("yyy.com")); + } +} diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml index 30946b612c..d2c23fba38 100644 --- a/docs/src/main/docbook/appendix-properties.xml +++ b/docs/src/main/docbook/appendix-properties.xml @@ -1097,7 +1097,7 @@ &jersey.client.ClientProperties.SNI_HOST_NAME; (Jersey 2.43 or later) - jersey.config.client.snihostname + jersey.config.client.sniHostName Sets the host name to be used for calculating the javax.net.ssl.SNIHostName