9.4.x->10.0.x

This commit is contained in:
WalkerWatch 2018-08-27 22:58:01 -04:00
commit 90f15f278b
21 changed files with 1071 additions and 358 deletions

View File

@ -65,7 +65,9 @@ public class HttpAuthenticationStore implements AuthenticationStore
@Override @Override
public void addAuthenticationResult(Authentication.Result result) public void addAuthenticationResult(Authentication.Result result)
{ {
results.put(result.getURI(), result); URI uri = result.getURI();
if (uri != null)
results.put(uri, result);
} }
@Override @Override

View File

@ -426,16 +426,7 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
if (getHttpExchanges().isEmpty()) if (getHttpExchanges().isEmpty())
{ {
if (getHttpClient().isRemoveIdleDestinations() && connectionPool.isEmpty()) tryRemoveIdleDestination();
{
// There is a race condition between this thread removing the destination
// and another thread queueing a request to this same destination.
// If this destination is removed, but the request queued, a new connection
// will be opened, the exchange will be executed and eventually the connection
// will idle timeout and be closed. Meanwhile a new destination will be created
// in HttpClient and will be used for other requests.
getHttpClient().removeDestination(this);
}
} }
else else
{ {
@ -460,6 +451,22 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
// The call to Request.abort() will remove the exchange from the exchanges queue. // The call to Request.abort() will remove the exchange from the exchanges queue.
for (HttpExchange exchange : new ArrayList<>(exchanges)) for (HttpExchange exchange : new ArrayList<>(exchanges))
exchange.getRequest().abort(cause); exchange.getRequest().abort(cause);
if (exchanges.isEmpty())
tryRemoveIdleDestination();
}
private void tryRemoveIdleDestination()
{
if (getHttpClient().isRemoveIdleDestinations() && connectionPool.isEmpty())
{
// There is a race condition between this thread removing the destination
// and another thread queueing a request to this same destination.
// If this destination is removed, but the request queued, a new connection
// will be opened, the exchange will be executed and eventually the connection
// will idle timeout and be closed. Meanwhile a new destination will be created
// in HttpClient and will be used for other requests.
getHttpClient().removeDestination(this);
}
} }
@Override @Override

View File

