diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
index 063cdbd5283..cf64c1c1c41 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ConnectHandler.java
@@ -20,43 +20,56 @@ package org.eclipse.jetty.proxy;
import java.io.IOException;
import java.net.InetSocketAddress;
-import java.net.SocketException;
-import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.HashSet;
import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executor;
+import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.http.HttpMethod;
+import org.eclipse.jetty.io.ByteBufferPool;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.io.MappedByteBufferPool;
+import org.eclipse.jetty.io.SelectChannelEndPoint;
import org.eclipse.jetty.io.SelectorManager;
import org.eclipse.jetty.server.Handler;
+import org.eclipse.jetty.server.HttpConnection;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.HandlerWrapper;
-import org.eclipse.jetty.util.HostMap;
+import org.eclipse.jetty.util.BufferUtil;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Scheduler;
+import org.eclipse.jetty.util.thread.TimerScheduler;
/**
*
Implementation of a {@link Handler} that supports HTTP CONNECT.
*/
public class ConnectHandler extends HandlerWrapper
{
- private static final Logger LOG = Log.getLogger(ConnectHandler.class);
+ protected static final Logger LOG = Log.getLogger(ConnectHandler.class);
- private final Set _whiteList = new HashSet<>();
- private final Set _blackList = new HashSet<>();
- private final SelectorManager _selector = new Manager(null, null, 1);
- private volatile int _connectTimeout = 15000;
- private volatile int _idleTimeout = 30000;
+ private final Set whiteList = new HashSet<>();
+ private final Set blackList = new HashSet<>();
+ private Executor executor;
+ private Scheduler scheduler;
+ private ByteBufferPool bufferPool;
+ private SelectorManager selector;
+ private long connectTimeout = 15000;
+ private long idleTimeout = 30000;
+ private int bufferSize = 4096;
public ConnectHandler()
{
@@ -68,51 +81,103 @@ public class ConnectHandler extends HandlerWrapper
setHandler(handler);
}
+ public Executor getExecutor()
+ {
+ return executor;
+ }
+
+ public void setExecutor(Executor executor)
+ {
+ this.executor = executor;
+ }
+
+ public Scheduler getScheduler()
+ {
+ return scheduler;
+ }
+
+ public void setScheduler(Scheduler scheduler)
+ {
+ this.scheduler = scheduler;
+ }
+
+ public ByteBufferPool getByteBufferPool()
+ {
+ return bufferPool;
+ }
+
+ public void setByteBufferPool(ByteBufferPool bufferPool)
+ {
+ this.bufferPool = bufferPool;
+ }
+
/**
* @return the timeout, in milliseconds, to connect to the remote server
*/
- public int getConnectTimeout()
+ public long getConnectTimeout()
{
- return _connectTimeout;
+ return connectTimeout;
}
/**
* @param connectTimeout the timeout, in milliseconds, to connect to the remote server
*/
- public void setConnectTimeout(int connectTimeout)
+ public void setConnectTimeout(long connectTimeout)
{
- _connectTimeout = connectTimeout;
+ this.connectTimeout = connectTimeout;
}
/**
* @return the idle timeout, in milliseconds
*/
- public int getIdleTimeout()
+ public long getIdleTimeout()
{
- return _idleTimeout;
+ return idleTimeout;
}
/**
* @param idleTimeout the idle timeout, in milliseconds
*/
- public void setIdleTimeout(int idleTimeout)
+ public void setIdleTimeout(long idleTimeout)
{
- _idleTimeout = idleTimeout;
+ this.idleTimeout = idleTimeout;
+ }
+
+ public int getBufferSize()
+ {
+ return bufferSize;
+ }
+
+ public void setBufferSize(int bufferSize)
+ {
+ this.bufferSize = bufferSize;
}
@Override
protected void doStart() throws Exception
{
- _selector.setConnectTimeout(getConnectTimeout());
- _selector.start();
+ if (executor == null)
+ {
+ setExecutor(getServer().getThreadPool());
+ }
+ if (scheduler == null)
+ {
+ setScheduler(new TimerScheduler());
+ addBean(getScheduler());
+ }
+ if (bufferPool == null)
+ {
+ setByteBufferPool(new MappedByteBufferPool());
+ addBean(getByteBufferPool());
+ }
+ addBean(selector = newSelectorManager());
+ selector.setConnectTimeout(getConnectTimeout());
super.doStart();
}
- @Override
- protected void doStop() throws Exception
+ protected SelectorManager newSelectorManager()
{
- super.doStop();
- _selector.stop();
+ return new Manager(getExecutor(), getScheduler(), 1);
}
@Override
@@ -120,15 +185,17 @@ public class ConnectHandler extends HandlerWrapper
{
if (HttpMethod.CONNECT.is(request.getMethod()))
{
- LOG.debug("CONNECT request for {}", request.getRequestURI());
+ String serverAddress = request.getRequestURI();
+ LOG.debug("CONNECT request for {}", serverAddress);
try
{
- handleConnect(baseRequest, request, response, request.getRequestURI());
+ handleConnect(baseRequest, request, response, serverAddress);
}
- catch(Exception e)
+ catch (Exception x)
{
- LOG.warn("ConnectHandler "+baseRequest.getUri()+" "+ e);
- LOG.debug(e);
+ // TODO
+ LOG.warn("ConnectHandler " + baseRequest.getUri() + " " + x);
+ LOG.debug(x);
}
}
else
@@ -142,129 +209,115 @@ public class ConnectHandler extends HandlerWrapper
* CONNECT requests may have authentication headers such as {@code Proxy-Authorization}
* that authenticate the client with the proxy.
*
- * @param jettyRequest Jetty-specific http request
+ * @param jettyRequest Jetty-specific http request
* @param request the http request
* @param response the http response
* @param serverAddress the remote server address in the form {@code host:port}
- * @throws ServletException if an application error occurs
- * @throws IOException if an I/O error occurs
*/
- protected void handleConnect(Request jettyRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress) throws ServletException, IOException
+ protected void handleConnect(Request jettyRequest, HttpServletRequest request, HttpServletResponse response, String serverAddress)
{
- boolean proceed = handleAuthentication(request, response, serverAddress);
- if (!proceed)
- return;
-
- String host = serverAddress;
- int port = 80;
- int colon = serverAddress.indexOf(':');
- if (colon > 0)
- {
- host = serverAddress.substring(0, colon);
- port = Integer.parseInt(serverAddress.substring(colon + 1));
- }
-
- if (!validateDestination(host))
- {
- LOG.info("ProxyHandler: Forbidden destination " + host);
- response.setStatus(HttpServletResponse.SC_FORBIDDEN);
- jettyRequest.setHandled(true);
- return;
- }
-
- SocketChannel channel = SocketChannel.open();
- channel.socket().setTcpNoDelay(true);
- channel.configureBlocking(false);
- channel.connect(new InetSocketAddress(host, port));
-
-// _selector.connect(channel, );
-
-// SocketChannel channel;
-
+ jettyRequest.setHandled(true);
try
{
- channel = connectToServer(request,host,port);
- }
- catch (SocketException se)
- {
- LOG.info("ConnectHandler: SocketException " + se.getMessage());
- response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- jettyRequest.setHandled(true);
- return;
- }
- catch (SocketTimeoutException ste)
- {
- LOG.info("ConnectHandler: SocketTimeoutException" + ste.getMessage());
- response.setStatus(HttpServletResponse.SC_GATEWAY_TIMEOUT);
- jettyRequest.setHandled(true);
- return;
- }
- catch (IOException ioe)
- {
- LOG.info("ConnectHandler: IOException" + ioe.getMessage());
- response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
- jettyRequest.setHandled(true);
- return;
- }
-
- // Transfer unread data from old connection to new connection
- // We need to copy the data to avoid races:
- // 1. when this unread data is written and the server replies before the clientToProxy
- // connection is installed (it is only installed after returning from this method)
- // 2. when the client sends data before this unread data has been written.
-
- /* TODO
- AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentHttpChannel();
- ByteBuffer headerBuffer = ((HttpParser)httpConnection.getParser()).getHeaderBuffer();
- ByteBuffer bodyBuffer = ((HttpParser)httpConnection.getParser()).getBodyBuffer();
- int length = headerBuffer == null ? 0 : headerBuffer.length();
- length += bodyBuffer == null ? 0 : bodyBuffer.length();
- IndirectNIOBuffer buffer = null;
- if (length > 0)
- {
- buffer = new IndirectNIOBuffer(length);
- if (headerBuffer != null)
+ boolean proceed = handleAuthentication(request, response, serverAddress);
+ if (!proceed)
{
- buffer.put(headerBuffer);
- headerBuffer.clear();
+ LOG.debug("Missing proxy authentication");
+ sendConnectResponse(request, response, HttpServletResponse.SC_PROXY_AUTHENTICATION_REQUIRED);
+ return;
}
- if (bodyBuffer != null)
+
+ String host = serverAddress;
+ int port = 80;
+ int colon = serverAddress.indexOf(':');
+ if (colon > 0)
{
- buffer.put(bodyBuffer);
- bodyBuffer.clear();
+ host = serverAddress.substring(0, colon);
+ port = Integer.parseInt(serverAddress.substring(colon + 1));
}
+
+ if (!validateDestination(host, port))
+ {
+ LOG.debug("Destination {}:{} forbidden", host, port);
+ sendConnectResponse(request, response, HttpServletResponse.SC_FORBIDDEN);
+ return;
+ }
+
+ SocketChannel channel = SocketChannel.open();
+ channel.socket().setTcpNoDelay(true);
+ channel.configureBlocking(false);
+ InetSocketAddress address = new InetSocketAddress(host, port);
+ channel.connect(address);
+
+ AsyncContext asyncContext = request.startAsync();
+ asyncContext.setTimeout(0);
+
+ LOG.debug("Connecting to {}", address);
+ ConnectContext connectContext = new ConnectContext(request, response, asyncContext, HttpConnection.getCurrentConnection());
+ selector.connect(channel, connectContext);
+ }
+ catch (Exception x)
+ {
+ onConnectFailure(request, response, null, x);
+ }
+ }
+
+ protected void onConnectSuccess(ConnectContext connectContext, UpstreamConnection upstreamConnection)
+ {
+ HttpConnection httpConnection = connectContext.getHttpConnection();
+ ByteBuffer requestBuffer = httpConnection.getRequestBuffer();
+ ByteBuffer buffer = BufferUtil.EMPTY_BUFFER;
+ int remaining = requestBuffer.remaining();
+ if (remaining > 0)
+ {
+ buffer = bufferPool.acquire(remaining, requestBuffer.isDirect());
+ BufferUtil.flipToFill(buffer);
+ buffer.put(requestBuffer);
+ buffer.flip();
}
- ConcurrentMap context = new ConcurrentHashMap();
+ ConcurrentMap context = connectContext.getContext();
+ HttpServletRequest request = connectContext.getRequest();
prepareContext(request, context);
- ClientToProxyConnection clientToProxy = prepareConnections(context, channel, buffer);
+ EndPoint downstreamEndPoint = httpConnection.getEndPoint();
+ DownstreamConnection downstreamConnection = newDownstreamConnection(downstreamEndPoint, context, buffer);
+ downstreamConnection.setInputBufferSize(getBufferSize());
- // CONNECT expects a 200 response
- response.setStatus(HttpServletResponse.SC_OK);
+ upstreamConnection.setConnection(downstreamConnection);
+ downstreamConnection.setConnection(upstreamConnection);
+ LOG.debug("Connection setup completed: {}<->{}", downstreamConnection, upstreamConnection);
- // Prevent close
- jettyRequest.getConnection().getGenerator().setPersistent(true);
+ HttpServletResponse response = connectContext.getResponse();
+ sendConnectResponse(request, response, HttpServletResponse.SC_OK);
- // Close to force last flush it so that the client receives it
- response.getOutputStream().close();
-
- upgradeConnection(request, response, clientToProxy);
- */
+ upgradeConnection(request, response, downstreamConnection);
+ connectContext.getAsyncContext().complete();
}
- /* TODO
- private ClientToProxyConnection prepareConnections(ConcurrentMap context, SocketChannel channel, ByteBuffer buffer)
+ protected void onConnectFailure(HttpServletRequest request, HttpServletResponse response, AsyncContext asyncContext, Throwable failure)
{
- AbstractHttpConnection httpConnection = AbstractHttpConnection.getCurrentHttpChannel();
- ProxyToServerConnection proxyToServer = newProxyToServerConnection(context, buffer);
- ClientToProxyConnection clientToProxy = newClientToProxyConnection(context, channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp());
- clientToProxy.setConnection(proxyToServer);
- proxyToServer.setConnection(clientToProxy);
- return clientToProxy;
- return null;
+ LOG.debug("CONNECT failed", failure);
+ sendConnectResponse(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
+ if (asyncContext != null)
+ asyncContext.complete();
+ }
+
+ private void sendConnectResponse(HttpServletRequest request, HttpServletResponse response, int statusCode)
+ {
+ try
+ {
+ response.setStatus(statusCode);
+ if (statusCode != HttpServletResponse.SC_OK)
+ response.setHeader(HttpHeader.CONNECTION.asString(), HttpHeaderValue.CLOSE.asString());
+ response.getOutputStream().close();
+ LOG.debug("CONNECT response sent {} {}", request.getProtocol(), response.getStatus());
+ }
+ catch (IOException x)
+ {
+ // TODO: nothing we can do, close the connection
+ }
}
- */
/**
* Handles the authentication before setting up the tunnel to the remote server.
@@ -274,95 +327,35 @@ public class ConnectHandler extends HandlerWrapper
* @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
+ protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address)
{
return true;
}
- /* TODO
- protected ClientToProxyConnection newClientToProxyConnection(ConcurrentMap context, SocketChannel channel, EndPoint endPoint, long timeStamp)
+ protected DownstreamConnection newDownstreamConnection(EndPoint endPoint, ConcurrentMap context, ByteBuffer buffer)
{
- return new ClientToProxyConnection(context, channel, endPoint, timeStamp);
+ return new DownstreamConnection(endPoint, getExecutor(), getByteBufferPool(), context, this, buffer);
}
- protected ProxyToServerConnection newProxyToServerConnection(ConcurrentMap context, ByteBuffer buffer)
+ protected UpstreamConnection newUpstreamConnection(EndPoint endPoint, ConnectContext connectContext)
{
- return new ProxyToServerConnection(context, buffer);
- }
- */
-
- // may return null
- private SocketChannel connectToServer(HttpServletRequest request, String host, int port) throws IOException
- {
- SocketChannel channel = connect(request, host, port);
- channel.configureBlocking(false);
- return channel;
- }
-
- /**
- * Establishes a connection to the remote server.
- *
- * @param request the HTTP request that initiated the tunnel
- * @param host the host to connect to
- * @param port the port to connect to
- * @return a {@link SocketChannel} connected to the remote server
- */
- protected SocketChannel connect(HttpServletRequest request, String host, int port) throws IOException
- {
- SocketChannel channel = null;
- try
- {
- channel = SocketChannel.open();
- if (channel == null)
- throw new IOException("Unable to connect to " + host + ":" + port);
-
- // Connect to remote server
- LOG.debug("Establishing connection to {}:{}", host, port);
- channel.socket().setTcpNoDelay(true);
- channel.socket().connect(new InetSocketAddress(host, port), getConnectTimeout());
- LOG.debug("Established connection to {}:{}", host, port);
- return channel;
- }
- catch (IOException x)
- {
- try
- {
- if (channel != null)
- channel.close();
- }
- catch (IOException xx)
- {
- LOG.ignore(xx);
- }
- LOG.debug("Failed to establish connection to " + host + ":" + port, x);
- throw x;
- }
+ return new UpstreamConnection(endPoint, getExecutor(), getByteBufferPool(), this, connectContext);
}
protected void prepareContext(HttpServletRequest request, ConcurrentMap context)
{
}
- private void upgradeConnection(HttpServletRequest request, HttpServletResponse response, Connection connection) throws IOException
+ private void upgradeConnection(HttpServletRequest request, HttpServletResponse response, Connection connection)
{
// 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);
+ request.setAttribute(HttpConnection.UPGRADE_CONNECTION_ATTRIBUTE, connection);
response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
LOG.debug("Upgraded connection to {}", connection);
}
- /* TODO
- private void register(SocketChannel channel, ProxyToServerConnection proxyToServer) throws IOException
- {
- _selector.register(channel, proxyToServer);
- proxyToServer.waitReady(_connectTimeout);
- }
- */
-
/**
* Reads (with non-blocking semantic) into the given {@code buffer} from the given {@code endPoint}.
*
@@ -379,50 +372,67 @@ public class ConnectHandler extends HandlerWrapper
}
/**
- * Writes (with blocking semantic) the given buffer of data onto the given endPoint.
+ * Writes (with non-blocking semantic) the given buffer of data onto the given endPoint.
*
* @param endPoint the endPoint to write to
* @param buffer the buffer to write
* @param context the context information related to the connection
- * @throws IOException if the buffer cannot be written
- * @return the number of bytes written
+ * @param callback the completion callback to invoke
*/
- protected int write(EndPoint endPoint, ByteBuffer buffer, ConcurrentMap context) throws IOException
+ protected void write(EndPoint endPoint, ByteBuffer buffer, ConcurrentMap context, Callback callback)
{
- /* TODO
- if (buffer == null)
- return 0;
-
- int length = buffer.length();
- final StringBuilder debug = LOG.isDebugEnabled()?new StringBuilder():null;
- int flushed = endPoint.flush(buffer);
- if (debug!=null)
- debug.append(flushed);
-
- // Loop until all written
- while (buffer.length()>0 && !endPoint.isOutputShutdown())
- {
- if (!endPoint.isBlocking())
- {
- boolean ready = endPoint.blockWritable(getIdleTimeout());
- if (!ready)
- throw new IOException("Write timeout");
- }
- flushed = endPoint.flush(buffer);
- if (debug!=null)
- debug.append("+").append(flushed);
- }
-
- LOG.debug("Written {}/{} bytes {}", debug, length, endPoint);
- buffer.compact();
- return length;
- */
- return -1;
+ endPoint.write(null, callback, buffer);
}
- // TODO
- private class Manager extends SelectorManager
+ public Set getWhiteListHosts()
{
+ return whiteList;
+ }
+
+ public Set getBlackListHosts()
+ {
+ return blackList;
+ }
+
+ /**
+ * Checks the given {@code host} and {@code port} against whitelist and blacklist.
+ *
+ * @param host the host to check
+ * @param port the port to check
+ * @return true if it is allowed to connect to the given host and port
+ */
+ public boolean validateDestination(String host, int port)
+ {
+ String hostPort = host + ":" + port;
+ if (!whiteList.isEmpty())
+ {
+ if (!whiteList.contains(hostPort))
+ {
+ LOG.debug("Host {}:{} not whitelisted", host, port);
+ return false;
+ }
+ }
+ if (!blackList.isEmpty())
+ {
+ if (blackList.contains(hostPort))
+ {
+ LOG.debug("Host {}:{} blacklisted", host, port);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void dump(Appendable out, String indent) throws IOException
+ {
+ dumpThis(out);
+ dump(out, indent, getBeans(), TypeUtil.asList(getHandlers()));
+ }
+
+ protected class Manager extends SelectorManager
+ {
+
private Manager(Executor executor, Scheduler scheduler, int selectors)
{
super(executor, scheduler, selectors);
@@ -431,557 +441,66 @@ public class ConnectHandler extends HandlerWrapper
@Override
protected EndPoint newEndPoint(SocketChannel channel, ManagedSelector selector, SelectionKey selectionKey) throws IOException
{
- return null;
+ return new SelectChannelEndPoint(channel, selector, selectionKey, getScheduler(), getIdleTimeout());
}
@Override
public Connection newConnection(SocketChannel channel, EndPoint endpoint, Object attachment) throws IOException
{
- return null;
- }
- // @Override
-// protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey key) throws IOException
-// {
-// SelectChannelEndPoint endp = new SelectChannelEndPoint(channel, selectSet, key, channel.socket().getSoTimeout());
-// endp.setConnection(selectSet.getSelectorManager().newConnection(channel,endp, key.attachment()));
-// endp.setIdleTimeout(_idleTimeout);
-// return endp;
-// }
-//
-// @Override
-// public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endpoint, Object attachment)
-// {
-// ProxyToServerConnection proxyToServer = (ProxyToServerConnection)attachment;
-// proxyToServer.setTimeStamp(System.currentTimeMillis());
-// proxyToServer.setEndPoint(endpoint);
-// return proxyToServer;
-// }
-//
-// @Override
-// protected void endPointOpened(SelectChannelEndPoint endpoint)
-// {
-// ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment();
-// proxyToServer.complete();
-// }
-//
-// @Override
-// public boolean dispatch(Runnable task)
-// {
-// return _threadPool.dispatch(task);
-// }
-//
-// @Override
-// protected void endPointClosed(AsyncEndPoint endpoint)
-// {
-// }
-//
-// @Override
-// protected void connectionUpgraded(ConnectedEndPoint endpoint, AsyncConnection oldConnection)
-// {
-// }
- }
-
-
-/*
- public class ProxyToServerConnection implements AsyncConnection
- {
- private final CountDownLatch _ready = new CountDownLatch(1);
- private final ByteBuffer _buffer = new IndirectNIOBuffer(4096);
- private final ConcurrentMap _context;
- private volatile ByteBuffer _data;
- private volatile ClientToProxyConnection _toClient;
- private volatile long _timestamp;
- private volatile AsyncEndPoint _endPoint;
-
- public ProxyToServerConnection(ConcurrentMap context, ByteBuffer data)
- {
- _context = context;
- _data = data;
+ ConnectHandler.LOG.debug("Connected to {}", channel.getRemoteAddress());
+ ConnectContext connectContext = (ConnectContext)attachment;
+ UpstreamConnection connection = newUpstreamConnection(endpoint, connectContext);
+ connection.setInputBufferSize(getBufferSize());
+ return connection;
}
@Override
- public String toString()
+ protected void connectionFailed(SocketChannel channel, Throwable ex, Object attachment)
{
- StringBuilder builder = new StringBuilder("ProxyToServer");
- builder.append("(:").write(_endPoint.getLocalPort());
- builder.append("<=>:").write(_endPoint.getRemotePort());
- return builder.append(")").toString();
- }
-
- public AsyncConnection handle() throws IOException
- {
- LOG.debug("{}: begin reading from server", this);
- try
- {
- writeData();
-
- while (true)
- {
- int read = read(_endPoint, _buffer, _context);
-
- if (read == -1)
- {
- LOG.debug("{}: server closed connection {}", this, _endPoint);
-
- if (_endPoint.isOutputShutdown() || !_endPoint.isOpen())
- closeClient();
- else
- _toClient.shutdownOutput();
-
- break;
- }
-
- if (read == 0)
- break;
-
- LOG.debug("{}: read from server {} bytes {}", this, read, _endPoint);
- int written = write(_toClient._endPoint, _buffer, _context);
- LOG.debug("{}: written to {} {} bytes", this, _toClient, written);
- }
- return this;
- }
- catch (ClosedChannelException x)
- {
- LOG.debug(x);
- throw x;
- }
- catch (IOException x)
- {
- LOG.warn(this + ": unexpected exception", x);
- close();
- throw x;
- }
- catch (RuntimeException x)
- {
- LOG.warn(this + ": unexpected exception", x);
- close();
- throw x;
- }
- finally
- {
- LOG.debug("{}: end reading from server", this);
- }
- }
-
- public void onInputShutdown() throws IOException
- {
- // TODO
- }
-
- private void writeData() throws IOException
- {
- // This method is called from handle() and closeServer()
- // which may happen concurrently (e.g. a client closing
- // while reading from the server), so needs synchronization
- synchronized (this)
- {
- if (_data != null)
- {
- try
- {
- int written = write(_endPoint, _data, _context);
- LOG.debug("{}: written to server {} bytes", this, written);
- }
- finally
- {
- // Attempt once to write the data; if the write fails (for example
- // because the connection is already closed), clear the data and
- // give up to avoid to continue to write data to a closed connection
- _data = null;
- }
- }
- }
- }
-
- public void setConnection(ClientToProxyConnection connection)
- {
- _toClient = connection;
- }
-
- public long getTimeStamp()
- {
- return _timestamp;
- }
-
- public void setTimeStamp(long timestamp)
- {
- _timestamp = timestamp;
- }
-
- public void setEndPoint(AsyncEndPoint endpoint)
- {
- _endPoint = endpoint;
- }
-
- public boolean isIdle()
- {
- return false;
- }
-
- public boolean isSuspended()
- {
- return false;
- }
-
- public void onClose()
- {
- }
-
- public void ready()
- {
- _ready.countDown();
- }
-
- public void waitReady(long timeout) throws IOException
- {
- try
- {
- _ready.await(timeout, TimeUnit.MILLISECONDS);
- }
- catch (final InterruptedException x)
- {
- throw new IOException()
- {{
- initCause(x);
- }};
- }
- }
-
- public void closeClient() throws IOException
- {
- _toClient.closeClient();
- }
-
- public void closeServer() throws IOException
- {
- _endPoint.close();
- }
-
- public void close()
- {
- try
- {
- closeClient();
- }
- catch (IOException x)
- {
- LOG.debug(this + ": unexpected exception closing the client", x);
- }
-
- try
- {
- closeServer();
- }
- catch (IOException x)
- {
- LOG.debug(this + ": unexpected exception closing the server", x);
- }
- }
-
- public void shutdownOutput() throws IOException
- {
- writeData();
- _endPoint.shutdownOutput();
- }
-
- public void onIdleExpired(long idleForMs)
- {
- try
- {
- shutdownOutput();
- }
- catch(Exception e)
- {
- LOG.debug(e);
- close();
- }
+ ConnectContext connectContext = (ConnectContext)attachment;
+ onConnectFailure(connectContext.request, connectContext.response, connectContext.asyncContext, ex);
}
}
- public class ClientToProxyConnection implements AsyncConnection
+ protected static class ConnectContext
{
- private final ByteBuffer _buffer = new IndirectNIOBuffer(4096);
- private final ConcurrentMap _context;
- private final SocketChannel _channel;
- private final EndPoint _endPoint;
- private final long _timestamp;
- private volatile ProxyToServerConnection _toServer;
- private boolean _firstTime = true;
+ private final ConcurrentMap context = new ConcurrentHashMap<>();
+ private final HttpServletRequest request;
+ private final HttpServletResponse response;
+ private final AsyncContext asyncContext;
+ private final HttpConnection httpConnection;
- public ClientToProxyConnection(ConcurrentMap context, SocketChannel channel, EndPoint endPoint, long timestamp)
+ public ConnectContext(HttpServletRequest request, HttpServletResponse response, AsyncContext asyncContext, HttpConnection httpConnection)
{
- _context = context;
- _channel = channel;
- _endPoint = endPoint;
- _timestamp = timestamp;
+ this.request = request;
+ this.response = response;
+ this.asyncContext = asyncContext;
+ this.httpConnection = httpConnection;
}
- @Override
- public String toString()
+ public ConcurrentMap getContext()
{
- StringBuilder builder = new StringBuilder("ClientToProxy");
- builder.append("(:").write(_endPoint.getLocalPort());
- builder.append("<=>:").write(_endPoint.getRemotePort());
- return builder.append(")").toString();
+ return context;
}
- public AsyncConnection handle() throws IOException
+ public HttpServletRequest getRequest()
{
- LOG.debug("{}: begin reading from client", this);
- try
- {
- if (_firstTime)
- {
- _firstTime = false;
- register(_channel, _toServer);
- LOG.debug("{}: registered channel {} with connection {}", this, _channel, _toServer);
- }
-
- while (true)
- {
- int read = read(_endPoint, _buffer, _context);
-
- if (read == -1)
- {
- LOG.debug("{}: client closed connection {}", this, _endPoint);
-
- if (_endPoint.isOutputShutdown() || !_endPoint.isOpen())
- closeServer();
- else
- _toServer.shutdownOutput();
-
- break;
- }
-
- if (read == 0)
- break;
-
- LOG.debug("{}: read from client {} bytes {}", this, read, _endPoint);
- int written = write(_toServer._endPoint, _buffer, _context);
- LOG.debug("{}: written to {} {} bytes", this, _toServer, written);
- }
- return this;
- }
- catch (ClosedChannelException x)
- {
- LOG.debug(x);
- closeServer();
- throw x;
- }
- catch (IOException x)
- {
- LOG.warn(this + ": unexpected exception", x);
- close();
- throw x;
- }
- catch (RuntimeException x)
- {
- LOG.warn(this + ": unexpected exception", x);
- close();
- throw x;
- }
- finally
- {
- LOG.debug("{}: end reading from client", this);
- }
+ return request;
}
- public void onInputShutdown() throws IOException
+ public HttpServletResponse getResponse()
{
- // TODO
+ return response;
}
- public long getTimeStamp()
+ public AsyncContext getAsyncContext()
{
- return _timestamp;
+ return asyncContext;
}
- public boolean isIdle()
+ public HttpConnection getHttpConnection()
{
- return false;
+ return httpConnection;
}
-
- public boolean isSuspended()
- {
- return false;
- }
-
- public void onClose()
- {
- }
-
- public void setConnection(ProxyToServerConnection connection)
- {
- _toServer = connection;
- }
-
- public void closeClient() throws IOException
- {
- _endPoint.close();
- }
-
- public void closeServer() throws IOException
- {
- _toServer.closeServer();
- }
-
- public void close()
- {
- try
- {
- closeClient();
- }
- catch (IOException x)
- {
- LOG.debug(this + ": unexpected exception closing the client", x);
- }
-
- try
- {
- closeServer();
- }
- catch (IOException x)
- {
- LOG.debug(this + ": unexpected exception closing the server", x);
- }
- }
-
- public void shutdownOutput() throws IOException
- {
- _endPoint.shutdownOutput();
- }
-
- public void onIdleExpired(long idleForMs)
- {
- try
- {
- shutdownOutput();
- }
- catch(Exception e)
- {
- LOG.debug(e);
- close();
- }
- }
- }
-
- /**
- * Adds the given {@code host} to the whitelist
- *
- * @param host the whitelisted host
- */
- public void addWhitelistHost(String host)
- {
- _whiteList.add(host);
- }
-
- /**
- * Adds the given {@code host} to the blacklist
- *
- * @param host the blacklisted host
- */
- public void addBlacklistHost(String host)
- {
- _blackList.add(host);
- }
-
- /**
- * Re-initialize the whitelist of existing handler object
- *
- * @param entries array of whitelist entries
- */
- public void setWhite(String[] entries)
- {
-// set(entries, _whiteList);
- }
-
- /**
- * Re-initialize the blacklist of existing handler object
- *
- * @param entries array of blacklist entries
- */
- public void setBlack(String[] entries)
- {
-// set(entries, _blackList);
- }
-
- /**
- * Helper method to process a list of new entries and replace
- * the content of the specified host map
- *
- * @param entries new entries
- * @param hostMap target host map
- */
- protected void set(String[] entries, HostMap hostMap)
- {
- hostMap.clear();
-
- if (entries != null && entries.length > 0)
- {
- for (String addrPath : entries)
- {
- add(addrPath, hostMap);
- }
- }
- }
-
- /**
- * Helper method to process the new entry and add it to
- * the specified host map.
- *
- * @param entry new entry
- * @param hostMap target host map
- */
- private void add(String entry, HostMap hostMap)
- {
- if (entry != null && entry.length() > 0)
- {
- entry = entry.trim();
- if (hostMap.get(entry) == null)
- {
- hostMap.put(entry, entry);
- }
- }
- }
-
- /**
- * Check the request hostname against white- and blacklist.
- *
- * @param host hostname to check
- * @return true if hostname is allowed to be proxied
- */
- public boolean validateDestination(String host)
- {
- if (_whiteList.size() > 0)
- {
-// Object whiteObj = _whiteList.getLazyMatches(host);
-// if (whiteObj == null)
- {
- return false;
- }
- }
-
- if (_blackList.size() > 0)
- {
-// Object blackObj = _blackList.getLazyMatches(host);
-// if (blackObj != null)
- {
- return false;
- }
- }
-
- return true;
- }
-
- @Override
- public void dump(Appendable out, String indent) throws IOException
- {
- dumpThis(out);
- /* TODO
- if (_privateThreadPool)
- dump(out, indent, Arrays.asList(_threadPool, _selector), TypeUtil.asList(getHandlers()), getBeans());
- else
- dump(out, indent, Arrays.asList(_selector), TypeUtil.asList(getHandlers()), getBeans());
- */
}
}
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/DownstreamConnection.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/DownstreamConnection.java
new file mode 100644
index 00000000000..24a241f183b
--- /dev/null
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/DownstreamConnection.java
@@ -0,0 +1,62 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.proxy;
+
+import java.nio.ByteBuffer;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.Callback;
+
+public class DownstreamConnection extends ProxyConnection
+{
+ private final ByteBuffer buffer;
+
+ public DownstreamConnection(EndPoint endPoint, Executor executor, ByteBufferPool bufferPool, ConcurrentMap context, ConnectHandler connectHandler, ByteBuffer buffer)
+ {
+ super(endPoint, executor, bufferPool, context, connectHandler);
+ this.buffer = buffer;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ final int remaining = buffer.remaining();
+ write(buffer, new Callback()
+ {
+ @Override
+ public void completed(Void context)
+ {
+ LOG.debug("{} wrote initial {} bytes to server", DownstreamConnection.this, remaining);
+ fillInterested();
+ }
+
+ @Override
+ public void failed(Void context, Throwable x)
+ {
+ LOG.debug(this + " failed to write initial " + remaining + " bytes to server", x);
+ close();
+ getConnection().close();
+ }
+ });
+ }
+}
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyConnection.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyConnection.java
new file mode 100644
index 00000000000..67a1037748a
--- /dev/null
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyConnection.java
@@ -0,0 +1,171 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.proxy;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.io.AbstractConnection;
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.Connection;
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.util.Callback;
+import org.eclipse.jetty.util.ForkInvoker;
+import org.eclipse.jetty.util.log.Logger;
+
+public abstract class ProxyConnection extends AbstractConnection
+{
+ protected static final Logger LOG = ConnectHandler.LOG;
+ private final ForkInvoker invoker = new ProxyForkInvoker();
+ private final ByteBufferPool bufferPool;
+ private final ConcurrentMap context;
+ private final ConnectHandler connectHandler;
+ private Connection connection;
+
+ protected ProxyConnection(EndPoint endp, Executor executor, ByteBufferPool bufferPool, ConcurrentMap context, ConnectHandler connectHandler)
+ {
+ super(endp, executor);
+ this.bufferPool = bufferPool;
+ this.context = context;
+ this.connectHandler = connectHandler;
+ }
+
+ public ByteBufferPool getByteBufferPool()
+ {
+ return bufferPool;
+ }
+
+ public ConcurrentMap getContext()
+ {
+ return context;
+ }
+
+ public ConnectHandler getConnectHandler()
+ {
+ return connectHandler;
+ }
+
+ public Connection getConnection()
+ {
+ return connection;
+ }
+
+ public void setConnection(Connection connection)
+ {
+ this.connection = connection;
+ }
+
+ @Override
+ public void onFillable()
+ {
+ ByteBuffer buffer = getByteBufferPool().acquire(getInputBufferSize(), true);
+ fill(buffer);
+ }
+
+ private void fill(final ByteBuffer buffer)
+ {
+ try
+ {
+ final int filled = connectHandler.read(getEndPoint(), buffer, getContext());
+ LOG.debug("{} filled {} bytes", this, filled);
+ if (filled > 0)
+ {
+ write(buffer, new Callback()
+ {
+ @Override
+ public void completed(Void context)
+ {
+ LOG.debug("{} wrote {} bytes", this, filled);
+ buffer.clear();
+ invoker.invoke(buffer);
+ }
+
+ @Override
+ public void failed(Void context, Throwable x)
+ {
+ LOG.debug(this + " failed to write " + filled + " bytes", x);
+ bufferPool.release(buffer);
+ connection.close();
+ }
+ });
+ }
+ else if (filled == 0)
+ {
+ bufferPool.release(buffer);
+ fillInterested();
+ }
+ else
+ {
+ bufferPool.release(buffer);
+ connection.getEndPoint().shutdownOutput();
+ }
+ }
+ catch (IOException x)
+ {
+ LOG.debug(this + " could not fill", x);
+ bufferPool.release(buffer);
+ close();
+ connection.close();
+ }
+ }
+
+ protected void write(ByteBuffer buffer, Callback callback)
+ {
+ LOG.debug("{} writing {} bytes", this, buffer.remaining());
+ connectHandler.write(getConnection().getEndPoint(), buffer, context, callback);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("%s[l:%d<=>r:%d]",
+ super.toString(),
+ getEndPoint().getLocalAddress().getPort(),
+ getEndPoint().getRemoteAddress().getPort());
+ }
+
+ private class ProxyForkInvoker extends ForkInvoker
+ {
+ private ProxyForkInvoker()
+ {
+ super(4);
+ }
+
+ @Override
+ public void fork(final ByteBuffer buffer)
+ {
+ getExecutor().execute(new Runnable()
+ {
+ @Override
+ public void run()
+ {
+ call(buffer);
+ }
+ });
+ }
+
+ @Override
+ public void call(ByteBuffer buffer)
+ {
+ fill(buffer);
+ }
+ }
+}
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
index 99d1ca247f9..b5656586148 100644
--- a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/ProxyServlet.java
@@ -124,11 +124,11 @@ public class ProxyServlet extends HttpServlet
String whiteList = config.getInitParameter("whiteList");
if (whiteList != null)
- getWhitelistHosts().addAll(parseList(whiteList));
+ getWhiteListHosts().addAll(parseList(whiteList));
String blackList = config.getInitParameter("blackList");
if (blackList != null)
- getBlacklistHosts().addAll(parseList(blackList));
+ getBlackListHosts().addAll(parseList(blackList));
}
catch (Exception e)
{
@@ -146,12 +146,12 @@ public class ProxyServlet extends HttpServlet
this._timeout = timeout;
}
- public Set getWhitelistHosts()
+ public Set getWhiteListHosts()
{
return _whiteList;
}
- public Set getBlacklistHosts()
+ public Set getBlackListHosts()
{
return _blackList;
}
@@ -313,12 +313,11 @@ public class ProxyServlet extends HttpServlet
}
/**
- * Checks the request hostname and path against whitelist and blacklist.
+ * Checks the given {@code host} and {@code port} against whitelist and blacklist.
*
- *
- * @param host host to check
+ * @param host the host to check
* @param port the port to check
- * @return true if request is allowed to be proxied
+ * @return true if it is allowed to be proxy to the given host and port
*/
public boolean validateDestination(String host, int port)
{
diff --git a/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/UpstreamConnection.java b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/UpstreamConnection.java
new file mode 100644
index 00000000000..43717e40e7d
--- /dev/null
+++ b/jetty-proxy/src/main/java/org/eclipse/jetty/proxy/UpstreamConnection.java
@@ -0,0 +1,43 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.proxy;
+
+import java.util.concurrent.Executor;
+
+import org.eclipse.jetty.io.ByteBufferPool;
+import org.eclipse.jetty.io.EndPoint;
+
+public class UpstreamConnection extends ProxyConnection
+{
+ private ConnectHandler.ConnectContext connectContext;
+
+ public UpstreamConnection(EndPoint endPoint, Executor executor, ByteBufferPool bufferPool, ConnectHandler connectHandler, ConnectHandler.ConnectContext connectContext)
+ {
+ super(endPoint, executor, bufferPool, connectContext.getContext(), connectHandler);
+ this.connectContext = connectContext;
+ }
+
+ @Override
+ public void onOpen()
+ {
+ super.onOpen();
+ getConnectHandler().onConnectSuccess(connectContext, this);
+ fillInterested();
+ }
+}
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AbstractConnectHandlerTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AbstractConnectHandlerTest.java
index ec55d5e9c71..57297e3b73f 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AbstractConnectHandlerTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/AbstractConnectHandlerTest.java
@@ -18,180 +18,62 @@
package org.eclipse.jetty.proxy;
-/**
- * @version $Revision$ $Date$
- */
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.net.Socket;
+
+import org.eclipse.jetty.server.Connector;
+import org.eclipse.jetty.server.NetworkConnector;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.toolchain.test.http.SimpleHttpParser;
+import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
+import org.junit.After;
+
public abstract class AbstractConnectHandlerTest
{
-// protected static Server server;
-// protected static Connector.NetConnector serverConnector;
-// protected static Server proxy;
-// protected static Connector proxyConnector;
-//
-// protected static void startServer(Connector.NetConnector connector, Handler handler) throws Exception
-// {
-// server = new Server();
-// serverConnector = connector;
-// server.addConnector(serverConnector);
-// server.setHandler(handler);
-// server.start();
-// }
-//
-// protected static void startProxy() throws Exception
-// {
-// proxy = new Server();
-// proxyConnector = new SelectChannelConnector();
-// proxy.addConnector(proxyConnector);
-// proxy.setHandler(new ConnectHandler());
-// proxy.start();
-// }
-//
-// @AfterClass
-// public static void stop() throws Exception
-// {
-// stopProxy();
-// stopServer();
-// }
-//
-// protected static void stopServer() throws Exception
-// {
-// server.stop();
-// server.join();
-// }
-//
-// protected static void stopProxy() throws Exception
-// {
-// proxy.stop();
-// proxy.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 headers = new LinkedHashMap();
-// 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;
-// if (headers.containsKey("content-length"))
-// {
-// int readLen = 0;
-// int length = Integer.parseInt(headers.get("content-length"));
-// body=new StringBuilder(length);
-// try
-// {
-// for (int i = 0; i < length; ++i)
-// {
-// char c = (char)reader.read();
-// body.append(c);
-// readLen++;
-//
-// }
-//
-// }
-// catch (SocketTimeoutException e)
-// {
-// System.err.printf("Read %,d bytes (out of an expected %,d bytes)%n",readLen,length);
-// throw e;
-// }
-// }
-// else if ("chunked".equals(headers.get("transfer-encoding")))
-// {
-// body = new StringBuilder(64*1024);
-// while ((line = reader.readLine()) != null)
-// {
-// if ("0".equals(line))
-// {
-// line = reader.readLine();
-// assertEquals("", line);
-// break;
-// }
-//
-// try
-// {
-// Thread.sleep(5);
-// }
-// catch (InterruptedException e)
-// {
-// e.printStackTrace();
-// }
-//
-// 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);
-// }
-// }
-// else throw new IllegalStateException();
-//
-// return new Response(code, headers, body.toString().trim());
-// }
-//
-// protected Socket newSocket() throws IOException
-// {
-// Socket socket = new Socket("localhost", ((Connector.NetConnector)proxyConnector).getLocalPort());
-// socket.setSoTimeout(10000);
-// return socket;
-// }
-//
-// protected class Response
-// {
-// private final String code;
-// private final Map headers;
-// private final String body;
-//
-// private Response(String code, Map headers, String body)
-// {
-// this.code = code;
-// this.headers = headers;
-// this.body = body;
-// }
-//
-// public String getCode()
-// {
-// return code;
-// }
-//
-// public Map 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 entry : headers.entrySet())
-// builder.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
-// builder.append("\r\n");
-// builder.append(body);
-// return builder.toString();
-// }
-// }
+ protected Server server;
+ protected ServerConnector serverConnector;
+ protected Server proxy;
+ protected Connector proxyConnector;
+ protected ConnectHandler connectHandler;
+
+ protected void prepareProxy() throws Exception
+ {
+ proxy = new Server();
+ proxyConnector = new ServerConnector(proxy);
+ proxy.addConnector(proxyConnector);
+ connectHandler = new ConnectHandler();
+ proxy.setHandler(connectHandler);
+ proxy.start();
+ }
+
+ @After
+ public void dispose() throws Exception
+ {
+ disposeServer();
+ disposeProxy();
+ }
+
+ protected void disposeServer() throws Exception
+ {
+ server.stop();
+ }
+
+ protected void disposeProxy() throws Exception
+ {
+ proxy.stop();
+ }
+
+ protected SimpleHttpResponse readResponse(BufferedReader reader) throws IOException
+ {
+ return new SimpleHttpParser().readResponse(reader);
+ }
+
+ protected Socket newSocket() throws IOException
+ {
+ Socket socket = new Socket("localhost", ((NetworkConnector)proxyConnector).getLocalPort());
+ socket.setSoTimeout(5000);
+ return socket;
+ }
}
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerSSLTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerSSLTest.java
index d6b8aaf0d10..53e9079da42 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerSSLTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerSSLTest.java
@@ -18,199 +18,184 @@
package org.eclipse.jetty.proxy;
-import org.junit.Ignore;
+import java.io.BufferedReader;
+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 javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.SSLSocketFactory;
+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.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
+import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
+import org.eclipse.jetty.util.ssl.SslContextFactory;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
-/**
- * @version $Revision$ $Date$
- */
-@Ignore
public class ConnectHandlerSSLTest extends AbstractConnectHandlerTest
{
-// @BeforeClass
-// public static void init() throws Exception
-// {
-// SslSelectChannelConnector connector = new SslSelectChannelConnector();
-// connector.setMaxIdleTime(3600000); // TODO remove
-//
-// String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore").getAbsolutePath();
-// SslContextFactory cf = connector.getSslContextFactory();
-// cf.setKeyStorePath(keyStorePath);
-// cf.setKeyStorePassword("storepwd");
-// cf.setKeyManagerPassword("keypwd");
-//
-// startServer(connector, new ServerHandler());
-// startProxy();
-// }
-//
-// @Test
-// public void testGETRequest() throws Exception
-// {
-// String hostPort = "localhost:" + ((Connector.NetConnector)serverConnector).getLocalPort();
-// String request = "" +
-// "CONNECT " + hostPort + " HTTP/1.1\r\n" +
-// "Host: " + hostPort + "\r\n" +
-// "\r\n";
-// Socket socket = newSocket();
-// socket.setSoTimeout(3600000); // TODO remove
-// 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);
-// assertEquals("200", response.getCode());
-// assertEquals("GET /echo", response.getBody());
-// }
-// finally
-// {
-// sslSocket.close();
-// }
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// @Test
-// public void testPOSTRequests() throws Exception
-// {
-// String hostPort = "localhost:" + ((Connector.NetConnector)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);
-// 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);
-// 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 new X509Certificate[]{};
-// }
-// }
-//
-// 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))
-// {
-// 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();
-//
-// ServletOutputStream output = httpResponse.getOutputStream();
-// output.println(builder.toString());
-// output.write(baos.toByteArray());
-// }
-// else
-// {
-// throw new ServletException();
-// }
-// }
-// }
+ private SslContextFactory sslContextFactory;
+
+ @Before
+ public void prepare() throws Exception
+ {
+ sslContextFactory = new SslContextFactory();
+ String keyStorePath = MavenTestingUtils.getTestResourceFile("keystore.jks").getAbsolutePath();
+ sslContextFactory.setKeyStorePath(keyStorePath);
+ sslContextFactory.setKeyStorePassword("storepwd");
+ String trustStorePath = MavenTestingUtils.getTestResourceFile("truststore.jks").getAbsolutePath();
+ sslContextFactory.setTrustStorePath(trustStorePath);
+ sslContextFactory.setTrustStorePassword("storepwd");
+ server = new Server();
+ serverConnector = new ServerConnector(server, sslContextFactory);
+ server.addConnector(serverConnector);
+ server.setHandler(new ServerHandler());
+ server.start();
+ prepareProxy();
+ }
+
+ @Test
+ 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";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+
+ // Be sure the buffered input does not have anything buffered
+ Assert.assertFalse(input.ready());
+
+ // Upgrade the socket to SSL
+ try (SSLSocket sslSocket = wrapSocket(socket))
+ {
+ 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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+ }
+ }
+ }
+
+ @Test
+ 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";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+
+ // Be sure the buffered input does not have anything buffered
+ Assert.assertFalse(input.ready());
+
+ // Upgrade the socket to SSL
+ try (SSLSocket sslSocket = wrapSocket(socket))
+ {
+ 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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("POST /echo?param=" + i + "\r\nHELLO", response.getBody());
+ }
+ }
+ }
+ }
+
+ private SSLSocket wrapSocket(Socket socket) throws Exception
+ {
+ SSLContext sslContext = sslContextFactory.getSslContext();
+ SSLSocketFactory socketFactory = sslContext.getSocketFactory();
+ SSLSocket sslSocket = (SSLSocket)socketFactory.createSocket(socket, socket.getInetAddress().getHostAddress(), socket.getPort(), true);
+ sslSocket.setUseClientMode(true);
+ sslSocket.startHandshake();
+ return sslSocket;
+ }
+
+ 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))
+ {
+ 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;
+ while ((read = input.read()) >= 0)
+ baos.write(read);
+ baos.close();
+
+ ServletOutputStream output = httpResponse.getOutputStream();
+ output.println(builder.toString());
+ output.write(baos.toByteArray());
+ }
+ else
+ {
+ throw new ServletException();
+ }
+ }
+ }
}
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java
index 0f2bb72df0c..0fcd031115c 100644
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java
+++ b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerTest.java
@@ -18,634 +18,780 @@
package org.eclipse.jetty.proxy;
-import org.junit.Ignore;
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.UnknownHostException;
+import java.nio.ByteBuffer;
+import java.util.Locale;
+import java.util.concurrent.ConcurrentMap;
+import javax.servlet.ServletException;
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.io.EndPoint;
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
+import org.eclipse.jetty.util.B64Code;
+import org.eclipse.jetty.util.Callback;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
-@Ignore
public class ConnectHandlerTest extends AbstractConnectHandlerTest
{
-// @BeforeClass
-// public static void init() throws Exception
-// {
-// startServer(new SelectChannelConnector(), new ServerHandler());
-// startProxy();
-// }
-//
-// @Test
-// public void testCONNECT() throws Exception
-// {
-// String hostPort = "localhost:" + ((Connector.NetConnector)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);
-// assertEquals("200", response.getCode());
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// @Test
-// public void testCONNECTAndGET() throws Exception
-// {
-// String hostPort = "localhost:" + ((Connector.NetConnector)serverConnector).getLocalPort();
-// String request = "" +
-// "CONNECT " + hostPort + " HTTP/1.1\r\n" +
-// "Host: " + hostPort + "\r\n" +
-// "\r\n";
-// Socket socket = newSocket();
-// socket.setSoTimeout(30000);
-// 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);
-// 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);
-// assertEquals("200", response.getCode());
-// assertEquals("GET /echo", response.getBody());
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-//
-// @Test
-// public void testCONNECTBadHostPort() throws Exception
-// {
-// String invalidHostname = "AMAZEBALLS_BADHOST.webtide.com";
-//
-// try
-// {
-// InetAddress addr = InetAddress.getByName(invalidHostname);
-// StringBuilder err = new StringBuilder();
-// err.append("DNS Hijacking detected: ");
-// err.append(invalidHostname).append(" should have not returned a valid IP address [");
-// err.append(addr.getHostAddress()).append("]. ");
-// err.append("Fix your DNS provider to have this test pass.");
-// err.append("\nFor more info see https://en.wikipedia.org/wiki/DNS_hijacking");
-// Assert.assertNull(err.toString(), addr);
-// }
-// catch (UnknownHostException e)
-// {
-// // expected path
-// }
-//
-// String hostPort = String.format("%s:%d",invalidHostname,serverConnector.getLocalPort());
-// String request = "" +
-// "CONNECT " + hostPort + " HTTP/1.1\r\n" +
-// "Host: " + hostPort + "\r\n" +
-// "\r\n";
-// Socket socket = newSocket();
-// socket.setSoTimeout(30000);
-// try
-// {
-// OutputStream output = socket.getOutputStream();
-// BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
-//
-// output.write(request.getBytes("UTF-8"));
-// output.flush();
-//
-// // Expect 500 OK from the CONNECT request
-// Response response = readResponse(input);
-// assertEquals("Response Code", "500", response.getCode());
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// @Test
-// public void testCONNECT10AndGET() throws Exception
-// {
-// String hostPort = "localhost:" + ((Connector.NetConnector)serverConnector).getLocalPort();
-// String request = "" +
-// "CONNECT " + hostPort + " HTTP/1.0\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);
-// 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);
-// assertEquals("200", response.getCode());
-// assertEquals("GET /echo", response.getBody());
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// @Test
-// public void testCONNECTAndGETPipelined() throws Exception
-// {
-// String hostPort = "localhost:" + ((Connector.NetConnector)serverConnector).getLocalPort();
-// String request = "" +
-// "CONNECT " + hostPort + " HTTP/1.1\r\n" +
-// "Host: " + hostPort + "\r\n" +
-// "\r\n" +
-// "GET /echo" + " 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);
-// assertEquals("200", response.getCode());
-//
-// // The pipelined request must have gone up to the server as is
-// response = readResponse(input);
-// assertEquals("200", response.getCode());
-// assertEquals("GET /echo", response.getBody());
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// @Test
-// public void testCONNECTAndMultipleGETs() throws Exception
-// {
-// String hostPort = "localhost:" + ((Connector.NetConnector)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);
-// 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);
-// assertEquals("200", response.getCode());
-// assertEquals("GET /echo", response.getBody());
-// }
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// @Test
-// public void testCONNECTAndGETServerStop() throws Exception
-// {
-// String hostPort = "localhost:" + ((Connector.NetConnector)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);
-// 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);
-// assertEquals("200", response.getCode());
-// assertEquals("GET /echo", response.getBody());
-//
-// // Idle server is shut down
-// stopServer();
-//
-// int read = input.read();
-// assertEquals(-1, read);
-// }
-// finally
-// {
-// socket.close();
-// // Restart the server for the next test
-// server.start();
-// }
-// }
-//
-// @Test
-// public void testCONNECTAndGETAndServerSideClose() throws Exception
-// {
-// String hostPort = "localhost:" + ((Connector.NetConnector)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);
-// assertEquals("200", response.getCode());
-//
-// request = "" +
-// "GET /close HTTP/1.1\r\n" +
-// "Host: " + hostPort + "\r\n" +
-// "\r\n";
-// output.write(request.getBytes("UTF-8"));
-// output.flush();
-//
-// int read = input.read();
-// assertEquals(-1, read);
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// @Test
-// public void testCONNECTAndPOSTAndGET() throws Exception
-// {
-// String hostPort = "localhost:" + ((Connector.NetConnector)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);
-// 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);
-// 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);
-// assertEquals("200", response.getCode());
-// assertEquals("GET /echo", response.getBody());
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// @Test
-// public void testCONNECTAndPOSTWithBigBody() throws Exception
-// {
-// String hostPort = "localhost:" + ((Connector.NetConnector)serverConnector).getLocalPort();
-// // fails under windows and occasionally on mac due to OOME
-// boolean stress = Boolean.getBoolean( "STRESS" );
-//
-// if (!stress)
-// {
-// return;
-// }
-//
-// // Log.getLogger(ConnectHandler.class).setDebugEnabled(true);
-// 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);
-// assertEquals("200", response.getCode());
-//
-// StringBuilder body = new StringBuilder();
-// String chunk = "0123456789ABCDEF";
-// for (int i = 0; i < 1024 * 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);
-// assertEquals("200", response.getCode());
-// assertEquals("POST /echo\r\n" + body, response.getBody());
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// @Test
-// public void testCONNECTAndPOSTWithContext() throws Exception
-// {
-// final String contextKey = "contextKey";
-// final String contextValue = "contextValue";
-//
-// // Replace the default ProxyHandler with a subclass to test context information passing
-// stopProxy();
-// proxy.setHandler(new ConnectHandler()
-// {
-// @Override
-// protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address) throws ServletException, IOException
-// {
-// request.setAttribute(contextKey, contextValue);
-// return super.handleAuthentication(request, response, address);
-// }
-//
-// @Override
-// protected SocketChannel connect(HttpServletRequest request, String host, int port) throws IOException
-// {
-// assertEquals(contextValue, request.getAttribute(contextKey));
-// return super.connect(request, host, port);
-// }
-//
-// @Override
-// protected void prepareContext(HttpServletRequest request, ConcurrentMap context)
-// {
-// // Transfer data from the HTTP request to the connection context
-// assertEquals(contextValue, request.getAttribute(contextKey));
-// context.put(contextKey, request.getAttribute(contextKey));
-// }
-//
-// @Override
-// protected int read(EndPoint endPoint, ByteBuffer buffer, ConcurrentMap context) throws IOException
-// {
-// assertEquals(contextValue, context.get(contextKey));
-// return super.read(endPoint, buffer, context);
-// }
-//
-// @Override
-// protected int write(EndPoint endPoint, ByteBuffer buffer, ConcurrentMap context) throws IOException
-// {
-// assertEquals(contextValue, context.get(contextKey));
-// return super.write(endPoint, buffer, context);
-// }
-// });
-// proxy.start();
-//
-// String hostPort = "localhost:" + ((Connector.NetConnector)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);
-// assertEquals("200", response.getCode());
-//
-// String body = "0123456789ABCDEF";
-// 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);
-// assertEquals("200", response.getCode());
-// assertEquals("POST /echo\r\n" + body, response.getBody());
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// @Test
-// public void testCONNECTAndGETPipelinedAndOutputShutdown() throws Exception
-// {
-// // TODO needs to be further investigated
-// assumeTrue(!OS.IS_OSX);
-//
-// String hostPort = "localhost:" + ((Connector.NetConnector)serverConnector).getLocalPort();
-// String request = "" +
-// "CONNECT " + hostPort + " HTTP/1.1\r\n" +
-// "Host: " + hostPort + "\r\n" +
-// "\r\n" +
-// "GET /echo" + " 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();
-// socket.shutdownOutput();
-//
-// // Expect 200 OK from the CONNECT request
-// Response response = readResponse(input);
-// assertEquals("200", response.getCode());
-//
-// // The pipelined request must have gone up to the server as is
-// response = readResponse(input);
-// assertEquals("200", response.getCode());
-// assertEquals("GET /echo", response.getBody());
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// @Test
-// public void testCONNECTAndGETAndOutputShutdown() throws Exception
-// {
-// // TODO needs to be further investigated
-// assumeTrue(!OS.IS_OSX);
-//
-// String hostPort = "localhost:" + ((Connector.NetConnector)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);
-// 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();
-// socket.shutdownOutput();
-//
-// // The pipelined request must have gone up to the server as is
-// response = readResponse(input);
-// assertEquals("200", response.getCode());
-// assertEquals("GET /echo", response.getBody());
-// }
-// finally
-// {
-// socket.close();
-// }
-// }
-//
-// 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))
-// {
-// 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();
-//
-// ServletOutputStream output = httpResponse.getOutputStream();
-// output.println(builder.toString());
-// output.write(baos.toByteArray());
-// }
-// else if ("/close".equals(uri))
-// {
-// request.getHttpChannel().getConnection().getEndPoint().close();
-// }
-// else
-// {
-// throw new ServletException();
-// }
-// }
-// }
+ @Before
+ public void prepare() throws Exception
+ {
+ server = new Server();
+ serverConnector = new ServerConnector(server);
+ server.addConnector(serverConnector);
+ server.setHandler(new ServerHandler());
+ server.start();
+ prepareProxy();
+ }
+
+ @Test
+ public void testCONNECT() throws Exception
+ {
+ String hostPort = "localhost:" + serverConnector.getLocalPort();
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+ }
+ }
+
+ @Test
+ public void testCONNECTAndGET() throws Exception
+ {
+ String hostPort = "localhost:" + serverConnector.getLocalPort();
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+ }
+ }
+
+ @Test
+ public void testProxyWhiteList() throws Exception
+ {
+ int port = serverConnector.getLocalPort();
+ String hostPort = "127.0.0.1:" + port;
+ connectHandler.getWhiteListHosts().add(hostPort);
+
+ // Try with the wrong host
+ String request = "" +
+ "CONNECT localhost:" + port + " HTTP/1.1\r\n" +
+ "Host: localhost:" + port + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ OutputStream output = socket.getOutputStream();
+ BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 403 from the CONNECT request
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("403", response.getCode());
+
+ // Socket should be closed
+ Assert.assertEquals(-1, input.read());
+ }
+
+ // Try again with the right host
+ request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ OutputStream output = socket.getOutputStream();
+ BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 200 from the CONNECT request
+ SimpleHttpResponse response = readResponse(input);
+ Assert.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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+ }
+ }
+
+ @Test
+ public void testProxyBlackList() throws Exception
+ {
+ int port = serverConnector.getLocalPort();
+ String hostPort = "localhost:" + port;
+ connectHandler.getBlackListHosts().add(hostPort);
+
+ // Try with the wrong host
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ OutputStream output = socket.getOutputStream();
+ BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 403 from the CONNECT request
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("403", response.getCode());
+
+ // Socket should be closed
+ Assert.assertEquals(-1, input.read());
+ }
+
+ // Try again with the right host
+ request = "" +
+ "CONNECT 127.0.0.1:" + port + " HTTP/1.1\r\n" +
+ "Host: 127.0.0.1:" + port + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ OutputStream output = socket.getOutputStream();
+ BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 200 from the CONNECT request
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+
+ request = "" +
+ "GET /echo" + " HTTP/1.1\r\n" +
+ "Host: 127.0.0.1:" + port + "\r\n" +
+ "\r\n";
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+ }
+ }
+
+ @Test
+ public void testProxyAuthentication() throws Exception
+ {
+ disposeProxy();
+ connectHandler = new ConnectHandler()
+ {
+ @Override
+ protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address)
+ {
+ String proxyAuthorization = request.getHeader("Proxy-Authorization");
+ if (proxyAuthorization == null)
+ {
+ response.setHeader("Proxy-Authenticate", "Basic realm=\"test\"");
+ return false;
+ }
+ String b64 = proxyAuthorization.substring("Basic ".length());
+ String credentials = B64Code.decode(b64, "UTF-8");
+ return "test:test".equals(credentials);
+ }
+ };
+ proxy.setHandler(connectHandler);
+ proxy.start();
+
+ int port = serverConnector.getLocalPort();
+ String hostPort = "localhost:" + port;
+
+ // Try without authentication
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ OutputStream output = socket.getOutputStream();
+ BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 407 from the CONNECT request
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("407", response.getCode());
+ Assert.assertTrue(response.getHeaders().containsKey("Proxy-Authenticate".toLowerCase(Locale.ENGLISH)));
+
+ // Socket should be closed
+ Assert.assertEquals(-1, input.read());
+ }
+
+ // Try with authentication
+ String credentials = "Basic " + B64Code.encode("test:test");
+ request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "Proxy-Authorization: " + credentials + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ OutputStream output = socket.getOutputStream();
+ BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 200 from the CONNECT request
+ SimpleHttpResponse response = readResponse(input);
+ Assert.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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+ }
+ }
+
+ @Test
+ public void testCONNECTBadHostPort() throws Exception
+ {
+ String invalidHostname = "badHost.webtide.com";
+
+ try
+ {
+ InetAddress address = InetAddress.getByName(invalidHostname);
+ StringBuilder err = new StringBuilder();
+ err.append("DNS Hijacking detected: ");
+ err.append(invalidHostname).append(" should have not returned a valid IP address [");
+ err.append(address.getHostAddress()).append("]. ");
+ err.append("Fix your DNS provider to have this test pass.");
+ err.append("\nFor more info see https://en.wikipedia.org/wiki/DNS_hijacking");
+ Assert.assertNull(err.toString(), address);
+ }
+ catch (UnknownHostException e)
+ {
+ // expected path
+ }
+
+ String hostPort = String.format("%s:%d", invalidHostname, serverConnector.getLocalPort());
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ Socket socket = newSocket();
+ socket.setSoTimeout(30000);
+ try
+ {
+ OutputStream output = socket.getOutputStream();
+ BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ // Expect 500 OK from the CONNECT request
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("Response Code", "500", response.getCode());
+ }
+ finally
+ {
+ socket.close();
+ }
+ }
+
+ @Test
+ public void testCONNECT10AndGET() throws Exception
+ {
+ String hostPort = "localhost:" + serverConnector.getLocalPort();
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.0\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+ }
+ }
+
+ @Test
+ public void testCONNECTAndGETPipelined() throws Exception
+ {
+ String hostPort = "localhost:" + serverConnector.getLocalPort();
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n" +
+ "GET /echo" + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+
+ // The pipelined request must have gone up to the server as is
+ response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+ }
+ }
+
+ @Test
+ public void testCONNECTAndMultipleGETs() throws Exception
+ {
+ String hostPort = "localhost:" + serverConnector.getLocalPort();
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+ }
+ }
+ }
+
+ @Test
+ 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";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+
+ // Idle server is shut down
+ disposeServer();
+
+ int read = input.read();
+ Assert.assertEquals(-1, read);
+ }
+ }
+
+ @Test
+ public void testCONNECTAndGETAndServerSideClose() throws Exception
+ {
+ String hostPort = "localhost:" + serverConnector.getLocalPort();
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+
+ request = "" +
+ "GET /close HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+
+ int read = input.read();
+ Assert.assertEquals(-1, read);
+ }
+ }
+
+ @Test
+ public void testCONNECTAndPOSTAndGET() throws Exception
+ {
+ String hostPort = "localhost:" + serverConnector.getLocalPort();
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+ }
+ }
+
+ @Test
+ 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";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+
+ StringBuilder body = new StringBuilder();
+ String chunk = "0123456789ABCDEF";
+ for (int i = 0; i < 1024 * 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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("POST /echo\r\n" + body, response.getBody());
+ }
+ }
+
+ @Test
+ public void testCONNECTAndPOSTWithContext() throws Exception
+ {
+ final String contextKey = "contextKey";
+ final String contextValue = "contextValue";
+
+ // Replace the default ProxyHandler with a subclass to test context information passing
+ disposeProxy();
+ proxy.setHandler(new ConnectHandler()
+ {
+ @Override
+ protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String address)
+ {
+ request.setAttribute(contextKey, contextValue);
+ return super.handleAuthentication(request, response, address);
+ }
+
+ @Override
+ protected void prepareContext(HttpServletRequest request, ConcurrentMap context)
+ {
+ // Transfer data from the HTTP request to the connection context
+ Assert.assertEquals(contextValue, request.getAttribute(contextKey));
+ context.put(contextKey, request.getAttribute(contextKey));
+ }
+
+ @Override
+ protected int read(EndPoint endPoint, ByteBuffer buffer, ConcurrentMap context) throws IOException
+ {
+ Assert.assertEquals(contextValue, context.get(contextKey));
+ return super.read(endPoint, buffer, context);
+ }
+
+ @Override
+ protected void write(EndPoint endPoint, ByteBuffer buffer, ConcurrentMap context, Callback callback)
+ {
+ Assert.assertEquals(contextValue, context.get(contextKey));
+ super.write(endPoint, buffer, context, callback);
+ }
+ });
+ proxy.start();
+
+ String hostPort = "localhost:" + serverConnector.getLocalPort();
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+
+ String body = "0123456789ABCDEF";
+ 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);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("POST /echo\r\n" + body, response.getBody());
+ }
+ }
+
+ @Test
+ public void testCONNECTAndGETPipelinedAndOutputShutdown() throws Exception
+ {
+ String hostPort = "localhost:" + serverConnector.getLocalPort();
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n" +
+ "GET /echo" + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ OutputStream output = socket.getOutputStream();
+ BufferedReader input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
+
+ output.write(request.getBytes("UTF-8"));
+ output.flush();
+ socket.shutdownOutput();
+
+ // Expect 200 OK from the CONNECT request
+ SimpleHttpResponse response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+
+ // The pipelined request must have gone up to the server as is
+ response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+ }
+ }
+
+ @Test
+ public void testCONNECTAndGETAndOutputShutdown() throws Exception
+ {
+ String hostPort = "localhost:" + serverConnector.getLocalPort();
+ String request = "" +
+ "CONNECT " + hostPort + " HTTP/1.1\r\n" +
+ "Host: " + hostPort + "\r\n" +
+ "\r\n";
+ try (Socket socket = newSocket())
+ {
+ 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
+ SimpleHttpResponse response = readResponse(input);
+ Assert.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();
+ socket.shutdownOutput();
+
+ // The pipelined request must have gone up to the server as is
+ response = readResponse(input);
+ Assert.assertEquals("200", response.getCode());
+ Assert.assertEquals("GET /echo", response.getBody());
+ }
+ }
+
+ 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();
+ switch (uri)
+ {
+ case "/echo":
+ {
+ 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;
+ while ((read = input.read()) >= 0)
+ baos.write(read);
+ baos.close();
+
+ ServletOutputStream output = httpResponse.getOutputStream();
+ output.println(builder.toString());
+ output.write(baos.toByteArray());
+ break;
+ }
+ case "/close":
+ {
+ request.getHttpChannel().getEndPoint().close();
+ break;
+ }
+ default:
+ {
+ throw new ServletException();
+ }
+ }
+ }
+ }
}
diff --git a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerUnitTest.java b/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerUnitTest.java
deleted file mode 100644
index 3bc16a53d41..00000000000
--- a/jetty-proxy/src/test/java/org/eclipse/jetty/proxy/ConnectHandlerUnitTest.java
+++ /dev/null
@@ -1,61 +0,0 @@
-//
-// ========================================================================
-// Copyright (c) 1995-2012 Mort Bay Consulting Pty. Ltd.
-// ------------------------------------------------------------------------
-// All rights reserved. This program and the accompanying materials
-// are made available under the terms of the Eclipse Public License v1.0
-// and Apache License v2.0 which accompanies this distribution.
-//
-// The Eclipse Public License is available at
-// http://www.eclipse.org/legal/epl-v10.html
-//
-// The Apache License v2.0 is available at
-// http://www.opensource.org/licenses/apache2.0.php
-//
-// You may elect to redistribute this code under either of these licenses.
-// ========================================================================
-//
-
-package org.eclipse.jetty.proxy;
-
-/* ------------------------------------------------------------ */
-/**
- */
-//@RunWith(MockitoJUnitRunner.class)
-public class ConnectHandlerUnitTest
-{
-// @Mock
-// private EndPoint endPoint;
-//
-// // TODO update for jetty-9
-// @Test
-// @Ignore
-// public void testPartialWritesWithNonFullBuffer() throws IOException
-// {
-// /*
-// ConnectHandler connectHandler = new ConnectHandler();
-// final byte[] bytes = "foo bar".getBytes();
-// Buffer buffer = new ByteArrayBuffer(bytes.length * 2);
-// buffer.put(bytes);
-// when(endPoint.flush(buffer)).thenAnswer(new Answer