393281: Add User-Agent filter to ReferrerPushStrategy

This commit is contained in:
Thomas Becker 2012-10-31 19:18:56 +01:00
parent ce1c1107eb
commit 3d3e077509
5 changed files with 226 additions and 151 deletions

View File

@ -7,16 +7,19 @@
<!-- HttpChannel Configuration -->
<!-- =========================================================== -->
<New id="httpConfig" class="org.eclipse.jetty.server.HttpConfiguration">
<Set name="secureScheme">https</Set>
<Set name="securePort"><SystemProperty name="jetty.spdy.port" default="8443"/></Set>
<Set name="outputBufferSize">32768</Set>
<Set name="requestHeaderSize">8192</Set>
<Set name="responseHeaderSize">8192</Set>
<Call name="addCustomizer">
<Arg><New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/></Arg>
</Call>
<Set name="secureScheme">https</Set>
<Set name="securePort">
<SystemProperty name="jetty.spdy.port" default="8443"/>
</Set>
<Set name="outputBufferSize">32768</Set>
<Set name="requestHeaderSize">8192</Set>
<Set name="responseHeaderSize">8192</Set>
<Call name="addCustomizer">
<Arg>
<New class="org.eclipse.jetty.server.ForwardedRequestCustomizer"/>
</Arg>
</Call>
</New>
<!-- =========================================================== -->
@ -44,18 +47,39 @@
</Ref>
<!-- =========================================================== -->
<!-- Create a push strategy -->
<!-- Create a push strategy which can be used by reference by -->
<!-- individual connection factories below. -->
<!-- -->
<!-- Consult the javadoc of o.e.j.spdy.server.http.ReferrerPushStrategy -->
<!-- for all configuration that may be set here. -->
<!-- =========================================================== -->
<New id="pushStrategy" class="org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy">
<Arg type="List">
<Array type="String">
<Item>.*\.css</Item>
<Item>.*\.js</Item>
<Item>.*\.png</Item>
<Item>.*\.jpg</Item>
<Item>.*\.gif</Item>
</Array>
</Arg>
<!-- Uncomment to blacklist browsers for this push strategy. If one of the blacklisted Strings occurs in the
user-agent header sent by the client, push will be disabled for this browser. This is case insensitive" -->
<!--
<Set name="BlacklistUserAgents">
<Array type="String">
<Item>.*(?i)firefox/14.*</Item>
<Item>.*(?i)firefox/15.*</Item>
<Item>.*(?i)firefox/16.*</Item>
</Array>
</Set>
-->
<!-- Uncomment to override default file extensions to push -->
<!--
<Set name="PushRegexps">
<Array type="String">
<Item>.*\.css</Item>
<Item>.*\.js</Item>
<Item>.*\.png</Item>
<Item>.*\.jpg</Item>
<Item>.*\.gif</Item>
</Array>
</Set>
-->
<Set name="referrerPushPeriod">5000</Set>
<Set name="maxAssociatedResources">32</Set>
</New>
<!-- =========================================================== -->

View File