@ -32,6 +32,7 @@ import org.eclipse.jetty.client.Origin;
import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.client.api.Destination; import org.eclipse.jetty.client.api.Destination;
import org.eclipse.jetty.client.api.Request;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpHeaderValue;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
@ -268,6 +269,33 @@ public class HttpDestinationOverHTTPTest extends AbstractHttpClientServerTest
Assert.assertNotSame(destinationBefore, destinationAfter); Assert.assertNotSame(destinationBefore, destinationAfter);
} }
@Test
public void testDestinationIsRemovedAfterConnectionError() throws Exception
{
String host = "localhost";
int port = connector.getLocalPort();
client.setRemoveIdleDestinations(true);
Assert.assertTrue("Destinations of a fresh client must be empty", client.getDestinations().isEmpty());
server.stop();
Request request = client.newRequest(host, port).scheme(this.scheme);
try
{
request.send();
Assert.fail("Request to a closed port must fail");
}
catch (Exception expected)
{
}
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(1);
while (!client.getDestinations().isEmpty() && System.nanoTime() < deadline)
{
Thread.sleep(10);
}
Assert.assertTrue("Destination must be removed after connection error", client.getDestinations().isEmpty());
}
private Connection pollIdleConnection(DuplexConnectionPool connectionPool, long time, TimeUnit unit) throws InterruptedException private Connection pollIdleConnection(DuplexConnectionPool connectionPool, long time, TimeUnit unit) throws InterruptedException
{ {
return await(() -> connectionPool.getIdleConnections().poll(), time, unit); return await(() -> connectionPool.getIdleConnections().poll(), time, unit);

View File

@ -40,18 +40,37 @@ See the `DefaultServlet` link:{JDURL}/org/eclipse/jetty/servlet/DefaultServlet.h
Jetty supports the following `initParameters`: Jetty supports the following `initParameters`:
acceptRanges:: acceptRanges::
If true, range requests and responses are supported. If `true`, range requests and responses are supported.
dirAllowed:: dirAllowed::
If true, directory listings are returned if no welcome file is found. If `true`, directory listings are returned if no welcome file is found.
Otherwise 403 Forbidden displays. Otherwise 403 Forbidden displays.
redirectWelcome:: redirectWelcome::
If true, welcome files are redirected rather that forwarded. If `true`, welcome files are redirected rather that forwarded.
welcomeServlets::
If `true`, attempt to dispatch to welcome files that are servlets, but only after no matching static
resources could be found. If `false`, then a welcome file must exist on disk. If `exact`, then exact
servlet matches are supported without an existing file. Default is `true`. This must be `false` if you want directory listings,
but have index.jsp in your welcome file list.
precompressed::
If set to a comma separated list of encoding types (that may be listed in a requests Accept-Encoding header) to file extension mappings to look for and serve.
For example: `br=.br,gzip=.gz,bzip2=.bz`.
If set to a boolean `true`, then a default set of compressed formats will be used, otherwise no precompressed formats supported.
gzip:: gzip::
If set to true, then static content is served as gzip content encoded if a matching resource is found ending with ".gz". Deprecated. Use `precompressed` instead. If set to `true`, then static content is served as gzip content encoded if a matching resource is found ending with ".gz".
resourceBase:: resourceBase::
Set to replace the context resource base. Set to replace the context resource base.
aliases:: resourceCache::
If true, aliases of resources are allowed (that is, symbolic links and caps variations) and may bypass security constraints. If set, this is a context attribute name, which the servlet will use to look for a shared ResourceCache instance.
relativeResourceBase::
Set with a pathname relative to the base of the servlet context root. Useful for only serving static content out of only specific subdirectories.
cacheControl::
If set, all static content will have this value set as the cache-control header.
pathInfoOnly::
If `true`, only the path info will be applied to the resourceBase
stylesheet::
Set with the location of an optional stylesheet that will be used to decorate the directory listing html.
etags::
If `true`, weak etags will be generated and handled.
maxCacheSize:: maxCacheSize::
Maximum total size of the cache or 0 for no cache. Maximum total size of the cache or 0 for no cache.
maxCachedFileSize:: maxCachedFileSize::
@ -59,9 +78,11 @@ Maximum size of a file to cache.
maxCachedFiles:: maxCachedFiles::
Maximum number of files to cache. Maximum number of files to cache.
useFileMappedBuffer:: useFileMappedBuffer::
If set to true, mapped file buffer serves static content. If set to `true`, mapped file buffer serves static content.
Setting this value to false means that a direct buffer is used instead of a mapped file buffer. Setting this value to `false` means that a direct buffer is used instead of a mapped file buffer.
By default, this is set to true. By default, this is set to `true`.
otherGzipFileExtensions:: otherGzipFileExtensions::
A comma separated list of other file extensions that signify that a file is gzip compressed. A comma separated list of other file extensions that signify that a file is gzip compressed.
If you don't explicitly set this, it defaults to ".svgz". If you don't explicitly set this, it defaults to `.svgz`.
encodingHeaderCacheSize::
Max entries in a cache of ACCEPT-ENCODING headers

View File

@ -34,13 +34,13 @@ It fixes many of the bugs in commonly available compression filters: it works wi
It has been tested with Jetty continuations and suspending requests. It has been tested with Jetty continuations and suspending requests.
Some user-agents might be excluded from compression to avoid common browser bugs (yes, this means IE!). Some user-agents might be excluded from compression to avoid common browser bugs (yes, this means IE!).
The `GzipHandler` is added to the entire server by the `etc/jetty-gzip.xml` file from the `gzip.mod` module. The `GzipHandler` can be added to the entire server by enabling the `gzip.mod` module.
It may also be added to individual contexts in a context xml file. It may also be added to individual contexts in a context xml file.
____ ____
[NOTE] [NOTE]
Jetty 9 only compresses using GZip. Jetty 9 only compresses using GZip.
Using deflate http compression is not supported and will not function. Using deflate HTTP compression is not supported and will not function.
____ ____
[[gzip-filter-rules]] [[gzip-filter-rules]]
@ -59,16 +59,25 @@ ____
Compressing the content can greatly improve the network bandwidth usage, but at the cost of memory and CPU cycles. Compressing the content can greatly improve the network bandwidth usage, but at the cost of memory and CPU cycles.
The link:#default-servlet[DefaultServlet] is capable of serving pre-compressed static content, which saves memory and CPU. The link:#default-servlet[DefaultServlet] is capable of serving pre-compressed static content, which saves memory and CPU.
By default, the `GzipHandler` will check to see if pre-compressed content exists, and pass the request through to be handled by the `DefaultServlet`.
The `GzipHandler` installs an output interceptor which passes through to the `DefaultServlet`.
If the content served by `DefaultServlet` is already compressed, the `GzipHandler` does nothing; if it is not compressed, the content is compressed on-the-fly.
____
[NOTE]
Automatic precompression by the `DefaultServlet` can be configured.
Read more about the `DefaultServlet` link:#default-servlet[here.]
____
[[gzip-filter-init]] [[gzip-filter-init]]
==== Gzip Configuration ==== Gzip Configuration
minGzipSize:: minGzipSize::
Content will only be compressed if content length is either unknown or greater than `minGzipSize`. Content will only be compressed if content length is either unknown or greater than `minGzipSize`.
checkGzExists:: checkGzExists (Deprecated)::
True by default. False by default.
If set to false, the handler will not check for pre-compressed content. If set to true, the handler will check for pre-compressed content.
includedMethods:: includedMethods::
List of HTTP methods to compress. List of HTTP methods to compress.
If not set, only `GET` requests are compressed. If not set, only `GET` requests are compressed.

View File

@ -8,15 +8,15 @@
<Configure id="Server" class="org.eclipse.jetty.server.Server"> <Configure id="Server" class="org.eclipse.jetty.server.Server">
<Call name="addBean"> <Call name="addBean">
<Arg> <Arg>
<New class="org.eclipse.jetty.server.LowResourceMonitor"> <New id="lowResourceMonitor" class="org.eclipse.jetty.server.LowResourceMonitor">
<Arg name="server"><Ref refid='Server'/></Arg> <Arg name="server"><Ref refid='Server'/></Arg>
<Set name="period"><Property name="jetty.lowresources.period" default="1000"/></Set> <Set name="period"><Property name="jetty.lowresources.period" deprecated="lowresources.period" default="1000"/></Set>
<Set name="lowResourcesIdleTimeout"><Property name="jetty.lowresources.idleTimeout" default="1000"/></Set> <Set name="lowResourcesIdleTimeout"><Property name="jetty.lowresources.idleTimeout" deprecated="lowresources.lowResourcesIdleTimeout" default="1000"/></Set>
<Set name="monitorThreads"><Property name="jetty.lowresources.monitorThreads" default="true"/></Set> <Set name="monitorThreads"><Property name="jetty.lowresources.monitorThreads" deprecated="lowresources.monitorThreads" default="true"/></Set>
<Set name="maxConnections"><Property name="jetty.lowresources.maxConnections" default="0"/></Set> <Set name="maxConnections"><Property name="jetty.lowresources.maxConnections" deprecated="lowresources.maxConnections" default="0"/></Set>
<Set name="maxMemory"><Property name="jetty.lowresources.maxMemory" default="0"/></Set> <Set name="maxMemory"><Property name="jetty.lowresources.maxMemory" deprecated="lowresources.maxMemory" default="0"/></Set>
<Set name="maxLowResourcesTime"><Property name="jetty.lowresources.maxLowResourcesTime" default="5000"/></Set> <Set name="maxLowResourcesTime"><Property name="jetty.lowresources.maxLowResourcesTime" deprecated="lowresources.maxLowResourcesTime" default="5000"/></Set>
<Set name="acceptingInLowResources"><Property name="jetty.lowresources.accepting" default="true"/></Set> <Set name="acceptingInLowResources"><Property name="jetty.lowresources.accepting" default="true"/></Set>
</New> </New>
</Arg> </Arg>
</Call> </Call>

View File

@ -18,6 +18,17 @@
package org.eclipse.jetty.server; package org.eclipse.jetty.server;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler;
import org.eclipse.jetty.util.thread.ThreadPool;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@ -27,66 +38,46 @@ import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.Name;
import org.eclipse.jetty.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.ScheduledExecutorScheduler;
import org.eclipse.jetty.util.thread.Scheduler;
import org.eclipse.jetty.util.thread.ThreadPool;
/** /**
* A monitor for low resources *
* <p> * A monitor for low resources, low resources can be detected by:
* An instance of this class will monitor all the connectors of a server (or a set of connectors
* configured with {@link #setMonitoredConnectors(Collection)}) for a low resources state.
* <p>
* Low resources can be detected by:
* <ul> * <ul>
* <li>{@link ThreadPool#isLowOnThreads()} if {@link Connector#getExecutor()} is * <li>{@link ThreadPool#isLowOnThreads()} if {@link Connector#getExecutor()} is
* an instance of {@link ThreadPool} and {@link #setMonitorThreads(boolean)} is true.</li> * an instance of {@link ThreadPool} and {@link #setMonitorThreads(boolean)} is true.</li>
* <li>If {@link #setMaxMemory(long)} is non zero then low resources is detected if the JVMs * <li>If {@link #setMaxMemory(long)} is non zero then low resources is detected if the JVMs
* {@link Runtime} instance has {@link Runtime#totalMemory()} minus {@link Runtime#freeMemory()} * {@link Runtime} instance has {@link Runtime#totalMemory()} minus {@link Runtime#freeMemory()}
* greater than {@link #getMaxMemory()}</li> * greater than {@link #getMaxMemory()}</li>
* <li>If {@link #setMaxConnections(int)} is non zero then low resources is dected if the total number * <li>If {@link #setMaxConnections(int)} is non zero then low resources is detected if the total number
* of connections exceeds {@link #getMaxConnections()}. This feature is deprecated and replaced by * of connections exceeds {@link #getMaxConnections()}. This feature is deprecated and replaced by
* {@link ConnectionLimit}</li> * {@link ConnectionLimit}</li>
* </ul> * </ul>
* <p> *
* Once low resources state is detected, the cause is logged and all existing connections returned
* by {@link Connector#getConnectedEndPoints()} have {@link EndPoint#setIdleTimeout(long)} set
* to {@link #getLowResourcesIdleTimeout()}. New connections are not affected, however if the low
* resources state persists for more than {@link #getMaxLowResourcesTime()}, then the
* {@link #getLowResourcesIdleTimeout()} to all connections again. Once the low resources state is
* cleared, the idle timeout is reset to the connector default given by {@link Connector#getIdleTimeout()}.
* <p>
* If {@link #setAcceptingInLowResources(boolean)} is set to false (Default is true), then no new connections
* are accepted when in low resources state.
*/ */
@ManagedObject ("Monitor for low resource conditions and activate a low resource mode if detected") @ManagedObject ("Monitor for low resource conditions and activate a low resource mode if detected")
public class LowResourceMonitor extends AbstractLifeCycle public class LowResourceMonitor extends ContainerLifeCycle
{ {
private static final Logger LOG = Log.getLogger(LowResourceMonitor.class); private static final Logger LOG = Log.getLogger(LowResourceMonitor.class);
private final Server _server;
protected final Server _server;
private Scheduler _scheduler; private Scheduler _scheduler;
private Connector[] _monitoredConnectors; private Connector[] _monitoredConnectors;
private Set<AbstractConnector> _acceptingConnectors = new HashSet<>(); private Set<AbstractConnector> _acceptingConnectors = new HashSet<>();
private int _period=1000; private int _period=1000;
private int _maxConnections;
private long _maxMemory;
private int _lowResourcesIdleTimeout=1000; private int _lowResourcesIdleTimeout=1000;
private int _maxLowResourcesTime=0; private int _maxLowResourcesTime=0;
private boolean _monitorThreads=true;
private final AtomicBoolean _low = new AtomicBoolean(); private final AtomicBoolean _low = new AtomicBoolean();
private String _cause;
private String _reasons; private String _reasons;
private long _lowStarted; private long _lowStarted;
private boolean _acceptingInLowResources = true; private boolean _acceptingInLowResources = true;
private Set<LowResourceCheck> _lowResourceChecks = new HashSet<>();
private final Runnable _monitor = new Runnable() private final Runnable _monitor = new Runnable()
{ {
@Override @Override
@ -95,14 +86,86 @@ public class LowResourceMonitor extends AbstractLifeCycle
if (isRunning()) if (isRunning())
{ {
monitor(); monitor();
_scheduler.schedule(_monitor,_period,TimeUnit.MILLISECONDS); _scheduler.schedule( _monitor, _period, TimeUnit.MILLISECONDS);
} }
} }
}; };
public LowResourceMonitor(@Name("server") Server server) public LowResourceMonitor(@Name("server") Server server)
{ {
_server=server; _server = server;
}
@ManagedAttribute("True if low available threads status is monitored")
public boolean getMonitorThreads()
{
return !getBeans(ConnectorsThreadPoolLowResourceCheck.class).isEmpty();
}
/**
* @param monitorThreads If true, check connectors executors to see if they are
* {@link ThreadPool} instances that are low on threads.
*/
public void setMonitorThreads(boolean monitorThreads)
{
if(monitorThreads)
// already configured?
if ( !getMonitorThreads() ) addLowResourceCheck( new ConnectorsThreadPoolLowResourceCheck() );
else
getBeans(ConnectorsThreadPoolLowResourceCheck.class).forEach(this::removeBean);
}
/**
* @return The maximum connections allowed for the monitored connectors before low resource handling is activated
* @deprecated Replaced by ConnectionLimit
*/
@ManagedAttribute("The maximum connections allowed for the monitored connectors before low resource handling is activated")
@Deprecated
public int getMaxConnections()
{
for(MaxConnectionsLowResourceCheck lowResourceCheck : getBeans(MaxConnectionsLowResourceCheck.class))
{
if (lowResourceCheck.getMaxConnections()>0)
{
return lowResourceCheck.getMaxConnections();
}
}
return -1;
}
/**
* @param maxConnections The maximum connections before low resources state is triggered
* @deprecated Replaced by ConnectionLimit
*/
@Deprecated
public void setMaxConnections(int maxConnections)
{
if (maxConnections>0)
{
if (getBeans(MaxConnectionsLowResourceCheck.class).isEmpty())
{
addLowResourceCheck(new MaxConnectionsLowResourceCheck(maxConnections));
} else
{
getBeans(MaxConnectionsLowResourceCheck.class).forEach( c -> c.setMaxConnections( maxConnections ) );
}
}
else
{
getBeans(ConnectorsThreadPoolLowResourceCheck.class).forEach(this::removeBean);
}
}
@ManagedAttribute("The reasons the monitored connectors are low on resources")
public String getReasons()
{
return _reasons;
}
protected void setReasons(String reasons)
{
_reasons = reasons;
} }
@ManagedAttribute("Are the monitored connectors low on resources?") @ManagedAttribute("Are the monitored connectors low on resources?")
@ -111,24 +174,39 @@ public class LowResourceMonitor extends AbstractLifeCycle
return _low.get(); return _low.get();
} }
protected boolean enableLowOnResources(boolean expectedValue, boolean newValue)
{
return _low.compareAndSet(expectedValue, newValue);
}
@ManagedAttribute("The reason(s) the monitored connectors are low on resources") @ManagedAttribute("The reason(s) the monitored connectors are low on resources")
public String getLowResourcesReasons() public String getLowResourcesReasons()
{ {
return _reasons; return _reasons;
} }
protected void setLowResourcesReasons(String reasons)
{
_reasons = reasons;
}
@ManagedAttribute("Get the timestamp in ms since epoch that low resources state started") @ManagedAttribute("Get the timestamp in ms since epoch that low resources state started")
public long getLowResourcesStarted() public long getLowResourcesStarted()
{ {
return _lowStarted; return _lowStarted;
} }
public void setLowResourcesStarted(long lowStarted)
{
_lowStarted = lowStarted;
}
@ManagedAttribute("The monitored connectors. If null then all server connectors are monitored") @ManagedAttribute("The monitored connectors. If null then all server connectors are monitored")
public Collection<Connector> getMonitoredConnectors() public Collection<Connector> getMonitoredConnectors()
{ {
if (_monitoredConnectors==null) if (_monitoredConnectors==null)
return Collections.emptyList(); return Collections.emptyList();
return Arrays.asList(_monitoredConnectors); return Arrays.asList( _monitoredConnectors);
} }
/** /**
@ -142,6 +220,13 @@ public class LowResourceMonitor extends AbstractLifeCycle
_monitoredConnectors = monitoredConnectors.toArray(new Connector[monitoredConnectors.size()]); _monitoredConnectors = monitoredConnectors.toArray(new Connector[monitoredConnectors.size()]);
} }
protected Connector[] getMonitoredOrServerConnectors()
{
if (_monitoredConnectors!=null && _monitoredConnectors.length>0)
return _monitoredConnectors;
return _server.getConnectors();
}
@ManagedAttribute("If false, new connections are not accepted while in low resources") @ManagedAttribute("If false, new connections are not accepted while in low resources")
public boolean isAcceptingInLowResources() public boolean isAcceptingInLowResources()
{ {
@ -152,7 +237,7 @@ public class LowResourceMonitor extends AbstractLifeCycle
{ {
_acceptingInLowResources = acceptingInLowResources; _acceptingInLowResources = acceptingInLowResources;
} }
@ManagedAttribute("The monitor period in ms") @ManagedAttribute("The monitor period in ms")
public int getPeriod() public int getPeriod()
{ {
@ -167,58 +252,6 @@ public class LowResourceMonitor extends AbstractLifeCycle
_period = periodMS; _period = periodMS;
} }
@ManagedAttribute("True if low available threads status is monitored")
public boolean getMonitorThreads()
{
return _monitorThreads;
}
/**
* @param monitorThreads If true, check connectors executors to see if they are
* {@link ThreadPool} instances that are low on threads.
*/
public void setMonitorThreads(boolean monitorThreads)
{
_monitorThreads = monitorThreads;
}
/**
* @return The maximum connections allowed for the monitored connectors before low resource handling is activated
* @deprecated Replaced by ConnectionLimit
*/
@ManagedAttribute("The maximum connections allowed for the monitored connectors before low resource handling is activated")
@Deprecated
public int getMaxConnections()
{
return _maxConnections;
}
/**
* @param maxConnections The maximum connections before low resources state is triggered
* @deprecated Replaced by ConnectionLimit
*/
@Deprecated
public void setMaxConnections(int maxConnections)
{
if (maxConnections>0)
LOG.warn("LowResourceMonitor.setMaxConnections is deprecated. Use ConnectionLimit.");
_maxConnections = maxConnections;
}
@ManagedAttribute("The maximum memory (in bytes) that can be used before low resources is triggered. Memory used is calculated as (totalMemory-freeMemory).")
public long getMaxMemory()
{
return _maxMemory;
}
/**
* @param maxMemoryBytes The maximum memory in bytes in use before low resources is triggered.
*/
public void setMaxMemory(long maxMemoryBytes)
{
_maxMemory = maxMemoryBytes;
}
@ManagedAttribute("The idletimeout in ms to apply to all existing connections when low resources is detected") @ManagedAttribute("The idletimeout in ms to apply to all existing connections when low resources is detected")
public int getLowResourcesIdleTimeout() public int getLowResourcesIdleTimeout()
{ {
@ -247,6 +280,99 @@ public class LowResourceMonitor extends AbstractLifeCycle
_maxLowResourcesTime = maxLowResourcesTimeMS; _maxLowResourcesTime = maxLowResourcesTimeMS;
} }
@ManagedAttribute("The maximum memory (in bytes) that can be used before low resources is triggered. Memory used is calculated as (totalMemory-freeMemory).")
public long getMaxMemory()
{
Collection<MemoryLowResourceCheck> beans = getBeans(MemoryLowResourceCheck.class);
if(beans.isEmpty())
{
return 0;
}
return beans.stream().findFirst().get().getMaxMemory();
}
/**
* @param maxMemoryBytes The maximum memory in bytes in use before low resources is triggered.
*/
public void setMaxMemory(long maxMemoryBytes)
{
if(maxMemoryBytes<=0)
{
return;
}
Collection<MemoryLowResourceCheck> beans = getBeans(MemoryLowResourceCheck.class);
if(beans.isEmpty())
addLowResourceCheck( new MemoryLowResourceCheck( maxMemoryBytes ) );
else
beans.forEach( lowResourceCheck -> lowResourceCheck.setMaxMemory( maxMemoryBytes ) );
}
public Set<LowResourceCheck> getLowResourceChecks()
{
return _lowResourceChecks;
}
public void setLowResourceChecks( Set<LowResourceCheck> lowResourceChecks )
{
updateBeans(_lowResourceChecks.toArray(),lowResourceChecks.toArray());
this._lowResourceChecks = lowResourceChecks;
}
public void addLowResourceCheck( LowResourceCheck lowResourceCheck )
{
addBean(lowResourceCheck);
this._lowResourceChecks.add(lowResourceCheck);
}
protected void monitor()
{
String reasons=null;
for(LowResourceCheck lowResourceCheck : _lowResourceChecks)
{
if(lowResourceCheck.isLowOnResources())
{
reasons = lowResourceCheck.toString();
break;
}
}
if (reasons!=null)
{
// Log the reasons if there is any change in the cause
if (!reasons.equals(getReasons()))
{
LOG.warn("Low Resources: {}",reasons);
setReasons(reasons);
}
// Enter low resources state?
if (enableLowOnResources(false,true))
{
setLowResourcesReasons(reasons);
setLowResourcesStarted(System.currentTimeMillis());
setLowResources();
}
// Too long in low resources state?
if ( getMaxLowResourcesTime()>0 && (System.currentTimeMillis()-getLowResourcesStarted())>getMaxLowResourcesTime())
setLowResources();
}
else
{
if (enableLowOnResources(true,false))
{
LOG.info("Low Resources cleared");
setLowResourcesReasons(null);
setLowResourcesStarted(0);
setReasons(null);
clearLowResources();
}
}
}
@Override @Override
protected void doStart() throws Exception protected void doStart() throws Exception
{ {
@ -257,6 +383,7 @@ public class LowResourceMonitor extends AbstractLifeCycle
_scheduler=new LRMScheduler(); _scheduler=new LRMScheduler();
_scheduler.start(); _scheduler.start();
} }
super.doStart(); super.doStart();
_scheduler.schedule(_monitor,_period,TimeUnit.MILLISECONDS); _scheduler.schedule(_monitor,_period,TimeUnit.MILLISECONDS);
@ -265,94 +392,11 @@ public class LowResourceMonitor extends AbstractLifeCycle
@Override @Override
protected void doStop() throws Exception protected void doStop() throws Exception
{ {
if (_scheduler instanceof LRMScheduler) if (_scheduler instanceof LRMScheduler )
_scheduler.stop(); _scheduler.stop();
super.doStop(); super.doStop();
} }
protected Connector[] getMonitoredOrServerConnectors()
{
if (_monitoredConnectors!=null && _monitoredConnectors.length>0)
return _monitoredConnectors;
return _server.getConnectors();
}
protected void monitor()
{
String reasons=null;
String cause="";
int connections=0;
ThreadPool serverThreads = _server.getThreadPool();
if (_monitorThreads && serverThreads.isLowOnThreads())
{
reasons=low(reasons,"Server low on threads: "+serverThreads);
cause+="S";
}
for(Connector connector : getMonitoredOrServerConnectors())
{
connections+=connector.getConnectedEndPoints().size();
Executor executor = connector.getExecutor();
if (executor instanceof ThreadPool && executor!=serverThreads)
{
ThreadPool connectorThreads=(ThreadPool)executor;
if (_monitorThreads && connectorThreads.isLowOnThreads())
{
reasons=low(reasons,"Connector low on threads: "+connectorThreads);
cause+="T";
}
}
}
if (_maxConnections>0 && connections>_maxConnections)
{
reasons=low(reasons,"Max Connections exceeded: "+connections+">"+_maxConnections);
cause+="C";
}
long memory=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
if (_maxMemory>0 && memory>_maxMemory)
{
reasons=low(reasons,"Max memory exceeded: "+memory+">"+_maxMemory);
cause+="M";
}
if (reasons!=null)
{
// Log the reasons if there is any change in the cause
if (!cause.equals(_cause))
{
LOG.warn("Low Resources: {}",reasons);
_cause=cause;
}
// Enter low resources state?
if (_low.compareAndSet(false,true))
{
_reasons=reasons;
_lowStarted=System.currentTimeMillis();
setLowResources();
}
// Too long in low resources state?
if (_maxLowResourcesTime>0 && (System.currentTimeMillis()-_lowStarted)>_maxLowResourcesTime)
setLowResources();
}
else
{
if (_low.compareAndSet(true,false))
{
LOG.info("Low Resources cleared");
_reasons=null;
_lowStarted=0;
_cause=null;
clearLowResources();
}
}
}
protected void setLowResources() protected void setLowResources()
{ {
for(Connector connector : getMonitoredOrServerConnectors()) for(Connector connector : getMonitoredOrServerConnectors())
@ -366,8 +410,8 @@ public class LowResourceMonitor extends AbstractLifeCycle
c.setAccepting(false); c.setAccepting(false);
} }
} }
for (EndPoint endPoint : connector.getConnectedEndPoints()) for ( EndPoint endPoint : connector.getConnectedEndPoints())
endPoint.setIdleTimeout(_lowResourcesIdleTimeout); endPoint.setIdleTimeout(_lowResourcesIdleTimeout);
} }
} }
@ -379,7 +423,7 @@ public class LowResourceMonitor extends AbstractLifeCycle
for (EndPoint endPoint : connector.getConnectedEndPoints()) for (EndPoint endPoint : connector.getConnectedEndPoints())
endPoint.setIdleTimeout(connector.getIdleTimeout()); endPoint.setIdleTimeout(connector.getIdleTimeout());
} }
for (AbstractConnector connector : _acceptingConnectors) for (AbstractConnector connector : _acceptingConnectors)
{ {
connector.setAccepting(true); connector.setAccepting(true);
@ -387,15 +431,219 @@ public class LowResourceMonitor extends AbstractLifeCycle
_acceptingConnectors.clear(); _acceptingConnectors.clear();
} }
private String low(String reasons, String newReason) protected String low(String reasons, String newReason)
{ {
if (reasons==null) if (reasons==null)
return newReason; return newReason;
return reasons+", "+newReason; return reasons+", "+newReason;
} }
private static class LRMScheduler extends ScheduledExecutorScheduler private static class LRMScheduler extends ScheduledExecutorScheduler
{ {
} }
interface LowResourceCheck
{
boolean isLowOnResources();
String getReason();
}
//------------------------------------------------------
// default implementations for backward compat
//------------------------------------------------------
public class MainThreadPoolLowResourceCheck implements LowResourceCheck
{
private String reason;
public MainThreadPoolLowResourceCheck()
{
// no op
}
@Override
public boolean isLowOnResources()
{
ThreadPool serverThreads = _server.getThreadPool();
if (serverThreads.isLowOnThreads())
{
reason="Server low on threads: "+serverThreads;
return true;
}
return false;
}
@Override
public String getReason()
{
return reason;
}
@Override
public String toString()
{
return "Check if the server ThreadPool is lowOnThreads";
}
}
public class ConnectorsThreadPoolLowResourceCheck implements LowResourceCheck
{
private String reason;
public ConnectorsThreadPoolLowResourceCheck()
{
// no op
}
@Override
public boolean isLowOnResources()
{
ThreadPool serverThreads = _server.getThreadPool();
if(serverThreads.isLowOnThreads())
{
reason ="Server low on threads: "+serverThreads.getThreads()+", idleThreads:"+serverThreads.getIdleThreads();
return true;
}
for(Connector connector : getMonitoredConnectors())
{
Executor executor = connector.getExecutor();
if (executor instanceof ThreadPool && executor!=serverThreads)
{
ThreadPool connectorThreads=(ThreadPool)executor;
if (connectorThreads.isLowOnThreads())
{
reason ="Connector low on threads: "+connectorThreads;
return true;
}
}
}
return false;
}
@Override
public String getReason()
{
return reason;
}
@Override
public String toString()
{
return "Check if the ThreadPool from monitored connectors are lowOnThreads and if all connections number is higher than the allowed maxConnection";
}
}
@ManagedObject("Check max allowed connections on connectors")
public class MaxConnectionsLowResourceCheck implements LowResourceCheck
{
private String reason;
private int maxConnections;
public MaxConnectionsLowResourceCheck(int maxConnections)
{
this.maxConnections = maxConnections;
}
/**
* @return The maximum connections allowed for the monitored connectors before low resource handling is activated
* @deprecated Replaced by ConnectionLimit
*/
@ManagedAttribute("The maximum connections allowed for the monitored connectors before low resource handling is activated")
@Deprecated
public int getMaxConnections()
{
return maxConnections;
}
/**
* @param maxConnections The maximum connections before low resources state is triggered
* @deprecated Replaced by ConnectionLimit
*/
@Deprecated
public void setMaxConnections(int maxConnections)
{
if (maxConnections>0)
LOG.warn("LowResourceMonitor.setMaxConnections is deprecated. Use ConnectionLimit.");
this.maxConnections = maxConnections;
}
@Override
public boolean isLowOnResources()
{
int connections=0;
for(Connector connector : getMonitoredConnectors())
{
connections+=connector.getConnectedEndPoints().size();
}
if (maxConnections>0 && connections>maxConnections)
{
reason ="Max Connections exceeded: "+connections+">"+maxConnections;
return true;
}
return false;
}
@Override
public String getReason()
{
return reason;
}
@Override
public String toString()
{
return "All connections number is higher than the allowed maxConnection";
}
}
public class MemoryLowResourceCheck implements LowResourceCheck
{
private String reason;
private long maxMemory;
public MemoryLowResourceCheck(long maxMemory)
{
this.maxMemory = maxMemory;
}
@Override
public boolean isLowOnResources()
{
long memory=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
if (maxMemory>0 && memory>maxMemory)
{
reason = "Max memory exceeded: "+memory+">"+maxMemory;
return true;
}
return false;
}
public long getMaxMemory()
{
return maxMemory;
}
/**
* @param maxMemoryBytes The maximum memory in bytes in use before low resources is triggered.
*/
public void setMaxMemory( long maxMemoryBytes )
{
this.maxMemory = maxMemoryBytes;
}
@Override
public String getReason()
{
return reason;
}
@Override
public String toString()
{
return "Check if used memory is higher than the allowed max memory";
}
}
} }

