Improvements and tests for #297104.
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@1521 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
e0d07733bd
commit
95430a1b33
|
@ -19,6 +19,7 @@ import org.eclipse.jetty.io.EndPoint;
|
|||
import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
|
||||
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
|
||||
import org.eclipse.jetty.io.nio.SelectorManager;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.HttpConnection;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
@ -36,32 +37,23 @@ import org.eclipse.jetty.util.thread.ThreadPool;
|
|||
*
|
||||
* @version $Revision$ $Date$
|
||||
*/
|
||||
public class ProxyHandler extends AbstractHandler
|
||||
public class ProxyHandler extends HandlerWrapper
|
||||
{
|
||||
private final Logger _logger = Log.getLogger(getClass().getName());
|
||||
private final SelectorManager _selectorManager = new Manager();
|
||||
private final String _serverAddress;
|
||||
private volatile int _connectTimeout = 5000;
|
||||
private volatile int _writeTimeout = 30000;
|
||||
private volatile ThreadPool _threadPool;
|
||||
private volatile boolean _privateThreadPool;
|
||||
|
||||
/**
|
||||
* <p>Constructor to be used to make this proxy work via HTTP CONNECT.</p>
|
||||
*/
|
||||
public ProxyHandler()
|
||||
{
|
||||
this(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Constructor to be used to make this proxy work as transparent proxy.</p>
|
||||
*
|
||||
* @param serverAddress the address of the remote server in the form {@code host:port}
|
||||
*/
|
||||
public ProxyHandler(String serverAddress)
|
||||
public ProxyHandler(Handler handler)
|
||||
{
|
||||
_serverAddress = serverAddress;
|
||||
setHandler(handler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -102,7 +94,7 @@ public class ProxyHandler extends AbstractHandler
|
|||
super.setServer(server);
|
||||
|
||||
server.getContainer().update(this,null,_selectorManager,"selectManager");
|
||||
|
||||
|
||||
if (_privateThreadPool)
|
||||
server.getContainer().update(this,null,_privateThreadPool,"threadpool",true);
|
||||
else
|
||||
|
@ -118,7 +110,7 @@ public class ProxyHandler extends AbstractHandler
|
|||
}
|
||||
|
||||
/**
|
||||
* @param the thread pool
|
||||
* @param threadPool the thread pool
|
||||
*/
|
||||
public void setThreadPool(ThreadPool threadPool)
|
||||
{
|
||||
|
@ -173,24 +165,22 @@ public class ProxyHandler extends AbstractHandler
|
|||
super.doStop();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
if (HttpMethods.CONNECT.equalsIgnoreCase(request.getMethod()))
|
||||
{
|
||||
_logger.debug("CONNECT request for {}", request.getRequestURI(), null);
|
||||
handle(request, response, request.getRequestURI());
|
||||
handleConnect(request, response, request.getRequestURI());
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.debug("Plain request for {}", _serverAddress, null);
|
||||
if (_serverAddress == null)
|
||||
throw new ServletException("Parameter 'serverAddress' cannot be null");
|
||||
handle(request, response, _serverAddress);
|
||||
super.handle(target, baseRequest, request, response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Handles a tunnelling request, either a CONNECT or a transparent request.</p>
|
||||
* <p>Handles a CONNECT request.</p>
|
||||
* <p>CONNECT requests may have authentication headers such as <code>Proxy-Authorization</code>
|
||||
* that authenticate the client with the proxy.</p>
|
||||
*
|
||||
|
@ -200,7 +190,7 @@ public class ProxyHandler extends AbstractHandler
|
|||
* @throws ServletException if an application error occurs
|
||||
* @throws IOException if an I/O error occurs
|
||||
*/
|
||||
protected void handle(HttpServletRequest request, HttpServletResponse response, String serverAddress) throws ServletException, IOException
|
||||
protected void handleConnect(HttpServletRequest request, HttpServletResponse response, String serverAddress) throws ServletException, IOException
|
||||
{
|
||||
boolean proceed = handleAuthentication(request, response, serverAddress);
|
||||
if (!proceed)
|
||||
|
@ -208,51 +198,14 @@ public class ProxyHandler extends AbstractHandler
|
|||
|
||||
String host = serverAddress;
|
||||
int port = 80;
|
||||
boolean secure = false;
|
||||
int colon = serverAddress.indexOf(':');
|
||||
if (colon > 0)
|
||||
{
|
||||
host = serverAddress.substring(0, colon);
|
||||
port = Integer.parseInt(serverAddress.substring(colon + 1));
|
||||
secure = isTunnelSecure(host, port);
|
||||
}
|
||||
|
||||
setupTunnel(request, response, host, port, secure);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Returns whether the given {@code host} and {@code port} identify a SSL communication channel.<p>
|
||||
* <p>Default implementation returns true if the {@code port} is 443.</p>
|
||||
*
|
||||
* @param host the host to connect to
|
||||
* @param port the port to connect to
|
||||
* @return true if the communication channel is confidential, false otherwise
|
||||
*/
|
||||
protected boolean isTunnelSecure(String host, int port)
|
||||
{
|
||||
return port == 443;
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Handles the authentication before setting up the tunnel to the remote server.</p>
|
||||
* <p>The default implementation returns true.</p>
|
||||
*
|
||||
* @param request the HTTP request
|
||||
* @param response the HTTP response
|
||||
* @param address the address of the remote server in the form {@code host:port}.
|
||||
* @return true to allow to connect to the remote host, false otherwise
|
||||
* @throws ServletException to report a server error to the caller
|
||||
* @throws IOException to report a server error to the caller
|
||||
*/
|
||||
protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address) throws ServletException, IOException
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void setupTunnel(HttpServletRequest request, HttpServletResponse response, String host, int port, boolean secure) throws IOException
|
||||
{
|
||||
SocketChannel channel = connect(request, host, port);
|
||||
channel.configureBlocking(false);
|
||||
SocketChannel channel = connectToServer(request, host, port);
|
||||
|
||||
// Transfer unread data from old connection to new connection
|
||||
// We need to copy the data to avoid races:
|
||||
|
@ -280,14 +233,40 @@ public class ProxyHandler extends AbstractHandler
|
|||
}
|
||||
}
|
||||
|
||||
// Setup connections, before registering the channel to avoid races
|
||||
// where the server sends data before the connections are set up
|
||||
ProxyToServerConnection proxyToServer = newProxyToServerConnection(secure, buffer);
|
||||
ClientToProxyConnection clientToProxy = prepareConnections(channel, buffer);
|
||||
|
||||
// CONNECT expects a 200 response
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
// Flush it so that the client receives it
|
||||
response.flushBuffer();
|
||||
|
||||
upgradeConnection(request, response, clientToProxy);
|
||||
}
|
||||
|
||||
private ClientToProxyConnection prepareConnections(SocketChannel channel, Buffer buffer)
|
||||
{
|
||||
HttpConnection httpConnection = HttpConnection.getCurrentConnection();
|
||||
ProxyToServerConnection proxyToServer = newProxyToServerConnection(buffer);
|
||||
ClientToProxyConnection clientToProxy = newClientToProxyConnection(channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp());
|
||||
clientToProxy.setConnection(proxyToServer);
|
||||
proxyToServer.setConnection(clientToProxy);
|
||||
return clientToProxy;
|
||||
}
|
||||
|
||||
upgradeConnection(request, response, clientToProxy);
|
||||
/**
|
||||
* <p>Handles the authentication before setting up the tunnel to the remote server.</p>
|
||||
* <p>The default implementation returns true.</p>
|
||||
*
|
||||
* @param request the HTTP request
|
||||
* @param response the HTTP response
|
||||
* @param address the address of the remote server in the form {@code host:port}.
|
||||
* @return true to allow to connect to the remote host, false otherwise
|
||||
* @throws ServletException to report a server error to the caller
|
||||
* @throws IOException to report a server error to the caller
|
||||
*/
|
||||
protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address) throws ServletException, IOException
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected ClientToProxyConnection newClientToProxyConnection(SocketChannel channel, EndPoint endPoint, long timeStamp)
|
||||
|
@ -295,9 +274,16 @@ public class ProxyHandler extends AbstractHandler
|
|||
return new ClientToProxyConnection(channel, endPoint, timeStamp);
|
||||
}
|
||||
|
||||
protected ProxyToServerConnection newProxyToServerConnection(boolean secure, IndirectNIOBuffer buffer)
|
||||
protected ProxyToServerConnection newProxyToServerConnection(Buffer buffer)
|
||||
{
|
||||
return new ProxyToServerConnection(secure, buffer);
|
||||
return new ProxyToServerConnection(buffer);
|
||||
}
|
||||
|
||||
private SocketChannel connectToServer(HttpServletRequest request, String host, int port) throws IOException
|
||||
{
|
||||
SocketChannel channel = connect(request, host, port);
|
||||
channel.configureBlocking(false);
|
||||
return channel;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -321,10 +307,6 @@ public class ProxyHandler extends AbstractHandler
|
|||
|
||||
private void upgradeConnection(HttpServletRequest request, HttpServletResponse response, Connection connection) throws IOException
|
||||
{
|
||||
// CONNECT expects a 200 response
|
||||
response.setStatus(HttpServletResponse.SC_OK);
|
||||
// Flush it so that the client receives it
|
||||
response.flushBuffer();
|
||||
// Set the new connection as request attribute and change the status to 101
|
||||
// so that Jetty understands that it has to upgrade the connection
|
||||
request.setAttribute("org.eclipse.jetty.io.Connection", connection);
|
||||
|
@ -358,10 +340,10 @@ public class ProxyHandler extends AbstractHandler
|
|||
* @param buffer the buffer to write
|
||||
* @throws IOException if the buffer cannot be written
|
||||
*/
|
||||
protected void write(EndPoint endPoint, Buffer buffer) throws IOException
|
||||
protected int write(EndPoint endPoint, Buffer buffer) throws IOException
|
||||
{
|
||||
if (buffer == null)
|
||||
return;
|
||||
return 0;
|
||||
|
||||
int length = buffer.length();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
@ -382,6 +364,7 @@ public class ProxyHandler extends AbstractHandler
|
|||
}
|
||||
}
|
||||
_logger.debug("Written {}/{} bytes " + endPoint, builder, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
private class Manager extends SelectorManager
|
||||
|
@ -396,16 +379,7 @@ public class ProxyHandler extends AbstractHandler
|
|||
@Override
|
||||
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey selectionKey) throws IOException
|
||||
{
|
||||
ProxyToServerConnection proxyToServer = (ProxyToServerConnection)selectionKey.attachment();
|
||||
if (proxyToServer._secure)
|
||||
{
|
||||
throw new UnsupportedOperationException();
|
||||
// return new SslSelectChannelEndPoint(???, channel, selectSet, selectionKey, sslContext.createSSLEngine(address.host, address.port));
|
||||
}
|
||||
else
|
||||
{
|
||||
return new SelectChannelEndPoint(channel, selectSet, selectionKey);
|
||||
}
|
||||
return new SelectChannelEndPoint(channel, selectSet, selectionKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -445,46 +419,64 @@ public class ProxyHandler extends AbstractHandler
|
|||
{
|
||||
private final CountDownLatch _ready = new CountDownLatch(1);
|
||||
private final Buffer _buffer = new IndirectNIOBuffer(1024);
|
||||
private final boolean _secure;
|
||||
private volatile Buffer _data;
|
||||
private volatile ClientToProxyConnection _toClient;
|
||||
private volatile long _timestamp;
|
||||
private volatile SelectChannelEndPoint _endPoint;
|
||||
|
||||
public ProxyToServerConnection(boolean secure, Buffer data)
|
||||
public ProxyToServerConnection(Buffer data)
|
||||
{
|
||||
_secure = secure;
|
||||
_data = data;
|
||||
}
|
||||
|
||||
public Connection handle() throws IOException
|
||||
{
|
||||
_logger.debug("ProxyToServer: handle entered");
|
||||
if (_data != null)
|
||||
_logger.debug("ProxyToServer: begin reading from server");
|
||||
try
|
||||
{
|
||||
write(_endPoint, _data);
|
||||
_data = null;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
int read = read(_endPoint, _buffer);
|
||||
|
||||
if (read == -1)
|
||||
if (_data != null)
|
||||
{
|
||||
_logger.debug("ProxyToServer: closed connection {}", _endPoint, null);
|
||||
_toClient.close();
|
||||
break;
|
||||
int written = write(_endPoint, _data);
|
||||
_logger.debug("ProxyToServer: written to server {} bytes", written, null);
|
||||
_data = null;
|
||||
}
|
||||
|
||||
if (read == 0)
|
||||
break;
|
||||
while (true)
|
||||
{
|
||||
int read = read(_endPoint, _buffer);
|
||||
|
||||
_logger.debug("ProxyToServer: read {} bytes {}", read, _endPoint);
|
||||
write(_toClient._endPoint, _buffer);
|
||||
if (read == -1)
|
||||
{
|
||||
_logger.debug("ProxyToServer: server closed connection {}", _endPoint, null);
|
||||
close();
|
||||
break;
|
||||
}
|
||||
|
||||
if (read == 0)
|
||||
break;
|
||||
|
||||
_logger.debug("ProxyToServer: read from server {} bytes {}", read, _endPoint);
|
||||
int written = write(_toClient._endPoint, _buffer);
|
||||
_logger.debug("ProxyToServer: written to client {} bytes", written, null);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
_logger.warn("ProxyToServer: Unexpected exception", x);
|
||||
close();
|
||||
throw x;
|
||||
}
|
||||
catch (RuntimeException x)
|
||||
{
|
||||
_logger.warn("ProxyToServer: Unexpected exception", x);
|
||||
close();
|
||||
throw x;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.debug("ProxyToServer: end reading from server");
|
||||
}
|
||||
_logger.debug("ProxyToServer: handle exited");
|
||||
return this;
|
||||
}
|
||||
|
||||
public void setConnection(ClientToProxyConnection connection)
|
||||
|
@ -505,6 +497,7 @@ public class ProxyHandler extends AbstractHandler
|
|||
public void setEndPoint(SelectChannelEndPoint endpoint)
|
||||
{
|
||||
_endPoint = endpoint;
|
||||
_logger.debug("ProxyToServer: {}", _endPoint, null);
|
||||
}
|
||||
|
||||
public boolean isIdle()
|
||||
|
@ -534,10 +527,36 @@ public class ProxyHandler extends AbstractHandler
|
|||
}
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
public void closeClient() throws IOException
|
||||
{
|
||||
_toClient.closeClient();
|
||||
}
|
||||
|
||||
public void closeServer() throws IOException
|
||||
{
|
||||
_endPoint.close();
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
try
|
||||
{
|
||||
closeClient();
|
||||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
_logger.debug("ProxyToServer: Unexpected exception closing the client", x);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
closeServer();
|
||||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
_logger.debug("ProxyToServer: Unexpected exception closing the server", x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class ClientToProxyConnection implements Connection
|
||||
|
@ -554,37 +573,57 @@ public class ProxyHandler extends AbstractHandler
|
|||
_channel = channel;
|
||||
_endPoint = endPoint;
|
||||
_timestamp = timestamp;
|
||||
_logger.debug("ClientToProxy: {}", _endPoint, null);
|
||||
}
|
||||
|
||||
public Connection handle() throws IOException
|
||||
{
|
||||
_logger.debug("ClientToProxy: handle entered");
|
||||
|
||||
if (_firstTime)
|
||||
_logger.debug("ClientToProxy: begin reading from client");
|
||||
try
|
||||
{
|
||||
_firstTime = false;
|
||||
register(_channel, _toServer);
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
int read = read(_endPoint, _buffer);
|
||||
|
||||
if (read == -1)
|
||||
if (_firstTime)
|
||||
{
|
||||
_logger.debug("ClientToProxy: closed connection {}", _endPoint, null);
|
||||
_toServer.close();
|
||||
break;
|
||||
_firstTime = false;
|
||||
register(_channel, _toServer);
|
||||
_logger.debug("ClientToProxy: registered channel {} with connection {}", _channel, _toServer);
|
||||
}
|
||||
|
||||
if (read == 0)
|
||||
break;
|
||||
while (true)
|
||||
{
|
||||
int read = read(_endPoint, _buffer);
|
||||
|
||||
_logger.debug("ClientToProxy: read {} bytes {}", read, _endPoint);
|
||||
write(_toServer._endPoint, _buffer);
|
||||
if (read == -1)
|
||||
{
|
||||
_logger.debug("ClientToProxy: client closed connection {}", _endPoint, null);
|
||||
close();
|
||||
break;
|
||||
}
|
||||
|
||||
if (read == 0)
|
||||
break;
|
||||
|
||||
_logger.debug("ClientToProxy: read from client {} bytes {}", read, _endPoint);
|
||||
int written = write(_toServer._endPoint, _buffer);
|
||||
_logger.debug("ClientToProxy: written to server {} bytes", written, null);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
_logger.warn("ClientToProxy: Unexpected exception", x);
|
||||
close();
|
||||
throw x;
|
||||
}
|
||||
catch (RuntimeException x)
|
||||
{
|
||||
_logger.warn("ClientToProxy: Unexpected exception", x);
|
||||
close();
|
||||
throw x;
|
||||
}
|
||||
finally
|
||||
{
|
||||
_logger.debug("ClientToProxy: end reading from client");
|
||||
}
|
||||
_logger.debug("ClientToProxy: handle exited");
|
||||
return this;
|
||||
}
|
||||
|
||||
public long getTimeStamp()
|
||||
|
@ -607,9 +646,35 @@ public class ProxyHandler extends AbstractHandler
|
|||
_toServer = connection;
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
public void closeClient() throws IOException
|
||||
{
|
||||
_endPoint.close();
|
||||
}
|
||||
|
||||
public void closeServer() throws IOException
|
||||
{
|
||||
_toServer.closeServer();
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
try
|
||||
{
|
||||
closeClient();
|
||||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
_logger.debug("ClientToProxy: Unexpected exception closing the client", x);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
closeServer();
|
||||
}
|
||||
catch (IOException x)
|
||||
{
|
||||
_logger.debug("ClientToProxy: Unexpected exception closing the server", x);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,172 @@
|
|||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.net.Socket;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||
|
||||
/**
|
||||
* @version $Revision$ $Date$
|
||||
*/
|
||||
public class AbstractProxyHandlerTest extends TestCase
|
||||
{
|
||||
protected Server server;
|
||||
protected Connector serverConnector;
|
||||
protected Server proxy;
|
||||
protected Connector proxyConnector;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
serverConnector = newServerConnector();
|
||||
server.addConnector(serverConnector);
|
||||
configureServer(server);
|
||||
server.start();
|
||||
|
||||
proxy = new Server();
|
||||
proxyConnector = new SelectChannelConnector();
|
||||
proxy.addConnector(proxyConnector);
|
||||
configureProxy(proxy);
|
||||
proxy.start();
|
||||
}
|
||||
|
||||
protected SelectChannelConnector newServerConnector()
|
||||
{
|
||||
return new SelectChannelConnector();
|
||||
}
|
||||
|
||||
protected void configureServer(Server server)
|
||||
{
|
||||
}
|
||||
|
||||
protected void configureProxy(Server proxy)
|
||||
{
|
||||
proxy.setHandler(new ProxyHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception
|
||||
{
|
||||
proxy.stop();
|
||||
proxy.join();
|
||||
|
||||
server.stop();
|
||||
server.join();
|
||||
}
|
||||
|
||||
protected Response readResponse(BufferedReader reader) throws IOException
|
||||
{
|
||||
// Simplified parser for HTTP responses
|
||||
String line = reader.readLine();
|
||||
if (line == null)
|
||||
throw new EOFException();
|
||||
Matcher responseLine = Pattern.compile("HTTP/1\\.1\\s+(\\d+)").matcher(line);
|
||||
assertTrue(responseLine.lookingAt());
|
||||
String code = responseLine.group(1);
|
||||
|
||||
Map<String, String> headers = new LinkedHashMap<String, String>();
|
||||
while ((line = reader.readLine()) != null)
|
||||
{
|
||||
if (line.trim().length() == 0)
|
||||
break;
|
||||
|
||||
Matcher header = Pattern.compile("([^:]+):\\s*(.*)").matcher(line);
|
||||
assertTrue(header.lookingAt());
|
||||
String headerName = header.group(1);
|
||||
String headerValue = header.group(2);
|
||||
headers.put(headerName.toLowerCase(), headerValue.toLowerCase());
|
||||
}
|
||||
|
||||
StringBuilder body = new StringBuilder();
|
||||
if (headers.containsKey("content-length"))
|
||||
{
|
||||
int length = Integer.parseInt(headers.get("content-length"));
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
char c = (char)reader.read();
|
||||
body.append(c);
|
||||
}
|
||||
}
|
||||
else if ("chunked".equals(headers.get("transfer-encoding")))
|
||||
{
|
||||
while ((line = reader.readLine()) != null)
|
||||
{
|
||||
if ("0".equals(line))
|
||||
{
|
||||
line = reader.readLine();
|
||||
assertEquals("", line);
|
||||
break;
|
||||
}
|
||||
|
||||
int length = Integer.parseInt(line, 16);
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
char c = (char)reader.read();
|
||||
body.append(c);
|
||||
}
|
||||
line = reader.readLine();
|
||||
assertEquals("", line);
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(code, headers, body.toString().trim());
|
||||
}
|
||||
|
||||
protected Socket newSocket() throws IOException
|
||||
{
|
||||
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||
socket.setSoTimeout(5000);
|
||||
return socket;
|
||||
}
|
||||
|
||||
protected class Response
|
||||
{
|
||||
private final String code;
|
||||
private final Map<String, String> headers;
|
||||
private final String body;
|
||||
|
||||
private Response(String code, Map<String, String> headers, String body)
|
||||
{
|
||||
this.code = code;
|
||||
this.headers = headers;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public String getCode()
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
public Map<String, String> getHeaders()
|
||||
{
|
||||
return headers;
|
||||
}
|
||||
|
||||
public String getBody()
|
||||
{
|
||||
return body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(code).append("\r\n");
|
||||
for (Map.Entry<String, String> entry : headers.entrySet())
|
||||
builder.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
|
||||
builder.append("\r\n");
|
||||
builder.append(body);
|
||||
return builder.toString();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,226 @@
|
|||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
|
||||
|
||||
/**
|
||||
* @version $Revision$ $Date$
|
||||
*/
|
||||
public class ProxyHandlerConnectSSLTest extends AbstractProxyHandlerTest
|
||||
{
|
||||
@Override
|
||||
protected SelectChannelConnector newServerConnector()
|
||||
{
|
||||
SslSelectChannelConnector connector = new SslSelectChannelConnector();
|
||||
|
||||
String keyStorePath = System.getProperty("basedir");
|
||||
keyStorePath += File.separator + "src" + File.separator + "test" + File.separator + "resources" + File.separator + "keystore";
|
||||
connector.setKeystore(keyStorePath);
|
||||
connector.setPassword("storepwd");
|
||||
connector.setKeyPassword("keypwd");
|
||||
|
||||
return connector;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void configureServer(Server server)
|
||||
{
|
||||
server.setHandler(new ServerHandler());
|
||||
}
|
||||
|
||||
public void testGETRequest() throws Exception
|
||||
{
|
||||
String hostPort = "localhost:" + serverConnector.getLocalPort();
|
||||
String request = "" +
|
||||
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
Socket socket = newSocket();
|
||||
try
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
// Expect 200 OK from the CONNECT request
|
||||
Response response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
|
||||
// Be sure the buffered input does not have anything buffered
|
||||
assertFalse(input.ready());
|
||||
|
||||
// Upgrade the socket to SSL
|
||||
SSLSocket sslSocket = wrapSocket(socket);
|
||||
try
|
||||
{
|
||||
output = sslSocket.getOutputStream();
|
||||
input = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
|
||||
|
||||
request = "" +
|
||||
"GET /echo HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
assertEquals("GET /echo", response.getBody());
|
||||
}
|
||||
finally
|
||||
{
|
||||
sslSocket.close();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void testPOSTRequests() throws Exception
|
||||
{
|
||||
String hostPort = "localhost:" + serverConnector.getLocalPort();
|
||||
String request = "" +
|
||||
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
Socket socket = newSocket();
|
||||
try
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
// Expect 200 OK from the CONNECT request
|
||||
Response response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
|
||||
// Be sure the buffered input does not have anything buffered
|
||||
assertFalse(input.ready());
|
||||
|
||||
// Upgrade the socket to SSL
|
||||
SSLSocket sslSocket = wrapSocket(socket);
|
||||
try
|
||||
{
|
||||
output = sslSocket.getOutputStream();
|
||||
input = new BufferedReader(new InputStreamReader(sslSocket.getInputStream()));
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
request = "" +
|
||||
"POST /echo?param=" + i + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"Content-Length: 5\r\n" +
|
||||
"\r\n" +
|
||||
"HELLO";
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
assertEquals("POST /echo?param=" + i + "\r\nHELLO", response.getBody());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
sslSocket.close();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
private SSLSocket wrapSocket(Socket socket) throws Exception
|
||||
{
|
||||
SSLContext sslContext = SSLContext.getInstance("SSLv3");
|
||||
sslContext.init(null, new TrustManager[]{new AlwaysTrustManager()}, new SecureRandom());
|
||||
SSLSocketFactory socketFactory = sslContext.getSocketFactory();
|
||||
SSLSocket sslSocket = (SSLSocket)socketFactory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true);
|
||||
sslSocket.setUseClientMode(true);
|
||||
sslSocket.startHandshake();
|
||||
return sslSocket;
|
||||
}
|
||||
|
||||
private class AlwaysTrustManager implements X509TrustManager
|
||||
{
|
||||
public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
|
||||
{
|
||||
}
|
||||
|
||||
public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException
|
||||
{
|
||||
}
|
||||
|
||||
public X509Certificate[] getAcceptedIssuers()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private 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))
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(httpRequest.getMethod()).append(" ").append(uri);
|
||||
if (httpRequest.getQueryString() != null)
|
||||
builder.append("?").append(httpRequest.getQueryString());
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
InputStream input = httpRequest.getInputStream();
|
||||
int read = -1;
|
||||
while ((read = input.read()) >= 0)
|
||||
baos.write(read);
|
||||
baos.close();
|
||||
|
||||
System.err.println("server echoing:\r\n" + builder);
|
||||
ServletOutputStream output = httpResponse.getOutputStream();
|
||||
output.println(builder.toString());
|
||||
output.write(baos.toByteArray());
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ServletException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,82 +1,51 @@
|
|||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.EOFException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletOutputStream;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||
|
||||
/**
|
||||
* @version $Revision$ $Date$
|
||||
*/
|
||||
public class ProxyHandlerConnectTest extends TestCase
|
||||
public class ProxyHandlerConnectTest extends AbstractProxyHandlerTest
|
||||
{
|
||||
private Server server;
|
||||
private Connector serverConnector;
|
||||
private Server proxy;
|
||||
private Connector proxyConnector;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception
|
||||
protected void configureServer(Server server)
|
||||
{
|
||||
server = new Server();
|
||||
serverConnector = new SelectChannelConnector();
|
||||
server.addConnector(serverConnector);
|
||||
server.setHandler(new ServerHandler());
|
||||
server.start();
|
||||
|
||||
proxy = new Server();
|
||||
proxyConnector = new SelectChannelConnector();
|
||||
proxy.addConnector(proxyConnector);
|
||||
proxy.setHandler(new ProxyHandler());
|
||||
proxy.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception
|
||||
{
|
||||
proxy.stop();
|
||||
proxy.join();
|
||||
|
||||
server.stop();
|
||||
server.join();
|
||||
}
|
||||
|
||||
public void testHttpConnect() throws Exception
|
||||
public void testCONNECT() throws Exception
|
||||
{
|
||||
String hostPort = "localhost:" + serverConnector.getLocalPort();
|
||||
String request = "" +
|
||||
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||
Socket socket = newSocket();
|
||||
try
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
// Expect 200 OK from the CONNECT request
|
||||
InputStream input = socket.getInputStream();
|
||||
Response response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.code);
|
||||
assertEquals("200", response.getCode());
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -84,37 +53,38 @@ public class ProxyHandlerConnectTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
public void testHttpConnectWithNormalRequest() throws Exception
|
||||
public void testCONNECTAndGET() throws Exception
|
||||
{
|
||||
String hostPort = "localhost:" + serverConnector.getLocalPort();
|
||||
String request = "" +
|
||||
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||
Socket socket = newSocket();
|
||||
try
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
// Expect 200 OK from the CONNECT request
|
||||
InputStream input = socket.getInputStream();
|
||||
Response response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.code);
|
||||
assertEquals("200", response.getCode());
|
||||
|
||||
String echoURI = "GET /echo";
|
||||
request = "" +
|
||||
echoURI + " HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"GET /echo" + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.code);
|
||||
assertEquals(echoURI, response.body);
|
||||
assertEquals("200", response.getCode());
|
||||
assertEquals("GET /echo", response.getBody());
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -122,34 +92,35 @@ public class ProxyHandlerConnectTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
public void testHttpConnectWithPipelinedRequest() throws Exception
|
||||
public void testCONNECTAndGETPipelined() throws Exception
|
||||
{
|
||||
String pipelinedMethodURI = "GET /echo";
|
||||
String hostPort = "localhost:" + serverConnector.getLocalPort();
|
||||
String request = "" +
|
||||
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n" +
|
||||
pipelinedMethodURI + " HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"GET /echo" + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||
Socket socket = newSocket();
|
||||
try
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
// Expect 200 OK from the CONNECT request
|
||||
InputStream input = socket.getInputStream();
|
||||
Response response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.code);
|
||||
assertEquals("200", response.getCode());
|
||||
|
||||
// The pipelined request must have gone up to the server as is
|
||||
response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.code);
|
||||
assertEquals(pipelinedMethodURI, response.body);
|
||||
assertEquals("200", response.getCode());
|
||||
assertEquals("GET /echo", response.getBody());
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
@ -157,24 +128,80 @@ public class ProxyHandlerConnectTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
public void testHttpConnectWithNoRequestServerClose() throws Exception
|
||||
public void testCONNECTAndMultipleGETs() throws Exception
|
||||
{
|
||||
String hostPort = "localhost:" + serverConnector.getLocalPort();
|
||||
String request = "" +
|
||||
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||
Socket socket = newSocket();
|
||||
try
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
// Expect 200 OK from the CONNECT request
|
||||
InputStream input = socket.getInputStream();
|
||||
Response response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.code);
|
||||
assertEquals("200", response.getCode());
|
||||
|
||||
for (int i = 0; i < 10; ++i)
|
||||
{
|
||||
request = "" +
|
||||
"GET /echo" + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
assertEquals("GET /echo", response.getBody());
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void testCONNECTAndGETServerStop() throws Exception
|
||||
{
|
||||
String hostPort = "localhost:" + serverConnector.getLocalPort();
|
||||
String request = "" +
|
||||
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
Socket socket = newSocket();
|
||||
try
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
// Expect 200 OK from the CONNECT request
|
||||
Response response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
|
||||
request = "" +
|
||||
"GET /echo HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
assertEquals("GET /echo", response.getBody());
|
||||
|
||||
// Idle server is shut down
|
||||
server.stop();
|
||||
|
@ -189,28 +216,30 @@ public class ProxyHandlerConnectTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
public void testHttpConnectWithRequestServerClose() throws Exception
|
||||
public void testCONNECTAndGETAndServerSideClose() throws Exception
|
||||
{
|
||||
String hostPort = "localhost:" + serverConnector.getLocalPort();
|
||||
String request = "" +
|
||||
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||
Socket socket = newSocket();
|
||||
try
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
// Expect 200 OK from the CONNECT request
|
||||
InputStream input = socket.getInputStream();
|
||||
Response response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.code);
|
||||
assertEquals("200", response.getCode());
|
||||
|
||||
request = "" +
|
||||
"GET /close HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
@ -224,57 +253,103 @@ public class ProxyHandlerConnectTest extends TestCase
|
|||
}
|
||||
}
|
||||
|
||||
private Response readResponse(InputStream input) throws IOException
|
||||
public void testCONNECTAndPOSTAndGET() throws Exception
|
||||
{
|
||||
// Simplified parser for HTTP responses
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
|
||||
String line = reader.readLine();
|
||||
if (line == null)
|
||||
throw new EOFException();
|
||||
Matcher responseLine = Pattern.compile("HTTP/1\\.1\\s+(\\d+)").matcher(line);
|
||||
assertTrue(responseLine.lookingAt());
|
||||
String code = responseLine.group(1);
|
||||
|
||||
Map<String, String> headers = new LinkedHashMap<String, String>();
|
||||
while ((line = reader.readLine()) != null)
|
||||
String hostPort = "localhost:" + serverConnector.getLocalPort();
|
||||
String request = "" +
|
||||
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
Socket socket = newSocket();
|
||||
try
|
||||
{
|
||||
if (line.trim().length() == 0)
|
||||
break;
|
||||
OutputStream output = socket.getOutputStream();
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
Matcher header = Pattern.compile("([^:]+):\\s*(.*)").matcher(line);
|
||||
assertTrue(header.lookingAt());
|
||||
String headerName = header.group(1);
|
||||
String headerValue = header.group(2);
|
||||
headers.put(headerName.toLowerCase(), headerValue.toLowerCase());
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
// Expect 200 OK from the CONNECT request
|
||||
Response response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
|
||||
request = "" +
|
||||
"POST /echo HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"Content-Length: 5\r\n" +
|
||||
"\r\n" +
|
||||
"HELLO";
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
assertEquals("POST /echo\r\nHELLO", response.getBody());
|
||||
|
||||
request = "" +
|
||||
"GET /echo" + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
assertEquals("GET /echo", response.getBody());
|
||||
}
|
||||
|
||||
StringBuilder body = new StringBuilder();
|
||||
if (headers.containsKey("content-length"))
|
||||
finally
|
||||
{
|
||||
int length = Integer.parseInt(headers.get("content-length"));
|
||||
for (int i = 0; i < length; ++i)
|
||||
body.append((char)reader.read());
|
||||
socket.close();
|
||||
}
|
||||
else if ("chunked".equals(headers.get("transfer-encoding")))
|
||||
{
|
||||
while ((line = reader.readLine()) != null)
|
||||
{
|
||||
if ("0".equals(line))
|
||||
{
|
||||
reader.readLine();
|
||||
break;
|
||||
}
|
||||
|
||||
body.append(reader.readLine());
|
||||
reader.readLine();
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(code, headers, body.toString().trim());
|
||||
}
|
||||
|
||||
public class TestServlet extends HttpServlet
|
||||
public void testCONNECTAndPOSTWithBigBody() throws Exception
|
||||
{
|
||||
String hostPort = "localhost:" + serverConnector.getLocalPort();
|
||||
String request = "" +
|
||||
"CONNECT " + hostPort + " HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"\r\n";
|
||||
Socket socket = newSocket();
|
||||
try
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
|
||||
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
// Expect 200 OK from the CONNECT request
|
||||
Response response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
|
||||
StringBuilder body = new StringBuilder();
|
||||
String chunk = "0123456789ABCDEF";
|
||||
for (int i = 0; i < 1024; ++i)
|
||||
body.append(chunk);
|
||||
|
||||
request = "" +
|
||||
"POST /echo HTTP/1.1\r\n" +
|
||||
"Host: " + hostPort + "\r\n" +
|
||||
"Content-Length: " + body.length() + "\r\n" +
|
||||
"\r\n" +
|
||||
body;
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
response = readResponse(input);
|
||||
System.err.println(response);
|
||||
assertEquals("200", response.getCode());
|
||||
assertEquals("POST /echo\r\n" + body, response.getBody());
|
||||
}
|
||||
finally
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
||||
private class ServerHandler extends AbstractHandler
|
||||
|
@ -288,41 +363,30 @@ public class ProxyHandlerConnectTest extends TestCase
|
|||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(httpRequest.getMethod()).append(" ").append(uri);
|
||||
if (httpRequest.getQueryString() != null)
|
||||
builder.append("?").append(httpRequest.getQueryString());
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
InputStream input = httpRequest.getInputStream();
|
||||
int read = -1;
|
||||
while ((read = input.read()) >= 0)
|
||||
baos.write(read);
|
||||
baos.close();
|
||||
|
||||
System.err.println("server echoing:\r\n" + builder);
|
||||
ServletOutputStream output = httpResponse.getOutputStream();
|
||||
output.println(builder.toString());
|
||||
output.write(baos.toByteArray());
|
||||
}
|
||||
else if ("/close".equals(uri))
|
||||
{
|
||||
request.getConnection().getEndPoint().close();
|
||||
System.err.println("server closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Response
|
||||
{
|
||||
private final String code;
|
||||
private final Map<String, String> headers;
|
||||
private final String body;
|
||||
|
||||
private Response(String code, Map<String, String> headers, String body)
|
||||
{
|
||||
this.code = code;
|
||||
this.headers = headers;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append(code).append("\r\n");
|
||||
for (Map.Entry<String, String> entry : headers.entrySet())
|
||||
builder.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
|
||||
builder.append("\r\n");
|
||||
builder.append(body);
|
||||
return builder.toString();
|
||||
else
|
||||
{
|
||||
throw new ServletException();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,78 +0,0 @@
|
|||
package org.eclipse.jetty.server.handler;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
/**
|
||||
* @version $Revision$ $Date$
|
||||
*/
|
||||
public class ProxyHandlerSSLTest extends TestCase
|
||||
{
|
||||
/*
|
||||
private Server server;
|
||||
private Connector serverConnector;
|
||||
private Server proxy;
|
||||
private Connector proxyConnector;
|
||||
|
||||
@Override
|
||||
protected void setUp() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
serverConnector = new SslSelectChannelConnector();
|
||||
server.addConnector(serverConnector);
|
||||
// server.setHandler(new EchoHandler());
|
||||
server.start();
|
||||
|
||||
proxy = new Server();
|
||||
proxyConnector = new SslSelectChannelConnector();
|
||||
proxy.addConnector(proxyConnector);
|
||||
proxy.setHandler(new ProxyHandler()
|
||||
{
|
||||
@Override
|
||||
protected boolean isTunnelSecure(String host, int port)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
});
|
||||
proxy.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception
|
||||
{
|
||||
proxy.stop();
|
||||
proxy.join();
|
||||
|
||||
server.stop();
|
||||
server.join();
|
||||
}
|
||||
|
||||
public void testHttpConnectWithNormalRequest() throws Exception
|
||||
{
|
||||
String request = "" +
|
||||
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"\r\n";
|
||||
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||
try
|
||||
{
|
||||
OutputStream output = socket.getOutputStream();
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
// Expect 200 OK from the CONNECT request
|
||||
InputStream input = socket.getInputStream();
|
||||
// Response response = readResponse(input);
|
||||
// System.err.println(response);
|
||||
// assertEquals("200", response.code);
|
||||
|
||||
// Now what ? Upgrade the socket to SSL ?
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
*/
|
||||
public void test() {}
|
||||
}
|
Loading…
Reference in New Issue