@ -25,7 +25,7 @@
<Set name="TrustStorePath"><Property name="jetty.home" default="." />/etc/keystore</Set>
<Set name="TrustStorePassword">OBF:1vny1zlo1x8e1vnw1vn61x8g1zlu1vn4</Set>
</New>
<!-- =========================================================== -->
<!-- Create a TLS specific HttpConfiguration based on the -->
<!-- common HttpConfiguration defined in jetty.xml -->
@ -38,7 +38,7 @@
<Arg><New class="org.eclipse.jetty.server.SecureRequestCustomizer"/></Arg>
</Call>
</New>
<!-- =========================================================== -->
<!-- Create a push strategy which can be used by reference by -->
<!-- individual connection factories below. -->
@ -47,15 +47,30 @@
<!-- for all configuration that may be set here. -->
<!-- =========================================================== -->
<New id="pushStrategy" class="org.eclipse.jetty.spdy.server.http.ReferrerPushStrategy">
<Arg name="pushPatterns" type="List">
<Array type="String">
<Item>.*\.css</Item>
<Item>.*\.js</Item>
<Item>.*\.png</Item>
<Item>.*\.jpg</Item>
<Item>.*\.gif</Item>
</Array>
</Arg>
<!-- Uncomment to blacklist browsers for this push strategy. If one of the blacklisted Strings occurs in the
user-agent header sent by the client, push will be disabled for this browser. This is case insensitive" -->
<!--
<Set name="BlacklistUserAgents">
<Array type="String">
<Item>.*(?i)firefox/14.*</Item>
<Item>.*(?i)firefox/15.*</Item>
<Item>.*(?i)firefox/16.*</Item>
</Array>
</Set>
-->
<!-- Uncomment to override default file extensions to push -->
<!--
<Set name="PushRegexps">
<Array type="String">
<Item>.*\.css</Item>
<Item>.*\.js</Item>
<Item>.*\.png</Item>
<Item>.*\.jpg</Item>
<Item>.*\.gif</Item>
</Array>
</Set>
-->
<Set name="referrerPushPeriod">5000</Set>
<Set name="maxAssociatedResources">32</Set>
</New>
@ -64,7 +79,7 @@
<!-- Add a SPDY/HTTPS Connector. -->
<!-- Configure an o.e.j.server.ServerConnector with connection -->
<!-- factories for TLS (aka SSL), NPN, SPDY and HTTP to provide -->
<!-- a connector that can accept HTTPS or SPDY connections. -->
<!-- a connector that can accept HTTPS or SPDY connections. -->
<!-- -->
<!-- All accepted TLS connections are initially wired to a NPN -->
<!-- connection, which attempts to use a TLS extension to -->
@ -87,7 +102,7 @@
<Arg name="server"><Ref id="Server" /></Arg>
<Arg name="factories">
<Array type="org.eclipse.jetty.server.ConnectionFactory">
<!-- SSL Connection factory with NPN as next protocol -->
<Item>
<New class="org.eclipse.jetty.server.SslConnectionFactory">
@ -113,7 +128,7 @@
<New class="org.eclipse.jetty.spdy.server.http.HTTPSPDYServerConnectionFactory">
<Arg name="version" type="int">3</Arg>
<Arg name="config"><Ref id="tlsHttpConfig" /></Arg>
<Arg name="pushStrategy"><Ref id="pushStrategy"/></Arg>
<Arg name="pushStrategy"><Ref id="pushStrategy"/></Arg>
</New>
</Item>
@ -133,7 +148,7 @@
</Item>
</Array>
</Arg>
<Set name="host"><Property name="jetty.host" /></Set>
<Set name="port"><Property name="jetty.tls.port" default="8443" /></Set>
<Set name="idleTimeout">30000</Set>

View File

