Completed implementation of the referrer SPDY push strategy.
This commit is contained in:
parent
362e011851
commit
14f8091252
|
@ -16,12 +16,14 @@
|
|||
|
||||
package org.eclipse.jetty.spdy.http;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
|
@ -44,77 +46,126 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* will have the CSS stylesheet as referrer, so there is some degree of recursion that
|
||||
* needs to be handled.</p>
|
||||
*
|
||||
*
|
||||
* TODO: this class is kind-of leaking since the resources map is always adding entries
|
||||
* TODO: although these entries will be limited by the number of application pages.
|
||||
* TODO: however, there is no ConcurrentLinkedHashMap yet in JDK (there is in Guava though)
|
||||
* TODO: so we cannot use the built-in LRU features of LinkedHashMap
|
||||
*
|
||||
* TODO: Wikipedia maps URLs like http://en.wikipedia.org/wiki/File:PNG-Gradient_hex.png
|
||||
* TODO: to text/html, so perhaps we need to improve isPushResource() by looking at the
|
||||
* TODO: response Content-Type header, and not only at the URL extension
|
||||
*/
|
||||
public class ReferrerPushStrategy implements PushStrategy
|
||||
{
|
||||
private static final Logger logger = Log.getLogger(ReferrerPushStrategy.class);
|
||||
private final ConcurrentMap<String, Set<String>> resources = new ConcurrentHashMap<>();
|
||||
private List<String> mainSuffixes = new ArrayList<>();
|
||||
private List<String> pushSuffixes = new ArrayList<>();
|
||||
private final Set<Pattern> pushRegexps = new LinkedHashSet<>();
|
||||
private final Set<Pattern> allowedPushOrigins = new LinkedHashSet<>();
|
||||
|
||||
public ReferrerPushStrategy()
|
||||
{
|
||||
this(Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpg", ".*\\.gif"));
|
||||
}
|
||||
|
||||
public ReferrerPushStrategy(List<String> pushRegexps)
|
||||
{
|
||||
this(pushRegexps, Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
public ReferrerPushStrategy(List<String> pushRegexps, List<String> allowedPushOrigins)
|
||||
{
|
||||
for (String pushRegexp : pushRegexps)
|
||||
this.pushRegexps.add(Pattern.compile(pushRegexp));
|
||||
for (String allowedPushOrigin : allowedPushOrigins)
|
||||
this.allowedPushOrigins.add(Pattern.compile(allowedPushOrigin.replace(".", "\\.").replace("*", ".*")));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> apply(Stream stream, Headers requestHeaders, Headers responseHeaders)
|
||||
{
|
||||
Set<String> result = Collections.emptySet();
|
||||
String scheme = requestHeaders.get("scheme").value();
|
||||
String host = requestHeaders.get("host").value();
|
||||
String origin = new StringBuilder(scheme).append("://").append(host).toString();
|
||||
String url = requestHeaders.get("url").value();
|
||||
if (!hasQueryString(url))
|
||||
String absoluteURL = new StringBuilder(origin).append(url).toString();
|
||||
logger.debug("Applying push strategy for {}", absoluteURL);
|
||||
if (isValidMethod(requestHeaders.get("method").value()))
|
||||
{
|
||||
if (isMainResource(url, responseHeaders))
|
||||
{
|
||||
return pushResources(url);
|
||||
result = pushResources(absoluteURL);
|
||||
}
|
||||
else if (isPushResource(url, responseHeaders))
|
||||
{
|
||||
String referrer = requestHeaders.get("referer").value();
|
||||
Set<String> pushResources = resources.get(referrer);
|
||||
if (pushResources == null || !pushResources.contains(url))
|
||||
Headers.Header referrerHeader = requestHeaders.get("referer");
|
||||
if (referrerHeader != null)
|
||||
{
|
||||
buildMetadata(url, referrer);
|
||||
}
|
||||
else
|
||||
{
|
||||
return pushResources(url);
|
||||
String referrer = referrerHeader.value();
|
||||
Set<String> pushResources = resources.get(referrer);
|
||||
if (pushResources == null || !pushResources.contains(url))
|
||||
buildMetadata(origin, url, referrer);
|
||||
else
|
||||
result = pushResources(absoluteURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.emptySet();
|
||||
logger.debug("Push resources for {}: {}", absoluteURL, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean hasQueryString(String url)
|
||||
private boolean isValidMethod(String method)
|
||||
{
|
||||
return url.contains("?");
|
||||
return "GET".equalsIgnoreCase(method);
|
||||
}
|
||||
|
||||
private boolean isMainResource(String url, Headers responseHeaders)
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
return !isPushResource(url, responseHeaders);
|
||||
}
|
||||
|
||||
private boolean isPushResource(String url, Headers responseHeaders)
|
||||
{
|
||||
// TODO
|
||||
for (Pattern pushRegexp : pushRegexps)
|
||||
{
|
||||
if (pushRegexp.matcher(url).matches())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private Set<String> pushResources(String url)
|
||||
private Set<String> pushResources(String absoluteURL)
|
||||
{
|
||||
Set<String> pushResources = resources.get(url);
|
||||
Set<String> pushResources = resources.get(absoluteURL);
|
||||
if (pushResources == null)
|
||||
return Collections.emptySet();
|
||||
return Collections.unmodifiableSet(pushResources);
|
||||
}
|
||||
|
||||
private void buildMetadata(String url, String referrer)
|
||||
private void buildMetadata(String origin, String url, String referrer)
|
||||
{
|
||||
Set<String> pushResources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
|
||||
Set<String> existing = resources.putIfAbsent(referrer, pushResources);
|
||||
if (existing != null)
|
||||
pushResources = existing;
|
||||
pushResources.add(url);
|
||||
if (referrer.startsWith(origin) || isPushOriginAllowed(origin))
|
||||
{
|
||||
Set<String> pushResources = resources.get(referrer);
|
||||
if (pushResources == null)
|
||||
{
|
||||
pushResources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
|
||||
Set<String> existing = resources.putIfAbsent(referrer, pushResources);
|
||||
if (existing != null)
|
||||
pushResources = existing;
|
||||
}
|
||||
pushResources.add(url);
|
||||
logger.debug("Built push metadata for {}: {}", referrer, pushResources);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isPushOriginAllowed(String origin)
|
||||
{
|
||||
for (Pattern allowedPushOrigin : allowedPushOrigins)
|
||||
{
|
||||
if (allowedPushOrigin.matcher(origin).matches())
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,7 +51,9 @@ import org.eclipse.jetty.spdy.api.DataInfo;
|
|||
import org.eclipse.jetty.spdy.api.Handler;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
import org.eclipse.jetty.spdy.api.RstInfo;
|
||||
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.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -255,10 +257,17 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
|
||||
private void respond(Stream stream, int status)
|
||||
{
|
||||
Headers headers = new Headers();
|
||||
headers.put("status", String.valueOf(status));
|
||||
headers.put("version", "HTTP/1.1");
|
||||
stream.reply(new ReplyInfo(headers, true));
|
||||
if (stream.isUnidirectional())
|
||||
{
|
||||
stream.getSession().rst(new RstInfo(stream.getId(), StreamStatus.INTERNAL_ERROR));
|
||||
}
|
||||
else
|
||||
{
|
||||
Headers headers = new Headers();
|
||||
headers.put("status", String.valueOf(status));
|
||||
headers.put("version", "HTTP/1.1");
|
||||
stream.reply(new ReplyInfo(headers, true));
|
||||
}
|
||||
}
|
||||
|
||||
private void close(Stream stream)
|
||||
|
@ -277,7 +286,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
state = newState;
|
||||
}
|
||||
|
||||
public void beginRequest(final Headers headers)
|
||||
public void beginRequest(final Headers headers, final boolean endRequest)
|
||||
{
|
||||
this.headers = headers.isEmpty() ? null : headers;
|
||||
post(new Runnable()
|
||||
|
@ -288,6 +297,8 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
if (!headers.isEmpty())
|
||||
updateState(State.REQUEST);
|
||||
handle();
|
||||
if (endRequest)
|
||||
performEndRequest();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -347,17 +358,22 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
{
|
||||
public void run()
|
||||
{
|
||||
if (state == State.HEADERS)
|
||||
{
|
||||
updateState(State.HEADERS_COMPLETE);
|
||||
handle();
|
||||
}
|
||||
updateState(State.FINAL);
|
||||
handle();
|
||||
performEndRequest();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void performEndRequest()
|
||||
{
|
||||
if (state == State.HEADERS)
|
||||
{
|
||||
updateState(State.HEADERS_COMPLETE);
|
||||
handle();
|
||||
}
|
||||
updateState(State.FINAL);
|
||||
handle();
|
||||
}
|
||||
|
||||
public void async()
|
||||
{
|
||||
post(new Runnable()
|
||||
|
@ -380,24 +396,30 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
if (replyInfo.getHeaders().get("status").value().startsWith("200") && !stream.isClosed())
|
||||
{
|
||||
// We have a 200 OK with some content to send
|
||||
|
||||
Headers.Header scheme = headers.get("scheme");
|
||||
Headers.Header host = headers.get("host");
|
||||
Headers.Header url = headers.get("url");
|
||||
Set<String> pushResources = pushStrategy.apply(stream, this.headers, replyInfo.getHeaders());
|
||||
for (String url : pushResources)
|
||||
String referrer = new StringBuilder(scheme.value()).append("://").append(host.value()).append(url.value()).toString();
|
||||
for (String pushURL : pushResources)
|
||||
{
|
||||
final Headers pushHeaders = new Headers();
|
||||
pushHeaders.put("method", "GET");
|
||||
pushHeaders.put("url", url);
|
||||
pushHeaders.put("url", pushURL);
|
||||
pushHeaders.put("version", "HTTP/1.1");
|
||||
Headers.Header acceptEncoding = headers.get("accept-encoding");
|
||||
if (acceptEncoding != null)
|
||||
pushHeaders.put(acceptEncoding);
|
||||
pushHeaders.put(scheme);
|
||||
pushHeaders.put(host);
|
||||
pushHeaders.put("referer", referrer);
|
||||
// Remember support for gzip encoding
|
||||
pushHeaders.put(headers.get("accept-encoding"));
|
||||
stream.syn(new SynInfo(pushHeaders, false), getMaxIdleTime(), TimeUnit.MILLISECONDS, new Handler.Adapter<Stream>()
|
||||
{
|
||||
@Override
|
||||
public void completed(Stream pushStream)
|
||||
{
|
||||
Synchronous pushConnection = new Synchronous(getConnector(), getEndPoint(), getServer(), connection, pushStrategy, pushStream);
|
||||
pushConnection.beginRequest(pushHeaders);
|
||||
pushConnection.endRequest();
|
||||
pushConnection.beginRequest(pushHeaders, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -85,7 +85,7 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
|
|||
stream.setAttribute(CONNECTION_ATTRIBUTE, connection);
|
||||
|
||||
Headers headers = synInfo.getHeaders();
|
||||
connection.beginRequest(headers);
|
||||
connection.beginRequest(headers, synInfo.isClose());
|
||||
|
||||
if (headers.isEmpty())
|
||||
{
|
||||
|
@ -95,14 +95,9 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
|
|||
else
|
||||
{
|
||||
if (synInfo.isClose())
|
||||
{
|
||||
connection.endRequest();
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,366 @@
|
|||
package org.eclipse.jetty.spdy.http;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.InetSocketAddress;
|
||||
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.server.Request;
|
||||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.spdy.AsyncConnectionFactory;
|
||||
import org.eclipse.jetty.spdy.SPDYServerConnector;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
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.SessionFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
||||
import org.eclipse.jetty.spdy.api.SynInfo;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
|
||||
{
|
||||
@Override
|
||||
protected SPDYServerConnector newHTTPSPDYServerConnector()
|
||||
{
|
||||
return new HTTPSPDYServerConnector()
|
||||
{
|
||||
private final AsyncConnectionFactory defaultAsyncConnectionFactory =
|
||||
new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, new ReferrerPushStrategy());
|
||||
|
||||
@Override
|
||||
protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
|
||||
{
|
||||
return defaultAsyncConnectionFactory;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAssociatedResourceIsPushed() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startHTTPServer(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
String url = request.getRequestURI();
|
||||
PrintWriter output = response.getWriter();
|
||||
if (url.endsWith(".html"))
|
||||
output.print("<html><head/><body>HELLO</body></html>");
|
||||
else if (url.endsWith(".css"))
|
||||
output.print("body { background: #FFF; }");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
Session session1 = startClient(address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put("method", "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put("url", mainResource);
|
||||
mainRequestHeaders.put("version", "HTTP/1.1");
|
||||
mainRequestHeaders.put("scheme", "http");
|
||||
mainRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@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 associatedResourceLatch = new CountDownLatch(1);
|
||||
Headers associatedRequestHeaders = new Headers();
|
||||
associatedRequestHeaders.put("method", "GET");
|
||||
associatedRequestHeaders.put("url", "/style.css");
|
||||
associatedRequestHeaders.put("version", "HTTP/1.1");
|
||||
associatedRequestHeaders.put("scheme", "http");
|
||||
associatedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
|
||||
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
|
||||
|
||||
final CountDownLatch mainStreamLatch = new CountDownLatch(2);
|
||||
final CountDownLatch pushDataLatch = new CountDownLatch(1);
|
||||
Session session2 = startClient(address, new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
Assert.assertTrue(stream.isUnidirectional());
|
||||
return new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
pushDataLatch.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedAssociatedResourceIsPushed() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startHTTPServer(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
String url = request.getRequestURI();
|
||||
PrintWriter output = response.getWriter();
|
||||
if (url.endsWith(".html"))
|
||||
output.print("<html><head/><body>HELLO</body></html>");
|
||||
else if (url.endsWith(".css"))
|
||||
output.print("body { background: #FFF; }");
|
||||
else if (url.endsWith(".gif"))
|
||||
output.print("\u0000");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
Session session1 = startClient(address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put("method", "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put("url", mainResource);
|
||||
mainRequestHeaders.put("version", "HTTP/1.1");
|
||||
mainRequestHeaders.put("scheme", "http");
|
||||
mainRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@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 associatedResourceLatch = new CountDownLatch(1);
|
||||
Headers associatedRequestHeaders = new Headers();
|
||||
associatedRequestHeaders.put("method", "GET");
|
||||
String associatedResource = "/style.css";
|
||||
associatedRequestHeaders.put("url", associatedResource);
|
||||
associatedRequestHeaders.put("version", "HTTP/1.1");
|
||||
associatedRequestHeaders.put("scheme", "http");
|
||||
associatedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
|
||||
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));
|
||||
|
||||
final CountDownLatch nestedResourceLatch = new CountDownLatch(1);
|
||||
Headers nestedRequestHeaders = new Headers();
|
||||
nestedRequestHeaders.put("method", "GET");
|
||||
nestedRequestHeaders.put("url", "/image.gif");
|
||||
nestedRequestHeaders.put("version", "HTTP/1.1");
|
||||
nestedRequestHeaders.put("scheme", "http");
|
||||
nestedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
nestedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + associatedResource);
|
||||
session1.syn(new SynInfo(nestedRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
nestedResourceLatch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(nestedResourceLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Create another client, and perform the same request for the main resource, we expect the css and the image being pushed
|
||||
|
||||
final CountDownLatch mainStreamLatch = new CountDownLatch(2);
|
||||
final CountDownLatch pushDataLatch = new CountDownLatch(2);
|
||||
Session session2 = startClient(address, new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
Assert.assertTrue(stream.isUnidirectional());
|
||||
return new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
pushDataLatch.countDown();
|
||||
}
|
||||
};
|
||||
}
|
||||
});
|
||||
session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMainResourceWithReferrerIsNotPushed() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startHTTPServer(new AbstractHandler()
|
||||
{
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
String url = request.getRequestURI();
|
||||
PrintWriter output = response.getWriter();
|
||||
if (url.endsWith(".html"))
|
||||
output.print("<html><head/><body>HELLO</body></html>");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
Session session1 = startClient(address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put("method", "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put("url", mainResource);
|
||||
mainRequestHeaders.put("version", "HTTP/1.1");
|
||||
mainRequestHeaders.put("scheme", "http");
|
||||
mainRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@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 associatedResourceLatch = new CountDownLatch(1);
|
||||
Headers associatedRequestHeaders = new Headers();
|
||||
associatedRequestHeaders.put("method", "GET");
|
||||
associatedRequestHeaders.put("url", "/home.html");
|
||||
associatedRequestHeaders.put("version", "HTTP/1.1");
|
||||
associatedRequestHeaders.put("scheme", "http");
|
||||
associatedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
|
||||
associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
|
||||
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 nothing being pushed
|
||||
|
||||
final CountDownLatch mainStreamLatch = new CountDownLatch(2);
|
||||
final CountDownLatch pushLatch = new CountDownLatch(1);
|
||||
Session session2 = startClient(address, new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
pushLatch.countDown();
|
||||
return null;
|
||||
}
|
||||
});
|
||||
session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onReply(Stream stream, ReplyInfo replyInfo)
|
||||
{
|
||||
Assert.assertFalse(replyInfo.isClose());
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
mainStreamLatch.countDown();
|
||||
}
|
||||
});
|
||||
|
||||
Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
|
||||
Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue