393281: Add User-Agent filter to ReferrerPushStrategy
This commit is contained in:
parent
ce1c1107eb
commit
3d3e077509
|
@ -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>
|
||||
|
||||
<!-- =========================================================== -->
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue