Further experimentation with PushBuilder

This commit is contained in:
Greg Wilkins 2014-12-09 12:27:39 +01:00
parent 06b1efc182
commit dfe9dc3115
10 changed files with 115 additions and 30 deletions

View File

@ -79,10 +79,12 @@ public class HttpTransportOverFCGI implements HttpTransport
}
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.server.HttpTransport#push(org.eclipse.jetty.http.MetaData.Request)
*/
@Override
public boolean isPushSupported()
{
return false;
}
@Override
public void push(org.eclipse.jetty.http.MetaData.Request request)
{

View File

@ -94,15 +94,15 @@ public class HttpFields implements Iterable<HttpField>
/**
* Get Collection of header names.
*/
public Collection<String> getFieldNamesCollection()
public Set<String> getFieldNamesCollection()
{
final Set<String> list = new HashSet<>(_size);
final Set<String> set = new HashSet<>(_size);
for (HttpField f : this)
{
if (f!=null)
list.add(f.getName());
set.add(f.getName());
}
return list;
return set;
}
/**

View File

@ -113,6 +113,12 @@ public class HttpTransportOverHTTP2 implements HttpTransport
}
}
@Override
public boolean isPushSupported()
{
return stream.getSession().isPushEnabled();
}
@Override
public void push(final MetaData.Request request)
{

View File

@ -662,6 +662,12 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http
getEndPoint().close();
}
@Override
public boolean isPushSupported()
{
return false;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.server.HttpTransport#push(org.eclipse.jetty.http.MetaData.Request)

View File

@ -20,15 +20,16 @@ package org.eclipse.jetty.server;
import java.nio.ByteBuffer;
import org.eclipse.jetty.http.HttpGenerator;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.util.Callback;
public interface HttpTransport
{
void send(MetaData.Response info, boolean head, ByteBuffer content, boolean lastContent, Callback callback);
boolean isPushSupported();
void push (MetaData.Request request);
void push(MetaData.Request request);
void completed();

View File

@ -19,6 +19,7 @@
package org.eclipse.jetty.server;
import java.util.Collection;
import java.util.Set;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
@ -57,36 +58,46 @@ public class PushBuilder
return _method;
}
public void setMethod(String method)
public PushBuilder setMethod(String method)
{
_method = method;
return this;
}
public String getQueryString()
{
return _queryString;
}
public void setQueryString(String queryString)
public PushBuilder setQueryString(String queryString)
{
_queryString = queryString;
return this;
}
public String getSessionId()
{
return _sessionId;
}
public void setSessionId(String sessionId)
public PushBuilder setSessionId(String sessionId)
{
_sessionId = sessionId;
return this;
}
public boolean isConditional()
{
return _conditional;
}
public void setConditional(boolean conditional)
public PushBuilder setConditional(boolean conditional)
{
_conditional = conditional;
return this;
}
public Collection<String> getHeaderNames()
public Set<String> getHeaderNames()
{
return _fields.getFieldNamesCollection();
}
@ -96,15 +107,27 @@ public class PushBuilder
return _fields.get(name);
}
public void setHeader(String name,String value)
public PushBuilder setHeader(String name,String value)
{
_fields.put(name,value);
return this;
}
public void addHeader(String name,String value)
{
_fields.add(name,value);
}
/* ------------------------------------------------------------ */
/** Push a resource.
* Equivalent to {@link #push(String, String, String)} with null etag and
* lastModified.
* @param uriInContext
*/
public void push(String uriInContext)
{
push(uriInContext,null,null);
}
/* ------------------------------------------------------------ */
/** Push a resource.
@ -152,6 +175,4 @@ public class PushBuilder
_request.getHttpChannel().getHttpTransport().push(push);
}
}

View File

@ -213,6 +213,18 @@ public class Request implements HttpServletRequest
{
return _input;
}
/* ------------------------------------------------------------ */
public boolean isPush()
{
return Boolean.TRUE.equals(getAttribute("org.eclipse.jetty.pushed"));
}
/* ------------------------------------------------------------ */
public boolean isPushSupported()
{
return getHttpChannel().getHttpTransport().isPushSupported();
}
/* ------------------------------------------------------------ */
/** Get a PushBuilder associated with this request initialized as follows:<ul>
@ -253,6 +265,9 @@ public class Request implements HttpServletRequest
*/
public PushBuilder getPushBuilder()
{
if (!isPushSupported())
throw new IllegalStateException();
HttpFields fields = new HttpFields(getHttpFields().size()+5);
boolean conditional=false;
UserIdentity user_identity=null;

View File

@ -29,6 +29,7 @@ import java.util.Collections;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Locale;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.Cookie;
@ -89,6 +90,12 @@ public class ResponseTest
callback.succeeded();
}
@Override
public boolean isPushSupported()
{
return false;
}
@Override
public void push(org.eclipse.jetty.http.MetaData.Request request)
{

View File

@ -50,6 +50,7 @@ import org.eclipse.jetty.util.log.Logger;
public class PushSessionCacheFilter implements Filter
{
private static final String TARGET_ATTR="PushCacheFilter.target";
private static final String TIMESTAMP_ATTR="PushCacheFilter.timestamp";
private static final Logger LOG = Log.getLogger(PushSessionCacheFilter.class);
private final ConcurrentMap<String, Target> _cache = new ConcurrentHashMap<>();
@ -65,8 +66,11 @@ public class PushSessionCacheFilter implements Filter
if (config.getInitParameter("associateDelay")!=null)
_associateDelay=Long.valueOf(config.getInitParameter("associateDelay"));
// Add a listener that is used to collect information about associated resource,
// etags and modified dates
config.getServletContext().addListener(new ServletRequestListener()
{
// Collect information when request is destroyed.
@Override
public void requestDestroyed(ServletRequestEvent sre)
{
@ -93,8 +97,9 @@ public class PushSessionCacheFilter implements Filter
Target referer_target = _cache.get(path_in_ctx);
if (referer_target!=null)
{
String sessionId = request.getSession(true).getId();
Long last = referer_target._timestamp.get(sessionId);
HttpSession session = request.getSession();
ConcurrentHashMap<String, Long> timestamps = (ConcurrentHashMap<String, Long>)session.getAttribute(TIMESTAMP_ATTR);
Long last = timestamps.get(referer_target._path);
if (last!=null && (System.currentTimeMillis()-last)<_associateDelay && !referer_target._associated.containsKey(path))
{
if (referer_target._associated.putIfAbsent(path,target)==null)
@ -121,8 +126,15 @@ public class PushSessionCacheFilter implements Filter
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
// Get Jetty request as these APIs are not yet standard
Request baseRequest = Request.getBaseRequest(request);
if (baseRequest.isPush())
{
LOG.info("PUSH {} if modified since {}",baseRequest,baseRequest.getHttpFields().get("If-Modified-Since"));
}
// Iterating over fields is more efficient than multiple gets
HttpFields fields = baseRequest.getHttpFields();
String referer=fields.get(HttpHeader.REFERER);
@ -130,7 +142,6 @@ public class PushSessionCacheFilter implements Filter
if (LOG.isDebugEnabled())
LOG.debug("{} {} referer={}%n",baseRequest.getMethod(),baseRequest.getRequestURI(),referer);
HttpSession session = baseRequest.getSession(true);
String sessionId = session.getId();
String path = URIUtil.addPaths(baseRequest.getServletPath(),baseRequest.getPathInfo());
@ -143,22 +154,27 @@ public class PushSessionCacheFilter implements Filter
target = _cache.putIfAbsent(path,t);
target = target==null?t:target;
}
target._timestamp.put(sessionId,System.currentTimeMillis());
ConcurrentHashMap<String, Long> timestamps = (ConcurrentHashMap<String, Long>)session.getAttribute(TIMESTAMP_ATTR);
if (timestamps==null)
{
timestamps=new ConcurrentHashMap<>();
session.setAttribute(TIMESTAMP_ATTR,timestamps);
}
timestamps.put(path,System.currentTimeMillis());
request.setAttribute(TARGET_ATTR,target);
// push any associated resources
if (target._associated.size()>0)
if (baseRequest.isPushSupported() && target._associated.size()>0)
{
PushBuilder builder = baseRequest.getPushBuilder();
if (!session.isNew())
builder.setConditional(true);
if (builder!=null)
for (Target associated : target._associated.values())
{
for (Target associated : target._associated.values())
{
LOG.info("PUSH {}->{}",path,associated._path);
builder.push(associated._path,associated._etag,associated._lastModified);
}
LOG.info("PUSH {}->{}",path,associated);
builder.push(associated._path,associated._etag,associated._lastModified);
}
}
@ -180,7 +196,6 @@ public class PushSessionCacheFilter implements Filter
{
final String _path;
final ConcurrentMap<String,Target> _associated = new ConcurrentHashMap<>();
final ConcurrentMap<String,Long> _timestamp = new ConcurrentHashMap<>();
volatile String _etag;
volatile String _lastModified;
@ -189,5 +204,10 @@ public class PushSessionCacheFilter implements Filter
_path=path;
}
@Override
public String toString()
{
return String.format("Target(p=%s,e=%s,m=%s)->%s",_path,_etag,_lastModified,_associated);
}
}
}

View File

@ -161,6 +161,13 @@ public class HttpTransportOverSPDY implements HttpTransport
}
/* ------------------------------------------------------------ */
@Override
public boolean isPushSupported()
{
return false;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.server.HttpTransport#push(org.eclipse.jetty.http.MetaData.Request)