diff options
author | Simone Bordet | 2010-07-29 23:34:47 +0000 |
---|---|---|
committer | Simone Bordet | 2010-07-29 23:34:47 +0000 |
commit | fcc0c71d8fc0121e948992c7b856663292b64b80 (patch) | |
tree | 3508bcc071d65ff2ec4d8438852f26274dd313c3 /jetty-client | |
parent | e23d08749a4a96fd7150a9a2d6c9bb75b1f24b7f (diff) | |
download | org.eclipse.jetty.project-fcc0c71d8fc0121e948992c7b856663292b64b80.tar.gz org.eclipse.jetty.project-fcc0c71d8fc0121e948992c7b856663292b64b80.tar.xz org.eclipse.jetty.project-fcc0c71d8fc0121e948992c7b856663292b64b80.zip |
Fixes #298502 (Https exchange through an http proxy).
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@2197 7e9141cc-0065-0410-87d8-b60c137991c4
Diffstat (limited to 'jetty-client')
5 files changed, 681 insertions, 133 deletions
diff --git a/jetty-client/pom.xml b/jetty-client/pom.xml index 37d6461b2e..c7ef18c08d 100644 --- a/jetty-client/pom.xml +++ b/jetty-client/pom.xml @@ -1,101 +1,104 @@ -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - <parent> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-project</artifactId> - <version>7.2.0-SNAPSHOT</version> - </parent> - <modelVersion>4.0.0</modelVersion> - <artifactId>jetty-client</artifactId> - <name>Jetty :: Asynchronous HTTP Client</name> - <url>{$jetty.url}</url> - <properties> - <bundle-symbolic-name>${project.groupId}.client</bundle-symbolic-name> - </properties> - <build> - <plugins> - <plugin> - <groupId>org.apache.felix</groupId> - <artifactId>maven-bundle-plugin</artifactId> - <version>${felix.bundle.version}</version> - <extensions>true</extensions> - <executions> - <execution> - <goals> - <goal>manifest</goal> - </goals> - <configuration> - <instructions> - <Import-Package>javax.net.*,*</Import-Package> - </instructions> - </configuration> - </execution> - </executions> - </plugin> - <plugin> - <!-- - Required for OSGI - --> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-jar-plugin</artifactId> - <configuration> - <archive> - <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> - </archive> - </configuration> - </plugin> - <plugin> - <groupId>org.codehaus.mojo</groupId> - <artifactId>findbugs-maven-plugin</artifactId> - <configuration> - <onlyAnalyze>org.eclipse.jetty.client.*</onlyAnalyze> - </configuration> - </plugin> - </plugins> - </build> - <dependencies> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-http</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-server</artifactId> - <version>${project.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-security</artifactId> - <version>${project.version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>junit</groupId> - <artifactId>junit</artifactId> - <version>${junit4-version}</version> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-servlet</artifactId> - <version>${project.version}</version> - <scope>test</scope> - </dependency> - <!-- - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-servlet</artifactId> - <version>${project.version}</version> - <type>jar</type> - <scope>test</scope> - </dependency> - --> - <dependency> - <groupId>org.eclipse.jetty</groupId> - <artifactId>jetty-websocket</artifactId> - <version>${project.version}</version> - <scope>test</scope> - </dependency> - </dependencies> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <parent> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-project</artifactId> + <version>7.2.0-SNAPSHOT</version> + </parent> + + <modelVersion>4.0.0</modelVersion> + <artifactId>jetty-client</artifactId> + <name>Jetty :: Asynchronous HTTP Client</name> + <url>{$jetty.url}</url> + + <properties> + <bundle-symbolic-name>${project.groupId}.client</bundle-symbolic-name> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <version>${felix.bundle.version}</version> + <extensions>true</extensions> + <executions> + <execution> + <goals> + <goal>manifest</goal> + </goals> + <configuration> + <instructions> + <Import-Package>javax.net.*,*</Import-Package> + </instructions> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <!-- + Required for OSGI + --> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile> + </archive> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>findbugs-maven-plugin</artifactId> + <configuration> + <onlyAnalyze>org.eclipse.jetty.client.*</onlyAnalyze> + </configuration> + </plugin> + </plugins> + </build> + + <dependencies> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-http</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-server</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-security</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlet</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-servlets</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jetty</groupId> + <artifactId>jetty-websocket</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>${junit4-version}</version> + <scope>test</scope> + </dependency> + </dependencies> </project> 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 9244314355..05253c0612 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 @@ -36,7 +36,6 @@ import org.eclipse.jetty.io.ByteArrayBuffer; import org.eclipse.jetty.io.Connection; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.View; -import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.io.nio.SslSelectChannelEndPoint; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.thread.Timeout; @@ -209,7 +208,7 @@ public class HttpConnection implements Connection if (_exchange == null) continue; } - + long flushed = _generator.flushBuffer(); io += flushed; @@ -236,7 +235,7 @@ public class HttpConnection implements Connection } else _generator.complete(); - } + } else _generator.complete(); } @@ -254,7 +253,7 @@ public class HttpConnection implements Connection long filled = _parser.parseAvailable(); io += filled; } - + if (io > 0) no_progress = 0; else if (no_progress++ >= 2 && !_endp.isBlocking()) @@ -342,12 +341,12 @@ public class HttpConnection implements Connection HttpExchange exchange=_exchange; _exchange.disassociate(); _exchange = null; - + if (_status==HttpStatus.SWITCHING_PROTOCOLS_101) { Connection switched=exchange.onSwitchProtocol(_endp); if (switched!=null) - { + { // switched protocol! exchange = _pipeline; _pipeline = null; @@ -394,13 +393,13 @@ public class HttpConnection implements Connection { _exchange.disassociate(); } - + if (!_generator.isComplete() && _generator.getBytesBuffered()>0 && _endp instanceof AsyncEndPoint) - { + { ((AsyncEndPoint)_endp).setWritable(false); } } - + return this; } @@ -436,18 +435,27 @@ public class HttpConnection implements Connection _exchange.setStatus(HttpExchange.STATUS_SENDING_REQUEST); _generator.setVersion(_exchange.getVersion()); + String method=_exchange.getMethod(); String uri = _exchange.getURI(); - if (_destination.isProxied() && uri.startsWith("/")) + if (_destination.isProxied() && !HttpMethods.CONNECT.equals(method) && uri.startsWith("/")) { - // TODO suppress port 80 or 443 - uri = (_destination.isSecure()?HttpSchemes.HTTPS:HttpSchemes.HTTP) + "://" + _destination.getAddress().getHost() + ":" - + _destination.getAddress().getPort() + uri; + boolean secure = _destination.isSecure(); + String host = _destination.getAddress().getHost(); + int port = _destination.getAddress().getPort(); + StringBuilder absoluteURI = new StringBuilder(); + absoluteURI.append(secure ? HttpSchemes.HTTPS : HttpSchemes.HTTP); + absoluteURI.append("://"); + absoluteURI.append(host); + // Avoid adding default ports + if (!(secure && port == 443 || !secure && port == 80)) + absoluteURI.append(":").append(port); + absoluteURI.append(uri); + uri = absoluteURI.toString(); Authentication auth = _destination.getProxyAuthentication(); if (auth != null) auth.setCredentials(_exchange); } - String method=_exchange.getMethod(); _generator.setRequest(method, uri); _parser.setHeadResponse(HttpMethods.HEAD.equalsIgnoreCase(method)); @@ -594,10 +602,10 @@ public class HttpConnection implements Connection public void close() throws IOException { - //if there is a live, unfinished exchange, set its status to be + //if there is a live, unfinished exchange, set its status to be //excepted and wake up anyone waiting on waitForDone() - - if (_exchange != null && !_exchange.isDone()) + + if (_exchange != null && !_exchange.isDone()) { switch (_exchange.getStatus()) { 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 b885828022..590179c5f3 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 @@ -12,9 +12,9 @@ // ======================================================================== package org.eclipse.jetty.client; - import java.io.IOException; import java.lang.reflect.Constructor; +import java.net.ConnectException; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; @@ -25,9 +25,12 @@ import org.eclipse.jetty.client.security.Authentication; import org.eclipse.jetty.client.security.SecurityListener; import org.eclipse.jetty.http.HttpCookie; import org.eclipse.jetty.http.HttpHeaders; +import org.eclipse.jetty.http.HttpMethods; +import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.PathMap; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.ByteArrayBuffer; +import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.log.Log; /** @@ -307,7 +310,7 @@ public class HttpDestination } } - public void onNewConnection(HttpConnection connection) throws IOException + public void onNewConnection(final HttpConnection connection) throws IOException { HttpConnection q_connection = null; @@ -328,8 +331,20 @@ public class HttpDestination } else { - HttpExchange ex = _queue.removeFirst(); - send(connection, ex); + EndPoint endPoint = connection.getEndPoint(); + if (isProxied() && endPoint instanceof SelectConnector.ProxySelectChannelEndPoint) + { + SelectConnector.ProxySelectChannelEndPoint proxyEndPoint = (SelectConnector.ProxySelectChannelEndPoint)endPoint; + HttpExchange exchange = _queue.peekFirst(); + ConnectExchange connect = new ConnectExchange(getAddress(), proxyEndPoint, exchange); + connect.setAddress(getProxy()); + send(connection, connect); + } + else + { + HttpExchange exchange = _queue.removeFirst(); + send(connection, exchange); + } } } @@ -580,4 +595,41 @@ public class HttpDestination } } } + + private class ConnectExchange extends ContentExchange + { + private final SelectConnector.ProxySelectChannelEndPoint proxyEndPoint; + private final HttpExchange exchange; + + public ConnectExchange(Address serverAddress, SelectConnector.ProxySelectChannelEndPoint proxyEndPoint, HttpExchange exchange) + { + this.proxyEndPoint = proxyEndPoint; + this.exchange = exchange; + setMethod(HttpMethods.CONNECT); + String serverHostAndPort = serverAddress.toString(); + setURI(serverHostAndPort); + addRequestHeader(HttpHeaders.HOST, serverHostAndPort); + addRequestHeader(HttpHeaders.PROXY_CONNECTION, "keep-alive"); + addRequestHeader(HttpHeaders.USER_AGENT, "Jetty-Client"); + } + + @Override + protected void onResponseComplete() throws IOException + { + if (getResponseStatus() == HttpStatus.OK_200) + { + proxyEndPoint.upgrade(); + } + else + { + onConnectionFailed(new ConnectException(exchange.getAddress().toString())); + } + } + + @Override + protected void onConnectionFailed(Throwable x) + { + HttpDestination.this.onConnectionFailed(x); + } + } } diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java b/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java index 24e714b803..cb58420caf 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/SelectConnector.java @@ -16,20 +16,18 @@ package org.eclipse.jetty.client; import java.io.IOException; import java.net.SocketTimeoutException; import java.nio.channels.SelectionKey; -import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLSession; -import org.eclipse.jetty.http.HttpMethods; -import org.eclipse.jetty.http.HttpVersions; +import org.eclipse.jetty.http.HttpGenerator; +import org.eclipse.jetty.http.HttpParser; import org.eclipse.jetty.io.Buffer; import org.eclipse.jetty.io.Buffers; import org.eclipse.jetty.io.ConnectedEndPoint; import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.ThreadLocalBuffers; import org.eclipse.jetty.io.nio.DirectNIOBuffer; import org.eclipse.jetty.io.nio.IndirectNIOBuffer; @@ -218,14 +216,14 @@ class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector, { if (dest.isProxied()) { - String connect = HttpMethods.CONNECT+" "+dest.getAddress()+HttpVersions.HTTP_1_0+"\r\n\r\n"; - // TODO need to send this over channel unencrypted and setup endpoint to ignore the 200 OK response. - - throw new IllegalStateException("Not Implemented"); + SSLEngine engine=newSslEngine(); + ep = new ProxySelectChannelEndPoint(channel,selectSet,key,_sslBuffers,engine); + } + else + { + SSLEngine engine=newSslEngine(); + ep = new SslSelectChannelEndPoint(_sslBuffers,channel,selectSet,key,engine); } - - SSLEngine engine=newSslEngine(); - ep = new SslSelectChannelEndPoint(_sslBuffers,channel,selectSet,key,engine); } else { @@ -283,4 +281,204 @@ class SelectConnector extends AbstractLifeCycle implements HttpClient.Connector, } } } + + /** + * An endpoint that is able to "upgrade" from a normal endpoint to a SSL endpoint. + * Since {@link HttpParser} and {@link HttpGenerator} only depend on the {@link EndPoint} + * interface, this class overrides all methods of {@link EndPoint} to provide the right + * behavior depending on the fact that it has been upgraded or not. + */ + public static class ProxySelectChannelEndPoint extends SslSelectChannelEndPoint + { + private final SelectChannelEndPoint plainEndPoint; + private volatile boolean upgraded = false; + + public ProxySelectChannelEndPoint(SocketChannel channel, SelectorManager.SelectSet selectSet, SelectionKey key, Buffers sslBuffers, SSLEngine engine) throws IOException + { + super(sslBuffers, channel, selectSet, key, engine); + this.plainEndPoint = new SelectChannelEndPoint(channel, selectSet, key); + } + + public void upgrade() + { + upgraded = true; + } + + public void shutdownOutput() throws IOException + { + if (upgraded) + super.shutdownOutput(); + else + plainEndPoint.shutdownOutput(); + } + + public void close() throws IOException + { + if (upgraded) + super.close(); + else + plainEndPoint.close(); + } + + public int fill(Buffer buffer) throws IOException + { + if (upgraded) + return super.fill(buffer); + else + return plainEndPoint.fill(buffer); + } + + public int flush(Buffer buffer) throws IOException + { + if (upgraded) + return super.flush(buffer); + else + return plainEndPoint.flush(buffer); + } + + public int flush(Buffer header, Buffer buffer, Buffer trailer) throws IOException + { + if (upgraded) + return super.flush(header, buffer, trailer); + else + return plainEndPoint.flush(header, buffer, trailer); + } + + public String getLocalAddr() + { + if (upgraded) + return super.getLocalAddr(); + else + return plainEndPoint.getLocalAddr(); + } + + public String getLocalHost() + { + if (upgraded) + return super.getLocalHost(); + else + return plainEndPoint.getLocalHost(); + } + + public int getLocalPort() + { + if (upgraded) + return super.getLocalPort(); + else + return plainEndPoint.getLocalPort(); + } + + public String getRemoteAddr() + { + if (upgraded) + return super.getRemoteAddr(); + else + return plainEndPoint.getRemoteAddr(); + } + + public String getRemoteHost() + { + if (upgraded) + return super.getRemoteHost(); + else + return plainEndPoint.getRemoteHost(); + } + + public int getRemotePort() + { + if (upgraded) + return super.getRemotePort(); + else + return plainEndPoint.getRemotePort(); + } + + public boolean isBlocking() + { + if (upgraded) + return super.isBlocking(); + else + return plainEndPoint.isBlocking(); + } + + public boolean isBufferred() + { + if (upgraded) + return super.isBufferred(); + else + return plainEndPoint.isBufferred(); + } + + public boolean blockReadable(long millisecs) throws IOException + { + if (upgraded) + return super.blockReadable(millisecs); + else + return plainEndPoint.blockReadable(millisecs); + } + + public boolean blockWritable(long millisecs) throws IOException + { + if (upgraded) + return super.blockWritable(millisecs); + else + return plainEndPoint.blockWritable(millisecs); + } + + public boolean isOpen() + { + if (upgraded) + return super.isOpen(); + else + return plainEndPoint.isOpen(); + } + + public Object getTransport() + { + if (upgraded) + return super.getTransport(); + else + return plainEndPoint.getTransport(); + } + + public boolean isBufferingInput() + { + if (upgraded) + return super.isBufferingInput(); + else + return plainEndPoint.isBufferingInput(); + } + + public boolean isBufferingOutput() + { + if (upgraded) + return super.isBufferingOutput(); + else + return plainEndPoint.isBufferingOutput(); + } + + public void flush() throws IOException + { + if (upgraded) + super.flush(); + else + plainEndPoint.flush(); + + } + + public int getMaxIdleTime() + { + if (upgraded) + return super.getMaxIdleTime(); + else + return plainEndPoint.getMaxIdleTime(); + } + + public void setMaxIdleTime(int timeMs) throws IOException + { + if (upgraded) + super.setMaxIdleTime(timeMs); + else + plainEndPoint.setMaxIdleTime(timeMs); + } + } } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/ProxyTunnellingTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/ProxyTunnellingTest.java new file mode 100644 index 0000000000..f0c864789f --- /dev/null +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/ProxyTunnellingTest.java @@ -0,0 +1,287 @@ +package org.eclipse.jetty.client; + +import java.io.File; +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.HandlerCollection; +import org.eclipse.jetty.server.handler.ProxyHandler; +import org.eclipse.jetty.server.nio.SelectChannelConnector; +import org.eclipse.jetty.server.ssl.SslSelectChannelConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.jetty.servlets.ProxyServlet; +import org.junit.After; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +public class ProxyTunnellingTest +{ + private Server server; + private Connector serverConnector; + private Server proxy; + private Connector proxyConnector; + + private void startSSLServer(Handler handler) throws Exception + { + SslSelectChannelConnector connector = new SslSelectChannelConnector(); + String keyStorePath = System.getProperty("basedir"); + assertNotNull(keyStorePath); + keyStorePath += File.separator + "src" + File.separator + "test" + File.separator + "resources" + File.separator + "keystore"; + connector.setKeystore(keyStorePath); + connector.setPassword("storepwd"); + connector.setKeyPassword("keypwd"); + startServer(connector, handler); + } + + private void startServer(Connector connector, Handler handler) throws Exception + { + server = new Server(); + serverConnector = connector; + server.addConnector(serverConnector); + server.setHandler(handler); + server.start(); + } + + private void startProxy() throws Exception + { + proxy = new Server(); + proxyConnector = new SelectChannelConnector(); + proxy.addConnector(proxyConnector); + ProxyHandler proxyHandler = new ProxyHandler(); + proxy.setHandler(proxyHandler); + HandlerCollection handlers = new HandlerCollection(); + proxyHandler.setHandler(handlers); + ServletContextHandler context = new ServletContextHandler(handlers, "/", ServletContextHandler.SESSIONS); + ServletHolder proxyServlet = new ServletHolder(ProxyServlet.class); + context.addServlet(proxyServlet, "/*"); + proxy.start(); + } + + @After + public void stop() throws Exception + { + stopProxy(); + stopServer(); + } + + private void stopServer() throws Exception + { + server.stop(); + server.join(); + } + + private void stopProxy() throws Exception + { + proxy.stop(); + proxy.join(); + } + + + @Test + public void testNoSSL() throws Exception + { + startServer(new SelectChannelConnector(), new ServerHandler()); + startProxy(); + + HttpClient httpClient = new HttpClient(); + httpClient.setProxy(new Address("localhost", proxyConnector.getLocalPort())); + httpClient.start(); + + try + { + ContentExchange exchange = new ContentExchange(true); + String body = "BODY"; + exchange.setURL("http://localhost:" + serverConnector.getLocalPort() + "/echo?body=" + URLEncoder.encode(body, "UTF-8")); + exchange.setMethod(HttpMethods.GET); + + httpClient.send(exchange); + assertEquals(HttpExchange.STATUS_COMPLETED, exchange.waitForDone()); + String content = exchange.getResponseContent(); + assertEquals(body, content); + } + finally + { + httpClient.stop(); + } + } + + @Test + public void testOneMessageSSL() throws Exception + { + startSSLServer(new ServerHandler()); + startProxy(); + + HttpClient httpClient = new HttpClient(); + httpClient.setProxy(new Address("localhost", proxyConnector.getLocalPort())); + 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", proxyConnector.getLocalPort())); + 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 = proxyConnector.getLocalPort(); + 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", proxyConnector.getLocalPort())); + 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:" + serverPort + "/echo?body=" + URLEncoder.encode(body, "UTF-8")); + + httpClient.send(exchange); + assertTrue(latch.await(1000, 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(); + } + } + } +} |