Support SNI for JNH connector

Signed-off-by: jansupol <jan.supol@oracle.com>
diff --git a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpClientProperties.java b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpClientProperties.java
index 3a48d2c..a964200 100644
--- a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpClientProperties.java
+++ b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpClientProperties.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -57,25 +57,34 @@
     public static final String SSL_PARAMETERS = "jersey.config.jnh.client.sslParameters";
 
     /**
-     * An instance of the {@link java.net.Authenticator} class that should be used to retrieve
-     * credentials from a user.
-     *
+     * <p>
+     *     An instance of the {@link java.net.Authenticator} class that should be used to retrieve
+     *     credentials from a user.
+     * </p>
+     * <p>
+     *     The name of the configuration property is <tt>{@value}</tt>.
+     * </p>
      */
     public static final String PREEMPTIVE_BASIC_AUTHENTICATION =
             "jersey.config.jnh.client.preemptiveBasicAuthentication";
 
     /**
-     * A value of {@code false} indicates the client should handle cookies
-     * automatically using HttpClient's default cookie policy. A value
-     * of {@code false} will cause the client to ignore all cookies.
-     * <p/>
-     * The value MUST be an instance of {@link java.lang.Boolean}.
-     * If the property is absent the default value is {@code false}
+     * <p>
+     *     A value of {@code false} indicates the client should handle cookies
+     *     automatically using HttpClient's default cookie policy. A value
+     *     of {@code false} will cause the client to ignore all cookies.
+     * </p>
+     * <p>
+     *     The value MUST be an instance of {@link java.lang.Boolean}.
+     *     If the property is absent the default value is {@code false}
+     * </p>
+     * <p>
+     *     The name of the configuration property is <tt>{@value}</tt>.
+     * </p>
      */
     public static final String DISABLE_COOKIES =
             "jersey.config.jnh.client.disableCookies";
 
-
     /**
      * Prevent this class from instantiation.
      */
diff --git a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java
index 7530c4e..975f5d8 100644
--- a/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java
+++ b/connectors/jnh-connector/src/main/java/org/glassfish/jersey/jnh/connector/JavaNetHttpConnector.java
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2021, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -25,6 +25,7 @@
 import org.glassfish.jersey.client.ClientResponse;
 import org.glassfish.jersey.client.innate.ClientProxy;
 import org.glassfish.jersey.client.innate.Expect100ContinueUsage;
+import org.glassfish.jersey.client.innate.http.SSLParamConfigurator;
 import org.glassfish.jersey.client.spi.AsyncConnectorCallback;
 import org.glassfish.jersey.client.spi.Connector;
 import org.glassfish.jersey.internal.Version;
@@ -49,14 +50,12 @@
 import java.net.http.HttpRequest;
 import java.net.http.HttpResponse;
 import java.time.Duration;
-import java.time.temporal.ChronoUnit;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.Future;
 import java.util.logging.Logger;
-import java.util.zip.GZIPOutputStream;
 
 /**
  * Provides a Jersey client {@link Connector}, which internally uses Java's {@link HttpClient}.
@@ -104,11 +103,14 @@
         } else {
             httpClientBuilder.followRedirects(HttpClient.Redirect.NORMAL);
         }
+
         SSLParameters sslParameters =
                 getPropertyOrNull(configuration, JavaNetHttpClientProperties.SSL_PARAMETERS, SSLParameters.class);
+        sslParameters = new SniSslParameters(sslParameters).getSslParameters(client);
         if (sslParameters != null) {
             httpClientBuilder.sslParameters(sslParameters);
         }
+
         final Authenticator preemptiveAuthenticator =
                 getPropertyOrNull(configuration,
                         JavaNetHttpClientProperties.PREEMPTIVE_BASIC_AUTHENTICATION, Authenticator.class);
@@ -170,12 +172,18 @@
      * @return the {@link HttpRequest} instance for the {@link HttpClient} request
      */
     private HttpRequest getHttpRequest(ClientRequest request) {
+        final SSLParamConfigurator sniConfig = SSLParamConfigurator.builder()
+                .uri(request.getUri())
+                .configuration(request.getConfiguration())
+                .build();
+
+        final URI sniUri = sniConfig.isSNIRequired() ? sniConfig.toIPRequestUri() : request.getUri();
+
         HttpRequest.Builder builder = HttpRequest.newBuilder();
-        builder.uri(request.getUri());
+        builder.uri(sniUri);
         HttpRequest.BodyPublisher bodyPublisher = HttpRequest.BodyPublishers.noBody();
         if (request.hasEntity()) {
             try {
-                request.enableBuffering();
                 ByteArrayOutputStreamProvider byteBufferStreamProvider = new ByteArrayOutputStreamProvider();
                 request.setStreamProvider(byteBufferStreamProvider);
                 request.writeEntity();
@@ -296,4 +304,29 @@
         }
         return null;
     }
+
+    private static class SniSslParameters {
+        private final SSLParameters sslParameters;
+
+        private SniSslParameters(SSLParameters sslParameters) {
+            this.sslParameters = sslParameters;
+        }
+
+        private SSLParameters getSslParameters(Client client) {
+            SSLParamConfigurator sniConfig = SSLParamConfigurator.builder()
+                    .configuration(client.getConfiguration())
+                    .build();
+
+            if (sniConfig.isSNIRequired()) {
+                SSLParameters sslParameters = this.sslParameters;
+                if (sslParameters == null) {
+                    sslParameters = new SSLParameters();
+                }
+                sniConfig.setSNIServerName(sslParameters);
+                return sslParameters;
+            } else {
+                return sslParameters;
+            }
+        }
+    }
 }
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 909af8d..556148d 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
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2022 Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2023 Oracle and/or its affiliates. All rights reserved.
  *
  * This program and the accompanying materials are made available under the
  * terms of the Eclipse Public License v. 2.0, which is available at
