Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-10.0.x

This commit is contained in:
gregw 2020-10-05 13:57:20 +02:00
commit 911173f8f7
3 changed files with 128 additions and 249 deletions

View File

@ -205,7 +205,7 @@ public class Request implements HttpServletRequest
private String _pathInContext;
private ServletPathMapping _servletPathMapping;
private boolean _secure;
private String _asyncNotSupportedSource = null;
private Object _asyncNotSupportedSource = null;
private boolean _newContext;
private boolean _cookiesExtracted = false;
private boolean _handled = false;
@ -1845,7 +1845,7 @@ public class Request implements HttpServletRequest
_requestAttributeListeners.remove(listener);
}
public void setAsyncSupported(boolean supported, String source)
public void setAsyncSupported(boolean supported, Object source)
{
_asyncNotSupportedSource = supported ? null : (source == null ? "unknown" : source);
}

View File

@ -35,6 +35,7 @@ import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.component.Dumpable;
import org.eclipse.jetty.util.component.DumpableCollection;
@ -200,11 +201,24 @@ public class FilterHolder extends Holder<Filter>
return _filter;
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain)
throws IOException, ServletException
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
{
_filter.doFilter(request, response, chain);
if (isAsyncSupported() || !request.isAsyncSupported())
getFilter().doFilter(request, response, chain);
else
{
Request baseRequest = Request.getBaseRequest(request);
Objects.requireNonNull(baseRequest);
try
{
baseRequest.setAsyncSupported(false, this);
getFilter().doFilter(request, response, chain);
}
finally
{
baseRequest.setAsyncSupported(true, null);
}
}
}
@Override

View File