View File

@ -2436,8 +2436,6 @@ public class Request implements HttpServletRequest
/* ------------------------------------------------------------ */ /* ------------------------------------------------------------ */
public void mergeQueryParameters(String oldQuery,String newQuery, boolean updateQueryString) public void mergeQueryParameters(String oldQuery,String newQuery, boolean updateQueryString)
{ {
// TODO This is seriously ugly
MultiMap<String> newQueryParams = null; MultiMap<String> newQueryParams = null;
// Have to assume ENCODING because we can't know otherwise. // Have to assume ENCODING because we can't know otherwise.
if (newQuery!=null) if (newQuery!=null)
@ -2450,7 +2448,14 @@ public class Request implements HttpServletRequest
if (oldQueryParams == null && oldQuery != null) if (oldQueryParams == null && oldQuery != null)
{ {
oldQueryParams = new MultiMap<>(); oldQueryParams = new MultiMap<>();
UrlEncoded.decodeTo(oldQuery, oldQueryParams, getQueryEncoding()); try
{
UrlEncoded.decodeTo(oldQuery, oldQueryParams, getQueryEncoding());
}
catch(Throwable th)
{
throw new BadMessageException(400,"Bad query encoding",th);
}
} }
MultiMap<String> mergedQueryParams; MultiMap<String> mergedQueryParams;

View File

@ -0,0 +1,194 @@
//
// ========================================================================
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.server;
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.TimerScheduler;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.IOException;
import java.io.InputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.junit.Assert.*;
@RunWith(AdvancedRunner.class)
public class CustomResourcesMonitorTest
{
Server _server;
ServerConnector _connector;
FileOnDirectoryMonitor _fileOnDirectoryMonitor;
Path _monitoredPath;
LowResourceMonitor _lowResourceMonitor;
@Before
public void before() throws Exception
{
_server = new Server();
_server.addBean(new TimerScheduler());
_connector = new ServerConnector(_server);
_connector.setPort(0);
_connector.setIdleTimeout(35000);
_server.addConnector(_connector);
_server.setHandler(new DumpHandler());
_monitoredPath = Files.createTempDirectory( "jetty_test" );
_fileOnDirectoryMonitor=new FileOnDirectoryMonitor(_monitoredPath);
_lowResourceMonitor = new LowResourceMonitor(_server);
_server.addBean( _lowResourceMonitor );
_lowResourceMonitor.addLowResourceCheck( _fileOnDirectoryMonitor );
_server.start();
}
@After
public void after() throws Exception
{
_server.stop();
}
@Test
public void testFileOnDirectoryMonitor() throws Exception
{
int monitorPeriod = _lowResourceMonitor.getPeriod();
int lowResourcesIdleTimeout = _lowResourceMonitor.getLowResourcesIdleTimeout();
assertThat(lowResourcesIdleTimeout, Matchers.lessThanOrEqualTo(monitorPeriod));
int maxLowResourcesTime = 5 * monitorPeriod;
_lowResourceMonitor.setMaxLowResourcesTime(maxLowResourcesTime);
assertFalse(_fileOnDirectoryMonitor.isLowOnResources());
try(Socket socket0 = new Socket("localhost",_connector.getLocalPort()))
{
Path tmpFile = Files.createTempFile( _monitoredPath, "yup", ".tmp" );
// Write a file
Files.write(tmpFile, "foobar".getBytes());
// Wait a couple of monitor periods so that
// fileOnDirectoryMonitor detects it is in low mode.
Thread.sleep(2 * monitorPeriod);
assertTrue(_fileOnDirectoryMonitor.isLowOnResources());
// We already waited enough for fileOnDirectoryMonitor to close socket0.
assertEquals(-1, socket0.getInputStream().read());
// New connections are not affected by the
// low mode until maxLowResourcesTime elapses.
try(Socket socket1 = new Socket("localhost",_connector.getLocalPort()))
{
// Set a very short read timeout so we can test if the server closed.
socket1.setSoTimeout(1);
InputStream input1 = socket1.getInputStream();
assertTrue(_fileOnDirectoryMonitor.isLowOnResources());
try
{
input1.read();
fail();
}
catch (SocketTimeoutException expected)
{
}
// Wait a couple of lowResources idleTimeouts.
Thread.sleep(2 * lowResourcesIdleTimeout);
// Verify the new socket is still open.
assertTrue(_fileOnDirectoryMonitor.isLowOnResources());
try
{
input1.read();
fail();
}
catch (SocketTimeoutException expected)
{
}
Files.delete( tmpFile );
// Let the maxLowResourcesTime elapse.
Thread.sleep(maxLowResourcesTime);
assertFalse(_fileOnDirectoryMonitor.isLowOnResources());
}
}
}
static class FileOnDirectoryMonitor implements LowResourceMonitor.LowResourceCheck
{
private static final Logger LOG = Log.getLogger( FileOnDirectoryMonitor.class);
private final Path _pathToMonitor;
private String reason;
public FileOnDirectoryMonitor(Path pathToMonitor )
{
_pathToMonitor = pathToMonitor;
}
@Override
public boolean isLowOnResources()
{
try
{
Stream<Path> paths = Files.list( _pathToMonitor );
List<Path> content = paths.collect( Collectors.toList() );
if(!content.isEmpty())
{
reason= "directory not empty so enable low resources";
return true;
}
}
catch ( IOException e )
{
LOG.info( "ignore issue looking at directory content", e );
}
return false;
}
@Override
public String getReason()
{
return reason;
}
@Override
public String toString()
{
return getClass().getName();
}
}
}

View File

@ -19,27 +19,27 @@
package org.eclipse.jetty.server; package org.eclipse.jetty.server;
import java.io.InputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import org.eclipse.jetty.toolchain.test.AdvancedRunner; import org.eclipse.jetty.toolchain.test.AdvancedRunner;
import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.util.thread.TimerScheduler; import org.eclipse.jetty.util.thread.TimerScheduler;
import org.hamcrest.Matchers; import org.hamcrest.Matchers;
import org.junit.After; import org.junit.After;
import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import java.io.InputStream;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertThat; import static org.junit.Assert.*;
@RunWith(AdvancedRunner.class) @RunWith(AdvancedRunner.class)
public class LowResourcesMonitorTest public class LowResourcesMonitorTest
@ -48,7 +48,7 @@ public class LowResourcesMonitorTest
Server _server; Server _server;
ServerConnector _connector; ServerConnector _connector;
LowResourceMonitor _lowResourcesMonitor; LowResourceMonitor _lowResourcesMonitor;
@Before @Before
public void before() throws Exception public void before() throws Exception
{ {
@ -71,6 +71,7 @@ public class LowResourcesMonitorTest
_lowResourcesMonitor.setLowResourcesIdleTimeout(200); _lowResourcesMonitor.setLowResourcesIdleTimeout(200);
_lowResourcesMonitor.setMaxConnections(20); _lowResourcesMonitor.setMaxConnections(20);
_lowResourcesMonitor.setPeriod(900); _lowResourcesMonitor.setPeriod(900);
_lowResourcesMonitor.setMonitoredConnectors( Collections.singleton( _connector ) );
_server.addBean(_lowResourcesMonitor); _server.addBean(_lowResourcesMonitor);
_server.start(); _server.start();
@ -86,38 +87,35 @@ public class LowResourcesMonitorTest
@Test @Test
public void testLowOnThreads() throws Exception public void testLowOnThreads() throws Exception
{ {
_lowResourcesMonitor.setMonitorThreads(true);
Thread.sleep(1200); Thread.sleep(1200);
_threadPool.setMaxThreads(_threadPool.getThreads()-_threadPool.getIdleThreads()+10); _threadPool.setMaxThreads(_threadPool.getThreads()-_threadPool.getIdleThreads()+10);
Thread.sleep(1200); Thread.sleep(1200);
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources()); assertFalse(_lowResourcesMonitor.getReasons(), _lowResourcesMonitor.isLowOnResources());
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
for (int i=0;i<100;i++) for (int i=0;i<100;i++)
{ {
_threadPool.execute(new Runnable() _threadPool.execute(() ->
{ {
@Override try
public void run()
{ {
try latch.await();
{ }
latch.await(); catch (InterruptedException e)
} {
catch (InterruptedException e) e.printStackTrace();
{
e.printStackTrace();
}
} }
}); });
} }
Thread.sleep(1200); Thread.sleep(1200);
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources()); assertTrue(_lowResourcesMonitor.isLowOnResources());
latch.countDown(); latch.countDown();
Thread.sleep(1200); Thread.sleep(1200);
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources()); assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
} }
@ -125,10 +123,13 @@ public class LowResourcesMonitorTest
public void testNotAccepting() throws Exception public void testNotAccepting() throws Exception
{ {
_lowResourcesMonitor.setAcceptingInLowResources(false); _lowResourcesMonitor.setAcceptingInLowResources(false);
_lowResourcesMonitor.setMonitorThreads( true );
Thread.sleep(1200); Thread.sleep(1200);
_threadPool.setMaxThreads(_threadPool.getThreads()-_threadPool.getIdleThreads()+10); int maxThreads = _threadPool.getThreads()-_threadPool.getIdleThreads()+10;
System.out.println("maxThreads:"+maxThreads);
_threadPool.setMaxThreads(maxThreads);
Thread.sleep(1200); Thread.sleep(1200);
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources()); assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
for (AbstractConnector c : _server.getBeans(AbstractConnector.class)) for (AbstractConnector c : _server.getBeans(AbstractConnector.class))
assertThat(c.isAccepting(),Matchers.is(true)); assertThat(c.isAccepting(),Matchers.is(true));
@ -136,31 +137,27 @@ public class LowResourcesMonitorTest
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
for (int i=0;i<100;i++) for (int i=0;i<100;i++)
{ {
_threadPool.execute(new Runnable() _threadPool.execute(() ->
{ {
@Override try
public void run()
{ {
try latch.await();
{ }
latch.await(); catch (InterruptedException e)
} {
catch (InterruptedException e) e.printStackTrace();
{
e.printStackTrace();
}
} }
}); });
} }
Thread.sleep(1200); Thread.sleep(1200);
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources()); assertTrue(_lowResourcesMonitor.isLowOnResources());
for (AbstractConnector c : _server.getBeans(AbstractConnector.class)) for (AbstractConnector c : _server.getBeans(AbstractConnector.class))
assertThat(c.isAccepting(),Matchers.is(false)); assertThat(c.isAccepting(),Matchers.is(false));
latch.countDown(); latch.countDown();
Thread.sleep(1200); Thread.sleep(1200);
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources()); assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
for (AbstractConnector c : _server.getBeans(AbstractConnector.class)) for (AbstractConnector c : _server.getBeans(AbstractConnector.class))
assertThat(c.isAccepting(),Matchers.is(true)); assertThat(c.isAccepting(),Matchers.is(true));
} }
@ -172,7 +169,7 @@ public class LowResourcesMonitorTest
{ {
_lowResourcesMonitor.setMaxMemory(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()+(100*1024*1024)); _lowResourcesMonitor.setMaxMemory(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()+(100*1024*1024));
Thread.sleep(1200); Thread.sleep(1200);
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources()); assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
byte[] data = new byte[100*1024*1024]; byte[] data = new byte[100*1024*1024];
Arrays.fill(data,(byte)1); Arrays.fill(data,(byte)1);
@ -180,13 +177,13 @@ public class LowResourcesMonitorTest
assertThat(hash,not(equalTo(0))); assertThat(hash,not(equalTo(0)));
Thread.sleep(1200); Thread.sleep(1200);
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources()); assertTrue(_lowResourcesMonitor.isLowOnResources());
data=null; data=null;
System.gc(); System.gc();
System.gc(); System.gc();
Thread.sleep(1200); Thread.sleep(1200);
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources()); assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
} }
@ -194,26 +191,27 @@ public class LowResourcesMonitorTest
public void testMaxConnectionsAndMaxIdleTime() throws Exception public void testMaxConnectionsAndMaxIdleTime() throws Exception
{ {
_lowResourcesMonitor.setMaxMemory(0); _lowResourcesMonitor.setMaxMemory(0);
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources()); assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
assertEquals( 20, _lowResourcesMonitor.getMaxConnections() );
Socket[] socket = new Socket[_lowResourcesMonitor.getMaxConnections()+1]; Socket[] socket = new Socket[_lowResourcesMonitor.getMaxConnections()+1];
for (int i=0;i<socket.length;i++) for (int i=0;i<socket.length;i++)
socket[i]=new Socket("localhost",_connector.getLocalPort()); socket[i]=new Socket("localhost",_connector.getLocalPort());
Thread.sleep(1200); Thread.sleep(1200);
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources()); assertTrue(_lowResourcesMonitor.isLowOnResources());
try(Socket newSocket = new Socket("localhost",_connector.getLocalPort())) try(Socket newSocket = new Socket("localhost",_connector.getLocalPort()))
{ {
// wait for low idle time to close sockets, but not new Socket // wait for low idle time to close sockets, but not new Socket
Thread.sleep(1200); Thread.sleep(1200);
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources()); assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
for (int i=0;i<socket.length;i++) for (int i=0;i<socket.length;i++)
Assert.assertEquals(-1,socket[i].getInputStream().read()); assertEquals(-1,socket[i].getInputStream().read());
newSocket.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.UTF_8)); newSocket.getOutputStream().write("GET / HTTP/1.0\r\n\r\n".getBytes(StandardCharsets.UTF_8));
Assert.assertEquals('H',newSocket.getInputStream().read()); assertEquals('H',newSocket.getInputStream().read());
} }
} }
@ -222,11 +220,11 @@ public class LowResourcesMonitorTest
{ {
int monitorPeriod = _lowResourcesMonitor.getPeriod(); int monitorPeriod = _lowResourcesMonitor.getPeriod();
int lowResourcesIdleTimeout = _lowResourcesMonitor.getLowResourcesIdleTimeout(); int lowResourcesIdleTimeout = _lowResourcesMonitor.getLowResourcesIdleTimeout();
Assert.assertThat(lowResourcesIdleTimeout, Matchers.lessThan(monitorPeriod)); assertThat(lowResourcesIdleTimeout, Matchers.lessThan(monitorPeriod));
int maxLowResourcesTime = 5 * monitorPeriod; int maxLowResourcesTime = 5 * monitorPeriod;
_lowResourcesMonitor.setMaxLowResourcesTime(maxLowResourcesTime); _lowResourcesMonitor.setMaxLowResourcesTime(maxLowResourcesTime);
Assert.assertFalse(_lowResourcesMonitor.isLowOnResources()); assertFalse(_lowResourcesMonitor.getReasons(),_lowResourcesMonitor.isLowOnResources());
try(Socket socket0 = new Socket("localhost",_connector.getLocalPort())) try(Socket socket0 = new Socket("localhost",_connector.getLocalPort()))
{ {
@ -236,10 +234,10 @@ public class LowResourcesMonitorTest
// Wait a couple of monitor periods so that // Wait a couple of monitor periods so that
// lowResourceMonitor detects it is in low mode. // lowResourceMonitor detects it is in low mode.
Thread.sleep(2 * monitorPeriod); Thread.sleep(2 * monitorPeriod);
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources()); assertTrue(_lowResourcesMonitor.isLowOnResources());
// We already waited enough for lowResourceMonitor to close socket0. // We already waited enough for lowResourceMonitor to close socket0.
Assert.assertEquals(-1, socket0.getInputStream().read()); assertEquals(-1, socket0.getInputStream().read());
// New connections are not affected by the // New connections are not affected by the
// low mode until maxLowResourcesTime elapses. // low mode until maxLowResourcesTime elapses.
@ -249,11 +247,11 @@ public class LowResourcesMonitorTest
socket1.setSoTimeout(1); socket1.setSoTimeout(1);
InputStream input1 = socket1.getInputStream(); InputStream input1 = socket1.getInputStream();
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources()); assertTrue(_lowResourcesMonitor.isLowOnResources());
try try
{ {
input1.read(); input1.read();
Assert.fail(); fail();
} }
catch (SocketTimeoutException expected) catch (SocketTimeoutException expected)
{ {
@ -263,22 +261,21 @@ public class LowResourcesMonitorTest
Thread.sleep(2 * lowResourcesIdleTimeout); Thread.sleep(2 * lowResourcesIdleTimeout);
// Verify the new socket is still open. // Verify the new socket is still open.
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources()); assertTrue(_lowResourcesMonitor.isLowOnResources());
try try
{ {
input1.read(); input1.read();
Assert.fail(); fail();
} }
catch (SocketTimeoutException expected) catch (SocketTimeoutException expected)
{ {
} }
// Let the maxLowResourcesTime elapse. // Let the maxLowResourcesTime elapse.
Thread.sleep(maxLowResourcesTime); Thread.sleep(maxLowResourcesTime);
Assert.assertTrue(_lowResourcesMonitor.isLowOnResources()); assertTrue(_lowResourcesMonitor.isLowOnResources());
// Now also the new socket should be closed. // Now also the new socket should be closed.
Assert.assertEquals(-1, input1.read()); assertEquals(-1, input1.read());
} }
} }
} }

