mirror of
https://github.com/openjdk/jdk.git
synced 2026-02-09 09:58:43 +00:00
8361060: Keep track of the origin server against which a jdk.internal.net.http.HttpConnection was constructed
Reviewed-by: dfuchs
This commit is contained in:
parent
2f683fdc4a
commit
1be29bd725
@ -41,7 +41,7 @@ import jdk.internal.net.http.common.Alpns;
|
||||
import jdk.internal.net.http.common.SSLTube;
|
||||
import jdk.internal.net.http.common.Log;
|
||||
import jdk.internal.net.http.common.Utils;
|
||||
import static jdk.internal.net.http.common.Utils.ServerName;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
|
||||
/**
|
||||
* Asynchronous version of SSLConnection.
|
||||
@ -63,6 +63,10 @@ import static jdk.internal.net.http.common.Utils.ServerName;
|
||||
*/
|
||||
abstract class AbstractAsyncSSLConnection extends HttpConnection
|
||||
{
|
||||
|
||||
private record ServerName(String name, boolean isLiteral) {
|
||||
}
|
||||
|
||||
protected final SSLEngine engine;
|
||||
protected final SSLParameters sslParameters;
|
||||
private final List<SNIServerName> sniServerNames;
|
||||
@ -71,17 +75,19 @@ abstract class AbstractAsyncSSLConnection extends HttpConnection
|
||||
private static final boolean disableHostnameVerification
|
||||
= Utils.isHostnameVerificationDisabled();
|
||||
|
||||
AbstractAsyncSSLConnection(InetSocketAddress addr,
|
||||
AbstractAsyncSSLConnection(Origin originServer,
|
||||
InetSocketAddress addr,
|
||||
HttpClientImpl client,
|
||||
ServerName serverName, int port,
|
||||
String[] alpn,
|
||||
String label) {
|
||||
super(addr, client, label);
|
||||
super(originServer, addr, client, label);
|
||||
assert originServer != null : "origin server is null";
|
||||
final ServerName serverName = getServerName(originServer);
|
||||
this.sniServerNames = formSNIServerNames(serverName, client);
|
||||
SSLContext context = client.theSSLContext();
|
||||
sslParameters = createSSLParameters(client, this.sniServerNames, alpn);
|
||||
Log.logParams(sslParameters);
|
||||
engine = createEngine(context, serverName.name(), port, sslParameters);
|
||||
engine = createEngine(context, serverName.name(), originServer.port(), sslParameters);
|
||||
}
|
||||
|
||||
abstract SSLTube getConnectionFlow();
|
||||
@ -187,6 +193,23 @@ abstract class AbstractAsyncSSLConnection extends HttpConnection
|
||||
return engine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyse the given {@linkplain Origin origin server} and determine
|
||||
* if the origin server's host is a literal or not, returning the server's
|
||||
* address in String form.
|
||||
*/
|
||||
private static ServerName getServerName(final Origin originServer) {
|
||||
final String host = originServer.host();
|
||||
byte[] literal = IPAddressUtil.textToNumericFormatV4(host);
|
||||
if (literal == null) {
|
||||
// not IPv4 literal. Check IPv6
|
||||
literal = IPAddressUtil.textToNumericFormatV6(host);
|
||||
return new ServerName(host, literal != null);
|
||||
} else {
|
||||
return new ServerName(host, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
final boolean isSecure() {
|
||||
return true;
|
||||
|
||||
@ -42,12 +42,13 @@ class AsyncSSLConnection extends AbstractAsyncSSLConnection {
|
||||
final PlainHttpPublisher writePublisher;
|
||||
private volatile SSLTube flow;
|
||||
|
||||
AsyncSSLConnection(InetSocketAddress addr,
|
||||
AsyncSSLConnection(Origin originServer,
|
||||
InetSocketAddress addr,
|
||||
HttpClientImpl client,
|
||||
String[] alpn,
|
||||
String label) {
|
||||
super(addr, client, Utils.getServerName(addr), addr.getPort(), alpn, label);
|
||||
plainConnection = new PlainHttpConnection(addr, client, label);
|
||||
super(originServer, addr, client, alpn, label);
|
||||
plainConnection = new PlainHttpConnection(originServer, addr, client, label);
|
||||
writePublisher = new PlainHttpPublisher();
|
||||
}
|
||||
|
||||
|
||||
@ -43,15 +43,17 @@ class AsyncSSLTunnelConnection extends AbstractAsyncSSLConnection {
|
||||
final PlainHttpPublisher writePublisher;
|
||||
volatile SSLTube flow;
|
||||
|
||||
AsyncSSLTunnelConnection(InetSocketAddress addr,
|
||||
AsyncSSLTunnelConnection(Origin originServer,
|
||||
InetSocketAddress addr,
|
||||
HttpClientImpl client,
|
||||
String[] alpn,
|
||||
InetSocketAddress proxy,
|
||||
ProxyHeaders proxyHeaders,
|
||||
String label)
|
||||
{
|
||||
super(addr, client, Utils.getServerName(addr), addr.getPort(), alpn, label);
|
||||
this.plainConnection = new PlainTunnelingConnection(addr, proxy, client, proxyHeaders, label);
|
||||
super(originServer, addr, client, alpn, label);
|
||||
this.plainConnection = new PlainTunnelingConnection(originServer, addr, proxy, client,
|
||||
proxyHeaders, label);
|
||||
this.writePublisher = new PlainHttpPublisher();
|
||||
}
|
||||
|
||||
|
||||
@ -100,7 +100,11 @@ abstract class HttpConnection implements Closeable {
|
||||
*/
|
||||
private final String label;
|
||||
|
||||
HttpConnection(InetSocketAddress address, HttpClientImpl client, String label) {
|
||||
private final Origin originServer;
|
||||
|
||||
HttpConnection(Origin originServer, InetSocketAddress address, HttpClientImpl client,
|
||||
String label) {
|
||||
this.originServer = originServer;
|
||||
this.address = address;
|
||||
this.client = client;
|
||||
trailingOperations = new TrailingOperations();
|
||||
@ -244,6 +248,14 @@ abstract class HttpConnection implements Closeable {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return the {@link Origin} server against which this connection communicates.
|
||||
* Returns {@code null} if the connection is a plain connection to a proxy}
|
||||
*/
|
||||
final Origin getOriginServer() {
|
||||
return this.originServer;
|
||||
}
|
||||
|
||||
interface HttpPublisher extends FlowTube.TubePublisher {
|
||||
void enqueue(List<ByteBuffer> buffers) throws IOException;
|
||||
void enqueueUnordered(List<ByteBuffer> buffers) throws IOException;
|
||||
@ -334,13 +346,20 @@ abstract class HttpConnection implements Closeable {
|
||||
String[] alpn,
|
||||
HttpRequestImpl request,
|
||||
HttpClientImpl client) {
|
||||
String label = nextLabel();
|
||||
final String label = nextLabel();
|
||||
final Origin originServer;
|
||||
try {
|
||||
originServer = Origin.from(request.uri());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// should never happen
|
||||
throw new AssertionError("failed to determine origin server from request URI", iae);
|
||||
}
|
||||
if (proxy != null)
|
||||
return new AsyncSSLTunnelConnection(addr, client, alpn, proxy,
|
||||
return new AsyncSSLTunnelConnection(originServer, addr, client, alpn, proxy,
|
||||
proxyTunnelHeaders(request),
|
||||
label);
|
||||
else
|
||||
return new AsyncSSLConnection(addr, client, alpn, label);
|
||||
return new AsyncSSLConnection(originServer, addr, client, alpn, label);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -414,14 +433,21 @@ abstract class HttpConnection implements Closeable {
|
||||
InetSocketAddress proxy,
|
||||
HttpRequestImpl request,
|
||||
HttpClientImpl client) {
|
||||
String label = nextLabel();
|
||||
final String label = nextLabel();
|
||||
final Origin originServer;
|
||||
try {
|
||||
originServer = Origin.from(request.uri());
|
||||
} catch (IllegalArgumentException iae) {
|
||||
// should never happen
|
||||
throw new AssertionError("failed to determine origin server from request URI", iae);
|
||||
}
|
||||
if (request.isWebSocket() && proxy != null)
|
||||
return new PlainTunnelingConnection(addr, proxy, client,
|
||||
return new PlainTunnelingConnection(originServer, addr, proxy, client,
|
||||
proxyTunnelHeaders(request),
|
||||
label);
|
||||
|
||||
if (proxy == null)
|
||||
return new PlainHttpConnection(addr, client, label);
|
||||
return new PlainHttpConnection(originServer, addr, client, label);
|
||||
else
|
||||
return new PlainProxyConnection(proxy, client, label);
|
||||
}
|
||||
|
||||
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation. Oracle designates this
|
||||
* particular file as subject to the "Classpath" exception as provided
|
||||
* by Oracle in the LICENSE file that accompanied this code.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
package jdk.internal.net.http;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import sun.net.util.IPAddressUtil;
|
||||
|
||||
/**
|
||||
* Represents an origin server to which a HTTP request is targeted.
|
||||
*
|
||||
* @param scheme The scheme of the origin (for example: https). Unlike the application layer
|
||||
* protocol (which can be a finer grained protocol like h2, h3 etc...),
|
||||
* this is actually a scheme. Only {@code http} and {@code https} literals are
|
||||
* supported. Cannot be null.
|
||||
* @param host The host of the origin, cannot be null. If the host is an IPv6 address,
|
||||
* then it must not be enclosed in square brackets ({@code '['} and {@code ']'}).
|
||||
* If the host is a DNS hostname, then it must be passed as a lower case String.
|
||||
* @param port The port of the origin. Must be greater than 0.
|
||||
*/
|
||||
public record Origin(String scheme, String host, int port) {
|
||||
public Origin {
|
||||
Objects.requireNonNull(scheme);
|
||||
Objects.requireNonNull(host);
|
||||
if (!isValidScheme(scheme)) {
|
||||
throw new IllegalArgumentException("Unsupported scheme: " + scheme);
|
||||
}
|
||||
if (host.startsWith("[") && host.endsWith("]")) {
|
||||
throw new IllegalArgumentException("Invalid host: " + host);
|
||||
}
|
||||
// expect DNS hostname to be passed as lower case
|
||||
if (isDNSHostName(host) && !host.toLowerCase(Locale.ROOT).equals(host)) {
|
||||
throw new IllegalArgumentException("non-lowercase hostname: " + host);
|
||||
}
|
||||
if (port <= 0) {
|
||||
throw new IllegalArgumentException("Invalid port: " + port);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return scheme + "://" + toAuthority(host, port);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return Creates and returns an Origin from an URI}
|
||||
*
|
||||
* @param uri The URI of the origin
|
||||
* @throws IllegalArgumentException if a Origin cannot be constructed from
|
||||
* the given {@code uri}
|
||||
*/
|
||||
public static Origin from(final URI uri) throws IllegalArgumentException {
|
||||
Objects.requireNonNull(uri);
|
||||
final String scheme = uri.getScheme();
|
||||
if (scheme == null) {
|
||||
throw new IllegalArgumentException("missing scheme in URI");
|
||||
}
|
||||
final String lcaseScheme = scheme.toLowerCase(Locale.ROOT);
|
||||
if (!isValidScheme(lcaseScheme)) {
|
||||
throw new IllegalArgumentException("Unsupported scheme: " + scheme);
|
||||
}
|
||||
final String host = uri.getHost();
|
||||
if (host == null) {
|
||||
throw new IllegalArgumentException("missing host in URI");
|
||||
}
|
||||
String effectiveHost;
|
||||
if (host.startsWith("[") && host.endsWith("]")) {
|
||||
// strip the square brackets from IPv6 host
|
||||
effectiveHost = host.substring(1, host.length() - 1);
|
||||
} else {
|
||||
effectiveHost = host;
|
||||
}
|
||||
assert !effectiveHost.isEmpty() : "unexpected URI host: " + host;
|
||||
// If the host is a DNS hostname, then convert the host to lower case.
|
||||
// The DNS hostname is expected to be ASCII characters and is case-insensitive.
|
||||
//
|
||||
// Its usage in areas like SNI too match this expectation - RFC-6066, section 3:
|
||||
// "HostName" contains the fully qualified DNS hostname of the server,
|
||||
// as understood by the client. The hostname is represented as a byte
|
||||
// string using ASCII encoding without a trailing dot. ... DNS hostnames
|
||||
// are case-insensitive.
|
||||
if (isDNSHostName(effectiveHost)) {
|
||||
effectiveHost = effectiveHost.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
int port = uri.getPort();
|
||||
if (port == -1) {
|
||||
port = switch (lcaseScheme) {
|
||||
case "http" -> 80;
|
||||
case "https" -> 443;
|
||||
// we have already verified that this is a valid scheme, so this
|
||||
// should never happen
|
||||
default -> throw new AssertionError("Unsupported scheme: " + scheme);
|
||||
};
|
||||
}
|
||||
return new Origin(lcaseScheme, effectiveHost, port);
|
||||
}
|
||||
|
||||
static String toAuthority(final String host, final int port) {
|
||||
assert port > 0 : "invalid port: " + port;
|
||||
// borrowed from code in java.net.URI
|
||||
final boolean needBrackets = host.indexOf(':') >= 0
|
||||
&& !host.startsWith("[")
|
||||
&& !host.endsWith("]");
|
||||
if (needBrackets) {
|
||||
return "[" + host + "]:" + port;
|
||||
}
|
||||
return host + ":" + port;
|
||||
}
|
||||
|
||||
private static boolean isValidScheme(final String scheme) {
|
||||
// only "http" and "https" literals allowed
|
||||
return "http".equals(scheme) || "https".equals(scheme);
|
||||
}
|
||||
|
||||
private static boolean isDNSHostName(final String host) {
|
||||
final boolean isLiteral = IPAddressUtil.isIPv4LiteralAddress(host)
|
||||
|| IPAddressUtil.isIPv6LiteralAddress(host);
|
||||
|
||||
return !isLiteral;
|
||||
}
|
||||
}
|
||||
@ -310,8 +310,9 @@ class PlainHttpConnection extends HttpConnection {
|
||||
return tube;
|
||||
}
|
||||
|
||||
PlainHttpConnection(InetSocketAddress addr, HttpClientImpl client, String label) {
|
||||
super(addr, client, label);
|
||||
PlainHttpConnection(Origin originServer, InetSocketAddress addr, HttpClientImpl client,
|
||||
String label) {
|
||||
super(originServer, addr, client, label);
|
||||
try {
|
||||
this.chan = SocketChannel.open();
|
||||
chan.configureBlocking(false);
|
||||
|
||||
@ -30,7 +30,9 @@ import java.net.InetSocketAddress;
|
||||
class PlainProxyConnection extends PlainHttpConnection {
|
||||
|
||||
PlainProxyConnection(InetSocketAddress proxy, HttpClientImpl client, String label) {
|
||||
super(proxy, client, label);
|
||||
// we don't track the origin server for a plain proxy connection, since it
|
||||
// can be used to serve requests against several different origin servers.
|
||||
super(null, proxy, client, label);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -51,15 +51,16 @@ final class PlainTunnelingConnection extends HttpConnection {
|
||||
final InetSocketAddress proxyAddr;
|
||||
private volatile boolean connected;
|
||||
|
||||
protected PlainTunnelingConnection(InetSocketAddress addr,
|
||||
protected PlainTunnelingConnection(Origin originServer,
|
||||
InetSocketAddress addr,
|
||||
InetSocketAddress proxy,
|
||||
HttpClientImpl client,
|
||||
ProxyHeaders proxyHeaders,
|
||||
String label) {
|
||||
super(addr, client, label);
|
||||
super(originServer, addr, client, label);
|
||||
this.proxyAddr = proxy;
|
||||
this.proxyHeaders = proxyHeaders;
|
||||
delegate = new PlainHttpConnection(proxy, client, label);
|
||||
delegate = new PlainHttpConnection(originServer, proxy, client, label);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 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
|
||||
@ -38,7 +38,6 @@ import java.io.UncheckedIOException;
|
||||
import java.lang.System.Logger.Level;
|
||||
import java.net.ConnectException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpHeaders;
|
||||
import java.net.http.HttpTimeoutException;
|
||||
import java.nio.ByteBuffer;
|
||||
@ -73,12 +72,10 @@ import jdk.internal.net.http.common.DebugLogger.LoggerConfig;
|
||||
import jdk.internal.net.http.HttpRequestImpl;
|
||||
|
||||
import sun.net.NetProperties;
|
||||
import sun.net.util.IPAddressUtil;
|
||||
import sun.net.www.HeaderParser;
|
||||
|
||||
import static java.lang.String.format;
|
||||
import static java.nio.charset.StandardCharsets.US_ASCII;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.net.Authenticator.RequestorType.PROXY;
|
||||
import static java.net.Authenticator.RequestorType.SERVER;
|
||||
|
||||
@ -487,39 +484,6 @@ public final class Utils {
|
||||
return !token.isEmpty();
|
||||
}
|
||||
|
||||
public record ServerName (String name, boolean isLiteral) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyse the given address and determine if it is literal or not,
|
||||
* returning the address in String form.
|
||||
*/
|
||||
public static ServerName getServerName(InetSocketAddress addr) {
|
||||
String host = addr.getHostString();
|
||||
byte[] literal = IPAddressUtil.textToNumericFormatV4(host);
|
||||
if (literal == null) {
|
||||
// not IPv4 literal. Check IPv6
|
||||
literal = IPAddressUtil.textToNumericFormatV6(host);
|
||||
return new ServerName(host, literal != null);
|
||||
} else {
|
||||
return new ServerName(host, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isLoopbackLiteral(byte[] bytes) {
|
||||
if (bytes.length == 4) {
|
||||
return bytes[0] == 127;
|
||||
} else if (bytes.length == 16) {
|
||||
for (int i=0; i<14; i++)
|
||||
if (bytes[i] != 0)
|
||||
return false;
|
||||
if (bytes[15] != 1)
|
||||
return false;
|
||||
return true;
|
||||
} else
|
||||
throw new InternalError();
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates an RFC 7230 field-value.
|
||||
*
|
||||
@ -895,33 +859,6 @@ public final class Utils {
|
||||
return DebugLogger.createHttpLogger(dbgTag, config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the host string from a HttpRequestImpl
|
||||
*
|
||||
* @param request
|
||||
* @return
|
||||
*/
|
||||
public static String hostString(HttpRequestImpl request) {
|
||||
URI uri = request.uri();
|
||||
int port = uri.getPort();
|
||||
String host = uri.getHost();
|
||||
|
||||
boolean defaultPort;
|
||||
if (port == -1) {
|
||||
defaultPort = true;
|
||||
} else if (uri.getScheme().equalsIgnoreCase("https")) {
|
||||
defaultPort = port == 443;
|
||||
} else {
|
||||
defaultPort = port == 80;
|
||||
}
|
||||
|
||||
if (defaultPort) {
|
||||
return host;
|
||||
} else {
|
||||
return host + ":" + port;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a logger for debug HPACK traces.The logger should only be used
|
||||
* with levels whose severity is {@code <= DEBUG}.
|
||||
|
||||
185
test/jdk/java/net/httpclient/OriginTest.java
Normal file
185
test/jdk/java/net/httpclient/OriginTest.java
Normal file
@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright (c) 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
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Locale;
|
||||
|
||||
import jdk.internal.net.http.Origin;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @summary verify the behaviour of jdk.internal.net.http.Origin
|
||||
* @modules java.net.http/jdk.internal.net.http
|
||||
* @run junit OriginTest
|
||||
*/
|
||||
class OriginTest {
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"foo", "Bar", "HttPS", "HTTP"})
|
||||
void testInvalidScheme(final String scheme) throws Exception {
|
||||
final String validHost = "127.0.0.1";
|
||||
final int validPort = 80;
|
||||
final IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Origin(scheme, validHost, validPort);
|
||||
});
|
||||
assertTrue(iae.getMessage().contains("scheme"),
|
||||
"unexpected exception message: " + iae.getMessage());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"http", "https"})
|
||||
void testValidScheme(final String scheme) throws Exception {
|
||||
final String validHost = "127.0.0.1";
|
||||
final int validPort = 80;
|
||||
final Origin o1 = new Origin(scheme, validHost, validPort);
|
||||
assertEquals(validHost, o1.host(), "unexpected host");
|
||||
assertEquals(validPort, o1.port(), "unexpected port");
|
||||
assertEquals(scheme, o1.scheme(), "unexpected scheme");
|
||||
|
||||
final URI uri = URI.create(scheme + "://" + validHost + ":" + validPort);
|
||||
final Origin o2 = Origin.from(uri);
|
||||
assertNotNull(o2, "null Origin for URI " + uri);
|
||||
assertEquals(validHost, o2.host(), "unexpected host");
|
||||
assertEquals(validPort, o2.port(), "unexpected port");
|
||||
assertEquals(scheme, o2.scheme(), "unexpected scheme");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"JDK.java.net", "[::1]", "[0:0:0:0:0:0:0:1]"})
|
||||
void testInvalidHost(final String host) throws Exception {
|
||||
final String validScheme = "http";
|
||||
final int validPort = 8000;
|
||||
final IllegalArgumentException iae = assertThrows(IllegalArgumentException.class, () -> {
|
||||
new Origin(validScheme, host, validPort);
|
||||
});
|
||||
assertTrue(iae.getMessage().contains("host"),
|
||||
"unexpected exception message: " + iae.getMessage());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(strings = {"127.0.0.1", "localhost", "jdk.java.net", "::1", "0:0:0:0:0:0:0:1"})
|
||||
void testValidHost(final String host) throws Exception {
|
||||
final String validScheme = "https";
|
||||
final int validPort = 42;
|
||||
final Origin o1 = new Origin(validScheme, host, validPort);
|
||||
assertEquals(host, o1.host(), "unexpected host");
|
||||
assertEquals(validPort, o1.port(), "unexpected port");
|
||||
assertEquals(validScheme, o1.scheme(), "unexpected scheme");
|
||||
|
||||
String uriHost = host;
|
||||
if (host.contains(":")) {
|
||||
uriHost = "[" + host + "]";
|
||||
}
|
||||
final URI uri = URI.create(validScheme + "://" + uriHost + ":" + validPort);
|
||||
final Origin o2 = Origin.from(uri);
|
||||
assertNotNull(o2, "null Origin for URI " + uri);
|
||||
assertEquals(host, o2.host(), "unexpected host");
|
||||
assertEquals(validPort, o2.port(), "unexpected port");
|
||||
assertEquals(validScheme, o2.scheme(), "unexpected scheme");
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {-1, 0})
|
||||
void testInvalidPort(final int port) throws Exception {
|
||||
final String validScheme = "http";
|
||||
final String validHost = "127.0.0.1";
|
||||
final IllegalArgumentException iae = assertThrows(IllegalArgumentException.class,
|
||||
() -> new Origin(validScheme, validHost, port));
|
||||
assertTrue(iae.getMessage().contains("port"),
|
||||
"unexpected exception message: " + iae.getMessage());
|
||||
}
|
||||
|
||||
@ParameterizedTest
|
||||
@ValueSource(ints = {100, 1024, 80, 8080, 42})
|
||||
void testValidPort(final int port) throws Exception {
|
||||
final String validScheme = "https";
|
||||
final String validHost = "localhost";
|
||||
final Origin o1 = new Origin(validScheme, validHost, port);
|
||||
assertEquals(validHost, o1.host(), "unexpected host");
|
||||
assertEquals(port, o1.port(), "unexpected port");
|
||||
assertEquals(validScheme, o1.scheme(), "unexpected scheme");
|
||||
|
||||
final URI uri = URI.create(validScheme + "://" + validHost + ":" + port);
|
||||
final Origin o2 = Origin.from(uri);
|
||||
assertNotNull(o2, "null Origin for URI " + uri);
|
||||
assertEquals(validHost, o2.host(), "unexpected host");
|
||||
assertEquals(port, o2.port(), "unexpected port");
|
||||
assertEquals(validScheme, o2.scheme(), "unexpected scheme");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testInferredPort() throws Exception {
|
||||
final URI httpURI = URI.create("http://localhost");
|
||||
final Origin httpOrigin = Origin.from(httpURI);
|
||||
assertNotNull(httpOrigin, "null Origin for URI " + httpURI);
|
||||
assertEquals("localhost", httpOrigin.host(), "unexpected host");
|
||||
assertEquals(80, httpOrigin.port(), "unexpected port");
|
||||
assertEquals("http", httpOrigin.scheme(), "unexpected scheme");
|
||||
|
||||
|
||||
final URI httpsURI = URI.create("https://[::1]");
|
||||
final Origin httpsOrigin = Origin.from(httpsURI);
|
||||
assertNotNull(httpsOrigin, "null Origin for URI " + httpsURI);
|
||||
assertEquals("::1", httpsOrigin.host(), "unexpected host");
|
||||
assertEquals(443, httpsOrigin.port(), "unexpected port");
|
||||
assertEquals("https", httpsOrigin.scheme(), "unexpected scheme");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testFromURI() {
|
||||
// non-lower case URI scheme is expected to be converted to lowercase in the Origin
|
||||
// constructed through Origin.from(URI)
|
||||
for (final String scheme : new String[]{"httPs", "HTTP"}) {
|
||||
final String expectedScheme = scheme.toLowerCase(Locale.ROOT);
|
||||
final URI uri = URI.create(scheme + "://localhost:1234");
|
||||
final Origin origin = Origin.from(uri);
|
||||
assertNotNull(origin, "null Origin for URI " + uri);
|
||||
assertEquals("localhost", origin.host(), "unexpected host");
|
||||
assertEquals(1234, origin.port(), "unexpected port");
|
||||
assertEquals(expectedScheme, origin.scheme(), "unexpected scheme");
|
||||
}
|
||||
// URI without a port is expected to be defaulted to port 80 or 443 for http and https
|
||||
// schemes respectively
|
||||
for (final String scheme : new String[]{"http", "https"}) {
|
||||
final int expectedPort = switch (scheme) {
|
||||
case "http" -> 80;
|
||||
case "https" -> 443;
|
||||
default -> fail("unexpected scheme: " + scheme);
|
||||
};
|
||||
final URI uri = URI.create(scheme + "://localhost");
|
||||
final Origin origin = Origin.from(uri);
|
||||
assertNotNull(origin, "null Origin for URI " + uri);
|
||||
assertEquals("localhost", origin.host(), "unexpected host");
|
||||
assertEquals(expectedPort, origin.port(), "unexpected port");
|
||||
assertEquals(scheme, origin.scheme(), "unexpected scheme");
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -33,6 +33,7 @@ import java.net.ProxySelector;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.SocketOption;
|
||||
import java.net.URI;
|
||||
import java.net.http.HttpHeaders;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
@ -459,7 +460,9 @@ public class ConnectionPoolTest {
|
||||
InetSocketAddress address,
|
||||
InetSocketAddress proxy,
|
||||
boolean secured) {
|
||||
super(address, impl, "testConn-" + IDS.incrementAndGet());
|
||||
final Origin originServer = Origin.from(
|
||||
URI.create("http://"+ address.getHostString() + ":" + address.getPort()));
|
||||
super(originServer, address, impl, "testConn-" + IDS.incrementAndGet());
|
||||
this.key = ConnectionPool.cacheKey(secured, address, proxy);
|
||||
this.address = address;
|
||||
this.proxy = proxy;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user