@ -27,10 +27,9 @@ import java.util.EventListener;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentMap;
import java.util.function.Consumer;
import java.util.stream.Stream;
@ -62,7 +61,6 @@ import org.eclipse.jetty.server.UserIdentity;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ScopedHandler;
import org.eclipse.jetty.util.ArrayUtil;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
@ -99,7 +97,7 @@ public class ServletHandler extends ScopedHandler
private int _matchBeforeIndex = -1; //index of last programmatic FilterMapping with isMatchAfter=false
private int _matchAfterIndex = -1; //index of 1st programmatic FilterMapping with isMatchAfter=true
private boolean _filterChainsCached = true;
private int _maxFilterChainsCacheSize = 512;
private int _maxFilterChainsCacheSize = 1024;
private boolean _startWithUnavailable = false;
private boolean _ensureDefaultServlet = true;
private IdentityService _identityService;
@ -110,6 +108,7 @@ public class ServletHandler extends ScopedHandler
private final Map<String, FilterHolder> _filterNameMap = new HashMap<>();
private List<FilterMapping> _filterPathMappings;
private MultiMap<FilterMapping> _filterNameMappings;
private List<FilterMapping> _wildFilterNameMappings;
private final Map<String, MappedServlet> _servletNameMap = new HashMap<>();
private PathMappings<MappedServlet> _servletPathMap;
@ -120,9 +119,6 @@ public class ServletHandler extends ScopedHandler
@SuppressWarnings("unchecked")
protected final ConcurrentMap<String, FilterChain>[] _chainCache = new ConcurrentMap[FilterMapping.ALL];
@SuppressWarnings("unchecked")
protected final Queue<String>[] _chainLRU = new Queue[FilterMapping.ALL];
/**
* Constructor.
*/
@ -138,7 +134,7 @@ public class ServletHandler extends ScopedHandler
@Override
public boolean isDumpable(Object o)
{
return !(o instanceof Holder || o instanceof BaseHolder || o instanceof FilterMapping || o instanceof ServletMapping);
return !(o instanceof BaseHolder || o instanceof FilterMapping || o instanceof ServletMapping);
}
@Override
@ -187,12 +183,6 @@ public class ServletHandler extends ScopedHandler
_chainCache[FilterMapping.INCLUDE] = new ConcurrentHashMap<>();
_chainCache[FilterMapping.ERROR] = new ConcurrentHashMap<>();
_chainCache[FilterMapping.ASYNC] = new ConcurrentHashMap<>();
_chainLRU[FilterMapping.REQUEST] = new ConcurrentLinkedQueue<>();
_chainLRU[FilterMapping.FORWARD] = new ConcurrentLinkedQueue<>();
_chainLRU[FilterMapping.INCLUDE] = new ConcurrentLinkedQueue<>();
_chainLRU[FilterMapping.ERROR] = new ConcurrentLinkedQueue<>();
_chainLRU[FilterMapping.ASYNC] = new ConcurrentLinkedQueue<>();
}
if (_contextHandler == null)
@ -274,14 +264,14 @@ public class ServletHandler extends ScopedHandler
}
//Retain only filters and mappings that were added using jetty api (ie Source.EMBEDDED)
FilterHolder[] fhs = (FilterHolder[])LazyList.toArray(filterHolders, FilterHolder.class);
FilterHolder[] fhs = filterHolders.toArray(new FilterHolder[0]);
updateBeans(_filters, fhs);
_filters = fhs;
FilterMapping[] fms = (FilterMapping[])LazyList.toArray(filterMappings, FilterMapping.class);
FilterMapping[] fms = filterMappings.toArray(new FilterMapping[0]);
updateBeans(_filterMappings, fms);
_filterMappings = fms;
_matchAfterIndex = (_filterMappings == null || _filterMappings.length == 0 ? -1 : _filterMappings.length - 1);
_matchAfterIndex = (_filterMappings.length == 0 ? -1 : _filterMappings.length - 1);
_matchBeforeIndex = -1;
// Stop servlets
@ -314,10 +304,10 @@ public class ServletHandler extends ScopedHandler
}
//Retain only Servlets and mappings added via jetty apis (ie Source.EMBEDDED)
ServletHolder[] shs = (ServletHolder[])LazyList.toArray(servletHolders, ServletHolder.class);
ServletHolder[] shs = servletHolders.toArray(new ServletHolder[0]);
updateBeans(_servlets, shs);
_servlets = shs;
ServletMapping[] sms = (ServletMapping[])LazyList.toArray(servletMappings, ServletMapping.class);
ServletMapping[] sms = servletMappings.toArray(new ServletMapping[0]);
updateBeans(_servletMappings, sms);
_servletMappings = sms;
@ -343,7 +333,7 @@ public class ServletHandler extends ScopedHandler
listenerHolders.add(listener);
}
}
ListenerHolder[] listeners = (ListenerHolder[])LazyList.toArray(listenerHolders, ListenerHolder.class);
ListenerHolder[] listeners = listenerHolders.toArray(new ListenerHolder[0]);
updateBeans(_listeners, listeners);
_listeners = listeners;
@ -581,85 +571,66 @@ public class ServletHandler extends ScopedHandler
return chain;
}
// Build list of filters (list of FilterHolder objects)
List<FilterHolder> filters = new ArrayList<>();
// Build the filter chain from the inside out.
// ie first wrap the servlet with the last filter to be applied.
// The mappings lists have been reversed to make this simple and fast.
FilterChain chain = null;
// Path filters
if (pathInContext != null && _filterPathMappings != null)
{
for (FilterMapping filterPathMapping : _filterPathMappings)
{
if (filterPathMapping.appliesTo(pathInContext, dispatch))
filters.add(filterPathMapping.getFilterHolder());
}
}
// Servlet name filters
if (servletHolder != null && _filterNameMappings != null && !_filterNameMappings.isEmpty())
{
Object o = _filterNameMappings.get(servletHolder.getName());
if (_wildFilterNameMappings != null)
for (FilterMapping mapping : _wildFilterNameMappings)
chain = newFilterChain(mapping.getFilterHolder(), chain == null ? new ChainEnd(servletHolder) : chain);
for (int i = 0; i < LazyList.size(o); i++)
for (FilterMapping mapping : _filterNameMappings.get(servletHolder.getName()))
{
FilterMapping mapping = LazyList.get(o, i);
if (mapping.appliesTo(dispatch))
filters.add(mapping.getFilterHolder());
}
o = _filterNameMappings.get("*");
for (int i = 0; i < LazyList.size(o); i++)
{
FilterMapping mapping = LazyList.get(o, i);
if (mapping.appliesTo(dispatch))
filters.add(mapping.getFilterHolder());
chain = newFilterChain(mapping.getFilterHolder(), chain == null ? new ChainEnd(servletHolder) : chain);
}
}
if (filters.isEmpty())
return null;
if (pathInContext != null && _filterPathMappings != null)
{
for (FilterMapping mapping : _filterPathMappings)
{
if (mapping.appliesTo(pathInContext, dispatch))
chain = newFilterChain(mapping.getFilterHolder(), chain == null ? new ChainEnd(servletHolder) : chain);
}
}
FilterChain chain = null;
if (_filterChainsCached)
{
chain = newCachedChain(filters, servletHolder);
final Map<String, FilterChain> cache = _chainCache[dispatch];
final Queue<String> lru = _chainLRU[dispatch];
// Do we have too many cached chains?
while (_maxFilterChainsCacheSize > 0 && cache.size() >= _maxFilterChainsCacheSize)
if (_maxFilterChainsCacheSize > 0 && cache.size() >= _maxFilterChainsCacheSize)
{
// The LRU list is not atomic with the cache map, so be prepared to invalidate if
// a key is not found to delete.
// Delete by LRU (where U==created)
String k = lru.poll();
if (k == null)
{
cache.clear();
break;
}
cache.remove(k);
// flush the cache
LOG.debug("{} flushed filter chain cache for {}", this, baseRequest.getDispatcherType());
cache.clear();
}
chain = chain == null ? new ChainEnd(servletHolder) : chain;
// flush the cache
LOG.debug("{} cached filter chain for {}: {}", this, baseRequest.getDispatcherType(), chain);
cache.put(key, chain);
lru.add(key);
}
else
chain = new Chain(baseRequest, filters, servletHolder);
return chain;
}
/**
* Create a FilterChain that calls the passed filter with the passed chain
* @param filterHolder The filter to invoke
* @param chain The chain to pass to the filter
* @return A FilterChain that invokes the filter with the chain
*/
protected FilterChain newFilterChain(FilterHolder filterHolder, FilterChain chain)
{
return new Chain(filterHolder, chain);
}
protected void invalidateChainsCache()
{
if (_chainLRU[FilterMapping.REQUEST] != null)
if (_chainCache[FilterMapping.REQUEST] != null)
{
_chainLRU[FilterMapping.REQUEST].clear();
_chainLRU[FilterMapping.FORWARD].clear();
_chainLRU[FilterMapping.INCLUDE].clear();
_chainLRU[FilterMapping.ERROR].clear();
_chainLRU[FilterMapping.ASYNC].clear();
_chainCache[FilterMapping.REQUEST].clear();
_chainCache[FilterMapping.FORWARD].clear();
_chainCache[FilterMapping.INCLUDE].clear();
@ -810,18 +781,6 @@ public class ServletHandler extends ScopedHandler
return new ListenerHolder(source);
}
/**
* Create a new CachedChain
*
* @param filters the filter chain to be cached as a collection of {@link FilterHolder}
* @param servletHolder the servletHolder
* @return a new {@link CachedChain} instance
*/
public CachedChain newCachedChain(List<FilterHolder> filters, ServletHolder servletHolder)
{
return new CachedChain(filters, servletHolder);
}
/**
* Add a new servlet holder
*
@ -872,6 +831,7 @@ public class ServletHandler extends ScopedHandler
*/
public void addServletWithMapping(ServletHolder servlet, String pathSpec)
{
Objects.requireNonNull(servlet);
ServletHolder[] holders = getServlets();
if (holders != null)
holders = holders.clone();
@ -880,7 +840,7 @@ public class ServletHandler extends ScopedHandler
{
try (AutoLock l = lock())
{
if (servlet != null && !containsServletHolder(servlet))
if (!containsServletHolder(servlet))
setServlets(ArrayUtil.addToArray(holders, servlet, ServletHolder.class));
}
@ -985,6 +945,7 @@ public class ServletHandler extends ScopedHandler
*/
public void addFilterWithMapping(FilterHolder holder, String pathSpec, EnumSet<DispatcherType> dispatches)
{
Objects.requireNonNull(holder);
FilterHolder[] holders = getFilters();
if (holders != null)
holders = holders.clone();
@ -993,7 +954,7 @@ public class ServletHandler extends ScopedHandler
{
try (AutoLock l = lock())
{
if (holder != null && !containsFilterHolder(holder))
if (!containsFilterHolder(holder))
setFilters(ArrayUtil.addToArray(holders, holder, FilterHolder.class));
}
@ -1053,6 +1014,7 @@ public class ServletHandler extends ScopedHandler
*/
public void addFilterWithMapping(FilterHolder holder, String pathSpec, int dispatches)
{
Objects.requireNonNull(holder);
FilterHolder[] holders = getFilters();
if (holders != null)
holders = holders.clone();
@ -1061,7 +1023,7 @@ public class ServletHandler extends ScopedHandler
{
try (AutoLock l = lock())
{
if (holder != null && !containsFilterHolder(holder))
if (!containsFilterHolder(holder))
setFilters(ArrayUtil.addToArray(holders, holder, FilterHolder.class));
}
@ -1295,6 +1257,7 @@ public class ServletHandler extends ScopedHandler
{
_filterPathMappings = null;
_filterNameMappings = null;
_wildFilterNameMappings = Collections.emptyList();
}
else
{
@ -1319,6 +1282,13 @@ public class ServletHandler extends ScopedHandler
}
}
}
// Reverse filter mappings to apply as wrappers last filter wrapped first
for (Map.Entry<String, List<FilterMapping>> entry : _filterNameMappings.entrySet())
Collections.reverse(entry.getValue());
Collections.reverse(_filterPathMappings);
_wildFilterNameMappings = _filterNameMappings.get("*");
if (_wildFilterNameMappings != null)
Collections.reverse(_wildFilterNameMappings);
}
// Map servlet paths to holders
@ -1547,159 +1517,6 @@ public class ServletHandler extends ScopedHandler
}
}
protected class CachedChain implements FilterChain
{
FilterHolder _filterHolder;
CachedChain _next;
ServletHolder _servletHolder;
/**
* @param filters list of {@link FilterHolder} objects
* @param servletHolder the current {@link ServletHolder}
*/
protected CachedChain(List<FilterHolder> filters, ServletHolder servletHolder)
{
if (!filters.isEmpty())
{
_filterHolder = filters.get(0);
filters.remove(0);
_next = new CachedChain(filters, servletHolder);
}
else
_servletHolder = servletHolder;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException
{
final Request baseRequest = Request.getBaseRequest(request);
// pass to next filter
if (_filterHolder != null)
{
if (LOG.isDebugEnabled())
LOG.debug("call filter {}", _filterHolder);
//if the request already does not support async, then the setting for the filter
//is irrelevant. However if the request supports async but this filter does not
//temporarily turn it off for the execution of the filter
if (baseRequest.isAsyncSupported() && !_filterHolder.isAsyncSupported())
{
try
{
baseRequest.setAsyncSupported(false, _filterHolder.toString());
_filterHolder.doFilter(request, response, _next);
}
finally
{
baseRequest.setAsyncSupported(true, null);
}
}
else
_filterHolder.doFilter(request, response, _next);
return;
}
// Call servlet
HttpServletRequest srequest = (HttpServletRequest)request;
if (_servletHolder == null)
notFound(baseRequest, srequest, (HttpServletResponse)response);
else
{
if (LOG.isDebugEnabled())
LOG.debug("call servlet {}", _servletHolder);
_servletHolder.handle(baseRequest, request, response);
}
}
@Override
public String toString()
{
if (_filterHolder != null)
return _filterHolder + "->" + _next.toString();
if (_servletHolder != null)
return _servletHolder.toString();
return "null";
}
}
private class Chain implements FilterChain
{
final Request _baseRequest;
final List<FilterHolder> _chain;
final ServletHolder _servletHolder;
int _filter = 0;
private Chain(Request baseRequest, List<FilterHolder> filters, ServletHolder servletHolder)
{
_baseRequest = baseRequest;
_chain = filters;
_servletHolder = servletHolder;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException
{
if (LOG.isDebugEnabled())
LOG.debug("doFilter {}", _filter);
// pass to next filter
if (_filter < _chain.size())
{
FilterHolder holder = _chain.get(_filter++);
if (LOG.isDebugEnabled())
LOG.debug("call filter {}", holder);
//if the request already does not support async, then the setting for the filter
//is irrelevant. However if the request supports async but this filter does not
//temporarily turn it off for the execution of the filter
if (!holder.isAsyncSupported() && _baseRequest.isAsyncSupported())
{
try
{
_baseRequest.setAsyncSupported(false, holder.toString());
holder.doFilter(request, response, this);
}
finally
{
_baseRequest.setAsyncSupported(true, null);
}
}
else
holder.doFilter(request, response, this);
return;
}
// Call servlet
HttpServletRequest srequest = (HttpServletRequest)request;
if (_servletHolder == null)
notFound(Request.getBaseRequest(request), srequest, (HttpServletResponse)response);
else
{
if (LOG.isDebugEnabled())
LOG.debug("call servlet {}", _servletHolder);
_servletHolder.handle(_baseRequest, request, response);
}
}
@Override
public String toString()
{
StringBuilder b = new StringBuilder();
for (FilterHolder f : _chain)
{
b.append(f.toString());
b.append("->");
}
b.append(_servletHolder);
return b.toString();
}
}
/**
* @return The maximum entries in a filter chain cache.
*/
@ -1810,4 +1627,52 @@ public class ServletHandler extends ScopedHandler
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
static class Chain implements FilterChain
{
private final FilterHolder _filterHolder;
private final FilterChain _filterChain;
Chain(FilterHolder filter, FilterChain chain)
{
_filterHolder = filter;
_filterChain = chain;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException
{
_filterHolder.doFilter(request, response, _filterChain);
}
@Override
public String toString()
{
return String.format("Chain@%x(%s)->%s", hashCode(), _filterHolder, _filterChain);
}
}
static class ChainEnd implements FilterChain
{
private final ServletHolder _servletHolder;
ChainEnd(ServletHolder holder)
{
_servletHolder = holder;
}
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException
{
Request baseRequest = Request.getBaseRequest(request);
Objects.requireNonNull(baseRequest);
_servletHolder.handle(baseRequest, request, response);
}
@Override
public String toString()
{
return String.format("ChainEnd@%x(%s)", hashCode(), _servletHolder);
}
}
}