diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java index 9d2869a1d3d..a2747fa45f3 100644 --- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java +++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java @@ -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) { diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index 3965dd0bdb7..1f47341f32a 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java @@ -94,15 +94,15 @@ public class HttpFields implements Iterable /** * Get Collection of header names. */ - public Collection getFieldNamesCollection() + public Set getFieldNamesCollection() { - final Set list = new HashSet<>(_size); + final Set set = new HashSet<>(_size); for (HttpField f : this) { if (f!=null) - list.add(f.getName()); + set.add(f.getName()); } - return list; + return set; } /** diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java index 3c5b259e5a7..ef92ffcb43e 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java @@ -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) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 6ec0d29dc1d..b705b0fde5a 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -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) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java index d4233744d2e..493acf45f14 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java @@ -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(); diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java index 3025e83c5ea..098aac17e82 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/PushBuilder.java @@ -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 getHeaderNames() + public Set 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); } - - } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index a4f05a4c544..73767d7211b 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -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:
    @@ -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; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java index 89d1f32c623..b7c8e5ac46e 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java @@ -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) { diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java index 1d2885cb265..39e0dd70915 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/PushSessionCacheFilter.java @@ -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 _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 timestamps = (ConcurrentHashMap)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 timestamps = (ConcurrentHashMap)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 _associated = new ConcurrentHashMap<>(); - final ConcurrentMap _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); + } } } diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java index b880d12dc4d..e64f201526f 100644 --- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java +++ b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java @@ -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)