Skip to content

Commit

Permalink
Unify the SniConfigurator with other branches
Browse files Browse the repository at this point in the history
Signed-off-by: jansupol <jan.supol@oracle.com>
  • Loading branch information
jansupol committed May 23, 2024
1 parent e2994ff commit 63506be
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -494,7 +494,7 @@ public final class ClientProperties {
* </p>
* @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";

/**
* <p>The {@link javax.net.ssl.SSLContext} {@link java.util.function.Supplier} to be used to set ssl context in the current
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;

Expand All @@ -44,21 +46,31 @@ public final class SSLParamConfigurator {
* Builder of the {@link SSLParamConfigurator} instance.
*/
public static final class Builder {
private ClientRequest clientRequest;
private URI uri;
private Map<String, List<Object>> httpHeaders;
private boolean setAlways = false;
private String sniHostNameHeader = null;
private String sniHostPrecedence = null;
private boolean setAlways = false;


/**
* Sets the {@link ClientRequest} instance.
* @param clientRequest the {@link ClientRequest}
* @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;
}

Expand All @@ -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;
}
Expand All @@ -79,8 +90,7 @@ public Builder uri(URI uri) {
* @return the builder instance
*/
public Builder headers(Map<String, List<Object>> httpHeaders) {
this.clientRequest = null;
this.httpHeaders = httpHeaders;
this.sniHostNameHeader = getSniHostNameHeader(httpHeaders);
return this;
}

Expand Down Expand Up @@ -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));
}

/**
Expand All @@ -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));
}

/**
Expand All @@ -158,14 +168,38 @@ public Builder setSNIHostName(PropertiesResolver resolver) {
public SSLParamConfigurator build() {
return new SSLParamConfigurator(this);
}

private static String getSniHostNameHeader(Map<String, List<Object>> httpHeaders) {
List<Object> 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<String, List<Object>> 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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<SniConfigurator> createWhenHostHeader(URI hostUri, Map<String, List<Object>> headers, boolean whenDiffer) {
List<Object> 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).
Expand Down
Original file line number Diff line number Diff line change
@@ -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<String, List<Object>> 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"));
}
}
2 changes: 1 addition & 1 deletion docs/src/main/docbook/appendix-properties.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1097,7 +1097,7 @@
</row>
<row>
<entry>&jersey.client.ClientProperties.SNI_HOST_NAME; (Jersey 2.43 or later)</entry>
<entry><literal>jersey.config.client.snihostname</literal></entry>
<entry><literal>jersey.config.client.sniHostName</literal></entry>
<entry>
<para>
Sets the host name to be used for calculating the <literal>javax.net.ssl.SNIHostName</literal>
Expand Down

0 comments on commit 63506be

Please sign in to comment.