diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java index 6aad1a187ca..704c595e979 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/StandardSession.java @@ -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()) diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java index dbe0c19dbd4..c8ca8a7bec9 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/api/Stream.java @@ -27,34 +27,28 @@ import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.Promise; /** - *
A {@link Stream} represents a bidirectional exchange of data on top of a {@link Session}.
- *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.
- *SPDY streams may terminate without this implying that the SPDY session is terminated.
- *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.
- *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.
- *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.
- *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:
+ *A {@link Stream} represents a bidirectional exchange of data on top of a {@link Session}.
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.
SPDY streams + * may terminate without this implying that the SPDY session is terminated.
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.
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.
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.
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:
** // 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>() { ... }); *- *
where the second call to {@link #data(DataInfo, Callback)} has a timeout smaller - * than the previous call.
- *The behavior of such style of invocations is unspecified (it may even throw an exception - similar - * to {@link WritePendingException}).
- *The correct sending of data frames is the following:
+ *where the second call to {@link #data(DataInfo, Callback)} has a timeout smaller than the previous call.
+ *The behavior of such style of invocations is unspecified (it may even throw an exception - similar to {@link + * WritePendingException}).
The correct sending of data frames is the following:
** final Stream stream = ...; * ... @@ -92,10 +86,8 @@ public interface Stream public Session getSession(); /** - *Initiate a unidirectional spdy pushstream associated to this stream asynchronously
- *
Callers may use the returned future to get the pushstream once it got created
- * - * + *Initiate a unidirectional spdy pushstream associated to this stream asynchronously
Callers may use the + * returned future to get the pushstream once it got created
* * @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; /** - *Initiate a unidirectional spdy pushstream associated to this stream asynchronously
- *
Callers may pass a non-null completion callback to be notified of when the - * pushstream has been established.
- * + *Initiate a unidirectional spdy pushstream associated to this stream asynchronously
Callers may pass a + * non-null completion callback to be notified of when the pushstream has been established.
* * @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, Promisecallback); /** - * Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.
- *Callers may use the returned future to wait for the reply to be actually sent.
- * + *Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.
Callers may use the returned + * future to wait for the reply to be actually sent.
* * @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; /** - *Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.
- *Callers may pass a non-null completion callback to be notified of when the - * reply has been actually sent.
+ *Sends asynchronously a SYN_REPLY frame in response to a SYN_STREAM frame.
Callers may pass a non-null + * completion callback to be notified of when the reply has been actually sent.
* * @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); /** - *Sends asynchronously a DATA frame on this stream.
- *DATA frames should always be sent after a SYN_REPLY frame.
- *Callers may use the returned future to wait for the data to be actually sent.
- * + *Sends asynchronously a DATA frame on this stream.
DATA frames should always be sent after a SYN_REPLY + * frame.
Callers may use the returned future to wait for the data to be actually sent.
* * @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; /** - *Sends asynchronously a DATA frame on this stream.
- *DATA frames should always be sent after a SYN_REPLY frame.
- *Callers may pass a non-null completion callback to be notified of when the - * data has been actually sent.
+ *Sends asynchronously a DATA frame on this stream.
DATA frames should always be sent after a SYN_REPLY + * frame.
Callers may pass a non-null completion callback to be notified of when the data has been actually + * sent.
* * @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); /** - *Sends asynchronously a HEADER frame on this stream.
- *HEADERS frames should always be sent after a SYN_REPLY frame.
- *Callers may use the returned future to wait for the headers to be actually sent.
- * + *Sends asynchronously a HEADER frame on this stream.
HEADERS frames should always be sent after a + * SYN_REPLY frame.
Callers may use the returned future to wait for the headers to be actually sent.
* * @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; /** - *Sends asynchronously a HEADER frame on this stream.
- *HEADERS frames should always be sent after a SYN_REPLY frame.
- *Callers may pass a non-null completion callback to be notified of when the - * headers have been actually sent.
+ *Sends asynchronously a HEADER frame on this stream.
HEADERS frames should always be sent after a + * SYN_REPLY frame.
Callers may pass a non-null completion callback to be notified of when the headers have + * been actually sent.
* * @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); diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPProxyEngine.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPProxyEngine.java index 17a4c4c0c55..971be08ea0c 100644 --- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPProxyEngine.java +++ b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/HTTPProxyEngine.java @@ -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; *{@link HTTPProxyEngine} implements a SPDY to HTTP proxy, that is, converts SPDY events received by clients into * HTTP events for the servers.
*/ -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 diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SPDYTestUtils.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SPDYTestUtils.java index 168ff3e6a2a..d861a737f5f 100644 --- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SPDYTestUtils.java +++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/SPDYTestUtils.java @@ -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; } diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ServerHTTPSPDYTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ServerHTTPSPDYTest.java index 3c4824859e3..0c9e37b750d 100644 --- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ServerHTTPSPDYTest.java +++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/http/ServerHTTPSPDYTest.java @@ -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() { diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPToSPDYTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPToSPDYTest.java index f1ae77caf7d..e65cebb4051 100644 --- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPToSPDYTest.java +++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPToSPDYTest.java @@ -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 diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPLoadTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPLoadTest.java new file mode 100644 index 00000000000..6d6ba31c8fb --- /dev/null +++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPLoadTest.java @@ -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 Collectionparameters() + { + 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 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); + } + } + +} diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPTest.java index 5b68c3366dd..2d6fcea0880 100644 --- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPTest.java +++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToHTTPTest.java @@ -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 parameters() - // { - // return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3}); - // } + private final short version; + + @Parameterized.Parameters + public static Collection 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())); diff --git a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToSPDYTest.java b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToSPDYTest.java index 843fbc5d653..211a2c4a790 100644 --- a/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToSPDYTest.java +++ b/jetty-spdy/spdy-http-server/src/test/java/org/eclipse/jetty/spdy/server/proxy/ProxySPDYToSPDYTest.java @@ -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() { diff --git a/jetty-spdy/spdy-http-server/src/test/resources/jetty-logging.properties b/jetty-spdy/spdy-http-server/src/test/resources/jetty-logging.properties index 76662696b05..4286a47e37d 100644 --- a/jetty-spdy/spdy-http-server/src/test/resources/jetty-logging.properties +++ b/jetty-spdy/spdy-http-server/src/test/resources/jetty-logging.properties @@ -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