Merge branch 'master' of ssh://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project
This commit is contained in:
commit
1801c4ebe8
|
@ -54,15 +54,18 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* and any 3 letter top-level domain (.com, .net, .org, etc.).</li>
|
||||
* <li><b>allowedMethods</b>, a comma separated list of HTTP methods that
|
||||
* are allowed to be used when accessing the resources. Default value is
|
||||
* <b>GET,POST</b></li>
|
||||
* <b>GET,POST,HEAD</b></li>
|
||||
* <li><b>allowedHeaders</b>, a comma separated list of HTTP headers that
|
||||
* are allowed to be specified when accessing the resources. Default value
|
||||
* is <b>X-Requested-With</b></li>
|
||||
* is <b>X-Requested-With,Content-Type,Accept,Origin</b></li>
|
||||
* <li><b>preflightMaxAge</b>, the number of seconds that preflight requests
|
||||
* can be cached by the client. Default value is <b>1800</b> seconds, or 30
|
||||
* minutes</li>
|
||||
* <li><b>allowCredentials</b>, a boolean indicating if the resource allows
|
||||
* requests with credentials. Default value is <b>false</b></li>
|
||||
* <li><b>exposeHeaders</b>, a comma separated list of HTTP headers that
|
||||
* are allowed to be exposed on the client. Default value is the
|
||||
* <b>empty list</b></li>
|
||||
* </ul></p>
|
||||
* <p>A typical configuration could be:
|
||||
* <pre>
|
||||
|
@ -79,8 +82,6 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* ...
|
||||
* </web-app>
|
||||
* </pre></p>
|
||||
*
|
||||
* @version $Revision$ $Date$
|
||||
*/
|
||||
public class CrossOriginFilter implements Filter
|
||||
{
|
||||
|
@ -96,12 +97,14 @@ public class CrossOriginFilter implements Filter
|
|||
public static final String ACCESS_CONTROL_ALLOW_HEADERS_HEADER = "Access-Control-Allow-Headers";
|
||||
public static final String ACCESS_CONTROL_MAX_AGE_HEADER = "Access-Control-Max-Age";
|
||||
public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER = "Access-Control-Allow-Credentials";
|
||||
public static final String ACCESS_CONTROL_EXPOSE_HEADERS_HEADER = "Access-Control-Expose-Headers";
|
||||
// Implementation constants
|
||||
public static final String ALLOWED_ORIGINS_PARAM = "allowedOrigins";
|
||||
public static final String ALLOWED_METHODS_PARAM = "allowedMethods";
|
||||
public static final String ALLOWED_HEADERS_PARAM = "allowedHeaders";
|
||||
public static final String PREFLIGHT_MAX_AGE_PARAM = "preflightMaxAge";
|
||||
public static final String ALLOW_CREDENTIALS_PARAM = "allowCredentials";
|
||||
public static final String EXPOSED_HEADERS_PARAM = "exposedHeaders";
|
||||
private static final String ANY_ORIGIN = "*";
|
||||
private static final List<String> SIMPLE_HTTP_METHODS = Arrays.asList("GET", "POST", "HEAD");
|
||||
|
||||
|
@ -109,6 +112,7 @@ public class CrossOriginFilter implements Filter
|
|||
private List<String> allowedOrigins = new ArrayList<String>();
|
||||
private List<String> allowedMethods = new ArrayList<String>();
|
||||
private List<String> allowedHeaders = new ArrayList<String>();
|
||||
private List<String> exposedHeaders = new ArrayList<String>();
|
||||
private int preflightMaxAge = 0;
|
||||
private boolean allowCredentials;
|
||||
|
||||
|
@ -163,6 +167,11 @@ public class CrossOriginFilter implements Filter
|
|||
allowedCredentialsConfig = "true";
|
||||
allowCredentials = Boolean.parseBoolean(allowedCredentialsConfig);
|
||||
|
||||
String exposedHeadersConfig = config.getInitParameter(EXPOSED_HEADERS_PARAM);
|
||||
if (exposedHeadersConfig == null)
|
||||
exposedHeadersConfig = "";
|
||||
exposedHeaders.addAll(Arrays.asList(exposedHeadersConfig.split(",")));
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Cross-origin filter configuration: " +
|
||||
|
@ -170,7 +179,9 @@ public class CrossOriginFilter implements Filter
|
|||
ALLOWED_METHODS_PARAM + " = " + allowedMethodsConfig + ", " +
|
||||
ALLOWED_HEADERS_PARAM + " = " + allowedHeadersConfig + ", " +
|
||||
PREFLIGHT_MAX_AGE_PARAM + " = " + preflightMaxAgeConfig + ", " +
|
||||
ALLOW_CREDENTIALS_PARAM + " = " + allowedCredentialsConfig);
|
||||
ALLOW_CREDENTIALS_PARAM + " = " + allowedCredentialsConfig + "," +
|
||||
EXPOSED_HEADERS_PARAM + " = " + exposedHeadersConfig
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -305,6 +316,8 @@ public class CrossOriginFilter implements Filter
|
|||
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, origin);
|
||||
if (allowCredentials)
|
||||
response.setHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");
|
||||
if (!exposedHeaders.isEmpty())
|
||||
response.setHeader(ACCESS_CONTROL_EXPOSE_HEADERS_HEADER, commify(exposedHeaders));
|
||||
}
|
||||
|
||||
private void handlePreflightResponse(HttpServletRequest request, HttpServletResponse response, String origin)
|
||||
|
|
|
@ -371,6 +371,27 @@ public class CrossOriginFilterTest
|
|||
Assert.assertTrue(latch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleRequestWithExposedHeaders() throws Exception
|
||||
{
|
||||
FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
|
||||
filterHolder.setInitParameter("exposedHeaders", "Content-Length");
|
||||
tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
|
||||
|
||||
CountDownLatch latch = new CountDownLatch(1);
|
||||
tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
|
||||
|
||||
String request = "" +
|
||||
"GET / HTTP/1.1\r\n" +
|
||||
"Host: localhost\r\n" +
|
||||
"Origin: http://localhost\r\n" +
|
||||
"\r\n";
|
||||
String response = tester.getResponses(request);
|
||||
Assert.assertTrue(response.contains("HTTP/1.1 200"));
|
||||
Assert.assertTrue(response.contains(CrossOriginFilter.ACCESS_CONTROL_EXPOSE_HEADERS_HEADER));
|
||||
Assert.assertTrue(latch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
public static class ResourceServlet extends HttpServlet
|
||||
{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
|
|
@ -18,7 +18,7 @@ package org.eclipse.jetty.spdy.http;
|
|||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
@ -39,58 +39,80 @@ import org.eclipse.jetty.util.log.Logger;
|
|||
* <p>However, also following a hyperlink generates a HTTP request with a <tt>Referer</tt>
|
||||
* HTTP header that points to <tt>index.html</tt>; therefore main resources and associated
|
||||
* resources must be distinguishable.</p>
|
||||
* <p>This class distinguishes associated resources by their URL path suffix.
|
||||
* <p>This class distinguishes associated resources by their URL path suffix and content
|
||||
* type.
|
||||
* CSS stylesheets, images and JavaScript files have recognizable URL path suffixes that
|
||||
* are classified as associated resources.</p>
|
||||
* <p>Note however, that CSS stylesheets may refer to images, and the CSS image request
|
||||
* 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
|
||||
* <p>When CSS stylesheets refer to images, the CSS image request will have the CSS
|
||||
* stylesheet as referrer. This implementation will push also the CSS image.</p>
|
||||
* <p>The push metadata built by this implementation is limited by the number of pages
|
||||
* of the application itself, and by the
|
||||
* {@link #getMaxAssociatedResources() max associated resources} parameter.
|
||||
* This parameter limits the number of associated resources per each main resource, so
|
||||
* that if a main resource has hundreds of associated resources, only up to the number
|
||||
* specified by this parameter will be pushed.</p>
|
||||
*/
|
||||
public class ReferrerPushStrategy implements PushStrategy
|
||||
{
|
||||
private static final Logger logger = Log.getLogger(ReferrerPushStrategy.class);
|
||||
private final ConcurrentMap<String, Set<String>> resources = new ConcurrentHashMap<>();
|
||||
private final Set<Pattern> pushRegexps = new LinkedHashSet<>();
|
||||
private final Set<Pattern> allowedPushOrigins = new LinkedHashSet<>();
|
||||
private final Set<Pattern> pushRegexps = new HashSet<>();
|
||||
private final Set<String> pushContentTypes = new HashSet<>();
|
||||
private final Set<Pattern> allowedPushOrigins = new HashSet<>();
|
||||
private volatile int maxAssociatedResources = 32;
|
||||
|
||||
public ReferrerPushStrategy()
|
||||
{
|
||||
this(Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpg", ".*\\.gif"));
|
||||
this(Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpeg", ".*\\.jpg", ".*\\.gif", ".*\\.ico"));
|
||||
}
|
||||
|
||||
public ReferrerPushStrategy(List<String> pushRegexps)
|
||||
{
|
||||
this(pushRegexps, Collections.<String>emptyList());
|
||||
this(pushRegexps, Arrays.asList(
|
||||
"text/css",
|
||||
"text/javascript", "application/javascript", "application/x-javascript",
|
||||
"image/png", "image/x-png",
|
||||
"image/jpeg",
|
||||
"image/gif",
|
||||
"image/x-icon", "image/vnd.microsoft.icon"));
|
||||
}
|
||||
|
||||
public ReferrerPushStrategy(List<String> pushRegexps, List<String> allowedPushOrigins)
|
||||
public ReferrerPushStrategy(List<String> pushRegexps, List<String> pushContentTypes)
|
||||
{
|
||||
this(pushRegexps, pushContentTypes, Collections.<String>emptyList());
|
||||
}
|
||||
|
||||
public ReferrerPushStrategy(List<String> pushRegexps, List<String> pushContentTypes, List<String> allowedPushOrigins)
|
||||
{
|
||||
for (String pushRegexp : pushRegexps)
|
||||
this.pushRegexps.add(Pattern.compile(pushRegexp));
|
||||
this.pushContentTypes.addAll(pushContentTypes);
|
||||
for (String allowedPushOrigin : allowedPushOrigins)
|
||||
this.allowedPushOrigins.add(Pattern.compile(allowedPushOrigin.replace(".", "\\.").replace("*", ".*")));
|
||||
}
|
||||
|
||||
public int getMaxAssociatedResources()
|
||||
{
|
||||
return maxAssociatedResources;
|
||||
}
|
||||
|
||||
public void setMaxAssociatedResources(int maxAssociatedResources)
|
||||
{
|
||||
this.maxAssociatedResources = maxAssociatedResources;
|
||||
}
|
||||
|
||||
@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();
|
||||
short version = stream.getSession().getVersion();
|
||||
String scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version)).value();
|
||||
String host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version)).value();
|
||||
String origin = new StringBuilder(scheme).append("://").append(host).toString();
|
||||
String url = requestHeaders.get("url").value();
|
||||
String url = requestHeaders.get(HTTPSPDYHeader.URI.name(version)).value();
|
||||
String absoluteURL = new StringBuilder(origin).append(url).toString();
|
||||
logger.debug("Applying push strategy for {}", absoluteURL);
|
||||
if (isValidMethod(requestHeaders.get("method").value()))
|
||||
if (isValidMethod(requestHeaders.get(HTTPSPDYHeader.METHOD.name(version)).value()))
|
||||
{
|
||||
if (isMainResource(url, responseHeaders))
|
||||
{
|
||||
|
@ -129,7 +151,16 @@ public class ReferrerPushStrategy implements PushStrategy
|
|||
for (Pattern pushRegexp : pushRegexps)
|
||||
{
|
||||
if (pushRegexp.matcher(url).matches())
|
||||
{
|
||||
Headers.Header header = responseHeaders.get("content-type");
|
||||
if (header == null)
|
||||
return true;
|
||||
|
||||
String contentType = header.value().toLowerCase();
|
||||
for (String pushContentType : pushContentTypes)
|
||||
if (contentType.startsWith(pushContentType))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -154,8 +185,19 @@ public class ReferrerPushStrategy implements PushStrategy
|
|||
if (existing != null)
|
||||
pushResources = existing;
|
||||
}
|
||||
// This check is not strictly concurrent-safe, but limiting
|
||||
// the number of associated resources is achieved anyway
|
||||
// although in rare cases few more resources will be stored
|
||||
if (pushResources.size() < getMaxAssociatedResources())
|
||||
{
|
||||
pushResources.add(url);
|
||||
logger.debug("Built push metadata for {}: {}", referrer, pushResources);
|
||||
logger.debug("Stored push metadata for {}: {}", referrer, pushResources);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.debug("Skipped store of push metadata {} for {}: max associated resources ({}) reached",
|
||||
url, referrer, maxAssociatedResources);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -113,4 +113,9 @@ public abstract class AbstractHTTPSPDYTest
|
|||
server.join();
|
||||
}
|
||||
}
|
||||
|
||||
protected short version()
|
||||
{
|
||||
return SPDY.V2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,10 +73,10 @@ public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
|
|||
|
||||
// Perform slow request. This will wait on server side until the fast request wakes it up
|
||||
Headers headers = new Headers();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/slow");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
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);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
@ -91,10 +91,10 @@ public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
|
|||
|
||||
// Perform the fast request. This will wake up the slow request
|
||||
headers.clear();
|
||||
headers.put("method", "GET");
|
||||
headers.put("url", "/fast");
|
||||
headers.put("version", "HTTP/1.1");
|
||||
headers.put("host", "localhost:" + connector.getLocalPort());
|
||||
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);
|
||||
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
|
|
|
@ -38,7 +38,6 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
|
|||
import org.eclipse.jetty.spdy.AsyncConnectionFactory;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
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;
|
||||
|
@ -62,11 +61,6 @@ public class PushStrategyBenchmarkTest extends AbstractHTTPSPDYTest
|
|||
private final long roundtrip = 100;
|
||||
private final int runs = 10;
|
||||
|
||||
protected short version()
|
||||
{
|
||||
return SPDY.V2;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void benchmarkPushStrategy() throws Exception
|
||||
{
|
||||
|
|
|
@ -1,463 +0,0 @@
|
|||
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.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(short version)
|
||||
{
|
||||
SPDYServerConnector connector = super.newHTTPSPDYServerConnector(version);
|
||||
AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, new ReferrerPushStrategy());
|
||||
connector.setDefaultAsyncConnectionFactory(defaultFactory);
|
||||
return connector;
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestWithIfModifiedSinceHeaderPreventsPush() 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());
|
||||
mainRequestHeaders.put("If-Modified-Since", "Tue, 27 Mar 2012 16:36:52 GMT");
|
||||
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 NOT being pushed as the main request contains an
|
||||
// if-modified-since header
|
||||
|
||||
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.assertFalse("We don't expect data to be pushed as the main request contained an if-modified-since header",pushDataLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,748 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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.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 ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest
|
||||
{
|
||||
@Override
|
||||
protected SPDYServerConnector newHTTPSPDYServerConnector(short version)
|
||||
{
|
||||
SPDYServerConnector connector = super.newHTTPSPDYServerConnector(version);
|
||||
AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, new ReferrerPushStrategy());
|
||||
connector.setDefaultAsyncConnectionFactory(defaultFactory);
|
||||
return connector;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxAssociatedResources() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startHTTPServer(version(), 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(".js"))
|
||||
output.print("function(){}();");
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy();
|
||||
pushStrategy.setMaxAssociatedResources(1);
|
||||
AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version(), connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy);
|
||||
connector.setDefaultAsyncConnectionFactory(defaultFactory);
|
||||
|
||||
Session session1 = startClient(version(), address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "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 associatedResourceLatch1 = new CountDownLatch(1);
|
||||
Headers associatedRequestHeaders1 = new Headers();
|
||||
associatedRequestHeaders1.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
associatedRequestHeaders1.put(HTTPSPDYHeader.URI.name(version()), "/style.css");
|
||||
associatedRequestHeaders1.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
associatedRequestHeaders1.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
associatedRequestHeaders1.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
associatedRequestHeaders1.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
|
||||
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));
|
||||
|
||||
final CountDownLatch associatedResourceLatch2 = new CountDownLatch(1);
|
||||
Headers associatedRequestHeaders2 = new Headers();
|
||||
associatedRequestHeaders2.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
associatedRequestHeaders2.put(HTTPSPDYHeader.URI.name(version()), "/application.js");
|
||||
associatedRequestHeaders2.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
associatedRequestHeaders2.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
associatedRequestHeaders2.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
associatedRequestHeaders2.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
|
||||
session1.syn(new SynInfo(associatedRequestHeaders2, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
associatedResourceLatch2.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(associatedResourceLatch2.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// 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 pushDataLatch = new CountDownLatch(1);
|
||||
Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
Assert.assertTrue(stream.isUnidirectional());
|
||||
Assert.assertTrue(synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version())).value().endsWith(".css"));
|
||||
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 testAssociatedResourceIsPushed() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startHTTPServer(version(), 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(version(), address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "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(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/style.css");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "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(version(), 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 testAssociatedResourceWithWrongContentTypeIsNotPushed() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startHTTPServer(version(), 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"))
|
||||
{
|
||||
response.setContentType("text/html");
|
||||
output.print("<html><head/><body>HELLO</body></html>");
|
||||
}
|
||||
else if (url.equals("/fake.png"))
|
||||
{
|
||||
response.setContentType("text/html");
|
||||
output.print("<html><head/><body>IMAGE</body></html>");
|
||||
}
|
||||
else if (url.endsWith(".css"))
|
||||
{
|
||||
response.setContentType("text/css");
|
||||
output.print("body { background: #FFF; }");
|
||||
}
|
||||
baseRequest.setHandled(true);
|
||||
}
|
||||
});
|
||||
Session session1 = startClient(version(), address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "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(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/stylesheet.css");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "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 fakeAssociatedResourceLatch = new CountDownLatch(1);
|
||||
Headers fakeAssociatedRequestHeaders = new Headers();
|
||||
fakeAssociatedRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
fakeAssociatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/fake.png");
|
||||
fakeAssociatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
fakeAssociatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
fakeAssociatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
fakeAssociatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
|
||||
session1.syn(new SynInfo(fakeAssociatedRequestHeaders, true), new StreamFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public void onData(Stream stream, DataInfo dataInfo)
|
||||
{
|
||||
dataInfo.consume(dataInfo.length());
|
||||
if (dataInfo.isClose())
|
||||
fakeAssociatedResourceLatch.countDown();
|
||||
}
|
||||
});
|
||||
Assert.assertTrue(fakeAssociatedResourceLatch.await(5, TimeUnit.SECONDS));
|
||||
|
||||
// Create another client, and perform the same request for the main resource,
|
||||
// we expect the css being pushed but not the fake PNG
|
||||
|
||||
final CountDownLatch mainStreamLatch = new CountDownLatch(2);
|
||||
final CountDownLatch pushDataLatch = new CountDownLatch(1);
|
||||
Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
|
||||
{
|
||||
@Override
|
||||
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
|
||||
{
|
||||
Assert.assertTrue(stream.isUnidirectional());
|
||||
Assert.assertTrue(synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version())).value().endsWith(".css"));
|
||||
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(version(), 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(version(), address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "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(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
String associatedResource = "/style.css";
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), associatedResource);
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "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(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
nestedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/image.gif");
|
||||
nestedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
nestedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
nestedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "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(version(), 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(version(), 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(version(), address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "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(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/home.html");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "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(version(), 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));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRequestWithIfModifiedSinceHeaderPreventsPush() throws Exception
|
||||
{
|
||||
InetSocketAddress address = startHTTPServer(version(), 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(version(), address, null);
|
||||
|
||||
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
|
||||
Headers mainRequestHeaders = new Headers();
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
String mainResource = "/index.html";
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
|
||||
mainRequestHeaders.put("If-Modified-Since", "Tue, 27 Mar 2012 16:36:52 GMT");
|
||||
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(HTTPSPDYHeader.METHOD.name(version()), "GET");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/style.css");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
|
||||
associatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "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 NOT being pushed as the main request contains an
|
||||
// if-modified-since header
|
||||
|
||||
final CountDownLatch mainStreamLatch = new CountDownLatch(2);
|
||||
final CountDownLatch pushDataLatch = new CountDownLatch(1);
|
||||
Session session2 = startClient(version(), 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.assertFalse("We don't expect data to be pushed as the main request contained an if-modified-since header",pushDataLatch.await(1, TimeUnit.SECONDS));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2012 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.eclipse.jetty.spdy.http;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.SPDY;
|
||||
|
||||
public class ReferrerPushStrategyV3Test extends ReferrerPushStrategyV2Test
|
||||
{
|
||||
@Override
|
||||
protected short version()
|
||||
{
|
||||
return SPDY.V3;
|
||||
}
|
||||
}
|
|
@ -41,7 +41,6 @@ import org.eclipse.jetty.spdy.api.BytesDataInfo;
|
|||
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.Stream;
|
||||
import org.eclipse.jetty.spdy.api.StreamFrameListener;
|
||||
|
@ -52,11 +51,6 @@ import org.junit.Test;
|
|||
|
||||
public class ServerHTTPSPDYv2Test extends AbstractHTTPSPDYTest
|
||||
{
|
||||
protected short version()
|
||||
{
|
||||
return SPDY.V2;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleGET() throws Exception
|
||||
{
|
||||
|
|
|
@ -239,23 +239,26 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
String servlet_class = node.getString("servlet-class", false, true);
|
||||
|
||||
// Handle JSP
|
||||
String jspServletName=null;
|
||||
String jspServletClass=null;;
|
||||
boolean hasJSP=false;
|
||||
|
||||
//Handle the default jsp servlet instance
|
||||
if (id != null && id.equals("jsp"))
|
||||
{
|
||||
jspServletName = servlet_name;
|
||||
jspServletClass = servlet_class;
|
||||
try
|
||||
{
|
||||
Loader.loadClass(this.getClass(), servlet_class);
|
||||
hasJSP = true;
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
LOG.info("NO JSP Support for {}, did not find {}", context.getContextPath(), servlet_class);
|
||||
jspServletClass = servlet_class = "org.eclipse.jetty.servlet.NoJspServlet";
|
||||
}
|
||||
}
|
||||
|
||||
//could be more than 1 declaration of the jsp servlet, so configure them all
|
||||
if (servlet_class != null && "org.apache.jasper.servlet.JspServlet".equals(servlet_class))
|
||||
{
|
||||
if (holder.getInitParameter("scratchdir") == null)
|
||||
{
|
||||
File tmp = context.getTempDirectory();
|
||||
|
@ -316,12 +319,12 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
|
|||
}
|
||||
}
|
||||
|
||||
// Handler JSP file
|
||||
// Handle JSP file
|
||||
String jsp_file = node.getString("jsp-file", false, true);
|
||||
if (jsp_file != null)
|
||||
{
|
||||
holder.setForcedPath(jsp_file);
|
||||
holder.setClassName(jspServletClass);
|
||||
holder.setClassName(jspServletClass); //only use our default instance
|
||||
//set the system classpath explicitly for the holder that will represent the JspServlet instance
|
||||
holder.setInitParameter("com.sun.appserv.jsp.classpath", getSystemClassPath(context));
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue