Merge branch 'SPDY_ff_fixes'

This commit is contained in:
Thomas Becker 2012-11-28 17:44:22 +01:00
commit 33e951d44a
6 changed files with 172 additions and 146 deletions

View File

@ -1060,15 +1060,19 @@ public class StandardSession implements ISession, Parser.Listener, Dumpable
execute(new Runnable() execute(new Runnable()
{ {
@Override @Override
public void run() { callback.succeeded() ; } public void run()
{
callback.succeeded();
flush();
}
}); });
} }
@Override @Override
public void call(Callback callback) public void call(Callback callback)
{ {
callback.succeeded(); callback.succeeded();
flush();
} }
} }

View File

@ -22,6 +22,8 @@ import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Set; import java.util.Set;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpGenerator;
@ -32,10 +34,12 @@ import org.eclipse.jetty.io.EofException;
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.server.HttpTransport; import org.eclipse.jetty.server.HttpTransport;
import org.eclipse.jetty.spdy.StreamException;
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo; import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
import org.eclipse.jetty.spdy.api.ReplyInfo; import org.eclipse.jetty.spdy.api.ReplyInfo;
import org.eclipse.jetty.spdy.api.SPDY; import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Stream; import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.SynInfo; import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.util.BlockingCallback; import org.eclipse.jetty.util.BlockingCallback;
import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.BufferUtil;
@ -56,6 +60,7 @@ public class HttpTransportOverSPDY implements HttpTransport
private final Stream stream; private final Stream stream;
private final Fields requestHeaders; private final Fields requestHeaders;
private final BlockingCallback streamBlocker = new BlockingCallback(); private final BlockingCallback streamBlocker = new BlockingCallback();
private final AtomicBoolean committed = new AtomicBoolean();
public HttpTransportOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint, PushStrategy pushStrategy, Stream stream, Fields requestHeaders) public HttpTransportOverSPDY(Connector connector, HttpConfiguration configuration, EndPoint endPoint, PushStrategy pushStrategy, Stream stream, Fields requestHeaders)
{ {
@ -71,9 +76,9 @@ public class HttpTransportOverSPDY implements HttpTransport
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback) public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback)
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled())
LOG.debug("send {} {} {} {} last={}%n",this,stream,info,BufferUtil.toDetailString(content),lastContent); LOG.debug("send {} {} {} {} last={}",this,stream,info,BufferUtil.toDetailString(content),lastContent);
if (stream.isClosed() || stream.isReset() ) if (stream.isClosed() || stream.isReset())
{ {
callback.failed(new EofException("stream closed")); callback.failed(new EofException("stream closed"));
return; return;
@ -93,6 +98,11 @@ public class HttpTransportOverSPDY implements HttpTransport
if (info!=null) if (info!=null)
{ {
if(!committed.compareAndSet(false, true)){
callback.failed(new StreamException(stream.getId(), StreamStatus.PROTOCOL_ERROR,
"Stream already committed!"));
return;
}
short version = stream.getSession().getVersion(); short version = stream.getSession().getVersion();
Fields headers = new Fields(); Fields headers = new Fields();
@ -157,20 +167,16 @@ public class HttpTransportOverSPDY implements HttpTransport
} }
@Override @Override
public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent) throws IOException public void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent) throws EofException
{ {
send(info,content,lastContent,streamBlocker); send(info,content,lastContent,streamBlocker);
try try
{ {
streamBlocker.block(); streamBlocker.block();
} }
catch (IOException e) catch (InterruptedException | TimeoutException | IOException e)
{ {
throw e; LOG.debug(e);
}
catch (Exception e)
{
throw new EofException(e);
} }
} }

View File

