mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8371887: HttpClient: SSLParameters with no protocols configured disable HTTP2+ support
Reviewed-by: jpai, dfuchs
This commit is contained in:
parent
86aae125f1
commit
1f417e7761
@ -74,7 +74,7 @@ public final class QuicTLSContext {
|
||||
/**
|
||||
* {@return {@code true} if protocols of the given {@code parameters} support QUIC TLS, {@code false} otherwise}
|
||||
*/
|
||||
public static boolean isQuicCompatible(SSLParameters parameters) {
|
||||
private static boolean isQuicCompatible(SSLParameters parameters) {
|
||||
String[] protocols = parameters.getProtocols();
|
||||
return protocols != null && Arrays.asList(protocols).contains("TLSv1.3");
|
||||
}
|
||||
|
||||
@ -53,6 +53,7 @@ import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@ -126,6 +127,8 @@ final class HttpClientImpl extends HttpClient implements Trackable {
|
||||
// Defaults to value used for HTTP/1 Keep-Alive Timeout. Can be overridden by jdk.httpclient.keepalive.timeout.h2 property.
|
||||
static final long IDLE_CONNECTION_TIMEOUT_H2 = getTimeoutProp("jdk.httpclient.keepalive.timeout.h2", KEEP_ALIVE_TIMEOUT);
|
||||
static final long IDLE_CONNECTION_TIMEOUT_H3 = getTimeoutProp("jdk.httpclient.keepalive.timeout.h3", IDLE_CONNECTION_TIMEOUT_H2);
|
||||
private final boolean hasRequiredH3TLS;
|
||||
private final boolean hasRequiredH2TLS;
|
||||
|
||||
static final UseVTForSelector USE_VT_FOR_SELECTOR =
|
||||
Utils.useVTForSelector("jdk.internal.httpclient.tcp.selector.useVirtualThreads", "default");
|
||||
@ -477,10 +480,17 @@ final class HttpClientImpl extends HttpClient implements Trackable {
|
||||
"HTTP3 is not supported"));
|
||||
}
|
||||
sslParams = requireNonNullElseGet(builder.sslParams, sslContext::getDefaultSSLParameters);
|
||||
boolean sslParamsSupportedForH3 = sslParams.getProtocols() == null
|
||||
|| sslParams.getProtocols().length == 0
|
||||
|| isQuicCompatible(sslParams);
|
||||
if (version == Version.HTTP_3 && !sslParamsSupportedForH3) {
|
||||
String[] sslProtocols = sslParams.getProtocols();
|
||||
if (sslProtocols == null) {
|
||||
sslProtocols = requireNonNullElseGet(sslContext.getDefaultSSLParameters().getProtocols(),
|
||||
() -> new String[0]);
|
||||
}
|
||||
// HTTP/3 MUST use TLS version 1.3 or higher
|
||||
hasRequiredH3TLS = Arrays.asList(sslProtocols).contains("TLSv1.3");
|
||||
// HTTP/2 MUST use TLS version 1.2 or higher for HTTP/2 over TLS
|
||||
hasRequiredH2TLS = hasRequiredH3TLS || Arrays.asList(sslProtocols).contains("TLSv1.2");
|
||||
|
||||
if (version == Version.HTTP_3 && !hasRequiredH3TLS) {
|
||||
throw new UncheckedIOException(new UnsupportedProtocolVersionException(
|
||||
"HTTP3 is not supported - TLSv1.3 isn't configured on SSLParameters"));
|
||||
}
|
||||
@ -507,7 +517,7 @@ final class HttpClientImpl extends HttpClient implements Trackable {
|
||||
debug.log("proxySelector is %s (user-supplied=%s)",
|
||||
this.proxySelector, userProxySelector != null);
|
||||
authenticator = builder.authenticator;
|
||||
boolean h3Supported = sslCtxSupportedForH3 && sslParamsSupportedForH3;
|
||||
boolean h3Supported = sslCtxSupportedForH3 && hasRequiredH3TLS;
|
||||
registry = new AltServicesRegistry(id);
|
||||
connections = new ConnectionPool(id);
|
||||
client2 = new Http2ClientImpl(this);
|
||||
@ -530,6 +540,22 @@ final class HttpClientImpl extends HttpClient implements Trackable {
|
||||
assert facadeRef.get() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the SSL parameter protocols contains at
|
||||
* least one TLS version that HTTP/3 requires.
|
||||
*/
|
||||
boolean hasRequiredHTTP3TLSVersion() {
|
||||
return hasRequiredH3TLS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the SSL parameter protocols contains at
|
||||
* least one TLS version that HTTP/2 requires.
|
||||
*/
|
||||
boolean hasRequiredHTTP2TLSVersion() {
|
||||
return hasRequiredH2TLS;
|
||||
}
|
||||
|
||||
// called when the facade is GC'ed.
|
||||
// Just wakes up the selector to cleanup...
|
||||
void facadeCleanup() {
|
||||
|
||||
@ -32,7 +32,6 @@ import java.net.http.HttpResponse;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.NetworkChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.IdentityHashMap;
|
||||
import java.util.List;
|
||||
@ -43,8 +42,6 @@ import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.concurrent.Flow;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Predicate;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpClient.Version;
|
||||
import java.net.http.HttpHeaders;
|
||||
|
||||
@ -285,23 +282,6 @@ abstract class HttpConnection implements Closeable {
|
||||
*/
|
||||
abstract HttpPublisher publisher();
|
||||
|
||||
// HTTP/2 MUST use TLS version 1.2 or higher for HTTP/2 over TLS
|
||||
private static final Predicate<String> testRequiredHTTP2TLSVersion = proto ->
|
||||
proto.equals("TLSv1.2") || proto.equals("TLSv1.3");
|
||||
|
||||
/**
|
||||
* Returns true if the given client's SSL parameter protocols contains at
|
||||
* least one TLS version that HTTP/2 requires.
|
||||
*/
|
||||
private static final boolean hasRequiredHTTP2TLSVersion(HttpClient client) {
|
||||
String[] protos = client.sslParameters().getProtocols();
|
||||
if (protos != null) {
|
||||
return Arrays.stream(protos).filter(testRequiredHTTP2TLSVersion).findAny().isPresent();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Factory for retrieving HttpConnections. A connection can be retrieved
|
||||
* from the connection pool, or a new one created if none available.
|
||||
@ -359,7 +339,7 @@ abstract class HttpConnection implements Closeable {
|
||||
} else {
|
||||
assert !request.isHttp3Only(version); // should have failed before
|
||||
String[] alpn = null;
|
||||
if (version == HTTP_2 && hasRequiredHTTP2TLSVersion(client)) {
|
||||
if (version == HTTP_2 && client.hasRequiredHTTP2TLSVersion()) {
|
||||
// We only come here after we have checked the HTTP/2 connection pool.
|
||||
// We will not negotiate HTTP/2 if we don't have the appropriate TLS version
|
||||
alpn = new String[] { Alpns.H2, Alpns.HTTP_1_1 };
|
||||
|
||||
@ -31,11 +31,9 @@ import java.net.InetSocketAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketOption;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpClient;
|
||||
import java.net.http.HttpConnectTimeoutException;
|
||||
import java.nio.channels.NetworkChannel;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
@ -43,7 +41,6 @@ import java.util.Set;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import javax.net.ssl.SNIServerName;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
@ -81,9 +78,6 @@ abstract class HttpQuicConnection extends HttpConnection {
|
||||
// the alt-service which was advertised, from some origin, for this connection co-ordinates.
|
||||
// can be null, which indicates this wasn't created because of an alt-service
|
||||
private final AltService sourceAltService;
|
||||
// HTTP/2 MUST use TLS version 1.3 or higher for HTTP/3 over TLS
|
||||
private static final Predicate<String> testRequiredHTTP3TLSVersion = proto ->
|
||||
proto.equals("TLSv1.3");
|
||||
|
||||
|
||||
HttpQuicConnection(Origin originServer, InetSocketAddress address, HttpClientImpl client,
|
||||
@ -178,19 +172,6 @@ abstract class HttpQuicConnection extends HttpConnection {
|
||||
return quicConnection;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given client's SSL parameter protocols contains at
|
||||
* least one TLS version that HTTP/3 requires.
|
||||
*/
|
||||
private static boolean hasRequiredHTTP3TLSVersion(HttpClient client) {
|
||||
String[] protos = client.sslParameters().getProtocols();
|
||||
if (protos != null) {
|
||||
return Arrays.stream(protos).anyMatch(testRequiredHTTP3TLSVersion);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the HTTP/3 connection is established, either successfully or
|
||||
* unsuccessfully
|
||||
@ -260,7 +241,7 @@ abstract class HttpQuicConnection extends HttpConnection {
|
||||
// to using HTTP/2
|
||||
var debug = h3client.debug();
|
||||
var where = "HttpQuicConnection.getHttpQuicConnection";
|
||||
if (proxy != null || !hasRequiredHTTP3TLSVersion(client)) {
|
||||
if (proxy != null || !client.hasRequiredHTTP3TLSVersion()) {
|
||||
if (debug.on())
|
||||
debug.log("%s: proxy required or SSL version mismatch", where);
|
||||
return null;
|
||||
|
||||
@ -31,6 +31,8 @@ import java.net.http.HttpResponse;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLParameters;
|
||||
|
||||
import jdk.httpclient.test.lib.common.HttpServerAdapters;
|
||||
import jdk.httpclient.test.lib.http2.Http2TestServer;
|
||||
import jdk.test.lib.net.SimpleSSLContext;
|
||||
@ -49,7 +51,7 @@ import jdk.test.lib.security.SecurityUtils;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8239594
|
||||
* @bug 8239594 8371887
|
||||
* @summary This test verifies that the TLS version handshake respects ssl context
|
||||
* @library /test/lib /test/jdk/java/net/httpclient/lib
|
||||
* @build jdk.test.lib.net.SimpleSSLContext TlsContextTest
|
||||
@ -101,9 +103,25 @@ public class TlsContextTest implements HttpServerAdapters {
|
||||
* Tests various scenarios between client and server tls handshake with valid http
|
||||
*/
|
||||
@Test(dataProvider = "scenarios")
|
||||
public void testVersionProtocols(SSLContext context,
|
||||
public void testVersionProtocolsNoParams(SSLContext context,
|
||||
Version version,
|
||||
String expectedProtocol) throws Exception {
|
||||
runTest(context, version, expectedProtocol, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests various scenarios between client and server tls handshake with valid http,
|
||||
* but with empty SSLParameters
|
||||
*/
|
||||
@Test(dataProvider = "scenarios")
|
||||
public void testVersionProtocolsEmptyParams(SSLContext context,
|
||||
Version version,
|
||||
String expectedProtocol) throws Exception {
|
||||
runTest(context, version, expectedProtocol, true);
|
||||
}
|
||||
|
||||
private void runTest(SSLContext context, Version version, String expectedProtocol,
|
||||
boolean setEmptyParams) throws Exception {
|
||||
// for HTTP/3 we won't accept to set the version to HTTP/3 on the
|
||||
// client if we don't have TLSv1.3; We will set the version
|
||||
// on the request instead in that case.
|
||||
@ -111,8 +129,11 @@ public class TlsContextTest implements HttpServerAdapters {
|
||||
: HttpClient.newBuilder().version(version);
|
||||
var reqBuilder = HttpRequest.newBuilder(new URI(https2URI));
|
||||
|
||||
if (setEmptyParams) {
|
||||
builder.sslParameters(new SSLParameters());
|
||||
}
|
||||
HttpClient client = builder.sslContext(context)
|
||||
.build();
|
||||
.build();
|
||||
if (version == HTTP_3) {
|
||||
// warmup to obtain AltService
|
||||
client.send(reqBuilder.version(HTTP_2).GET().build(), ofString());
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 2025, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
@ -41,7 +41,7 @@ import jdk.httpclient.test.lib.http2.Http2Handler;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8150769 8157107
|
||||
* @bug 8150769 8157107 8371887
|
||||
* @library /test/jdk/java/net/httpclient/lib
|
||||
* @build jdk.httpclient.test.lib.http2.Http2TestServer
|
||||
* @summary Checks that SSL parameters can be set for HTTP/2 connection
|
||||
@ -135,6 +135,12 @@ public class TLSConnection {
|
||||
success &= checkCipherSuite(handler.getSSLSession(),
|
||||
"TLS_RSA_WITH_AES_128_CBC_SHA");
|
||||
|
||||
success &= expectSuccess(
|
||||
"---\nTest #5: empty SSL parameters, "
|
||||
+ "expect successful connection",
|
||||
() -> connect(uriString, new SSLParameters()));
|
||||
success &= checkProtocol(handler.getSSLSession(), expectedTLSVersion(null));
|
||||
|
||||
if (success) {
|
||||
System.out.println("Test passed");
|
||||
} else {
|
||||
|
||||
@ -34,6 +34,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8371887
|
||||
* @summary Tests that a HttpClient configured with SSLParameters that doesn't include TLSv1.3
|
||||
* cannot be used for HTTP3
|
||||
* @library /test/lib /test/jdk/java/net/httpclient/lib
|
||||
@ -75,4 +76,18 @@ public class H3UnsupportedSSLParametersTest {
|
||||
.version(HttpClient.Version.HTTP_3).build();
|
||||
assertNotNull(client, "HttpClient is null");
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a HttpClient with SSLParameters without protocol versions
|
||||
* and expects the build() to succeed and return a HttpClient instance
|
||||
*/
|
||||
@Test
|
||||
public void testDefault() throws Exception {
|
||||
final SSLParameters params = new SSLParameters();
|
||||
final HttpClient client = HttpServerAdapters.createClientBuilderForH3()
|
||||
.proxy(HttpClient.Builder.NO_PROXY)
|
||||
.sslParameters(params)
|
||||
.version(HttpClient.Version.HTTP_3).build();
|
||||
assertNotNull(client, "HttpClient is null");
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user