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 7d956887c40..8e166001898 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 @@ -159,7 +159,7 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable public Future newConnection() { FuturePromise result = new FuturePromise<>(); - newConnection(new CONNECTPromise(result)); + newConnection(new ProxyPromise(result)); return result; } @@ -191,7 +191,8 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable { LOG.debug("Creating connection {}/{} for {}", next, maxConnections, this); - CONNECTPromise connectPromise = new CONNECTPromise(new Promise() + // This is the promise that is being called when a connection (eventually proxied) succeeds or fails. + Promise promise = new Promise() { @Override public void succeeded(Connection connection) @@ -211,8 +212,29 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable } }); } + }; + + // Create a new connection, and pass a ProxyPromise to establish a proxy tunnel, if needed. + // Differently from the case where the connection is created explicitly by applications, here + // we need to do a bit more logging and keep track of the connection count in case of failures. + newConnection(new ProxyPromise(promise) + { + @Override + public void succeeded(Connection connection) + { + LOG.debug("Created connection {}/{} {} for {}", next, maxConnections, connection, HttpDestination.this); + super.succeeded(connection); + } + + @Override + public void failed(Throwable x) + { + LOG.debug("Connection failed {} for {}", x, HttpDestination.this); + connectionCount.decrementAndGet(); + super.failed(x); + } }); - newConnection(new TCPPromise(next, maxConnections, connectPromise)); + // Try again the idle connections return idleConnections.poll(); } @@ -423,40 +445,17 @@ public class HttpDestination implements Destination, AutoCloseable, Dumpable } } - private class TCPPromise implements Promise - { - private final int current; - private final int max; - private final Promise delegate; - - private TCPPromise(int current, int max, Promise delegate) - { - this.current = current; - this.max = max; - this.delegate = delegate; - } - - @Override - public void succeeded(Connection connection) - { - LOG.debug("Created connection {}/{} {} for {}", current, max, connection, HttpDestination.this); - delegate.succeeded(connection); - } - - @Override - public void failed(Throwable x) - { - LOG.debug("Connection failed {} for {}", x, HttpDestination.this); - connectionCount.decrementAndGet(); - delegate.failed(x); - } - } - - private class CONNECTPromise implements Promise + /** + * 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 { private final Promise delegate; - private CONNECTPromise(Promise delegate) + private ProxyPromise(Promise delegate) { this.delegate = delegate; }