Skip to content

Commit

Permalink
Dsaible SNI failing with Java22-23, regression reported and confirmed…
Browse files Browse the repository at this point in the history
… in https://bugs.openjdk.org/browse/JDK-8346705  (#68)

Enable IT disabled for now
Split SNI tests from other tests
Ignore tests with SNI on Java 22-23

regression reported and confirmed in https://bugs.openjdk.org/browse/JDK-8346705
  • Loading branch information
nhenneaux authored Jan 6, 2025
1 parent ed7cc00 commit ffc176a
Show file tree
Hide file tree
Showing 6 changed files with 117 additions and 82 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
fail-fast: false
matrix:
os: [ ubuntu-latest, macos-latest, windows-latest ]
java: [ 11, 17, 21, 23]
java: [ 11, 17, 21, 22, 23]
experimental: [ false ]
include:
- java: 24-ea
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,18 @@ class HttpClientPoolTest {

private static final Set<HealthCheckResult.HealthStatus> NOT_ERROR = Set.of(HealthCheckResult.HealthStatus.OK, HealthCheckResult.HealthStatus.WARNING);
public static final List<String> PUBLIC_HOST_TO_TEST = List.of(
//"nicolas.henneaux.io",
"openjdk.org", "github.com", "twitter.com", "cloudflare.com", "facebook.com", "amazon.com", "en.wikipedia.org"
// , "travis-ci.com", "google.com" //failing on Java22
"openjdk.org",
"github.com",
"twitter.com",
"cloudflare.com",
"facebook.com",
"amazon.com",
"en.wikipedia.org"
);
public static final List<String> PUBLIC_HOST_TO_TEST_WITH_SNI = List.of(
"nicolas.henneaux.io", //failing on Java23
"travis-ci.com", //failing on Java22
"google.com" //failing on Java22
);

static {
Expand All @@ -59,6 +68,10 @@ public static List<String> publicHosts() {
return PUBLIC_HOST_TO_TEST;
}

public static List<String> publicSpecificHosts() {
return PUBLIC_HOST_TO_TEST_WITH_SNI;
}

@BeforeEach
void setUp(TestInfo testInfo) {
var testClass = testInfo.getTestClass().orElseThrow();
Expand All @@ -73,10 +86,10 @@ void tearDown(TestInfo testInfo) {
System.out.println(testClass.getSimpleName() + "::" + testMethod.getName() + " test has finished.");
}

public

@ParameterizedTest
@MethodSource("publicHosts")
void getNextHttpClient(String hostname) throws MalformedURLException, URISyntaxException {
void getNextHttpClient(String hostname) throws URISyntaxException {

final ServerConfiguration serverConfiguration = new ServerConfiguration(hostname);
try (HttpClientPool httpClientPool = HttpClientPool.newHttpClientPool(serverConfiguration)) {
Expand All @@ -89,7 +102,7 @@ void getNextHttpClient(String hostname) throws MalformedURLException, URISyntaxE
httpClient = singleIpHttpClient.getHttpClient();
}
final int statusCode = httpClient.sendAsync(HttpRequest.newBuilder()
.uri(new URL("https", hostname, -1, serverConfiguration.getHealthPath()).toURI())
.uri(getUri(hostname, serverConfiguration))
.build(),
HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::statusCode)
Expand All @@ -99,6 +112,42 @@ void getNextHttpClient(String hostname) throws MalformedURLException, URISyntaxE

}

@ParameterizedTest
@Timeout(60L)
@MethodSource("publicSpecificHosts")
void specificPublicEndpoints(String hostname) throws URISyntaxException {
if (List.of(22, 23).contains(Runtime.version().feature())) {
// Failing in Java 22-23, regression in JDK https://bugs.openjdk.org/browse/JDK-8346705
return;
}

final ServerConfiguration serverConfiguration = new ServerConfiguration(hostname);
try (HttpClientPool httpClientPool = HttpClientPool.newHttpClientPool(serverConfiguration)) {
waitOneMinute(hostname)
.until(httpClientPool::getNextHttpClient, Optional::isPresent);

final Optional<SingleIpHttpClient> nextHttpClient = httpClientPool.getNextHttpClient();
try (SingleIpHttpClient singleIpHttpClient = nextHttpClient.orElseThrow()) {

final HttpClient httpClient = singleIpHttpClient.getHttpClient();
System.out.println("Calling " + hostname + " " + singleIpHttpClient.getInetAddress() + " healthiness " + singleIpHttpClient.getHealthy());

final int statusCode = httpClient.sendAsync(HttpRequest.newBuilder()
.uri(getUri(hostname, serverConfiguration))
.build(),
HttpResponse.BodyHandlers.discarding())
.thenApply(HttpResponse::statusCode)
.join();
assertThat(statusCode, allOf(Matchers.greaterThanOrEqualTo(200), Matchers.lessThanOrEqualTo(499)));
}
}

}

private static URI getUri(String hostname, ServerConfiguration serverConfiguration) throws URISyntaxException {
return new URI("https", hostname, serverConfiguration.getHealthPath(),null);
}

@ParameterizedTest
@MethodSource("publicHosts")
void resilientClient(String hostname) throws MalformedURLException, URISyntaxException {
Expand Down Expand Up @@ -254,6 +303,7 @@ void checkHttp() {

private static ConditionFactory waitOneMinute(String hostname) {
return await("waiting " + hostname + " being available")
.pollDelay(1, TimeUnit.SECONDS)
.atMost(1, TimeUnit.MINUTES);
}

Expand Down
6 changes: 2 additions & 4 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.5.0</version>
<executions>
Expand Down Expand Up @@ -92,7 +91,6 @@
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<executions>
<execution>
Expand Down Expand Up @@ -199,14 +197,14 @@
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<version>3.2.5</version>
<version>3.5.2</version>
<configuration>
<trimStackTrace>false</trimStackTrace>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.2.5</version>
<version>3.5.2</version>
<configuration>
<trimStackTrace>false</trimStackTrace>
<systemProperties>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ private SingleHostHttpClientBuilder(String hostname, InetAddress hostAddress, Ht
* The returned java.net.http.HttpClient is wrapped to force the HTTP header <code>Host</code> with the given hostname.
*/
public static HttpClient newHttpClient(String hostname, InetAddress hostAddress) {
return builder(hostname, hostAddress, HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(2L)))
return builder(hostname, hostAddress, HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(2L)))
.withTlsNameMatching()
.withSni()
.buildWithHostHeader();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,23 +24,25 @@
import java.util.concurrent.ExecutionException;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;

class SingleHostHttpClientBuilderIT {
class SingleHostHttpClientBuilderTest {
public static final List<String> PUBLIC_HOST_TO_TEST = List.of(
"nicolas.henneaux.io",
"openjdk.org",
"github.com",
"twitter.com",
"cloudflare.com",
"facebook.com",
"amazon.com",
"google.com",
"travis-ci.com",
"en.wikipedia.org");
public static final List<String> PUBLIC_HOST_TO_TEST_WITH_SNI = List.of(
"nicolas.henneaux.io",
"google.com",
"travis-ci.com");

static {
// Force properties
Expand All @@ -52,6 +54,10 @@ public static List<String> publicHosts() {
return PUBLIC_HOST_TO_TEST;
}

public static List<String> publicSpecificHosts() {
return PUBLIC_HOST_TO_TEST_WITH_SNI;
}

@ParameterizedTest
@Timeout(61)
@MethodSource("publicHosts")
Expand All @@ -77,6 +83,35 @@ void shouldBuildSingleIpHttpClientAndWorksWithPublicWebsite(String hostname) {
assertNotNull(response);
}

@ParameterizedTest
@Timeout(61)
@MethodSource("publicSpecificHosts")
void shouldBuildSingleIpHttpClientAndWorksWithSpecificPublicWebsite(String hostname) {
if (List.of(22, 23).contains(Runtime.version().feature())) {
// Failing in Java 22-23, regression in JDK https://bugs.openjdk.org/browse/JDK-8346705
return;
}
// Given
System.out.println("Validate " + hostname);
final InetAddress ip = new DnsLookupWrapper().getInetAddressesByDnsLookUp(hostname).iterator().next();

final HttpClient client = SingleHostHttpClientBuilder.newHttpClient(hostname, ip);


HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://" + ip))
.build();


// When
final int statusCode = client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
.thenApply(HttpResponse::statusCode)
.join();

// Then
assertThat(statusCode, allOf(Matchers.greaterThanOrEqualTo(200), Matchers.lessThanOrEqualTo(499)));
}

@ParameterizedTest
@Timeout(61)
@MethodSource("publicHosts")
Expand Down Expand Up @@ -185,6 +220,10 @@ void shouldBuildSingleIpHttpClientAndWorksWithNullTruststore() {
@Test
@Timeout(61)
void shouldBuildSingleIpHttpClientWithMutualTls() throws Exception {
if (List.of(22, 23).contains(Runtime.version().feature())) {
// Failing in Java 22-23, regression in JDK https://bugs.openjdk.org/browse/JDK-8346705
return;
}
// Given
final var hostname = "client.badssl.com";
final InetAddress ip = new DnsLookupWrapper().getInetAddressesByDnsLookUp(hostname).iterator().next();
Expand Down Expand Up @@ -219,6 +258,10 @@ void shouldBuildSingleIpHttpClientWithMutualTls() throws Exception {
@Test
@Timeout(61)
void shouldBuildSingleIpHttpClientWithMutualTlsCertMissing() throws Exception {
if (List.of(22, 23).contains(Runtime.version().feature())) {
// Failing in Java 22-23, regression in JDK https://bugs.openjdk.org/browse/JDK-8346705
return;
}
// Given
final var hostname = "client-cert-missing.badssl.com";
final InetAddress ip = new DnsLookupWrapper().getInetAddressesByDnsLookUp(hostname).iterator().next();
Expand Down Expand Up @@ -253,6 +296,10 @@ void shouldBuildSingleIpHttpClientWithMutualTlsCertMissing() throws Exception {
@Test
@Timeout(61)
void shouldTestWithSni() {
if (List.of(22, 23).contains(Runtime.version().feature())) {
// Failing in Java 22-23, regression in JDK https://bugs.openjdk.org/browse/JDK-8346705
return;
}
// Given
// Domain is not working when sni is not working correctly
final var hostname = "24max.de";
Expand All @@ -277,55 +324,15 @@ void shouldTestWithSni() {
}


@Test
@Timeout(61)
void shouldValidateWrongHost() {
// Given
String hostname = "wrong.host.badssl.com";
final InetAddress ip = new DnsLookupWrapper().getInetAddressesByDnsLookUp(hostname).iterator().next();

final HttpClient client = SingleHostHttpClientBuilder.newHttpClient(hostname, ip);


HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://" + ip))
.build();

final CompletableFuture<String> stringCompletableFuture = client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(HttpResponse::body);

// When
final CompletionException completionException = assertThrows(CompletionException.class, stringCompletableFuture::join);
// Then
assertEquals(SSLHandshakeException.class, completionException.getCause().getClass());
}

@Test
@Timeout(61)
void shouldValidateWith1000SAN() {
// Given
String hostname = "1000-sans.badssl.com";
final InetAddress ip = new DnsLookupWrapper().getInetAddressesByDnsLookUp(hostname).iterator().next();

final HttpClient client = SingleHostHttpClientBuilder.builder(hostname, ip, HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(2L))).withTlsNameMatching().buildWithHostHeader();


HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://" + ip))
.build();

final CompletableFuture<String> stringCompletableFuture = client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(HttpResponse::body);

// When
final CompletionException completionException = assertThrows(CompletionException.class, stringCompletableFuture::join);
// Then
assertEquals(SSLHandshakeException.class, completionException.getCause().getClass());
public static List<String> hostValidateCertificates() {
return List.of("wrong.host.badssl.com", "1000-sans.badssl.com", "no-subject.badssl.com", "no-common-name.badssl.com");
}

@Test
@ParameterizedTest
@MethodSource("hostValidateCertificates")
@Timeout(61)
void shouldValidateNoSubject() {
void shouldValidateCertificate(String hostname) {
// Given
String hostname = "no-subject.badssl.com";
final InetAddress ip = new DnsLookupWrapper().getInetAddressesByDnsLookUp(hostname).iterator().next();

final HttpClient client = SingleHostHttpClientBuilder.builder(hostname, ip, HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(2L))).withTlsNameMatching().buildWithHostHeader();
Expand All @@ -343,27 +350,6 @@ void shouldValidateNoSubject() {
assertEquals(SSLHandshakeException.class, completionException.getCause().getClass());
}

@Test
@Timeout(61)
void shouldValidateNoCommonName() {
// Given
String hostname = "no-common-name.badssl.com";
final InetAddress ip = new DnsLookupWrapper().getInetAddressesByDnsLookUp(hostname).iterator().next();

final HttpClient client = SingleHostHttpClientBuilder.builder(hostname, ip, HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(2L))).withTlsNameMatching().buildWithHostHeader();


HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://" + ip))
.build();
final CompletableFuture<String> stringCompletableFuture = client.sendAsync(request, HttpResponse.BodyHandlers.ofString()).thenApply(HttpResponse::body);

// When
final CompletionException completionException = assertThrows(CompletionException.class, stringCompletableFuture::join);
// Then
assertEquals(SSLHandshakeException.class, completionException.getCause().getClass());
}

@Test
@Timeout(61)
void unknownHost() {
Expand Down
Binary file modified single-host-httpclient/src/test/resources/truststore.p12
Binary file not shown.

0 comments on commit ffc176a

Please sign in to comment.