diff options
author | Thomas Becker | 2012-07-04 17:28:08 +0000 |
---|---|---|
committer | Thomas Becker | 2012-07-04 17:28:08 +0000 |
commit | 1ff0b18b94d422ac7524e03e66999019119039a6 (patch) | |
tree | 33b792304c67819190b66e13d74818da9bac2f91 | |
parent | 5e619ce525994ee6d8227a45fefdff32974533ce (diff) | |
download | org.eclipse.jetty.project-1ff0b18b94d422ac7524e03e66999019119039a6.tar.gz org.eclipse.jetty.project-1ff0b18b94d422ac7524e03e66999019119039a6.tar.xz org.eclipse.jetty.project-1ff0b18b94d422ac7524e03e66999019119039a6.zip |
spdy: ReferrerPushStrategy configurable by SPDY version and add referrer push period
6 files changed, 494 insertions, 211 deletions
diff --git a/jetty-spdy/spdy-jetty-http/pom.xml b/jetty-spdy/spdy-jetty-http/pom.xml index 3a2c730eb6..d39ad23278 100644 --- a/jetty-spdy/spdy-jetty-http/pom.xml +++ b/jetty-spdy/spdy-jetty-http/pom.xml @@ -72,6 +72,11 @@ <version>${slf4j-version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <scope>test</scope> + </dependency> </dependencies> </project> diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategy.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategy.java index 57f3e3206f..fae2c749df 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategy.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategy.java @@ -23,9 +23,11 @@ import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import org.eclipse.jetty.spdy.api.Headers; +import org.eclipse.jetty.spdy.api.SPDY; import org.eclipse.jetty.spdy.api.Stream; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -37,12 +39,13 @@ import org.eclipse.jetty.util.log.Logger; * will have a <tt>Referer</tt> HTTP header that points to <tt>index.html</tt>, which we * use to link the associated resource to the main resource.</p> * <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 and content + * HTTP header that points to <tt>index.html</tt>; therefore a proper value for {@link #getReferrerPushPeriod()} + * has to be set. If the referrerPushPeriod for a main resource has been passed, no more + * associated resources will be added for that main resource.</p> + * <p>This class distinguishes associated main 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> + * are classified as associated resources. The suffix regexs can be configured by constructor argument</p> * <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 @@ -55,11 +58,13 @@ import org.eclipse.jetty.util.log.Logger; 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 ConcurrentMap<String, MainResource> mainResources = new ConcurrentHashMap<>(); private final Set<Pattern> pushRegexps = new HashSet<>(); private final Set<String> pushContentTypes = new HashSet<>(); private final Set<Pattern> allowedPushOrigins = new HashSet<>(); + private final HashSet<Short> supportedSPDYVersions = new HashSet<Short>(); private volatile int maxAssociatedResources = 32; + private volatile int referrerPushPeriod = 5000; public ReferrerPushStrategy() { @@ -89,6 +94,9 @@ public class ReferrerPushStrategy implements PushStrategy this.pushContentTypes.addAll(pushContentTypes); for (String allowedPushOrigin : allowedPushOrigins) this.allowedPushOrigins.add(Pattern.compile(allowedPushOrigin.replace(".", "\\.").replace("*", ".*"))); + // by default we support v2 and v3 + supportedSPDYVersions.add(SPDY.V2); + supportedSPDYVersions.add(SPDY.V3); } public int getMaxAssociatedResources() @@ -101,22 +109,51 @@ public class ReferrerPushStrategy implements PushStrategy this.maxAssociatedResources = maxAssociatedResources; } + public int getReferrerPushPeriod() + { + return referrerPushPeriod; + } + + public void setReferrerPushPeriod(int referrerPushPeriod) + { + this.referrerPushPeriod = referrerPushPeriod; + } + + public void removeSPDYVersionSupport(Short version) + { + supportedSPDYVersions.remove(version); + } + + public void addSPDYVersionSupport(Short version) + { + // consider to make SPDY.Vx an enum when we add support for more than two drafts + if (version == SPDY.V2 || version == SPDY.V3) + supportedSPDYVersions.add(version); + } + @Override public Set<String> apply(Stream stream, Headers requestHeaders, Headers responseHeaders) { Set<String> result = Collections.<String>emptySet(); short version = stream.getSession().getVersion(); + if (!supportedSPDYVersions.contains(version)) + { + logger.debug("SPDY version {} not supported. Returning empty Set.", version); + return result; + } + if (!isIfModifiedSinceHeaderPresent(requestHeaders) && isValidMethod(requestHeaders.get(HTTPSPDYHeader.METHOD.name(version)).value())) { 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 origin = scheme + "://" + host; String url = requestHeaders.get(HTTPSPDYHeader.URI.name(version)).value(); - String absoluteURL = new StringBuilder(origin).append(url).toString(); + String absoluteURL = origin + url; logger.debug("Applying push strategy for {}", absoluteURL); if (isMainResource(url, responseHeaders)) { - result = pushResources(absoluteURL); + MainResource mainResource = getOrCreateMainResource(absoluteURL); + result = mainResource.getResources(); } else if (isPushResource(url, responseHeaders)) { @@ -124,18 +161,44 @@ public class ReferrerPushStrategy implements PushStrategy if (referrerHeader != null) { String referrer = referrerHeader.value(); - Set<String> pushResources = resources.get(referrer); - if (pushResources == null || !pushResources.contains(url)) - buildMetadata(origin, url, referrer); + MainResource mainResource = mainResources.get(referrer); + if (mainResource == null) + mainResource = getOrCreateMainResource(referrer); + + Set<String> pushResources = mainResource.getResources(); + if (!pushResources.contains(url)) + mainResource.addResource(url, origin, referrer); else - result = pushResources(absoluteURL); + result = getPushResources(absoluteURL); } } - logger.debug("Push resources for {}: {}", absoluteURL, result); + logger.debug("Pushing {} resources for {}: {}", result.size(), absoluteURL, result); } return result; } + private Set<String> getPushResources(String absoluteURL) + { + Set<String> result = Collections.emptySet(); + if (mainResources.get(absoluteURL) != null) + result = mainResources.get(absoluteURL).getResources(); + return result; + } + + private MainResource getOrCreateMainResource(String absoluteURL) + { + MainResource mainResource = mainResources.get(absoluteURL); + if (mainResource == null) + { + logger.debug("Creating new main resource for {}", absoluteURL); + MainResource value = new MainResource(absoluteURL); + mainResource = mainResources.putIfAbsent(absoluteURL, value); + if (mainResource == null) + mainResource = value; + } + return mainResource; + } + private boolean isIfModifiedSinceHeaderPresent(Headers headers) { return headers.get("if-modified-since") != null; @@ -170,49 +233,65 @@ public class ReferrerPushStrategy implements PushStrategy return false; } - private Set<String> pushResources(String absoluteURL) + private class MainResource { - Set<String> pushResources = resources.get(absoluteURL); - if (pushResources == null) - return Collections.emptySet(); - return Collections.unmodifiableSet(pushResources); - } + private final String name; + private final long created = System.nanoTime(); + private final Set<String> resources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); - private void buildMetadata(String origin, String url, String referrer) - { - if (referrer.startsWith(origin) || isPushOriginAllowed(origin)) + MainResource(String name) + { + this.name = name; + } + + public boolean addResource(String url, String origin, String referrer) { - Set<String> pushResources = resources.get(referrer); - if (pushResources == null) + long delay = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - created); + if (!referrer.startsWith(origin) && !isPushOriginAllowed(origin)) { - pushResources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); - Set<String> existing = resources.putIfAbsent(referrer, pushResources); - if (existing != null) - pushResources = existing; + logger.debug("Skipped store of push metadata {} for {}: Origin: {} doesn't match or origin not allowed", + url, name, origin); + return false; } + // 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()) + if (resources.size() >= maxAssociatedResources) { - pushResources.add(url); - logger.debug("Stored push metadata for {}: {}", referrer, pushResources); + logger.debug("Skipped store of push metadata {} for {}: max associated resources ({}) reached", + url, name, maxAssociatedResources); + return false; } - else + if (delay > referrerPushPeriod) { - logger.debug("Skipped store of push metadata {} for {}: max associated resources ({}) reached", - url, referrer, maxAssociatedResources); + logger.debug("Delay: {}ms longer than referrerPushPeriod: {}ms. Not adding resource: {} for: {}", delay, referrerPushPeriod, url, name); + return false; } + + logger.debug("Adding resource: {} for: {} with delay: {}ms.", url, name, delay); + resources.add(url); + return true; } - } - private boolean isPushOriginAllowed(String origin) - { - for (Pattern allowedPushOrigin : allowedPushOrigins) + public Set<String> getResources() { - if (allowedPushOrigin.matcher(origin).matches()) - return true; + return Collections.unmodifiableSet(resources); + } + + public String toString() + { + return "MainResource: " + name + " associated resources:" + resources.size(); + } + + private boolean isPushOriginAllowed(String origin) + { + for (Pattern allowedPushOrigin : allowedPushOrigins) + { + if (allowedPushOrigin.matcher(origin).matches()) + return true; + } + return false; } - return false; } } diff --git a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java index b4e278f5f3..92b83d1269 100644 --- a/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java +++ b/jetty-spdy/spdy-jetty-http/src/main/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYAsyncConnection.java @@ -55,6 +55,7 @@ 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.SPDY; import org.eclipse.jetty.spdy.api.Stream; import org.eclipse.jetty.spdy.api.StreamStatus; import org.eclipse.jetty.spdy.api.SynInfo; @@ -411,19 +412,12 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem Headers.Header host = headers.get(HTTPSPDYHeader.HOST.name(version)); Headers.Header uri = headers.get(HTTPSPDYHeader.URI.name(version)); Set<String> pushResources = pushStrategy.apply(stream, headers, replyInfo.getHeaders()); - String referrer = new StringBuilder(scheme.value()).append("://").append(host.value()).append(uri.value()).toString(); - for (String pushURL : pushResources) + + for (String pushResourcePath : pushResources) { - final Headers pushHeaders = new Headers(); - pushHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET"); - pushHeaders.put(HTTPSPDYHeader.URI.name(version), pushURL); - pushHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1"); - pushHeaders.put(scheme); - pushHeaders.put(host); - pushHeaders.put("referer", referrer); - pushHeaders.put("x-spdy-push", "true"); - // Remember support for gzip encoding - pushHeaders.put(headers.get("accept-encoding")); + final Headers requestHeaders = createRequestHeaders(scheme, host, uri, pushResourcePath); + final Headers pushHeaders = createPushHeaders(scheme, host, pushResourcePath); + stream.syn(new SynInfo(pushHeaders, false), getMaxIdleTime(), TimeUnit.MILLISECONDS, new Handler.Adapter<Stream>() { @Override @@ -431,13 +425,50 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem { ServerHTTPSPDYAsyncConnection pushConnection = new ServerHTTPSPDYAsyncConnection(getConnector(), getEndPoint(), getServer(), version, connection, pushStrategy, pushStream); - pushConnection.beginRequest(pushHeaders, true); + pushConnection.beginRequest(requestHeaders, true); } }); } } } + private Headers createRequestHeaders(Headers.Header scheme, Headers.Header host, Headers.Header uri, String pushResourcePath) + { + final Headers requestHeaders = new Headers(); + requestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET"); + requestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1"); + requestHeaders.put(scheme); + requestHeaders.put(host); + requestHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath); + String referrer = scheme.value() + "://" + host.value() + uri.value(); + requestHeaders.put("referer", referrer); + // Remember support for gzip encoding + requestHeaders.put(headers.get("accept-encoding")); + return requestHeaders; + } + + private Headers createPushHeaders(Headers.Header scheme, Headers.Header host, String pushResourcePath) + { + final Headers pushHeaders = new Headers(); + if (version == SPDY.V2) + pushHeaders.put(HTTPSPDYHeader.URI.name(version), scheme.value() + "://" + host.value() + pushResourcePath); + else + { + pushHeaders.put(HTTPSPDYHeader.URI.name(version), pushResourcePath); + pushHeaders.put(scheme); + pushHeaders.put(host); + } + pushHeaders.put(HTTPSPDYHeader.STATUS.name(version), "200"); + pushHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1"); + pushHeaders.put("x-spdy-push", "true"); + return pushHeaders; + } + + private boolean isIfModifiedSinceHeaderPresent() + { + return headers.get("if-modified-since") != null; + } + private Buffer consumeContent(long maxIdleTime) throws IOException, InterruptedException { while (true) diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/PushStrategyBenchmarkTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/PushStrategyBenchmarkTest.java index 29b5952d4e..4ca4a65e6b 100644 --- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/PushStrategyBenchmarkTest.java +++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/PushStrategyBenchmarkTest.java @@ -137,39 +137,21 @@ public class PushStrategyBenchmarkTest extends AbstractHTTPSPDYTest for (int i = 0; i < cssResources.length; ++i) { String path = "/" + i + ".css"; - exchange = new TestExchange(); - exchange.setMethod("GET"); - exchange.setRequestURI(path); - exchange.setVersion("HTTP/1.1"); - exchange.setAddress(new Address("localhost", connector.getLocalPort())); - exchange.setRequestHeader("Host", "localhost:" + connector.getLocalPort()); - exchange.setRequestHeader("referer", referrer); + exchange = createExchangeWithReferrer(referrer, path); ++result; httpClient.send(exchange); } for (int i = 0; i < jsResources.length; ++i) { String path = "/" + i + ".js"; - exchange = new TestExchange(); - exchange.setMethod("GET"); - exchange.setRequestURI(path); - exchange.setVersion("HTTP/1.1"); - exchange.setAddress(new Address("localhost", connector.getLocalPort())); - exchange.setRequestHeader("Host", "localhost:" + connector.getLocalPort()); - exchange.setRequestHeader("referer", referrer); + exchange = createExchangeWithReferrer(referrer, path); ++result; httpClient.send(exchange); } for (int i = 0; i < pngResources.length; ++i) { String path = "/" + i + ".png"; - exchange = new TestExchange(); - exchange.setMethod("GET"); - exchange.setRequestURI(path); - exchange.setVersion("HTTP/1.1"); - exchange.setAddress(new Address("localhost", connector.getLocalPort())); - exchange.setRequestHeader("Host", "localhost:" + connector.getLocalPort()); - exchange.setRequestHeader("referer", referrer); + exchange = createExchangeWithReferrer(referrer, path); ++result; httpClient.send(exchange); } @@ -180,6 +162,19 @@ public class PushStrategyBenchmarkTest extends AbstractHTTPSPDYTest return result; } + private ContentExchange createExchangeWithReferrer(String referrer, String path) + { + ContentExchange exchange; + exchange = new TestExchange(); + exchange.setMethod("GET"); + exchange.setRequestURI(path); + exchange.setVersion("HTTP/1.1"); + exchange.setAddress(new Address("localhost", connector.getLocalPort())); + exchange.setRequestHeader("Host", "localhost:" + connector.getLocalPort()); + exchange.setRequestHeader("referer", referrer); + return exchange; + } + private void benchmarkSPDY(PushStrategy pushStrategy, Session session) throws Exception { @@ -238,13 +233,7 @@ public class PushStrategyBenchmarkTest extends AbstractHTTPSPDYTest String path = "/" + i + ".css"; if (pushedResources.contains(path)) continue; - headers = new Headers(); - headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET"); - headers.put(HTTPSPDYHeader.URI.name(version()), path); - headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1"); - headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http"); - headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort()); - headers.put("referer", referrer); + headers = createRequestHeaders(referrer, path); ++result; session.syn(new SynInfo(headers, true), new DataListener()); } @@ -253,13 +242,7 @@ public class PushStrategyBenchmarkTest extends AbstractHTTPSPDYTest String path = "/" + i + ".js"; if (pushedResources.contains(path)) continue; - headers = new Headers(); - headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET"); - headers.put(HTTPSPDYHeader.URI.name(version()), path); - headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1"); - headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http"); - headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort()); - headers.put("referer", referrer); + headers = createRequestHeaders(referrer, path); ++result; session.syn(new SynInfo(headers, true), new DataListener()); } @@ -268,13 +251,7 @@ public class PushStrategyBenchmarkTest extends AbstractHTTPSPDYTest String path = "/" + i + ".png"; if (pushedResources.contains(path)) continue; - headers = new Headers(); - headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET"); - headers.put(HTTPSPDYHeader.URI.name(version()), path); - headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1"); - headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http"); - headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort()); - headers.put("referer", referrer); + headers = createRequestHeaders(referrer, path); ++result; session.syn(new SynInfo(headers, true), new DataListener()); } @@ -285,6 +262,19 @@ public class PushStrategyBenchmarkTest extends AbstractHTTPSPDYTest return result; } + private Headers createRequestHeaders(String referrer, String path) + { + Headers headers; + headers = new Headers(); + headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET"); + headers.put(HTTPSPDYHeader.URI.name(version()), path); + headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1"); + headers.put(HTTPSPDYHeader.SCHEME.name(version()), "http"); + headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort()); + headers.put("referer", referrer); + return headers; + } + private void sleep(long delay) throws ServletException { try diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyUnitTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyUnitTest.java new file mode 100644 index 0000000000..847361c085 --- /dev/null +++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyUnitTest.java @@ -0,0 +1,126 @@ +package org.eclipse.jetty.spdy.http; + +import java.util.Set; + +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.Stream; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; + +import static org.hamcrest.CoreMatchers.is; +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.when; + +@RunWith(MockitoJUnitRunner.class) +public class ReferrerPushStrategyUnitTest +{ + public static final short VERSION = SPDY.V3; + public static final String SCHEME = "http"; + public static final String HOST = "localhost"; + public static final String MAIN_URI = "/index.html"; + public static final String METHOD = "GET"; + + // class under test + private ReferrerPushStrategy referrerPushStrategy; + + @Mock + Stream stream; + @Mock + Session session; + + + @Before + public void setup() + { + referrerPushStrategy = new ReferrerPushStrategy(); + } + + @Test + public void testReferrerCallsAfterTimeoutAreNotAddedAsPushResources() throws InterruptedException + { + Headers requestHeaders = getBaseHeaders(VERSION); + int referrerCallTimeout = 1000; + referrerPushStrategy.setReferrerPushPeriod(referrerCallTimeout); + setMockExpectations(); + + String referrerUrl = fillPushStrategyCache(requestHeaders); + Set<String> pushResources; + + // sleep to pretend that the user manually clicked on a linked resource instead the browser requesting subresources immediately + Thread.sleep(referrerCallTimeout + 1); + + requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), "image2.jpg"); + requestHeaders.put("referer", referrerUrl); + pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers()); + assertThat("pushResources is empty", pushResources.size(), is(0)); + + requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), MAIN_URI); + pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers()); + // as the image2.jpg request has been a link and not a subresource, we expect that pushResources.size() is still 2 + assertThat("pushResources contains two elements image.jpg and style.css", pushResources.size(), is(2)); + } + + @Test + public void testDisablePushByVersion() throws InterruptedException + { + referrerPushStrategy.removeSPDYVersionSupport(SPDY.V2); + + Headers requestHeaders = getBaseHeaders(VERSION); + setMockExpectations(); + + String referrerUrl = fillPushStrategyCache(requestHeaders); + + requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), MAIN_URI); + Set<String> pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers()); + assertThat("pushResources contains two elements image.jpg and style.css", pushResources.size(), is(2)); + + requestHeaders = getBaseHeaders(SPDY.V2); + when(session.getVersion()).thenReturn(SPDY.V2); + pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers()); + assertThat("no push resources are returned for SPDY.V2", pushResources.size(), is(0)); + } + + private Headers getBaseHeaders(short version) + { + Headers requestHeaders = new Headers(); + requestHeaders.put(HTTPSPDYHeader.SCHEME.name(version), SCHEME); + requestHeaders.put(HTTPSPDYHeader.HOST.name(version), HOST); + requestHeaders.put(HTTPSPDYHeader.URI.name(version), MAIN_URI); + requestHeaders.put(HTTPSPDYHeader.METHOD.name(version), METHOD); + return requestHeaders; + } + + private void setMockExpectations() + { + when(stream.getSession()).thenReturn(session); + when(session.getVersion()).thenReturn(VERSION); + } + + private String fillPushStrategyCache(Headers requestHeaders) + { + Set<String> pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers()); + assertThat("pushResources is empty", pushResources.size(), is(0)); + + String origin = SCHEME + "://" + HOST; + String referrerUrl = origin + MAIN_URI; + + requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), "image.jpg"); + requestHeaders.put("referer", referrerUrl); + pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers()); + assertThat("pushResources is empty", pushResources.size(), is(0)); + + requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), "style.css"); + pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers()); + assertThat("pushResources is empty", pushResources.size(), is(0)); + + requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), MAIN_URI); + pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Headers()); + assertThat("pushResources contains two elements image.jpg and style.css", pushResources.size(), is(2)); + return referrerUrl; + } +} diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV2Test.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV2Test.java index ce88e712c7..cc2435ed4f 100644 --- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV2Test.java +++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV2Test.java @@ -7,7 +7,7 @@ * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software + * Unless required by ap‰plicable 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 @@ -32,6 +32,7 @@ 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; @@ -42,6 +43,10 @@ import org.junit.Test; public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest { + + private final String mainResource = "/index.html"; + private final String cssResource = "/style.css"; + @Override protected SPDYServerConnector newHTTPSPDYServerConnector(short version) { @@ -52,9 +57,70 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest } @Test + public void testPushHeadersAreValid() throws Exception + { + InetSocketAddress address = createServer(); + + ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy(); + int referrerPushPeriod = 1000; + pushStrategy.setReferrerPushPeriod(referrerPushPeriod); + AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version(), connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy); + connector.setDefaultAsyncConnectionFactory(defaultFactory); + + Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource); + Session session1 = sendMainRequestAndCSSRequest(address, mainRequestHeaders); + + // Sleep for pushPeriod This should prevent application.js from being mapped as pushResource + Thread.sleep(referrerPushPeriod + 1); + + sendJSRequest(session1); + + run2ndClientRequests(address, mainRequestHeaders, true); + } + + @Test + public void testReferrerPushPeriod() throws Exception + { + InetSocketAddress address = createServer(); + + ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy(); + int referrerPushPeriod = 1000; + pushStrategy.setReferrerPushPeriod(referrerPushPeriod); + AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version(), connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy); + connector.setDefaultAsyncConnectionFactory(defaultFactory); + + Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource); + Session session1 = sendMainRequestAndCSSRequest(address, mainRequestHeaders); + + // Sleep for pushPeriod This should prevent application.js from being mapped as pushResource + Thread.sleep(referrerPushPeriod+1); + + sendJSRequest(session1); + + run2ndClientRequests(address, mainRequestHeaders, false); + } + + @Test public void testMaxAssociatedResources() throws Exception { - InetSocketAddress address = startHTTPServer(version(), new AbstractHandler() + InetSocketAddress address = createServer(); + + ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy(); + pushStrategy.setMaxAssociatedResources(1); + AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version(), connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy); + connector.setDefaultAsyncConnectionFactory(defaultFactory); + + Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource); + Session session1 = sendMainRequestAndCSSRequest(address, mainRequestHeaders); + + sendJSRequest(session1); + + run2ndClientRequests(address, mainRequestHeaders, false); + } + + private InetSocketAddress createServer() throws Exception + { + return startHTTPServer(version(), new AbstractHandler() { @Override public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException @@ -70,21 +136,13 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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); + } + private Session sendMainRequestAndCSSRequest(InetSocketAddress address, Headers mainRequestHeaders) throws Exception + { 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 @@ -98,13 +156,7 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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); + Headers associatedRequestHeaders1 = createHeaders(cssResource); session1.syn(new SynInfo(associatedRequestHeaders1, true), new StreamFrameListener.Adapter() { @Override @@ -116,15 +168,15 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest } }); Assert.assertTrue(associatedResourceLatch1.await(5, TimeUnit.SECONDS)); + return session1; + } + + private void sendJSRequest(Session session1) throws InterruptedException + { 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); + String jsResource = "/application.js"; + Headers associatedRequestHeaders2 = createHeaders(jsResource); session1.syn(new SynInfo(associatedRequestHeaders2, true), new StreamFrameListener.Adapter() { @Override @@ -136,17 +188,24 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest } }); Assert.assertTrue(associatedResourceLatch2.await(5, TimeUnit.SECONDS)); + } + private void run2ndClientRequests(InetSocketAddress address, Headers mainRequestHeaders, final boolean validateHeaders) throws Exception + { // Create another client, and perform the same request for the main resource, // we expect the css being pushed, but not the js final CountDownLatch mainStreamLatch = new CountDownLatch(2); final CountDownLatch pushDataLatch = new CountDownLatch(1); + final CountDownLatch pushSynHeadersValid = new CountDownLatch(1); Session session2 = startClient(version(), address, new SessionFrameListener.Adapter() { @Override public StreamFrameListener onSyn(Stream stream, SynInfo synInfo) { + if(validateHeaders) + validateHeaders(synInfo.getHeaders(), pushSynHeadersValid); + Assert.assertTrue(stream.isUnidirectional()); Assert.assertTrue(synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version())).value().endsWith(".css")); return new StreamFrameListener.Adapter() @@ -180,8 +239,10 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest } }); - Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS)); - Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS)); + Assert.assertTrue("Main request reply and/or data not received", mainStreamLatch.await(5, TimeUnit.SECONDS)); + Assert.assertTrue("Pushed data not received", pushDataLatch.await(5, TimeUnit.SECONDS)); + if(validateHeaders) + Assert.assertTrue("Push syn headers not valid", pushSynHeadersValid.await(5, TimeUnit.SECONDS)); } @Test @@ -204,13 +265,8 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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()); + Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource); + session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter() { @Override @@ -224,13 +280,7 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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); + Headers associatedRequestHeaders = createHeaders(cssResource); session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter() { @Override @@ -290,6 +340,7 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest @Test public void testAssociatedResourceWithWrongContentTypeIsNotPushed() throws Exception { + final String fakeResource = "/fake.png"; InetSocketAddress address = startHTTPServer(version(), new AbstractHandler() { @Override @@ -302,7 +353,7 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest response.setContentType("text/html"); output.print("<html><head/><body>HELLO</body></html>"); } - else if (url.equals("/fake.png")) + else if (url.equals(fakeResource)) { response.setContentType("text/html"); output.print("<html><head/><body>IMAGE</body></html>"); @@ -318,13 +369,8 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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()); + Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource); + session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter() { @Override @@ -338,13 +384,8 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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); + String cssResource = "/stylesheet.css"; + Headers associatedRequestHeaders = createHeaders(cssResource); session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter() { @Override @@ -358,13 +399,7 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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); + Headers fakeAssociatedRequestHeaders = createHeaders(fakeResource); session1.syn(new SynInfo(fakeAssociatedRequestHeaders, true), new StreamFrameListener.Adapter() { @Override @@ -445,13 +480,8 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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()); + Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource); + session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter() { @Override @@ -465,14 +495,7 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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); + Headers associatedRequestHeaders = createHeaders(cssResource); session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter() { @Override @@ -486,13 +509,9 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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); + String imageUrl = "/image.gif"; + Headers nestedRequestHeaders = createHeaders(imageUrl, cssResource); + session1.syn(new SynInfo(nestedRequestHeaders, true), new StreamFrameListener.Adapter() { @Override @@ -567,13 +586,8 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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()); + Headers mainRequestHeaders = createHeadersWithoutReferrer(mainResource); + session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter() { @Override @@ -587,13 +601,9 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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); + String associatedResource = "/home.html"; + Headers associatedRequestHeaders = createHeaders(associatedResource); + session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter() { @Override @@ -661,13 +671,7 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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()); + Headers mainRequestHeaders = createHeaders(mainResource); mainRequestHeaders.put("If-Modified-Since", "Tue, 27 Mar 2012 16:36:52 GMT"); session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter() { @@ -682,13 +686,7 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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); + Headers associatedRequestHeaders = createHeaders(cssResource); session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter() { @Override @@ -745,4 +743,58 @@ public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest 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)); } + + private void validateHeaders(Headers headers, CountDownLatch pushSynHeadersValid) + { + if (validateHeader(headers, HTTPSPDYHeader.STATUS.name(version()), "200") + && validateHeader(headers, HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1") + && validateUriHeader(headers) + && validateHeader(headers, "x-spdy-push", "true")) + pushSynHeadersValid.countDown(); + } + + private boolean validateHeader(Headers headers, String name, String expectedValue) + { + Headers.Header header = headers.get(name); + if (header != null && expectedValue.equals(header.value())) + return true; + System.out.println(name + " not valid! " + headers); + return false; + } + + private boolean validateUriHeader(Headers headers) + { + Headers.Header uriHeader = headers.get(HTTPSPDYHeader.URI.name(version())); + if (uriHeader != null) + if (version() == SPDY.V2 && uriHeader.value().startsWith("http://")) + return true; + else if (version() == SPDY.V3 && uriHeader.value().startsWith("/") + && headers.get(HTTPSPDYHeader.HOST.name(version())) != null && headers.get(HTTPSPDYHeader.SCHEME.name(version())) != null) + return true; + System.out.println(HTTPSPDYHeader.URI.name(version()) + " not valid!"); + return false; + } + + private Headers createHeaders(String resource) + { + return createHeaders(resource, mainResource); + } + + private Headers createHeaders(String resource, String referrer) + { + Headers associatedRequestHeaders = createHeadersWithoutReferrer(resource); + associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + referrer); + return associatedRequestHeaders; + } + + private Headers createHeadersWithoutReferrer(String resource) + { + Headers associatedRequestHeaders = new Headers(); + associatedRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET"); + associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), resource); + 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()); + return associatedRequestHeaders; + } } |