@@ -463,6 +463,24 @@
      */
     public static final String QUERY_PARAM_STYLE = "jersey.config.client.uri.query.param.style";
 
+    /**
+     * <p>
+     *     Most connectors support HOST header value to be used as an SNIHostName. However, the HOST header is restricted in JDK.
+     *     {@code HttpUrlConnector} and {@code JavaNetHttpConnector} need
+     *     to have an extra System Property set to allow HOST header.
+     *     As an option to HOST header, this property allows the HOST name to be pre-set on a Client and does not need to
+     *     be set on each request.
+     * </p>
+     * <p>
+     *     The value MUST be an instance of {@link java.lang.String}.
+     * </p>
+     * <p>
+     *     The name of the configuration property is <tt>{@value}</tt>.
+     * </p>
+     * @since 3.1.2
+     */
+    public static final String SNI_HOST_NAME = "jersey.config.client.sniHostName";
+
     private ClientProperties() {
         // prevents instantiation
     }
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 33ef0b6..cb32a5c 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
@@ -16,18 +16,25 @@
 
 package org.glassfish.jersey.client.innate.http;
 
+import jakarta.ws.rs.core.Configuration;
+import jakarta.ws.rs.core.HttpHeaders;
+import org.glassfish.jersey.client.ClientProperties;
 import org.glassfish.jersey.client.ClientRequest;
 
 import javax.net.ssl.SSLEngine;
 import javax.net.ssl.SSLParameters;
 import javax.net.ssl.SSLSocket;
 import jakarta.ws.rs.core.UriBuilder;
