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:
Jaikiran Pai 2025-07-03 09:32:09 +00:00
parent 2f683fdc4a
commit 1be29bd725
11 changed files with 416 additions and 89 deletions

View File

@ -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;

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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}.

View 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");
}
}
}

View File

@ -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;