();
private int preflightMaxAge = 0;
private boolean allowCredentials;
@@ -163,6 +167,11 @@ public class CrossOriginFilter implements Filter
allowedCredentialsConfig = "true";
allowCredentials = Boolean.parseBoolean(allowedCredentialsConfig);
+ String exposedHeadersConfig = config.getInitParameter(EXPOSED_HEADERS_PARAM);
+ if (exposedHeadersConfig == null)
+ exposedHeadersConfig = "";
+ exposedHeaders.addAll(Arrays.asList(exposedHeadersConfig.split(",")));
+
if (LOG.isDebugEnabled())
{
LOG.debug("Cross-origin filter configuration: " +
@@ -170,7 +179,9 @@ public class CrossOriginFilter implements Filter
ALLOWED_METHODS_PARAM + " = " + allowedMethodsConfig + ", " +
ALLOWED_HEADERS_PARAM + " = " + allowedHeadersConfig + ", " +
PREFLIGHT_MAX_AGE_PARAM + " = " + preflightMaxAgeConfig + ", " +
- ALLOW_CREDENTIALS_PARAM + " = " + allowedCredentialsConfig);
+ ALLOW_CREDENTIALS_PARAM + " = " + allowedCredentialsConfig + "," +
+ EXPOSED_HEADERS_PARAM + " = " + exposedHeadersConfig
+ );
}
}
@@ -305,6 +316,8 @@ public class CrossOriginFilter implements Filter
response.setHeader(ACCESS_CONTROL_ALLOW_ORIGIN_HEADER, origin);
if (allowCredentials)
response.setHeader(ACCESS_CONTROL_ALLOW_CREDENTIALS_HEADER, "true");
+ if (!exposedHeaders.isEmpty())
+ response.setHeader(ACCESS_CONTROL_EXPOSE_HEADERS_HEADER, commify(exposedHeaders));
}
private void handlePreflightResponse(HttpServletRequest request, HttpServletResponse response, String origin)
diff --git a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
index fb8d6bba490..bd631ebf37c 100644
--- a/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
+++ b/jetty-servlets/src/test/java/org/eclipse/jetty/servlets/CrossOriginFilterTest.java
@@ -371,6 +371,27 @@ public class CrossOriginFilterTest
Assert.assertTrue(latch.await(1, TimeUnit.SECONDS));
}
+ @Test
+ public void testSimpleRequestWithExposedHeaders() throws Exception
+ {
+ FilterHolder filterHolder = new FilterHolder(new CrossOriginFilter());
+ filterHolder.setInitParameter("exposedHeaders", "Content-Length");
+ tester.getContext().addFilter(filterHolder, "/*", FilterMapping.DEFAULT);
+
+ CountDownLatch latch = new CountDownLatch(1);
+ tester.getContext().addServlet(new ServletHolder(new ResourceServlet(latch)), "/*");
+
+ String request = "" +
+ "GET / HTTP/1.1\r\n" +
+ "Host: localhost\r\n" +
+ "Origin: http://localhost\r\n" +
+ "\r\n";
+ String response = tester.getResponses(request);
+ Assert.assertTrue(response.contains("HTTP/1.1 200"));
+ Assert.assertTrue(response.contains(CrossOriginFilter.ACCESS_CONTROL_EXPOSE_HEADERS_HEADER));
+ Assert.assertTrue(latch.await(1, TimeUnit.SECONDS));
+ }
+
public static class ResourceServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
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 cf3fe06f1b6..52f1243d734 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
@@ -18,7 +18,7 @@ package org.eclipse.jetty.spdy.http;
import java.util.Arrays;
import java.util.Collections;
-import java.util.LinkedHashSet;
+import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@@ -39,58 +39,80 @@ import org.eclipse.jetty.util.log.Logger;
* However, also following a hyperlink generates a HTTP request with a Referer
* HTTP header that points to index.html; therefore main resources and associated
* resources must be distinguishable.
- * This class distinguishes associated resources by their URL path suffix.
+ *
This class distinguishes associated resources by their URL path suffix and content
+ * type.
* CSS stylesheets, images and JavaScript files have recognizable URL path suffixes that
* are classified as associated resources.
- * Note however, that CSS stylesheets may refer to images, and the CSS image request
- * will have the CSS stylesheet as referrer, so there is some degree of recursion that
- * needs to be handled.
- *
- * TODO: this class is kind-of leaking since the resources map is always adding entries
- * TODO: although these entries will be limited by the number of application pages.
- * TODO: however, there is no ConcurrentLinkedHashMap yet in JDK (there is in Guava though)
- * TODO: so we cannot use the built-in LRU features of LinkedHashMap
- *
- * TODO: Wikipedia maps URLs like http://en.wikipedia.org/wiki/File:PNG-Gradient_hex.png
- * TODO: to text/html, so perhaps we need to improve isPushResource() by looking at the
- * TODO: response Content-Type header, and not only at the URL extension
+ * 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.
+ * 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.
*/
public class ReferrerPushStrategy implements PushStrategy
{
private static final Logger logger = Log.getLogger(ReferrerPushStrategy.class);
private final ConcurrentMap> resources = new ConcurrentHashMap<>();
- private final Set pushRegexps = new LinkedHashSet<>();
- private final Set allowedPushOrigins = new LinkedHashSet<>();
+ private final Set pushRegexps = new HashSet<>();
+ private final Set pushContentTypes = new HashSet<>();
+ private final Set allowedPushOrigins = new HashSet<>();
+ private volatile int maxAssociatedResources = 32;
public ReferrerPushStrategy()
{
- this(Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpg", ".*\\.gif"));
+ this(Arrays.asList(".*\\.css", ".*\\.js", ".*\\.png", ".*\\.jpeg", ".*\\.jpg", ".*\\.gif", ".*\\.ico"));
}
public ReferrerPushStrategy(List pushRegexps)
{
- this(pushRegexps, Collections.emptyList());
+ this(pushRegexps, Arrays.asList(
+ "text/css",
+ "text/javascript", "application/javascript", "application/x-javascript",
+ "image/png", "image/x-png",
+ "image/jpeg",
+ "image/gif",
+ "image/x-icon", "image/vnd.microsoft.icon"));
}
- public ReferrerPushStrategy(List pushRegexps, List allowedPushOrigins)
+ public ReferrerPushStrategy(List pushRegexps, List pushContentTypes)
+ {
+ this(pushRegexps, pushContentTypes, Collections.emptyList());
+ }
+
+ public ReferrerPushStrategy(List pushRegexps, List pushContentTypes, List allowedPushOrigins)
{
for (String pushRegexp : pushRegexps)
this.pushRegexps.add(Pattern.compile(pushRegexp));
+ this.pushContentTypes.addAll(pushContentTypes);
for (String allowedPushOrigin : allowedPushOrigins)
this.allowedPushOrigins.add(Pattern.compile(allowedPushOrigin.replace(".", "\\.").replace("*", ".*")));
}
+ public int getMaxAssociatedResources()
+ {
+ return maxAssociatedResources;
+ }
+
+ public void setMaxAssociatedResources(int maxAssociatedResources)
+ {
+ this.maxAssociatedResources = maxAssociatedResources;
+ }
+
@Override
public Set apply(Stream stream, Headers requestHeaders, Headers responseHeaders)
{
Set result = Collections.emptySet();
- String scheme = requestHeaders.get("scheme").value();
- String host = requestHeaders.get("host").value();
+ short version = stream.getSession().getVersion();
+ String scheme = requestHeaders.get(HTTPSPDYHeader.SCHEME.name(version)).value();
+ String host = requestHeaders.get(HTTPSPDYHeader.HOST.name(version)).value();
String origin = new StringBuilder(scheme).append("://").append(host).toString();
- String url = requestHeaders.get("url").value();
+ String url = requestHeaders.get(HTTPSPDYHeader.URI.name(version)).value();
String absoluteURL = new StringBuilder(origin).append(url).toString();
logger.debug("Applying push strategy for {}", absoluteURL);
- if (isValidMethod(requestHeaders.get("method").value()))
+ if (isValidMethod(requestHeaders.get(HTTPSPDYHeader.METHOD.name(version)).value()))
{
if (isMainResource(url, responseHeaders))
{
@@ -129,7 +151,16 @@ public class ReferrerPushStrategy implements PushStrategy
for (Pattern pushRegexp : pushRegexps)
{
if (pushRegexp.matcher(url).matches())
- return true;
+ {
+ Headers.Header header = responseHeaders.get("content-type");
+ if (header == null)
+ return true;
+
+ String contentType = header.value().toLowerCase();
+ for (String pushContentType : pushContentTypes)
+ if (contentType.startsWith(pushContentType))
+ return true;
+ }
}
return false;
}
@@ -154,8 +185,19 @@ public class ReferrerPushStrategy implements PushStrategy
if (existing != null)
pushResources = existing;
}
- pushResources.add(url);
- logger.debug("Built push metadata for {}: {}", referrer, pushResources);
+ // This check is not strictly concurrent-safe, but limiting
+ // the number of associated resources is achieved anyway
+ // although in rare cases few more resources will be stored
+ if (pushResources.size() < getMaxAssociatedResources())
+ {
+ pushResources.add(url);
+ logger.debug("Stored push metadata for {}: {}", referrer, pushResources);
+ }
+ else
+ {
+ logger.debug("Skipped store of push metadata {} for {}: max associated resources ({}) reached",
+ url, referrer, maxAssociatedResources);
+ }
}
}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java
index b875379f3ba..f6f41776d24 100644
--- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/AbstractHTTPSPDYTest.java
@@ -113,4 +113,9 @@ public abstract class AbstractHTTPSPDYTest
server.join();
}
}
+
+ protected short version()
+ {
+ return SPDY.V2;
+ }
}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ConcurrentStreamsTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ConcurrentStreamsTest.java
index a26555004b9..0bbf0e68ff3 100644
--- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ConcurrentStreamsTest.java
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ConcurrentStreamsTest.java
@@ -73,10 +73,10 @@ public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
// Perform slow request. This will wait on server side until the fast request wakes it up
Headers headers = new Headers();
- headers.put("method", "GET");
- headers.put("url", "/slow");
- headers.put("version", "HTTP/1.1");
- headers.put("host", "localhost:" + connector.getLocalPort());
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/slow");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
final CountDownLatch slowClientLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
@@ -91,10 +91,10 @@ public class ConcurrentStreamsTest extends AbstractHTTPSPDYTest
// Perform the fast request. This will wake up the slow request
headers.clear();
- headers.put("method", "GET");
- headers.put("url", "/fast");
- headers.put("version", "HTTP/1.1");
- headers.put("host", "localhost:" + connector.getLocalPort());
+ headers.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ headers.put(HTTPSPDYHeader.URI.name(version()), "/fast");
+ headers.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ headers.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
final CountDownLatch fastClientLatch = new CountDownLatch(1);
session.syn(new SynInfo(headers, true), new StreamFrameListener.Adapter()
{
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 0ff4893bd09..29b5952d4e5 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
@@ -38,7 +38,6 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.spdy.AsyncConnectionFactory;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.Headers;
-import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.SessionFrameListener;
import org.eclipse.jetty.spdy.api.Stream;
@@ -62,11 +61,6 @@ public class PushStrategyBenchmarkTest extends AbstractHTTPSPDYTest
private final long roundtrip = 100;
private final int runs = 10;
- protected short version()
- {
- return SPDY.V2;
- }
-
@Test
public void benchmarkPushStrategy() throws Exception
{
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyTest.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyTest.java
deleted file mode 100644
index 4215118d5a1..00000000000
--- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyTest.java
+++ /dev/null
@@ -1,463 +0,0 @@
-package org.eclipse.jetty.spdy.http;
-
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.net.InetSocketAddress;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
-import org.eclipse.jetty.server.Request;
-import org.eclipse.jetty.server.handler.AbstractHandler;
-import org.eclipse.jetty.spdy.AsyncConnectionFactory;
-import org.eclipse.jetty.spdy.SPDYServerConnector;
-import org.eclipse.jetty.spdy.api.DataInfo;
-import org.eclipse.jetty.spdy.api.Headers;
-import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.Session;
-import org.eclipse.jetty.spdy.api.SessionFrameListener;
-import org.eclipse.jetty.spdy.api.Stream;
-import org.eclipse.jetty.spdy.api.StreamFrameListener;
-import org.eclipse.jetty.spdy.api.SynInfo;
-import org.junit.Assert;
-import org.junit.Test;
-
-public class ReferrerPushStrategyTest extends AbstractHTTPSPDYTest
-{
- @Override
- protected SPDYServerConnector newHTTPSPDYServerConnector(short version)
- {
- SPDYServerConnector connector = super.newHTTPSPDYServerConnector(version);
- AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, new ReferrerPushStrategy());
- connector.setDefaultAsyncConnectionFactory(defaultFactory);
- return connector;
- }
-
- @Test
- public void testAssociatedResourceIsPushed() throws Exception
- {
- InetSocketAddress address = startHTTPServer(new AbstractHandler()
- {
- @Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
- {
- String url = request.getRequestURI();
- PrintWriter output = response.getWriter();
- if (url.endsWith(".html"))
- output.print("HELLO");
- else if (url.endsWith(".css"))
- output.print("body { background: #FFF; }");
- baseRequest.setHandled(true);
- }
- });
- Session session1 = startClient(address, null);
-
- final CountDownLatch mainResourceLatch = new CountDownLatch(1);
- Headers mainRequestHeaders = new Headers();
- mainRequestHeaders.put("method", "GET");
- String mainResource = "/index.html";
- mainRequestHeaders.put("url", mainResource);
- mainRequestHeaders.put("version", "HTTP/1.1");
- mainRequestHeaders.put("scheme", "http");
- mainRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
- session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- mainResourceLatch.countDown();
- }
- });
- Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
-
- final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
- Headers associatedRequestHeaders = new Headers();
- associatedRequestHeaders.put("method", "GET");
- associatedRequestHeaders.put("url", "/style.css");
- associatedRequestHeaders.put("version", "HTTP/1.1");
- associatedRequestHeaders.put("scheme", "http");
- associatedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
- associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
- session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- associatedResourceLatch.countDown();
- }
- });
- Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
-
- // Create another client, and perform the same request for the main resource, we expect the css being pushed
-
- final CountDownLatch mainStreamLatch = new CountDownLatch(2);
- final CountDownLatch pushDataLatch = new CountDownLatch(1);
- Session session2 = startClient(address, new SessionFrameListener.Adapter()
- {
- @Override
- public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
- {
- Assert.assertTrue(stream.isUnidirectional());
- return new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- pushDataLatch.countDown();
- }
- };
- }
- });
- session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onReply(Stream stream, ReplyInfo replyInfo)
- {
- Assert.assertFalse(replyInfo.isClose());
- mainStreamLatch.countDown();
- }
-
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- mainStreamLatch.countDown();
- }
- });
-
- Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
- Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
- }
-
- @Test
- public void testNestedAssociatedResourceIsPushed() throws Exception
- {
- InetSocketAddress address = startHTTPServer(new AbstractHandler()
- {
- @Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
- {
- String url = request.getRequestURI();
- PrintWriter output = response.getWriter();
- if (url.endsWith(".html"))
- output.print("HELLO");
- else if (url.endsWith(".css"))
- output.print("body { background: #FFF; }");
- else if (url.endsWith(".gif"))
- output.print("\u0000");
- baseRequest.setHandled(true);
- }
- });
- Session session1 = startClient(address, null);
-
- final CountDownLatch mainResourceLatch = new CountDownLatch(1);
- Headers mainRequestHeaders = new Headers();
- mainRequestHeaders.put("method", "GET");
- String mainResource = "/index.html";
- mainRequestHeaders.put("url", mainResource);
- mainRequestHeaders.put("version", "HTTP/1.1");
- mainRequestHeaders.put("scheme", "http");
- mainRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
- session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- mainResourceLatch.countDown();
- }
- });
- Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
-
- final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
- Headers associatedRequestHeaders = new Headers();
- associatedRequestHeaders.put("method", "GET");
- String associatedResource = "/style.css";
- associatedRequestHeaders.put("url", associatedResource);
- associatedRequestHeaders.put("version", "HTTP/1.1");
- associatedRequestHeaders.put("scheme", "http");
- associatedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
- associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
- session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- associatedResourceLatch.countDown();
- }
- });
- Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
-
- final CountDownLatch nestedResourceLatch = new CountDownLatch(1);
- Headers nestedRequestHeaders = new Headers();
- nestedRequestHeaders.put("method", "GET");
- nestedRequestHeaders.put("url", "/image.gif");
- nestedRequestHeaders.put("version", "HTTP/1.1");
- nestedRequestHeaders.put("scheme", "http");
- nestedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
- nestedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + associatedResource);
- session1.syn(new SynInfo(nestedRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- nestedResourceLatch.countDown();
- }
- });
- Assert.assertTrue(nestedResourceLatch.await(5, TimeUnit.SECONDS));
-
- // Create another client, and perform the same request for the main resource, we expect the css and the image being pushed
-
- final CountDownLatch mainStreamLatch = new CountDownLatch(2);
- final CountDownLatch pushDataLatch = new CountDownLatch(2);
- Session session2 = startClient(address, new SessionFrameListener.Adapter()
- {
- @Override
- public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
- {
- Assert.assertTrue(stream.isUnidirectional());
- return new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- pushDataLatch.countDown();
- }
- };
- }
- });
- session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onReply(Stream stream, ReplyInfo replyInfo)
- {
- Assert.assertFalse(replyInfo.isClose());
- mainStreamLatch.countDown();
- }
-
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- mainStreamLatch.countDown();
- }
- });
-
- Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
- Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
- }
-
- @Test
- public void testMainResourceWithReferrerIsNotPushed() throws Exception
- {
- InetSocketAddress address = startHTTPServer(new AbstractHandler()
- {
- @Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
- {
- String url = request.getRequestURI();
- PrintWriter output = response.getWriter();
- if (url.endsWith(".html"))
- output.print("HELLO");
- baseRequest.setHandled(true);
- }
- });
- Session session1 = startClient(address, null);
-
- final CountDownLatch mainResourceLatch = new CountDownLatch(1);
- Headers mainRequestHeaders = new Headers();
- mainRequestHeaders.put("method", "GET");
- String mainResource = "/index.html";
- mainRequestHeaders.put("url", mainResource);
- mainRequestHeaders.put("version", "HTTP/1.1");
- mainRequestHeaders.put("scheme", "http");
- mainRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
- session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- mainResourceLatch.countDown();
- }
- });
- Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
-
- final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
- Headers associatedRequestHeaders = new Headers();
- associatedRequestHeaders.put("method", "GET");
- associatedRequestHeaders.put("url", "/home.html");
- associatedRequestHeaders.put("version", "HTTP/1.1");
- associatedRequestHeaders.put("scheme", "http");
- associatedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
- associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
- session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- associatedResourceLatch.countDown();
- }
- });
- Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
-
- // Create another client, and perform the same request for the main resource, we expect nothing being pushed
-
- final CountDownLatch mainStreamLatch = new CountDownLatch(2);
- final CountDownLatch pushLatch = new CountDownLatch(1);
- Session session2 = startClient(address, new SessionFrameListener.Adapter()
- {
- @Override
- public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
- {
- pushLatch.countDown();
- return null;
- }
- });
- session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onReply(Stream stream, ReplyInfo replyInfo)
- {
- Assert.assertFalse(replyInfo.isClose());
- mainStreamLatch.countDown();
- }
-
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- mainStreamLatch.countDown();
- }
- });
-
- Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
- Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
- }
-
- @Test
- public void testRequestWithIfModifiedSinceHeaderPreventsPush() throws Exception
- {
- InetSocketAddress address = startHTTPServer(new AbstractHandler()
- {
- @Override
- public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
- {
- String url = request.getRequestURI();
- PrintWriter output = response.getWriter();
- if (url.endsWith(".html"))
- output.print("HELLO");
- else if (url.endsWith(".css"))
- output.print("body { background: #FFF; }");
- baseRequest.setHandled(true);
- }
- });
- Session session1 = startClient(address, null);
-
- final CountDownLatch mainResourceLatch = new CountDownLatch(1);
- Headers mainRequestHeaders = new Headers();
- mainRequestHeaders.put("method", "GET");
- String mainResource = "/index.html";
- mainRequestHeaders.put("url", mainResource);
- mainRequestHeaders.put("version", "HTTP/1.1");
- mainRequestHeaders.put("scheme", "http");
- mainRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
- mainRequestHeaders.put("If-Modified-Since", "Tue, 27 Mar 2012 16:36:52 GMT");
- session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- mainResourceLatch.countDown();
- }
- });
- Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
-
- final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
- Headers associatedRequestHeaders = new Headers();
- associatedRequestHeaders.put("method", "GET");
- associatedRequestHeaders.put("url", "/style.css");
- associatedRequestHeaders.put("version", "HTTP/1.1");
- associatedRequestHeaders.put("scheme", "http");
- associatedRequestHeaders.put("host", "localhost:" + connector.getLocalPort());
- associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
- session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- associatedResourceLatch.countDown();
- }
- });
- Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
-
- // Create another client, and perform the same request for the main resource, we expect the css NOT being pushed as the main request contains an
- // if-modified-since header
-
- final CountDownLatch mainStreamLatch = new CountDownLatch(2);
- final CountDownLatch pushDataLatch = new CountDownLatch(1);
- Session session2 = startClient(address, new SessionFrameListener.Adapter()
- {
- @Override
- public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
- {
- Assert.assertTrue(stream.isUnidirectional());
- return new StreamFrameListener.Adapter()
- {
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- pushDataLatch.countDown();
- }
- };
- }
- });
- session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
- {
- @Override
- public void onReply(Stream stream, ReplyInfo replyInfo)
- {
- Assert.assertFalse(replyInfo.isClose());
- mainStreamLatch.countDown();
- }
-
- @Override
- public void onData(Stream stream, DataInfo dataInfo)
- {
- dataInfo.consume(dataInfo.length());
- if (dataInfo.isClose())
- mainStreamLatch.countDown();
- }
- });
-
- Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
- Assert.assertFalse("We don't expect data to be pushed as the main request contained an if-modified-since header",pushDataLatch.await(1, TimeUnit.SECONDS));
- }
-}
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
new file mode 100644
index 00000000000..ce88e712c74
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV2Test.java
@@ -0,0 +1,748 @@
+/*
+ * Copyright (c) 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.eclipse.jetty.spdy.http;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.InetSocketAddress;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.server.Request;
+import org.eclipse.jetty.server.handler.AbstractHandler;
+import org.eclipse.jetty.spdy.AsyncConnectionFactory;
+import org.eclipse.jetty.spdy.SPDYServerConnector;
+import org.eclipse.jetty.spdy.api.DataInfo;
+import org.eclipse.jetty.spdy.api.Headers;
+import org.eclipse.jetty.spdy.api.ReplyInfo;
+import org.eclipse.jetty.spdy.api.Session;
+import org.eclipse.jetty.spdy.api.SessionFrameListener;
+import org.eclipse.jetty.spdy.api.Stream;
+import org.eclipse.jetty.spdy.api.StreamFrameListener;
+import org.eclipse.jetty.spdy.api.SynInfo;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class ReferrerPushStrategyV2Test extends AbstractHTTPSPDYTest
+{
+ @Override
+ protected SPDYServerConnector newHTTPSPDYServerConnector(short version)
+ {
+ SPDYServerConnector connector = super.newHTTPSPDYServerConnector(version);
+ AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version, connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, new ReferrerPushStrategy());
+ connector.setDefaultAsyncConnectionFactory(defaultFactory);
+ return connector;
+ }
+
+ @Test
+ public void testMaxAssociatedResources() throws Exception
+ {
+ InetSocketAddress address = startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ output.print("HELLO");
+ else if (url.endsWith(".css"))
+ output.print("body { background: #FFF; }");
+ else if (url.endsWith(".js"))
+ output.print("function(){}();");
+ baseRequest.setHandled(true);
+ }
+ });
+ ReferrerPushStrategy pushStrategy = new ReferrerPushStrategy();
+ pushStrategy.setMaxAssociatedResources(1);
+ AsyncConnectionFactory defaultFactory = new ServerHTTPSPDYAsyncConnectionFactory(version(), connector.getByteBufferPool(), connector.getExecutor(), connector.getScheduler(), connector, pushStrategy);
+ connector.setDefaultAsyncConnectionFactory(defaultFactory);
+
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ Headers mainRequestHeaders = new Headers();
+ mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ String mainResource = "/index.html";
+ mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
+ mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch1 = new CountDownLatch(1);
+ Headers associatedRequestHeaders1 = new Headers();
+ associatedRequestHeaders1.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ associatedRequestHeaders1.put(HTTPSPDYHeader.URI.name(version()), "/style.css");
+ associatedRequestHeaders1.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ associatedRequestHeaders1.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ associatedRequestHeaders1.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ associatedRequestHeaders1.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
+ session1.syn(new SynInfo(associatedRequestHeaders1, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch1.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch1.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch2 = new CountDownLatch(1);
+ Headers associatedRequestHeaders2 = new Headers();
+ associatedRequestHeaders2.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ associatedRequestHeaders2.put(HTTPSPDYHeader.URI.name(version()), "/application.js");
+ associatedRequestHeaders2.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ associatedRequestHeaders2.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ associatedRequestHeaders2.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ associatedRequestHeaders2.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
+ session1.syn(new SynInfo(associatedRequestHeaders2, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch2.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch2.await(5, TimeUnit.SECONDS));
+
+ // Create another client, and perform the same request for the main resource,
+ // we expect the css being pushed, but not the js
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushDataLatch = new CountDownLatch(1);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Assert.assertTrue(stream.isUnidirectional());
+ Assert.assertTrue(synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version())).value().endsWith(".css"));
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ pushDataLatch.countDown();
+ }
+ };
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testAssociatedResourceIsPushed() throws Exception
+ {
+ InetSocketAddress address = startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ output.print("HELLO");
+ else if (url.endsWith(".css"))
+ output.print("body { background: #FFF; }");
+ baseRequest.setHandled(true);
+ }
+ });
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ Headers mainRequestHeaders = new Headers();
+ mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ String mainResource = "/index.html";
+ mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
+ mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
+ Headers associatedRequestHeaders = new Headers();
+ associatedRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/style.css");
+ associatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ associatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ associatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
+ session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ // Create another client, and perform the same request for the main resource, we expect the css being pushed
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushDataLatch = new CountDownLatch(1);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Assert.assertTrue(stream.isUnidirectional());
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ pushDataLatch.countDown();
+ }
+ };
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testAssociatedResourceWithWrongContentTypeIsNotPushed() throws Exception
+ {
+ InetSocketAddress address = startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ {
+ response.setContentType("text/html");
+ output.print("HELLO");
+ }
+ else if (url.equals("/fake.png"))
+ {
+ response.setContentType("text/html");
+ output.print("IMAGE");
+ }
+ else if (url.endsWith(".css"))
+ {
+ response.setContentType("text/css");
+ output.print("body { background: #FFF; }");
+ }
+ baseRequest.setHandled(true);
+ }
+ });
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ Headers mainRequestHeaders = new Headers();
+ mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ String mainResource = "/index.html";
+ mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
+ mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
+ Headers associatedRequestHeaders = new Headers();
+ associatedRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/stylesheet.css");
+ associatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ associatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ associatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
+ session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch fakeAssociatedResourceLatch = new CountDownLatch(1);
+ Headers fakeAssociatedRequestHeaders = new Headers();
+ fakeAssociatedRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ fakeAssociatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/fake.png");
+ fakeAssociatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ fakeAssociatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ fakeAssociatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ fakeAssociatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
+ session1.syn(new SynInfo(fakeAssociatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ fakeAssociatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(fakeAssociatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ // Create another client, and perform the same request for the main resource,
+ // we expect the css being pushed but not the fake PNG
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushDataLatch = new CountDownLatch(1);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Assert.assertTrue(stream.isUnidirectional());
+ Assert.assertTrue(synInfo.getHeaders().get(HTTPSPDYHeader.URI.name(version())).value().endsWith(".css"));
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ pushDataLatch.countDown();
+ }
+ };
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testNestedAssociatedResourceIsPushed() throws Exception
+ {
+ InetSocketAddress address = startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ output.print("HELLO");
+ else if (url.endsWith(".css"))
+ output.print("body { background: #FFF; }");
+ else if (url.endsWith(".gif"))
+ output.print("\u0000");
+ baseRequest.setHandled(true);
+ }
+ });
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ Headers mainRequestHeaders = new Headers();
+ mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ String mainResource = "/index.html";
+ mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
+ mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
+ Headers associatedRequestHeaders = new Headers();
+ associatedRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ String associatedResource = "/style.css";
+ associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), associatedResource);
+ associatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ associatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ associatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
+ session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch nestedResourceLatch = new CountDownLatch(1);
+ Headers nestedRequestHeaders = new Headers();
+ nestedRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ nestedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/image.gif");
+ nestedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ nestedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ nestedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ nestedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + associatedResource);
+ session1.syn(new SynInfo(nestedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ nestedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(nestedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ // Create another client, and perform the same request for the main resource, we expect the css and the image being pushed
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushDataLatch = new CountDownLatch(2);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Assert.assertTrue(stream.isUnidirectional());
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ pushDataLatch.countDown();
+ }
+ };
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertTrue(pushDataLatch.await(5, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testMainResourceWithReferrerIsNotPushed() throws Exception
+ {
+ InetSocketAddress address = startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ output.print("HELLO");
+ baseRequest.setHandled(true);
+ }
+ });
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ Headers mainRequestHeaders = new Headers();
+ mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ String mainResource = "/index.html";
+ mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
+ mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
+ Headers associatedRequestHeaders = new Headers();
+ associatedRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/home.html");
+ associatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ associatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ associatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
+ session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ // Create another client, and perform the same request for the main resource, we expect nothing being pushed
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushLatch = new CountDownLatch(1);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ pushLatch.countDown();
+ return null;
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertFalse(pushLatch.await(1, TimeUnit.SECONDS));
+ }
+
+ @Test
+ public void testRequestWithIfModifiedSinceHeaderPreventsPush() throws Exception
+ {
+ InetSocketAddress address = startHTTPServer(version(), new AbstractHandler()
+ {
+ @Override
+ public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
+ {
+ String url = request.getRequestURI();
+ PrintWriter output = response.getWriter();
+ if (url.endsWith(".html"))
+ output.print("HELLO");
+ else if (url.endsWith(".css"))
+ output.print("body { background: #FFF; }");
+ baseRequest.setHandled(true);
+ }
+ });
+ Session session1 = startClient(version(), address, null);
+
+ final CountDownLatch mainResourceLatch = new CountDownLatch(1);
+ Headers mainRequestHeaders = new Headers();
+ mainRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ String mainResource = "/index.html";
+ mainRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), mainResource);
+ mainRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ mainRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ mainRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ mainRequestHeaders.put("If-Modified-Since", "Tue, 27 Mar 2012 16:36:52 GMT");
+ session1.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(mainResourceLatch.await(5, TimeUnit.SECONDS));
+
+ final CountDownLatch associatedResourceLatch = new CountDownLatch(1);
+ Headers associatedRequestHeaders = new Headers();
+ associatedRequestHeaders.put(HTTPSPDYHeader.METHOD.name(version()), "GET");
+ associatedRequestHeaders.put(HTTPSPDYHeader.URI.name(version()), "/style.css");
+ associatedRequestHeaders.put(HTTPSPDYHeader.VERSION.name(version()), "HTTP/1.1");
+ associatedRequestHeaders.put(HTTPSPDYHeader.SCHEME.name(version()), "http");
+ associatedRequestHeaders.put(HTTPSPDYHeader.HOST.name(version()), "localhost:" + connector.getLocalPort());
+ associatedRequestHeaders.put("referer", "http://localhost:" + connector.getLocalPort() + mainResource);
+ session1.syn(new SynInfo(associatedRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ associatedResourceLatch.countDown();
+ }
+ });
+ Assert.assertTrue(associatedResourceLatch.await(5, TimeUnit.SECONDS));
+
+ // Create another client, and perform the same request for the main resource, we expect the css NOT being pushed as the main request contains an
+ // if-modified-since header
+
+ final CountDownLatch mainStreamLatch = new CountDownLatch(2);
+ final CountDownLatch pushDataLatch = new CountDownLatch(1);
+ Session session2 = startClient(version(), address, new SessionFrameListener.Adapter()
+ {
+ @Override
+ public StreamFrameListener onSyn(Stream stream, SynInfo synInfo)
+ {
+ Assert.assertTrue(stream.isUnidirectional());
+ return new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ pushDataLatch.countDown();
+ }
+ };
+ }
+ });
+ session2.syn(new SynInfo(mainRequestHeaders, true), new StreamFrameListener.Adapter()
+ {
+ @Override
+ public void onReply(Stream stream, ReplyInfo replyInfo)
+ {
+ Assert.assertFalse(replyInfo.isClose());
+ mainStreamLatch.countDown();
+ }
+
+ @Override
+ public void onData(Stream stream, DataInfo dataInfo)
+ {
+ dataInfo.consume(dataInfo.length());
+ if (dataInfo.isClose())
+ mainStreamLatch.countDown();
+ }
+ });
+
+ Assert.assertTrue(mainStreamLatch.await(5, TimeUnit.SECONDS));
+ Assert.assertFalse("We don't expect data to be pushed as the main request contained an if-modified-since header",pushDataLatch.await(1, TimeUnit.SECONDS));
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV3Test.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV3Test.java
new file mode 100644
index 00000000000..a722fde9f46
--- /dev/null
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ReferrerPushStrategyV3Test.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2012 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.eclipse.jetty.spdy.http;
+
+import org.eclipse.jetty.spdy.api.SPDY;
+
+public class ReferrerPushStrategyV3Test extends ReferrerPushStrategyV2Test
+{
+ @Override
+ protected short version()
+ {
+ return SPDY.V3;
+ }
+}
diff --git a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv2Test.java b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv2Test.java
index 3363d124a50..61ebfd127d9 100644
--- a/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv2Test.java
+++ b/jetty-spdy/spdy-jetty-http/src/test/java/org/eclipse/jetty/spdy/http/ServerHTTPSPDYv2Test.java
@@ -41,7 +41,6 @@ import org.eclipse.jetty.spdy.api.BytesDataInfo;
import org.eclipse.jetty.spdy.api.DataInfo;
import org.eclipse.jetty.spdy.api.Headers;
import org.eclipse.jetty.spdy.api.ReplyInfo;
-import org.eclipse.jetty.spdy.api.SPDY;
import org.eclipse.jetty.spdy.api.Session;
import org.eclipse.jetty.spdy.api.Stream;
import org.eclipse.jetty.spdy.api.StreamFrameListener;
@@ -52,11 +51,6 @@ import org.junit.Test;
public class ServerHTTPSPDYv2Test extends AbstractHTTPSPDYTest
{
- protected short version()
- {
- return SPDY.V2;
- }
-
@Test
public void testSimpleGET() throws Exception
{
diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
index db5ec955afa..51800f4d7c6 100644
--- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
+++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/StandardDescriptorProcessor.java
@@ -239,23 +239,26 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
String servlet_class = node.getString("servlet-class", false, true);
// Handle JSP
- String jspServletName=null;
String jspServletClass=null;;
- boolean hasJSP=false;
+
+ //Handle the default jsp servlet instance
if (id != null && id.equals("jsp"))
{
- jspServletName = servlet_name;
jspServletClass = servlet_class;
try
{
Loader.loadClass(this.getClass(), servlet_class);
- hasJSP = true;
}
catch (ClassNotFoundException e)
{
LOG.info("NO JSP Support for {}, did not find {}", context.getContextPath(), servlet_class);
jspServletClass = servlet_class = "org.eclipse.jetty.servlet.NoJspServlet";
}
+ }
+
+ //could be more than 1 declaration of the jsp servlet, so configure them all
+ if (servlet_class != null && "org.apache.jasper.servlet.JspServlet".equals(servlet_class))
+ {
if (holder.getInitParameter("scratchdir") == null)
{
File tmp = context.getTempDirectory();
@@ -316,12 +319,12 @@ public class StandardDescriptorProcessor extends IterativeDescriptorProcessor
}
}
- // Handler JSP file
+ // Handle JSP file
String jsp_file = node.getString("jsp-file", false, true);
if (jsp_file != null)
{
holder.setForcedPath(jsp_file);
- holder.setClassName(jspServletClass);
+ holder.setClassName(jspServletClass); //only use our default instance
//set the system classpath explicitly for the holder that will represent the JspServlet instance
holder.setInitParameter("com.sun.appserv.jsp.classpath", getSystemClassPath(context));
}