View File

@ -123,7 +123,8 @@ import org.eclipse.jetty.util.resource.ResourceFactory;
* otherGzipFileExtensions * otherGzipFileExtensions
* Other file extensions that signify that a file is already compressed. Eg ".svgz" * Other file extensions that signify that a file is already compressed. Eg ".svgz"
* *
* * encodingHeaderCacheSize
* Max entries in a cache of ACCEPT-ENCODING headers.
* </pre> * </pre>
* *
*/ */

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.servlet; package org.eclipse.jetty.servlet;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.startsWith; import static org.hamcrest.Matchers.startsWith;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -59,6 +60,8 @@ import org.eclipse.jetty.server.handler.ResourceHandler;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.UrlEncoded; import org.eclipse.jetty.util.UrlEncoded;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.junit.After; import org.junit.After;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
@ -157,6 +160,42 @@ public class DispatcherTest
assertEquals(expected, responses); assertEquals(expected, responses);
} }
@Test
public void testForwardWithBadParams() throws Exception
{
try(StacklessLogging nostack = new StacklessLogging(ServletHandler.class))
{
Log.getLogger(ServletHandler.class).info("Expect Not valid UTF8 warnings...");
_contextHandler.addServlet(AlwaysForwardServlet.class, "/forward/*");
_contextHandler.addServlet(EchoServlet.class, "/echo/*");
String response;
response = _connector.getResponse("GET /context/forward/?echo=allgood HTTP/1.0\n\n");
assertThat(response,containsString(" 200 OK"));
assertThat(response,containsString("allgood"));
response = _connector.getResponse("GET /context/forward/params?echo=allgood HTTP/1.0\n\n");
assertThat(response,containsString(" 200 OK"));
assertThat(response,containsString("allgood"));
assertThat(response,containsString("forward"));
response = _connector.getResponse("GET /context/forward/badparams?echo=badparams HTTP/1.0\n\n");
assertThat(response,containsString(" 500 "));
response = _connector.getResponse("GET /context/forward/?echo=badclient&bad=%88%A4 HTTP/1.0\n\n");
assertThat(response,containsString(" 400 "));
response = _connector.getResponse("GET /context/forward/params?echo=badclient&bad=%88%A4 HTTP/1.0\n\n");
assertThat(response,containsString(" 400 "));
response = _connector.getResponse("GET /context/forward/badparams?echo=badclientandparam&bad=%88%A4 HTTP/1.0\n\n");
assertThat(response,containsString(" 500 "));
}
}
@Test @Test
public void testInclude() throws Exception public void testInclude() throws Exception
{ {
@ -358,6 +397,20 @@ public class DispatcherTest
dispatcher.forward(request, response); dispatcher.forward(request, response);
} }
} }
public static class AlwaysForwardServlet extends HttpServlet implements Servlet
{
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
if ("/params".equals(request.getPathInfo()))
getServletContext().getRequestDispatcher("/echo?echo=forward").forward(request, response);
else if ("/badparams".equals(request.getPathInfo()))
getServletContext().getRequestDispatcher("/echo?echo=forward&fbad=%88%A4").forward(request, response);
else
getServletContext().getRequestDispatcher("/echo").forward(request, response);
}
}
public static class ForwardNonUTF8Servlet extends HttpServlet implements Servlet public static class ForwardNonUTF8Servlet extends HttpServlet implements Servlet
@ -484,15 +537,20 @@ public class DispatcherTest
@Override @Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException
{ {
String echoText = req.getParameter("echo"); String[] echoText = req.getParameterValues("echo");
if ( echoText == null ) if ( echoText == null || echoText.length==0)
{ {
throw new ServletException("echo is a required parameter"); throw new ServletException("echo is a required parameter");
} }
else if (echoText.length==1)
{
res.getWriter().print(echoText[0]);
}
else else
{ {
res.getWriter().print(echoText); for (String text:echoText)
res.getWriter().print(text);
} }
} }
} }

