From 6c4ee083be2d4ffb528a4a6259dfcf63bd74ac51 Mon Sep 17 00:00:00 2001
From: Simone Bordet
Date: Thu, 17 Jan 2019 10:43:03 +0100
Subject: [PATCH] Issue #132 - ClientConnector abstraction.
Introduced ClientConnector and refactored HttpClient transports,
removing duplicated code that was connect() to a remote host.
Refactored also HTTP2Client to reference ClientConnector.
Refactored tests accordingly to the changes introduced in the
implementations.
Signed-off-by: Simone Bordet
---
.../alpn/client/ALPNClientConnection.java | 8 +-
.../client/ALPNClientConnectionFactory.java | 3 +-
jetty-alpn/jetty-alpn-java-client/pom.xml | 1 +
.../alpn/java/client/JDK9HTTP2Client.java | 5 +-
.../jetty/alpn/java/server/JDK9ALPNTest.java | 21 +-
.../AbstractConnectorHttpClientTransport.java | 156 ++------
.../client/AbstractHttpClientTransport.java | 13 +
.../org/eclipse/jetty/client/HttpClient.java | 91 ++---
.../jetty/client/HttpClientTransport.java | 4 +-
.../java/org/eclipse/jetty/client/Origin.java | 42 ++-
.../jetty/client/ResponseNotifier.java | 27 +-
.../http/HttpClientTransportOverHTTP.java | 11 +-
.../client/http/HttpConnectionOverHTTP.java | 1 -
.../client/HttpClientCustomProxyTest.java | 7 +-
.../jetty/client/HttpClientTLSTest.java | 20 +-
.../eclipse/jetty/client/HttpClientTest.java | 28 +-
.../InsufficientThreadsDetectionTest.java | 12 +-
.../http/HttpDestinationOverHTTPTest.java | 18 +-
.../http/HttpClientTransportOverFCGI.java | 9 +-
.../jetty/http2/client/HTTP2Client.java | 245 +++----------
.../client/HTTP2ClientConnectionFactory.java | 15 +-
.../eclipse/jetty/http2/client/Client.java | 94 -----
.../http/HttpClientTransportOverHTTP2.java | 19 +-
.../client/http/MaxConcurrentStreamsTest.java | 6 +-
.../jetty/io/ClientConnectionFactory.java | 8 +-
.../org/eclipse/jetty/io/ClientConnector.java | 333 ++++++++++++++++++
.../jetty/io/NegotiatingClientConnection.java | 6 +-
.../io/ssl/SslClientConnectionFactory.java | 17 +-
.../eclipse/jetty/proxy/ProxyServletTest.java | 18 +-
.../HttpClientTransportOverUnixSockets.java | 154 +++++---
.../jetty/unixsocket/UnixSocketTest.java | 97 +++--
.../http/client/ConnectionStatisticsTest.java | 21 +-
.../http/client/HttpClientContinueTest.java | 10 +-
.../client/HttpClientIdleTimeoutTest.java | 18 +-
.../jetty/http/client/HttpClientTest.java | 25 +-
.../test/resources/jetty-logging.properties | 2 +-
36 files changed, 795 insertions(+), 770 deletions(-)
delete mode 100644 jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/Client.java
create mode 100644 jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java
diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
index 9779f0ec49a..4b6c25573b8 100644
--- a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
+++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnection.java
@@ -27,13 +27,9 @@ import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.NegotiatingClientConnection;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
public class ALPNClientConnection extends NegotiatingClientConnection
{
- private static final Logger LOG = Log.getLogger(ALPNClientConnection.class);
-
private final List protocols;
public ALPNClientConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, SSLEngine sslEngine, Map context, List protocols)
@@ -49,9 +45,9 @@ public class ALPNClientConnection extends NegotiatingClientConnection
public void selected(String protocol)
{
- if (protocol==null || !protocols.contains(protocol))
+ if (protocol == null || !protocols.contains(protocol))
close();
else
- super.completed();
+ completed();
}
}
diff --git a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
index c1a00326620..75d14ff337c 100644
--- a/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
+++ b/jetty-alpn/jetty-alpn-client/src/main/java/org/eclipse/jetty/alpn/client/ALPNClientConnectionFactory.java
@@ -18,7 +18,6 @@
package org.eclipse.jetty.alpn.client;
-import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@@ -96,7 +95,7 @@ public class ALPNClientConnectionFactory extends NegotiatingClientConnectionFact
}
@Override
- public Connection newConnection(EndPoint endPoint, Map context) throws IOException
+ public Connection newConnection(EndPoint endPoint, Map context)
{
SSLEngine engine = (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY);
for (Client processor : processors)
diff --git a/jetty-alpn/jetty-alpn-java-client/pom.xml b/jetty-alpn/jetty-alpn-java-client/pom.xml
index 31660c91677..6054bc0d7a6 100644
--- a/jetty-alpn/jetty-alpn-java-client/pom.xml
+++ b/jetty-alpn/jetty-alpn-java-client/pom.xml
@@ -42,6 +42,7 @@
jetty-alpn-client
${project.version}
+
org.eclipse.jetty.http2
http2-client
diff --git a/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2Client.java b/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2Client.java
index 9dbcf11e479..aa772b826e3 100644
--- a/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2Client.java
+++ b/jetty-alpn/jetty-alpn-java-client/src/test/java/org/eclipse/jetty/alpn/java/client/JDK9HTTP2Client.java
@@ -35,22 +35,19 @@ import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
public class JDK9HTTP2Client
{
public static void main(String[] args) throws Exception
{
HTTP2Client client = new HTTP2Client();
- SslContextFactory sslContextFactory = new SslContextFactory();
- client.addBean(sslContextFactory);
client.start();
String host = "webtide.com";
int port = 443;
FuturePromise sessionPromise = new FuturePromise<>();
- client.connect(sslContextFactory, new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
+ client.connect(client.getClientConnector().getSslContextFactory(), new InetSocketAddress(host, port), new Session.Listener.Adapter(), sessionPromise);
Session session = sessionPromise.get(5, TimeUnit.SECONDS);
HttpFields requestFields = new HttpFields();
diff --git a/jetty-alpn/jetty-alpn-java-server/src/test/java/org/eclipse/jetty/alpn/java/server/JDK9ALPNTest.java b/jetty-alpn/jetty-alpn-java-server/src/test/java/org/eclipse/jetty/alpn/java/server/JDK9ALPNTest.java
index 8f74502dfe3..72a5aef503a 100644
--- a/jetty-alpn/jetty-alpn-java-server/src/test/java/org/eclipse/jetty/alpn/java/server/JDK9ALPNTest.java
+++ b/jetty-alpn/jetty-alpn-java-server/src/test/java/org/eclipse/jetty/alpn/java/server/JDK9ALPNTest.java
@@ -18,11 +18,7 @@
package org.eclipse.jetty.alpn.java.server;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-
import java.io.BufferedReader;
-import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
@@ -31,7 +27,6 @@ import java.nio.charset.StandardCharsets;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
-import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -45,8 +40,12 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+
public class JDK9ALPNTest
{
private Server server;
@@ -66,6 +65,13 @@ public class JDK9ALPNTest
server.start();
}
+ @AfterEach
+ public void stopServer() throws Exception
+ {
+ if (server != null)
+ server.stop();
+ }
+
private SslContextFactory newSslContextFactory()
{
SslContextFactory sslContextFactory = new SslContextFactory();
@@ -84,7 +90,7 @@ public class JDK9ALPNTest
startServer(new AbstractHandler.ErrorDispatchHandler()
{
@Override
- protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{
baseRequest.setHandled(true);
}
@@ -126,7 +132,7 @@ public class JDK9ALPNTest
startServer(new AbstractHandler.ErrorDispatchHandler()
{
@Override
- protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
{
baseRequest.setHandled(true);
}
@@ -163,6 +169,5 @@ public class JDK9ALPNTest
break;
}
}
-
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java
index 67f57a11486..0c234daa160 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractConnectorHttpClientTransport.java
@@ -18,21 +18,12 @@
package org.eclipse.jetty.client;
-import java.io.IOException;
import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.net.SocketException;
-import java.nio.channels.SelectableChannel;
-import java.nio.channels.SelectionKey;
-import java.nio.channels.SocketChannel;
+import java.time.Duration;
import java.util.Map;
import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.ManagedSelector;
-import org.eclipse.jetty.io.SelectorManager;
-import org.eclipse.jetty.io.SocketChannelEndPoint;
-import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
+import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
@@ -40,147 +31,48 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
@ManagedObject
public abstract class AbstractConnectorHttpClientTransport extends AbstractHttpClientTransport
{
- private final int selectors;
- private SelectorManager selectorManager;
+ private final ClientConnector connector;
- protected AbstractConnectorHttpClientTransport(int selectors)
+ protected AbstractConnectorHttpClientTransport(ClientConnector connector)
{
- this.selectors = selectors;
+ this.connector = connector;
+ addBean(connector);
+ }
+
+ public ClientConnector getClientConnector()
+ {
+ return connector;
}
@ManagedAttribute(value = "The number of selectors", readonly = true)
public int getSelectors()
{
- return selectors;
+ return connector.getSelectors();
}
@Override
protected void doStart() throws Exception
{
HttpClient httpClient = getHttpClient();
- selectorManager = newSelectorManager(httpClient);
- selectorManager.setConnectTimeout(httpClient.getConnectTimeout());
- addBean(selectorManager);
+ connector.setBindAddress(httpClient.getBindAddress());
+ connector.setByteBufferPool(httpClient.getByteBufferPool());
+ connector.setConnectBlocking(httpClient.isConnectBlocking());
+ connector.setConnectTimeout(Duration.ofMillis(httpClient.getConnectTimeout()));
+ connector.setExecutor(httpClient.getExecutor());
+ connector.setIdleTimeout(Duration.ofMillis(httpClient.getIdleTimeout()));
+ connector.setScheduler(httpClient.getScheduler());
+ connector.setSslContextFactory(httpClient.getSslContextFactory());
super.doStart();
}
- @Override
- protected void doStop() throws Exception
- {
- super.doStop();
- removeBean(selectorManager);
- }
-
@Override
public void connect(InetSocketAddress address, Map context)
{
- SocketChannel channel = null;
- try
- {
- channel = SocketChannel.open();
- HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
- HttpClient client = destination.getHttpClient();
- SocketAddress bindAddress = client.getBindAddress();
- if (bindAddress != null)
- channel.bind(bindAddress);
- configure(client, channel);
-
- context.put(SslClientConnectionFactory.SSL_PEER_HOST_CONTEXT_KEY, destination.getHost());
- context.put(SslClientConnectionFactory.SSL_PEER_PORT_CONTEXT_KEY, destination.getPort());
-
- boolean connected = true;
- if (client.isConnectBlocking())
- {
- channel.socket().connect(address, (int)client.getConnectTimeout());
- channel.configureBlocking(false);
- }
- else
- {
- channel.configureBlocking(false);
- connected = channel.connect(address);
- }
- if (connected)
- selectorManager.accept(channel, context);
- else
- selectorManager.connect(channel, context);
- }
- // Must catch all exceptions, since some like
- // UnresolvedAddressException are not IOExceptions.
- catch (Throwable x)
- {
- // If IPv6 is not deployed, a generic SocketException "Network is unreachable"
- // exception is being thrown, so we attempt to provide a better error message.
- if (x.getClass() == SocketException.class)
- x = new SocketException("Could not connect to " + address).initCause(x);
-
- try
- {
- if (channel != null)
- channel.close();
- }
- catch (IOException xx)
- {
- LOG.ignore(xx);
- }
- finally
- {
- connectFailed(context, x);
- }
- }
- }
-
- protected void connectFailed(Map context, Throwable x)
- {
- if (LOG.isDebugEnabled())
- LOG.debug("Could not connect to {}", context.get(HTTP_DESTINATION_CONTEXT_KEY));
+ HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
+ context.put(ClientConnector.CLIENT_CONNECTION_FACTORY_CONTEXT_KEY, destination.getClientConnectionFactory());
@SuppressWarnings("unchecked")
Promise promise = (Promise)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
- promise.failed(x);
- }
-
- protected void configure(HttpClient client, SocketChannel channel) throws IOException
- {
- channel.socket().setTcpNoDelay(client.isTCPNoDelay());
- }
-
- protected SelectorManager newSelectorManager(HttpClient client)
- {
- return new ClientSelectorManager(client, getSelectors());
- }
-
- protected class ClientSelectorManager extends SelectorManager
- {
- private final HttpClient client;
-
- protected ClientSelectorManager(HttpClient client, int selectors)
- {
- super(client.getExecutor(), client.getScheduler(), selectors);
- this.client = client;
- }
-
- @Override
- protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey key)
- {
- SocketChannelEndPoint endp = new SocketChannelEndPoint(channel, selector, key, getScheduler());
- endp.setIdleTimeout(client.getIdleTimeout());
- return endp;
- }
-
- @Override
- public org.eclipse.jetty.io.Connection newConnection(SelectableChannel channel, EndPoint endPoint, Object attachment) throws IOException
- {
- @SuppressWarnings("unchecked")
- Map context = (Map)attachment;
- HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
- return destination.getClientConnectionFactory().newConnection(endPoint, context);
- }
-
- @Override
- protected void connectionFailed(SelectableChannel channel, Throwable x, Object attachment)
- {
- @SuppressWarnings("unchecked")
- Map context = (Map)attachment;
- connectFailed(context, x);
- }
+ context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, new Promise.Wrapper<>(promise));
+ connector.connect(address, context);
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java
index 3a601f7c237..e5282e0fce9 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AbstractHttpClientTransport.java
@@ -18,6 +18,10 @@
package org.eclipse.jetty.client;
+import java.util.Map;
+
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
@@ -53,4 +57,13 @@ public abstract class AbstractHttpClientTransport extends ContainerLifeCycle imp
{
this.factory = factory;
}
+
+ protected void connectFailed(Map context, Throwable failure)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not connect to {}", context.get(HTTP_DESTINATION_CONTEXT_KEY));
+ @SuppressWarnings("unchecked")
+ Promise promise = (Promise)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
+ promise.failed(failure);
+ }
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
index e6761f41eca..555871270cc 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClient.java
@@ -81,16 +81,16 @@ import org.eclipse.jetty.util.thread.Scheduler;
import org.eclipse.jetty.util.thread.ThreadPool;
/**
- * {@link HttpClient} provides an efficient, asynchronous, non-blocking implementation
+ *
HttpClient provides an efficient, asynchronous, non-blocking implementation
* to perform HTTP requests to a server through a simple API that offers also blocking semantic.
- * {@link HttpClient} provides easy-to-use methods such as {@link #GET(String)} that allow to perform HTTP
+ *
HttpClient provides easy-to-use methods such as {@link #GET(String)} that allow to perform HTTP
* requests in a one-liner, but also gives the ability to fine tune the configuration of requests via
* {@link HttpClient#newRequest(URI)}.
- * {@link HttpClient} acts as a central configuration point for network parameters (such as idle timeouts)
+ *
HttpClient acts as a central configuration point for network parameters (such as idle timeouts)
* and HTTP parameters (such as whether to follow redirects).
- * {@link HttpClient} transparently pools connections to servers, but allows direct control of connections
+ *
HttpClient transparently pools connections to servers, but allows direct control of connections
* for cases where this is needed.
- * {@link HttpClient} also acts as a central configuration point for cookies, via {@link #getCookieStore()}.
+ * HttpClient also acts as a central configuration point for cookies, via {@link #getCookieStore()}.
* Typical usage:
*
* HttpClient httpClient = new HttpClient();
@@ -157,7 +157,7 @@ public class HttpClient extends ContainerLifeCycle
private String defaultRequestContentType = "application/octet-stream";
/**
- * Creates a {@link HttpClient} instance that can perform requests to non-TLS destinations only
+ * Creates a HttpClient instance that can perform requests to non-TLS destinations only
* (that is, requests with the "http" scheme only, and not "https").
*
* @see #HttpClient(SslContextFactory) to perform requests to TLS destinations.
@@ -168,7 +168,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * Creates a {@link HttpClient} instance that can perform requests to non-TLS and TLS destinations
+ * Creates a HttpClient instance that can perform requests to non-TLS and TLS destinations
* (that is, both requests with the "http" scheme and with the "https" scheme).
*
* @param sslContextFactory the {@link SslContextFactory} that manages TLS encryption
@@ -517,7 +517,7 @@ public class HttpClient extends ContainerLifeCycle
/**
* Returns a {@link Destination} for the given scheme, host and port.
* Applications may use {@link Destination}s to create {@link Connection}s
- * that will be outside {@link HttpClient}'s pooling mechanism, to explicitly
+ * that will be outside HttpClient's pooling mechanism, to explicitly
* control the connection lifecycle (in particular their termination with
* {@link Connection#close()}).
*
@@ -570,7 +570,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @return the list of destinations known to this {@link HttpClient}.
+ * @return the list of destinations known to this HttpClient.
*/
public List getDestinations()
{
@@ -586,13 +586,13 @@ public class HttpClient extends ContainerLifeCycle
protected void newConnection(final HttpDestination destination, final Promise promise)
{
Origin.Address address = destination.getConnectAddress();
- resolver.resolve(address.getHost(), address.getPort(), new Promise>()
+ resolver.resolve(address.getHost(), address.getPort(), new Promise<>()
{
@Override
public void succeeded(List socketAddresses)
{
Map context = new HashMap<>();
- context.put(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY, HttpClient.this);
+ context.put(ClientConnectionFactory.CLIENT_CONTEXT_KEY, HttpClient.this);
context.put(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY, destination);
connect(socketAddresses, 0, context);
}
@@ -605,7 +605,7 @@ public class HttpClient extends ContainerLifeCycle
private void connect(List socketAddresses, int index, Map context)
{
- context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, new Promise.Wrapper(promise)
+ context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, new Promise.Wrapper<>(promise)
{
@Override
public void failed(Throwable x)
@@ -638,7 +638,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @return the {@link ByteBufferPool} of this {@link HttpClient}
+ * @return the {@link ByteBufferPool} of this HttpClient
*/
public ByteBufferPool getByteBufferPool()
{
@@ -646,7 +646,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @param byteBufferPool the {@link ByteBufferPool} of this {@link HttpClient}
+ * @param byteBufferPool the {@link ByteBufferPool} of this HttpClient
*/
public void setByteBufferPool(ByteBufferPool byteBufferPool)
{
@@ -706,7 +706,7 @@ public class HttpClient extends ContainerLifeCycle
/**
* Sets the socket address resolution timeout used by the default {@link SocketAddressResolver}
- * created by this {@link HttpClient} at startup.
+ * created by this HttpClient at startup.
* For more fine tuned configuration of socket address resolution, see
* {@link #setSocketAddressResolver(SocketAddressResolver)}.
*
@@ -755,7 +755,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @return the "User-Agent" HTTP field of this {@link HttpClient}
+ * @return the "User-Agent" HTTP field of this HttpClient
*/
public HttpField getUserAgentField()
{
@@ -763,7 +763,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @param agent the "User-Agent" HTTP header string of this {@link HttpClient}
+ * @param agent the "User-Agent" HTTP header string of this HttpClient
*/
public void setUserAgentField(HttpField agent)
{
@@ -773,7 +773,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @return whether this {@link HttpClient} follows HTTP redirects
+ * @return whether this HttpClient follows HTTP redirects
* @see Request#isFollowRedirects()
*/
@ManagedAttribute("Whether HTTP redirects are followed")
@@ -783,7 +783,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @param follow whether this {@link HttpClient} follows HTTP redirects
+ * @param follow whether this HttpClient follows HTTP redirects
* @see #setMaxRedirects(int)
*/
public void setFollowRedirects(boolean follow)
@@ -792,7 +792,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @return the {@link Executor} of this {@link HttpClient}
+ * @return the {@link Executor} of this HttpClient
*/
public Executor getExecutor()
{
@@ -800,7 +800,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @param executor the {@link Executor} of this {@link HttpClient}
+ * @param executor the {@link Executor} of this HttpClient
*/
public void setExecutor(Executor executor)
{
@@ -811,7 +811,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @return the {@link Scheduler} of this {@link HttpClient}
+ * @return the {@link Scheduler} of this HttpClient
*/
public Scheduler getScheduler()
{
@@ -819,7 +819,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @param scheduler the {@link Scheduler} of this {@link HttpClient}
+ * @param scheduler the {@link Scheduler} of this HttpClient
*/
public void setScheduler(Scheduler scheduler)
{
@@ -830,7 +830,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @return the {@link SocketAddressResolver} of this {@link HttpClient}
+ * @return the {@link SocketAddressResolver} of this HttpClient
*/
public SocketAddressResolver getSocketAddressResolver()
{
@@ -838,7 +838,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @param resolver the {@link SocketAddressResolver} of this {@link HttpClient}
+ * @param resolver the {@link SocketAddressResolver} of this HttpClient
*/
public void setSocketAddressResolver(SocketAddressResolver resolver)
{
@@ -849,7 +849,7 @@ public class HttpClient extends ContainerLifeCycle
}
/**
- * @return the max number of connections that this {@link HttpClient} opens to {@link Destination}s
+ * @return the max number of connections that this HttpClient opens to {@link Destination}s
*/
@ManagedAttribute("The max number of connections per each destination")
public int getMaxConnectionsPerDestination()
@@ -862,11 +862,11 @@ public class HttpClient extends ContainerLifeCycle
*
* RFC 2616 suggests that 2 connections should be opened per each destination,
* but browsers commonly open 6.
- * If this {@link HttpClient} is used for load testing, it is common to have only one destination
+ * If this HttpClient is used for load testing, it is common to have only one destination
* (the server to load test), and it is recommended to set this value to a high value (at least as
* much as the threads present in the {@link #getExecutor() executor}).
*
- * @param maxConnectionsPerDestination the max number of connections that this {@link HttpClient} opens to {@link Destination}s
+ * @param maxConnectionsPerDestination the max number of connections that this HttpClient opens to {@link Destination}s
*/
public void setMaxConnectionsPerDestination(int maxConnectionsPerDestination)
{
@@ -885,11 +885,11 @@ public class HttpClient extends ContainerLifeCycle
/**
* Sets the max number of requests that may be queued to a destination.
*
- * If this {@link HttpClient} performs a high rate of requests to a destination,
+ * If this HttpClient performs a high rate of requests to a destination,
* and all the connections managed by that destination are busy with other requests,
* then new requests will be queued up in the destination.
* This parameter controls how many requests can be queued before starting to reject them.
- * If this {@link HttpClient} is used for load testing, it is common to have this parameter
+ * If this HttpClient is used for load testing, it is common to have this parameter
* set to a high value, although this may impact latency (requests sit in the queue for a long
* time before being sent).
*
@@ -970,35 +970,6 @@ public class HttpClient extends ContainerLifeCycle
this.tcpNoDelay = tcpNoDelay;
}
- /**
- * @return true to dispatch I/O operations in a different thread, false to execute them in the selector thread
- * @see #setDispatchIO(boolean)
- */
- @Deprecated
- public boolean isDispatchIO()
- {
- // TODO this did default to true, so usage needs to be evaluated.
- return false;
- }
-
- /**
- * Whether to dispatch I/O operations from the selector thread to a different thread.
- *
- * This implementation never blocks on I/O operation, but invokes application callbacks that may
- * take time to execute or block on other I/O.
- * If application callbacks are known to take time or block on I/O, then parameter {@code dispatchIO}
- * should be set to true.
- * If application callbacks are known to be quick and never block on I/O, then parameter {@code dispatchIO}
- * may be set to false.
- *
- * @param dispatchIO true to dispatch I/O operations in a different thread,
- * false to execute them in the selector thread
- */
- @Deprecated
- public void setDispatchIO(boolean dispatchIO)
- {
- }
-
/**
* Gets the http compliance mode for parsing http responses.
* The default http compliance level is {@link HttpCompliance#RFC7230} which is the latest HTTP/1.1 specification
@@ -1256,7 +1227,7 @@ public class HttpClient extends ContainerLifeCycle
public Iterator iterator()
{
final Iterator iterator = set.iterator();
- return new Iterator()
+ return new Iterator<>()
{
@Override
public boolean hasNext()
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClientTransport.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClientTransport.java
index ff5f9269d6d..7d2366a19fd 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClientTransport.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpClientTransport.java
@@ -36,8 +36,8 @@ import org.eclipse.jetty.io.ClientConnectionFactory;
*/
public interface HttpClientTransport extends ClientConnectionFactory
{
- public static final String HTTP_DESTINATION_CONTEXT_KEY = "http.destination";
- public static final String HTTP_CONNECTION_PROMISE_CONTEXT_KEY = "http.connection.promise";
+ public static final String HTTP_DESTINATION_CONTEXT_KEY = "org.eclipse.jetty.client.destination";
+ public static final String HTTP_CONNECTION_PROMISE_CONTEXT_KEY = "org.eclipse.jetty.client.connection.promise";
/**
* Sets the {@link HttpClient} instance on this transport.
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/Origin.java b/jetty-client/src/main/java/org/eclipse/jetty/client/Origin.java
index c94dfb8928c..344b1c16edb 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/Origin.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/Origin.java
@@ -48,6 +48,23 @@ public class Origin
return address;
}
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj)
+ return true;
+ if (obj == null || getClass() != obj.getClass())
+ return false;
+ Origin that = (Origin)obj;
+ return scheme.equals(that.scheme) && address.equals(that.address);
+ }
+
+ @Override
+ public int hashCode()
+ {
+ return Objects.hash(scheme, address);
+ }
+
public String asString()
{
StringBuilder result = new StringBuilder();
@@ -56,20 +73,9 @@ public class Origin
}
@Override
- public boolean equals(Object obj)
+ public String toString()
{
- if (this == obj) return true;
- if (obj == null || getClass() != obj.getClass()) return false;
- Origin that = (Origin)obj;
- return scheme.equals(that.scheme) && address.equals(that.address);
- }
-
- @Override
- public int hashCode()
- {
- int result = scheme.hashCode();
- result = 31 * result + address.hashCode();
- return result;
+ return asString();
}
public static class Address
@@ -96,8 +102,10 @@ public class Origin
@Override
public boolean equals(Object obj)
{
- if (this == obj) return true;
- if (obj == null || getClass() != obj.getClass()) return false;
+ if (this == obj)
+ return true;
+ if (obj == null || getClass() != obj.getClass())
+ return false;
Address that = (Address)obj;
return host.equals(that.host) && port == that.port;
}
@@ -105,9 +113,7 @@ public class Origin
@Override
public int hashCode()
{
- int result = host.hashCode();
- result = 31 * result + port;
- return result;
+ return Objects.hash(host, port);
}
public String asString()
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
index 6d9faa94089..7db9af83851 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ResponseNotifier.java
@@ -203,16 +203,7 @@ public class ResponseNotifier
public void forwardSuccess(List listeners, Response response)
{
- notifyBegin(listeners, response);
- for (Iterator iterator = response.getHeaders().iterator(); iterator.hasNext();)
- {
- HttpField field = iterator.next();
- if (!notifyHeader(listeners, response, field))
- iterator.remove();
- }
- notifyHeaders(listeners, response);
- if (response instanceof ContentResponse)
- notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), Callback.NOOP);
+ forwardEvents(listeners, response);
notifySuccess(listeners, response);
}
@@ -223,9 +214,16 @@ public class ResponseNotifier
}
public void forwardFailure(List listeners, Response response, Throwable failure)
+ {
+ forwardEvents(listeners, response);
+ notifyFailure(listeners, response, failure);
+ }
+
+ private void forwardEvents(List listeners, Response response)
{
notifyBegin(listeners, response);
- for (Iterator iterator = response.getHeaders().iterator(); iterator.hasNext();)
+ Iterator iterator = response.getHeaders().iterator();
+ while (iterator.hasNext())
{
HttpField field = iterator.next();
if (!notifyHeader(listeners, response, field))
@@ -233,8 +231,11 @@ public class ResponseNotifier
}
notifyHeaders(listeners, response);
if (response instanceof ContentResponse)
- notifyContent(listeners, response, ByteBuffer.wrap(((ContentResponse)response).getContent()), Callback.NOOP);
- notifyFailure(listeners, response, failure);
+ {
+ byte[] content = ((ContentResponse)response).getContent();
+ if (content != null && content.length > 0)
+ notifyContent(listeners, response, ByteBuffer.wrap(content), Callback.NOOP);
+ }
}
public void forwardFailureComplete(List listeners, Request request, Throwable requestFailure, Response response, Throwable responseFailure)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientTransportOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientTransportOverHTTP.java
index 8c353e98f79..9ef6bd7c49e 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientTransportOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpClientTransportOverHTTP.java
@@ -26,6 +26,7 @@ import org.eclipse.jetty.client.DuplexConnectionPool;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.ProcessorUtils;
import org.eclipse.jetty.util.Promise;
@@ -36,12 +37,18 @@ public class HttpClientTransportOverHTTP extends AbstractConnectorHttpClientTran
{
public HttpClientTransportOverHTTP()
{
- this(Math.max( 1, ProcessorUtils.availableProcessors() / 2));
+ this(Math.max(1, ProcessorUtils.availableProcessors() / 2));
}
public HttpClientTransportOverHTTP(int selectors)
{
- super(selectors);
+ this(new ClientConnector());
+ getClientConnector().setSelectors(selectors);
+ }
+
+ public HttpClientTransportOverHTTP(ClientConnector connector)
+ {
+ super(connector);
setConnectionPoolFactory(destination -> new DuplexConnectionPool(destination, getHttpClient().getMaxConnectionsPerDestination(), destination));
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
index 001989b7913..746e54ec2b0 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java
@@ -93,7 +93,6 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec
return bytesOut.longValue();
}
-
protected void addBytesOut(long bytesOut)
{
this.bytesOut.add(bytesOut);
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java
index 529df7b4112..4dffe0b939b 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.client;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.Map;
@@ -49,9 +46,11 @@ import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.AfterEach;
-
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
public class HttpClientCustomProxyTest
{
public static final byte[] CAFE_BABE = new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE};
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java
index ecb7789efb7..ea806cb8c31 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTLSTest.java
@@ -18,15 +18,6 @@
package org.eclipse.jetty.client;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-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.Assumptions.assumeTrue;
-
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
@@ -53,17 +44,22 @@ import org.eclipse.jetty.io.ssl.SslHandshakeListener;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
-import org.eclipse.jetty.util.JavaVersion;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.ExecutorThreadPool;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
-import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.JRE;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+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;
+
public class HttpClientTLSTest
{
private Server server;
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
index b32c271ff45..9fba73442ca 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java
@@ -18,16 +18,6 @@
package org.eclipse.jetty.client;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-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 java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -106,6 +96,16 @@ import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
@ExtendWith(WorkDirExtension.class)
public class HttpClientTest extends AbstractHttpClientServerTest
{
@@ -1716,10 +1716,12 @@ public class HttpClientTest extends AbstractHttpClientServerTest
try (ServerSocket server = new ServerSocket(0))
{
- startClient(scenario);
- client.setMaxConnectionsPerDestination(1);
int idleTimeout = 2000;
- client.setIdleTimeout(idleTimeout);
+ startClient(scenario, null, httpClient ->
+ {
+ httpClient.setMaxConnectionsPerDestination(1);
+ httpClient.setIdleTimeout(idleTimeout);
+ });
Request request = client.newRequest("localhost", server.getLocalPort())
.scheme(scenario.getScheme())
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/InsufficientThreadsDetectionTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/InsufficientThreadsDetectionTest.java
index ea209422f79..86051d6f8d7 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/InsufficientThreadsDetectionTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/InsufficientThreadsDetectionTest.java
@@ -18,13 +18,12 @@
package org.eclipse.jetty.client;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
-
import org.junit.jupiter.api.Test;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
public class InsufficientThreadsDetectionTest
{
@Test
@@ -33,9 +32,7 @@ public class InsufficientThreadsDetectionTest
QueuedThreadPool clientThreads = new QueuedThreadPool(1);
HttpClient httpClient = new HttpClient(new HttpClientTransportOverHTTP(1), null);
httpClient.setExecutor(clientThreads);
- assertThrows(IllegalStateException.class, ()->{
- httpClient.start();
- });
+ assertThrows(IllegalStateException.class, httpClient::start);
}
@Test
@@ -46,7 +43,8 @@ public class InsufficientThreadsDetectionTest
httpClient1.setExecutor(clientThreads);
httpClient1.start();
- assertThrows(IllegalStateException.class, ()->{
+ assertThrows(IllegalStateException.class, () ->
+ {
// Share the same thread pool with another instance.
HttpClient httpClient2 = new HttpClient(new HttpClientTransportOverHTTP(1), null);
httpClient2.setExecutor(clientThreads);
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java
index 1d10407b6bb..7dff8d953a7 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTPTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.client.http;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.junit.jupiter.api.Assertions.*;
-
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.TimeUnit;
@@ -39,10 +36,18 @@ import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue;
import org.hamcrest.Matchers;
-import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNotSame;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
{
@ParameterizedTest
@@ -178,10 +183,9 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
@ArgumentsSource(ScenarioProvider.class)
public void test_IdleConnection_IdleTimeout(Scenario scenario) throws Exception
{
- start(scenario, new EmptyServerHandler());
-
+ startServer(scenario, new EmptyServerHandler());
long idleTimeout = 1000;
- client.setIdleTimeout(idleTimeout);
+ startClient(scenario, null, httpClient -> httpClient.setIdleTimeout(idleTimeout));
try (HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort())))
{
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java
index 4ab77cb2d57..2c095ee2dab 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/client/http/HttpClientTransportOverFCGI.java
@@ -29,6 +29,7 @@ import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.fcgi.FCGI;
import org.eclipse.jetty.http.HttpFields;
+import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.ProcessorUtils;
import org.eclipse.jetty.util.Promise;
@@ -47,7 +48,13 @@ public class HttpClientTransportOverFCGI extends AbstractConnectorHttpClientTran
public HttpClientTransportOverFCGI(int selectors, String scriptRoot)
{
- super(selectors);
+ this(new ClientConnector(), scriptRoot);
+ getClientConnector().setSelectors(selectors);
+ }
+
+ public HttpClientTransportOverFCGI(ClientConnector connector, String scriptRoot)
+ {
+ super(connector);
this.scriptRoot = scriptRoot;
setConnectionPoolFactory(destination ->
{
diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
index 3b969874d63..a058215190a 100644
--- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
+++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2Client.java
@@ -18,13 +18,10 @@
package org.eclipse.jetty.http2.client;
-import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
-import java.nio.channels.SelectableChannel;
-import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
-import java.util.Arrays;
+import java.time.Duration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -38,32 +35,24 @@ import org.eclipse.jetty.http2.frames.Frame;
import org.eclipse.jetty.http2.frames.SettingsFrame;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnectionFactory;
-import org.eclipse.jetty.io.Connection;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.ManagedSelector;
-import org.eclipse.jetty.io.MappedByteBufferPool;
-import org.eclipse.jetty.io.SelectorManager;
-import org.eclipse.jetty.io.SocketChannelEndPoint;
+import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.ssl.SslContextFactory;
-import org.eclipse.jetty.util.thread.QueuedThreadPool;
-import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler;
/**
- * {@link HTTP2Client} provides an asynchronous, non-blocking implementation
+ *
HTTP2Client provides an asynchronous, non-blocking implementation
* to send HTTP/2 frames to a server.
* Typical usage:
*
* // Create and start HTTP2Client.
* HTTP2Client client = new HTTP2Client();
- * SslContextFactory sslContextFactory = new SslContextFactory();
- * client.addBean(sslContextFactory);
* client.start();
+ * SslContextFactory sslContextFactory = client.getClientConnector().getSslContextFactory();
*
* // Connect to host.
* String host = "webtide.com";
@@ -108,7 +97,7 @@ import org.eclipse.jetty.util.thread.Scheduler;
* // Use the Stream object to send request content, if any, using a DATA frame.
* ByteBuffer content = ...;
* DataFrame requestContent = new DataFrame(stream.getId(), content, true);
- * stream.data(requestContent, Callback.Adapter.INSTANCE);
+ * stream.data(requestContent, Callback.NOOP);
*
* // When done, stop the client.
* client.stop();
@@ -117,18 +106,9 @@ import org.eclipse.jetty.util.thread.Scheduler;
@ManagedObject
public class HTTP2Client extends ContainerLifeCycle
{
- private Executor executor;
- private Scheduler scheduler;
- private ByteBufferPool bufferPool;
- private ClientConnectionFactory connectionFactory;
- private SelectorManager selector;
- private int selectors = 1;
- private long idleTimeout = 30000;
- private long connectTimeout = 10000;
- private boolean connectBlocking;
- private SocketAddress bindAddress;
+ private final ClientConnector connector;
private int inputBufferSize = 8192;
- private List protocols = Arrays.asList("h2", "h2-17", "h2-16", "h2-15", "h2-14");
+ private List protocols = List.of("h2");
private int initialSessionRecvWindow = 16 * 1024 * 1024;
private int initialStreamRecvWindow = 8 * 1024 * 1024;
private int maxFrameLength = Frame.DEFAULT_MAX_LENGTH;
@@ -136,96 +116,50 @@ public class HTTP2Client extends ContainerLifeCycle
private int maxSettingsKeys = SettingsFrame.DEFAULT_MAX_KEYS;
private FlowControlStrategy.Factory flowControlStrategyFactory = () -> new BufferingFlowControlStrategy(0.5F);
- @Override
- protected void doStart() throws Exception
+ public HTTP2Client()
{
- if (executor == null)
- setExecutor(new QueuedThreadPool());
-
- if (scheduler == null)
- setScheduler(new ScheduledExecutorScheduler());
-
- if (bufferPool == null)
- setByteBufferPool(new MappedByteBufferPool());
-
- if (connectionFactory == null)
- {
- HTTP2ClientConnectionFactory h2 = new HTTP2ClientConnectionFactory();
- setClientConnectionFactory((endPoint, context) ->
- {
- ClientConnectionFactory factory = h2;
- SslContextFactory sslContextFactory = (SslContextFactory)context.get(SslClientConnectionFactory.SSL_CONTEXT_FACTORY_CONTEXT_KEY);
- if (sslContextFactory != null)
- {
- ALPNClientConnectionFactory alpn = new ALPNClientConnectionFactory(getExecutor(), h2, getProtocols());
- factory = newSslClientConnectionFactory(sslContextFactory, alpn);
- }
- return factory.newConnection(endPoint, context);
- });
- }
-
- if (selector == null)
- {
- selector = newSelectorManager();
- addBean(selector);
- }
- selector.setConnectTimeout(getConnectTimeout());
-
- super.doStart();
+ this(new ClientConnector());
}
- protected SelectorManager newSelectorManager()
+ public HTTP2Client(ClientConnector connector)
{
- return new ClientSelectorManager(getExecutor(), getScheduler(), getSelectors());
+ this.connector = connector;
+ addBean(connector);
}
- protected ClientConnectionFactory newSslClientConnectionFactory(SslContextFactory sslContextFactory, ClientConnectionFactory connectionFactory)
+ public ClientConnector getClientConnector()
{
- return new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), connectionFactory);
+ return connector;
}
public Executor getExecutor()
{
- return executor;
+ return connector.getExecutor();
}
public void setExecutor(Executor executor)
{
- this.updateBean(this.executor, executor);
- this.executor = executor;
+ connector.setExecutor(executor);
}
public Scheduler getScheduler()
{
- return scheduler;
+ return connector.getScheduler();
}
public void setScheduler(Scheduler scheduler)
{
- this.updateBean(this.scheduler, scheduler);
- this.scheduler = scheduler;
+ connector.setScheduler(scheduler);
}
public ByteBufferPool getByteBufferPool()
{
- return bufferPool;
+ return connector.getByteBufferPool();
}
public void setByteBufferPool(ByteBufferPool bufferPool)
{
- this.updateBean(this.bufferPool, bufferPool);
- this.bufferPool = bufferPool;
- }
-
- public ClientConnectionFactory getClientConnectionFactory()
- {
- return connectionFactory;
- }
-
- public void setClientConnectionFactory(ClientConnectionFactory connectionFactory)
- {
- this.updateBean(this.connectionFactory, connectionFactory);
- this.connectionFactory = connectionFactory;
+ connector.setByteBufferPool(bufferPool);
}
public FlowControlStrategy.Factory getFlowControlStrategyFactory()
@@ -241,58 +175,55 @@ public class HTTP2Client extends ContainerLifeCycle
@ManagedAttribute("The number of selectors")
public int getSelectors()
{
- return selectors;
+ return connector.getSelectors();
}
public void setSelectors(int selectors)
{
- this.selectors = selectors;
+ connector.setSelectors(selectors);
}
@ManagedAttribute("The idle timeout in milliseconds")
public long getIdleTimeout()
{
- return idleTimeout;
+ return connector.getIdleTimeout().toMillis();
}
public void setIdleTimeout(long idleTimeout)
{
- this.idleTimeout = idleTimeout;
+ connector.setIdleTimeout(Duration.ofMillis(idleTimeout));
}
@ManagedAttribute("The connect timeout in milliseconds")
public long getConnectTimeout()
{
- return connectTimeout;
+ return connector.getConnectTimeout().toMillis();
}
public void setConnectTimeout(long connectTimeout)
{
- this.connectTimeout = connectTimeout;
- SelectorManager selector = this.selector;
- if (selector != null)
- selector.setConnectTimeout(connectTimeout);
+ connector.setConnectTimeout(Duration.ofMillis(connectTimeout));
}
@ManagedAttribute("Whether the connect() operation is blocking")
public boolean isConnectBlocking()
{
- return connectBlocking;
+ return connector.isConnectBlocking();
}
public void setConnectBlocking(boolean connectBlocking)
{
- this.connectBlocking = connectBlocking;
+ connector.setConnectBlocking(connectBlocking);
}
public SocketAddress getBindAddress()
{
- return bindAddress;
+ return connector.getBindAddress();
}
public void setBindAddress(SocketAddress bindAddress)
{
- this.bindAddress = bindAddress;
+ connector.setBindAddress(bindAddress);
}
@ManagedAttribute("The size of the buffer used to read from the network")
@@ -374,6 +305,7 @@ public class HTTP2Client extends ContainerLifeCycle
public void connect(InetSocketAddress address, Session.Listener listener, Promise promise)
{
+ // Prior-knowledge clear-text HTTP/2 (h2c).
connect(null, address, listener, promise);
}
@@ -384,112 +316,49 @@ public class HTTP2Client extends ContainerLifeCycle
public void connect(SslContextFactory sslContextFactory, InetSocketAddress address, Session.Listener listener, Promise promise, Map context)
{
- try
- {
- SocketChannel channel = SocketChannel.open();
- SocketAddress bindAddress = getBindAddress();
- if (bindAddress != null)
- channel.bind(bindAddress);
- configure(channel);
- boolean connected = true;
- if (isConnectBlocking())
- {
- channel.socket().connect(address, (int)getConnectTimeout());
- channel.configureBlocking(false);
- }
- else
- {
- channel.configureBlocking(false);
- connected = channel.connect(address);
- }
- context = contextFrom(sslContextFactory, address, listener, promise, context);
- if (connected)
- selector.accept(channel, context);
- else
- selector.connect(channel, context);
- }
- catch (Throwable x)
- {
- promise.failed(x);
- }
+ ClientConnectionFactory factory = newClientConnectionFactory(sslContextFactory);
+ connect(address, factory, listener, promise, context);
+ }
+
+ public void connect(SocketAddress address, ClientConnectionFactory factory, Session.Listener listener, Promise promise, Map context)
+ {
+ context = contextFrom(factory, listener, promise, context);
+ context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, new Promise.Wrapper<>(promise));
+ connector.connect(address, context);
}
public void accept(SslContextFactory sslContextFactory, SocketChannel channel, Session.Listener listener, Promise promise)
{
- try
- {
- if (!channel.isConnected())
- throw new IllegalStateException("SocketChannel must be connected");
- channel.configureBlocking(false);
- Map context = contextFrom(sslContextFactory, (InetSocketAddress)channel.getRemoteAddress(), listener, promise, null);
- selector.accept(channel, context);
- }
- catch (Throwable x)
- {
- promise.failed(x);
- }
+ ClientConnectionFactory factory = newClientConnectionFactory(sslContextFactory);
+ accept(channel, factory, listener, promise);
}
- private Map contextFrom(SslContextFactory sslContextFactory, InetSocketAddress address, Session.Listener listener, Promise promise, Map context)
+ public void accept(SocketChannel channel, ClientConnectionFactory factory, Session.Listener listener, Promise promise)
+ {
+ Map context = contextFrom(factory, listener, promise, null);
+ context.put(ClientConnector.CONNECTION_PROMISE_CONTEXT_KEY, new Promise.Wrapper<>(promise));
+ connector.accept(channel, context);
+ }
+
+ private Map contextFrom(ClientConnectionFactory factory, Session.Listener listener, Promise promise, Map context)
{
if (context == null)
context = new HashMap<>();
context.put(HTTP2ClientConnectionFactory.CLIENT_CONTEXT_KEY, this);
context.put(HTTP2ClientConnectionFactory.SESSION_LISTENER_CONTEXT_KEY, listener);
context.put(HTTP2ClientConnectionFactory.SESSION_PROMISE_CONTEXT_KEY, promise);
- if (sslContextFactory != null)
- context.put(SslClientConnectionFactory.SSL_CONTEXT_FACTORY_CONTEXT_KEY, sslContextFactory);
- context.put(SslClientConnectionFactory.SSL_PEER_HOST_CONTEXT_KEY, address.getHostString());
- context.put(SslClientConnectionFactory.SSL_PEER_PORT_CONTEXT_KEY, address.getPort());
- context.putIfAbsent(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY, this);
+ context.put(ClientConnector.CLIENT_CONNECTION_FACTORY_CONTEXT_KEY, factory);
return context;
}
- protected void configure(SocketChannel channel) throws IOException
+ private ClientConnectionFactory newClientConnectionFactory(SslContextFactory sslContextFactory)
{
- channel.socket().setTcpNoDelay(true);
- }
-
- private class ClientSelectorManager extends SelectorManager
- {
- private ClientSelectorManager(Executor executor, Scheduler scheduler, int selectors)
+ ClientConnectionFactory factory = new HTTP2ClientConnectionFactory();
+ if (sslContextFactory != null)
{
- super(executor, scheduler, selectors);
- }
-
- @Override
- protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey selectionKey) throws IOException
- {
- SocketChannelEndPoint endp = new SocketChannelEndPoint(channel, selector, selectionKey, getScheduler());
- endp.setIdleTimeout(getIdleTimeout());
- return endp;
- }
-
- @Override
- public Connection newConnection(SelectableChannel channel, EndPoint endpoint, Object attachment) throws IOException
- {
- @SuppressWarnings("unchecked")
- Map context = (Map)attachment;
- context.put(HTTP2ClientConnectionFactory.BYTE_BUFFER_POOL_CONTEXT_KEY, getByteBufferPool());
- context.put(HTTP2ClientConnectionFactory.EXECUTOR_CONTEXT_KEY, getExecutor());
- context.put(HTTP2ClientConnectionFactory.SCHEDULER_CONTEXT_KEY, getScheduler());
- return getClientConnectionFactory().newConnection(endpoint, context);
- }
-
- @Override
- protected void connectionFailed(SelectableChannel channel, Throwable failure, Object attachment)
- {
- @SuppressWarnings("unchecked")
- Map context = (Map)attachment;
- if (LOG.isDebugEnabled())
- {
- Object host = context.get(SslClientConnectionFactory.SSL_PEER_HOST_CONTEXT_KEY);
- Object port = context.get(SslClientConnectionFactory.SSL_PEER_PORT_CONTEXT_KEY);
- LOG.debug("Could not connect to {}:{}", host, port);
- }
- @SuppressWarnings("unchecked")
- Promise promise = (Promise)context.get(HTTP2ClientConnectionFactory.SESSION_PROMISE_CONTEXT_KEY);
- promise.failed(failure);
+ ALPNClientConnectionFactory alpn = new ALPNClientConnectionFactory(getExecutor(), factory, getProtocols());
+ factory = new SslClientConnectionFactory(sslContextFactory, getByteBufferPool(), getExecutor(), alpn);
}
+ return factory;
}
}
diff --git a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java
index cfe45c13d49..5236e9e143e 100644
--- a/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java
+++ b/jetty-http2/http2-client/src/main/java/org/eclipse/jetty/http2/client/HTTP2ClientConnectionFactory.java
@@ -42,12 +42,9 @@ import org.eclipse.jetty.util.thread.Scheduler;
public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
{
- public static final String CLIENT_CONTEXT_KEY = "http2.client";
- public static final String BYTE_BUFFER_POOL_CONTEXT_KEY = "http2.client.byteBufferPool";
- public static final String EXECUTOR_CONTEXT_KEY = "http2.client.executor";
- public static final String SCHEDULER_CONTEXT_KEY = "http2.client.scheduler";
- public static final String SESSION_LISTENER_CONTEXT_KEY = "http2.client.sessionListener";
- public static final String SESSION_PROMISE_CONTEXT_KEY = "http2.client.sessionPromise";
+ public static final String CLIENT_CONTEXT_KEY = "org.eclipse.jetty.client.http2";
+ public static final String SESSION_LISTENER_CONTEXT_KEY = "org.eclipse.jetty.client.http2.sessionListener";
+ public static final String SESSION_PROMISE_CONTEXT_KEY = "org.eclipse.jetty.client.http2.sessionPromise";
private final Connection.Listener connectionListener = new ConnectionListener();
@@ -55,9 +52,9 @@ public class HTTP2ClientConnectionFactory implements ClientConnectionFactory
public Connection newConnection(EndPoint endPoint, Map context)
{
HTTP2Client client = (HTTP2Client)context.get(CLIENT_CONTEXT_KEY);
- ByteBufferPool byteBufferPool = (ByteBufferPool)context.get(BYTE_BUFFER_POOL_CONTEXT_KEY);
- Executor executor = (Executor)context.get(EXECUTOR_CONTEXT_KEY);
- Scheduler scheduler = (Scheduler)context.get(SCHEDULER_CONTEXT_KEY);
+ ByteBufferPool byteBufferPool = client.getByteBufferPool();
+ Executor executor = client.getExecutor();
+ Scheduler scheduler = client.getScheduler();
Session.Listener listener = (Session.Listener)context.get(SESSION_LISTENER_CONTEXT_KEY);
@SuppressWarnings("unchecked")
Promise promise = (Promise)context.get(SESSION_PROMISE_CONTEXT_KEY);
diff --git a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/Client.java b/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/Client.java
deleted file mode 100644
index 956a7b39c8f..00000000000
--- a/jetty-http2/http2-client/src/test/java/org/eclipse/jetty/http2/client/Client.java
+++ /dev/null
@@ -1,94 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
-// ------------------------------------------------------------------------
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.opensource.org/licenses/apache2.0.php
-//
-// You may elect to redistribute this code under either of these licenses.
-// ========================================================================
-//
-
-package org.eclipse.jetty.http2.client;
-
-import java.net.InetSocketAddress;
-import java.util.concurrent.Phaser;
-import java.util.concurrent.TimeUnit;
-
-import org.eclipse.jetty.http.HttpFields;
-import org.eclipse.jetty.http.HttpURI;
-import org.eclipse.jetty.http.HttpVersion;
-import org.eclipse.jetty.http.MetaData;
-import org.eclipse.jetty.http2.api.Session;
-import org.eclipse.jetty.http2.api.Stream;
-import org.eclipse.jetty.http2.api.server.ServerSessionListener;
-import org.eclipse.jetty.http2.frames.DataFrame;
-import org.eclipse.jetty.http2.frames.HeadersFrame;
-import org.eclipse.jetty.http2.frames.PushPromiseFrame;
-import org.eclipse.jetty.util.Callback;
-import org.eclipse.jetty.util.FuturePromise;
-import org.eclipse.jetty.util.Jetty;
-import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
-
-public class Client
-{
- public static void main(String[] args) throws Exception
- {
- HTTP2Client client = new HTTP2Client();
- SslContextFactory sslContextFactory = new SslContextFactory();
- client.addBean(sslContextFactory);
- client.start();
-
- String host = "webtide.com";
- int port = 443;
-
- FuturePromise sessionPromise = new FuturePromise<>();
- client.connect(sslContextFactory, new InetSocketAddress(host, port), new ServerSessionListener.Adapter(), sessionPromise);
- Session session = sessionPromise.get(5, TimeUnit.SECONDS);
-
- HttpFields requestFields = new HttpFields();
- requestFields.put("User-Agent", client.getClass().getName() + "/" + Jetty.VERSION);
- MetaData.Request metaData = new MetaData.Request("GET", new HttpURI("https://" + host + ":" + port + "/"), HttpVersion.HTTP_2, requestFields);
- HeadersFrame headersFrame = new HeadersFrame(metaData, null, true);
- final Phaser phaser = new Phaser(2);
- session.newStream(headersFrame, new Promise.Adapter<>(), new Stream.Listener.Adapter()
- {
- @Override
- public void onHeaders(Stream stream, HeadersFrame frame)
- {
- System.err.println(frame);
- if (frame.isEndStream())
- phaser.arrive();
- }
-
- @Override
- public void onData(Stream stream, DataFrame frame, Callback callback)
- {
- System.err.println(frame);
- callback.succeeded();
- if (frame.isEndStream())
- phaser.arrive();
- }
-
- @Override
- public Stream.Listener onPush(Stream stream, PushPromiseFrame frame)
- {
- System.err.println(frame);
- phaser.register();
- return this;
- }
- });
-
- phaser.awaitAdvanceInterruptibly(phaser.arrive(), 5, TimeUnit.SECONDS);
-
- client.stop();
- }
-}
diff --git a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
index 7584d1966e1..cacb023db7d 100644
--- a/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
+++ b/jetty-http2/http2-http-client-transport/src/main/java/org/eclipse/jetty/http2/client/http/HttpClientTransportOverHTTP2.java
@@ -45,7 +45,6 @@ import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
@ManagedObject("The HTTP/2 client transport")
public class HttpClientTransportOverHTTP2 extends AbstractHttpClientTransport
@@ -101,13 +100,7 @@ public class HttpClientTransportOverHTTP2 extends AbstractHttpClientTransport
}
addBean(client);
super.doStart();
-
- this.connectionFactory = new HTTP2ClientConnectionFactory();
- client.setClientConnectionFactory((endPoint, context) ->
- {
- HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
- return destination.getClientConnectionFactory().newConnection(endPoint, context);
- });
+ connectionFactory = new HTTP2ClientConnectionFactory();
}
@Override
@@ -134,16 +127,12 @@ public class HttpClientTransportOverHTTP2 extends AbstractHttpClientTransport
SessionListenerPromise listenerPromise = new SessionListenerPromise(context);
HttpDestinationOverHTTP2 destination = (HttpDestinationOverHTTP2)context.get(HTTP_DESTINATION_CONTEXT_KEY);
- SslContextFactory sslContextFactory = null;
- if (HttpScheme.HTTPS.is(destination.getScheme()))
- sslContextFactory = httpClient.getSslContextFactory();
-
- connect(sslContextFactory, address, listenerPromise, listenerPromise, context);
+ connect(address, destination.getClientConnectionFactory(), listenerPromise, listenerPromise, context);
}
- protected void connect(SslContextFactory sslContextFactory, InetSocketAddress address, Session.Listener listener, Promise promise, Map context)
+ protected void connect(InetSocketAddress address, ClientConnectionFactory factory, Session.Listener listener, Promise promise, Map context)
{
- getHTTP2Client().connect(sslContextFactory, address, listener, promise, context);
+ getHTTP2Client().connect(address, factory, listener, promise, context);
}
@Override
diff --git a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/MaxConcurrentStreamsTest.java b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/MaxConcurrentStreamsTest.java
index cee3802220d..21225c96a95 100644
--- a/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/MaxConcurrentStreamsTest.java
+++ b/jetty-http2/http2-http-client-transport/src/test/java/org/eclipse/jetty/http2/client/http/MaxConcurrentStreamsTest.java
@@ -51,11 +51,11 @@ import org.eclipse.jetty.http2.frames.PingFrame;
import org.eclipse.jetty.http2.frames.ResetFrame;
import org.eclipse.jetty.http2.frames.SettingsFrame;
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
+import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.Promise;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.Test;
@@ -171,9 +171,9 @@ public class MaxConcurrentStreamsTest extends AbstractTest
client = new HttpClient(new HttpClientTransportOverHTTP2(new HTTP2Client())
{
@Override
- protected void connect(SslContextFactory sslContextFactory, InetSocketAddress address, Session.Listener listener, Promise promise, Map context)
+ protected void connect(InetSocketAddress address, ClientConnectionFactory factory, Session.Listener listener, Promise promise, Map context)
{
- super.connect(sslContextFactory, address, new Wrapper(listener)
+ super.connect(address, factory, new Wrapper(listener)
{
@Override
public void onSettings(Session session, SettingsFrame frame)
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java
index 9902ce0d4e0..d935b3d1eb2 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java
@@ -28,10 +28,9 @@ import org.eclipse.jetty.util.component.ContainerLifeCycle;
*/
public interface ClientConnectionFactory
{
- public static final String CONNECTOR_CONTEXT_KEY = "client.connector";
+ public static final String CLIENT_CONTEXT_KEY = "org.eclipse.jetty.client";
/**
- *
* @param endPoint the {@link org.eclipse.jetty.io.EndPoint} to link the newly created connection to
* @param context the context data to create the connection
* @return a new {@link Connection}
@@ -41,8 +40,9 @@ public interface ClientConnectionFactory
public default Connection customize(Connection connection, Map context)
{
- ContainerLifeCycle connector = (ContainerLifeCycle)context.get(CONNECTOR_CONTEXT_KEY);
- connector.getBeans(Connection.Listener.class).forEach(connection::addListener);
+ ContainerLifeCycle client = (ContainerLifeCycle)context.get(CLIENT_CONTEXT_KEY);
+ if (client != null)
+ client.getBeans(Connection.Listener.class).forEach(connection::addListener);
return connection;
}
}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java
new file mode 100644
index 00000000000..5bb069124fb
--- /dev/null
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnector.java
@@ -0,0 +1,333 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.io;
+
+import java.io.IOException;
+import java.net.SocketAddress;
+import java.net.SocketException;
+import java.nio.channels.SelectableChannel;
+import java.nio.channels.SelectionKey;
+import java.nio.channels.SocketChannel;
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.component.ContainerLifeCycle;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
+import org.eclipse.jetty.util.thread.Scheduler;
+
+public class ClientConnector extends ContainerLifeCycle
+{
+ public static final String CLIENT_CONNECTOR_CONTEXT_KEY = "org.eclipse.jetty.client.connector";
+ public static final String SOCKET_ADDRESS_CONTEXT_KEY = CLIENT_CONNECTOR_CONTEXT_KEY + ".socketAddress";
+ public static final String CLIENT_CONNECTION_FACTORY_CONTEXT_KEY = CLIENT_CONNECTOR_CONTEXT_KEY + ".clientConnectionFactory";
+ public static final String CONNECTION_PROMISE_CONTEXT_KEY = CLIENT_CONNECTOR_CONTEXT_KEY + ".connectionPromise";
+ private static final Logger LOG = Log.getLogger(ClientConnector.class);
+
+ private Executor executor;
+ private Scheduler scheduler;
+ private ByteBufferPool byteBufferPool;
+ private SslContextFactory sslContextFactory;
+ private SelectorManager selectorManager;
+ private int selectors = 1;
+ private boolean connectBlocking;
+ private Duration connectTimeout = Duration.ofSeconds(5);
+ private Duration idleTimeout = Duration.ofSeconds(30);
+ private SocketAddress bindAddress;
+
+ public Executor getExecutor()
+ {
+ return executor;
+ }
+
+ public void setExecutor(Executor executor)
+ {
+ if (isStarted())
+ throw new IllegalStateException();
+ updateBean(this.executor, executor);
+ this.executor = executor;
+ }
+
+ public Scheduler getScheduler()
+ {
+ return scheduler;
+ }
+
+ public void setScheduler(Scheduler scheduler)
+ {
+ if (isStarted())
+ throw new IllegalStateException();
+ updateBean(this.scheduler, scheduler);
+ this.scheduler = scheduler;
+ }
+
+ public ByteBufferPool getByteBufferPool()
+ {
+ return byteBufferPool;
+ }
+
+ public void setByteBufferPool(ByteBufferPool byteBufferPool)
+ {
+ if (isStarted())
+ throw new IllegalStateException();
+ updateBean(this.byteBufferPool, byteBufferPool);
+ this.byteBufferPool = byteBufferPool;
+ }
+
+ public SslContextFactory getSslContextFactory()
+ {
+ return sslContextFactory;
+ }
+
+ public void setSslContextFactory(SslContextFactory sslContextFactory)
+ {
+ if (isStarted())
+ throw new IllegalStateException();
+ updateBean(this.sslContextFactory, sslContextFactory);
+ this.sslContextFactory = sslContextFactory;
+ }
+
+ public int getSelectors()
+ {
+ return selectors;
+ }
+
+ public void setSelectors(int selectors)
+ {
+ if (isStarted())
+ throw new IllegalStateException();
+ this.selectors = selectors;
+ }
+
+ public boolean isConnectBlocking()
+ {
+ return connectBlocking;
+ }
+
+ public void setConnectBlocking(boolean connectBlocking)
+ {
+ this.connectBlocking = connectBlocking;
+ }
+
+ public Duration getConnectTimeout()
+ {
+ return connectTimeout;
+ }
+
+ public void setConnectTimeout(Duration connectTimeout)
+ {
+ this.connectTimeout = connectTimeout;
+ if (selectorManager != null)
+ selectorManager.setConnectTimeout(connectTimeout.toMillis());
+ }
+
+ public Duration getIdleTimeout()
+ {
+ return idleTimeout;
+ }
+
+ public void setIdleTimeout(Duration idleTimeout)
+ {
+ this.idleTimeout = idleTimeout;
+ }
+
+ public SocketAddress getBindAddress()
+ {
+ return bindAddress;
+ }
+
+ public void setBindAddress(SocketAddress bindAddress)
+ {
+ this.bindAddress = bindAddress;
+ }
+
+ @Override
+ protected void doStart() throws Exception
+ {
+ if (executor == null)
+ setExecutor(new QueuedThreadPool());
+ if (scheduler == null)
+ setScheduler(new ScheduledExecutorScheduler());
+ if (byteBufferPool == null)
+ setByteBufferPool(new MappedByteBufferPool());
+ if (sslContextFactory == null)
+ setSslContextFactory(newSslContextFactory());
+ selectorManager = newSelectorManager();
+ selectorManager.setConnectTimeout(getConnectTimeout().toMillis());
+ addBean(selectorManager);
+ super.doStart();
+ }
+
+ @Override
+ protected void doStop() throws Exception
+ {
+ super.doStop();
+ removeBean(selectorManager);
+ }
+
+ protected SslContextFactory newSslContextFactory()
+ {
+ SslContextFactory sslContextFactory = new SslContextFactory(false);
+ sslContextFactory.setEndpointIdentificationAlgorithm("HTTPS");
+ return sslContextFactory;
+ }
+
+ protected SelectorManager newSelectorManager()
+ {
+ return new ClientSelectorManager(getExecutor(), getScheduler(), getSelectors());
+ }
+
+ public void connect(SocketAddress address, Map context)
+ {
+ SocketChannel channel = null;
+ try
+ {
+ if (context == null)
+ context = new HashMap<>();
+ context.put(ClientConnector.CLIENT_CONNECTOR_CONTEXT_KEY, this);
+ context.putIfAbsent(SOCKET_ADDRESS_CONTEXT_KEY, address);
+
+ channel = SocketChannel.open();
+ SocketAddress bindAddress = getBindAddress();
+ if (bindAddress != null)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Binding to {} to connect to {}", bindAddress, address);
+ channel.bind(bindAddress);
+ }
+ configure(channel);
+
+ boolean connected = true;
+ boolean blocking = isConnectBlocking();
+ if (LOG.isDebugEnabled())
+ LOG.debug("Connecting {} to {}", blocking ? "blocking" : "non-blocking", address);
+ if (blocking)
+ {
+ channel.socket().connect(address, (int)getConnectTimeout().toMillis());
+ channel.configureBlocking(false);
+ }
+ else
+ {
+ channel.configureBlocking(false);
+ connected = channel.connect(address);
+ }
+
+ if (connected)
+ selectorManager.accept(channel, context);
+ else
+ selectorManager.connect(channel, context);
+ }
+ // Must catch all exceptions, since some like
+ // UnresolvedAddressException are not IOExceptions.
+ catch (Throwable x)
+ {
+ // If IPv6 is not deployed, a generic SocketException "Network is unreachable"
+ // exception is being thrown, so we attempt to provide a better error message.
+ if (x.getClass() == SocketException.class)
+ x = new SocketException("Could not connect to " + address).initCause(x);
+
+ try
+ {
+ if (channel != null)
+ channel.close();
+ }
+ catch (IOException xx)
+ {
+ LOG.ignore(xx);
+ }
+ finally
+ {
+ connectFailed(x, context);
+ }
+ }
+ }
+
+ public void accept(SocketChannel channel, Map context)
+ {
+ try
+ {
+ context.put(ClientConnector.CLIENT_CONNECTOR_CONTEXT_KEY, this);
+
+ if (!channel.isConnected())
+ throw new IllegalStateException("SocketChannel must be connected");
+ configure(channel);
+ channel.configureBlocking(false);
+ selectorManager.accept(channel, context);
+ }
+ catch (Throwable failure)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not accept {}", channel);
+ Promise> promise = (Promise>)context.get(CONNECTION_PROMISE_CONTEXT_KEY);
+ promise.failed(failure);
+ }
+ }
+
+ protected void configure(SocketChannel channel) throws IOException
+ {
+ channel.socket().setTcpNoDelay(true);
+ }
+
+ private void connectFailed(Throwable failure, Map context)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug("Could not connect to {}", context.get(SOCKET_ADDRESS_CONTEXT_KEY));
+ Promise> promise = (Promise>)context.get(CONNECTION_PROMISE_CONTEXT_KEY);
+ promise.failed(failure);
+ }
+
+ private class ClientSelectorManager extends SelectorManager
+ {
+ private ClientSelectorManager(Executor executor, Scheduler scheduler, int selectors)
+ {
+ super(executor, scheduler, selectors);
+ }
+
+ @Override
+ protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey selectionKey)
+ {
+ SocketChannelEndPoint endPoint = new SocketChannelEndPoint(channel, selector, selectionKey, getScheduler());
+ endPoint.setIdleTimeout(getIdleTimeout().toMillis());
+ return endPoint;
+ }
+
+ @Override
+ public Connection newConnection(SelectableChannel channel, EndPoint endPoint, Object attachment) throws IOException
+ {
+ @SuppressWarnings("unchecked")
+ Map context = (Map)attachment;
+ ClientConnectionFactory factory = (ClientConnectionFactory)context.get(CLIENT_CONNECTION_FACTORY_CONTEXT_KEY);
+ return factory.newConnection(endPoint, context);
+ }
+
+ @Override
+ protected void connectionFailed(SelectableChannel channel, Throwable failure, Object attachment)
+ {
+ @SuppressWarnings("unchecked")
+ Map context = (Map)attachment;
+ connectFailed(failure, context);
+ }
+ }
+}
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java
index 932644d6e62..867f88957aa 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/NegotiatingClientConnection.java
@@ -37,9 +37,9 @@ public abstract class NegotiatingClientConnection extends AbstractConnection
private final Map context;
private volatile boolean completed;
- protected NegotiatingClientConnection(EndPoint endp, Executor executor, SSLEngine sslEngine, ClientConnectionFactory connectionFactory, Map context)
+ protected NegotiatingClientConnection(EndPoint endPoint, Executor executor, SSLEngine sslEngine, ClientConnectionFactory connectionFactory, Map context)
{
- super(endp, executor);
+ super(endPoint, executor);
this.engine = sslEngine;
this.connectionFactory = connectionFactory;
this.context = context;
@@ -67,7 +67,7 @@ public abstract class NegotiatingClientConnection extends AbstractConnection
else
fillInterested();
}
- catch (IOException x)
+ catch (Throwable x)
{
close();
throw new RuntimeIOException(x);
diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java
index a86fcb9f3c7..874f2a815aa 100644
--- a/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java
@@ -19,6 +19,7 @@
package org.eclipse.jetty.io.ssl;
import java.io.IOException;
+import java.net.InetSocketAddress;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Executor;
@@ -27,6 +28,7 @@ import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.ClientConnector;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
@@ -34,10 +36,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
public class SslClientConnectionFactory implements ClientConnectionFactory
{
- public static final String SSL_CONTEXT_FACTORY_CONTEXT_KEY = "ssl.context.factory";
- public static final String SSL_PEER_HOST_CONTEXT_KEY = "ssl.peer.host";
- public static final String SSL_PEER_PORT_CONTEXT_KEY = "ssl.peer.port";
- public static final String SSL_ENGINE_CONTEXT_KEY = "ssl.engine";
+ public static final String SSL_ENGINE_CONTEXT_KEY = "org.eclipse.jetty.client.ssl.engine";
private final SslContextFactory sslContextFactory;
private final ByteBufferPool byteBufferPool;
@@ -88,9 +87,8 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
@Override
public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map context) throws IOException
{
- String host = (String)context.get(SSL_PEER_HOST_CONTEXT_KEY);
- int port = (Integer)context.get(SSL_PEER_PORT_CONTEXT_KEY);
- SSLEngine engine = sslContextFactory.newSSLEngine(host, port);
+ InetSocketAddress address = (InetSocketAddress)context.get(ClientConnector.SOCKET_ADDRESS_CONTEXT_KEY);
+ SSLEngine engine = sslContextFactory.newSSLEngine(address);
engine.setUseClientMode(true);
context.put(SSL_ENGINE_CONTEXT_KEY, engine);
@@ -119,8 +117,9 @@ public class SslClientConnectionFactory implements ClientConnectionFactory
sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
sslConnection.setRenegotiationLimit(sslContextFactory.getRenegotiationLimit());
sslConnection.setAllowMissingCloseMessage(isAllowMissingCloseMessage());
- ContainerLifeCycle connector = (ContainerLifeCycle)context.get(ClientConnectionFactory.CONNECTOR_CONTEXT_KEY);
- connector.getBeans(SslHandshakeListener.class).forEach(sslConnection::addHandshakeListener);
+ ContainerLifeCycle client = (ContainerLifeCycle)context.get(ClientConnectionFactory.CLIENT_CONTEXT_KEY);
+ if (client != null)
+ client.getBeans(SslHandshakeListener.class).forEach(sslConnection::addHandshakeListener);
}
return ClientConnectionFactory.super.customize(connection, context);
}
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
index a42b626d11a..fe62af7044e 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyServletTest.java
@@ -41,6 +41,7 @@ import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
import java.util.stream.Stream;
import java.util.zip.GZIPOutputStream;
@@ -171,16 +172,23 @@ public class ProxyServletTest
private void startClient() throws Exception
{
- client = prepareClient();
+ startClient(null);
}
- private HttpClient prepareClient() throws Exception
+ private void startClient(Consumer consumer) throws Exception
+ {
+ client = prepareClient(consumer);
+ }
+
+ private HttpClient prepareClient(Consumer consumer) throws Exception
{
QueuedThreadPool clientPool = new QueuedThreadPool();
clientPool.setName("client");
HttpClient result = new HttpClient();
result.setExecutor(clientPool);
result.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyConnector.getLocalPort()));
+ if (consumer != null)
+ consumer.accept(result);
result.start();
return result;
}
@@ -987,7 +995,7 @@ public class ProxyServletTest
assertEquals(name, cookies.get(0).getName());
assertEquals(value1, cookies.get(0).getValue());
- HttpClient client2 = prepareClient();
+ HttpClient client2 = prepareClient(null);
try
{
String value2 = "2";
@@ -1373,10 +1381,8 @@ public class ProxyServletTest
}
});
startProxy(proxyServletClass);
- startClient();
-
long idleTimeout = 1000;
- client.setIdleTimeout(idleTimeout);
+ startClient(httpClient -> httpClient.setIdleTimeout(idleTimeout));
byte[] content = new byte[1024];
new Random().nextBytes(content);
diff --git a/jetty-unixsocket/src/main/java/org/eclipse/jetty/unixsocket/client/HttpClientTransportOverUnixSockets.java b/jetty-unixsocket/src/main/java/org/eclipse/jetty/unixsocket/client/HttpClientTransportOverUnixSockets.java
index 9e30713099a..aa2c77463a8 100644
--- a/jetty-unixsocket/src/main/java/org/eclipse/jetty/unixsocket/client/HttpClientTransportOverUnixSockets.java
+++ b/jetty-unixsocket/src/main/java/org/eclipse/jetty/unixsocket/client/HttpClientTransportOverUnixSockets.java
@@ -21,85 +21,110 @@ package org.eclipse.jetty.unixsocket.client;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
-import java.net.SocketException;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
-import java.nio.file.Files;
-import java.nio.file.Paths;
+import java.nio.channels.SocketChannel;
import java.util.Map;
-import org.eclipse.jetty.client.HttpClient;
-import org.eclipse.jetty.client.HttpDestination;
-import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
-import org.eclipse.jetty.io.EndPoint;
-import org.eclipse.jetty.io.ManagedSelector;
-import org.eclipse.jetty.io.SelectorManager;
-import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
-import org.eclipse.jetty.unixsocket.UnixSocketEndPoint;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
-
import jnr.enxio.channels.NativeSelectorProvider;
import jnr.unixsocket.UnixSocketAddress;
import jnr.unixsocket.UnixSocketChannel;
+import org.eclipse.jetty.client.AbstractHttpClientTransport;
+import org.eclipse.jetty.client.DuplexConnectionPool;
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
+import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ManagedSelector;
+import org.eclipse.jetty.io.SelectorManager;
+import org.eclipse.jetty.unixsocket.UnixSocketEndPoint;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
-public class HttpClientTransportOverUnixSockets
- extends HttpClientTransportOverHTTP
+// TODO: this class needs a thorough review.
+public class HttpClientTransportOverUnixSockets extends AbstractHttpClientTransport
{
- private static final Logger LOG = Log.getLogger( HttpClientTransportOverUnixSockets.class );
-
+ private static final Logger LOG = Log.getLogger(HttpClientTransportOverUnixSockets.class);
+
private String _unixSocket;
private SelectorManager selectorManager;
private UnixSocketChannel channel;
- public HttpClientTransportOverUnixSockets( String unixSocket )
+ public HttpClientTransportOverUnixSockets(String unixSocket)
{
- if ( unixSocket == null )
- {
- throw new IllegalArgumentException( "Unix socket file cannot be null" );
- }
+ if (unixSocket == null)
+ throw new IllegalArgumentException("Unix socket file cannot be null");
this._unixSocket = unixSocket;
+ setConnectionPoolFactory(destination ->
+ {
+ HttpClient httpClient = getHttpClient();
+ int maxConnections = httpClient.getMaxConnectionsPerDestination();
+ return new DuplexConnectionPool(destination, maxConnections, destination);
+ });
}
@Override
- protected SelectorManager newSelectorManager(HttpClient client)
+ protected void doStart() throws Exception
{
- return selectorManager = new UnixSocketSelectorManager(client,getSelectors());
+ HttpClient httpClient = getHttpClient();
+ selectorManager = new UnixSocketSelectorManager(httpClient, 1);
+ selectorManager.setConnectTimeout(httpClient.getConnectTimeout());
+ addBean(selectorManager);
+ super.doStart();
}
@Override
- public void connect( InetSocketAddress address, Map context )
+ protected void doStop() throws Exception
{
+ super.doStop();
+ try
+ {
+ if (channel != null)
+ channel.close();
+ }
+ catch (IOException xx)
+ {
+ LOG.ignore(xx);
+ }
+ }
+ @Override
+ public HttpDestination newHttpDestination(Origin origin)
+ {
+ return new HttpDestinationOverHTTP(getHttpClient(), origin);
+ }
+
+ @Override
+ public void connect(InetSocketAddress address, Map context)
+ {
try
{
InetAddress inet = address.getAddress();
if (!inet.isLoopbackAddress() && !inet.isLinkLocalAddress() && !inet.isSiteLocalAddress())
- throw new IOException("UnixSocket cannot connect to "+address.getHostString());
-
+ throw new IOException("UnixSocket cannot connect to " + address.getHostString());
+
// Open a unix socket
- UnixSocketAddress unixAddress = new UnixSocketAddress( this._unixSocket );
- channel = UnixSocketChannel.open( unixAddress );
-
+ UnixSocketAddress unixAddress = new UnixSocketAddress(this._unixSocket);
+ channel = UnixSocketChannel.open(unixAddress);
+
HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
HttpClient client = destination.getHttpClient();
configure(client, channel);
channel.configureBlocking(false);
- selectorManager.accept(channel, context);
+ selectorManager.accept(channel, context);
}
// Must catch all exceptions, since some like
// UnresolvedAddressException are not IOExceptions.
catch (Throwable x)
{
- // If IPv6 is not deployed, a generic SocketException "Network is unreachable"
- // exception is being thrown, so we attempt to provide a better error message.
- if (x.getClass() == SocketException.class)
- x = new SocketException("Could not connect to " + address).initCause(x);
-
try
{
if (channel != null)
@@ -116,11 +141,33 @@ public class HttpClientTransportOverUnixSockets
}
}
- public class UnixSocketSelectorManager extends ClientSelectorManager
+ @Override
+ public Connection newConnection(EndPoint endPoint, Map context)
+ {
+ HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
+ @SuppressWarnings("unchecked")
+ Promise promise = (Promise)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
+ HttpConnectionOverHTTP connection = newHttpConnection(endPoint, destination, promise);
+ if (LOG.isDebugEnabled())
+ LOG.debug("Created {}", connection);
+ return customize(connection, context);
+ }
+
+ protected HttpConnectionOverHTTP newHttpConnection(EndPoint endPoint, HttpDestination destination, Promise promise)
+ {
+ return new HttpConnectionOverHTTP(endPoint, destination, promise);
+ }
+
+ protected void configure(HttpClient client, SocketChannel channel) throws IOException
+ {
+ channel.socket().setTcpNoDelay(client.isTCPNoDelay());
+ }
+
+ public class UnixSocketSelectorManager extends SelectorManager
{
protected UnixSocketSelectorManager(HttpClient client, int selectors)
{
- super(client,selectors);
+ super(client.getExecutor(), client.getScheduler(), selectors);
}
@Override
@@ -132,25 +179,26 @@ public class HttpClientTransportOverUnixSockets
@Override
protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey key)
{
- UnixSocketEndPoint endp = new UnixSocketEndPoint((UnixSocketChannel)channel, selector, key, getScheduler());
- endp.setIdleTimeout(getHttpClient().getIdleTimeout());
- return endp;
+ UnixSocketEndPoint endPoint = new UnixSocketEndPoint((UnixSocketChannel)channel, selector, key, getScheduler());
+ endPoint.setIdleTimeout(getHttpClient().getIdleTimeout());
+ return endPoint;
}
- }
- @Override
- protected void doStop()
- throws Exception
- {
- super.doStop();
- try
+ @Override
+ public Connection newConnection(SelectableChannel channel, EndPoint endPoint, Object attachment) throws IOException
{
- if (channel != null)
- channel.close();
+ @SuppressWarnings("unchecked")
+ Map context = (Map)attachment;
+ HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
+ return destination.getClientConnectionFactory().newConnection(endPoint, context);
}
- catch (IOException xx)
+
+ @Override
+ protected void connectionFailed(SelectableChannel channel, Throwable x, Object attachment)
{
- LOG.ignore(xx);
+ @SuppressWarnings("unchecked")
+ Map context = (Map)attachment;
+ connectFailed(context, x);
}
}
}
diff --git a/jetty-unixsocket/src/test/java/org/eclipse/jetty/unixsocket/UnixSocketTest.java b/jetty-unixsocket/src/test/java/org/eclipse/jetty/unixsocket/UnixSocketTest.java
index 682d55c78d1..3481fcb0daf 100644
--- a/jetty-unixsocket/src/test/java/org/eclipse/jetty/unixsocket/UnixSocketTest.java
+++ b/jetty-unixsocket/src/test/java/org/eclipse/jetty/unixsocket/UnixSocketTest.java
@@ -18,14 +18,6 @@
package org.eclipse.jetty.unixsocket;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.hamcrest.Matchers.instanceOf;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.condition.OS.LINUX;
-import static org.junit.jupiter.api.condition.OS.MAC;
-
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
@@ -34,7 +26,6 @@ import java.nio.file.Paths;
import java.util.Date;
import java.util.concurrent.ExecutionException;
-import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -54,6 +45,14 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledOnOs;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.condition.OS.LINUX;
+import static org.junit.jupiter.api.condition.OS.MAC;
+
@EnabledOnOs({LINUX, MAC})
public class UnixSocketTest
{
@@ -68,94 +67,86 @@ public class UnixSocketTest
{
server = null;
httpClient = null;
- String unixSocketTmp = System.getProperty( "unix.socket.tmp" );
- if(StringUtil.isNotBlank( unixSocketTmp ) )
- {
- sockFile = Files.createTempFile( Paths.get(unixSocketTmp), "unix", ".sock" );
- } else {
- sockFile = Files.createTempFile("unix", ".sock" );
- }
- assertTrue(Files.deleteIfExists(sockFile),"temp sock file cannot be deleted");
+ String unixSocketTmp = System.getProperty("unix.socket.tmp");
+ if (StringUtil.isNotBlank(unixSocketTmp))
+ sockFile = Files.createTempFile(Paths.get(unixSocketTmp), "unix", ".sock");
+ else
+ sockFile = Files.createTempFile("unix", ".sock");
+ assertTrue(Files.deleteIfExists(sockFile), "temp sock file cannot be deleted");
}
-
+
@AfterEach
public void after() throws Exception
{
- if (httpClient!=null)
+ if (httpClient != null)
httpClient.stop();
- if (server!=null)
+ if (server != null)
server.stop();
// Force delete, this will fail if UnixSocket was not closed properly in the implementation
- FS.delete( sockFile);
+ FS.delete(sockFile);
}
-
+
@Test
public void testUnixSocket() throws Exception
{
server = new Server();
-
HttpConnectionFactory http = new HttpConnectionFactory();
+ UnixSocketConnector connector = new UnixSocketConnector(server, http);
+ connector.setUnixSocket(sockFile.toString());
+ server.addConnector(connector);
- UnixSocketConnector connector = new UnixSocketConnector( server, http );
- connector.setUnixSocket( sockFile.toString() );
- server.addConnector( connector );
-
- server.setHandler( new AbstractHandler.ErrorDispatchHandler()
+ server.setHandler(new AbstractHandler.ErrorDispatchHandler()
{
@Override
- protected void doNonErrorHandle( String target, Request baseRequest, HttpServletRequest request,
- HttpServletResponse response )
- throws IOException, ServletException
+ protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
int l = 0;
- if ( request.getContentLength() != 0 )
+ if (request.getContentLength() != 0)
{
InputStream in = request.getInputStream();
byte[] buffer = new byte[4096];
int r = 0;
- while ( r >= 0 )
+ while (r >= 0)
{
l += r;
- r = in.read( buffer );
+ r = in.read(buffer);
}
}
- log.info( "UnixSocketTest: request received" );
- baseRequest.setHandled( true );
- response.setStatus( 200 );
- response.getWriter().write( "Hello World " + new Date() + "\r\n" );
+ log.info("UnixSocketTest: request received");
+ baseRequest.setHandled(true);
+ response.setStatus(200);
+ response.getWriter().write("Hello World " + new Date() + "\r\n");
response.getWriter().write(
- "remote=" + request.getRemoteAddr() + ":" + request.getRemotePort() + "\r\n" );
+ "remote=" + request.getRemoteAddr() + ":" + request.getRemotePort() + "\r\n");
response.getWriter().write(
- "local =" + request.getLocalAddr() + ":" + request.getLocalPort() + "\r\n" );
- response.getWriter().write( "read =" + l + "\r\n" );
+ "local =" + request.getLocalAddr() + ":" + request.getLocalPort() + "\r\n");
+ response.getWriter().write("read =" + l + "\r\n");
}
- } );
+ });
server.start();
- httpClient = new HttpClient( new HttpClientTransportOverUnixSockets( sockFile.toString() ), null );
+ httpClient = new HttpClient(new HttpClientTransportOverUnixSockets(sockFile.toString()), null);
httpClient.start();
ContentResponse contentResponse = httpClient
- .newRequest( "http://localhost" )
+ .newRequest("http://localhost")
.send();
- log.debug( "response from server: {}", contentResponse.getContentAsString() );
+ log.debug("response from server: {}", contentResponse.getContentAsString());
- assertThat(contentResponse.getContentAsString(), containsString( "Hello World" ));
+ assertThat(contentResponse.getContentAsString(), containsString("Hello World"));
}
@Test
public void testNotLocal() throws Exception
- {
- httpClient = new HttpClient( new HttpClientTransportOverUnixSockets( sockFile.toString() ), null );
+ {
+ httpClient = new HttpClient(new HttpClientTransportOverUnixSockets(sockFile.toString()), null);
httpClient.start();
-
- ExecutionException e = assertThrows(ExecutionException.class, ()->{
- httpClient.newRequest( "http://google.com" ).send();
- });
+
+ ExecutionException e = assertThrows(ExecutionException.class, () -> httpClient.newRequest("http://google.com").send());
assertThat(e.getCause(), instanceOf(IOException.class));
- assertThat(e.getCause().getMessage(),containsString("UnixSocket cannot connect to google.com"));
+ assertThat(e.getCause().getMessage(), containsString("UnixSocket cannot connect to google.com"));
}
}
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ConnectionStatisticsTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ConnectionStatisticsTest.java
index ef651ee12e5..16c5fd66bdc 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ConnectionStatisticsTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/ConnectionStatisticsTest.java
@@ -18,10 +18,6 @@
package org.eclipse.jetty.http.client;
-import static org.eclipse.jetty.http.client.Transport.H2C;
-import static org.eclipse.jetty.http.client.Transport.HTTP;
-import static org.hamcrest.MatcherAssert.assertThat;
-
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -44,6 +40,11 @@ import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
+import static org.eclipse.jetty.http.client.Transport.H2C;
+import static org.eclipse.jetty.http.client.Transport.HTTP;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertTrue;
+
public class ConnectionStatisticsTest extends AbstractTest
{
@Override
@@ -73,7 +74,7 @@ public class ConnectionStatisticsTest extends AbstractTest
{
@Override
public void onOpened(Connection connection)
- {
+ {
}
@Override
@@ -82,7 +83,7 @@ public class ConnectionStatisticsTest extends AbstractTest
closed.countDown();
}
};
-
+
ConnectionStatistics serverStats = new ConnectionStatistics();
scenario.connector.addBean(serverStats);
scenario.connector.addBean(closer);
@@ -93,20 +94,20 @@ public class ConnectionStatisticsTest extends AbstractTest
scenario.client.addBean(closer);
clientStats.start();
- scenario.client.setIdleTimeout(1000);
+ long idleTimeout = 1000;
+ scenario.client.setIdleTimeout(idleTimeout);
byte[] content = new byte[3072];
long contentLength = content.length;
ContentResponse response = scenario.client.newRequest(scenario.newURI())
- .header(HttpHeader.CONNECTION,"close")
+ .header(HttpHeader.CONNECTION, "close")
.content(new BytesContentProvider(content))
.timeout(5, TimeUnit.SECONDS)
.send();
assertThat(response.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
+ assertTrue(closed.await(2 * idleTimeout, TimeUnit.MILLISECONDS));
- closed.await();
-
assertThat(serverStats.getConnectionsMax(), Matchers.greaterThan(0L));
assertThat(serverStats.getReceivedBytes(), Matchers.greaterThan(contentLength));
assertThat(serverStats.getSentBytes(), Matchers.greaterThan(contentLength));
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientContinueTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientContinueTest.java
index 082d7de6b9d..28d59edf9db 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientContinueTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientContinueTest.java
@@ -327,7 +327,7 @@ public class HttpClientContinueTest extends AbstractTest
{
init(transport);
final long idleTimeout = 1000;
- scenario.start(new AbstractHandler()
+ scenario.startServer(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException
@@ -343,8 +343,7 @@ public class HttpClientContinueTest extends AbstractTest
}
}
});
-
- scenario.client.setIdleTimeout(2 * idleTimeout);
+ scenario.startClient(httpClient -> httpClient.setIdleTimeout(2 * idleTimeout));
byte[] content = new byte[1024];
final CountDownLatch latch = new CountDownLatch(1);
@@ -375,7 +374,7 @@ public class HttpClientContinueTest extends AbstractTest
{
init(transport);
final long idleTimeout = 1000;
- scenario.start(new AbstractHandler()
+ scenario.startServer(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
@@ -393,8 +392,7 @@ public class HttpClientContinueTest extends AbstractTest
}
}
});
-
- scenario.client.setIdleTimeout(idleTimeout);
+ scenario.startClient(httpClient -> httpClient.setIdleTimeout(idleTimeout));
byte[] content = new byte[1024];
final CountDownLatch latch = new CountDownLatch(1);
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java
index 3cde40558bf..6ba09196945 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientIdleTimeoutTest.java
@@ -18,9 +18,6 @@
package org.eclipse.jetty.http.client;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -37,6 +34,9 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
public class HttpClientIdleTimeoutTest extends AbstractTest
{
private long idleTimeout = 1000;
@@ -52,7 +52,7 @@ public class HttpClientIdleTimeoutTest extends AbstractTest
public void testClientIdleTimeout(Transport transport) throws Exception
{
init(transport);
- scenario.start(new AbstractHandler()
+ scenario.startServer(new AbstractHandler()
{
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
@@ -65,9 +65,7 @@ public class HttpClientIdleTimeoutTest extends AbstractTest
}
}
});
- scenario.client.stop();
- scenario.client.setIdleTimeout(idleTimeout);
- scenario.client.start();
+ scenario.startClient(httpClient -> httpClient.setIdleTimeout(idleTimeout));
final CountDownLatch latch = new CountDownLatch(1);
scenario.client.newRequest(scenario.newURI())
@@ -126,10 +124,8 @@ public class HttpClientIdleTimeoutTest extends AbstractTest
public void testIdleClientIdleTimeout(Transport transport) throws Exception
{
init(transport);
- scenario.start(new EmptyServerHandler());
- scenario.client.stop();
- scenario.client.setIdleTimeout(idleTimeout);
- scenario.client.start();
+ scenario.startServer(new EmptyServerHandler());
+ scenario.startClient(httpClient -> httpClient.setIdleTimeout(idleTimeout));
// Make a first request to open a connection.
ContentResponse response = scenario.client.newRequest(scenario.newURI()).send();
diff --git a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java
index 97eeb3727e8..fc5934c7aeb 100644
--- a/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java
+++ b/tests/test-http-client-transport/src/test/java/org/eclipse/jetty/http/client/HttpClientTest.java
@@ -18,15 +18,6 @@
package org.eclipse.jetty.http.client;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsString;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.fail;
-
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
@@ -63,6 +54,15 @@ import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
+
public class HttpClientTest extends AbstractTest
{
@Override
@@ -453,7 +453,9 @@ public class HttpClientTest extends AbstractTest
public void testConnectionListener(Transport transport) throws Exception
{
init(transport);
- scenario.start(new EmptyServerHandler());
+ scenario.startServer(new EmptyServerHandler());
+ long idleTimeout = 1000;
+ scenario.startClient(httpClient -> httpClient.setIdleTimeout(idleTimeout));
CountDownLatch openLatch = new CountDownLatch(1);
CountDownLatch closeLatch = new CountDownLatch(1);
@@ -472,9 +474,6 @@ public class HttpClientTest extends AbstractTest
}
});
- long idleTimeout = 1000;
- scenario.client.setIdleTimeout(idleTimeout);
-
ContentResponse response = scenario.client.newRequest(scenario.newURI())
.scheme(scenario.getScheme())
.timeout(5, TimeUnit.SECONDS)
diff --git a/tests/test-http-client-transport/src/test/resources/jetty-logging.properties b/tests/test-http-client-transport/src/test/resources/jetty-logging.properties
index 1047ac8946d..914cac87711 100644
--- a/tests/test-http-client-transport/src/test/resources/jetty-logging.properties
+++ b/tests/test-http-client-transport/src/test/resources/jetty-logging.properties
@@ -2,6 +2,6 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
#org.eclipse.jetty.LEVEL=DEBUG
#org.eclipse.jetty.client.LEVEL=DEBUG
#org.eclipse.jetty.http2.LEVEL=DEBUG
-#org.eclipse.jetty.http2.hpack.LEVEL=INFO
+org.eclipse.jetty.http2.hpack.LEVEL=INFO
#org.eclipse.jetty.http2.client.LEVEL=DEBUG
#org.eclipse.jetty.io.LEVEL=DEBUG