@ -48,6 +48,8 @@ public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
{ {
final CountDownLatch slowServerLatch = new CountDownLatch(1); final CountDownLatch slowServerLatch = new CountDownLatch(1);
final CountDownLatch fastServerLatch = new CountDownLatch(1); final CountDownLatch fastServerLatch = new CountDownLatch(1);
final String fastPath = "/fast";
final String slowPath = "/slow";
Session session = startClient(version, startHTTPServer(version, new AbstractHandler() Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
{ {
@Override @Override
@ -59,11 +61,11 @@ public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
request.setHandled(true); request.setHandled(true);
switch (target) switch (target)
{ {
case "/slow": case slowPath:
Assert.assertTrue(fastServerLatch.await(10, TimeUnit.SECONDS)); Assert.assertTrue(fastServerLatch.await(10, TimeUnit.SECONDS));
slowServerLatch.countDown(); slowServerLatch.countDown();
break; break;
case "/fast": case fastPath:
fastServerLatch.countDown(); fastServerLatch.countDown();
break; break;
default: default:
@ -79,11 +81,7 @@ public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
}), null); }), null);
// Perform slow request. This will wait on server side until the fast request wakes it up // Perform slow request. This will wait on server side until the fast request wakes it up
Fields headers = new Fields(); Fields headers = createHeaders(slowPath);
headers.put(HTTPSPDYHeader.METHOD.name(version), "GET");
headers.put(HTTPSPDYHeader.URI.name(version), "/slow");
headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + connector.getLocalPort());
final CountDownLatch slowClientLatch = new CountDownLatch(1); final CountDownLatch slowClientLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{ {
@ -97,11 +95,7 @@ public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
}); });
// Perform the fast request. This will wake up the slow request // Perform the fast request. This will wake up the slow request
headers.clear(); headers = createHeaders(fastPath);
headers.put(HTTPSPDYHeader.METHOD.name(version), "GET");
headers.put(HTTPSPDYHeader.URI.name(version), "/fast");
headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + connector.getLocalPort());
final CountDownLatch fastClientLatch = new CountDownLatch(1); final CountDownLatch fastClientLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{ {
@ -119,4 +113,14 @@ public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
Assert.assertTrue(fastClientLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(fastClientLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(slowClientLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(slowClientLatch.await(5, TimeUnit.SECONDS));
} }
private Fields createHeaders(String path)
{
Fields headers = new Fields();
headers.put(HTTPSPDYHeader.METHOD.name(version), "GET");
headers.put(HTTPSPDYHeader.URI.name(version), path);
headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + connector.getLocalPort());
return headers;
}
} }

View File

