Updates to the SPDY push implementation.
This commit is contained in:
parent
3ce07230d5
commit
f75a9d83f1
|
@ -29,6 +29,7 @@ import org.eclipse.jetty.util.ssl.SslContextFactory;
|
|||
public class HTTPSPDYServerConnector extends SPDYServerConnector
|
||||
{
|
||||
private final AsyncConnectionFactory defaultConnectionFactory;
|
||||
private final PushStrategy pushStrategy = new PushStrategy.None();
|
||||
|
||||
public HTTPSPDYServerConnector()
|
||||
{
|
||||
|
@ -47,7 +48,7 @@ public class HTTPSPDYServerConnector extends SPDYServerConnector
|
|||
{
|
||||
super.doStart();
|
||||
// Override the "spdy/2" protocol by handling HTTP over SPDY
|
||||
putAsyncConnectionFactory("spdy/2", new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this));
|
||||
putAsyncConnectionFactory("spdy/2", new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, pushStrategy));
|
||||
// Add the "http/1.1" protocol for browsers that do not support NPN
|
||||
putAsyncConnectionFactory("http/1.1", new ServerHTTPAsyncConnectionFactory(this));
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
package org.eclipse.jetty.spdy.http;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
|
@ -27,4 +28,13 @@ import org.eclipse.jetty.spdy.api.Stream;
|
|||
public interface PushStrategy
|
||||
{
|
||||
public Set<String> apply(Stream stream, Headers requestHeaders, Headers responseHeaders);
|
||||
|
||||
public static class None implements PushStrategy
|
||||
{
|
||||
@Override
|
||||
public Set<String> apply(Stream stream, Headers requestHeaders, Headers responseHeaders)
|
||||
{
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
* 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.util.ArrayList;
|
||||
|
@ -13,8 +29,24 @@ 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 we
|
||||
* use to link the associated resource to the main resource.</p>
|
||||
* <p>However, also following a hyperlink generates a HTTP request with a <tt>Referer</tt>
|
||||
* HTTP header that points to <tt>index.html</tt>; therefore main resources and associated
|
||||
* resources must be distinguishable.</p>
|
||||
* <p>This class distinguishes associated resources by their URL path suffix.
|
||||
* CSS stylesheets, images and JavaScript files have recognizable URL path suffixes that
|
||||
* are classified as associated resources.</p>
|
||||
* <p>Note however, that CSS stylesheets may refer to images, and the CSS image request
|
||||
* will have the CSS stylesheet as referrer, so there is some degree of recursion that
|
||||
* needs to be handled.</p>
|
||||
*
|
||||
*
|
||||
* TODO: this class is kind-of leaking since the resources map is always adding entries
|
||||
* TODO: although these entries will be limited by the application pages
|
||||
* 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
|
||||
*/
|
||||
|
@ -31,17 +63,17 @@ public class ReferrerPushStrategy implements PushStrategy
|
|||
String url = requestHeaders.get("url").value();
|
||||
if (!hasQueryString(url))
|
||||
{
|
||||
if (isMainResource(url))
|
||||
if (isMainResource(url, responseHeaders))
|
||||
{
|
||||
return pushResources(url);
|
||||
}
|
||||
else if (isPushResource(url))
|
||||
else if (isPushResource(url, responseHeaders))
|
||||
{
|
||||
String referrer = requestHeaders.get("referer").value();
|
||||
Set<String> pushResources = resources.get(referrer);
|
||||
if (pushResources == null || !pushResources.contains(url))
|
||||
{
|
||||
buildPushResources(url, referrer);
|
||||
buildMetadata(url, referrer);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -57,13 +89,13 @@ public class ReferrerPushStrategy implements PushStrategy
|
|||
return url.contains("?");
|
||||
}
|
||||
|
||||
private boolean isMainResource(String url)
|
||||
private boolean isMainResource(String url, Headers responseHeaders)
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isPushResource(String url)
|
||||
private boolean isPushResource(String url, Headers responseHeaders)
|
||||
{
|
||||
// TODO
|
||||
return false;
|
||||
|
@ -77,7 +109,7 @@ public class ReferrerPushStrategy implements PushStrategy
|
|||
return Collections.unmodifiableSet(pushResources);
|
||||
}
|
||||
|
||||
private void buildPushResources(String url, String referrer)
|
||||
private void buildMetadata(String url, String referrer)
|
||||
{
|
||||
Set<String> pushResources = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
|
||||
Set<String> existing = resources.putIfAbsent(referrer, pushResources);
|
||||
|
|
|
@ -48,6 +48,7 @@ import org.eclipse.jetty.server.Server;
|
|||
import org.eclipse.jetty.spdy.SPDYAsyncConnection;
|
||||
import org.eclipse.jetty.spdy.api.ByteBufferDataInfo;
|
||||
import org.eclipse.jetty.spdy.api.DataInfo;
|
||||
import org.eclipse.jetty.spdy.api.Handler;
|
||||
import org.eclipse.jetty.spdy.api.Headers;
|
||||
import org.eclipse.jetty.spdy.api.ReplyInfo;
|
||||
import org.eclipse.jetty.spdy.api.Stream;
|
||||
|
@ -64,6 +65,7 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
private final Queue<Runnable> tasks = new LinkedList<>();
|
||||
private final BlockingQueue<DataInfo> dataInfos = new LinkedBlockingQueue<>();
|
||||
private final SPDYAsyncConnection connection;
|
||||
private final PushStrategy pushStrategy;
|
||||
private final Stream stream;
|
||||
private Headers headers; // No need for volatile, guarded by state
|
||||
private DataInfo dataInfo; // No need for volatile, guarded by state
|
||||
|
@ -71,10 +73,11 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
private volatile State state = State.INITIAL;
|
||||
private boolean dispatched; // Guarded by synchronization on tasks
|
||||
|
||||
public ServerHTTPSPDYAsyncConnection(Connector connector, AsyncEndPoint endPoint, Server server, SPDYAsyncConnection connection, Stream stream)
|
||||
public ServerHTTPSPDYAsyncConnection(Connector connector, AsyncEndPoint endPoint, Server server, SPDYAsyncConnection connection, PushStrategy pushStrategy, Stream stream)
|
||||
{
|
||||
super(connector, endPoint, server);
|
||||
this.connection = connection;
|
||||
this.pushStrategy = pushStrategy;
|
||||
this.stream = stream;
|
||||
getParser().setPersistent(true);
|
||||
}
|
||||
|
@ -380,17 +383,23 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
Set<String> pushResources = pushStrategy.apply(stream, this.headers, replyInfo.getHeaders());
|
||||
for (String url : pushResources)
|
||||
{
|
||||
Headers pushHeaders = new Headers();
|
||||
final Headers pushHeaders = new Headers();
|
||||
pushHeaders.put("method", "GET");
|
||||
pushHeaders.put("url", url);
|
||||
pushHeaders.put("version", "HTTP/1.1");
|
||||
Headers.Header acceptEncoding = headers.get("accept-encoding");
|
||||
if (acceptEncoding != null)
|
||||
pushHeaders.put(acceptEncoding);
|
||||
Stream pushStream = stream.syn(new SynInfo(pushHeaders, true));
|
||||
Synchronous connection = new Synchronous(getConnector(), getEndPoint(), getServer(), , pushStream);
|
||||
connection.beginRequest(pushHeaders);
|
||||
connection.endRequest();
|
||||
stream.syn(new SynInfo(pushHeaders, false), getMaxIdleTime(), TimeUnit.MILLISECONDS, new Handler.Adapter<Stream>()
|
||||
{
|
||||
@Override
|
||||
public void completed(Stream pushStream)
|
||||
{
|
||||
Synchronous pushConnection = new Synchronous(getConnector(), getEndPoint(), getServer(), connection, pushStrategy, pushStream);
|
||||
pushConnection.beginRequest(pushHeaders);
|
||||
pushConnection.endRequest();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -709,9 +718,9 @@ public class ServerHTTPSPDYAsyncConnection extends AbstractHttpConnection implem
|
|||
|
||||
private static class Synchronous extends ServerHTTPSPDYAsyncConnection
|
||||
{
|
||||
private Synchronous(Connector connector, AsyncEndPoint endPoint, Server server, SPDYAsyncConnection connection, Stream stream)
|
||||
private Synchronous(Connector connector, AsyncEndPoint endPoint, Server server, SPDYAsyncConnection connection, PushStrategy pushStrategy, Stream stream)
|
||||
{
|
||||
super(connector, endPoint, server, connection, stream);
|
||||
super(connector, endPoint, server, connection, pushStrategy, stream);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -42,11 +42,13 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
|
|||
private static final Logger logger = Log.getLogger(ServerHTTPSPDYAsyncConnectionFactory.class);
|
||||
|
||||
private final Connector connector;
|
||||
private final PushStrategy pushStrategy;
|
||||
|
||||
public ServerHTTPSPDYAsyncConnectionFactory(short version, ByteBufferPool bufferPool, Executor threadPool, ScheduledExecutorService scheduler, Connector connector)
|
||||
public ServerHTTPSPDYAsyncConnectionFactory(short version, ByteBufferPool bufferPool, Executor threadPool, ScheduledExecutorService scheduler, Connector connector, PushStrategy pushStrategy)
|
||||
{
|
||||
super(version, bufferPool, threadPool, scheduler);
|
||||
this.connector = connector;
|
||||
this.pushStrategy = pushStrategy;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -77,8 +79,8 @@ public class ServerHTTPSPDYAsyncConnectionFactory extends ServerSPDYAsyncConnect
|
|||
|
||||
HTTPSPDYAsyncEndPoint asyncEndPoint = new HTTPSPDYAsyncEndPoint(endPoint, stream);
|
||||
ServerHTTPSPDYAsyncConnection connection = new ServerHTTPSPDYAsyncConnection(connector,
|
||||
asyncEndPoint, connector.getServer(),
|
||||
(SPDYAsyncConnection)endPoint.getConnection(), stream);
|
||||
asyncEndPoint, connector.getServer(), (SPDYAsyncConnection)endPoint.getConnection(),
|
||||
pushStrategy, stream);
|
||||
asyncEndPoint.setConnection(connection);
|
||||
stream.setAttribute(CONNECTION_ATTRIBUTE, connection);
|
||||
|
||||
|
|
|
@ -72,7 +72,7 @@ public abstract class AbstractHTTPSPDYTest
|
|||
@Override
|
||||
protected AsyncConnectionFactory getDefaultAsyncConnectionFactory()
|
||||
{
|
||||
return new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this);
|
||||
return new ServerHTTPSPDYAsyncConnectionFactory(SPDY.V2, getByteBufferPool(), getExecutor(), getScheduler(), this, new PushStrategy.None());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue