package org.eclipse.jetty.client; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.IOException; import java.net.URLEncoder; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpHeaders; import org.eclipse.jetty.http.HttpMethods; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.ConnectHandler; import org.eclipse.jetty.server.nio.SelectChannelConnector; import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.After; import org.junit.Test; public class ProxyTunnellingTest { private Server server; private Connector serverConnector; private Server proxy; private Connector proxyConnector; private int serverConnectTimeout = 1000; protected int proxyPort() { return proxyConnector.getLocalPort(); } protected void startSSLServer(Handler handler) throws Exception { SslSelectChannelConnector connector = new SslSelectChannelConnector(); String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath(); SslContextFactory cf = connector.getSslContextFactory(); cf.setKeyStorePath(keyStorePath); cf.setKeyStorePassword("storepwd"); cf.setKeyManagerPassword("keypwd"); startServer(connector, handler); } protected void startServer(Connector connector, Handler handler) throws Exception { server = new Server(); serverConnector = connector; server.addConnector(serverConnector); server.setHandler(handler); server.start(); } protected void startProxy() throws Exception { proxy = new Server(); proxyConnector = new SelectChannelConnector(); proxy.addConnector(proxyConnector); ConnectHandler connectHandler = new ConnectHandler(); // Under Windows, it takes a while to detect that a connection // attempt fails, so use an explicit timeout connectHandler.setConnectTimeout(serverConnectTimeout); proxy.setHandler(connectHandler); proxy.start(); } @After public void stop() throws Exception { stopProxy(); stopServer(); } protected void stopServer() throws Exception { server.stop(); server.join(); } protected void stopProxy() throws Exception { proxy.stop(); proxy.join(); } @Test public void testOneMessageSSL() throws Exception { startSSLServer(new ServerHandler()); startProxy(); HttpClient httpClient = new HttpClient(); httpClient.setProxy(new Address("localhost", proxyPort())); httpClient.start(); try { ContentExchange exchange = new ContentExchange(true); exchange.setMethod(HttpMethods.GET); String body = "BODY"; exchange.setURL("https://localhost:" + serverConnector.getLocalPort() + "/echo?body=" + URLEncoder.encode(body, "UTF-8")); httpClient.send(exchange); assertEquals(HttpExchange.STATUS_COMPLETED, exchange.waitForDone()); String content = exchange.getResponseContent(); assertEquals(body, content); } finally { httpClient.stop(); } } @Test public void testTwoMessagesSSL() throws Exception { startSSLServer(new ServerHandler()); startProxy(); HttpClient httpClient = new HttpClient(); httpClient.setProxy(new Address("localhost", proxyPort())); httpClient.start(); try { ContentExchange exchange = new ContentExchange(true); exchange.setMethod(HttpMethods.GET); String body = "BODY"; exchange.setURL("https://localhost:" + serverConnector.getLocalPort() + "/echo?body=" + URLEncoder.encode(body, "UTF-8")); httpClient.send(exchange); assertEquals(HttpExchange.STATUS_COMPLETED, exchange.waitForDone()); String content = exchange.getResponseContent(); assertEquals(body, content); exchange = new ContentExchange(true); exchange.setMethod(HttpMethods.POST); exchange.setURL("https://localhost:" + serverConnector.getLocalPort() + "/echo"); exchange.setRequestHeader(HttpHeaders.CONTENT_TYPE, MimeTypes.FORM_ENCODED); content = "body=" + body; exchange.setRequestHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(content.length())); exchange.setRequestContent(new ByteArrayBuffer(content, "UTF-8")); httpClient.send(exchange); assertEquals(HttpExchange.STATUS_COMPLETED, exchange.waitForDone()); content = exchange.getResponseContent(); assertEquals(body, content); } finally { httpClient.stop(); } } @Test public void testProxyDown() throws Exception { startSSLServer(new ServerHandler()); startProxy(); int proxyPort = proxyPort(); stopProxy(); HttpClient httpClient = new HttpClient(); httpClient.setProxy(new Address("localhost", proxyPort)); httpClient.start(); try { final CountDownLatch latch = new CountDownLatch(1); ContentExchange exchange = new ContentExchange(true) { @Override protected void onConnectionFailed(Throwable x) { latch.countDown(); } }; exchange.setMethod(HttpMethods.GET); String body = "BODY"; exchange.setURL("https://localhost:" + serverConnector.getLocalPort() + "/echo?body=" + URLEncoder.encode(body, "UTF-8")); httpClient.send(exchange); assertTrue(latch.await(1000, TimeUnit.MILLISECONDS)); } finally { httpClient.stop(); } } @Test public void testServerDown() throws Exception { startSSLServer(new ServerHandler()); int serverPort = serverConnector.getLocalPort(); stopServer(); startProxy(); HttpClient httpClient = new HttpClient(); httpClient.setProxy(new Address("localhost", proxyPort())); httpClient.start(); try { final CountDownLatch latch = new CountDownLatch(1); ContentExchange exchange = new ContentExchange(true) { @Override protected void onException(Throwable x) { latch.countDown(); } }; exchange.setMethod(HttpMethods.GET); String body = "BODY"; exchange.setURL("https://localhost:" + serverPort + "/echo?body=" + URLEncoder.encode(body, "UTF-8")); httpClient.send(exchange); assertTrue(latch.await(serverConnectTimeout * 2, TimeUnit.MILLISECONDS)); } finally { httpClient.stop(); } } private static class ServerHandler extends AbstractHandler { public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException { request.setHandled(true); String uri = httpRequest.getRequestURI(); if ("/echo".equals(uri)) { String body = httpRequest.getParameter("body"); ServletOutputStream output = httpResponse.getOutputStream(); output.print(body); } else { throw new ServletException(); } } } }