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
This commit is contained in:
Simone Bordet 2010-07-29 23:34:47 +00:00
parent e23d08749a
commit fcc0c71d8f
5 changed files with 681 additions and 133 deletions

View File

@ -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>

View File

@ -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())
{

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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();
}
}
}
}