+import org.glassfish.jersey.internal.PropertiesResolver;
+import org.glassfish.jersey.internal.util.PropertiesHelper;
+
 import java.net.InetAddress;
 import java.net.URI;
 import java.net.UnknownHostException;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Properties;
 
 /**
  * A unified routines to configure {@link SSLParameters}.
@@ -35,27 +42,36 @@
  */
 public final class SSLParamConfigurator {
     private final URI uri;
-    private final Map<String, List<Object>> httpHeaders;
     private final Optional<SniConfigurator> sniConfigurator;
 
     /**
      * Builder of the {@link SSLParamConfigurator} instance.
      */
     public static final class Builder {
-        private ClientRequest clientRequest;
-        private URI uri;
-        private Map<String, List<Object>> httpHeaders;
+        private URI uri = null;
+        private String sniHostNameHeader = null;
+        private String sniHostNameProperty = null;
         private boolean setAlways = false;
 
         /**
-         * Sets the {@link ClientRequest} instance.
+         * Sets the SNIHostName and {@link URI} from 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.sniHostNameProperty = clientRequest.resolveProperty(ClientProperties.SNI_HOST_NAME, String.class);
+            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.sniHostNameProperty = (String) configuration.getProperty(ClientProperties.SNI_HOST_NAME);
             return this;
         }
 
@@ -65,7 +81,6 @@
          * @return the builder instance
          */
         public Builder uri(URI uri) {
-            this.clientRequest = null;
             this.uri = uri;
             return this;
         }
@@ -76,8 +91,7 @@
          * @return the builder instance
          */
         public Builder headers(Map<String, List<Object>> httpHeaders) {
-            this.clientRequest = null;
-            this.httpHeaders = httpHeaders;
+            this.sniHostNameHeader = getSniHostNameHeader(httpHeaders);
             return this;
         }
 
@@ -99,12 +113,31 @@
         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();
+            final String trimmedHeader;
+            if (hostHeader != null) {
+                int index = hostHeader.indexOf(':'); // RFC 7230  Host = uri-host [ ":" port ] ;
+                final String trimmedHeader0 = index != -1 ? hostHeader.substring(0, index).trim() : hostHeader.trim();
+                trimmedHeader = trimmedHeader0.isEmpty() ? hostHeader : trimmedHeader0;
+            } else {
+                trimmedHeader = null;
+            }
+
+            return trimmedHeader;
+        }
     }
 
     private SSLParamConfigurator(SSLParamConfigurator.Builder builder) {
-        this.uri = builder.clientRequest != null ? builder.clientRequest.getUri() : builder.uri;
-        this.httpHeaders = builder.clientRequest != null ? builder.clientRequest.getHeaders() : builder.httpHeaders;
-        sniConfigurator = SniConfigurator.createWhenHostHeader(uri, httpHeaders, builder.setAlways);
+        String sniHostName = builder.sniHostNameHeader == null ? builder.sniHostNameProperty : builder.sniHostNameHeader;
+        uri = builder.uri;
+        sniConfigurator = SniConfigurator.createWhenHostHeader(uri, sniHostName, builder.setAlways);
     }
 
     /**
@@ -178,6 +211,15 @@
     }
 
     /**
+     * Set {@link javax.net.ssl.SNIServerName} for the {@link SSLParameters} when SNI should be used
+     * (i.e. {@link jakarta.ws.rs.core.HttpHeaders#HOST} differs from HTTP request host name)
+     * @param parameters the {@link SSLParameters} to be set
+     */
+    public void setSNIServerName(SSLParameters parameters) {
+        sniConfigurator.ifPresent(sni -> sni.updateSSLParameters(parameters));
+    }
+
+    /**
      * Set setEndpointIdentificationAlgorithm to HTTPS. This is to prevent man-in-the-middle attacks.
      * @param sslEngine the {@link SSLEngine} the algorithm is set for.
      * @see SSLParameters#setEndpointIdentificationAlgorithm(String)
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 39a339d..47a0777 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
@@ -25,7 +25,6 @@
 import java.net.URI;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.Map;
 import java.util.Optional;
 
 /**
@@ -49,32 +48,24 @@
     /**
      * Create ClientSNI 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 sniHostName the SniHostName either from HttpHeaders or the
+     *      {@link org.glassfish.jersey.client.ClientProperties#SNI_HOST_NAME} property from Configuration object.
      * @param whenDiffer create {@SniConfigurator only when different from the request URI host}
      * @return ClientSNI or empty when {@link HttpHeaders#HOST}
      */
