Enhancements for #297104.
git-svn-id: svn+ssh://dev.eclipse.org/svnroot/rt/org.eclipse.jetty/jetty/trunk@1496 7e9141cc-0065-0410-87d8-b60c137991c4
This commit is contained in:
parent
9cb41db6df
commit
92c95f7fea
|
@ -16,7 +16,6 @@ package org.eclipse.jetty.server;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
|
|
||||||
import javax.servlet.ServletInputStream;
|
import javax.servlet.ServletInputStream;
|
||||||
import javax.servlet.ServletOutputStream;
|
import javax.servlet.ServletOutputStream;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
@ -41,13 +40,12 @@ import org.eclipse.jetty.http.MimeTypes;
|
||||||
import org.eclipse.jetty.http.Parser;
|
import org.eclipse.jetty.http.Parser;
|
||||||
import org.eclipse.jetty.io.AsyncEndPoint;
|
import org.eclipse.jetty.io.AsyncEndPoint;
|
||||||
import org.eclipse.jetty.io.Buffer;
|
import org.eclipse.jetty.io.Buffer;
|
||||||
|
import org.eclipse.jetty.io.BufferCache.CachedBuffer;
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.Connection;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.io.EofException;
|
import org.eclipse.jetty.io.EofException;
|
||||||
import org.eclipse.jetty.io.RuntimeIOException;
|
import org.eclipse.jetty.io.RuntimeIOException;
|
||||||
import org.eclipse.jetty.io.UncheckedPrintWriter;
|
import org.eclipse.jetty.io.UncheckedPrintWriter;
|
||||||
import org.eclipse.jetty.io.BufferCache.CachedBuffer;
|
|
||||||
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
|
|
||||||
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
import org.eclipse.jetty.util.QuotedStringTokenizer;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
import org.eclipse.jetty.util.URIUtil;
|
||||||
|
@ -68,7 +66,7 @@ import org.eclipse.jetty.util.thread.Timeout;
|
||||||
* with the connection via the parser and/or generator.
|
* with the connection via the parser and/or generator.
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* The connection state is held by 3 separate state machines: The request state, the
|
* The connection state is held by 3 separate state machines: The request state, the
|
||||||
* response state and the continuation state. All three state machines must be driven
|
* response state and the continuation state. All three state machines must be driven
|
||||||
* to completion for every request, and all three can complete in any order.
|
* to completion for every request, and all three can complete in any order.
|
||||||
* </p>
|
* </p>
|
||||||
|
@ -76,12 +74,12 @@ import org.eclipse.jetty.util.thread.Timeout;
|
||||||
* The HttpConnection support protocol upgrade. If on completion of a request, the
|
* The HttpConnection support protocol upgrade. If on completion of a request, the
|
||||||
* response code is 101 (switch protocols), then the org.eclipse.jetty.io.Connection
|
* response code is 101 (switch protocols), then the org.eclipse.jetty.io.Connection
|
||||||
* request attribute is checked to see if there is a new Connection instance. If so,
|
* request attribute is checked to see if there is a new Connection instance. If so,
|
||||||
* the new connection is returned from {@link #handle()} and is used for future
|
* the new connection is returned from {@link #handle()} and is used for future
|
||||||
* handling of the underlying connection. Note that for switching protocols that
|
* handling of the underlying connection. Note that for switching protocols that
|
||||||
* don't use 101 responses (eg CONNECT), the response should be sent and then the
|
* don't use 101 responses (eg CONNECT), the response should be sent and then the
|
||||||
* status code changed to 101 before returning from the handler. Implementors
|
* status code changed to 101 before returning from the handler. Implementors
|
||||||
* of new Connection types should be careful to extract any buffered data from
|
* of new Connection types should be careful to extract any buffered data from
|
||||||
* (HttpParser)http.getParser()).getHeaderBuffer() and
|
* (HttpParser)http.getParser()).getHeaderBuffer() and
|
||||||
* (HttpParser)http.getParser()).getBodyBuffer() to initialise their new connection.
|
* (HttpParser)http.getParser()).getBodyBuffer() to initialise their new connection.
|
||||||
* </p>
|
* </p>
|
||||||
*
|
*
|
||||||
|
@ -378,7 +376,7 @@ public class HttpConnection implements Connection
|
||||||
public Connection handle() throws IOException
|
public Connection handle() throws IOException
|
||||||
{
|
{
|
||||||
Connection connection = this;
|
Connection connection = this;
|
||||||
|
|
||||||
// Loop while more in buffer
|
// Loop while more in buffer
|
||||||
boolean more_in_buffer =true; // assume true until proven otherwise
|
boolean more_in_buffer =true; // assume true until proven otherwise
|
||||||
boolean progress=true;
|
boolean progress=true;
|
||||||
|
@ -396,9 +394,9 @@ public class HttpConnection implements Connection
|
||||||
{
|
{
|
||||||
if (_request._async.isAsync())
|
if (_request._async.isAsync())
|
||||||
{
|
{
|
||||||
// TODO - handle the case of input being read for a
|
// TODO - handle the case of input being read for a
|
||||||
// suspended request.
|
// suspended request.
|
||||||
|
|
||||||
Log.debug("async request",_request);
|
Log.debug("async request",_request);
|
||||||
if (!_request._async.isComplete())
|
if (!_request._async.isComplete())
|
||||||
handleRequest();
|
handleRequest();
|
||||||
|
@ -470,7 +468,7 @@ public class HttpConnection implements Connection
|
||||||
// look for a switched connection instance?
|
// look for a switched connection instance?
|
||||||
Connection switched=(_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
|
Connection switched=(_response.getStatus()==HttpStatus.SWITCHING_PROTOCOLS_101)
|
||||||
?(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection"):null;
|
?(Connection)_request.getAttribute("org.eclipse.jetty.io.Connection"):null;
|
||||||
|
|
||||||
// have we switched?
|
// have we switched?
|
||||||
if (switched!=null)
|
if (switched!=null)
|
||||||
{
|
{
|
||||||
|
@ -490,7 +488,7 @@ public class HttpConnection implements Connection
|
||||||
if (more_in_buffer)
|
if (more_in_buffer)
|
||||||
{
|
{
|
||||||
reset(false);
|
reset(false);
|
||||||
more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
|
more_in_buffer = _parser.isMoreInBuffer() || _endp.isBufferingInput();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
reset(true);
|
reset(true);
|
||||||
|
@ -503,7 +501,7 @@ public class HttpConnection implements Connection
|
||||||
Log.debug("return with suspended request");
|
Log.debug("return with suspended request");
|
||||||
more_in_buffer=false;
|
more_in_buffer=false;
|
||||||
}
|
}
|
||||||
else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof AsyncEndPoint)
|
else if (_generator.isCommitted() && !_generator.isComplete() && _endp instanceof AsyncEndPoint)
|
||||||
((AsyncEndPoint)_endp).setWritable(false);
|
((AsyncEndPoint)_endp).setWritable(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -628,7 +626,7 @@ public class HttpConnection implements Connection
|
||||||
Log.debug(e);
|
Log.debug(e);
|
||||||
_request.setHandled(true);
|
_request.setHandled(true);
|
||||||
_generator.sendError(info==null?400:500, null, null, true);
|
_generator.sendError(info==null?400:500, null, null, true);
|
||||||
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
@ -839,18 +837,17 @@ public class HttpConnection implements Connection
|
||||||
switch (HttpMethods.CACHE.getOrdinal(method))
|
switch (HttpMethods.CACHE.getOrdinal(method))
|
||||||
{
|
{
|
||||||
case HttpMethods.CONNECT_ORDINAL:
|
case HttpMethods.CONNECT_ORDINAL:
|
||||||
// _uri.parseConnect(uri.array(), uri.getIndex(), uri.length());
|
_uri.parseConnect(uri.array(), uri.getIndex(), uri.length());
|
||||||
_uri.parse("http://"+uri+"/");
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case HttpMethods.HEAD_ORDINAL:
|
case HttpMethods.HEAD_ORDINAL:
|
||||||
_head=true;
|
_head=true;
|
||||||
// fall through
|
// fall through
|
||||||
|
|
||||||
default:
|
default:
|
||||||
_uri.parse(uri.array(), uri.getIndex(), uri.length());
|
_uri.parse(uri.array(), uri.getIndex(), uri.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
_request.setUri(_uri);
|
_request.setUri(_uri);
|
||||||
|
|
||||||
if (version==null)
|
if (version==null)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,497 @@
|
||||||
|
package org.eclipse.jetty.server.handler;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.InetSocketAddress;
|
||||||
|
import java.nio.channels.SelectionKey;
|
||||||
|
import java.nio.channels.SocketChannel;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.http.HttpMethods;
|
||||||
|
import org.eclipse.jetty.http.HttpParser;
|
||||||
|
import org.eclipse.jetty.io.Buffer;
|
||||||
|
import org.eclipse.jetty.io.ConnectedEndPoint;
|
||||||
|
import org.eclipse.jetty.io.Connection;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.io.nio.IndirectNIOBuffer;
|
||||||
|
import org.eclipse.jetty.io.nio.SelectChannelEndPoint;
|
||||||
|
import org.eclipse.jetty.io.nio.SelectorManager;
|
||||||
|
import org.eclipse.jetty.server.HttpConnection;
|
||||||
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @version $Revision$ $Date$
|
||||||
|
*/
|
||||||
|
public class ProxyHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
private final Logger logger = Log.getLogger(getClass().getName());
|
||||||
|
private final SelectorManager selectorManager = new Manager();
|
||||||
|
private final String serverAddress;
|
||||||
|
private volatile int connectTimeout = 5000;
|
||||||
|
private volatile int writeTimeout = 30000;
|
||||||
|
private volatile QueuedThreadPool threadPool;
|
||||||
|
|
||||||
|
public ProxyHandler()
|
||||||
|
{
|
||||||
|
this(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ProxyHandler(String serverAddress)
|
||||||
|
{
|
||||||
|
this.serverAddress = serverAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getConnectTimeout()
|
||||||
|
{
|
||||||
|
return connectTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConnectTimeout(int connectTimeout)
|
||||||
|
{
|
||||||
|
this.connectTimeout = connectTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getWriteTimeout()
|
||||||
|
{
|
||||||
|
return writeTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setWriteTimeout(int writeTimeout)
|
||||||
|
{
|
||||||
|
this.writeTimeout = writeTimeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doStart() throws Exception
|
||||||
|
{
|
||||||
|
super.doStart();
|
||||||
|
// TODO: configure threadPool
|
||||||
|
threadPool = new QueuedThreadPool();
|
||||||
|
threadPool.start();
|
||||||
|
|
||||||
|
selectorManager.start();
|
||||||
|
threadPool.dispatch(new Runnable()
|
||||||
|
{
|
||||||
|
public void run()
|
||||||
|
{
|
||||||
|
while (isRunning())
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
selectorManager.doSelect(0);
|
||||||
|
}
|
||||||
|
catch (IOException x)
|
||||||
|
{
|
||||||
|
logger.warn("Unexpected exception", x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void doStop() throws Exception
|
||||||
|
{
|
||||||
|
selectorManager.stop();
|
||||||
|
|
||||||
|
QueuedThreadPool threadPool = this.threadPool;
|
||||||
|
if (threadPool != null)
|
||||||
|
threadPool.stop();
|
||||||
|
|
||||||
|
super.doStop();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||||
|
{
|
||||||
|
if (HttpMethods.CONNECT.equalsIgnoreCase(request.getMethod()))
|
||||||
|
{
|
||||||
|
logger.info("CONNECT request for {}", request.getRequestURI(), null);
|
||||||
|
handle(request, response, request.getRequestURI());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.info("Plain request for {}", serverAddress, null);
|
||||||
|
if (serverAddress == null)
|
||||||
|
throw new ServletException("Parameter 'serverAddress' cannot be null");
|
||||||
|
handle(request, response, serverAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Handles a CONNECT request.</p>
|
||||||
|
* <p>CONNECT requests may have authentication headers such as <code>Proxy-Authorization</code>
|
||||||
|
* that authenticate the client with the proxy.</p>
|
||||||
|
* @param request the http request
|
||||||
|
* @param response the http response
|
||||||
|
* @param connectURI the CONNECT URI
|
||||||
|
* @throws ServletException if an application error occurs
|
||||||
|
* @throws IOException if an I/O error occurs
|
||||||
|
*/
|
||||||
|
protected void handle(HttpServletRequest request, HttpServletResponse response, String connectURI) throws ServletException, IOException
|
||||||
|
{
|
||||||
|
boolean proceed = handleAuthentication(request, response, connectURI);
|
||||||
|
if (!proceed)
|
||||||
|
return;
|
||||||
|
|
||||||
|
String host = connectURI;
|
||||||
|
int port = 80;
|
||||||
|
boolean secure = false;
|
||||||
|
int colon = connectURI.indexOf(':');
|
||||||
|
if (colon > 0)
|
||||||
|
{
|
||||||
|
host = connectURI.substring(0, colon);
|
||||||
|
port = Integer.parseInt(connectURI.substring(colon + 1));
|
||||||
|
secure = isTunnelSecure(host, port);
|
||||||
|
}
|
||||||
|
|
||||||
|
setupTunnel(request, response, host, port, secure);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isTunnelSecure(String host, int port)
|
||||||
|
{
|
||||||
|
return port == 443;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean handleAuthentication(HttpServletRequest request, HttpServletResponse response, String connectURI) throws ServletException, IOException
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setupTunnel(HttpServletRequest request, HttpServletResponse response, String host, int port, boolean secure) throws IOException
|
||||||
|
{
|
||||||
|
SocketChannel channel = connect(request, host, port);
|
||||||
|
channel.configureBlocking(false);
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
HttpConnection httpConnection = HttpConnection.getCurrentConnection();
|
||||||
|
Buffer headerBuffer = ((HttpParser)httpConnection.getParser()).getHeaderBuffer();
|
||||||
|
Buffer 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)
|
||||||
|
{
|
||||||
|
buffer.put(headerBuffer);
|
||||||
|
headerBuffer.clear();
|
||||||
|
}
|
||||||
|
if (bodyBuffer != null)
|
||||||
|
{
|
||||||
|
buffer.put(bodyBuffer);
|
||||||
|
bodyBuffer.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup connections, before registering the channel to avoid races
|
||||||
|
// where the server sends data before the connections are set up
|
||||||
|
ProxyToServerConnection proxyToServer = new ProxyToServerConnection(secure, buffer);
|
||||||
|
ClientToProxyConnection clientToProxy = new ClientToProxyConnection(channel, httpConnection.getEndPoint(), httpConnection.getTimeStamp());
|
||||||
|
clientToProxy.setConnection(proxyToServer);
|
||||||
|
proxyToServer.setConnection(clientToProxy);
|
||||||
|
|
||||||
|
upgradeConnection(request, response, clientToProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SocketChannel connect(HttpServletRequest request, String host, int port) throws IOException
|
||||||
|
{
|
||||||
|
logger.info("Establishing connection to {}:{}", host, port);
|
||||||
|
// Connect to remote server
|
||||||
|
SocketChannel channel = SocketChannel.open();
|
||||||
|
channel.socket().setTcpNoDelay(true);
|
||||||
|
channel.socket().connect(new InetSocketAddress(host, port), getConnectTimeout());
|
||||||
|
logger.info("Established connection to {}:{}", host, port);
|
||||||
|
return channel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void upgradeConnection(HttpServletRequest request, HttpServletResponse response, Connection connection) throws IOException
|
||||||
|
{
|
||||||
|
// CONNECT expects a 200 response
|
||||||
|
response.setStatus(HttpServletResponse.SC_OK);
|
||||||
|
// Flush it so that the client receives it
|
||||||
|
response.flushBuffer();
|
||||||
|
// Set the new connection as request attribute and change the status to 101
|
||||||
|
// so that Jetty understands that it has to upgrade the connection
|
||||||
|
request.setAttribute("org.eclipse.jetty.io.Connection", connection);
|
||||||
|
response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
|
||||||
|
logger.info("Upgraded connection to {}", connection, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void register(SocketChannel channel, ProxyToServerConnection proxyToServer) throws IOException
|
||||||
|
{
|
||||||
|
selectorManager.register(channel, proxyToServer);
|
||||||
|
proxyToServer.waitReady(connectTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes (with blocking semantic) the given buffer of data onto the given endPoint
|
||||||
|
* @param endPoint the endPoint to write to
|
||||||
|
* @param buffer the buffer to write
|
||||||
|
* @throws IOException if the buffer cannot be written
|
||||||
|
*/
|
||||||
|
private void write(EndPoint endPoint, Buffer buffer) throws IOException
|
||||||
|
{
|
||||||
|
if (buffer == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
int length = buffer.length();
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
int written = endPoint.flush(buffer);
|
||||||
|
builder.append(written);
|
||||||
|
buffer.compact();
|
||||||
|
if (!endPoint.isBlocking())
|
||||||
|
{
|
||||||
|
while (buffer.space() == 0)
|
||||||
|
{
|
||||||
|
boolean ready = endPoint.blockWritable(getWriteTimeout());
|
||||||
|
if (!ready)
|
||||||
|
throw new IOException("Write timeout");
|
||||||
|
|
||||||
|
written = endPoint.flush(buffer);
|
||||||
|
builder.append("+").append(written);
|
||||||
|
buffer.compact();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info("Written {}/{} bytes " + endPoint, builder, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Manager extends SelectorManager
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected SocketChannel acceptChannel(SelectionKey key) throws IOException
|
||||||
|
{
|
||||||
|
// This is a client-side selector manager
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectSet selectSet, SelectionKey selectionKey) throws IOException
|
||||||
|
{
|
||||||
|
ProxyToServerConnection proxyToServer = (ProxyToServerConnection)selectionKey.attachment();
|
||||||
|
if (proxyToServer.secure)
|
||||||
|
{
|
||||||
|
throw new UnsupportedOperationException();
|
||||||
|
// return new SslSelectChannelEndPoint(???, channel, selectSet, selectionKey, sslContext.createSSLEngine(address.host, address.port));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return new SelectChannelEndPoint(channel, selectSet, selectionKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Connection newConnection(SocketChannel channel, SelectChannelEndPoint endpoint)
|
||||||
|
{
|
||||||
|
ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment();
|
||||||
|
proxyToServer.setTimeStamp(System.currentTimeMillis());
|
||||||
|
proxyToServer.setEndPoint(endpoint);
|
||||||
|
return proxyToServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void endPointOpened(SelectChannelEndPoint endpoint)
|
||||||
|
{
|
||||||
|
ProxyToServerConnection proxyToServer = (ProxyToServerConnection)endpoint.getSelectionKey().attachment();
|
||||||
|
proxyToServer.ready();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean dispatch(Runnable task)
|
||||||
|
{
|
||||||
|
return threadPool.dispatch(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void endPointClosed(SelectChannelEndPoint endpoint)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void endPointUpgraded(ConnectedEndPoint endpoint, Connection oldConnection)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ProxyToServerConnection implements Connection
|
||||||
|
{
|
||||||
|
private final CountDownLatch ready = new CountDownLatch(1);
|
||||||
|
private final Buffer buffer = new IndirectNIOBuffer(1024);
|
||||||
|
private final boolean secure;
|
||||||
|
private volatile Buffer data;
|
||||||
|
private volatile ClientToProxyConnection connection;
|
||||||
|
private volatile long timestamp;
|
||||||
|
private volatile SelectChannelEndPoint endPoint;
|
||||||
|
|
||||||
|
public ProxyToServerConnection(boolean secure, Buffer data)
|
||||||
|
{
|
||||||
|
this.secure = secure;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection handle() throws IOException
|
||||||
|
{
|
||||||
|
logger.info("ProxyToServer: handle entered");
|
||||||
|
if (data != null)
|
||||||
|
{
|
||||||
|
write(endPoint, data);
|
||||||
|
data = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int read = endPoint.fill(buffer);
|
||||||
|
|
||||||
|
if (read == -1)
|
||||||
|
{
|
||||||
|
logger.info("ProxyToServer: closed connection {}", endPoint, null);
|
||||||
|
connection.close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
logger.info("ProxyToServer: read {} bytes {}", read, endPoint);
|
||||||
|
write(connection.endPoint, buffer);
|
||||||
|
}
|
||||||
|
logger.info("ProxyToServer: handle exited");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConnection(ClientToProxyConnection connection)
|
||||||
|
{
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimeStamp()
|
||||||
|
{
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTimeStamp(long timestamp)
|
||||||
|
{
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndPoint(SelectChannelEndPoint endpoint)
|
||||||
|
{
|
||||||
|
this.endPoint = endpoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIdle()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuspended()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ready()
|
||||||
|
{
|
||||||
|
ready.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void waitReady(long timeout) throws IOException
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ready.await(timeout, TimeUnit.MILLISECONDS);
|
||||||
|
}
|
||||||
|
catch (InterruptedException x)
|
||||||
|
{
|
||||||
|
throw new IOException(x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException
|
||||||
|
{
|
||||||
|
endPoint.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ClientToProxyConnection implements Connection
|
||||||
|
{
|
||||||
|
private final Buffer buffer = new IndirectNIOBuffer(1024);
|
||||||
|
private final SocketChannel channel;
|
||||||
|
private final EndPoint endPoint;
|
||||||
|
private final long timestamp;
|
||||||
|
private volatile ProxyToServerConnection connection;
|
||||||
|
private boolean firstTime = true;
|
||||||
|
|
||||||
|
public ClientToProxyConnection(SocketChannel channel, EndPoint endPoint, long timestamp)
|
||||||
|
{
|
||||||
|
this.channel = channel;
|
||||||
|
this.endPoint = endPoint;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Connection handle() throws IOException
|
||||||
|
{
|
||||||
|
logger.info("ClientToProxy: handle entered");
|
||||||
|
|
||||||
|
if (firstTime)
|
||||||
|
{
|
||||||
|
firstTime = false;
|
||||||
|
register(channel, connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
int read = endPoint.fill(buffer);
|
||||||
|
|
||||||
|
if (read == -1)
|
||||||
|
{
|
||||||
|
logger.info("ClientToProxy: closed connection {}", endPoint, null);
|
||||||
|
connection.close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (read == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
logger.info("ClientToProxy: read {} bytes {}", read, endPoint);
|
||||||
|
write(connection.endPoint, buffer);
|
||||||
|
}
|
||||||
|
logger.info("ClientToProxy: handle exited");
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getTimeStamp()
|
||||||
|
{
|
||||||
|
return timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isIdle()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isSuspended()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setConnection(ProxyToServerConnection connection)
|
||||||
|
{
|
||||||
|
this.connection = connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() throws IOException
|
||||||
|
{
|
||||||
|
endPoint.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,328 @@
|
||||||
|
package org.eclipse.jetty.server.handler;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.EOFException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import javax.servlet.ServletException;
|
||||||
|
import javax.servlet.ServletOutputStream;
|
||||||
|
import javax.servlet.http.HttpServlet;
|
||||||
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
import org.eclipse.jetty.server.Request;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.nio.SelectChannelConnector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @version $Revision$ $Date$
|
||||||
|
*/
|
||||||
|
public class ProxyHandlerConnectTest extends TestCase
|
||||||
|
{
|
||||||
|
private Server server;
|
||||||
|
private Connector serverConnector;
|
||||||
|
private Server proxy;
|
||||||
|
private Connector proxyConnector;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception
|
||||||
|
{
|
||||||
|
server = new Server();
|
||||||
|
serverConnector = new SelectChannelConnector();
|
||||||
|
server.addConnector(serverConnector);
|
||||||
|
server.setHandler(new ServerHandler());
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
proxy = new Server();
|
||||||
|
proxyConnector = new SelectChannelConnector();
|
||||||
|
proxy.addConnector(proxyConnector);
|
||||||
|
proxy.setHandler(new ProxyHandler());
|
||||||
|
proxy.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception
|
||||||
|
{
|
||||||
|
proxy.stop();
|
||||||
|
proxy.join();
|
||||||
|
|
||||||
|
server.stop();
|
||||||
|
server.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testHttpConnect() throws Exception
|
||||||
|
{
|
||||||
|
String request = "" +
|
||||||
|
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(request.getBytes("UTF-8"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
// Expect 200 OK from the CONNECT request
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
Response response = readResponse(input);
|
||||||
|
System.err.println(response);
|
||||||
|
assertEquals("200", response.code);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testHttpConnectWithNormalRequest() throws Exception
|
||||||
|
{
|
||||||
|
String request = "" +
|
||||||
|
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(request.getBytes("UTF-8"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
// Expect 200 OK from the CONNECT request
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
Response response = readResponse(input);
|
||||||
|
System.err.println(response);
|
||||||
|
assertEquals("200", response.code);
|
||||||
|
|
||||||
|
String echoURI = "GET /echo";
|
||||||
|
request = "" +
|
||||||
|
echoURI + " HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
output.write(request.getBytes("UTF-8"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
response = readResponse(input);
|
||||||
|
System.err.println(response);
|
||||||
|
assertEquals("200", response.code);
|
||||||
|
assertEquals(echoURI, response.body);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testHttpConnectWithPipelinedRequest() throws Exception
|
||||||
|
{
|
||||||
|
String pipelinedMethodURI = "GET /echo";
|
||||||
|
String request = "" +
|
||||||
|
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"\r\n" +
|
||||||
|
pipelinedMethodURI + " HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(request.getBytes("UTF-8"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
// Expect 200 OK from the CONNECT request
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
Response response = readResponse(input);
|
||||||
|
System.err.println(response);
|
||||||
|
assertEquals("200", response.code);
|
||||||
|
|
||||||
|
// The pipelined request must have gone up to the server as is
|
||||||
|
response = readResponse(input);
|
||||||
|
System.err.println(response);
|
||||||
|
assertEquals("200", response.code);
|
||||||
|
assertEquals(pipelinedMethodURI, response.body);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testHttpConnectWithNoRequestServerClose() throws Exception
|
||||||
|
{
|
||||||
|
String request = "" +
|
||||||
|
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(request.getBytes("UTF-8"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
// Expect 200 OK from the CONNECT request
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
Response response = readResponse(input);
|
||||||
|
System.err.println(response);
|
||||||
|
assertEquals("200", response.code);
|
||||||
|
|
||||||
|
// Idle server is shut down
|
||||||
|
server.stop();
|
||||||
|
server.join();
|
||||||
|
|
||||||
|
int read = input.read();
|
||||||
|
assertEquals(-1, read);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testHttpConnectWithRequestServerClose() throws Exception
|
||||||
|
{
|
||||||
|
String request = "" +
|
||||||
|
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(request.getBytes("UTF-8"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
// Expect 200 OK from the CONNECT request
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
Response response = readResponse(input);
|
||||||
|
System.err.println(response);
|
||||||
|
assertEquals("200", response.code);
|
||||||
|
|
||||||
|
request = "" +
|
||||||
|
"GET /close HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
output.write(request.getBytes("UTF-8"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
int read = input.read();
|
||||||
|
assertEquals(-1, read);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Response readResponse(InputStream input) throws IOException
|
||||||
|
{
|
||||||
|
// Simplified parser for HTTP responses
|
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
|
||||||
|
String line = reader.readLine();
|
||||||
|
if (line == null)
|
||||||
|
throw new EOFException();
|
||||||
|
Matcher responseLine = Pattern.compile("HTTP/1\\.1\\s+(\\d+)").matcher(line);
|
||||||
|
assertTrue(responseLine.lookingAt());
|
||||||
|
String code = responseLine.group(1);
|
||||||
|
|
||||||
|
Map<String, String> headers = new LinkedHashMap<String, String>();
|
||||||
|
while ((line = reader.readLine()) != null)
|
||||||
|
{
|
||||||
|
if (line.trim().length() == 0)
|
||||||
|
break;
|
||||||
|
|
||||||
|
Matcher header = Pattern.compile("([^:]+):\\s*(.*)").matcher(line);
|
||||||
|
assertTrue(header.lookingAt());
|
||||||
|
String headerName = header.group(1);
|
||||||
|
String headerValue = header.group(2);
|
||||||
|
headers.put(headerName.toLowerCase(), headerValue.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder body = new StringBuilder();
|
||||||
|
if (headers.containsKey("content-length"))
|
||||||
|
{
|
||||||
|
int length = Integer.parseInt(headers.get("content-length"));
|
||||||
|
for (int i = 0; i < length; ++i)
|
||||||
|
body.append((char)reader.read());
|
||||||
|
}
|
||||||
|
else if ("chunked".equals(headers.get("transfer-encoding")))
|
||||||
|
{
|
||||||
|
while ((line = reader.readLine()) != null)
|
||||||
|
{
|
||||||
|
if ("0".equals(line))
|
||||||
|
{
|
||||||
|
reader.readLine();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.append(reader.readLine());
|
||||||
|
reader.readLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Response(code, headers, body.toString().trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestServlet extends HttpServlet
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ServerHandler extends AbstractHandler
|
||||||
|
{
|
||||||
|
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) throws IOException, ServletException
|
||||||
|
{
|
||||||
|
request.setHandled(true);
|
||||||
|
|
||||||
|
String uri = httpRequest.getRequestURI();
|
||||||
|
if ("/echo".equals(uri))
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(httpRequest.getMethod()).append(" ").append(uri);
|
||||||
|
System.err.println("server echoing:\r\n" + builder);
|
||||||
|
ServletOutputStream output = httpResponse.getOutputStream();
|
||||||
|
output.println(builder.toString());
|
||||||
|
}
|
||||||
|
else if ("/close".equals(uri))
|
||||||
|
{
|
||||||
|
request.getConnection().getEndPoint().close();
|
||||||
|
System.err.println("server closed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class Response
|
||||||
|
{
|
||||||
|
private final String code;
|
||||||
|
private final Map<String, String> headers;
|
||||||
|
private final String body;
|
||||||
|
|
||||||
|
private Response(String code, Map<String, String> headers, String body)
|
||||||
|
{
|
||||||
|
this.code = code;
|
||||||
|
this.headers = headers;
|
||||||
|
this.body = body;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(code).append("\r\n");
|
||||||
|
for (Map.Entry<String, String> entry : headers.entrySet())
|
||||||
|
builder.append(entry.getKey()).append(": ").append(entry.getValue()).append("\r\n");
|
||||||
|
builder.append("\r\n");
|
||||||
|
builder.append(body);
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package org.eclipse.jetty.server.handler;
|
||||||
|
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
import junit.framework.TestCase;
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
import org.eclipse.jetty.server.Server;
|
||||||
|
import org.eclipse.jetty.server.ssl.SslSelectChannelConnector;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @version $Revision$ $Date$
|
||||||
|
*/
|
||||||
|
public class ProxyHandlerSSLTest extends TestCase
|
||||||
|
{
|
||||||
|
private Server server;
|
||||||
|
private Connector serverConnector;
|
||||||
|
private Server proxy;
|
||||||
|
private Connector proxyConnector;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setUp() throws Exception
|
||||||
|
{
|
||||||
|
server = new Server();
|
||||||
|
serverConnector = new SslSelectChannelConnector();
|
||||||
|
server.addConnector(serverConnector);
|
||||||
|
// server.setHandler(new EchoHandler());
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
proxy = new Server();
|
||||||
|
proxyConnector = new SslSelectChannelConnector();
|
||||||
|
proxy.addConnector(proxyConnector);
|
||||||
|
proxy.setHandler(new ProxyHandler()
|
||||||
|
{
|
||||||
|
@Override
|
||||||
|
protected boolean isTunnelSecure(String host, int port)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
proxy.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void tearDown() throws Exception
|
||||||
|
{
|
||||||
|
proxy.stop();
|
||||||
|
proxy.join();
|
||||||
|
|
||||||
|
server.stop();
|
||||||
|
server.join();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testHttpConnectWithNormalRequest() throws Exception
|
||||||
|
{
|
||||||
|
String request = "" +
|
||||||
|
"CONNECT localhost:" + serverConnector.getLocalPort() + " HTTP/1.1\r\n" +
|
||||||
|
"Host: localhost\r\n" +
|
||||||
|
"\r\n";
|
||||||
|
Socket socket = new Socket("localhost", proxyConnector.getLocalPort());
|
||||||
|
try
|
||||||
|
{
|
||||||
|
OutputStream output = socket.getOutputStream();
|
||||||
|
output.write(request.getBytes("UTF-8"));
|
||||||
|
output.flush();
|
||||||
|
|
||||||
|
// Expect 200 OK from the CONNECT request
|
||||||
|
InputStream input = socket.getInputStream();
|
||||||
|
// Response response = readResponse(input);
|
||||||
|
// System.err.println(response);
|
||||||
|
// assertEquals("200", response.code);
|
||||||
|
|
||||||
|
// Now what ? Upgrade the socket to SSL ?
|
||||||
|
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue