Merged branch 'jetty-9.1' into 'http_client_connection_factories'.
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 46cf76a..fac4bcf 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
@@ -19,24 +19,20 @@
package org.eclipse.jetty.client;
import java.io.IOException;
-import java.net.ConnectException;
import java.net.SocketAddress;
-import java.net.SocketException;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
-import javax.net.ssl.SSLEngine;
+import java.util.Map;
-import org.eclipse.jetty.http.HttpScheme;
-import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.io.SelectorManager;
-import org.eclipse.jetty.io.ssl.SslConnection;
+import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
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;
public abstract class AbstractHttpClientTransport extends ContainerLifeCycle implements HttpClientTransport
{
@@ -72,12 +68,20 @@
}
@Override
- public void connect(HttpDestination destination, SocketAddress address, Promise<org.eclipse.jetty.client.api.Connection> promise)
+ protected void doStop() throws Exception
+ {
+ super.doStop();
+ removeBean(selectorManager);
+ }
+
+ @Override
+ public void connect(SocketAddress address, Map<String, Object> 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)
@@ -86,8 +90,9 @@
channel.configureBlocking(false);
channel.connect(address);
- ConnectionCallback callback = new ConnectionCallback(destination, promise);
- selectorManager.connect(channel, callback);
+ context.put(SslClientConnectionFactory.SSL_PEER_HOST_CONTEXT_KEY, destination.getHost());
+ context.put(SslClientConnectionFactory.SSL_PEER_PORT_CONTEXT_KEY, destination.getPort());
+ selectorManager.connect(channel, context);
}
// Must catch all exceptions, since some like
// UnresolvedAddressException are not IOExceptions.
@@ -104,12 +109,14 @@
}
finally
{
+ @SuppressWarnings("unchecked")
+ Promise<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
promise.failed(x);
}
}
}
- protected void configure(HttpClient client, SocketChannel channel) throws SocketException
+ protected void configure(HttpClient client, SocketChannel channel) throws IOException
{
channel.socket().setTcpNoDelay(client.isTCPNoDelay());
}
@@ -119,40 +126,6 @@
return new ClientSelectorManager(client, selectors);
}
- protected SslConnection createSslConnection(EndPoint endPoint, HttpDestination destination)
- {
- HttpClient httpClient = destination.getHttpClient();
- SslContextFactory sslContextFactory = httpClient.getSslContextFactory();
- SSLEngine engine = sslContextFactory.newSSLEngine(destination.getHost(), destination.getPort());
- engine.setUseClientMode(true);
-
- SslConnection sslConnection = newSslConnection(httpClient, endPoint, engine);
- sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
- endPoint.setConnection(sslConnection);
- EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
- Connection connection = newConnection(appEndPoint, destination);
- appEndPoint.setConnection(connection);
-
- return sslConnection;
- }
-
- protected SslConnection newSslConnection(HttpClient httpClient, EndPoint endPoint, SSLEngine engine)
- {
- return new SslConnection(httpClient.getByteBufferPool(), httpClient.getExecutor(), endPoint, engine);
- }
-
- protected abstract Connection newConnection(EndPoint endPoint, HttpDestination destination);
-
- protected org.eclipse.jetty.client.api.Connection tunnel(EndPoint endPoint, HttpDestination destination, org.eclipse.jetty.client.api.Connection connection)
- {
- SslConnection sslConnection = createSslConnection(endPoint, destination);
- Connection result = sslConnection.getDecryptedEndPoint().getConnection();
- selectorManager.connectionClosed((Connection)connection);
- selectorManager.connectionOpened(sslConnection);
- LOG.debug("Tunnelled {} over {}", connection, result);
- return (org.eclipse.jetty.client.api.Connection)result;
- }
-
protected class ClientSelectorManager extends SelectorManager
{
private final HttpClient client;
@@ -170,63 +143,21 @@
}
@Override
- public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) throws IOException
+ public org.eclipse.jetty.io.Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) throws IOException
{
- ConnectionCallback callback = (ConnectionCallback)attachment;
- HttpDestination destination = callback.destination;
-
- SslContextFactory sslContextFactory = client.getSslContextFactory();
- if (!destination.isProxied() && HttpScheme.HTTPS.is(destination.getScheme()))
- {
- if (sslContextFactory == null)
- {
- IOException failure = new ConnectException("Missing " + SslContextFactory.class.getSimpleName() + " for " + destination.getScheme() + " requests");
- callback.failed(failure);
- throw failure;
- }
- else
- {
- SslConnection sslConnection = createSslConnection(endPoint, destination);
- callback.succeeded((org.eclipse.jetty.client.api.Connection)sslConnection.getDecryptedEndPoint().getConnection());
- return sslConnection;
- }
- }
- else
- {
- Connection connection = AbstractHttpClientTransport.this.newConnection(endPoint, destination);
- callback.succeeded((org.eclipse.jetty.client.api.Connection)connection);
- return connection;
- }
+ @SuppressWarnings("unchecked")
+ Map<String, Object> context = (Map<String, Object>)attachment;
+ HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
+ return destination.getClientConnectionFactory().newConnection(endPoint, context);
}
@Override
- protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
+ protected void connectionFailed(SocketChannel channel, Throwable x, Object attachment)
{
- ConnectionCallback callback = (ConnectionCallback)attachment;
- callback.failed(ex);
- }
- }
-
- private class ConnectionCallback implements Promise<org.eclipse.jetty.client.api.Connection>
- {
- private final HttpDestination destination;
- private final Promise<org.eclipse.jetty.client.api.Connection> promise;
-
- private ConnectionCallback(HttpDestination destination, Promise<org.eclipse.jetty.client.api.Connection> promise)
- {
- this.destination = destination;
- this.promise = promise;
- }
-
- @Override
- public void succeeded(org.eclipse.jetty.client.api.Connection result)
- {
- promise.succeeded(result);
- }
-
- @Override
- public void failed(Throwable x)
- {
+ @SuppressWarnings("unchecked")
+ Map<String, Object> context = (Map<String, Object>)attachment;
+ @SuppressWarnings("unchecked")
+ Promise<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
promise.failed(x);
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
index 7adcb80..b19c52c 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/AuthenticationProtocolHandler.java
@@ -109,16 +109,19 @@
return;
}
- URI uri = getAuthenticationURI(request);
Authentication authentication = null;
Authentication.HeaderInfo headerInfo = null;
- for (Authentication.HeaderInfo element : headerInfos)
+ URI uri = getAuthenticationURI(request);
+ if (uri != null)
{
- authentication = client.getAuthenticationStore().findAuthentication(element.getType(), uri, element.getRealm());
- if (authentication != null)
+ for (Authentication.HeaderInfo element : headerInfos)
{
- headerInfo = element;
- break;
+ authentication = client.getAuthenticationStore().findAuthentication(element.getType(), uri, element.getRealm());
+ if (authentication != null)
+ {
+ headerInfo = element;
+ break;
+ }
}
}
if (authentication == null)
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionPool.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java
similarity index 95%
rename from jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionPool.java
rename to jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java
index a9d9841..9ed8d93 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionPool.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ConnectionPool.java
@@ -16,7 +16,7 @@
// ========================================================================
//
-package org.eclipse.jetty.client.http;
+package org.eclipse.jetty.client;
import java.io.IOException;
import java.util.concurrent.BlockingDeque;
@@ -33,9 +33,9 @@
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
-public class HttpConnectionPool implements Dumpable
+public class ConnectionPool implements Dumpable
{
- private static final Logger LOG = Log.getLogger(HttpConnectionPool.class);
+ private static final Logger LOG = Log.getLogger(ConnectionPool.class);
private final AtomicInteger connectionCount = new AtomicInteger();
private final Destination destination;
@@ -44,7 +44,7 @@
private final BlockingDeque<Connection> idleConnections;
private final BlockingQueue<Connection> activeConnections;
- public HttpConnectionPool(Destination destination, int maxConnections, Promise<Connection> connectionPromise)
+ public ConnectionPool(Destination destination, int maxConnections, Promise<Connection> connectionPromise)
{
this.destination = destination;
this.maxConnections = maxConnections;
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 ef1fc0f..cfabe91 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
@@ -27,10 +27,12 @@
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -44,7 +46,6 @@
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination;
-import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
@@ -57,7 +58,6 @@
import org.eclipse.jetty.util.Jetty;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.SocketAddressResolver;
-import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -104,12 +104,13 @@
{
private static final Logger LOG = Log.getLogger(HttpClient.class);
- private final ConcurrentMap<String, HttpDestination> destinations = new ConcurrentHashMap<>();
+ private final ConcurrentMap<Origin, HttpDestination> destinations = new ConcurrentHashMap<>();
private final ConcurrentMap<Long, HttpConversation> conversations = new ConcurrentHashMap<>();
private final List<ProtocolHandler> handlers = new ArrayList<>();
private final List<Request.Listener> requestListeners = new ArrayList<>();
private final AuthenticationStore authenticationStore = new HttpAuthenticationStore();
private final Set<ContentDecoder.Factory> decoderFactories = new ContentDecoderFactorySet();
+ private final ProxyConfiguration proxyConfig = new ProxyConfiguration();
private final HttpClientTransport transport;
private final SslContextFactory sslContextFactory;
private volatile CookieManager cookieManager;
@@ -132,7 +133,6 @@
private volatile boolean tcpNoDelay = true;
private volatile boolean dispatchIO = true;
private volatile boolean strictEventOrdering = false;
- private volatile ProxyConfiguration proxyConfig;
private volatile HttpField encodingField;
/**
@@ -359,7 +359,7 @@
*/
public Request newRequest(String host, int port)
{
- return newRequest(address("http", host, port));
+ return newRequest(new Origin("http", host, port).asString());
}
/**
@@ -417,13 +417,6 @@
return newRequest;
}
- public String address(String scheme, String host, int port)
- {
- StringBuilder result = new StringBuilder();
- URIUtil.appendSchemeHostPort(result, scheme, host, port);
- return result.toString();
- }
-
/**
* Returns a {@link Destination} for the given scheme, host and port.
* Applications may use {@link Destination}s to create {@link Connection}s
@@ -446,20 +439,20 @@
{
port = normalizePort(scheme, port);
- String address = address(scheme, host, port);
- HttpDestination destination = destinations.get(address);
+ Origin origin = new Origin(scheme, host, port);
+ HttpDestination destination = destinations.get(origin);
if (destination == null)
{
- destination = transport.newHttpDestination(scheme, host, port);
+ destination = transport.newHttpDestination(origin);
if (isRunning())
{
- HttpDestination existing = destinations.putIfAbsent(address, destination);
+ HttpDestination existing = destinations.putIfAbsent(origin, destination);
if (existing != null)
destination = existing;
else
LOG.debug("Created {}", destination);
if (!isRunning())
- destinations.remove(address);
+ destinations.remove(origin);
}
}
@@ -486,13 +479,16 @@
protected void newConnection(final HttpDestination destination, final Promise<Connection> promise)
{
- Destination.Address address = destination.getConnectAddress();
+ Origin.Address address = destination.getConnectAddress();
resolver.resolve(address.getHost(), address.getPort(), new Promise<SocketAddress>()
{
@Override
public void succeeded(SocketAddress socketAddress)
{
- transport.connect(destination, socketAddress, promise);
+ Map<String, Object> context = new HashMap<>();
+ context.put(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY, destination);
+ context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, promise);
+ transport.connect(socketAddress, context);
}
@Override
@@ -881,14 +877,6 @@
return proxyConfig;
}
- /**
- * @param proxyConfig the forward proxy configuration
- */
- public void setProxyConfiguration(ProxyConfiguration proxyConfig)
- {
- this.proxyConfig = proxyConfig;
- }
-
protected HttpField getAcceptEncodingField()
{
return encodingField;
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 de943a6..f6a21ef 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
@@ -19,9 +19,9 @@
package org.eclipse.jetty.client;
import java.net.SocketAddress;
+import java.util.Map;
-import org.eclipse.jetty.client.api.Connection;
-import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.io.ClientConnectionFactory;
/**
* {@link HttpClientTransport} represents what transport implementations should provide
@@ -34,8 +34,11 @@
* but the HTTP exchange may also be carried using the SPDY protocol or the FCGI protocol or, in future,
* other protocols.
*/
-public interface HttpClientTransport
+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";
+
/**
* Sets the {@link HttpClient} instance on this transport.
* <p />
@@ -53,27 +56,16 @@
* {@link HttpDestination} controls the destination-connection cardinality: protocols like
* HTTP have 1-N cardinality, while multiplexed protocols like SPDY have a 1-1 cardinality.
*
- * @param scheme the destination scheme
- * @param host the destination host
- * @param port the destination port
+ * @param origin the destination origin
* @return a new, transport-specific, {@link HttpDestination} object
*/
- public HttpDestination newHttpDestination(String scheme, String host, int port);
+ public HttpDestination newHttpDestination(Origin origin);
/**
* Establishes a physical connection to the given {@code address}.
*
- * @param destination the destination
* @param address the address to connect to
- * @param promise the promise to notify when the connection succeeds or fails
+ * @param context the context information to establish the connection
*/
- public void connect(HttpDestination destination, SocketAddress address, Promise<Connection> promise);
-
- /**
- * Establishes an encrypted tunnel over the given {@code connection}
- *
- * @param connection the connection to tunnel
- * @return the tunnelled connection
- */
- public Connection tunnel(Connection connection);
+ public void connect(SocketAddress address, Map<String, Object> context);
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
index e09079b..c9bbacf 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpConnection.java
@@ -83,6 +83,7 @@
HttpVersion version = request.getVersion();
HttpFields headers = request.getHeaders();
ContentProvider content = request.getContent();
+ ProxyConfiguration.Proxy proxy = destination.getProxy();
// Make sure the path is there
String path = request.getPath();
@@ -91,7 +92,7 @@
path = "/";
request.path(path);
}
- if (destination.isProxied() && !HttpMethod.CONNECT.is(method))
+ if (proxy != null && !HttpMethod.CONNECT.is(method))
{
path = request.getURI().toString();
request.path(path);
@@ -136,9 +137,12 @@
request.header(HttpHeader.COOKIE.asString(), cookieString.toString());
// Authorization
- URI authenticationURI = destination.isProxied() ? destination.getProxyURI() : request.getURI();
- Authentication.Result authnResult = getHttpClient().getAuthenticationStore().findAuthenticationResult(authenticationURI);
- if (authnResult != null)
- authnResult.apply(request);
+ URI authenticationURI = proxy != null ? proxy.getURI() : request.getURI();
+ if (authenticationURI != null)
+ {
+ Authentication.Result authnResult = getHttpClient().getAuthenticationStore().findAuthenticationResult(authenticationURI);
+ if (authnResult != null)
+ authnResult.apply(request);
+ }
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
index cf4c2df..534f248 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpDestination.java
@@ -20,51 +20,45 @@
import java.io.Closeable;
import java.io.IOException;
-import java.net.URI;
import java.nio.channels.AsynchronousCloseException;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.RejectedExecutionException;
-import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Destination;
-import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
-import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
-import org.eclipse.jetty.util.ssl.SslContextFactory;
public abstract class HttpDestination implements Destination, Closeable, Dumpable
{
protected static final Logger LOG = Log.getLogger(HttpDestination.class);
private final HttpClient client;
- private final String scheme;
- private final String host;
- private final Address address;
+ private final Origin origin;
private final Queue<HttpExchange> exchanges;
private final RequestNotifier requestNotifier;
private final ResponseNotifier responseNotifier;
- private final Address proxyAddress;
+ private final ProxyConfiguration.Proxy proxy;
+ private final ClientConnectionFactory connectionFactory;
private final HttpField hostField;
- public HttpDestination(HttpClient client, String scheme, String host, int port)
+ public HttpDestination(HttpClient client, Origin origin)
{
this.client = client;
- this.scheme = scheme;
- this.host = host;
- this.address = new Address(host, port);
+ this.origin = origin;
this.exchanges = new BlockingArrayQueue<>(client.getMaxRequestsQueuedPerDestination());
@@ -72,19 +66,40 @@
this.responseNotifier = new ResponseNotifier(client);
ProxyConfiguration proxyConfig = client.getProxyConfiguration();
- proxyAddress = proxyConfig != null && proxyConfig.matches(host, port) ?
- new Address(proxyConfig.getHost(), proxyConfig.getPort()) : null;
+ proxy = proxyConfig.match(origin);
+ ClientConnectionFactory connectionFactory = client.getTransport();
+ if (proxy != null)
+ {
+ connectionFactory = proxy.newClientConnectionFactory(connectionFactory);
+ }
+ else
+ {
+ if (HttpScheme.HTTPS.is(getScheme()))
+ connectionFactory = newSslClientConnectionFactory(connectionFactory);
+ }
+ this.connectionFactory = connectionFactory;
- if (!client.isDefaultPort(scheme, port))
- host += ":" + port;
+ String host = getHost();
+ if (!client.isDefaultPort(getScheme(), getPort()))
+ host += ":" + getPort();
hostField = new HttpField(HttpHeader.HOST, host);
}
+ protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
+ {
+ return new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
+ }
+
public HttpClient getHttpClient()
{
return client;
}
+ public Origin getOrigin()
+ {
+ return origin;
+ }
+
public Queue<HttpExchange> getHttpExchanges()
{
return exchanges;
@@ -100,10 +115,20 @@
return responseNotifier;
}
+ public ProxyConfiguration.Proxy getProxy()
+ {
+ return proxy;
+ }
+
+ public ClientConnectionFactory getClientConnectionFactory()
+ {
+ return connectionFactory;
+ }
+
@Override
public String getScheme()
{
- return scheme;
+ return origin.getScheme();
}
@Override
@@ -111,32 +136,18 @@
{
// InetSocketAddress.getHostString() transforms the host string
// in case of IPv6 addresses, so we return the original host string
- return host;
+ return origin.getAddress().getHost();
}
@Override
public int getPort()
{
- return address.getPort();
+ return origin.getAddress().getPort();
}
- public Address getConnectAddress()
+ public Origin.Address getConnectAddress()
{
- return isProxied() ? proxyAddress : address;
- }
-
- public boolean isProxied()
- {
- return proxyAddress != null;
- }
-
- public URI getProxyURI()
- {
- ProxyConfiguration proxyConfiguration = client.getProxyConfiguration();
- String uri = getScheme() + "://" + proxyConfiguration.getHost();
- if (!client.isDefaultPort(getScheme(), proxyConfiguration.getPort()))
- uri += ":" + proxyConfiguration.getPort();
- return URI.create(uri);
+ return proxy == null ? origin.getAddress() : proxy.getAddress();
}
public HttpField getHostField()
@@ -146,7 +157,7 @@
protected void send(Request request, List<Response.ResponseListener> listeners)
{
- if (!scheme.equals(request.getScheme()))
+ if (!getScheme().equals(request.getScheme()))
throw new IllegalArgumentException("Invalid request scheme " + request.getScheme() + " for destination " + this);
if (!getHost().equals(request.getHost()))
throw new IllegalArgumentException("Invalid request host " + request.getHost() + " for destination " + this);
@@ -188,7 +199,7 @@
public void newConnection(Promise<Connection> promise)
{
- createConnection(new ProxyPromise(promise));
+ createConnection(promise);
}
protected void createConnection(Promise<Connection> promise)
@@ -236,18 +247,6 @@
getResponseNotifier().notifyComplete(listeners, new Result(request, cause, response, cause));
}
- protected void tunnelSucceeded(Connection connection, Promise<Connection> promise)
- {
- // Wrap the connection with TLS
- promise.succeeded(client.getTransport().tunnel(connection));
- }
-
- protected void tunnelFailed(Connection connection, Promise<Connection> promise, Throwable failure)
- {
- promise.failed(failure);
- connection.close();
- }
-
@Override
public String dump()
{
@@ -262,7 +261,7 @@
public String asString()
{
- return client.address(getScheme(), getHost(), getPort());
+ return origin.asString();
}
@Override
@@ -271,84 +270,6 @@
return String.format("%s(%s)%s",
HttpDestination.class.getSimpleName(),
asString(),
- proxyAddress == null ? "" : " via " + proxyAddress.getHost() + ":" + proxyAddress.getPort());
- }
-
- /**
- * Decides whether to establish a proxy tunnel using HTTP CONNECT.
- * It is implemented as a promise because it needs to establish the tunnel
- * when the TCP connection is succeeded, and needs to notify another
- * promise when the tunnel is established (or failed).
- */
- private class ProxyPromise implements Promise<Connection>
- {
- private final Promise<Connection> delegate;
-
- private ProxyPromise(Promise<Connection> delegate)
- {
- this.delegate = delegate;
- }
-
- @Override
- public void succeeded(Connection connection)
- {
- if (isProxied() && HttpScheme.HTTPS.is(getScheme()))
- {
- if (client.getSslContextFactory() != null)
- {
- tunnel(connection);
- }
- else
- {
- String message = String.format("Cannot perform requests over SSL, no %s in %s",
- SslContextFactory.class.getSimpleName(), HttpClient.class.getSimpleName());
- delegate.failed(new IllegalStateException(message));
- }
- }
- else
- {
- delegate.succeeded(connection);
- }
- }
-
- @Override
- public void failed(Throwable x)
- {
- delegate.failed(x);
- }
-
- private void tunnel(final Connection connection)
- {
- String target = address.getHost() + ":" + address.getPort();
- Request connect = client.newRequest(proxyAddress.getHost(), proxyAddress.getPort())
- .scheme(HttpScheme.HTTP.asString())
- .method(HttpMethod.CONNECT)
- .path(target)
- .header(HttpHeader.HOST, target)
- .timeout(client.getConnectTimeout(), TimeUnit.MILLISECONDS);
- connection.send(connect, new Response.CompleteListener()
- {
- @Override
- public void onComplete(Result result)
- {
- if (result.isFailed())
- {
- tunnelFailed(connection, delegate, result.getFailure());
- }
- else
- {
- Response response = result.getResponse();
- if (response.getStatus() == 200)
- {
- tunnelSucceeded(connection, delegate);
- }
- else
- {
- tunnelFailed(connection, delegate, new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
- }
- }
- }
- });
- }
+ proxy == null ? "" : " via " + proxy);
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java
new file mode 100644
index 0000000..23cc2d2
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java
@@ -0,0 +1,200 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 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.client;
+
+import java.io.IOException;
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.api.Request;
+import org.eclipse.jetty.client.api.Response;
+import org.eclipse.jetty.client.api.Result;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class HttpProxy extends ProxyConfiguration.Proxy
+{
+ public HttpProxy(String host, int port)
+ {
+ this(new Origin.Address(host, port), false);
+ }
+
+ public HttpProxy(Origin.Address address, boolean secure)
+ {
+ super(address, secure);
+ }
+
+ @Override
+ public ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory)
+ {
+ return new HttpProxyClientConnectionFactory(connectionFactory);
+ }
+
+ @Override
+ public URI getURI()
+ {
+ String scheme = isSecure() ? HttpScheme.HTTPS.asString() : HttpScheme.HTTP.asString();
+ return URI.create(new Origin(scheme, getAddress()).asString());
+ }
+
+ public static class HttpProxyClientConnectionFactory implements ClientConnectionFactory
+ {
+ private static final Logger LOG = Log.getLogger(HttpProxyClientConnectionFactory.class);
+ private final ClientConnectionFactory connectionFactory;
+
+ public HttpProxyClientConnectionFactory(ClientConnectionFactory connectionFactory)
+ {
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
+ {
+ @SuppressWarnings("unchecked")
+ Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
+ final ProxyPromise proxyPromise = new ProxyPromise(endPoint, promise, context);
+ // Replace the promise with the proxy one
+ context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, proxyPromise);
+ return connectionFactory.newConnection(endPoint, context);
+ }
+
+ /**
+ * Decides whether to establish a proxy tunnel using HTTP CONNECT.
+ * It is implemented as a promise because it needs to establish the
+ * tunnel after the TCP connection is succeeded, and needs to notify
+ * the nested promise when the tunnel is established (or failed).
+ */
+ private class ProxyPromise implements Promise<Connection>
+ {
+ private final EndPoint endPoint;
+ private final Promise<Connection> promise;
+ private final Map<String, Object> context;
+
+ private ProxyPromise(EndPoint endPoint, Promise<Connection> promise, Map<String, Object> context)
+ {
+ this.endPoint = endPoint;
+ this.promise = promise;
+ this.context = context;
+ }
+
+ @Override
+ public void succeeded(Connection connection)
+ {
+ HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
+ if (HttpScheme.HTTPS.is(destination.getScheme()))
+ {
+ SslContextFactory sslContextFactory = destination.getHttpClient().getSslContextFactory();
+ if (sslContextFactory != null)
+ {
+ tunnel(destination, connection);
+ }
+ else
+ {
+ String message = String.format("Cannot perform requests over SSL, no %s in %s",
+ SslContextFactory.class.getSimpleName(), HttpClient.class.getSimpleName());
+ promise.failed(new IllegalStateException(message));
+ }
+ }
+ else
+ {
+ promise.succeeded(connection);
+ }
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ promise.failed(x);
+ }
+
+ private void tunnel(HttpDestination destination, final Connection connection)
+ {
+ String target = destination.getOrigin().getAddress().asString();
+ Origin.Address proxyAddress = destination.getConnectAddress();
+ HttpClient httpClient = destination.getHttpClient();
+ Request connect = httpClient.newRequest(proxyAddress.getHost(), proxyAddress.getPort())
+ .scheme(HttpScheme.HTTP.asString())
+ .method(HttpMethod.CONNECT)
+ .path(target)
+ .header(HttpHeader.HOST, target)
+ .timeout(httpClient.getConnectTimeout(), TimeUnit.MILLISECONDS);
+
+ connection.send(connect, new Response.CompleteListener()
+ {
+ @Override
+ public void onComplete(Result result)
+ {
+ if (result.isFailed())
+ {
+ tunnelFailed(result.getFailure());
+ }
+ else
+ {
+ Response response = result.getResponse();
+ if (response.getStatus() == 200)
+ {
+ tunnelSucceeded();
+ }
+ else
+ {
+ tunnelFailed(new HttpResponseException("Received " + response + " for " + result.getRequest(), response));
+ }
+ }
+ }
+ });
+ }
+
+ private void tunnelSucceeded()
+ {
+ try
+ {
+ // Replace the promise back with the original
+ context.put(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY, promise);
+ HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
+ HttpClient client = destination.getHttpClient();
+ ClientConnectionFactory sslConnectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
+ org.eclipse.jetty.io.Connection oldConnection = endPoint.getConnection();
+ org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context);
+ Helper.replaceConnection(oldConnection, newConnection);
+ LOG.debug("HTTP tunnel established: {} over {}", oldConnection, newConnection);
+ }
+ catch (Throwable x)
+ {
+ tunnelFailed(x);
+ }
+ }
+
+ private void tunnelFailed(Throwable failure)
+ {
+ endPoint.close();
+ failed(failure);
+ }
+ }
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
index ee86fc2..d8d9f55 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpRequest.java
@@ -589,7 +589,7 @@
path += "?" + query;
URI result = URI.create(path);
if (!result.isAbsolute() && !result.isOpaque())
- result = URI.create(client.address(getScheme(), getHost(), getPort()) + path);
+ result = URI.create(new Origin(getScheme(), getHost(), getPort()).asString() + path);
return result;
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java
index aa2106e..02687d9 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/MultiplexHttpDestination.java
@@ -29,9 +29,9 @@
private final AtomicReference<ConnectState> connect = new AtomicReference<>(ConnectState.DISCONNECTED);
private C connection;
- protected MultiplexHttpDestination(HttpClient client, String scheme, String host, int port)
+ protected MultiplexHttpDestination(HttpClient client, Origin origin)
{
- super(client, scheme, host, port);
+ super(client, origin);
}
@Override
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
new file mode 100644
index 0000000..93c4a33
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/Origin.java
@@ -0,0 +1,122 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 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.client;
+
+import org.eclipse.jetty.util.URIUtil;
+
+public class Origin
+{
+ private final String scheme;
+ private final Address address;
+
+ public Origin(String scheme, String host, int port)
+ {
+ this(scheme, new Address(host, port));
+ }
+
+ public Origin(String scheme, Address address)
+ {
+ this.scheme = scheme;
+ this.address = address;
+ }
+
+ public String getScheme()
+ {
+ return scheme;
+ }
+
+ public Address getAddress()
+ {
+ return address;
+ }
+
+ public String asString()
+ {
+ StringBuilder result = new StringBuilder();
+ URIUtil.appendSchemeHostPort(result, scheme, address.host, address.port);
+ return result.toString();
+ }
+
+ @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()
+ {
+ int result = scheme.hashCode();
+ result = 31 * result + address.hashCode();
+ return result;
+ }
+
+ public static class Address
+ {
+ private final String host;
+ private final int port;
+
+ public Address(String host, int port)
+ {
+ this.host = host;
+ this.port = port;
+ }
+
+ public String getHost()
+ {
+ return host;
+ }
+
+ public int getPort()
+ {
+ return port;
+ }
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ 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;
+ }
+
+ @Override
+ public int hashCode()
+ {
+ int result = host.hashCode();
+ result = 31 * result + port;
+ return result;
+ }
+
+ public String asString()
+ {
+ return String.format("%s:%d", host, port);
+ }
+
+ @Override
+ public String toString()
+ {
+ return asString();
+ }
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java
index 841c661..d7e96f3 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyAuthenticationProtocolHandler.java
@@ -59,6 +59,7 @@
protected URI getAuthenticationURI(Request request)
{
HttpDestination destination = getHttpClient().destinationFor(request.getScheme(), request.getHost(), request.getPort());
- return destination.isProxied() ? destination.getProxyURI() : request.getURI();
+ ProxyConfiguration.Proxy proxy = destination.getProxy();
+ return proxy != null ? proxy.getURI() : request.getURI();
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyConfiguration.java b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyConfiguration.java
new file mode 100644
index 0000000..153d33a
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/ProxyConfiguration.java
@@ -0,0 +1,137 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 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.client;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jetty.io.ClientConnectionFactory;
+
+/**
+ * The configuration of the forward proxy to use with {@link org.eclipse.jetty.client.HttpClient}.
+ * <p />
+ * Applications add subclasses of {@link Proxy} to this configuration via:
+ * <pre>
+ * ProxyConfiguration proxyConfig = httpClient.getProxyConfiguration();
+ * proxyConfig.getProxies().add(new HttpProxy(proxyHost, 8080));
+ * </pre>
+ *
+ * @see HttpClient#getProxyConfiguration()
+ */
+public class ProxyConfiguration
+{
+ private final List<Proxy> proxies = new ArrayList<>();
+
+ public List<Proxy> getProxies()
+ {
+ return proxies;
+ }
+
+ public Proxy match(Origin origin)
+ {
+ for (Proxy proxy : getProxies())
+ {
+ if (proxy.matches(origin))
+ return proxy;
+ }
+ return null;
+ }
+
+ public static abstract class Proxy
+ {
+ private final Set<Origin> included = new HashSet<>();
+ private final Set<Origin> excluded = new HashSet<>();
+ private final Origin.Address address;
+ private final boolean secure;
+
+ protected Proxy(Origin.Address address, boolean secure)
+ {
+ this.address = address;
+ this.secure = secure;
+ }
+
+ /**
+ * @return the address of this proxy
+ */
+ public Origin.Address getAddress()
+ {
+ return address;
+ }
+
+ /**
+ * @return whether the connection to the proxy must be secured via TLS
+ */
+ public boolean isSecure()
+ {
+ return secure;
+ }
+
+ /**
+ * @return the list of origins that must be proxied
+ */
+ public Set<Origin> getIncludedOrigins()
+ {
+ return included;
+ }
+
+ /**
+ * @return the list of origins that must not be proxied.
+ */
+ public Set<Origin> getExcludedOrigins()
+ {
+ return excluded;
+ }
+
+ /**
+ * @return an URI representing this proxy, or null if no URI can represent this proxy
+ */
+ public URI getURI()
+ {
+ return null;
+ }
+
+ /**
+ * Matches the given {@code origin} with the included and excluded origins,
+ * returning true if the given {@code origin} is to be proxied.
+ *
+ * @param origin the origin to test for proxying
+ * @return true if the origin must be proxied, false otherwise
+ */
+ public boolean matches(Origin origin)
+ {
+ return included.contains(origin) || !excluded.contains(origin);
+ }
+
+ /**
+ * @param connectionFactory the nested {@link ClientConnectionFactory}
+ * @return a new {@link ClientConnectionFactory} for this {@link Proxy}
+ */
+ public abstract ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory);
+
+ @Override
+ public String toString()
+ {
+ return address.toString();
+ }
+ }
+
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java b/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java
new file mode 100644
index 0000000..e06d3f2
--- /dev/null
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/Socks4Proxy.java
@@ -0,0 +1,190 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 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.client;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.Charset;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.http.HttpScheme;
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+public class Socks4Proxy extends ProxyConfiguration.Proxy
+{
+ public Socks4Proxy(String host, int port)
+ {
+ this(new Origin.Address(host, port), false);
+ }
+
+ public Socks4Proxy(Origin.Address address, boolean secure)
+ {
+ super(address, secure);
+ }
+
+ @Override
+ public ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory)
+ {
+ return new Socks4ProxyClientConnectionFactory(connectionFactory);
+ }
+
+ public static class Socks4ProxyClientConnectionFactory implements ClientConnectionFactory
+ {
+ private final ClientConnectionFactory connectionFactory;
+
+ public Socks4ProxyClientConnectionFactory(ClientConnectionFactory connectionFactory)
+ {
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
+ {
+ HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
+ Executor executor = destination.getHttpClient().getExecutor();
+ return new Socks4ProxyConnection(endPoint, executor, connectionFactory, context);
+ }
+ }
+
+ private static class Socks4ProxyConnection extends AbstractConnection implements Callback
+ {
+ private static final Pattern IPv4_PATTERN = Pattern.compile("(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})");
+ private static final Logger LOG = Log.getLogger(Socks4ProxyConnection.class);
+
+ private final ClientConnectionFactory connectionFactory;
+ private final Map<String, Object> context;
+
+ public Socks4ProxyConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, Map<String, Object> context)
+ {
+ super(endPoint, executor);
+ this.connectionFactory = connectionFactory;
+ this.context = context;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ writeSocks4Connect();
+ }
+
+ /**
+ * Writes the SOCKS "connect" bytes, differentiating between SOCKS 4 and 4A;
+ * the former sends an IPv4 address, the latter the full domain name.
+ */
+ private void writeSocks4Connect()
+ {
+ HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
+ String host = destination.getHost();
+ short port = (short)destination.getPort();
+ Matcher matcher = IPv4_PATTERN.matcher(host);
+ if (matcher.matches())
+ {
+ // SOCKS 4
+ ByteBuffer buffer = ByteBuffer.allocate(9);
+ buffer.put((byte)4).put((byte)1).putShort(port);
+ for (int i = 1; i <= 4; ++i)
+ buffer.put((byte)Integer.parseInt(matcher.group(i)));
+ buffer.put((byte)0);
+ buffer.flip();
+ getEndPoint().write(this, buffer);
+ }
+ else
+ {
+ // SOCKS 4A
+ byte[] hostBytes = host.getBytes(Charset.forName("UTF-8"));
+ ByteBuffer buffer = ByteBuffer.allocate(9 + hostBytes.length + 1);
+ buffer.put((byte)4).put((byte)1).putShort(port);
+ buffer.put((byte)0).put((byte)0).put((byte)0).put((byte)1).put((byte)0);
+ buffer.put(hostBytes).put((byte)0);
+ buffer.flip();
+ getEndPoint().write(this, buffer);
+ }
+ }
+
+ @Override
+ public void succeeded()
+ {
+ LOG.debug("Written SOCKS4 connect request");
+ fillInterested();
+ }
+
+ @Override
+ public void failed(Throwable x)
+ {
+ close();
+ @SuppressWarnings("unchecked")
+ Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
+ promise.failed(x);
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ ByteBuffer buffer = BufferUtil.allocate(8);
+ int filled = getEndPoint().fill(buffer);
+ LOG.debug("Read SOCKS4 connect response, {} bytes", filled);
+ if (filled != 8)
+ throw new IOException("Invalid response from SOCKS4 proxy");
+ int result = buffer.get(1);
+ if (result == 0x5A)
+ tunnel();
+ else
+ throw new IOException("SOCKS4 tunnel failed with code " + result);
+ }
+ catch (Throwable x)
+ {
+ failed(x);
+ }
+ }
+
+ private void tunnel()
+ {
+ try
+ {
+ HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
+ HttpClient client = destination.getHttpClient();
+ ClientConnectionFactory connectionFactory = this.connectionFactory;
+ if (HttpScheme.HTTPS.is(destination.getScheme()))
+ connectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory);
+ org.eclipse.jetty.io.Connection connection = connectionFactory.newConnection(getEndPoint(), context);
+ ClientConnectionFactory.Helper.replaceConnection(this, connection);
+ LOG.debug("SOCKS4 tunnel established: {} over {}", this, connection);
+ }
+ catch (Throwable x)
+ {
+ failed(x);
+ }
+ }
+ }
+}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java
index 03cece3..736a14b 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/api/Destination.java
@@ -63,43 +63,4 @@
* @param promise the promise of a new, unpooled, {@link Connection}
*/
void newConnection(Promise<Connection> promise);
-
- public static class Address
- {
- private final String host;
- private final int port;
-
- public Address(String host, int port)
- {
- this.host = host;
- this.port = port;
- }
-
- public String getHost()
- {
- return host;
- }
-
- public int getPort()
- {
- return port;
- }
-
- @Override
- public boolean equals(Object obj)
- {
- 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;
- }
-
- @Override
- public int hashCode()
- {
- int result = host.hashCode();
- result = 31 * result + port;
- return result;
- }
- }
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ProxyConfiguration.java b/jetty-client/src/main/java/org/eclipse/jetty/client/api/ProxyConfiguration.java
deleted file mode 100644
index 4b84557..0000000
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/api/ProxyConfiguration.java
+++ /dev/null
@@ -1,81 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2013 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.client.api;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * The configuration of the forward proxy to use with {@link org.eclipse.jetty.client.HttpClient}.
- * <p />
- * Configuration parameters include the host and port of the forward proxy, and a list of
- * {@link #getExcludedOrigins() origins} that are excluded from being proxied.
- *
- * @see org.eclipse.jetty.client.HttpClient#setProxyConfiguration(ProxyConfiguration)
- */
-public class ProxyConfiguration
-{
- private final Set<String> excluded = new HashSet<>();
- private final String host;
- private final int port;
-
- public ProxyConfiguration(String host, int port)
- {
- this.host = host;
- this.port = port;
- }
-
- /**
- * @return the host name of the forward proxy
- */
- public String getHost()
- {
- return host;
- }
-
- /**
- * @return the port of the forward proxy
- */
- public int getPort()
- {
- return port;
- }
-
- /**
- * Matches the given {@code host} and {@code port} with the list of excluded origins,
- * returning true if the origin is to be proxied, false if it is excluded from proxying.
- * @param host the host to match
- * @param port the port to match
- * @return true if the origin made of {@code host} and {@code port} is to be proxied,
- * false if it is excluded from proxying.
- */
- public boolean matches(String host, int port)
- {
- String hostPort = host + ":" + port;
- return !getExcludedOrigins().contains(hostPort);
- }
-
- /**
- * @return the list of origins to exclude from proxying, in the form "host:port".
- */
- public Set<String> getExcludedOrigins()
- {
- return excluded;
- }
-}
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 2f752ff..d2383b0 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
@@ -18,16 +18,21 @@
package org.eclipse.jetty.client.http;
+import java.io.IOException;
+import java.util.Map;
+
import org.eclipse.jetty.client.AbstractHttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
-import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.Promise;
public class HttpClientTransportOverHTTP extends AbstractHttpClientTransport
{
public HttpClientTransportOverHTTP()
{
- this(Runtime.getRuntime().availableProcessors());
+ this(Math.max(1, Runtime.getRuntime().availableProcessors() / 2));
}
public HttpClientTransportOverHTTP(int selectors)
@@ -36,21 +41,20 @@
}
@Override
- public HttpDestination newHttpDestination(String scheme, String host, int port)
+ public HttpDestination newHttpDestination(Origin origin)
{
- return new HttpDestinationOverHTTP(getHttpClient(), scheme, host, port);
+ return new HttpDestinationOverHTTP(getHttpClient(), origin);
}
@Override
- protected Connection newConnection(EndPoint endPoint, HttpDestination destination)
+ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
- return new HttpConnectionOverHTTP(endPoint, destination);
- }
- @Override
- public org.eclipse.jetty.client.api.Connection tunnel(org.eclipse.jetty.client.api.Connection connection)
- {
- HttpConnectionOverHTTP httpConnection = (HttpConnectionOverHTTP)connection;
- return tunnel(httpConnection.getEndPoint(), httpConnection.getHttpDestination(), connection);
+ HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
+ HttpConnectionOverHTTP connection = new HttpConnectionOverHTTP(endPoint, destination);
+ @SuppressWarnings("unchecked")
+ Promise<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
+ promise.succeeded(connection);
+ return connection;
}
}
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
index e7f4f19..8419199 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpDestinationOverHTTP.java
@@ -21,30 +21,32 @@
import java.io.IOException;
import java.util.Arrays;
+import org.eclipse.jetty.client.ConnectionPool;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpDestination;
import org.eclipse.jetty.client.HttpExchange;
+import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
-public class HttpDestinationOverHTTP extends HttpDestination implements Promise<Connection>
+public class HttpDestinationOverHTTP extends HttpDestination implements Promise<Connection>
{
- private final HttpConnectionPool connectionPool;
+ private final ConnectionPool connectionPool;
- public HttpDestinationOverHTTP(HttpClient client, String scheme, String host, int port)
+ public HttpDestinationOverHTTP(HttpClient client, Origin origin)
{
- super(client, scheme, host, port);
- this.connectionPool = newHttpConnectionPool(client);
+ super(client, origin);
+ this.connectionPool = newConnectionPool(client);
}
- protected HttpConnectionPool newHttpConnectionPool(HttpClient client)
+ protected ConnectionPool newConnectionPool(HttpClient client)
{
- return new HttpConnectionPool(this, client.getMaxConnectionsPerDestination(), this);
+ return new ConnectionPool(this, client.getMaxConnectionsPerDestination(), this);
}
- public HttpConnectionPool getHttpConnectionPool()
+ public ConnectionPool getConnectionPool()
{
return connectionPool;
}
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
new file mode 100644
index 0000000..b60de3a
--- /dev/null
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientCustomProxyTest.java
@@ -0,0 +1,245 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 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.client;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.AbstractConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConnectionFactory;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HttpClientCustomProxyTest
+{
+ public static final byte[] CAFE_BABE = new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE};
+
+ private Server server;
+ private ServerConnector connector;
+ private HttpClient client;
+
+ public void prepare(Handler handler) throws Exception
+ {
+ server = new Server();
+ connector = new ServerConnector(server, new CAFEBABEServerConnectionFactory(new HttpConnectionFactory()));
+ server.addConnector(connector);
+ server.setHandler(handler);
+ server.start();
+
+ QueuedThreadPool executor = new QueuedThreadPool();
+ executor.setName(executor.getName() + "-client");
+ client = new HttpClient();
+ client.setExecutor(executor);
+ client.start();
+ }
+
+ @After
+ public void dispose() throws Exception
+ {
+ if (client != null)
+ client.stop();
+ if (server != null)
+ server.stop();
+ }
+
+ @Test
+ public void testCustomProxy() throws Exception
+ {
+ final String serverHost = "server";
+ final int status = HttpStatus.NO_CONTENT_204;
+ prepare(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ if (!URI.create(baseRequest.getUri().toString()).isAbsolute())
+ response.setStatus(HttpServletResponse.SC_USE_PROXY);
+ else if (serverHost.equals(request.getServerName()))
+ response.setStatus(status);
+ else
+ response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
+ }
+ });
+
+ // Setup the custom proxy
+ int proxyPort = connector.getLocalPort();
+ int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
+ client.getProxyConfiguration().getProxies().add(new CAFEBABEProxy(new Origin.Address("localhost", proxyPort), false));
+
+ ContentResponse response = client.newRequest(serverHost, serverPort)
+ .timeout(5, TimeUnit.SECONDS)
+ .send();
+
+ Assert.assertEquals(status, response.getStatus());
+ }
+
+ private class CAFEBABEProxy extends ProxyConfiguration.Proxy
+ {
+ private CAFEBABEProxy(Origin.Address address, boolean secure)
+ {
+ super(address, secure);
+ }
+
+ @Override
+ public ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory)
+ {
+ return new CAFEBABEClientConnectionFactory(connectionFactory);
+ }
+ }
+
+ private class CAFEBABEClientConnectionFactory implements ClientConnectionFactory
+ {
+ private final ClientConnectionFactory connectionFactory;
+
+ private CAFEBABEClientConnectionFactory(ClientConnectionFactory connectionFactory)
+ {
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
+ {
+ HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
+ Executor executor = destination.getHttpClient().getExecutor();
+ return new CAFEBABEConnection(endPoint, executor, connectionFactory, context);
+ }
+ }
+
+ private class CAFEBABEConnection extends AbstractConnection
+ {
+ private final ClientConnectionFactory connectionFactory;
+ private final Map<String, Object> context;
+
+ public CAFEBABEConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, Map<String, Object> context)
+ {
+ super(endPoint, executor);
+ this.connectionFactory = connectionFactory;
+ this.context = context;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ fillInterested();
+ getEndPoint().write(new Callback.Adapter(), ByteBuffer.wrap(CAFE_BABE));
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ ByteBuffer buffer = BufferUtil.allocate(4);
+ int filled = getEndPoint().fill(buffer);
+ Assert.assertEquals(4, filled);
+ Assert.assertArrayEquals(CAFE_BABE, buffer.array());
+
+ // We are good, upgrade the connection
+ ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(getEndPoint(), context));
+ }
+ catch (Throwable x)
+ {
+ close();
+ @SuppressWarnings("unchecked")
+ Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
+ promise.failed(x);
+ }
+ }
+ }
+
+ private class CAFEBABEServerConnectionFactory extends AbstractConnectionFactory
+ {
+ private final org.eclipse.jetty.server.ConnectionFactory connectionFactory;
+
+ private CAFEBABEServerConnectionFactory(org.eclipse.jetty.server.ConnectionFactory connectionFactory)
+ {
+ super("cafebabe");
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ public org.eclipse.jetty.io.Connection newConnection(Connector connector, EndPoint endPoint)
+ {
+ return new CAFEBABEServerConnection(connector, endPoint, connectionFactory);
+ }
+ }
+
+ private class CAFEBABEServerConnection extends AbstractConnection
+ {
+ private final org.eclipse.jetty.server.ConnectionFactory connectionFactory;
+
+ public CAFEBABEServerConnection(Connector connector, EndPoint endPoint, org.eclipse.jetty.server.ConnectionFactory connectionFactory)
+ {
+ super(endPoint, connector.getExecutor());
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ fillInterested();
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ ByteBuffer buffer = BufferUtil.allocate(4);
+ int filled = getEndPoint().fill(buffer);
+ Assert.assertEquals(4, filled);
+ Assert.assertArrayEquals(CAFE_BABE, buffer.array());
+ getEndPoint().write(new Callback.Adapter(), buffer);
+
+ // We are good, upgrade the connection
+ ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(connector, getEndPoint()));
+ }
+ catch (Throwable x)
+ {
+ close();
+ }
+ }
+ }
+}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java
index 6b7acc6..7260ae6 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientExplicitConnectionTest.java
@@ -25,7 +25,6 @@
import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
-import org.eclipse.jetty.client.http.HttpConnectionPool;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.toolchain.test.annotation.Slow;
@@ -60,7 +59,7 @@
Assert.assertEquals(200, response.getStatus());
HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination;
- HttpConnectionPool connectionPool = httpDestination.getHttpConnectionPool();
+ ConnectionPool connectionPool = httpDestination.getConnectionPool();
Assert.assertTrue(connectionPool.getActiveConnections().isEmpty());
Assert.assertTrue(connectionPool.getIdleConnections().isEmpty());
}
@@ -92,7 +91,7 @@
Assert.assertFalse(httpConnection.getEndPoint().isOpen());
HttpDestinationOverHTTP httpDestination = (HttpDestinationOverHTTP)destination;
- HttpConnectionPool connectionPool = httpDestination.getHttpConnectionPool();
+ ConnectionPool connectionPool = httpDestination.getConnectionPool();
Assert.assertTrue(connectionPool.getActiveConnections().isEmpty());
Assert.assertTrue(connectionPool.getIdleConnections().isEmpty());
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientLoadTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientLoadTest.java
index 1ac4ae4..41213c2 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientLoadTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientLoadTest.java
@@ -37,7 +37,6 @@
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
-import org.eclipse.jetty.client.http.HttpConnectionPool;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.http.HttpHeader;
@@ -85,7 +84,7 @@
}
// Re-run after warmup
- iterations = 50_000;
+ iterations = 5_000;
for (int i = 0; i < runs; ++i)
{
run(random, iterations);
@@ -111,7 +110,7 @@
for (String host : Arrays.asList("localhost", "127.0.0.1"))
{
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, connector.getLocalPort());
- HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
+ ConnectionPool connectionPool = destination.getConnectionPool();
for (Connection connection : new ArrayList<>(connectionPool.getActiveConnections()))
{
HttpConnectionOverHTTP active = (HttpConnectionOverHTTP)connection;
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java
index 9f7ff33..78d9784 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientProxyTest.java
@@ -27,7 +27,6 @@
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.util.BasicAuthentication;
import org.eclipse.jetty.http.HttpHeader;
@@ -68,7 +67,7 @@
int proxyPort = connector.getLocalPort();
int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
- client.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort));
+ client.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort));
ContentResponse response = client.newRequest(serverHost, serverPort)
.scheme(scheme)
@@ -115,7 +114,7 @@
String proxyHost = "localhost";
int proxyPort = connector.getLocalPort();
int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
- client.setProxyConfiguration(new ProxyConfiguration(proxyHost, proxyPort));
+ client.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
ContentResponse response1 = client.newRequest(serverHost, serverPort)
.scheme(scheme)
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 974dad8..d261af5 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
@@ -51,7 +51,6 @@
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpConnectionOverHTTP;
-import org.eclipse.jetty.client.http.HttpConnectionPool;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BytesContentProvider;
import org.eclipse.jetty.http.HttpField;
@@ -85,7 +84,7 @@
Assert.assertEquals(200, response.getStatus());
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
+ ConnectionPool connectionPool = destination.getConnectionPool();
long start = System.nanoTime();
HttpConnectionOverHTTP connection = null;
@@ -637,7 +636,7 @@
public void onBegin(Request request)
{
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- destination.getHttpConnectionPool().getActiveConnections().peek().close();
+ destination.getConnectionPool().getActiveConnections().peek().close();
}
})
.send(new Response.Listener.Adapter()
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTimeoutTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTimeoutTest.java
index ccb4842..2b5a822 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTimeoutTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTimeoutTest.java
@@ -21,6 +21,7 @@
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
@@ -35,9 +36,13 @@
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
+import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.BufferingResponseListener;
import org.eclipse.jetty.client.util.InputStreamContentProvider;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.io.ssl.SslConnection;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.annotation.Slow;
@@ -244,15 +249,30 @@
client = new HttpClient(new HttpClientTransportOverHTTP()
{
@Override
- protected SslConnection newSslConnection(HttpClient httpClient, EndPoint endPoint, SSLEngine engine)
+ public HttpDestination newHttpDestination(Origin origin)
{
- return new SslConnection(httpClient.getByteBufferPool(), httpClient.getExecutor(), endPoint, engine)
+ return new HttpDestinationOverHTTP(getHttpClient(), origin)
{
@Override
- protected boolean onReadTimeout()
+ protected ClientConnectionFactory newSslClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
- sslIdle.set(true);
- return super.onReadTimeout();
+ HttpClient client = getHttpClient();
+ return new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory)
+ {
+ @Override
+ protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine)
+ {
+ return new SslConnection(byteBufferPool, executor, endPoint, engine)
+ {
+ @Override
+ protected boolean onReadTimeout()
+ {
+ sslIdle.set(true);
+ return super.onReadTimeout();
+ }
+ };
+ }
+ };
}
};
}
diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
index 9fcf5d0..f22bc5e 100644
--- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
+++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpConnectionLifecycleTest.java
@@ -33,7 +33,6 @@
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
-import org.eclipse.jetty.client.http.HttpConnectionPool;
import org.eclipse.jetty.client.http.HttpDestinationOverHTTP;
import org.eclipse.jetty.client.util.ByteBufferContentProvider;
import org.eclipse.jetty.http.HttpHeader;
@@ -68,7 +67,7 @@
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
+ ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@@ -129,7 +128,7 @@
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
+ ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@@ -180,7 +179,7 @@
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
+ ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@@ -240,7 +239,7 @@
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
+ ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@@ -313,7 +312,7 @@
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
+ ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@@ -366,7 +365,7 @@
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
+ ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@@ -416,7 +415,7 @@
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
+ ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
@@ -466,7 +465,7 @@
String host = "localhost";
int port = connector.getLocalPort();
HttpDestinationOverHTTP destination = (HttpDestinationOverHTTP)client.getDestination(scheme, host, port);
- HttpConnectionPool connectionPool = destination.getHttpConnectionPool();
+ ConnectionPool connectionPool = destination.getConnectionPool();
final BlockingQueue<Connection> idleConnections = connectionPool.getIdleConnections();
Assert.assertEquals(0, idleConnections.size());
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 611335f..f7ed4c9 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
@@ -24,6 +24,7 @@
import org.eclipse.jetty.client.AbstractHttpClientServerTest;
import org.eclipse.jetty.client.EmptyServerHandler;
+import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Request;
@@ -52,12 +53,12 @@
@Test
public void test_FirstAcquire_WithEmptyQueue() throws Exception
{
- HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, "http", "localhost", connector.getLocalPort());
+ HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
Connection connection = destination.acquire();
if (connection == null)
{
// There are no queued requests, so the newly created connection will be idle
- connection = destination.getHttpConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
+ connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
}
Assert.assertNotNull(connection);
}
@@ -65,7 +66,7 @@
@Test
public void test_SecondAcquire_AfterFirstAcquire_WithEmptyQueue_ReturnsSameConnection() throws Exception
{
- HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, "http", "localhost", connector.getLocalPort());
+ HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
Connection connection1 = destination.acquire();
if (connection1 == null)
{
@@ -74,7 +75,7 @@
while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5)
{
TimeUnit.MILLISECONDS.sleep(50);
- connection1 = destination.getHttpConnectionPool().getIdleConnections().peek();
+ connection1 = destination.getConnectionPool().getIdleConnections().peek();
}
Assert.assertNotNull(connection1);
@@ -87,7 +88,7 @@
public void test_SecondAcquire_ConcurrentWithFirstAcquire_WithEmptyQueue_CreatesTwoConnections() throws Exception
{
final CountDownLatch latch = new CountDownLatch(1);
- HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, "http", "localhost", connector.getLocalPort())
+ HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()))
{
@Override
protected void process(HttpConnectionOverHTTP connection, boolean dispatch)
@@ -115,23 +116,23 @@
latch.countDown();
// There must be 2 idle connections
- Connection connection = destination.getHttpConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
+ Connection connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
Assert.assertNotNull(connection);
- connection = destination.getHttpConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
+ connection = destination.getConnectionPool().getIdleConnections().poll(5, TimeUnit.SECONDS);
Assert.assertNotNull(connection);
}
@Test
public void test_Acquire_Process_Release_Acquire_ReturnsSameConnection() throws Exception
{
- HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, "http", "localhost", connector.getLocalPort());
+ HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
HttpConnectionOverHTTP connection1 = destination.acquire();
long start = System.nanoTime();
while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5)
{
TimeUnit.MILLISECONDS.sleep(50);
- connection1 = (HttpConnectionOverHTTP)destination.getHttpConnectionPool().getIdleConnections().peek();
+ connection1 = (HttpConnectionOverHTTP)destination.getConnectionPool().getIdleConnections().peek();
}
Assert.assertNotNull(connection1);
@@ -152,7 +153,7 @@
long idleTimeout = 1000;
client.setIdleTimeout(idleTimeout);
- HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, "http", "localhost", connector.getLocalPort());
+ HttpDestinationOverHTTP destination = new HttpDestinationOverHTTP(client, new Origin("http", "localhost", connector.getLocalPort()));
Connection connection1 = destination.acquire();
if (connection1 == null)
{
@@ -161,13 +162,13 @@
while (connection1 == null && TimeUnit.NANOSECONDS.toSeconds(System.nanoTime() - start) < 5)
{
TimeUnit.MILLISECONDS.sleep(50);
- connection1 = destination.getHttpConnectionPool().getIdleConnections().peek();
+ connection1 = destination.getConnectionPool().getIdleConnections().peek();
}
Assert.assertNotNull(connection1);
TimeUnit.MILLISECONDS.sleep(2 * idleTimeout);
- connection1 = destination.getHttpConnectionPool().getIdleConnections().poll();
+ connection1 = destination.getConnectionPool().getIdleConnections().poll();
Assert.assertNull(connection1);
}
}
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
new file mode 100644
index 0000000..075e261
--- /dev/null
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ClientConnectionFactory.java
@@ -0,0 +1,89 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 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.util.Map;
+
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+
+/**
+ * Factory for client-side {@link Connection} instances.
+ */
+public interface ClientConnectionFactory
+{
+ /**
+ *
+ * @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}
+ * @throws IOException if the connection cannot be created
+ */
+ public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException;
+
+ public static class Helper
+ {
+ private static Logger LOG = Log.getLogger(Helper.class);
+
+ private Helper()
+ {
+ }
+
+ /**
+ * Replaces the given {@code oldConnection} with the given {@code newConnection} on the
+ * {@link EndPoint} associated with {@code oldConnection}, performing connection lifecycle management.
+ * <p />
+ * The {@code oldConnection} will be closed by invoking {@link org.eclipse.jetty.io.Connection#onClose()}
+ * and the {@code newConnection} will be opened by invoking {@link org.eclipse.jetty.io.Connection#onOpen()}.
+ * @param oldConnection the old connection to replace
+ * @param newConnection the new connection replacement
+ */
+ public static void replaceConnection(Connection oldConnection, Connection newConnection)
+ {
+ close(oldConnection);
+ oldConnection.getEndPoint().setConnection(newConnection);
+ open(newConnection);
+ }
+
+ private static void open(Connection connection)
+ {
+ try
+ {
+ connection.onOpen();
+ }
+ catch (Throwable x)
+ {
+ LOG.debug(x);
+ }
+ }
+
+ private static void close(Connection connection)
+ {
+ try
+ {
+ connection.onClose();
+ }
+ catch (Throwable x)
+ {
+ LOG.debug(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
new file mode 100644
index 0000000..1289c59
--- /dev/null
+++ b/jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslClientConnectionFactory.java
@@ -0,0 +1,72 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 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.ssl;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import javax.net.ssl.SSLEngine;
+
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+
+public class SslClientConnectionFactory implements ClientConnectionFactory
+{
+ 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";
+
+ private final SslContextFactory sslContextFactory;
+ private final ByteBufferPool byteBufferPool;
+ private final Executor executor;
+ private final ClientConnectionFactory connectionFactory;
+
+ public SslClientConnectionFactory(SslContextFactory sslContextFactory, ByteBufferPool byteBufferPool, Executor executor, ClientConnectionFactory connectionFactory)
+ {
+ this.sslContextFactory = sslContextFactory;
+ this.byteBufferPool = byteBufferPool;
+ this.executor = executor;
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> 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);
+ engine.setUseClientMode(true);
+ context.put(SSL_ENGINE_CONTEXT_KEY, engine);
+
+ SslConnection sslConnection = newSslConnection(byteBufferPool, executor, endPoint, engine);
+ sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
+ endPoint.setConnection(sslConnection);
+ EndPoint appEndPoint = sslConnection.getDecryptedEndPoint();
+ appEndPoint.setConnection(connectionFactory.newConnection(appEndPoint, context));
+
+ return sslConnection;
+ }
+
+ protected SslConnection newSslConnection(ByteBufferPool byteBufferPool, Executor executor, EndPoint endPoint, SSLEngine engine)
+ {
+ return new SslConnection(byteBufferPool, executor, endPoint, engine);
+ }
+}
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 2d356ed..45c871e 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
@@ -48,8 +48,9 @@
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpContentResponse;
+import org.eclipse.jetty.client.HttpProxy;
+import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.ContentResponse;
-import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.client.api.Response;
import org.eclipse.jetty.client.api.Result;
@@ -110,7 +111,7 @@
private HttpClient prepareClient() throws Exception
{
HttpClient result = new HttpClient();
- result.setProxyConfiguration(new ProxyConfiguration("localhost", proxyConnector.getLocalPort()));
+ result.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyConnector.getLocalPort()));
result.start();
return result;
}
@@ -236,7 +237,7 @@
prepareProxy(new ProxyServlet());
HttpClient result = new HttpClient();
- result.setProxyConfiguration(new ProxyConfiguration("localhost", proxyConnector.getLocalPort()));
+ result.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyConnector.getLocalPort()));
QueuedThreadPool threadPool = new QueuedThreadPool();
threadPool.setName("foo");
threadPool.setMaxThreads(20);
@@ -631,7 +632,7 @@
}
});
int port = serverConnector.getLocalPort();
- client.getProxyConfiguration().getExcludedOrigins().add("127.0.0.1:" + port);
+ client.getProxyConfiguration().getProxies().get(0).getExcludedOrigins().add(new Origin("http", "127.0.0.1", port));
// Try with a proxied host
ContentResponse response = client.newRequest("localhost", port)
@@ -865,7 +866,7 @@
}
});
- ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort())
+ client.newRequest("localhost", serverConnector.getLocalPort())
.timeout(5, TimeUnit.SECONDS)
.send();
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java
index 01fa377..d7b55b4 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ProxyTunnellingTest.java
@@ -26,17 +26,16 @@
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
-
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpProxy;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination;
-import org.eclipse.jetty.client.api.ProxyConfiguration;
import org.eclipse.jetty.client.util.FutureResponseListener;
import org.eclipse.jetty.client.util.StringContentProvider;
import org.eclipse.jetty.http.HttpHeader;
@@ -143,7 +142,7 @@
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
- httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort()));
+ httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
httpClient.start();
try
@@ -172,7 +171,7 @@
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
- httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort()));
+ httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
httpClient.start();
try
@@ -215,7 +214,7 @@
startProxy();
final HttpClient httpClient = new HttpClient(sslContextFactory);
- httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort()));
+ httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
httpClient.start();
try
@@ -285,7 +284,7 @@
stopProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
- httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort));
+ httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort));
httpClient.start();
try
@@ -317,7 +316,7 @@
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
- httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort()));
+ httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
httpClient.start();
try
@@ -354,7 +353,7 @@
});
HttpClient httpClient = new HttpClient(sslContextFactory);
- httpClient.setProxyConfiguration(new ProxyConfiguration("localhost", proxyPort()));
+ httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
httpClient.start();
try
@@ -394,7 +393,7 @@
sslContextFactory.start();
HttpClient httpClient = new HttpClient(sslContextFactory);
- httpClient.setProxyConfiguration(new ProxyConfiguration(proxyHost, proxyPort));
+ httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
httpClient.start();
try
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NextProtoNegoClientConnection.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnection.java
similarity index 73%
rename from jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NextProtoNegoClientConnection.java
rename to jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnection.java
index cd745f3..66c1aee 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NextProtoNegoClientConnection.java
+++ b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnection.java
@@ -19,38 +19,36 @@
package org.eclipse.jetty.spdy.client;
import java.io.IOException;
-import java.nio.channels.SocketChannel;
import java.util.List;
-import java.util.concurrent.Executor;
-
+import java.util.Map;
import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.RuntimeIOException;
-import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
import org.eclipse.jetty.npn.NextProtoNego;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
-public class NextProtoNegoClientConnection extends AbstractConnection implements NextProtoNego.ClientProvider
+public class NPNClientConnection extends AbstractConnection implements NextProtoNego.ClientProvider
{
private final Logger LOG = Log.getLogger(getClass());
- private final SocketChannel channel;
- private final Object attachment;
private final SPDYClient client;
+ private final ClientConnectionFactory connectionFactory;
private final SSLEngine engine;
+ private final Map<String, Object> context;
private volatile boolean completed;
- public NextProtoNegoClientConnection(SocketChannel channel, DecryptedEndPoint endPoint, Object attachment, Executor executor, SPDYClient client)
+ public NPNClientConnection(EndPoint endPoint, SPDYClient client, ClientConnectionFactory connectionFactory, SSLEngine sslEngine, Map<String, Object> context)
{
- super(endPoint, executor);
- this.channel = channel;
- this.attachment = attachment;
+ super(endPoint, client.getFactory().getExecutor());
this.client = client;
- this.engine = endPoint.getSslConnection().getSSLEngine();
+ this.connectionFactory = connectionFactory;
+ this.engine = sslEngine;
+ this.context = context;
NextProtoNego.put(engine, this);
}
@@ -97,7 +95,7 @@
{
LOG.debug(x);
NextProtoNego.remove(engine);
- getEndPoint().close();
+ close();
return -1;
}
}
@@ -120,16 +118,22 @@
{
NextProtoNego.remove(engine);
completed = true;
- String protocol = client.selectProtocol(protocols);
- return protocol == null ? null : protocol;
+ return client.selectProtocol(protocols);
}
private void replaceConnection()
{
EndPoint endPoint = getEndPoint();
- Connection connection = client.getConnectionFactory().newConnection(channel, endPoint, attachment);
- endPoint.getConnection().onClose();
- endPoint.setConnection(connection);
- connection.onOpen();
+ try
+ {
+ Connection oldConnection = endPoint.getConnection();
+ Connection newConnection = connectionFactory.newConnection(endPoint, context);
+ ClientConnectionFactory.Helper.replaceConnection(oldConnection, newConnection);
+ }
+ catch (Throwable x)
+ {
+ LOG.debug(x);
+ close();
+ }
}
}
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnectionFactory.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnectionFactory.java
new file mode 100644
index 0000000..7e2d472
--- /dev/null
+++ b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/NPNClientConnectionFactory.java
@@ -0,0 +1,47 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 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.spdy.client;
+
+import java.io.IOException;
+import java.util.Map;
+import javax.net.ssl.SSLEngine;
+
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
+
+public class NPNClientConnectionFactory implements ClientConnectionFactory
+{
+ private final SPDYClient client;
+ private final ClientConnectionFactory connectionFactory;
+
+ public NPNClientConnectionFactory(SPDYClient client, ClientConnectionFactory connectionFactory)
+ {
+ this.client = client;
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
+ {
+ return new NPNClientConnection(endPoint, client, connectionFactory,
+ (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY), context);
+ }
+}
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java
index 80d2286..974b32a 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java
+++ b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java
@@ -19,26 +19,28 @@
package org.eclipse.jetty.spdy.client;
import java.io.IOException;
+import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Collection;
import java.util.Collections;
+import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
-import javax.net.ssl.SSLEngine;
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.MappedByteBufferPool;
import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.io.SelectorManager;
-import org.eclipse.jetty.io.ssl.SslConnection;
-import org.eclipse.jetty.io.ssl.SslConnection.DecryptedEndPoint;
+import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
import org.eclipse.jetty.spdy.FlowControlStrategy;
import org.eclipse.jetty.spdy.api.GoAwayInfo;
import org.eclipse.jetty.spdy.api.Session;
@@ -55,7 +57,7 @@
/**
* A {@link SPDYClient} allows applications to connect to one or more SPDY servers,
* obtaining {@link Session} objects that can be used to send/receive SPDY frames.
- * <p />
+ * <p/>
* {@link SPDYClient} instances are created through a {@link Factory}:
* <pre>
* SPDYClient.Factory factory = new SPDYClient.Factory();
@@ -70,19 +72,113 @@
*/
public class SPDYClient
{
- private final SPDYClientConnectionFactory connectionFactory = new SPDYClientConnectionFactory();
- final short version;
- final Factory factory;
+ private final short version;
+ private final Factory factory;
private volatile SocketAddress bindAddress;
private volatile long idleTimeout = -1;
private volatile int initialWindowSize;
- private volatile boolean executeOnFillable;
+ private volatile boolean dispatchIO;
+ private volatile ClientConnectionFactory connectionFactory;
protected SPDYClient(short version, Factory factory)
{
this.version = version;
this.factory = factory;
setInitialWindowSize(65536);
+ setDispatchIO(true);
+ ClientConnectionFactory connectionFactory = new SPDYClientConnectionFactory();
+ if (factory.sslContextFactory != null)
+ connectionFactory = new SslClientConnectionFactory(factory.getSslContextFactory(), factory.getByteBufferPool(), factory.getExecutor(), new NPNClientConnectionFactory(this, connectionFactory));
+ setClientConnectionFactory(connectionFactory);
+ }
+
+ public short getVersion()
+ {
+ return version;
+ }
+
+ public Factory getFactory()
+ {
+ return factory;
+ }
+
+ /**
+ * Equivalent to:
+ * <pre>
+ * Future<Session> promise = new FuturePromise<>();
+ * connect(address, listener, promise);
+ * </pre>
+ *
+ * @param address the address to connect to
+ * @param listener the session listener that will be notified of session events
+ * @return a {@link Session} when connected
+ */
+ public Session connect(SocketAddress address, SessionFrameListener listener) throws ExecutionException, InterruptedException
+ {
+ FuturePromise<Session> promise = new FuturePromise<>();
+ connect(address, listener, promise);
+ return promise.get();
+ }
+
+ /**
+ * Equivalent to:
+ * <pre>
+ * connect(address, listener, promise, null);
+ * </pre>
+ *
+ * @param address the address to connect to
+ * @param listener the session listener that will be notified of session events
+ * @param promise the promise notified of connection success/failure
+ */
+ public void connect(SocketAddress address, SessionFrameListener listener, Promise<Session> promise)
+ {
+ connect(address, listener, promise, new HashMap<String, Object>());
+ }
+
+ /**
+ * Connects to the given {@code address}, binding the given {@code listener} to session events,
+ * and notified the given {@code promise} of the connect result.
+ * <p/>
+ * If the connect operation is successful, the {@code promise} will be invoked with the {@link Session}
+ * object that applications can use to perform SPDY requests.
+ *
+ * @param address the address to connect to
+ * @param listener the session listener that will be notified of session events
+ * @param promise the promise notified of connection success/failure
+ * @param context a context object passed to the {@link #getClientConnectionFactory() ConnectionFactory}
+ * for the creation of the connection
+ */
+ public void connect(final SocketAddress address, final SessionFrameListener listener, final Promise<Session> promise, Map<String, Object> context)
+ {
+ if (!factory.isStarted())
+ throw new IllegalStateException(Factory.class.getSimpleName() + " is not started");
+
+ try
+ {
+ SocketChannel channel = SocketChannel.open();
+ if (bindAddress != null)
+ channel.bind(bindAddress);
+ configure(channel);
+ channel.configureBlocking(false);
+ channel.connect(address);
+
+ context.put(SslClientConnectionFactory.SSL_PEER_HOST_CONTEXT_KEY, ((InetSocketAddress)address).getHostString());
+ context.put(SslClientConnectionFactory.SSL_PEER_PORT_CONTEXT_KEY, ((InetSocketAddress)address).getPort());
+ context.put(SPDYClientConnectionFactory.SPDY_CLIENT_CONTEXT_KEY, this);
+ context.put(SPDYClientConnectionFactory.SPDY_SESSION_LISTENER_CONTEXT_KEY, listener);
+ context.put(SPDYClientConnectionFactory.SPDY_SESSION_PROMISE_CONTEXT_KEY, promise);
+
+ factory.selector.connect(channel, context);
+ }
+ catch (IOException x)
+ {
+ promise.failed(x);
+ }
+ }
+
+ protected void configure(SocketChannel channel) throws IOException
+ {
+ channel.socket().setTcpNoDelay(true);
}
/**
@@ -103,59 +199,6 @@
this.bindAddress = bindAddress;
}
- /**
- * Equivalent to:
- * <pre>
- * Future<Session> promise = new FuturePromise<>();
- * connect(address, listener, promise);
- * </pre>
- *
- * @param address the address to connect to
- * @param listener the session listener that will be notified of session events
- * @return a {@link Session} when connected
- */
- public Session connect(SocketAddress address, SessionFrameListener listener) throws ExecutionException, InterruptedException
- {
- FuturePromise<Session> promise = new FuturePromise<>();
- connect(address, listener, promise);
- return promise.get();
- }
-
- /**
- * Connects to the given {@code address}, binding the given {@code listener} to session events,
- * and notified the given {@code promise} of the connect result.
- * <p />
- * If the connect operation is successful, the {@code promise} will be invoked with the {@link Session}
- * object that applications can use to perform SPDY requests.
- *
- * @param address the address to connect to
- * @param listener the session listener that will be notified of session events
- * @param promise the promise notified of connection success/failure
- */
- public void connect(SocketAddress address, SessionFrameListener listener, Promise<Session> promise)
- {
- if (!factory.isStarted())
- throw new IllegalStateException(Factory.class.getSimpleName() + " is not started");
-
- try
- {
- SocketChannel channel = SocketChannel.open();
- if (bindAddress != null)
- channel.bind(bindAddress);
- channel.socket().setTcpNoDelay(true);
- channel.configureBlocking(false);
-
- SessionPromise result = new SessionPromise(promise, channel, this, listener);
-
- channel.connect(address);
- factory.selector.connect(channel, result);
- }
- catch (IOException x)
- {
- promise.failed(x);
- }
- }
-
public long getIdleTimeout()
{
return idleTimeout;
@@ -176,14 +219,24 @@
this.initialWindowSize = initialWindowSize;
}
- public boolean isExecuteOnFillable()
+ public boolean isDispatchIO()
{
- return executeOnFillable;
+ return dispatchIO;
}
- public void setExecuteOnFillable(boolean executeOnFillable)
+ public void setDispatchIO(boolean dispatchIO)
{
- this.executeOnFillable = executeOnFillable;
+ this.dispatchIO = dispatchIO;
+ }
+
+ public ClientConnectionFactory getClientConnectionFactory()
+ {
+ return connectionFactory;
+ }
+
+ public void setClientConnectionFactory(ClientConnectionFactory connectionFactory)
+ {
+ this.connectionFactory = connectionFactory;
}
protected String selectProtocol(List<String> serverProtocols)
@@ -197,20 +250,6 @@
return null;
}
- public SPDYClientConnectionFactory getConnectionFactory()
- {
- return connectionFactory;
- }
-
- protected SSLEngine newSSLEngine(SslContextFactory sslContextFactory, SocketChannel channel)
- {
- String peerHost = channel.socket().getInetAddress().getHostName();
- int peerPort = channel.socket().getPort();
- SSLEngine engine = sslContextFactory.newSSLEngine(peerHost, peerPort);
- engine.setUseClientMode(true);
- return engine;
- }
-
protected FlowControlStrategy newFlowControlStrategy()
{
return FlowControlStrategyFactory.newFlowControlStrategy(version);
@@ -225,7 +264,7 @@
private final SslContextFactory sslContextFactory;
private final SelectorManager selector;
private final long idleTimeout;
- private long connectTimeout = 15000;
+ private long connectTimeout;
public Factory()
{
@@ -255,6 +294,7 @@
public Factory(Executor executor, Scheduler scheduler, SslContextFactory sslContextFactory, long idleTimeout)
{
this.idleTimeout = idleTimeout;
+ setConnectTimeout(15000);
if (executor == null)
executor = new QueuedThreadPool();
@@ -290,6 +330,11 @@
return executor;
}
+ public SslContextFactory getSslContextFactory()
+ {
+ return sslContextFactory;
+ }
+
public long getConnectTimeout()
{
return connectTimeout;
@@ -347,89 +392,33 @@
@Override
protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selectSet, SelectionKey key) throws IOException
{
- SessionPromise attachment = (SessionPromise)key.attachment();
-
- long clientIdleTimeout = attachment.client.getIdleTimeout();
+ @SuppressWarnings("unchecked")
+ Map<String, Object> context = (Map<String, Object>)key.attachment();
+ SPDYClient client = (SPDYClient)context.get(SPDYClientConnectionFactory.SPDY_CLIENT_CONTEXT_KEY);
+ long clientIdleTimeout = client.getIdleTimeout();
if (clientIdleTimeout < 0)
clientIdleTimeout = idleTimeout;
-
return new SelectChannelEndPoint(channel, selectSet, key, getScheduler(), clientIdleTimeout);
}
@Override
- public Connection newConnection(final SocketChannel channel, EndPoint endPoint, final Object attachment)
+ public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment) throws IOException
{
- SessionPromise sessionPromise = (SessionPromise)attachment;
- final SPDYClient client = sessionPromise.client;
-
+ @SuppressWarnings("unchecked")
+ Map<String, Object> context = (Map<String, Object>)attachment;
try
{
- if (sslContextFactory != null)
- {
- final SSLEngine engine = client.newSSLEngine(sslContextFactory, channel);
- SslConnection sslConnection = new SslConnection(bufferPool, getExecutor(), endPoint, engine);
- sslConnection.setRenegotiationAllowed(sslContextFactory.isRenegotiationAllowed());
- DecryptedEndPoint sslEndPoint = sslConnection.getDecryptedEndPoint();
- NextProtoNegoClientConnection connection = new NextProtoNegoClientConnection(channel, sslEndPoint, attachment, getExecutor(), client);
- sslEndPoint.setConnection(connection);
- return sslConnection;
- }
-
- SPDYClientConnectionFactory connectionFactory = new SPDYClientConnectionFactory();
- return connectionFactory.newConnection(channel, endPoint, attachment);
+ SPDYClient client = (SPDYClient)context.get(SPDYClientConnectionFactory.SPDY_CLIENT_CONTEXT_KEY);
+ return client.getClientConnectionFactory().newConnection(endPoint, context);
}
- catch (RuntimeException x)
+ catch (Throwable x)
{
- sessionPromise.failed(x);
+ @SuppressWarnings("unchecked")
+ Promise<Session> promise = (Promise<Session>)context.get(SPDYClientConnectionFactory.SPDY_SESSION_PROMISE_CONTEXT_KEY);
+ promise.failed(x);
throw x;
}
}
}
}
-
- static class SessionPromise extends FuturePromise<Session>
- {
- private final SocketChannel channel;
- private final Promise<Session> wrappedPromise;
- final SPDYClient client;
- final SessionFrameListener listener;
-
- private SessionPromise(Promise<Session> promise, SocketChannel channel, SPDYClient client,
- SessionFrameListener listener)
- {
- this.channel = channel;
- this.client = client;
- this.listener = listener;
- this.wrappedPromise = promise;
- }
-
- @Override
- public void succeeded(Session result)
- {
- wrappedPromise.succeeded(result);
- super.succeeded(result);
- }
-
- @Override
- public void failed(Throwable cause)
- {
- wrappedPromise.failed(cause);
- super.failed(cause);
- }
-
- @Override
- public boolean cancel(boolean mayInterruptIfRunning)
- {
- try
- {
- super.cancel(mayInterruptIfRunning);
- channel.close();
- return true;
- }
- catch (IOException x)
- {
- return true;
- }
- }
- }
}
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClientConnectionFactory.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClientConnectionFactory.java
index 18244e8..dae5268 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClientConnectionFactory.java
+++ b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClientConnectionFactory.java
@@ -18,46 +18,54 @@
package org.eclipse.jetty.spdy.client;
-import java.nio.channels.SocketChannel;
+import java.io.IOException;
+import java.util.Map;
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.spdy.CompressionFactory;
import org.eclipse.jetty.spdy.FlowControlStrategy;
import org.eclipse.jetty.spdy.StandardCompressionFactory;
import org.eclipse.jetty.spdy.StandardSession;
+import org.eclipse.jetty.spdy.api.Session;
+import org.eclipse.jetty.spdy.api.SessionFrameListener;
import org.eclipse.jetty.spdy.client.SPDYClient.Factory;
-import org.eclipse.jetty.spdy.client.SPDYClient.SessionPromise;
import org.eclipse.jetty.spdy.generator.Generator;
import org.eclipse.jetty.spdy.parser.Parser;
+import org.eclipse.jetty.util.Promise;
-public class SPDYClientConnectionFactory
+public class SPDYClientConnectionFactory implements ClientConnectionFactory
{
- public Connection newConnection(SocketChannel channel, EndPoint endPoint, Object attachment)
- {
- SessionPromise sessionPromise = (SessionPromise)attachment;
- SPDYClient client = sessionPromise.client;
- Factory factory = client.factory;
- ByteBufferPool bufferPool = factory.getByteBufferPool();
+ public static final String SPDY_CLIENT_CONTEXT_KEY = "spdy.client";
+ public static final String SPDY_SESSION_LISTENER_CONTEXT_KEY = "spdy.session.listener";
+ public static final String SPDY_SESSION_PROMISE_CONTEXT_KEY = "spdy.session.promise";
+ @Override
+ public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
+ {
+ SPDYClient client = (SPDYClient)context.get(SPDY_CLIENT_CONTEXT_KEY);
+ SPDYClient.Factory factory = client.getFactory();
+ ByteBufferPool byteBufferPool = factory.getByteBufferPool();
CompressionFactory compressionFactory = new StandardCompressionFactory();
Parser parser = new Parser(compressionFactory.newDecompressor());
- Generator generator = new Generator(bufferPool, compressionFactory.newCompressor());
+ Generator generator = new Generator(byteBufferPool, compressionFactory.newCompressor());
- SPDYConnection connection = new ClientSPDYConnection(endPoint, bufferPool, parser, factory, client.isExecuteOnFillable());
+ SPDYConnection connection = new ClientSPDYConnection(endPoint, byteBufferPool, parser, factory, client.isDispatchIO());
FlowControlStrategy flowControlStrategy = client.newFlowControlStrategy();
- StandardSession session = new StandardSession(client.version, bufferPool, factory.getExecutor(),
- factory.getScheduler(), connection, endPoint, connection, 1, sessionPromise.listener, generator,
- flowControlStrategy);
+ SessionFrameListener listener = (SessionFrameListener)context.get(SPDY_SESSION_LISTENER_CONTEXT_KEY);
+ StandardSession session = new StandardSession(client.getVersion(), byteBufferPool, factory.getExecutor(),
+ factory.getScheduler(), connection, endPoint, connection, 1, listener, generator, flowControlStrategy);
session.setWindowSize(client.getInitialWindowSize());
parser.addListener(session);
- sessionPromise.succeeded(session);
connection.setSession(session);
- factory.sessionOpened(session);
+ @SuppressWarnings("unchecked")
+ Promise<Session> promise = (Promise<Session>)context.get(SPDY_SESSION_PROMISE_CONTEXT_KEY);
+ promise.succeeded(session);
return connection;
}
@@ -66,14 +74,20 @@
{
private final Factory factory;
- public ClientSPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Factory factory,
- boolean executeOnFillable)
+ public ClientSPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Factory factory, boolean dispatchIO)
{
- super(endPoint, bufferPool, parser, factory.getExecutor(), executeOnFillable);
+ super(endPoint, bufferPool, parser, factory.getExecutor(), dispatchIO);
this.factory = factory;
}
@Override
+ public void onOpen()
+ {
+ super.onOpen();
+ factory.sessionOpened(getSession());
+ }
+
+ @Override
public void onClose()
{
super.onClose();
diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYConnection.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYConnection.java
index e1217a5..b66edba 100644
--- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYConnection.java
+++ b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYConnection.java
@@ -44,26 +44,20 @@
private volatile ISession session;
private volatile boolean idle = false;
- public SPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Executor executor,
- boolean executeOnFillable)
+ public SPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Executor executor, boolean dispatchIO)
{
- this(endPoint, bufferPool, parser, executor, executeOnFillable, 8192);
+ this(endPoint, bufferPool, parser, executor, dispatchIO, 8192);
}
- public SPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Executor executor,
- boolean executeOnFillable, int bufferSize)
+ public SPDYConnection(EndPoint endPoint, ByteBufferPool bufferPool, Parser parser, Executor executor, boolean dispatchIO, int bufferSize)
{
// Since SPDY is multiplexed, onFillable() must never block while calling application code. In fact,
// the SPDY code always dispatches to a new thread when calling application code,
- // so here we can safely pass false as last parameter, and avoid to dispatch to onFillable(). The IO
- // operation (read, parse, etc.) will not block and will be fast in almost all cases. Big uploads to a server
- // however might block the Selector thread for a long time and therefore block other connections to be read.
- // This might be a good reason to set executeOnFillable to true.
- //
- // Due to a jvm bug we've had a Selector thread being stuck at
- // sun.nio.ch.FileDispatcherImpl.preClose0(Native Method). That's why we now default executeOnFillable to
- // true even if for most use cases it is faster to not dispatch the IO events.
- super(endPoint, executor, executeOnFillable);
+ // so here we can safely pass false as last parameter, and avoid to dispatch to onFillable().
+ // The IO operation (read, parse, etc.) will not block and will be fast in almost all cases.
+ // Big uploads to a server, however, might occupy the Selector thread for a long time and
+ // therefore starve other connections, so by default dispatchIO is true.
+ super(endPoint, executor, dispatchIO);
this.bufferPool = bufferPool;
this.parser = parser;
onIdle(true);
diff --git a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpClientTransportOverSPDY.java b/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpClientTransportOverSPDY.java
index b2fb3a6..813f550 100644
--- a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpClientTransportOverSPDY.java
+++ b/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpClientTransportOverSPDY.java
@@ -18,12 +18,17 @@
package org.eclipse.jetty.spdy.client.http;
+import java.io.IOException;
import java.net.SocketAddress;
+import java.util.Map;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpClientTransport;
import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.SessionFrameListener;
import org.eclipse.jetty.spdy.client.SPDYClient;
@@ -32,11 +37,22 @@
public class HttpClientTransportOverSPDY implements HttpClientTransport
{
private final SPDYClient client;
- private volatile HttpClient httpClient;
+ private final ClientConnectionFactory connectionFactory;
+ private HttpClient httpClient;
public HttpClientTransportOverSPDY(SPDYClient client)
{
this.client = client;
+ this.connectionFactory = client.getClientConnectionFactory();
+ client.setClientConnectionFactory(new ClientConnectionFactory()
+ {
+ @Override
+ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
+ {
+ HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
+ return destination.getClientConnectionFactory().newConnection(endPoint, context);
+ }
+ });
}
@Override
@@ -46,51 +62,46 @@
}
@Override
- public HttpDestination newHttpDestination(String scheme, String host, int port)
+ public HttpDestination newHttpDestination(Origin origin)
{
- return new HttpDestinationOverSPDY(httpClient, scheme, host, port);
+ return new HttpDestinationOverSPDY(httpClient, origin);
}
@Override
- public void connect(final HttpDestination destination, SocketAddress address, final Promise<Connection> promise)
+ public void connect(SocketAddress address, Map<String, Object> context)
{
+ final HttpDestination destination = (HttpDestination)context.get(HTTP_DESTINATION_CONTEXT_KEY);
+ @SuppressWarnings("unchecked")
+ final Promise<Connection> promise = (Promise<Connection>)context.get(HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
+
SessionFrameListener.Adapter listener = new SessionFrameListener.Adapter()
{
@Override
public void onFailure(Session session, Throwable x)
{
- // TODO: is this correct ?
- // TODO: if I get a stream error (e.g. invalid response headers)
- // TODO: I must abort the *current* exchange, while below I will abort
- // TODO: the queued exchanges only.
- // TODO: The problem is that a single destination/connection multiplexes
- // TODO: several exchanges, so I would need to cancel them all,
- // TODO: or only the one that failed ?
destination.abort(x);
}
};
client.connect(address, listener, new Promise<Session>()
- {
- @Override
- public void succeeded(Session session)
- {
- Connection result = new HttpConnectionOverSPDY(destination, session);
- promise.succeeded(result);
- }
+ {
+ @Override
+ public void succeeded(Session session)
+ {
+ promise.succeeded(new HttpConnectionOverSPDY(destination, session));
+ }
- @Override
- public void failed(Throwable x)
- {
- promise.failed(x);
- }
- }
- );
+ @Override
+ public void failed(Throwable x)
+ {
+ promise.failed(x);
+ }
+ }, context);
}
@Override
- public Connection tunnel(Connection connection)
+ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{
- throw new UnsupportedOperationException();
+ return connectionFactory.newConnection(endPoint, context);
}
}
diff --git a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpDestinationOverSPDY.java b/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpDestinationOverSPDY.java
index 321fb99..10ff922 100644
--- a/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpDestinationOverSPDY.java
+++ b/jetty-spdy/spdy-http-client-transport/src/main/java/org/eclipse/jetty/spdy/client/http/HttpDestinationOverSPDY.java
@@ -21,12 +21,13 @@
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpExchange;
import org.eclipse.jetty.client.MultiplexHttpDestination;
+import org.eclipse.jetty.client.Origin;
public class HttpDestinationOverSPDY extends MultiplexHttpDestination<HttpConnectionOverSPDY>
{
- public HttpDestinationOverSPDY(HttpClient client, String scheme, String host, int port)
+ public HttpDestinationOverSPDY(HttpClient client, Origin origin)
{
- super(client, scheme, host, port);
+ super(client, origin);
}
@Override
@@ -34,4 +35,12 @@
{
connection.send(exchange);
}
+
+ @Override
+ public void abort(Throwable cause)
+ {
+ // TODO: in case of connection failure, we need to abort also
+ // TODO: all pending exchanges, so we need to track them.
+ super.abort(cause);
+ }
}
diff --git a/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/HttpClientCustomProxyTest.java b/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/HttpClientCustomProxyTest.java
new file mode 100644
index 0000000..7b06d39
--- /dev/null
+++ b/jetty-spdy/spdy-http-client-transport/src/test/java/org/eclipse/jetty/spdy/client/http/HttpClientCustomProxyTest.java
@@ -0,0 +1,261 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2013 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.spdy.client.http;
+
+import java.io.IOException;
+import java.net.URI;
+import java.nio.ByteBuffer;
+import java.util.Map;
+import java.util.concurrent.Executor;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.client.HttpClient;
+import org.eclipse.jetty.client.HttpClientTransport;
+import org.eclipse.jetty.client.HttpDestination;
+import org.eclipse.jetty.client.Origin;
+import org.eclipse.jetty.client.ProxyConfiguration;
+import org.eclipse.jetty.client.api.Connection;
+import org.eclipse.jetty.client.api.ContentResponse;
+import org.eclipse.jetty.http.HttpStatus;
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.ClientConnectionFactory;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.AbstractConnectionFactory;
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConfiguration;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.spdy.api.SPDY;
+import org.eclipse.jetty.spdy.client.SPDYClient;
+import org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory;
+import org.eclipse.jetty.spdy.server.http.PushStrategy;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.Promise;
+import org.eclipse.jetty.util.thread.QueuedThreadPool;
+import org.junit.After;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class HttpClientCustomProxyTest
+{
+ public static final byte[] CAFE_BABE = new byte[]{(byte)0xCA, (byte)0xFE, (byte)0xBA, (byte)0xBE};
+
+ private Server server;
+ private ServerConnector connector;
+ private SPDYClient.Factory factory;
+ private HttpClient httpClient;
+
+ public void prepare(Handler handler) throws Exception
+ {
+ server = new Server();
+ connector = new ServerConnector(server, new CAFEBABEServerConnectionFactory(new HTTPSPDYServerConnectionFactory(SPDY.V3, new HttpConfiguration(), new PushStrategy.None())));
+ server.addConnector(connector);
+ server.setHandler(handler);
+ server.start();
+
+ QueuedThreadPool executor = new QueuedThreadPool();
+ executor.setName(executor.getName() + "-client");
+
+ factory = new SPDYClient.Factory(executor);
+ factory.start();
+
+ httpClient = new HttpClient(new HttpClientTransportOverSPDY(factory.newSPDYClient(SPDY.V3)), null);
+ httpClient.setExecutor(executor);
+ httpClient.start();
+ }
+
+ @After
+ public void dispose() throws Exception
+ {
+ if (httpClient != null)
+ httpClient.stop();
+ if (factory != null)
+ factory.stop();
+ if (server != null)
+ server.stop();
+ }
+
+ @Test
+ public void testCustomProxy() throws Exception
+ {
+ final String serverHost = "server";
+ final int status = HttpStatus.NO_CONTENT_204;
+ prepare(new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ baseRequest.setHandled(true);
+ if (!URI.create(baseRequest.getUri().toString()).isAbsolute())
+ response.setStatus(HttpServletResponse.SC_USE_PROXY);
+ else if (serverHost.equals(request.getServerName()))
+ response.setStatus(status);
+ else
+ response.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
+ }
+ });
+
+ // Setup the custom proxy
+ int proxyPort = connector.getLocalPort();
+ int serverPort = proxyPort + 1; // Any port will do for these tests - just not the same as the proxy
+ httpClient.getProxyConfiguration().getProxies().add(new CAFEBABEProxy(new Origin.Address("localhost", proxyPort), false));
+
+ ContentResponse response = httpClient.newRequest(serverHost, serverPort)
+ .timeout(5, TimeUnit.SECONDS)
+ .send();
+
+ Assert.assertEquals(status, response.getStatus());
+ }
+
+ private class CAFEBABEProxy extends ProxyConfiguration.Proxy
+ {
+ private CAFEBABEProxy(Origin.Address address, boolean secure)
+ {
+ super(address, secure);
+ }
+
+ @Override
+ public ClientConnectionFactory newClientConnectionFactory(ClientConnectionFactory connectionFactory)
+ {
+ return new CAFEBABEClientConnectionFactory(connectionFactory);
+ }
+ }
+
+ private static class CAFEBABEClientConnectionFactory implements ClientConnectionFactory
+ {
+ private final ClientConnectionFactory connectionFactory;
+
+ private CAFEBABEClientConnectionFactory(ClientConnectionFactory connectionFactory)
+ {
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ public org.eclipse.jetty.io.Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
+ {
+ HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY);
+ Executor executor = destination.getHttpClient().getExecutor();
+ return new CAFEBABEConnection(endPoint, executor, connectionFactory, context);
+ }
+ }
+
+ private static class CAFEBABEConnection extends AbstractConnection
+ {
+ private final ClientConnectionFactory connectionFactory;
+ private final Map<String, Object> context;
+
+ public CAFEBABEConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, Map<String, Object> context)
+ {
+ super(endPoint, executor);
+ this.connectionFactory = connectionFactory;
+ this.context = context;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ fillInterested();
+ getEndPoint().write(new Callback.Adapter(), ByteBuffer.wrap(CAFE_BABE));
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ ByteBuffer buffer = BufferUtil.allocate(4);
+ int filled = getEndPoint().fill(buffer);
+ Assert.assertEquals(4, filled);
+ Assert.assertArrayEquals(CAFE_BABE, buffer.array());
+
+ // We are good, upgrade the connection
+ ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(getEndPoint(), context));
+ }
+ catch (Throwable x)
+ {
+ close();
+ @SuppressWarnings("unchecked")
+ Promise<Connection> promise = (Promise<Connection>)context.get(HttpClientTransport.HTTP_CONNECTION_PROMISE_CONTEXT_KEY);
+ promise.failed(x);
+ }
+ }
+ }
+
+ private class CAFEBABEServerConnectionFactory extends AbstractConnectionFactory
+ {
+ private final org.eclipse.jetty.server.ConnectionFactory connectionFactory;
+
+ private CAFEBABEServerConnectionFactory(org.eclipse.jetty.server.ConnectionFactory connectionFactory)
+ {
+ super("cafebabe");
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ public org.eclipse.jetty.io.Connection newConnection(Connector connector, EndPoint endPoint)
+ {
+ return new CAFEBABEServerConnection(connector, endPoint, connectionFactory);
+ }
+ }
+
+ private class CAFEBABEServerConnection extends AbstractConnection
+ {
+ private final org.eclipse.jetty.server.ConnectionFactory connectionFactory;
+
+ public CAFEBABEServerConnection(Connector connector, EndPoint endPoint, org.eclipse.jetty.server.ConnectionFactory connectionFactory)
+ {
+ super(endPoint, connector.getExecutor());
+ this.connectionFactory = connectionFactory;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ fillInterested();
+ }
+
+ @Override
+ public void onFillable()
+ {
+ try
+ {
+ ByteBuffer buffer = BufferUtil.allocate(4);
+ int filled = getEndPoint().fill(buffer);
+ Assert.assertEquals(4, filled);
+ Assert.assertArrayEquals(CAFE_BABE, buffer.array());
+ getEndPoint().write(new Callback.Adapter(), buffer);
+
+ // We are good, upgrade the connection
+ ClientConnectionFactory.Helper.replaceConnection(this, connectionFactory.newConnection(connector, getEndPoint()));
+ }
+ catch (Throwable x)
+ {
+ close();
+ }
+ }
+ }
+}
diff --git a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnectionFactory.java b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnectionFactory.java
index 5e6815b..147c03c 100644
--- a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnectionFactory.java
+++ b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnectionFactory.java
@@ -42,14 +42,10 @@
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
-import org.eclipse.jetty.util.log.Log;
-import org.eclipse.jetty.util.log.Logger;
@ManagedObject("SPDY Server Connection Factory")
public class SPDYServerConnectionFactory extends AbstractConnectionFactory
{
- private static final Logger LOG = Log.getLogger(SPDYServerConnectionFactory.class);
-
// This method is placed here so as to provide a check for NPN before attempting to load any
// NPN classes.
public static void checkNPNAvailable()
@@ -66,11 +62,11 @@
}
}
+ private final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
private final short version;
private final ServerSessionFrameListener listener;
private int initialWindowSize;
- private boolean executeOnFillable = true;
- private final Queue<Session> sessions = new ConcurrentLinkedQueue<>();
+ private boolean dispatchIO;
public SPDYServerConnectionFactory(int version)
{
@@ -83,6 +79,7 @@
this.version = (short)version;
this.listener = listener;
setInitialWindowSize(65536);
+ setDispatchIO(true);
}
@ManagedAttribute("SPDY version")
@@ -105,14 +102,14 @@
ServerSessionFrameListener listener = provideServerSessionFrameListener(connector, endPoint);
SPDYConnection connection = new ServerSPDYConnection(connector, endPoint, parser, listener,
- executeOnFillable, getInputBufferSize());
+ isDispatchIO(), getInputBufferSize());
FlowControlStrategy flowControlStrategy = newFlowControlStrategy(version);
StandardSession session = new StandardSession(getVersion(), connector.getByteBufferPool(),
connector.getExecutor(), connector.getScheduler(), connection, endPoint, connection, 2, listener,
generator, flowControlStrategy);
- session.setWindowSize(initialWindowSize);
+ session.setWindowSize(getInitialWindowSize());
parser.addListener(session);
connection.setSession(session);
@@ -142,15 +139,15 @@
this.initialWindowSize = initialWindowSize;
}
- @ManagedAttribute("Execute onFillable")
- public boolean isExecuteOnFillable()
+ @ManagedAttribute("Dispatch I/O to a pooled thread")
+ public boolean isDispatchIO()
{
- return executeOnFillable;
+ return dispatchIO;
}
- public void setExecuteOnFillable(boolean executeOnFillable)
+ public void setDispatchIO(boolean dispatchIO)
{
- this.executeOnFillable = executeOnFillable;
+ this.dispatchIO = dispatchIO;
}
protected boolean sessionOpened(Session session)
@@ -191,10 +188,10 @@
private final AtomicBoolean connected = new AtomicBoolean();
private ServerSPDYConnection(Connector connector, EndPoint endPoint, Parser parser,
- ServerSessionFrameListener listener, boolean executeOnFillable, int bufferSize)
+ ServerSessionFrameListener listener, boolean dispatchIO, int bufferSize)
{
super(endPoint, connector.getByteBufferPool(), parser, connector.getExecutor(),
- executeOnFillable, bufferSize);
+ dispatchIO, bufferSize);
this.listener = listener;
}