-    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) {
+    static Optional<SniConfigurator> createWhenHostHeader(URI hostUri, String sniHostName, boolean whenDiffer) {
+        if (sniHostName == null) {
             return Optional.empty();
         }
 
-        final String hostHeader = hostHeaders.get(0).toString();
-        final String trimmedHeader;
-        if (hostHeader != null) {
-            int index = hostHeader.indexOf(':'); // RFC 7230  Host = uri-host [ ":" port ] ;
-            final String trimmedHeader0 = index != -1 ? hostHeader.substring(0, index).trim() : hostHeader.trim();
-            trimmedHeader = trimmedHeader0.isEmpty() ? hostHeader : trimmedHeader0;
-        } else {
-            return Optional.empty();
+        if (hostUri != null) {
+            final String hostUriString = hostUri.getHost();
+            if (!whenDiffer && hostUriString.equals(sniHostName)) {
+                return Optional.empty();
+            }
         }
 
-        final String hostUriString = hostUri.getHost();
-        if (!whenDiffer && hostUriString.equals(trimmedHeader)) {
-            return Optional.empty();
-        }
-
-        return Optional.of(new SniConfigurator(trimmedHeader));
+        return Optional.of(new SniConfigurator(sniHostName));
     }
 
     /**
@@ -97,7 +88,7 @@
         sslSocket.setSSLParameters(sslParameters);
     }
 
-    private SSLParameters updateSSLParameters(SSLParameters sslParameters) {
+    SSLParameters updateSSLParameters(SSLParameters sslParameters) {
         SNIHostName serverName = new SNIHostName(hostName);
         List<SNIServerName> serverNames = new LinkedList<>();
         serverNames.add(serverName);
diff --git a/docs/src/main/docbook/appendix-properties.xml b/docs/src/main/docbook/appendix-properties.xml
index c979b32..381d1b9 100644
--- a/docs/src/main/docbook/appendix-properties.xml
+++ b/docs/src/main/docbook/appendix-properties.xml
@@ -1,7 +1,7 @@
 <?xml version="1.0"?>
 <!--
 
-    Copyright (c) 2013, 2022 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2013, 2023 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -1153,13 +1153,30 @@
                             <para>
                                 Property for threshold size for content length after which Expect:100-Continue header would be applied
                                 before the main request.
-                                Default threshold size (64kb) after which which Expect:100-Continue header would be applied before
+                                Default threshold size (64kb) after which Expect:100-Continue header would be applied before
                                 the main request.
                                 <literal>Since 2.32</literal>
                             </para>
                         </entry>
                     </row>
                     <row>
+                        <entry>&jersey.client.ClientProperties.SNI_HOST_NAME;</entry>
+                        <entry><literal>jersey.config.client.sniHostName</literal></entry>
+                        <entry>
+                            <para>
+                                Most connectors support HOST header value to be used as an SNIHostName. However, the HOST header is restricted in JDK.
+                                <literal>HttpUrlConnector</literal> and <literal>JavaNetHttpConnector</literal> need
+                                an extra System Property set to allow HOST header.
+                                As an option to HOST header, this property allows the HOST name to be pre-set on a Client and does not need to
+                                be set on each request.
+                                <literal>Since 3.1.2</literal>
+                            </para>
+                            <para>
+                                The value MUST be an instance of &lit.jdk6.String;
+                            </para>
+                        </entry>
+                    </row>
+                    <row>
                         <entry>&jersey.logging.LoggingFeature.LOGGING_FEATURE_LOGGER_NAME_CLIENT;
                         </entry>
                         <entry>
diff --git a/docs/src/main/docbook/jersey.ent b/docs/src/main/docbook/jersey.ent
index 702c511..2cc0800 100644
--- a/docs/src/main/docbook/jersey.ent
+++ b/docs/src/main/docbook/jersey.ent
@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="iso-8859-1" ?>
 <!--
 
-    Copyright (c) 2010, 2022 Oracle and/or its affiliates. All rights reserved.
+    Copyright (c) 2010, 2023 Oracle and/or its affiliates. All rights reserved.
 
     This program and the accompanying materials are made available under the
     terms of the Eclipse Public License v. 2.0, which is available at
@@ -360,6 +360,7 @@
 <!ENTITY jersey.client.ClientProperties.DIGESTAUTH_URI_CACHE_SIZELIMIT "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#DIGESTAUTH_URI_CACHE_SIZELIMIT'>ClientProperties.DIGESTAUTH_URI_CACHE_SIZELIMIT</link>" >
 <!ENTITY jersey.client.ClientProperties.EXPECT_100_CONTINUE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#EXPECT_100_CONTINUE'>ClientProperties.EXPECT_100_CONTINUE</link>" >
 <!ENTITY jersey.client.ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#EXPECT_100_CONTINUE_THRESHOLD_SIZE'>ClientProperties.EXPECT_100_CONTINUE_THRESHOLD_SIZE</link>" >
+<!ENTITY jersey.client.ClientProperties.SNI_HOST_NAME "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientProperties.html#SNI_HOST_NAME'>ClientProperties.SNI_HOST_NAME</link>" >
 <!ENTITY jersey.client.ClientLifecycleListener "<link xlink:href='&jersey.javadoc.uri.prefix;/client/ClientLifecycleListener.html'>ClientLifecycleListener</link>">
 <!ENTITY jersey.client.Connector "<link xlink:href='&jersey.javadoc.uri.prefix;/client/spi/Connector.html'>Connector</link>">
 <!ENTITY jersey.client.ConnectorProvider "<link xlink:href='&jersey.javadoc.uri.prefix;/client/spi/ConnectorProvider.html'>ConnectorProvider</link>">
diff --git a/tests/e2e-tls/pom.xml b/tests/e2e-tls/pom.xml
index 5c6b7fd..eaa7198 100644
--- a/tests/e2e-tls/pom.xml
+++ b/tests/e2e-tls/pom.xml
@@ -44,6 +44,7 @@
                     <skipTests>${skip.e2e}</skipTests>
                     <systemPropertyVariables>
                         <sun.net.http.allowRestrictedHeaders>true</sun.net.http.allowRestrictedHeaders>
+                        <jdk.httpclient.allowRestrictedHeaders>Host</jdk.httpclient.allowRestrictedHeaders>
                         <property>
                             <name>ssl.debug</name>
                             <value>true</value>
@@ -110,6 +111,11 @@
             <scope>test</scope>
         </dependency>
         <dependency>
+            <groupId>org.glassfish.jersey.connectors</groupId>
+            <artifactId>jersey-jnh-connector</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.glassfish.jersey.security</groupId>
             <artifactId>oauth1-signature</artifactId>
             <version>${project.version}</version>
diff --git a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java
index 1e8d893..3964fce 100644
--- a/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java
+++ b/tests/e2e-tls/src/test/java/org/glassfish/jersey/tests/e2e/tls/SniTest.java
@@ -16,6 +16,7 @@
 
 package org.glassfish.jersey.tests.e2e.tls;
 
+import jakarta.ws.rs.client.Invocation;
 import org.glassfish.jersey.apache.connector.ApacheConnectorProvider;
 import org.glassfish.jersey.apache5.connector.Apache5ConnectorProvider;
 import org.glassfish.jersey.client.ClientConfig;
@@ -23,9 +24,9 @@
 import org.glassfish.jersey.client.HttpUrlConnectorProvider;
 import org.glassfish.jersey.client.spi.ConnectorProvider;
 import org.glassfish.jersey.jdk.connector.JdkConnectorProvider;
+import org.glassfish.jersey.jnh.connector.JavaNetHttpConnectorProvider;
 import org.glassfish.jersey.netty.connector.NettyConnectorProvider;
 import org.glassfish.jersey.tests.e2e.tls.explorer.SSLCapabilities;
-import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
 
@@ -39,21 +40,19 @@
 import java.net.InetAddress;
 import java.net.Socket;
 import java.net.SocketTimeoutException;
-import java.net.URI;
 import java.net.UnknownHostException;
-import java.nio.charset.StandardCharsets;
 import java.util.LinkedList;
 import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
 import java.util.concurrent.TimeoutException;
 
 public class SniTest {
     private static final int PORT = 8443;
     private static final String LOCALHOST = "127.0.0.1";
 
-
     static {
+// Debug
+//        System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
+//        System.setProperty("jdk.httpclient.allowRestrictedHeaders", "Host");
         // JDK specific settings
         System.setProperty("jdk.net.hosts.file", SniTest.class.getResource("/hosts").getPath());
     }
@@ -64,7 +63,8 @@
                 new ApacheConnectorProvider(),
                 new Apache5ConnectorProvider(),
                 new JdkConnectorProvider(),
-                new HttpUrlConnectorProvider()
+                new HttpUrlConnectorProvider(),
+                new JavaNetHttpConnectorProvider()
         };
     }
 
@@ -73,10 +73,11 @@
     public void server1Test(ConnectorProvider provider) {
         ClientConfig clientConfig = new ClientConfig();
         clientConfig.connectorProvider(provider);
-        serverTest(clientConfig, "www.host1.com");
+        clientConfig.property(ClientProperties.SNI_HOST_NAME, "www.host1.com");
+        serverTest(clientConfig, provider, "www.host1.com");
     }
 
-    public void serverTest(ClientConfig clientConfig, String hostName) {
+    public void serverTest(ClientConfig clientConfig, ConnectorProvider provider, String hostName) {
         String newHostName = replaceWhenHostNotKnown(hostName);
         final List<SNIServerName> serverNames = new LinkedList<>();
         final String[] requestHostName = new String[1];
@@ -91,7 +92,7 @@
 
         clientConfig.property(ClientProperties.READ_TIMEOUT, 2000);
         clientConfig.property(ClientProperties.CONNECT_TIMEOUT, 2000);
-        try (Response r = ClientBuilder.newClient(clientConfig)
+        Invocation.Builder builder = ClientBuilder.newClient(clientConfig)
                 .register(new ClientRequestFilter() {
                     @Override
                     public void filter(ClientRequestContext requestContext) throws IOException {
@@ -100,9 +101,11 @@
                 })
                 .target("https://" + (newHostName.equals(LOCALHOST) ? LOCALHOST : "www.host0.com") + ":" + PORT)
                 .path("host")
-                .request()
-                .header(HttpHeaders.HOST, hostName + ":8080")
-                .get()) {
+                .request();
+        if (!JavaNetHttpConnectorProvider.class.isInstance(provider)) {
+            builder = builder.header(HttpHeaders.HOST, hostName + ":8080");
+        }
+        try (Response r = builder.get()) {
             // empty
         } catch (Exception e) {
             Throwable cause = e;
@@ -111,7 +114,7 @@
                     && TimeoutException.class.isInstance(cause)) {
                 cause = cause.getCause();
             }
-            if (cause == null && /*IOE*/ !e.getMessage().contains("Stream closed")) {
+            if ((!e.getMessage().contains("Stream closed")) && !e.getMessage().contains("timed out")) {
                 throw e;
             }
         }