Issue #416 (Support HTTPS forward proxies)

Implemented. We were never checking HttpProxy.isSecure().
Now we do, and if so we wrap the connection factory with an SSL one.
This commit is contained in:
Simone Bordet 2016-03-10 10:00:57 +01:00
parent ead37b1b57
commit ffbd817254
3 changed files with 78 additions and 57 deletions

View File

@ -73,6 +73,8 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
if (proxy != null)
{
connectionFactory = proxy.newClientConnectionFactory(connectionFactory);
if (proxy.isSecure())
connectionFactory = newSslClientConnectionFactory(connectionFactory);
}
else
{

View File

@ -63,7 +63,7 @@ public class HttpProxy extends ProxyConfiguration.Proxy
return URI.create(new Origin(scheme, getAddress()).asString());
}
public static class HttpProxyClientConnectionFactory implements ClientConnectionFactory
private static class HttpProxyClientConnectionFactory implements ClientConnectionFactory
{
private static final Logger LOG = Log.getLogger(HttpProxyClientConnectionFactory.class);
private final ClientConnectionFactory connectionFactory;
@ -140,7 +140,6 @@ public class HttpProxy extends ProxyConfiguration.Proxy
HttpClient httpClient = destination.getHttpClient();
long connectTimeout = httpClient.getConnectTimeout();
Request connect = httpClient.newRequest(proxyAddress.getHost(), proxyAddress.getPort())
.scheme(HttpScheme.HTTP.asString())
.method(HttpMethod.CONNECT)
.path(target)
.header(HttpHeader.HOST, target)

View File

@ -34,6 +34,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.client.HttpClient;
import org.eclipse.jetty.client.HttpProxy;
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.Destination;
@ -54,6 +55,7 @@ import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.TestTracker;
import org.eclipse.jetty.util.Promise;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
@ -61,34 +63,39 @@ import org.junit.Assume;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import static org.junit.Assert.assertEquals;
public class ProxyTunnellingTest
@RunWith(Parameterized.class)
public class ForwardProxyTLSServerTest
{
@Rule
public TestTracker tracker = new TestTracker();
@Parameterized.Parameters
public static Object[] parameters()
{
return new Object[]{null, newSslContextFactory()};
}
private SslContextFactory sslContextFactory;
@Rule
public final TestTracker tracker = new TestTracker();
private final SslContextFactory proxySslContextFactory;
private Server server;
private ServerConnector serverConnector;
private Server proxy;
private ServerConnector proxyConnector;
protected int proxyPort()
public ForwardProxyTLSServerTest(SslContextFactory proxySslContextFactory)
{
return proxyConnector.getLocalPort();
this.proxySslContextFactory = proxySslContextFactory;
}
protected void startSSLServer(Handler handler) throws Exception
protected void startTLSServer(Handler handler) throws Exception
{
sslContextFactory = new SslContextFactory();
String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath();
sslContextFactory.setKeyStorePath(keyStorePath);
sslContextFactory.setKeyStorePassword("storepwd");
sslContextFactory.setKeyManagerPassword("keypwd");
server = new Server();
serverConnector = new ServerConnector(server, sslContextFactory);
QueuedThreadPool serverThreads = new QueuedThreadPool();
serverThreads.setName("server");
server = new Server(serverThreads);
serverConnector = new ServerConnector(server, newSslContextFactory());
server.addConnector(serverConnector);
server.setHandler(handler);
server.start();
@ -101,8 +108,10 @@ public class ProxyTunnellingTest
protected void startProxy(ConnectHandler connectHandler) throws Exception
{
proxy = new Server();
proxyConnector = new ServerConnector(proxy);
QueuedThreadPool proxyThreads = new QueuedThreadPool();
proxyThreads.setName("proxy");
proxy = new Server(proxyThreads);
proxyConnector = new ServerConnector(proxy, proxySslContextFactory);
proxy.addConnector(proxyConnector);
// Under Windows, it takes a while to detect that a connection
// attempt fails, so use an explicit timeout
@ -111,6 +120,21 @@ public class ProxyTunnellingTest
proxy.start();
}
protected HttpProxy newHttpProxy()
{
return new HttpProxy(new Origin.Address("localhost", proxyConnector.getLocalPort()), proxySslContextFactory != null);
}
private static SslContextFactory newSslContextFactory()
{
SslContextFactory sslContextFactory = new SslContextFactory();
String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath();
sslContextFactory.setKeyStorePath(keyStorePath);
sslContextFactory.setKeyStorePassword("storepwd");
sslContextFactory.setKeyManagerPassword("keypwd");
return sslContextFactory;
}
@After
public void stop() throws Exception
{
@ -137,13 +161,13 @@ public class ProxyTunnellingTest
}
@Test
public void testOneExchangeViaSSL() throws Exception
public void testOneExchange() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
httpClient.start();
try
@ -168,13 +192,13 @@ public class ProxyTunnellingTest
}
@Test
public void testTwoExchangesViaSSL() throws Exception
public void testTwoExchanges() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
httpClient.start();
try
@ -211,13 +235,13 @@ public class ProxyTunnellingTest
}
@Test
public void testTwoConcurrentExchangesViaSSL() throws Exception
public void testTwoConcurrentExchanges() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy();
final HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
final HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
httpClient.start();
try
@ -229,22 +253,18 @@ public class ProxyTunnellingTest
.scheme(HttpScheme.HTTPS.asString())
.method(HttpMethod.GET)
.path("/echo?body=" + URLEncoder.encode(body1, "UTF-8"))
.onRequestCommit(new org.eclipse.jetty.client.api.Request.CommitListener()
.onRequestCommit(request ->
{
@Override
public void onCommit(org.eclipse.jetty.client.api.Request request)
Destination destination = httpClient.getDestination(HttpScheme.HTTPS.asString(), "localhost", serverConnector.getLocalPort());
destination.newConnection(new Promise.Adapter<Connection>()
{
Destination destination = httpClient.getDestination(HttpScheme.HTTPS.asString(), "localhost", serverConnector.getLocalPort());
destination.newConnection(new Promise.Adapter<Connection>()
@Override
public void succeeded(Connection result)
{
@Override
public void succeeded(Connection result)
{
connection.set(result);
connectionLatch.countDown();
}
});
}
connection.set(result);
connectionLatch.countDown();
}
});
})
.send();
@ -284,7 +304,7 @@ public class ProxyTunnellingTest
// Short idle timeout for HttpClient.
long idleTimeout = 500;
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy(new ConnectHandler()
{
@Override
@ -303,8 +323,8 @@ public class ProxyTunnellingTest
}
});
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
// Short idle timeout for HttpClient.
httpClient.setIdleTimeout(idleTimeout);
httpClient.start();
@ -334,13 +354,13 @@ public class ProxyTunnellingTest
@Test
public void testProxyDown() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy();
int proxyPort = proxyPort();
int proxyPort = proxyConnector.getLocalPort();
stopProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(new Origin.Address("localhost", proxyPort), proxySslContextFactory != null));
httpClient.start();
try
@ -366,13 +386,13 @@ public class ProxyTunnellingTest
@Test
public void testServerDown() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
int serverPort = serverConnector.getLocalPort();
stopServer();
startProxy();
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
httpClient.start();
try
@ -398,7 +418,7 @@ public class ProxyTunnellingTest
@Test
public void testProxyClosesConnection() throws Exception
{
startSSLServer(new ServerHandler());
startTLSServer(new ServerHandler());
startProxy(new ConnectHandler()
{
@Override
@ -408,8 +428,8 @@ public class ProxyTunnellingTest
}
});
HttpClient httpClient = new HttpClient(sslContextFactory);
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy("localhost", proxyPort()));
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(newHttpProxy());
httpClient.start();
try
@ -448,7 +468,7 @@ public class ProxyTunnellingTest
SslContextFactory sslContextFactory = new SslContextFactory();
sslContextFactory.start();
HttpClient httpClient = new HttpClient(sslContextFactory);
HttpClient httpClient = new HttpClient(newSslContextFactory());
httpClient.getProxyConfiguration().getProxies().add(new HttpProxy(proxyHost, proxyPort));
httpClient.start();