Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unify the SniConfigurator with other branches #5652

Merged
merged 1 commit into from
May 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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