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 5055c12f619..32bf44fbc79 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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 @@ -20,12 +20,14 @@ import org.glassfish.jersey.client.ClientRequest; import org.glassfish.jersey.http.HttpHeaders; import org.glassfish.jersey.internal.PropertiesResolver; +import org.glassfish.jersey.internal.guava.InetAddresses; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLParameters; import javax.net.ssl.SSLSocket; import javax.ws.rs.core.Configuration; import javax.ws.rs.core.UriBuilder; +import java.net.Inet6Address; import java.net.InetAddress; import java.net.URI; import java.net.UnknownHostException; @@ -233,7 +235,9 @@ public URI toIPRequestUri() { String host = uri.getHost(); try { InetAddress ip = InetAddress.getByName(host); - return UriBuilder.fromUri(uri).host(ip.getHostAddress()).build(); + // ipv6 is expected in square brackets in UriBuilder#host() + final String hostAddress = ip instanceof Inet6Address ? '[' + ip.getHostAddress() + ']' : ip.getHostAddress(); + return UriBuilder.fromUri(uri).host(hostAddress).build(); } catch (UnknownHostException e) { return uri; } 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 733e4d54998..f38db94e6bf 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023, 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, 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,7 +61,10 @@ String getHostName() { static Optional createWhenHostHeader(URI hostUri, String sniHost, boolean whenDiffer) { final String trimmedHeader; if (sniHost != null) { - int index = sniHost.indexOf(':'); // RFC 7230 Host = uri-host [ ":" port ] ; + int index = sniHost.lastIndexOf(':'); // RFC 7230 Host = uri-host [ ":" port ] ; + if (sniHost.indexOf(']', index) != -1) { + index = -1; // beware of ipv6 [:1] without port + } final String trimmedHeader0 = index != -1 ? sniHost.substring(0, index).trim() : sniHost.trim(); trimmedHeader = trimmedHeader0.isEmpty() ? sniHost : trimmedHeader0; } else { 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 index 41f8fa74c64..036eba521d8 100644 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2024 Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2024, 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 @@ -27,8 +27,12 @@ import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; +import javax.ws.rs.ProcessingException; +import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.core.MultivaluedHashMap; +import javax.ws.rs.core.Response; +import java.net.ConnectException; import java.net.URI; import java.util.Collections; import java.util.HashMap; @@ -155,4 +159,56 @@ public void testUriAndHeadersAndConfig() { MatcherAssert.assertThat(configurator.isSNIRequired(), Matchers.is(true)); MatcherAssert.assertThat(configurator.getSNIHostName(), Matchers.is("yyy.com")); } + + @Test + public void testIPv6Header() { + final String HOST_HEADER_IPv6 = "[172:30::333b]"; + final URI uri = URI.create("http://[172:30::333a]:8080/api/demo/v1"); + final JerseyClient client = (JerseyClient) ClientBuilder.newClient(); + Map> httpHeaders = new MultivaluedHashMap<>(); + httpHeaders.put(HttpHeaders.HOST, Collections.singletonList(HOST_HEADER_IPv6 + ":8080")); + 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(HOST_HEADER_IPv6)); + URI expected = URI.create("http://" + HOST_HEADER_IPv6 + ":8080/api/demo/v1"); + MatcherAssert.assertThat(configurator.getSNIUri(), Matchers.is(expected)); + MatcherAssert.assertThat(configurator.toIPRequestUri().toString(), + Matchers.is(uri.toString().replace("::", ":0:0:0:0:0:"))); + } + + @Test + public void testIpv6Request() { + Client client = ClientBuilder.newClient(); + String u = "http://[::1]:8080"; + try { + client.target(u) + .request() + .header(HttpHeaders.HOST, "[172:30::333b]:8080") + .get(); + } catch (ProcessingException pe) { + if (!ConnectException.class.isInstance(pe.getCause())) { + throw pe; + } + } + } + + @Test + public void testIpv6RequestNoPort() { + Client client = ClientBuilder.newClient(); + String u = "http://[::1]"; + try { + client.target(u) + .request() + .header(HttpHeaders.HOST, "[172:30::333b]") + .get(); + } catch (ProcessingException pe) { + if (!ConnectException.class.isInstance(pe.getCause())) { + throw pe; + } + } + } }