@ -33,32 +33,25 @@ import java.util.regex.Pattern;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/**
* <p>A SPDY push strategy that auto-populates push metadata based on referrer URLs.</p>
* <p>A typical request for a main resource such as <tt>index.html</tt> is immediately
* followed by a number of requests for associated resources. Associated resource requests
* will have a <tt>Referer</tt> HTTP header that points to <tt>index.html</tt>, which is
* used 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 a proper value for {@link #getReferrerPushPeriod()}
* has to be set. If the referrerPushPeriod for a main resource has elapsed, 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. 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
* 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>
* <p>A SPDY push strategy that auto-populates push metadata based on referrer URLs.</p> <p>A typical request for a main
* resource such as <tt>index.html</tt> is immediately followed by a number of requests for associated resources.
* Associated resource requests will have a <tt>Referer</tt> HTTP header that points to <tt>index.html</tt>, which is
* used 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 a proper value for
* {@link #setReferrerPushPeriod(int)} has to be set. If the referrerPushPeriod for a main resource has elapsed,
* 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. 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 of the application itself, and by the {@link #setMaxAssociatedResources(int)} 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
{
@ -67,42 +60,56 @@ public class ReferrerPushStrategy implements PushStrategy
private final Set<Pattern> pushRegexps = new HashSet<>();
private final Set<String> pushContentTypes = new HashSet<>();
private final Set<Pattern> allowedPushOrigins = new HashSet<>();
private final Set<Pattern> userAgentBlacklist = new HashSet<>();
private volatile int maxAssociatedResources = 32;
private volatile int referrerPushPeriod = 5000;
public ReferrerPushStrategy()
{
this(Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpeg", ".*\\.jpg", ".*\\.gif", ".*\\.ico"));
}
List<String> defaultPushRegexps = Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpeg", ".*\\.jpg",
".*\\.gif", ".*\\.ico");
addPushRegexps(defaultPushRegexps);
public ReferrerPushStrategy(@Name("pushPatterns") List<String> pushRegexps)
{
this(pushRegexps, Arrays.asList(
List<String> defaultPushContentTypes = 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"));
"image/x-icon", "image/vnd.microsoft.icon");
this.pushContentTypes.addAll(defaultPushContentTypes);
}
public ReferrerPushStrategy(List<String> pushRegexps, List<String> pushContentTypes)
public void setPushRegexps(List<String> pushRegexps)
{
this(pushRegexps, pushContentTypes, Collections.<String>emptyList());
pushRegexps.clear();
addPushRegexps(pushRegexps);
}
public ReferrerPushStrategy(List<String> pushRegexps, List<String> pushContentTypes, List<String> allowedPushOrigins)
private void addPushRegexps(List<String> pushRegexps)
{
for (String pushRegexp : pushRegexps)
this.pushRegexps.add(Pattern.compile(pushRegexp));
this.pushContentTypes.addAll(pushContentTypes);
}
public void setPushContentTypes(List<String> pushContentTypes)
{
pushContentTypes.clear();
pushContentTypes.addAll(pushContentTypes);
}
public void setAllowedPushOrigins(List<String> allowedPushOrigins)
{
allowedPushOrigins.clear();
for (String allowedPushOrigin : allowedPushOrigins)
this.allowedPushOrigins.add(Pattern.compile(allowedPushOrigin.replace(".", "\\.").replace("*", ".*")));
}
public int getMaxAssociatedResources()
public void setUserAgentBlacklist(List<String> userAgentPatterns)
{
return maxAssociatedResources;
userAgentBlacklist.clear();
for (String userAgentPattern : userAgentPatterns)
userAgentBlacklist.add(Pattern.compile(userAgentPattern));
}
public void setMaxAssociatedResources(int maxAssociatedResources)
@ -110,11 +117,6 @@ public class ReferrerPushStrategy implements PushStrategy
this.maxAssociatedResources = maxAssociatedResources;
}
public int getReferrerPushPeriod()
{
return referrerPushPeriod;
}
public void setReferrerPushPeriod(int referrerPushPeriod)
{
this.referrerPushPeriod = referrerPushPeriod;
@ -125,7 +127,8 @@ public class ReferrerPushStrategy implements PushStrategy
{
Set<String> result = Collections.<String>emptySet();
short version = stream.getSession().getVersion();
if (!isIfModifiedSinceHeaderPresent(requestHeaders) && isValidMethod(requestHeaders.get(HTTPSPDYHeader.METHOD.name(version)).value()))
if (!isIfModifiedSinceHeaderPresent(requestHeaders) && isValidMethod(requestHeaders.get(HTTPSPDYHeader.METHOD
.name(version)).value()) && !isUserAgentBlacklisted(requestHeaders))
{
String scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version)).value();
String host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version)).value();
@ -197,6 +200,16 @@ public class ReferrerPushStrategy implements PushStrategy
return !isPushResource(url, responseHeaders);
}
public boolean isUserAgentBlacklisted(Fields headers)
{
Fields.Field userAgentHeader = headers.get("user-agent");
if (userAgentHeader != null)
for (Pattern userAgentPattern : userAgentBlacklist)
if (userAgentPattern.matcher(userAgentHeader.value()).matches())
return true;
return false;
}
private boolean isPushResource(String url, Fields responseHeaders)
{
for (Pattern pushRegexp : pushRegexps)
@ -276,10 +289,8 @@ public class ReferrerPushStrategy implements PushStrategy
private boolean isPushOriginAllowed(String origin)
{
for (Pattern allowedPushOrigin : allowedPushOrigins)
{
if (allowedPushOrigin.matcher(origin).matches())
return true;
}
return false;
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.spdy.server.http;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import javax.servlet.ServletException;
@ -42,12 +43,21 @@ import org.eclipse.jetty.spdy.api.SynInfo;
import org.eclipse.jetty.spdy.server.NPNServerConnectionFactory;
import org.eclipse.jetty.util.Fields;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
{
private final String mainResource = "/index.html";
private final int referrerPushPeriod = 1000;
private final String cssResource = "/style.css";
private InetSocketAddress serverAddress;
private ReferrerPushStrategy pushStrategy;
private ConnectionFactory defaultFactory;
private Fields mainRequestHeaders;
public ReferrerPushStrategyTest(short version)
{
@ -57,82 +67,63 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
@Override
protected HTTPSPDYServerConnector newHTTPSPDYServerConnector(short version)
{
HTTPSPDYServerConnector connector =
new HTTPSPDYServerConnector(server,version,new HttpConfiguration(),new ReferrerPushStrategy());
return connector;
return new HTTPSPDYServerConnector(server, version, new HttpConfiguration(), new ReferrerPushStrategy());
}
@Before
public void setUp() throws Exception
{
serverAddress = createServer();
pushStrategy = new ReferrerPushStrategy();
pushStrategy.setReferrerPushPeriod(referrerPushPeriod);
defaultFactory = new HTTPSPDYServerConnectionFactory(version, new HttpConfiguration(), pushStrategy);
connector.addConnectionFactory(defaultFactory);
if (connector.getConnectionFactory(NPNServerConnectionFactory.class) != null)
connector.getConnectionFactory(NPNServerConnectionFactory.class).setDefaultProtocol(defaultFactory.getProtocol());
else
connector.setDefaultProtocol(defaultFactory.getProtocol());
mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
}
@Test
public void testPushHeadersAreValid() throws Exception
{
InetSocketAddress address = createServer();
sendMainRequestAndCSSRequest();
run2ndClientRequests(true, true);
}
ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy();
int referrerPushPeriod = 1000;
pushStrategy.setReferrerPushPeriod(referrerPushPeriod);
ConnectionFactory defaultFactory = new HTTPSPDYServerConnectionFactory(version,new HttpConfiguration(), pushStrategy);
connector.addConnectionFactory(defaultFactory);
if (connector.getConnectionFactory(NPNServerConnectionFactory.class)!=null)
connector.getConnectionFactory(NPNServerConnectionFactory.class).setDefaultProtocol(defaultFactory.getProtocol());
else
connector.setDefaultProtocol(defaultFactory.getProtocol());
connector.setDefaultProtocol(defaultFactory.getProtocol()); // TODO I don't think this is right
Fields 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 testUserAgentBlackList() throws Exception
{
pushStrategy.setUserAgentBlacklist(Arrays.asList(".*(?i)firefox/16.*"));
sendMainRequestAndCSSRequest();
run2ndClientRequests(false, false);
}
@Test
public void testReferrerPushPeriod() throws Exception
{
InetSocketAddress address = createServer();
ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy();
int referrerPushPeriod = 1000;
pushStrategy.setReferrerPushPeriod(referrerPushPeriod);
ConnectionFactory defaultFactory = new HTTPSPDYServerConnectionFactory(version,new HttpConfiguration(), pushStrategy);
connector.addConnectionFactory(defaultFactory);
if (connector.getConnectionFactory(NPNServerConnectionFactory.class)!=null)
connector.getConnectionFactory(NPNServerConnectionFactory.class).setDefaultProtocol(defaultFactory.getProtocol());
else
connector.setDefaultProtocol(defaultFactory.getProtocol());
Fields mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
Session session1 = sendMainRequestAndCSSRequest(address, mainRequestHeaders);
Session session1 = sendMainRequestAndCSSRequest();
// Sleep for pushPeriod This should prevent application.js from being mapped as pushResource
Thread.sleep(referrerPushPeriod+1);
Thread.sleep(referrerPushPeriod + 1);
sendJSRequest(session1);
run2ndClientRequests(address, mainRequestHeaders, false);
run2ndClientRequests(false, true);
}
@Test
public void testMaxAssociatedResources() throws Exception
{
InetSocketAddress address = createServer();
ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy();
pushStrategy.setMaxAssociatedResources(1);
ConnectionFactory defaultFactory = new HTTPSPDYServerConnectionFactory(version,new HttpConfiguration(), pushStrategy);
connector.addConnectionFactory(defaultFactory);
connector.setDefaultProtocol(defaultFactory.getProtocol()); // TODO I don't think this is right
Fields mainRequestHeaders = createHeadersWithoutReferrer(mainResource);
Session session1 = sendMainRequestAndCSSRequest(address, mainRequestHeaders);
Session session1 = sendMainRequestAndCSSRequest();
sendJSRequest(session1);
run2ndClientRequests(address, mainRequestHeaders, false);
run2ndClientRequests(false, true);
}
private InetSocketAddress createServer() throws Exception
@ -155,9 +146,9 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
});
}
private Session sendMainRequestAndCSSRequest(InetSocketAddress address, Fields mainRequestHeaders) throws Exception
private Session sendMainRequestAndCSSRequest() throws Exception
{
Session session1 = startClient(version, address, null);
Session session1 = startClient(version, serverAddress, null);
final CountDownLatch mainResourceLatch = new CountDownLatch(1);
session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
@ -207,7 +198,8 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
Assert.assertTrue(associatedResourceLatch2.await(5, TimeUnit.SECONDS));
}
private void run2ndClientRequests(InetSocketAddress address, Fields mainRequestHeaders, final boolean validateHeaders) throws Exception
private void run2ndClientRequests(final boolean validateHeaders,
boolean expectPushResource) throws Exception
{
// Create another client, and perform the same request for the main resource,
// we expect the css being pushed, but not the js
@ -215,16 +207,20 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
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()
Session session2 = startClient(version, serverAddress, new SessionFrameListener.Adapter()
{
@Override
public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
{
if(validateHeaders)
if (validateHeaders)
validateHeaders(synInfo.getHeaders(), pushSynHeadersValid);
Assert.assertTrue(stream.isUnidirectional());
Assert.assertTrue(synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version)).value().endsWith(".css"));
assertThat("Stream is unidirectional",stream.isUnidirectional(),is(true));
assertThat("URI header ends with css", synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version))
.value().endsWith
("" +
".css"),
is(true));
return new StreamFrameListener.Adapter()
{
@Override
@ -243,7 +239,7 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
@Override
public void onReply(Stream stream, ReplyInfo replyInfo)
{
Assert.assertFalse(replyInfo.isClose());
assertThat("replyInfo.isClose() is false", replyInfo.isClose(), is(false));
mainStreamLatch.countDown();
}
@ -256,10 +252,13 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
}
});
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));
assertThat("Main request reply and/or data not received", mainStreamLatch.await(5, TimeUnit.SECONDS), is(true));
if (expectPushResource)
assertThat("Pushed data not received", pushDataLatch.await(5, TimeUnit.SECONDS), is(true));
else
assertThat("No push data is received", pushDataLatch.await(1, TimeUnit.SECONDS), is(false));
if (validateHeaders)
assertThat("Push syn headers not valid", pushSynHeadersValid.await(5, TimeUnit.SECONDS), is(true));
}
@Test
@ -758,7 +757,7 @@ public class ReferrerPushStrategyTest 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));
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(Fields headers, CountDownLatch pushSynHeadersValid)
@ -805,12 +804,14 @@ public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
private Fields createHeadersWithoutReferrer(String resource)
{
Fields associatedRequestHeaders = new Fields();
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;
Fields requestHeaders = new Fields();
requestHeaders.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:16.0) " +
"Gecko/20100101 Firefox/16.0");
requestHeaders.put(HTTPSPDYHeader.METHOD.name(version), "GET");
requestHeaders.put(HTTPSPDYHeader.URI.name(version), resource);
requestHeaders.put(HTTPSPDYHeader.VERSION.name(version), "HTTP/1.1");
requestHeaders.put(HTTPSPDYHeader.SCHEME.name(version), "http");
requestHeaders.put(HTTPSPDYHeader.HOST.name(version), "localhost:" + connector.getLocalPort());
return requestHeaders;
}
}

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.spdy.server.http;
import java.util.Arrays;
import java.util.Set;
import org.eclipse.jetty.spdy.api.SPDY;
@ -44,7 +45,7 @@ public class ReferrerPushStrategyUnitTest
public static final String METHOD = "GET";
// class under test
private ReferrerPushStrategy referrerPushStrategy;
private ReferrerPushStrategy referrerPushStrategy = new ReferrerPushStrategy();
@Mock
Stream stream;
@ -55,7 +56,7 @@ public class ReferrerPushStrategyUnitTest
@Before
public void setup()
{
referrerPushStrategy = new ReferrerPushStrategy();
referrerPushStrategy.setUserAgentBlacklist(Arrays.asList(".*(?i)firefox/16.*"));
}
@Test
@ -67,22 +68,45 @@ public class ReferrerPushStrategyUnitTest
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
// sleep to pretend that the user manually clicked on a linked resource instead the browser requesting sub
// resources immediately
Thread.sleep(referrerCallTimeout + 1);
requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), "image2.jpg");
requestHeaders.put("referer", referrerUrl);
pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
Set<String> pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
assertThat("pushResources is empty", pushResources.size(), is(0));
requestHeaders.put(HTTPSPDYHeader.URI.name(VERSION), MAIN_URI);
pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
// as the image2.jpg request has been a link and not a subresource, we expect that pushResources.size() is still 2
// as the image2.jpg request has been a link and not a sub resource, 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 testUserAgentFilter() throws InterruptedException
{
Fields requestHeaders = getBaseHeaders(VERSION);
setMockExpectations();
fillPushStrategyCache(requestHeaders);
Set<String> pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
assertThat("pushResources contains two elements image.jpg and style.css as no user-agent header is present",
pushResources.size(), is(2));
requestHeaders.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5) AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4");
pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
assertThat("pushResources contains two elements image.jpg and style.css as chrome is not blacklisted",
pushResources.size(), is(2));
requestHeaders.put("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:16.0) Gecko/20100101 Firefox/16.0");
pushResources = referrerPushStrategy.apply(stream, requestHeaders, new Fields());
assertThat("no resources are returned as we want to filter firefox", pushResources.size(), is(0));
}
private Fields getBaseHeaders(short version)
{
Fields requestHeaders = new Fields();