@ -18,15 +18,18 @@
package org.eclipse.jetty.spdy.server.http; package org.eclipse.jetty.spdy.server.http;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Random; import java.util.Random;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConfiguration;
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo; import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.ReplyInfo; import org.eclipse.jetty.spdy.api.ReplyInfo;
import org.eclipse.jetty.spdy.api.SPDY; import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.Session;
@ -45,7 +48,6 @@ import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.Matchers.any; import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong; import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyObject;
import static org.mockito.Mockito.times; import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when; import static org.mockito.Mockito.when;
@ -79,6 +81,7 @@ public class HttpTransportOverSPDYTest
{ {
httpTransportOverSPDY = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint, pushStrategy, httpTransportOverSPDY = new HttpTransportOverSPDY(connector, httpConfiguration, endPoint, pushStrategy,
stream, new Fields()); stream, new Fields());
when(responseInfo.getStatus()).thenReturn(HttpStatus.OK_200);
when(stream.getSession()).thenReturn(session); when(stream.getSession()).thenReturn(session);
when(session.getVersion()).thenReturn(SPDY.V3); when(session.getVersion()).thenReturn(SPDY.V3);
when(stream.isClosed()).thenReturn(false); when(stream.isClosed()).thenReturn(false);
@ -169,7 +172,7 @@ public class HttpTransportOverSPDYTest
ByteBuffer content = null; ByteBuffer content = null;
boolean lastContent = true; boolean lastContent = true;
// when stream.isClosed() is called a 2nd time, the teply has closed the stream already // when stream.isClosed() is called a 2nd time, the reply has closed the stream already
when(stream.isClosed()).thenReturn(false).thenReturn(true); when(stream.isClosed()).thenReturn(false).thenReturn(true);
httpTransportOverSPDY.send(responseInfo, content, lastContent, callback); httpTransportOverSPDY.send(responseInfo, content, lastContent, callback);
@ -234,6 +237,23 @@ public class HttpTransportOverSPDYTest
assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096)); assertThat("ByteBuffer length is 4096", dataInfoCaptor.getValue().length(), is(4096));
} }
@Test
public void testVerifyThatAStreamIsNotCommittedTwice() throws IOException
{
ByteBuffer content = createRandomByteBuffer();
boolean lastContent = false;
httpTransportOverSPDY.send(responseInfo,content,lastContent, callback);
ArgumentCaptor<ReplyInfo> replyInfoCaptor = ArgumentCaptor.forClass(ReplyInfo.class);
verify(stream, times(1)).reply(replyInfoCaptor.capture());
assertThat("ReplyInfo close is false", replyInfoCaptor.getValue().isClose(), is(false));
httpTransportOverSPDY.send(HttpGenerator.RESPONSE_500_INFO, null,true);
verify(stream, times(0)).data(any(DataInfo.class));
verify(stream, times(1)).data(any(DataInfo.class), anyLong(), any(TimeUnit.class), any(Callback.class));
}
private ByteBuffer createRandomByteBuffer() private ByteBuffer createRandomByteBuffer()
{ {
ByteBuffer content = BufferUtil.allocate(8192); ByteBuffer content = BufferUtil.allocate(8192);

View File

@ -34,11 +34,13 @@ import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.spdy.api.DataInfo; import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.ReplyInfo; 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.SPDY;
import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.SessionFrameListener; import org.eclipse.jetty.spdy.api.SessionFrameListener;
import org.eclipse.jetty.spdy.api.Stream; import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener; import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.StreamStatus;
import org.eclipse.jetty.spdy.api.SynInfo; import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory; import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.Fields;
@ -51,13 +53,15 @@ import static org.junit.Assert.assertThat;
public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
{ {
private final String mainResource = "/index.html";
private final int referrerPushPeriod = 1000; private final int referrerPushPeriod = 1000;
private final String mainResource = "/index.html";
private final String cssResource = "/style.css"; private final String cssResource = "/style.css";
private InetSocketAddress serverAddress; private InetSocketAddress serverAddress;
private ReferrerPushStrategy pushStrategy; private ReferrerPushStrategy pushStrategy;
private ConnectionFactory defaultFactory; private ConnectionFactory defaultFactory;
private Fields mainRequestHeaders; private Fields mainRequestHeaders;
private Fields associatedCSSRequestHeaders;
private Fields associatedJSRequestHeaders;
public ReferrerPushStrategyTest(short version) public ReferrerPushStrategyTest(short version)
{ {
@ -83,6 +87,8 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
else else
connector.setDefaultProtocol(defaultFactory.getProtocol()); connector.setDefaultProtocol(defaultFactory.getProtocol());
mainRequestHeaders = createHeadersWithoutReferrer(mainResource); mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
associatedCSSRequestHeaders = createHeaders(cssResource);
associatedJSRequestHeaders = createHeaders("/application.js");
} }
@Test @Test
@ -92,6 +98,47 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
run2ndClientRequests(true, true); run2ndClientRequests(true, true);
} }
@Test
public void testClientResetsPushStreams() throws Exception
{
sendMainRequestAndCSSRequest();
final CountDownLatch pushDataLatch = new CountDownLatch(1);
final CountDownLatch pushSynHeadersValid = new CountDownLatch(1);
Session session = startClient(version, serverAddress, new SessionFrameListener.Adapter()
{
@Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
{
validateHeaders(synInfo.getHeaders(), pushSynHeadersValid);
assertThat("Stream is unidirectional", stream.isUnidirectional(), is(true));
assertThat("URI header ends with css", synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version))
.value().endsWith
("" +
".css"),
is(true));
stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.REFUSED_STREAM));
return new StreamFrameListener.Adapter()
{
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
dataInfo.consume(dataInfo.length());
pushDataLatch.countDown();
}
};
}
});
// Send main request. That should initiate the push syn's which get reset by the client
sendRequest(session, mainRequestHeaders);
assertThat("No push data is received", pushDataLatch.await(1, TimeUnit.SECONDS), is(false));
assertThat("Push syn headers valid", pushSynHeadersValid.await(5, TimeUnit.SECONDS), is(true));
sendRequest(session, associatedCSSRequestHeaders);
}
@Test @Test
public void testUserAgentBlackList() throws Exception public void testUserAgentBlackList() throws Exception
{ {
@ -103,11 +150,11 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
@Test @Test
public void testReferrerPushPeriod() throws Exception public void testReferrerPushPeriod() throws Exception
{ {
Session session1 = sendMainRequestAndCSSRequest(); Session session = sendMainRequestAndCSSRequest();
// Sleep for pushPeriod This should prevent application.js from being mapped as pushResource // Sleep for pushPeriod This should prevent application.js from being mapped as pushResource
Thread.sleep(referrerPushPeriod + 1); Thread.sleep(referrerPushPeriod + 1);
sendJSRequest(session1); sendRequest(session, associatedJSRequestHeaders);
run2ndClientRequests(false, true); run2ndClientRequests(false, true);
} }
@ -119,9 +166,9 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
connector.addConnectionFactory(defaultFactory); connector.addConnectionFactory(defaultFactory);
connector.setDefaultProtocol(defaultFactory.getProtocol()); // TODO I don't think this is right connector.setDefaultProtocol(defaultFactory.getProtocol()); // TODO I don't think this is right
Session session1 = sendMainRequestAndCSSRequest(); Session session = sendMainRequestAndCSSRequest();
sendJSRequest(session1); sendRequest(session, associatedJSRequestHeaders);
run2ndClientRequests(false, true); run2ndClientRequests(false, true);
} }
@ -148,62 +195,43 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
private Session sendMainRequestAndCSSRequest() throws Exception private Session sendMainRequestAndCSSRequest() throws Exception
{ {
Session session1 = startClient(version, serverAddress, null); Session session = startClient(version, serverAddress, null);
final CountDownLatch mainResourceLatch = new CountDownLatch(1); sendRequest(session, mainRequestHeaders);
session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter() sendRequest(session, associatedCSSRequestHeaders);
{
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
dataInfo.consume(dataInfo.length());
if (dataInfo.isClose())
mainResourceLatch.countDown();
}
});
Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
final CountDownLatch associatedResourceLatch1 = new CountDownLatch(1); return session;
Fields associatedRequestHeaders1 = createHeaders(cssResource);
session1.syn(new SynInfo(associatedRequestHeaders1, true), new StreamFrameListener.Adapter()
{
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
dataInfo.consume(dataInfo.length());
if (dataInfo.isClose())
associatedResourceLatch1.countDown();
}
});
Assert.assertTrue(associatedResourceLatch1.await(5, TimeUnit.SECONDS));
return session1;
} }
private void sendRequest(Session session, Fields requestHeaders) throws InterruptedException
private void sendJSRequest(Session session1) throws InterruptedException
{ {
final CountDownLatch associatedResourceLatch2 = new CountDownLatch(1); final CountDownLatch dataReceivedLatch = new CountDownLatch(1);
String jsResource = "/application.js"; final CountDownLatch received200OKLatch = new CountDownLatch(1);
Fields associatedRequestHeaders2 = createHeaders(jsResource); session.syn(new SynInfo(requestHeaders, true), new StreamFrameListener.Adapter()
session1.syn(new SynInfo(associatedRequestHeaders2, true), new StreamFrameListener.Adapter()
{ {
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
if ("200 OK".equals(replyInfo.getHeaders().get(HTTPSPDYHeader.STATUS.name(version)).value()))
received200OKLatch.countDown();
super.onReply(stream, replyInfo);
}
@Override @Override
public void onData(Stream stream, DataInfo dataInfo) public void onData(Stream stream, DataInfo dataInfo)
{ {
dataInfo.consume(dataInfo.length()); dataInfo.consume(dataInfo.length());
if (dataInfo.isClose()) if (dataInfo.isClose())
associatedResourceLatch2.countDown(); dataReceivedLatch.countDown();
} }
}); });
Assert.assertTrue(associatedResourceLatch2.await(5, TimeUnit.SECONDS)); Assert.assertTrue(received200OKLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(dataReceivedLatch.await(5, TimeUnit.SECONDS));
} }
private void run2ndClientRequests(final boolean validateHeaders, private void run2ndClientRequests(final boolean validateHeaders,
boolean expectPushResource) throws Exception boolean expectPushResource) throws Exception
{ {
// Create another client, and perform the same request for the main resource,
// we expect the css being pushed, but not the js
final CountDownLatch mainStreamLatch = new CountDownLatch(2); final CountDownLatch mainStreamLatch = new CountDownLatch(2);
final CountDownLatch pushDataLatch = new CountDownLatch(1); final CountDownLatch pushDataLatch = new CountDownLatch(1);
final CountDownLatch pushSynHeadersValid = new CountDownLatch(1); final CountDownLatch pushSynHeadersValid = new CountDownLatch(1);
@ -215,11 +243,11 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
if (validateHeaders) if (validateHeaders)
validateHeaders(synInfo.getHeaders(), pushSynHeadersValid); validateHeaders(synInfo.getHeaders(), pushSynHeadersValid);
assertThat("Stream is unidirectional",stream.isUnidirectional(),is(true)); assertThat("Stream is unidirectional", stream.isUnidirectional(), is(true));
assertThat("URI header ends with css", synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version)) assertThat("URI header ends with css", synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version))
.value().endsWith .value().endsWith
("" + ("" +
".css"), ".css"),
is(true)); is(true));
return new StreamFrameListener.Adapter() return new StreamFrameListener.Adapter()
{ {
@ -252,13 +280,13 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
} }
}); });
assertThat("Main request reply and/or data not received", mainStreamLatch.await(5, TimeUnit.SECONDS), is(true)); assertThat("Main request reply and/or data received", mainStreamLatch.await(5, TimeUnit.SECONDS), is(true));
if (expectPushResource) if (expectPushResource)
assertThat("Pushed data not received", pushDataLatch.await(5, TimeUnit.SECONDS), is(true)); assertThat("Pushed data received", pushDataLatch.await(5, TimeUnit.SECONDS), is(true));
else else
assertThat("No push data is received", pushDataLatch.await(1, TimeUnit.SECONDS), is(false)); assertThat("No push data is received", pushDataLatch.await(1, TimeUnit.SECONDS), is(false));
if (validateHeaders) if (validateHeaders)
assertThat("Push syn headers not valid", pushSynHeadersValid.await(5, TimeUnit.SECONDS), is(true)); assertThat("Push syn headers valid", pushSynHeadersValid.await(5, TimeUnit.SECONDS), is(true));
} }
@Test @Test
@ -295,19 +323,7 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
}); });
Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
final CountDownLatch associatedResourceLatch = new CountDownLatch(1); sendRequest(session1, createHeaders(cssResource));
Fields associatedRequestHeaders = createHeaders(cssResource);
session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
{
@Override
public void onData(Stream stream, DataInfo dataInfo)
{
dataInfo.consume(dataInfo.length());
if (dataInfo.isClose())
associatedResourceLatch.countDown();
}
});
Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
// Create another client, and perform the same request for the main resource, we expect the css being pushed // Create another client, and perform the same request for the main resource, we expect the css being pushed

