396606 make spdy proxy capable of receiving SPDY and talk HTTP to the upstream server
This commit is contained in:
parent
cd30ac104d
commit
95698ca248
|
@ -281,7 +281,6 @@ public class StandardSession implements ISession, Parser.Listener, Dumpable
|
|||
|
||||
private void goAway(SessionStatus sessionStatus, long timeout, TimeUnit unit, Callback callback)
|
||||
{
|
||||
new Exception().printStackTrace();
|
||||
if (goAwaySent.compareAndSet(false, true))
|
||||
{
|
||||
if (!goAwayReceived.get())
|
||||
|
|
|
@ -27,34 +27,28 @@ import org.eclipse.jetty.util.Callback;
|
|||
import org.eclipse.jetty.util.Promise;
|
||||
|
||||
/**
|
||||
* <p>A {@link Stream} represents a bidirectional exchange of data on top of a {@link Session}.</p>
|
||||
* <p>Differently from socket streams, where the input and output streams are permanently associated
|
||||
* with the socket (and hence with the connection that the socket represents), there can be multiple
|
||||
* SPDY streams for a SPDY session.</p>
|
||||
* <p>SPDY streams may terminate without this implying that the SPDY session is terminated.</p>
|
||||
* <p>If SPDY is used to transport the HTTP protocol, then a SPDY stream maps to a HTTP request/response
|
||||
* cycle, and after the request/response cycle is completed, the stream is closed, and other streams
|
||||
* may be opened. Differently from HTTP, though, multiple SPDY streams may be opened concurrently
|
||||
* on the same SPDY session.</p>
|
||||
* <p>Like {@link Session}, {@link Stream} is the active part and by calling its API applications
|
||||
* can generate events on the stream; conversely, {@link StreamFrameListener} is the passive part, and its
|
||||
* callbacks are invoked when events happen on the stream.</p>
|
||||
* <p>A {@link Stream} can send multiple data frames one after the other but implementations use a
|
||||
* flow control mechanism that only sends the data frames if the other end has signalled that it can
|
||||
* accept the frame.</p>
|
||||
* <p>Data frames should be sent sequentially only when the previous frame has been completely sent.
|
||||
* The reason for this requirement is to avoid potentially confusing code such as:</p>
|
||||
* <p>A {@link Stream} represents a bidirectional exchange of data on top of a {@link Session}.</p> <p>Differently from
|
||||
* socket streams, where the input and output streams are permanently associated with the socket (and hence with the
|
||||
* connection that the socket represents), there can be multiple SPDY streams for a SPDY session.</p> <p>SPDY streams
|
||||
* may terminate without this implying that the SPDY session is terminated.</p> <p>If SPDY is used to transport the HTTP
|
||||
* protocol, then a SPDY stream maps to a HTTP request/response cycle, and after the request/response cycle is
|
||||
* completed, the stream is closed, and other streams may be opened. Differently from HTTP, though, multiple SPDY
|
||||
* streams may be opened concurrently on the same SPDY session.</p> <p>Like {@link Session}, {@link Stream} is the
|
||||
* active part and by calling its API applications can generate events on the stream; conversely, {@link
|
||||
* StreamFrameListener} is the passive part, and its callbacks are invoked when events happen on the stream.</p> <p>A
|
||||
* {@link Stream} can send multiple data frames one after the other but implementations use a flow control mechanism
|
||||
* that only sends the data frames if the other end has signalled that it can accept the frame.</p> <p>Data frames
|
||||
* should be sent sequentially only when the previous frame has been completely sent. The reason for this requirement is
|
||||
* to avoid potentially confusing code such as:</p>
|
||||
* <pre>
|
||||
* // WRONG CODE, DO NOT USE IT
|
||||
* final Stream stream = ...;
|
||||
* stream.data(StringDataInfo("chunk1", false), 5, TimeUnit.SECONDS, new Handler<Void>() { ... });
|
||||
* stream.data(StringDataInfo("chunk2", true), 1, TimeUnit.SECONDS, new Handler<Void>() { ... });
|
||||
* </pre>
|
||||
* <p>where the second call to {@link #data(DataInfo, Callback)} has a timeout smaller
|
||||
* than the previous call.</p>
|
||||
* <p>The behavior of such style of invocations is unspecified (it may even throw an exception - similar
|
||||
* to {@link WritePendingException}).</p>
|
||||
* <p>The correct sending of data frames is the following:</p>
|
||||
* <p>where the second call to {@link #data(DataInfo, Callback)} has a timeout smaller than the previous call.</p>
|
||||
* <p>The behavior of such style of invocations is unspecified (it may even throw an exception - similar to {@link
|
||||
* WritePendingException}).</p> <p>The correct sending of data frames is the following:</p>
|
||||
* <pre>
|
||||
* final Stream stream = ...;
|
||||
* ...
|
||||
|
@ -92,10 +86,8 @@ public interface Stream
|
|||
public Session getSession();
|
||||
|
||||
/**
|
||||
* <p>Initiate a unidirectional spdy pushstream associated to this stream asynchronously<p>
|
||||
* <p>Callers may use the returned future to get the pushstream once it got created</p>
|
||||
*
|
||||
*
|
||||
* <p>Initiate a unidirectional spdy pushstream associated to this stream asynchronously<p> <p>Callers may use the
|
||||
* returned future to get the pushstream once it got created</p>
|
||||
*
|
||||
* @param pushInfo the metadata to send on stream creation
|
||||
* @return a future containing the stream once it got established
|
||||
|
@ -104,21 +96,18 @@ public interface Stream
|
|||
public Stream push(PushInfo pushInfo) throws InterruptedException, ExecutionException, TimeoutException;
|
||||
|
||||
/**
|
||||
* <p>Initiate a unidirectional spdy pushstream associated to this stream asynchronously<p>
|
||||
* <p>Callers may pass a non-null completion callback to be notified of when the
|
||||
* pushstream has been established.</p>
|
||||
*
|
||||
* <p>Initiate a unidirectional spdy pushstream associated to this stream asynchronously<p> <p>Callers may pass a
|
||||
* non-null completion callback to be notified of when the pushstream has been established.</p>
|
||||
*
|
||||
* @param pushInfo the metadata to send on stream creation
|
||||
* @param callback the completion callback that gets notified once the pushstream is established
|
||||
* @param callback the completion callback that gets notified once the pushstream is established
|
||||
* @see #push(PushInfo)
|
||||
*/
|
||||
public void push(PushInfo pushInfo, Promise<Stream> callback);
|
||||
|
||||
/**
|
||||
* <p>Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.</p>
|
||||
* <p>Callers may use the returned future to wait for the reply to be actually sent.</p>
|
||||
*
|
||||
* <p>Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.</p> <p>Callers may use the returned
|
||||
* future to wait for the reply to be actually sent.</p>
|
||||
*
|
||||
* @param replyInfo the metadata to send
|
||||
* @return a future to wait for the reply to be sent
|
||||
|
@ -128,21 +117,18 @@ public interface Stream
|
|||
public void reply(ReplyInfo replyInfo) throws InterruptedException, ExecutionException, TimeoutException;
|
||||
|
||||
/**
|
||||
* <p>Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.</p>
|
||||
* <p>Callers may pass a non-null completion callback to be notified of when the
|
||||
* reply has been actually sent.</p>
|
||||
* <p>Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.</p> <p>Callers may pass a non-null
|
||||
* completion callback to be notified of when the reply has been actually sent.</p>
|
||||
*
|
||||
* @param replyInfo the metadata to send
|
||||
* @param callback the completion callback that gets notified of reply sent
|
||||
* @param callback the completion callback that gets notified of reply sent
|
||||
* @see #reply(ReplyInfo)
|
||||
*/
|
||||
public void reply(ReplyInfo replyInfo, Callback callback);
|
||||
|
||||
/**
|
||||
* <p>Sends asynchronously a DATA frame on this stream.</p>
|
||||
* <p>DATA frames should always be sent after a SYN_REPLY frame.</p>
|
||||
* <p>Callers may use the returned future to wait for the data to be actually sent.</p>
|
||||
*
|
||||
* <p>Sends asynchronously a DATA frame on this stream.</p> <p>DATA frames should always be sent after a SYN_REPLY
|
||||
* frame.</p> <p>Callers may use the returned future to wait for the data to be actually sent.</p>
|
||||
*
|
||||
* @param dataInfo the metadata to send
|
||||
* @return a future to wait for the data to be sent
|
||||
|
@ -152,22 +138,19 @@ public interface Stream
|
|||
public void data(DataInfo dataInfo) throws InterruptedException, ExecutionException, TimeoutException;
|
||||
|
||||
/**
|
||||
* <p>Sends asynchronously a DATA frame on this stream.</p>
|
||||
* <p>DATA frames should always be sent after a SYN_REPLY frame.</p>
|
||||
* <p>Callers may pass a non-null completion callback to be notified of when the
|
||||
* data has been actually sent.</p>
|
||||
* <p>Sends asynchronously a DATA frame on this stream.</p> <p>DATA frames should always be sent after a SYN_REPLY
|
||||
* frame.</p> <p>Callers may pass a non-null completion callback to be notified of when the data has been actually
|
||||
* sent.</p>
|
||||
*
|
||||
* @param dataInfo the metadata to send
|
||||
* @param callback the completion callback that gets notified of data sent
|
||||
* @param callback the completion callback that gets notified of data sent
|
||||
* @see #data(DataInfo)
|
||||
*/
|
||||
public void data(DataInfo dataInfo, Callback callback);
|
||||
|
||||
/**
|
||||
* <p>Sends asynchronously a HEADER frame on this stream.</p>
|
||||
* <p>HEADERS frames should always be sent after a SYN_REPLY frame.</p>
|
||||
* <p>Callers may use the returned future to wait for the headers to be actually sent.</p>
|
||||
*
|
||||
* <p>Sends asynchronously a HEADER frame on this stream.</p> <p>HEADERS frames should always be sent after a
|
||||
* SYN_REPLY frame.</p> <p>Callers may use the returned future to wait for the headers to be actually sent.</p>
|
||||
*
|
||||
* @param headersInfo the metadata to send
|
||||
* @return a future to wait for the headers to be sent
|
||||
|
@ -177,13 +160,12 @@ public interface Stream
|
|||
public void headers(HeadersInfo headersInfo) throws InterruptedException, ExecutionException, TimeoutException;
|
||||
|
||||
/**
|
||||
* <p>Sends asynchronously a HEADER frame on this stream.</p>
|
||||
* <p>HEADERS frames should always be sent after a SYN_REPLY frame.</p>
|
||||
* <p>Callers may pass a non-null completion callback to be notified of when the
|
||||
* headers have been actually sent.</p>
|
||||
* <p>Sends asynchronously a HEADER frame on this stream.</p> <p>HEADERS frames should always be sent after a
|
||||
* SYN_REPLY frame.</p> <p>Callers may pass a non-null completion callback to be notified of when the headers have
|
||||
* been actually sent.</p>
|
||||
*
|
||||
* @param headersInfo the metadata to send
|
||||
* @param callback the completion callback that gets notified of headers sent
|
||||
* @param callback the completion callback that gets notified of headers sent
|
||||
* @see #headers(HeadersInfo)
|
||||
*/
|
||||
public void headers(HeadersInfo headersInfo, Callback callback);
|
||||
|
@ -212,7 +194,8 @@ public interface Stream
|
|||
|
||||
/**
|
||||
* @param key the attribute key
|
||||
* @return an arbitrary object associated with the given key to this stream
|
||||
* @return an arbitrary object associated with the given key to this stream or null if no object can be found for
|
||||
* the given key.
|
||||
* @see #setAttribute(String, Object)
|
||||
*/
|
||||
public Object getAttribute(String key);
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.eclipse.jetty.spdy.server.http.HTTPSPDYHeader;
|
|||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
import org.eclipse.jetty.util.HttpCookieStore;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -50,45 +51,25 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* <p>{@link HTTPProxyEngine} implements a SPDY to HTTP proxy, that is, converts SPDY events received by clients into
|
||||
* HTTP events for the servers.</p>
|
||||
*/
|
||||
public class HTTPProxyEngine extends ProxyEngine implements StreamFrameListener
|
||||
public class HTTPProxyEngine extends ProxyEngine
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(HTTPProxyEngine.class);
|
||||
private static final String CLIENT_REQUEST_ATTRIBUTE = "org.eclipse.jetty.spdy.server.http.proxy.request";
|
||||
private static final Callback LOGGING_CALLBACK = new LoggingCallback();
|
||||
|
||||
private final HttpClient httpClient = new HttpClient();
|
||||
private volatile boolean committed;
|
||||
private final HttpClient httpClient;
|
||||
|
||||
public HTTPProxyEngine()
|
||||
public HTTPProxyEngine(HttpClient httpClient)
|
||||
{
|
||||
try
|
||||
{
|
||||
httpClient.start();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOG.warn("Exception while starting HttpClient: ", e);
|
||||
}
|
||||
this.httpClient = httpClient;
|
||||
configureHttpClient(httpClient);
|
||||
}
|
||||
|
||||
public long getConnectTimeout()
|
||||
private void configureHttpClient(HttpClient httpClient)
|
||||
{
|
||||
return httpClient.getConnectTimeout();
|
||||
}
|
||||
|
||||
public void setConnectTimeout(long connectTimeout)
|
||||
{
|
||||
httpClient.setConnectTimeout(connectTimeout);
|
||||
}
|
||||
|
||||
public long getIdleTimeout()
|
||||
{
|
||||
return httpClient.getIdleTimeout();
|
||||
}
|
||||
|
||||
public void setIdleTimeout(long idleTimeout)
|
||||
{
|
||||
httpClient.setIdleTimeout(idleTimeout);
|
||||
// Redirects must be proxied as is, not followed
|
||||
httpClient.setFollowRedirects(false);
|
||||
// Must not store cookies, otherwise cookies of different clients will mix
|
||||
httpClient.setCookieStore(new HttpCookieStore.Empty());
|
||||
}
|
||||
|
||||
public StreamFrameListener proxy(final Stream clientStream, SynInfo clientSynInfo, ProxyEngineSelector.ProxyServerInfo proxyServerInfo)
|
||||
|
@ -105,27 +86,55 @@ public class HTTPProxyEngine extends ProxyEngine implements StreamFrameListener
|
|||
|
||||
String host = proxyServerInfo.getHost();
|
||||
int port = proxyServerInfo.getAddress().getPort();
|
||||
|
||||
LOG.debug("Sending HTTP request to: {}", host + ":" + port);
|
||||
Request request = httpClient.newRequest(host, port)
|
||||
final Request request = httpClient.newRequest(host, port)
|
||||
.path(path)
|
||||
.method(HttpMethod.fromString(method));
|
||||
addNonSpdyHeadersToRequest(version, headers, request);
|
||||
|
||||
if (!clientSynInfo.isClose())
|
||||
{
|
||||
clientStream.setAttribute(CLIENT_REQUEST_ATTRIBUTE, request);
|
||||
request.content(new DeferredContentProvider());
|
||||
}
|
||||
|
||||
sendRequest(clientStream, request);
|
||||
|
||||
return this;
|
||||
return new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
// We proxy to HTTP so we do not receive replies
|
||||
throw new UnsupportedOperationException("Not Yet Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(Stream stream, HeadersInfo headersInfo)
|
||||
{
|
||||
throw new UnsupportedOperationException("Not Yet Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream clientStream, final DataInfo clientDataInfo)
|
||||
{
|
||||
LOG.debug("received clientDataInfo: {} for stream: {}", clientDataInfo, clientStream);
|
||||
|
||||
DeferredContentProvider contentProvider = (DeferredContentProvider)request.getContent();
|
||||
contentProvider.offer(clientDataInfo.asByteBuffer(true));
|
||||
|
||||
if (clientDataInfo.isClose())
|
||||
contentProvider.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void sendRequest(final Stream clientStream, Request request)
|
||||
{
|
||||
request.send(new Response.Listener.Empty()
|
||||
{
|
||||
private volatile boolean committed;
|
||||
|
||||
@Override
|
||||
public void onHeaders(final Response response)
|
||||
{
|
||||
|
@ -140,8 +149,13 @@ public class HTTPProxyEngine extends ProxyEngine implements StreamFrameListener
|
|||
LOG.debug("failed: ", x);
|
||||
response.abort(x);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void succeeded()
|
||||
{
|
||||
committed = true;
|
||||
}
|
||||
});
|
||||
committed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -185,7 +199,7 @@ public class HTTPProxyEngine extends ProxyEngine implements StreamFrameListener
|
|||
@Override
|
||||
public void onFailure(Response response, Throwable failure)
|
||||
{
|
||||
LOG.debug("onFailure called: {}", failure);
|
||||
LOG.debug("onFailure called: ", failure);
|
||||
if (committed)
|
||||
{
|
||||
LOG.debug("clientStream already committed. Resetting stream.");
|
||||
|
@ -221,6 +235,9 @@ public class HTTPProxyEngine extends ProxyEngine implements StreamFrameListener
|
|||
Fields responseHeaders = new Fields();
|
||||
for (HttpField header : response.getHeaders())
|
||||
responseHeaders.add(header.getName(), header.getValue());
|
||||
if (response.getStatus() > 0)
|
||||
responseHeaders.add(HTTPSPDYHeader.STATUS.name(clientStream.getSession().getVersion()),
|
||||
String.valueOf(response.getStatus()));
|
||||
addResponseProxyHeaders(clientStream, responseHeaders);
|
||||
return responseHeaders;
|
||||
}
|
||||
|
@ -232,32 +249,6 @@ public class HTTPProxyEngine extends ProxyEngine implements StreamFrameListener
|
|||
request.header(header.name(), header.value());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
// We proxy to HTTP so we do not receive replies
|
||||
throw new UnsupportedOperationException("Not Yet Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHeaders(Stream stream, HeadersInfo headersInfo)
|
||||
{
|
||||
throw new UnsupportedOperationException("Not Yet Implemented");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream clientStream, final DataInfo clientDataInfo)
|
||||
{
|
||||
LOG.debug("received clientDataInfo: {} for stream: {}", clientDataInfo, clientStream);
|
||||
Request request = (Request)clientStream.getAttribute(CLIENT_REQUEST_ATTRIBUTE);
|
||||
|
||||
DeferredContentProvider contentProvider = (DeferredContentProvider)request.getContent();
|
||||
contentProvider.offer(clientDataInfo.asByteBuffer(true));
|
||||
|
||||
if (clientDataInfo.isClose())
|
||||
contentProvider.close();
|
||||
}
|
||||
|
||||
static class LoggingCallback extends Callback.Adapter
|
||||
{
|
||||
@Override
|
||||
|
|
|
@ -24,14 +24,14 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
|
||||
public class SPDYTestUtils
|
||||
{
|
||||
public static Fields createHeaders(int port, short version, String httpMethod, String path)
|
||||
public static Fields createHeaders(String host, int port, short version, String httpMethod, String path)
|
||||
{
|
||||
Fields headers = new Fields();
|
||||
headers.put(HTTPSPDYHeader.METHOD.name(version), httpMethod);
|
||||
headers.put(HTTPSPDYHeader.URI.name(version), path);
|
||||
headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
|
||||
headers.put(HTTPSPDYHeader.SCHEME.name(version), "http");
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + port);
|
||||
headers.put(HTTPSPDYHeader.HOST.name(version), host + ":" + port);
|
||||
return headers;
|
||||
}
|
||||
|
||||
|
|
|
@ -80,7 +80,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", path);
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", path);
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -119,7 +119,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", uri);
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", uri);
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -155,7 +155,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "HEAD", path);
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "HEAD", path);
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -200,7 +200,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "POST", path);
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", path);
|
||||
headers.put("content-type", "application/x-www-form-urlencoded");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0),
|
||||
|
@ -242,7 +242,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "POST", path);
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", path);
|
||||
headers.put("content-type", "application/x-www-form-urlencoded");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0),
|
||||
|
@ -287,7 +287,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "POST", path);
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", path);
|
||||
headers.put("content-type", "application/x-www-form-urlencoded");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
|
||||
|
@ -329,7 +329,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -375,7 +375,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -426,7 +426,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(2);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -481,7 +481,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -534,7 +534,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -587,7 +587,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -645,7 +645,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -698,7 +698,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -735,7 +735,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -780,7 +780,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch latch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -834,7 +834,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(2);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -916,7 +916,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
|
@ -972,7 +972,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "GET", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "GET", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
final AtomicInteger contentLength = new AtomicInteger();
|
||||
|
@ -1036,7 +1036,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "POST", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -1079,7 +1079,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "POST", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, true, (byte)0), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -1124,7 +1124,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "POST", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -1192,7 +1192,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "POST", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -1260,7 +1260,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "POST", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
|
||||
final CountDownLatch responseLatch = new CountDownLatch(2);
|
||||
Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -1301,7 +1301,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
|
|||
}
|
||||
}), null);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(connector.getPort(), version, "POST", "/foo");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", connector.getPort(), version, "POST", "/foo");
|
||||
final CountDownLatch responseLatch = new CountDownLatch(1);
|
||||
Stream stream = session.syn(new SynInfo(5, TimeUnit.SECONDS, headers, false, (byte)0), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
|
|
@ -256,8 +256,43 @@ public class ProxyHTTPToSPDYTest
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testHEADRequest() throws Exception{
|
||||
// fail("Not yet implemented"); //TODO:
|
||||
public void testHEADRequest() throws Exception
|
||||
{
|
||||
InetSocketAddress proxyAddress = startProxy(startServer(new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
Assert.assertTrue(synInfo.isClose());
|
||||
Fields requestHeaders = synInfo.getHeaders();
|
||||
Assert.assertNotNull(requestHeaders.get("via"));
|
||||
|
||||
Fields responseHeaders = new Fields();
|
||||
responseHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
|
||||
responseHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200 OK");
|
||||
ReplyInfo replyInfo = new ReplyInfo(responseHeaders, true);
|
||||
stream.reply(replyInfo, new Callback.Adapter());
|
||||
|
||||
return null;
|
||||
}
|
||||
}));
|
||||
Socket client = new Socket();
|
||||
client.connect(proxyAddress);
|
||||
OutputStream output = client.getOutputStream();
|
||||
|
||||
String request = "" +
|
||||
"HEAD / HTTP/1.1\r\n" +
|
||||
"Host: localhost:" + proxyAddress.getPort() + "\r\n" +
|
||||
"\r\n";
|
||||
output.write(request.getBytes("UTF-8"));
|
||||
output.flush();
|
||||
|
||||
InputStream input = client.getInputStream();
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
|
||||
String line = reader.readLine();
|
||||
Assert.assertTrue(line.contains(" 200 "));
|
||||
|
||||
client.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -0,0 +1,301 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.spdy.server.proxy;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.DefaultHandler;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.api.Session;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.StringDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.eclipse.jetty.spdy.client.SPDYClient;
|
||||
import org.eclipse.jetty.spdy.server.http.SPDYTestUtils;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.Fields;
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static junit.framework.Assert.fail;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
@RunWith(value = Parameterized.class)
|
||||
public class ProxySPDYToHTTPLoadTest
|
||||
{
|
||||
@Rule
|
||||
public final TestWatcher testName = new TestWatcher()
|
||||
{
|
||||
|
||||
@Override
|
||||
public void starting(Description description)
|
||||
{
|
||||
super.starting(description);
|
||||
System.err.printf("Running %s.%s()%n",
|
||||
description.getClassName(),
|
||||
description.getMethodName());
|
||||
}
|
||||
};
|
||||
|
||||
private final short version;
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Short[]> parameters()
|
||||
{
|
||||
return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
|
||||
}
|
||||
|
||||
private SPDYClient.Factory factory;
|
||||
private Server server;
|
||||
private Server proxy;
|
||||
private ServerConnector proxyConnector;
|
||||
private SslContextFactory sslContextFactory = SPDYTestUtils.newSslContextFactory();
|
||||
|
||||
public ProxySPDYToHTTPLoadTest(short version)
|
||||
{
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
protected InetSocketAddress startServer(Handler handler) throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
server.setHandler(handler);
|
||||
server.addConnector(connector);
|
||||
server.start();
|
||||
return new InetSocketAddress("localhost", connector.getLocalPort());
|
||||
}
|
||||
|
||||
protected InetSocketAddress startProxy(InetSocketAddress server1, InetSocketAddress server2,
|
||||
long proxyConnectorTimeout, long proxyEngineTimeout) throws Exception
|
||||
{
|
||||
proxy = new Server();
|
||||
ProxyEngineSelector proxyEngineSelector = new ProxyEngineSelector();
|
||||
HttpClient httpClient = new HttpClient();
|
||||
httpClient.start();
|
||||
httpClient.setIdleTimeout(proxyEngineTimeout);
|
||||
HTTPProxyEngine httpProxyEngine = new HTTPProxyEngine(httpClient);
|
||||
proxyEngineSelector.putProxyEngine("http/1.1", httpProxyEngine);
|
||||
|
||||
proxyEngineSelector.putProxyServerInfo("localhost", new ProxyEngineSelector.ProxyServerInfo("http/1.1",
|
||||
server1.getHostName(), server1.getPort()));
|
||||
// server2 will be available at two different ProxyServerInfos with different hosts
|
||||
proxyEngineSelector.putProxyServerInfo("127.0.0.1", new ProxyEngineSelector.ProxyServerInfo("http/1.1",
|
||||
server2.getHostName(), server2.getPort()));
|
||||
proxyEngineSelector.putProxyServerInfo("127.0.0.2", new ProxyEngineSelector.ProxyServerInfo("http/1.1",
|
||||
server2.getHostName(), server2.getPort()));
|
||||
|
||||
proxyConnector = new HTTPSPDYProxyServerConnector(proxy, sslContextFactory, proxyEngineSelector);
|
||||
proxyConnector.setPort(0);
|
||||
proxyConnector.setIdleTimeout(proxyConnectorTimeout);
|
||||
proxy.addConnector(proxyConnector);
|
||||
proxy.start();
|
||||
return new InetSocketAddress("localhost", proxyConnector.getLocalPort());
|
||||
}
|
||||
|
||||
@Before
|
||||
public void init() throws Exception
|
||||
{
|
||||
factory = new SPDYClient.Factory(sslContextFactory);
|
||||
factory.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void destroy() throws Exception
|
||||
{
|
||||
if (server != null)
|
||||
{
|
||||
server.stop();
|
||||
server.join();
|
||||
}
|
||||
if (proxy != null)
|
||||
{
|
||||
proxy.stop();
|
||||
proxy.join();
|
||||
}
|
||||
factory.stop();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleLoadTest() throws Exception
|
||||
{
|
||||
String server1String = "server1";
|
||||
String server2String = "server2";
|
||||
|
||||
InetSocketAddress server1 = startServer(new TestServerHandler(server1String, null));
|
||||
InetSocketAddress server2 = startServer(new TestServerHandler(server2String, null));
|
||||
final InetSocketAddress proxyAddress = startProxy(server1, server2, 30000, 30000);
|
||||
|
||||
final int requestsPerClient = 50;
|
||||
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(3);
|
||||
|
||||
Runnable client1 = createClientRunnable(proxyAddress, requestsPerClient, server1String, "localhost");
|
||||
Runnable client2 = createClientRunnable(proxyAddress, requestsPerClient, server2String, "127.0.0.1");
|
||||
Runnable client3 = createClientRunnable(proxyAddress, requestsPerClient, server2String, "127.0.0.2");
|
||||
|
||||
List<Future> futures = new ArrayList<>();
|
||||
|
||||
futures.add(executorService.submit(client1));
|
||||
futures.add(executorService.submit(client2));
|
||||
futures.add(executorService.submit(client3));
|
||||
|
||||
for (Future future : futures)
|
||||
{
|
||||
future.get(60, TimeUnit.SECONDS);
|
||||
}
|
||||
}
|
||||
|
||||
private Runnable createClientRunnable(final InetSocketAddress proxyAddress, final int requestsPerClient,
|
||||
final String serverIdentificationString, final String serverHost)
|
||||
{
|
||||
Runnable client = new Runnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
Session client = factory.newSPDYClient(version).connect(proxyAddress, null).get(5, TimeUnit.SECONDS);
|
||||
for (int i = 0; i < requestsPerClient; i++)
|
||||
{
|
||||
sendSingleClientRequest(proxyAddress, client, serverIdentificationString, serverHost);
|
||||
}
|
||||
}
|
||||
catch (InterruptedException | ExecutionException | TimeoutException | IOException e)
|
||||
{
|
||||
fail();
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
return client;
|
||||
}
|
||||
|
||||
private void sendSingleClientRequest(InetSocketAddress proxyAddress, Session client, final String serverIdentificationString, String serverHost) throws ExecutionException, InterruptedException, TimeoutException
|
||||
{
|
||||
final String data = UUID.randomUUID().toString();
|
||||
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(serverHost, proxyAddress.getPort(), version, "POST", "/");
|
||||
|
||||
Stream stream = client.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
|
||||
{
|
||||
private final ByteArrayOutputStream result = new ByteArrayOutputStream();
|
||||
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Fields headers = replyInfo.getHeaders();
|
||||
assertThat("response comes from the given server", headers.get(serverIdentificationString),
|
||||
is(notNullValue()));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
result.write(dataInfo.asBytes(true), 0, dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
{
|
||||
assertThat("received data matches send data", data, is(result.toString()));
|
||||
dataLatch.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
stream.data(new StringDataInfo(data, true), new Callback.Adapter());
|
||||
|
||||
assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
assertThat("data has been received", dataLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
}
|
||||
|
||||
private class TestServerHandler extends DefaultHandler
|
||||
{
|
||||
private final String responseHeader;
|
||||
private final byte[] responseData;
|
||||
|
||||
private TestServerHandler(String responseHeader, byte[] responseData)
|
||||
{
|
||||
this.responseHeader = responseHeader;
|
||||
this.responseData = responseData;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request,
|
||||
HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
assertThat("Via Header is set", baseRequest.getHeader("X-Forwarded-For"), is(notNullValue()));
|
||||
assertThat("X-Forwarded-For Header is set", baseRequest.getHeader("X-Forwarded-For"),
|
||||
is(notNullValue()));
|
||||
assertThat("X-Forwarded-Host Header is set", baseRequest.getHeader("X-Forwarded-Host"),
|
||||
is(notNullValue()));
|
||||
assertThat("X-Forwarded-Proto Header is set", baseRequest.getHeader("X-Forwarded-Proto"),
|
||||
is(notNullValue()));
|
||||
assertThat("X-Forwarded-Server Header is set", baseRequest.getHeader("X-Forwarded-Server"),
|
||||
is(notNullValue()));
|
||||
baseRequest.setHandled(true);
|
||||
|
||||
IO.copy(request.getInputStream(), response.getOutputStream());
|
||||
|
||||
if (responseHeader != null)
|
||||
response.addHeader(responseHeader, "bar");
|
||||
if (responseData != null)
|
||||
response.getOutputStream().write(responseData);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -22,12 +22,15 @@ import java.io.BufferedReader;
|
|||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
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.client.HttpClient;
|
||||
import org.eclipse.jetty.server.Handler;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
|
@ -38,6 +41,7 @@ import org.eclipse.jetty.spdy.api.GoAwayReceivedInfo;
|
|||
import org.eclipse.jetty.spdy.api.PingInfo;
|
||||
import org.eclipse.jetty.spdy.api.PingResultInfo;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
import org.eclipse.jetty.spdy.api.RstInfo;
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
import org.eclipse.jetty.spdy.api.Session;
|
||||
import org.eclipse.jetty.spdy.api.SessionFrameListener;
|
||||
|
@ -45,6 +49,7 @@ import org.eclipse.jetty.spdy.api.Stream;
|
|||
import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.StringDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
|
||||
import org.eclipse.jetty.spdy.client.SPDYClient;
|
||||
import org.eclipse.jetty.spdy.server.http.HTTPSPDYHeader;
|
||||
import org.eclipse.jetty.spdy.server.http.SPDYTestUtils;
|
||||
|
@ -59,15 +64,14 @@ import org.junit.Test;
|
|||
import org.junit.rules.TestWatcher;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
import org.junit.runners.Parameterized;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
//@RunWith(value = Parameterized.class)
|
||||
@RunWith(JUnit4.class)
|
||||
@RunWith(value = Parameterized.class)
|
||||
public class ProxySPDYToHTTPTest
|
||||
{
|
||||
@Rule
|
||||
|
@ -83,13 +87,14 @@ public class ProxySPDYToHTTPTest
|
|||
description.getMethodName());
|
||||
}
|
||||
};
|
||||
private final short version = SPDY.V3;
|
||||
|
||||
// @Parameterized.Parameters
|
||||
// public static Collection<Short[]> parameters()
|
||||
// {
|
||||
// return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
|
||||
// }
|
||||
private final short version;
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Short[]> parameters()
|
||||
{
|
||||
return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
|
||||
}
|
||||
|
||||
private SPDYClient.Factory factory;
|
||||
private Server server;
|
||||
|
@ -97,10 +102,10 @@ public class ProxySPDYToHTTPTest
|
|||
private ServerConnector proxyConnector;
|
||||
private SslContextFactory sslContextFactory = SPDYTestUtils.newSslContextFactory();
|
||||
|
||||
// public ProxySPDYToHTTPTest(short version)
|
||||
// {
|
||||
// this.version = version;
|
||||
// }
|
||||
public ProxySPDYToHTTPTest(short version)
|
||||
{
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
protected InetSocketAddress startServer(Handler handler) throws Exception
|
||||
{
|
||||
|
@ -116,8 +121,10 @@ public class ProxySPDYToHTTPTest
|
|||
{
|
||||
proxy = new Server();
|
||||
ProxyEngineSelector proxyEngineSelector = new ProxyEngineSelector();
|
||||
HTTPProxyEngine httpProxyEngine = new HTTPProxyEngine();
|
||||
httpProxyEngine.setIdleTimeout(proxyEngineTimeout);
|
||||
HttpClient httpClient = new HttpClient();
|
||||
httpClient.start();
|
||||
httpClient.setIdleTimeout(proxyEngineTimeout);
|
||||
HTTPProxyEngine httpProxyEngine = new HTTPProxyEngine(httpClient);
|
||||
proxyEngineSelector.putProxyEngine("http/1.1", httpProxyEngine);
|
||||
proxyEngineSelector.putProxyServerInfo("localhost", new ProxyEngineSelector.ProxyServerInfo("http/1.1", address.getHostName(), address.getPort()));
|
||||
proxyConnector = new HTTPSPDYProxyServerConnector(proxy, sslContextFactory, proxyEngineSelector);
|
||||
|
@ -162,7 +169,7 @@ public class ProxySPDYToHTTPTest
|
|||
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(proxyAddress.getPort(), version, "GET", "/");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
|
||||
headers.put(header, "bar");
|
||||
headers.put("connection", "close");
|
||||
|
||||
|
@ -195,7 +202,7 @@ public class ProxySPDYToHTTPTest
|
|||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(proxyAddress.getPort(), version, "GET", "/");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
|
||||
headers.put(header, "bar");
|
||||
headers.put("connection", "close");
|
||||
|
||||
|
@ -227,6 +234,92 @@ public class ProxySPDYToHTTPTest
|
|||
assertThat("data has been received", dataLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpServerCommitsResponseTwice() throws Exception
|
||||
{
|
||||
final long timeout = 1000;
|
||||
|
||||
InetSocketAddress proxyAddress = startProxy(startServer(new DefaultHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
response.addHeader("some response", "header");
|
||||
response.flushBuffer();
|
||||
try
|
||||
{
|
||||
Thread.sleep(timeout * 2);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
}), 30000, timeout);
|
||||
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch resetLatch = new CountDownLatch(1);
|
||||
|
||||
Session client = factory.newSPDYClient(version).connect(proxyAddress, new ServerSessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onRst(Session session, RstInfo rstInfo)
|
||||
{
|
||||
resetLatch.countDown();
|
||||
}
|
||||
}).get(5, TimeUnit.SECONDS);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
|
||||
headers.put("connection", "close");
|
||||
|
||||
client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
replyLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
assertThat("stream is reset", resetLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHttpServerSendsRedirect() throws Exception
|
||||
{
|
||||
InetSocketAddress proxyAddress = startProxy(startServer(new DefaultHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
baseRequest.setHandled(true);
|
||||
response.setStatus(HttpServletResponse.SC_FOUND);
|
||||
response.setHeader("Location", "http://doesnot.exist");
|
||||
}
|
||||
}), 30000, 30000);
|
||||
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
|
||||
Session client = factory.newSPDYClient(version).connect(proxyAddress, null).get(5, TimeUnit.SECONDS);
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
|
||||
|
||||
client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
assertThat("Status code is 302", replyInfo.getHeaders().get(HTTPSPDYHeader.STATUS.name(version)).value(),
|
||||
is("302"));
|
||||
assertThat("Location header has been received", replyInfo.getHeaders().get("Location"), is(notNullValue()));
|
||||
replyLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSYNWithRequestContentThenREPLYAndDATA() throws Exception
|
||||
{
|
||||
|
@ -240,7 +333,7 @@ public class ProxySPDYToHTTPTest
|
|||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(proxyAddress.getPort(), version, "POST", "/");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "POST", "/");
|
||||
headers.put(header, "bar");
|
||||
headers.put("connection", "close");
|
||||
|
||||
|
@ -288,7 +381,7 @@ public class ProxySPDYToHTTPTest
|
|||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(proxyAddress.getPort(), version, "POST", "/");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "POST", "/");
|
||||
headers.put(header, "bar");
|
||||
headers.put("connection", "close");
|
||||
|
||||
|
@ -310,16 +403,13 @@ public class ProxySPDYToHTTPTest
|
|||
result.write(dataInfo.asBytes(true), 0, dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
{
|
||||
System.out.println("client received DATA: " + result);
|
||||
assertThat("received data matches send data", result.toString(), is(data + data2));
|
||||
dataLatch.countDown();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
System.out.println("DATA1 sent!!!!!!!!");
|
||||
stream.data(new StringDataInfo(data, false), new Callback.Adapter());
|
||||
System.out.println("DATA2 sent!!!!!!!!");
|
||||
stream.data(new StringDataInfo(data2, true), new Callback.Adapter());
|
||||
|
||||
assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
|
@ -344,7 +434,7 @@ public class ProxySPDYToHTTPTest
|
|||
}
|
||||
}).get(5, TimeUnit.SECONDS);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(proxyAddress.getPort(), version, "POST", "/");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "POST", "/");
|
||||
client.syn(new SynInfo(headers, false), null);
|
||||
assertThat("goAway has been received by proxy", goAwayLatch.await(2 * timeout, TimeUnit.MILLISECONDS),
|
||||
is(true));
|
||||
|
@ -376,7 +466,7 @@ public class ProxySPDYToHTTPTest
|
|||
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
|
||||
Fields headers = SPDYTestUtils.createHeaders(proxyAddress.getPort(), version, "POST", "/");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "POST", "/");
|
||||
headers.put(header, "bar");
|
||||
headers.put("connection", "close");
|
||||
|
||||
|
@ -393,8 +483,6 @@ public class ProxySPDYToHTTPTest
|
|||
});
|
||||
|
||||
assertThat("reply has been received", replyLatch.await(5, TimeUnit.SECONDS), is(true));
|
||||
|
||||
// client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -419,8 +507,6 @@ public class ProxySPDYToHTTPTest
|
|||
client.ping(new PingInfo(5, TimeUnit.SECONDS));
|
||||
|
||||
Assert.assertTrue(pingLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// client.goAway(new GoAwayInfo(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
private class TestServerHandler extends DefaultHandler
|
||||
|
@ -438,7 +524,6 @@ public class ProxySPDYToHTTPTest
|
|||
public void handle(String target, Request baseRequest, HttpServletRequest request,
|
||||
HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
System.out.println("HANDLER CALLED!!!");
|
||||
assertThat("Via Header is set", baseRequest.getHeader("X-Forwarded-For"), is(notNullValue()));
|
||||
assertThat("X-Forwarded-For Header is set", baseRequest.getHeader("X-Forwarded-For"),
|
||||
is(notNullValue()));
|
||||
|
|
|
@ -169,7 +169,7 @@ public class ProxySPDYToSPDYTest
|
|||
Session client = factory.newSPDYClient(version).connect(proxyAddress, null).get(5, TimeUnit.SECONDS);
|
||||
|
||||
final CountDownLatch replyLatch = new CountDownLatch(1);
|
||||
Fields headers = SPDYTestUtils.createHeaders(proxyAddress.getPort(), version, "GET", "/");
|
||||
Fields headers = SPDYTestUtils.createHeaders("localhost", proxyAddress.getPort(), version, "GET", "/");
|
||||
headers.put(header, "bar");
|
||||
client.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
|
|
@ -2,6 +2,6 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
|||
#org.eclipse.jetty.spdy.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.server.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.io.ssl.LEVEL=DEBUG
|
||||
org.eclipse.jetty.spdy.LEVEL=DEBUG
|
||||
org.eclipse.jetty.client.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.spdy.LEVEL=DEBUG
|
||||
#org.eclipse.jetty.client.LEVEL=DEBUG
|
||||
#org.mortbay.LEVEL=DEBUG
|
||||
|
|
Loading…
Reference in New Issue