View File

@ -212,7 +212,19 @@ public class SslContextFactory extends AbstractLifeCycle implements Dumpable
{ {
setTrustAll(trustAll); setTrustAll(trustAll);
addExcludeProtocols("SSL", "SSLv2", "SSLv2Hello", "SSLv3"); addExcludeProtocols("SSL", "SSLv2", "SSLv2Hello", "SSLv3");
// Exclude weak / insecure ciphers
setExcludeCipherSuites("^.*_(MD5|SHA|SHA1)$"); setExcludeCipherSuites("^.*_(MD5|SHA|SHA1)$");
// Exclude ciphers that don't support forward secrecy
addExcludeCipherSuites("^TLS_RSA_.*$");
// The following exclusions are present to cleanup known bad cipher
// suites that may be accidentally included via include patterns.
// The default enabled cipher list in Java will not include these
// (but they are available in the supported list).
addExcludeCipherSuites("^SSL_.*$");
addExcludeCipherSuites("^.*_NULL_.*$");
addExcludeCipherSuites("^.*_anon_.*$");
if (keyStorePath != null) if (keyStorePath != null)
setKeyStorePath(keyStorePath); setKeyStorePath(keyStorePath);
} }

View File

@ -95,7 +95,7 @@ public class ExecutorThreadPool extends ContainerLifeCycle implements ThreadPool
_group = group; _group = group;
_minThreads = minThreads; _minThreads = minThreads;
_reservedThreads = reservedThreads; _reservedThreads = reservedThreads;
_budget = new ThreadPoolBudget(this,minThreads); _budget = new ThreadPoolBudget(this);
} }
/** /**
@ -140,6 +140,8 @@ public class ExecutorThreadPool extends ContainerLifeCycle implements ThreadPool
@Override @Override
public void setMaxThreads(int threads) public void setMaxThreads(int threads)
{ {
if (_budget!=null)
_budget.check(threads);
_executor.setCorePoolSize(threads); _executor.setCorePoolSize(threads);
_executor.setMaximumPoolSize(threads); _executor.setMaximumPoolSize(threads);
} }

View File

@ -32,6 +32,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.jetty.util.BlockingArrayQueue; import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.ProcessorUtils;
import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedAttribute;
import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation; import org.eclipse.jetty.util.annotation.ManagedOperation;
@ -117,7 +118,7 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
} }
_jobs=queue; _jobs=queue;
_threadGroup=threadGroup; _threadGroup=threadGroup;
setThreadPoolBudget(new ThreadPoolBudget(this,_minThreads)); setThreadPoolBudget(new ThreadPoolBudget(this));
} }
@Override @Override
@ -257,6 +258,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
@Override @Override
public void setMaxThreads(int maxThreads) public void setMaxThreads(int maxThreads)
{ {
if (_budget!=null)
_budget.check(maxThreads);
_maxThreads = maxThreads; _maxThreads = maxThreads;
if (_minThreads > _maxThreads) if (_minThreads > _maxThreads)
_minThreads = _maxThreads; _minThreads = _maxThreads;

View File

@ -19,13 +19,11 @@
package org.eclipse.jetty.util.thread; package org.eclipse.jetty.util.thread;
import java.io.Closeable; import java.io.Closeable;
import java.io.IOException;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.eclipse.jetty.util.ProcessorUtils;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
@ -48,8 +46,8 @@ public class ThreadPoolBudget
*/ */
public class Leased implements Lease public class Leased implements Lease
{ {
final Object leasee; private final Object leasee;
final int threads; private final int threads;
private Leased(Object leasee,int threads) private Leased(Object leasee,int threads)
{ {
@ -66,8 +64,7 @@ public class ThreadPoolBudget
@Override @Override
public void close() public void close()
{ {
info.remove(this); leases.remove(this);
allocations.remove(this);
warned.set(false); warned.set(false);
} }
} }
@ -75,7 +72,7 @@ public class ThreadPoolBudget
private static final Lease NOOP_LEASE = new Lease() private static final Lease NOOP_LEASE = new Lease()
{ {
@Override @Override
public void close() throws IOException public void close()
{ {
} }
@ -86,25 +83,26 @@ public class ThreadPoolBudget
} }
}; };
final ThreadPool.SizedThreadPool pool; private final Set<Leased> leases = new CopyOnWriteArraySet<>();
final Set<Leased> allocations = new CopyOnWriteArraySet<>(); private final AtomicBoolean warned = new AtomicBoolean();
final Set<Leased> info = new CopyOnWriteArraySet<>(); private final ThreadPool.SizedThreadPool pool;
final AtomicBoolean warned = new AtomicBoolean(); private final int warnAt;
final int warnAt;
/** /**
* Construct a budget for a SizedThreadPool, with the warning level set by heuristic. * Construct a budget for a SizedThreadPool.
* @param pool The pool to budget thread allocation for. * @param pool The pool to budget thread allocation for.
*/ */
public ThreadPoolBudget(ThreadPool.SizedThreadPool pool) public ThreadPoolBudget(ThreadPool.SizedThreadPool pool)
{ {
this(pool,Math.min(ProcessorUtils.availableProcessors(),pool.getMinThreads())); this.pool = pool;
this.warnAt = -1;
} }
/** /**
* @param pool The pool to budget thread allocation for. * @param pool The pool to budget thread allocation for.
* @param warnAt The level of free threads at which a warning is generated. * @param warnAt The level of free threads at which a warning is generated.
*/ */
@Deprecated
public ThreadPoolBudget(ThreadPool.SizedThreadPool pool, int warnAt) public ThreadPoolBudget(ThreadPool.SizedThreadPool pool, int warnAt)
{ {
this.pool = pool; this.pool = pool;
@ -118,52 +116,60 @@ public class ThreadPoolBudget
public void reset() public void reset()
{ {
allocations.clear(); leases.clear();
info.clear();
warned.set(false); warned.set(false);
} }
public Lease leaseTo(Object leasee, int threads) public Lease leaseTo(Object leasee, int threads)
{ {
Leased lease = new Leased(leasee,threads); Leased lease = new Leased(leasee,threads);
allocations.add(lease); leases.add(lease);
check(); try
return lease; {
check(pool.getMaxThreads());
return lease;
}
catch(IllegalStateException e)
{
lease.close();
throw e;
}
} }
/** /**
* Check registered allocations against the budget. * <p>Checks leases against the given number of {@code maxThreads}.</p>
*
* @param maxThreads A proposed change to the maximum threads to check.
* @return true if passes check, false if otherwise (see logs for details)
* @throws IllegalStateException if insufficient threads are configured. * @throws IllegalStateException if insufficient threads are configured.
*/ */
public void check() throws IllegalStateException public boolean check(int maxThreads) throws IllegalStateException
{ {
int required = allocations.stream() int required = leases.stream()
.mapToInt(Lease::getThreads) .mapToInt(Lease::getThreads)
.sum(); .sum();
int maximum = pool.getMaxThreads(); int left = maxThreads - required;
int actual = maximum - required; if (left <= 0)
if (actual <= 0)
{ {
infoOnLeases(); printInfoOnLeases();
throw new IllegalStateException(String.format("Insufficient configured threads: required=%d < max=%d for %s", required, maximum, pool)); throw new IllegalStateException(String.format("Insufficient configured threads: required=%d < max=%d for %s", required, maxThreads, pool));
} }
if (actual < warnAt) if (left < warnAt)
{ {
infoOnLeases();
if (warned.compareAndSet(false,true)) if (warned.compareAndSet(false,true))
LOG.warn("Low configured threads: (max={} - required={})={} < warnAt={} for {}", maximum, required, actual, warnAt, pool); {
printInfoOnLeases();
LOG.info("Low configured threads: (max={} - required={})={} < warnAt={} for {}", maxThreads, required, left, warnAt, pool);
}
return false;
} }
return true;
} }
private void infoOnLeases() private void printInfoOnLeases()
{ {
allocations.stream().filter(lease->!info.contains(lease)) leases.forEach(lease-> LOG.info("{} requires {} threads from {}",lease.leasee,lease.getThreads(),pool));
.forEach(lease->{
info.add(lease);
LOG.info("{} requires {} threads from {}",lease.leasee,lease.getThreads(),pool);
});
} }
public static Lease leaseFrom(Executor executor, Object leasee, int threads) public static Lease leaseFrom(Executor executor, Object leasee, int threads)

View File

@ -0,0 +1,94 @@
//
// ========================================================================
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util.thread;
import static org.hamcrest.MatcherAssert.assertThat;
import org.eclipse.jetty.util.ProcessorUtils;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
public abstract class AbstractThreadPoolTest
{
private static int originalCoreCount;
@BeforeClass
public static void saveState()
{
originalCoreCount = ProcessorUtils.availableProcessors();
}
@AfterClass
public static void restoreState()
{
ProcessorUtils.setAvailableProcessors(originalCoreCount);
}
abstract protected SizedThreadPool newPool(int max);
@Test
public void testBudget_constructMaxThenLease()
{
SizedThreadPool pool = newPool(4);
pool.getThreadPoolBudget().leaseTo(this,2);
try
{
pool.getThreadPoolBudget().leaseTo(this,3);
Assert.fail();
}
catch(IllegalStateException e)
{
Assert.assertThat(e.getMessage(),Matchers.containsString("Insufficient configured threads"));
}
pool.getThreadPoolBudget().leaseTo(this,1);
}
@Test
public void testBudget_LeaseThenSetMax()
{
SizedThreadPool pool = newPool(4);
pool.getThreadPoolBudget().leaseTo(this,2);
pool.setMaxThreads(3);
try
{
pool.setMaxThreads(1);
Assert.fail();
}
catch(IllegalStateException e)
{
Assert.assertThat(e.getMessage(),Matchers.containsString("Insufficient configured threads"));
}
Assert.assertThat(pool.getMaxThreads(),Matchers.is(3));
}
}

View File

@ -0,0 +1,30 @@
//
// ========================================================================
// Copyright (c) 1995-2018 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.util.thread;
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
public class ExecutorThreadPoolTest extends AbstractThreadPoolTest
{
@Override
protected SizedThreadPool newPool(int max)
{
return new ExecutorThreadPool(max);
}
}

View File

@ -18,16 +18,6 @@
package org.eclipse.jetty.util.thread; package org.eclipse.jetty.util.thread;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.toolchain.test.AdvancedRunner;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.Matchers.greaterThanOrEqualTo; import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@ -35,8 +25,17 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@RunWith(AdvancedRunner.class) import java.util.concurrent.CountDownLatch;
public class QueuedThreadPoolTest import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.util.ProcessorUtils;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
import org.junit.Assert;
import org.junit.Test;
public class QueuedThreadPoolTest extends AbstractThreadPoolTest
{ {
private final AtomicInteger _jobs=new AtomicInteger(); private final AtomicInteger _jobs=new AtomicInteger();
@ -297,4 +296,11 @@ public class QueuedThreadPoolTest
{ {
new QueuedThreadPool(4, 8); new QueuedThreadPool(4, 8);
} }
@Override
protected SizedThreadPool newPool(int max)
{
return new QueuedThreadPool(max);
}
} }

View File

@ -95,9 +95,10 @@
* found ending with ".gz" (default false) * found ending with ".gz" (default false)
* (deprecated: use precompressed) * (deprecated: use precompressed)
* *
* precompressed If set to a comma separated list of file extensions, these * precompressed If set to a comma separated list of encoding types (that may be
* indicate compressed formats that are known to map to a mime-type * listed in a requests Accept-Encoding header) to file
* that may be listed in a requests Accept-Encoding header. * extension mappings to look for and serve. For example:
* "br=.br,gzip=.gz,bzip2=.bz".
* If set to a boolean True, then a default set of compressed formats * If set to a boolean True, then a default set of compressed formats
* will be used, otherwise no precompressed formats. * will be used, otherwise no precompressed formats.
* *
@ -115,9 +116,6 @@
* *
* stylesheet Set with the location of an optional stylesheet that will be used * stylesheet Set with the location of an optional stylesheet that will be used
* to decorate the directory listing html. * to decorate the directory listing html.
*
* aliases If True, aliases of resources are allowed (eg. symbolic
* links and caps variations). May bypass security constraints.
* *
* etags If True, weak etags will be generated and handled. * etags If True, weak etags will be generated and handled.
* *
@ -134,15 +132,17 @@
* cacheControl If set, all static content will have this value set as the cache-control * cacheControl If set, all static content will have this value set as the cache-control
* header. * header.
* *
* encodingHeaderCacheSize
* Max entries in a cache of ACCEPT-ENCODING headers.
*
* otherGzipFileExtensions
* defaults to .svgz but a comma separated list of gzip equivalent file extensions can be supplied
*
--> -->
<!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - --> <!-- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -->
<servlet> <servlet>
<servlet-name>default</servlet-name> <servlet-name>default</servlet-name>
<servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class> <servlet-class>org.eclipse.jetty.servlet.DefaultServlet</servlet-class>
<init-param>
<param-name>aliases</param-name>
<param-value>false</param-value>
</init-param>
<init-param> <init-param>
<param-name>acceptRanges</param-name> <param-name>acceptRanges</param-name>
<param-value>true</param-value> <param-value>true</param-value>
@ -171,12 +171,6 @@
<param-name>maxCachedFiles</param-name> <param-name>maxCachedFiles</param-name>
<param-value>2048</param-value> <param-value>2048</param-value>
</init-param> </init-param>
<!--
<init-param>
<param-name>precompressed</param-name>
<param-value>gzip,br</param-value>
</init-param>
-->
<init-param> <init-param>
<param-name>etags</param-name> <param-name>etags</param-name>
<param-value>false</param-value> <param-value>false</param-value>
@ -185,18 +179,6 @@
<param-name>useFileMappedBuffer</param-name> <param-name>useFileMappedBuffer</param-name>
<param-value>true</param-value> <param-value>true</param-value>
</init-param> </init-param>
<!--
<init-param>
<param-name>resourceCache</param-name>
<param-value>resourceCache</param-value>
</init-param>
-->
<!--
<init-param>
<param-name>cacheControl</param-name>
<param-value>max-age=3600,public</param-value>
</init-param>
-->
<load-on-startup>0</load-on-startup> <load-on-startup>0</load-on-startup>
</servlet> </servlet>

View File

@ -15,6 +15,14 @@
<param-value>a context value</param-value> <param-value>a context value</param-value>
</context-param> </context-param>
<!-- Add or override servlet init parameter -->
<servlet>
<servlet-name>default</servlet-name>
<init-param>
<param-name>precompressed</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<!-- Add or override servlet init parameter --> <!-- Add or override servlet init parameter -->
<servlet> <servlet>