View File

@ -36,6 +36,7 @@ import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.continuation.Continuation; import org.eclipse.jetty.continuation.Continuation;
import org.eclipse.jetty.continuation.ContinuationSupport; import org.eclipse.jetty.continuation.ContinuationSupport;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.spdy.api.BytesDataInfo; import org.eclipse.jetty.spdy.api.BytesDataInfo;
@ -47,6 +48,7 @@ import org.eclipse.jetty.spdy.api.StreamFrameListener;
import org.eclipse.jetty.spdy.api.StringDataInfo; import org.eclipse.jetty.spdy.api.StringDataInfo;
import org.eclipse.jetty.spdy.api.SynInfo; import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.util.Fields; import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.log.StdErrLog;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
@ -58,7 +60,6 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
super(version); super(version);
} }
@Test
public void testSimpleGET() throws Exception public void testSimpleGET() throws Exception
{ {
final String path = "/foo"; final String path = "/foo";
@ -78,12 +79,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = new Fields(); Fields headers = createHeaders("GET", path);
headers.put(HTTPSPDYHeader.METHOD.name(version), "GET");
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:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{ {
@ -122,12 +118,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = new Fields(); Fields headers = createHeaders("GET", uri);
headers.put(HTTPSPDYHeader.METHOD.name(version), "GET");
headers.put(HTTPSPDYHeader.URI.name(version), uri);
headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
headers.put(HTTPSPDYHeader.SCHEME.name(version), "http");
headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{ {
@ -163,12 +154,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = new Fields(); Fields headers = createHeaders("HEAD", path);
headers.put(HTTPSPDYHeader.METHOD.name(version), "HEAD");
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:" + connector.getLocalPort());
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{ {
@ -213,12 +199,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = new Fields(); Fields headers = createHeaders("POST", path);
headers.put(HTTPSPDYHeader.METHOD.name(version), "POST");
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:" + connector.getLocalPort());
headers.put("content-type", "application/x-www-form-urlencoded"); headers.put("content-type", "application/x-www-form-urlencoded");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter() Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
@ -259,12 +240,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = new Fields(); Fields headers = createHeaders("POST", path);
headers.put(HTTPSPDYHeader.METHOD.name(version), "POST");
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:" + connector.getLocalPort());
headers.put("content-type", "application/x-www-form-urlencoded"); headers.put("content-type", "application/x-www-form-urlencoded");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter() Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
@ -308,12 +284,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = new Fields(); Fields headers = createHeaders("POST", path);
headers.put(HTTPSPDYHeader.METHOD.name(version), "POST");
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:" + connector.getLocalPort());
headers.put("content-type", "application/x-www-form-urlencoded"); headers.put("content-type", "application/x-www-form-urlencoded");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter() Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
@ -354,7 +325,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
@ -400,7 +371,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
@ -451,7 +422,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(2); final CountDownLatch dataLatch = new CountDownLatch(2);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
@ -506,7 +477,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
@ -559,7 +530,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
@ -612,7 +583,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
@ -670,7 +641,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
@ -723,7 +694,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{ {
@ -760,7 +731,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
@ -792,18 +763,20 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
@Test @Test
public void testGETWithException() throws Exception public void testGETWithException() throws Exception
{ {
StdErrLog log = StdErrLog.getLogger(HttpChannel.class);
log.setHideStacks(true);
Session session = startClient(version, startHTTPServer(version, new AbstractHandler() Session session = startClient(version, startHTTPServer(version, new AbstractHandler()
{ {
@Override @Override
public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse) public void handle(String target, Request request, HttpServletRequest httpRequest, HttpServletResponse httpResponse)
throws IOException, ServletException throws IOException, ServletException
{ {
// TODO suppress stack from test log
throw new NullPointerException("thrown_explicitly_by_the_test"); throw new NullPointerException("thrown_explicitly_by_the_test");
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
@ -830,6 +803,8 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
}); });
Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(replyLatch.await(5, TimeUnit.SECONDS));
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
log.setHideStacks(false);
} }
@Test @Test
@ -855,7 +830,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(2); final CountDownLatch dataLatch = new CountDownLatch(2);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
@ -937,7 +912,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
@ -993,7 +968,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("GET"); Fields headers = createHeaders("GET", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
final CountDownLatch dataLatch = new CountDownLatch(1); final CountDownLatch dataLatch = new CountDownLatch(1);
final AtomicInteger contentLength = new AtomicInteger(); final AtomicInteger contentLength = new AtomicInteger();
@ -1057,7 +1032,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("POST"); Fields headers = createHeaders("POST", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter() Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
{ {
@ -1100,7 +1075,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("POST"); Fields headers = createHeaders("POST", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter() session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{ {
@ -1145,7 +1120,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("POST"); Fields headers = createHeaders("POST", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter() Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
{ {
@ -1213,7 +1188,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("POST"); Fields headers = createHeaders("POST", "/foo");
final CountDownLatch replyLatch = new CountDownLatch(1); final CountDownLatch replyLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter() Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
{ {
@ -1281,7 +1256,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("POST"); Fields headers = createHeaders("POST", "/foo");
final CountDownLatch responseLatch = new CountDownLatch(2); final CountDownLatch responseLatch = new CountDownLatch(2);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter() Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
{ {
@ -1322,7 +1297,7 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
} }
}), null); }), null);
Fields headers = createHeaders("POST"); Fields headers = createHeaders("POST", "/foo");
final CountDownLatch responseLatch = new CountDownLatch(1); final CountDownLatch responseLatch = new CountDownLatch(1);
Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter() Stream stream = session.syn(new SynInfo(headers, false), new StreamFrameListener.Adapter()
{ {
@ -1341,14 +1316,15 @@ public class ServerHTTPSPDYTest extends AbstractHTTPSPDYTest
Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS)); Assert.assertTrue(responseLatch.await(5, TimeUnit.SECONDS));
} }
private Fields createHeaders(String httpMethod) private Fields createHeaders(String httpMethod, String path)
{ {
Fields headers = new Fields(); Fields headers = new Fields();
headers.put(HTTPSPDYHeader.METHOD.name(version), httpMethod); headers.put(HTTPSPDYHeader.METHOD.name(version), httpMethod);
headers.put(HTTPSPDYHeader.URI.name(version), "/foo"); headers.put(HTTPSPDYHeader.URI.name(version), path);
headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1"); headers.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
headers.put(HTTPSPDYHeader.SCHEME.name(version), "http"); headers.put(HTTPSPDYHeader.SCHEME.name(version), "http");
headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + connector.getLocalPort()); headers.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + connector.getLocalPort());
return headers; return headers;
} }
} }