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

This commit is contained in:
Lachlan Roberts 2020-08-26 14:27:47 +10:00
commit cff6121c45
16 changed files with 233 additions and 169 deletions

View File

@ -29,6 +29,7 @@ import org.eclipse.jetty.deploy.PropertiesConfigurationManager;
import org.eclipse.jetty.deploy.bindings.DebugListenerBinding;
import org.eclipse.jetty.deploy.providers.WebAppProvider;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.io.ConnectionStatistics;
import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.rewrite.handler.MsieSslRule;
import org.eclipse.jetty.rewrite.handler.RewriteHandler;
@ -43,7 +44,6 @@ import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.LowResourceMonitor;
import org.eclipse.jetty.server.SecureRequestCustomizer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnectionStatistics;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.server.SslConnectionFactory;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
@ -180,7 +180,7 @@ public class LikeJettyXml
StatisticsHandler stats = new StatisticsHandler();
stats.setHandler(server.getHandler());
server.setHandler(stats);
ServerConnectionStatistics.addToAllConnectors(server);
server.addBeanToAllConnectors(new ConnectionStatistics());
// === Rewrite Handler
RewriteHandler rewrite = new RewriteHandler();

View File

@ -20,9 +20,9 @@ package org.eclipse.jetty.embedded;
import java.lang.management.ManagementFactory;
import org.eclipse.jetty.io.ConnectionStatistics;
import org.eclipse.jetty.jmx.MBeanContainer;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnectionStatistics;
import org.eclipse.jetty.servlet.DefaultServlet;
import org.eclipse.jetty.servlet.ServletContextHandler;
@ -45,7 +45,7 @@ public class OneServletContextJmxStats
context.addServlet(DefaultServlet.class, "/");
// Add Connector Statistics tracking to all connectors
ServerConnectionStatistics.addToAllConnectors(server);
server.addBeanToAllConnectors(new ConnectionStatistics());
return server;
}

View File

@ -440,9 +440,14 @@ public abstract class HttpDestination extends ContainerLifeCycle implements Dest
{
// Trigger the next request after releasing the connection.
if (connectionPool.release(connection))
{
send(false);
}
else
{
connection.close();
send(true);
}
}
else
{

View File

@ -18,22 +18,22 @@
package org.eclipse.jetty.client;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.client.api.Connection;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Pool;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.Locker;
@ManagedObject
public class RoundRobinConnectionPool extends MultiplexConnectionPool
{
private static final Logger LOG = Log.getLogger(RoundRobinConnectionPool.class);
private final AtomicInteger offset = new AtomicInteger();
private final Locker lock = new Locker();
private final Pool<Connection> pool;
private int offset;
public RoundRobinConnectionPool(HttpDestination destination, int maxConnections, Callback requester)
{
@ -47,26 +47,32 @@ public class RoundRobinConnectionPool extends MultiplexConnectionPool
}
@Override
protected Connection activate()
protected Connection acquire(boolean create)
{
int offset = this.offset.get();
Connection connection = activate(offset);
if (connection != null)
this.offset.getAndIncrement();
return connection;
// If there are queued requests and connections get
// closed due to idle timeout or overuse, we want to
// aggressively try to open new connections to replace
// those that were closed to process queued requests.
return super.acquire(true);
}
private Connection activate(int offset)
@Override
protected Connection activate()
{
Pool<Connection>.Entry entry = pool.acquireAt(Math.abs(offset % pool.getMaxEntries()));
if (LOG.isDebugEnabled())
LOG.debug("activated '{}'", entry);
if (entry != null)
Pool<Connection>.Entry entry;
try (Locker.Lock l = lock.lock())
{
Connection connection = entry.getPooled();
acquired(connection);
return connection;
int index = Math.abs(offset % pool.getMaxEntries());
entry = pool.acquireAt(index);
if (LOG.isDebugEnabled())
LOG.debug("activated at index={} entry={}", index, entry);
if (entry != null)
++offset;
}
return null;
if (entry == null)
return null;
Connection connection = entry.getPooled();
acquired(connection);
return connection;
}
}

View File

@ -49,6 +49,8 @@ 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.resource.Resource;
import org.eclipse.jetty.util.thread.Locker;
import org.eclipse.jetty.util.thread.Locker.Lock;
import org.eclipse.jetty.xml.XmlConfiguration;
/**
@ -69,7 +71,6 @@ import org.eclipse.jetty.xml.XmlConfiguration;
public class DeploymentManager extends ContainerLifeCycle
{
private static final Logger LOG = Log.getLogger(DeploymentManager.class);
private MultiException onStartupErrors;
/**
* Represents a single tracked app within the deployment manager.
@ -124,7 +125,9 @@ public class DeploymentManager extends ContainerLifeCycle
this.stateTimestamps.put(node, Long.valueOf(System.currentTimeMillis()));
}
}
private MultiException _onStartupErrors;
private Locker _errorLocker = new Locker();
private final List<AppProvider> _providers = new ArrayList<AppProvider>();
private final AppLifeCycle _lifecycle = new AppLifeCycle();
private final Queue<AppEntry> _apps = new ConcurrentLinkedQueue<AppEntry>();
@ -249,9 +252,10 @@ public class DeploymentManager extends ContainerLifeCycle
startAppProvider(provider);
}
if (onStartupErrors != null)
try (Lock lock = _errorLocker.lock())
{
onStartupErrors.ifExceptionThrow();
if (_onStartupErrors != null)
_onStartupErrors.ifExceptionThrow();
}
super.doStart();
@ -538,13 +542,15 @@ public class DeploymentManager extends ContainerLifeCycle
}
}
private synchronized void addOnStartupError(Throwable cause)
private void addOnStartupError(Throwable cause)
{
if (onStartupErrors == null)
try (Lock lock = _errorLocker.lock())
{
onStartupErrors = new MultiException();
if (_onStartupErrors == null)
_onStartupErrors = new MultiException();
_onStartupErrors.add(cause);
}
onStartupErrors.add(cause);
}
/**

View File

@ -25,7 +25,6 @@ import org.eclipse.jetty.client.HttpReceiver;
import org.eclipse.jetty.client.HttpSender;
import org.eclipse.jetty.client.api.Result;
import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.IStream;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.ResetFrame;
@ -99,12 +98,9 @@ public class HttpChannelOverHTTP2 extends HttpChannel
@Override
public void release()
{
setStream(null);
connection.release(this);
}
void onStreamClosed(IStream stream)
{
connection.onStreamClosed(stream, this);
getHttpDestination().release(getHttpConnection());
}
@Override

View File

@ -35,7 +35,6 @@ import org.eclipse.jetty.client.HttpRequest;
import org.eclipse.jetty.client.SendFailure;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.IStream;
import org.eclipse.jetty.http2.api.Session;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.Log;
@ -119,16 +118,6 @@ public class HttpConnectionOverHTTP2 extends HttpConnection implements Sweeper.S
}
}
void onStreamClosed(IStream stream, HttpChannelOverHTTP2 channel)
{
if (LOG.isDebugEnabled())
LOG.debug("{} closed for {}", stream, channel);
channel.setStream(null);
// Only non-push channels are released.
if (stream.isLocal())
getHttpDestination().release(this);
}
@Override
public boolean onIdleTimeout(long idleTimeout)
{

View File

@ -38,7 +38,6 @@ import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.http2.ErrorCode;
import org.eclipse.jetty.http2.IStream;
import org.eclipse.jetty.http2.api.Stream;
import org.eclipse.jetty.http2.frames.DataFrame;
import org.eclipse.jetty.http2.frames.HeadersFrame;
@ -201,12 +200,6 @@ public class HttpReceiverOverHTTP2 extends HttpReceiver implements Stream.Listen
callback.succeeded();
}
@Override
public void onClosed(Stream stream)
{
getHttpChannel().onStreamClosed((IStream)stream);
}
private void notifyContent(HttpExchange exchange, DataFrame frame, Callback callback)
{
contentNotifier.offer(exchange, frame, callback);

View File

@ -68,7 +68,8 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDir;
import org.eclipse.jetty.toolchain.test.jupiter.WorkDirExtension;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.Utf8StringBuilder;
@ -78,9 +79,9 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.ExtendWith;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -89,11 +90,13 @@ import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
@ExtendWith(WorkDirExtension.class)
public class AsyncMiddleManServletTest
{
private static final Logger LOG = Log.getLogger(AsyncMiddleManServletTest.class);
private static final String PROXIED_HEADER = "X-Proxied";
public WorkDir workDir;
private HttpClient client;
private Server proxy;
private ServerConnector proxyConnector;
@ -193,7 +196,7 @@ public class AsyncMiddleManServletTest
testClientRequestContentKnownLengthGzipped(1024 * 1024, true);
}
private void testClientRequestContentKnownLengthGzipped(int length, final boolean expectChunked) throws Exception
private void testClientRequestContentKnownLengthGzipped(int length, boolean expectChunked) throws Exception
{
byte[] bytes = new byte[length];
new Random().nextBytes(bytes);
@ -240,7 +243,7 @@ public class AsyncMiddleManServletTest
{
byte[] bytes = new byte[1024];
new Random().nextBytes(bytes);
final byte[] gzipBytes = gzip(bytes);
byte[] gzipBytes = gzip(bytes);
startServer(new HttpServlet()
{
@ -450,7 +453,7 @@ public class AsyncMiddleManServletTest
@Test
public void testDiscardUpstreamAndDownstreamKnownContentLengthGzipped() throws Exception
{
final byte[] bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(StandardCharsets.UTF_8);
byte[] bytes = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".getBytes(StandardCharsets.UTF_8);
startServer(new HttpServlet()
{
@Override
@ -520,7 +523,7 @@ public class AsyncMiddleManServletTest
@Test
public void testUpstreamTransformationThrowsAfterCommittingProxyRequest() throws Exception
{
try (StacklessLogging scope = new StacklessLogging(HttpChannel.class))
try (StacklessLogging ignored = new StacklessLogging(HttpChannel.class))
{
startServer(new EchoHttpServlet());
startProxy(new AsyncMiddleManServlet()
@ -545,7 +548,7 @@ public class AsyncMiddleManServletTest
});
startClient();
final CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
DeferredContentProvider content = new DeferredContentProvider();
client.newRequest("localhost", serverConnector.getLocalPort())
.content(content)
@ -644,7 +647,7 @@ public class AsyncMiddleManServletTest
testLargeChunkedBufferedDownstreamTransformation(true);
}
private void testLargeChunkedBufferedDownstreamTransformation(final boolean gzipped) throws Exception
private void testLargeChunkedBufferedDownstreamTransformation(boolean gzipped) throws Exception
{
// Tests the race between a incomplete write performed from ProxyResponseListener.onSuccess()
// and ProxyResponseListener.onComplete() being called before the write has completed.
@ -684,7 +687,7 @@ public class AsyncMiddleManServletTest
});
startClient();
final CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
client.newRequest("localhost", serverConnector.getLocalPort())
.onResponseContent((response, content) ->
{
@ -755,7 +758,7 @@ public class AsyncMiddleManServletTest
});
startClient();
final CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
DeferredContentProvider content = new DeferredContentProvider();
client.newRequest("localhost", serverConnector.getLocalPort())
.content(content)
@ -776,7 +779,7 @@ public class AsyncMiddleManServletTest
@Test
public void testClientRequestReadFailsOnSecondRead() throws Exception
{
try (StacklessLogging scope = new StacklessLogging(HttpChannel.class))
try (StacklessLogging ignored = new StacklessLogging(HttpChannel.class))
{
startServer(new EchoHttpServlet());
startProxy(new AsyncMiddleManServlet()
@ -794,7 +797,7 @@ public class AsyncMiddleManServletTest
});
startClient();
final CountDownLatch latch = new CountDownLatch(1);
CountDownLatch latch = new CountDownLatch(1);
DeferredContentProvider content = new DeferredContentProvider();
client.newRequest("localhost", serverConnector.getLocalPort())
.content(content)
@ -824,7 +827,7 @@ public class AsyncMiddleManServletTest
testProxyResponseWriteFails(2);
}
private void testProxyResponseWriteFails(final int writeCount) throws Exception
private void testProxyResponseWriteFails(int writeCount) throws Exception
{
startServer(new HttpServlet()
{
@ -862,11 +865,11 @@ public class AsyncMiddleManServletTest
@Test
public void testAfterContentTransformer() throws Exception
{
final String key0 = "id";
String key0 = "id";
long value0 = 1;
final String key1 = "channel";
String key1 = "channel";
String value1 = "foo";
final String json = "{ \"" + key0 + "\":" + value0 + ", \"" + key1 + "\":\"" + value1 + "\" }";
String json = "{ \"" + key0 + "\":" + value0 + ", \"" + key1 + "\":\"" + value1 + "\" }";
startServer(new HttpServlet()
{
@Override
@ -875,7 +878,7 @@ public class AsyncMiddleManServletTest
response.getOutputStream().write(json.getBytes(StandardCharsets.UTF_8));
}
});
final String key2 = "c";
String key2 = "c";
startProxy(new AsyncMiddleManServlet()
{
@Override
@ -927,9 +930,9 @@ public class AsyncMiddleManServletTest
testAfterContentTransformerInputStreamReset(true);
}
private void testAfterContentTransformerInputStreamReset(final boolean overflow) throws Exception
private void testAfterContentTransformerInputStreamReset(boolean overflow) throws Exception
{
final byte[] data = new byte[]{'c', 'o', 'f', 'f', 'e', 'e'};
byte[] data = new byte[]{'c', 'o', 'f', 'f', 'e', 'e'};
startServer(new HttpServlet()
{
@Override
@ -979,18 +982,17 @@ public class AsyncMiddleManServletTest
assertArrayEquals(data, response.getContent());
}
@Disabled("See issue #3974")
@Test
public void testAfterContentTransformerOverflowingToDisk() throws Exception
{
// Make sure the temporary directory we use exists and it's empty.
final Path targetTestsDir = prepareTargetTestsDir();
Path targetTestsDir = workDir.getEmptyPathDir();
final String key0 = "id";
String key0 = "id";
long value0 = 1;
final String key1 = "channel";
String key1 = "channel";
String value1 = "foo";
final String json = "{ \"" + key0 + "\":" + value0 + ", \"" + key1 + "\":\"" + value1 + "\" }";
String json = "{ \"" + key0 + "\":" + value0 + ", \"" + key1 + "\":\"" + value1 + "\" }";
startServer(new HttpServlet()
{
@Override
@ -999,9 +1001,9 @@ public class AsyncMiddleManServletTest
response.getOutputStream().write(json.getBytes(StandardCharsets.UTF_8));
}
});
final String inputPrefix = "in_";
final String outputPrefix = "out_";
final String key2 = "c";
String inputPrefix = "in_";
String outputPrefix = "out_";
String key2 = "c";
startProxy(new AsyncMiddleManServlet()
{
@Override
@ -1065,7 +1067,7 @@ public class AsyncMiddleManServletTest
@Test
public void testAfterContentTransformerClosingFilesOnClientRequestException() throws Exception
{
final Path targetTestsDir = prepareTargetTestsDir();
Path targetTestsDir = workDir.getEmptyPathDir();
startServer(new HttpServlet()
{
@ -1075,7 +1077,7 @@ public class AsyncMiddleManServletTest
IO.copy(request.getInputStream(), IO.getNullStream());
}
});
final CountDownLatch destroyLatch = new CountDownLatch(1);
CountDownLatch destroyLatch = new CountDownLatch(1);
startProxy(new AsyncMiddleManServlet()
{
@Override
@ -1110,7 +1112,7 @@ public class AsyncMiddleManServletTest
startClient();
// Send only part of the content; the proxy will idle timeout.
final byte[] data = new byte[]{'c', 'a', 'f', 'e'};
byte[] data = new byte[]{'c', 'a', 'f', 'e'};
ContentResponse response = client.newRequest("localhost", serverConnector.getLocalPort())
.content(new BytesContentProvider(data)
{
@ -1130,9 +1132,9 @@ public class AsyncMiddleManServletTest
@Test
public void testAfterContentTransformerClosingFilesOnServerResponseException() throws Exception
{
final Path targetTestsDir = prepareTargetTestsDir();
Path targetTestsDir = workDir.getEmptyPathDir();
final CountDownLatch serviceLatch = new CountDownLatch(1);
CountDownLatch serviceLatch = new CountDownLatch(1);
startServer(new HttpServlet()
{
@Override
@ -1147,7 +1149,7 @@ public class AsyncMiddleManServletTest
serviceLatch.countDown();
}
});
final CountDownLatch destroyLatch = new CountDownLatch(1);
CountDownLatch destroyLatch = new CountDownLatch(1);
startProxy(new AsyncMiddleManServlet()
{
@Override
@ -1200,13 +1202,13 @@ public class AsyncMiddleManServletTest
testAfterContentTransformerDoNoTransform(true, true);
}
private void testAfterContentTransformerDoNoTransform(final boolean readSource, final boolean useDisk) throws Exception
private void testAfterContentTransformerDoNoTransform(boolean readSource, boolean useDisk) throws Exception
{
final String key0 = "id";
String key0 = "id";
long value0 = 1;
final String key1 = "channel";
String key1 = "channel";
String value1 = "foo";
final String json = "{ \"" + key0 + "\":" + value0 + ", \"" + key1 + "\":\"" + value1 + "\" }";
String json = "{ \"" + key0 + "\":" + value0 + ", \"" + key1 + "\":\"" + value1 + "\" }";
startServer(new HttpServlet()
{
@Override
@ -1268,7 +1270,7 @@ public class AsyncMiddleManServletTest
response.setHeader(HttpHeader.WWW_AUTHENTICATE.asString(), "Basic realm=\"test\"");
}
});
final AtomicBoolean transformed = new AtomicBoolean();
AtomicBoolean transformed = new AtomicBoolean();
startProxy(new AsyncMiddleManServlet()
{
@Override
@ -1299,7 +1301,7 @@ public class AsyncMiddleManServletTest
public void testProxyRequestHeadersSentWhenDiscardingContent() throws Exception
{
startServer(new EchoHttpServlet());
final CountDownLatch proxyRequestLatch = new CountDownLatch(1);
CountDownLatch proxyRequestLatch = new CountDownLatch(1);
startProxy(new AsyncMiddleManServlet()
{
@Override
@ -1347,7 +1349,7 @@ public class AsyncMiddleManServletTest
public void testProxyRequestHeadersNotSentUntilContent() throws Exception
{
startServer(new EchoHttpServlet());
final CountDownLatch proxyRequestLatch = new CountDownLatch(1);
CountDownLatch proxyRequestLatch = new CountDownLatch(1);
startProxy(new AsyncMiddleManServlet()
{
@Override
@ -1395,7 +1397,7 @@ public class AsyncMiddleManServletTest
public void testProxyRequestHeadersNotSentUntilFirstContent() throws Exception
{
startServer(new EchoHttpServlet());
final CountDownLatch proxyRequestLatch = new CountDownLatch(1);
CountDownLatch proxyRequestLatch = new CountDownLatch(1);
startProxy(new AsyncMiddleManServlet()
{
@Override
@ -1464,7 +1466,7 @@ public class AsyncMiddleManServletTest
@Test
public void testTransparentProxyWithIdentityContentTransformer() throws Exception
{
final String target = "/test";
String target = "/test";
startServer(new HttpServlet()
{
@Override
@ -1475,7 +1477,7 @@ public class AsyncMiddleManServletTest
resp.setStatus(target.equals(req.getRequestURI()) ? 200 : 404);
}
});
final String proxyTo = "http://localhost:" + serverConnector.getLocalPort();
String proxyTo = "http://localhost:" + serverConnector.getLocalPort();
AsyncMiddleManServlet proxyServlet = new AsyncMiddleManServlet.Transparent()
{
@Override
@ -1498,21 +1500,6 @@ public class AsyncMiddleManServletTest
assertTrue(response.getHeaders().containsKey(PROXIED_HEADER));
}
private Path prepareTargetTestsDir() throws IOException
{
final Path targetTestsDir = MavenTestingUtils.getTargetTestingDir().toPath();
Files.createDirectories(targetTestsDir);
try (DirectoryStream<Path> files = Files.newDirectoryStream(targetTestsDir, "*.*"))
{
for (Path file : files)
{
if (!Files.isDirectory(file))
Files.delete(file);
}
}
return targetTestsDir;
}
private void sleep(long delay)
{
try

View File

@ -8,7 +8,9 @@
<New id="StatsHandler" class="org.eclipse.jetty.server.handler.StatisticsHandler"></New>
</Arg>
</Call>
<Call class="org.eclipse.jetty.server.ServerConnectionStatistics" name="addToAllConnectors">
<Arg><Ref refid="Server"/></Arg>
<Call name="addBeanToAllConnectors">
<Arg>
<New class="org.eclipse.jetty.io.ConnectionStatistics"/>
</Arg>
</Call>
</Configure>

View File

@ -41,7 +41,7 @@ import org.eclipse.jetty.util.statistic.SampleStatistic;
* Adding an instance of this class as with {@link AbstractConnector#addBean(Object)}
* will register the listener with all connections accepted by that connector.
*
* @deprecated use {@link ServerConnectionStatistics} instead.
* @deprecated use {@link org.eclipse.jetty.io.ConnectionStatistics} instead.
*/
@Deprecated
@ManagedObject("Connector Statistics")

View File

@ -42,6 +42,7 @@ import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpURI;
import org.eclipse.jetty.http.PreEncodedHttpField;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.server.handler.HandlerWrapper;
@ -224,8 +225,8 @@ public class Server extends HandlerWrapper implements Attributes
if (connector.getServer() != this)
throw new IllegalArgumentException("Connector " + connector +
" cannot be shared among server " + connector.getServer() + " and server " + this);
if (_connectors.add(connector))
addBean(connector);
_connectors.add(connector);
addBean(connector);
}
/**
@ -265,6 +266,20 @@ public class Server extends HandlerWrapper implements Attributes
_connectors.addAll(Arrays.asList(connectors));
}
/**
* Add a bean to all connectors on the server.
* If the bean is an instance of {@link Connection.Listener} it will also be
* registered as a listener on all connections accepted by the connectors.
* @param bean the bean to be added.
*/
public void addBeanToAllConnectors(Object bean)
{
for (Connector connector : getConnectors())
{
connector.addBean(bean);
}
}
/**
* @return Returns the threadPool.
*/

View File

@ -19,16 +19,19 @@
package org.eclipse.jetty.server;
import org.eclipse.jetty.io.ConnectionStatistics;
import org.eclipse.jetty.util.component.Container;
@Deprecated
public class ServerConnectionStatistics extends ConnectionStatistics
{
/**
* @param server the server to use to add {@link ConnectionStatistics} to all Connectors.
* @deprecated use {@link Server#addBeanToAllConnectors(Object)} instead.
*/
public static void addToAllConnectors(Server server)
{
for (Connector connector : server.getConnectors())
{
if (connector instanceof Container)
connector.addBean(new ConnectionStatistics());
connector.addBean(new ConnectionStatistics());
}
}
}

View File

@ -49,13 +49,6 @@ public class PoolTest
return data.stream();
}
@ParameterizedTest
@MethodSource(value = "cacheSize")
public void testCache(int cacheSize)
{
System.err.println(cacheSize);
}
@ParameterizedTest
@MethodSource(value = "cacheSize")
public void testAcquireRelease(int cacheSize)

View File

@ -57,6 +57,7 @@ import java.util.Set;
import org.eclipse.jetty.util.LazyList;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.MultiException;
import org.eclipse.jetty.util.Pool;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.annotation.Name;
@ -99,7 +100,8 @@ public class XmlConfiguration
ArrayList.class, HashSet.class, Queue.class, List.class, Set.class, Collection.class
};
private static final Iterable<ConfigurationProcessorFactory> __factoryLoader = ServiceLoader.load(ConfigurationProcessorFactory.class);
private static final XmlParser __parser = initParser();
private static final Pool<ConfigurationParser> __parsers =
new Pool<>(Math.min(8, Runtime.getRuntime().availableProcessors()),1);
public static final Comparator<Executable> EXECUTABLE_COMPARATOR = (e1, e2) ->
{
// Favour methods with less parameters
@ -144,31 +146,6 @@ public class XmlConfiguration
return compare;
};
private static XmlParser initParser()
{
ClassLoader loader = XmlConfiguration.class.getClassLoader();
XmlParser parser = new XmlParser();
URL config60 = loader.getResource("org/eclipse/jetty/xml/configure_6_0.dtd");
URL config76 = loader.getResource("org/eclipse/jetty/xml/configure_7_6.dtd");
URL config90 = loader.getResource("org/eclipse/jetty/xml/configure_9_0.dtd");
URL config93 = loader.getResource("org/eclipse/jetty/xml/configure_9_3.dtd");
parser.redirectEntity("configure.dtd", config90);
parser.redirectEntity("configure_1_0.dtd", config60);
parser.redirectEntity("configure_1_1.dtd", config60);
parser.redirectEntity("configure_1_2.dtd", config60);
parser.redirectEntity("configure_1_3.dtd", config60);
parser.redirectEntity("configure_6_0.dtd", config60);
parser.redirectEntity("configure_7_6.dtd", config76);
parser.redirectEntity("configure_9_0.dtd", config90);
parser.redirectEntity("configure_9_3.dtd", config93);
parser.redirectEntity("http://jetty.mortbay.org/configure.dtd", config93);
parser.redirectEntity("http://jetty.eclipse.org/configure.dtd", config93);
parser.redirectEntity("http://www.eclipse.org/jetty/configure.dtd", config93);
parser.redirectEntity("-//Mort Bay Consulting//DTD Configure//EN", config93);
parser.redirectEntity("-//Jetty//Configure//EN", config93);
return parser;
}
/**
* Set the standard IDs and properties expected in a jetty XML file:
* <ul>
@ -226,6 +203,14 @@ public class XmlConfiguration
private final String _dtd;
private ConfigurationProcessor _processor;
ConfigurationParser getParser()
{
Pool<ConfigurationParser>.Entry entry = __parsers.acquire(ConfigurationParser::new);
if (entry == null)
return new ConfigurationParser(null);
return entry.getPooled();
}
/**
* Reads and parses the XML configuration file.
*
@ -235,14 +220,11 @@ public class XmlConfiguration
*/
public XmlConfiguration(Resource resource) throws SAXException, IOException
{
synchronized (__parser)
try (ConfigurationParser parser = getParser(); InputStream inputStream = resource.getInputStream())
{
_location = resource;
try (InputStream inputStream = resource.getInputStream())
{
setConfig(__parser.parse(inputStream));
}
_dtd = __parser.getDTD();
setConfig(parser.parse(inputStream));
_dtd = parser.getDTD();
}
}
@ -275,15 +257,12 @@ public class XmlConfiguration
configuration = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" +
"<!DOCTYPE Configure PUBLIC \"-//Jetty//Configure//EN\" \"http://www.eclipse.org/jetty/configure_9_3.dtd\">" +
configuration;
try (StringReader reader = new StringReader(configuration))
try (ConfigurationParser parser = getParser(); StringReader reader = new StringReader(configuration))
{
InputSource source = new InputSource(reader);
synchronized (__parser)
{
_location = null;
setConfig(__parser.parse(source));
_dtd = __parser.getDTD();
}
_location = null;
setConfig(parser.parse(source));
_dtd = parser.getDTD();
}
}
@ -299,11 +278,11 @@ public class XmlConfiguration
public XmlConfiguration(InputStream configuration) throws SAXException, IOException
{
InputSource source = new InputSource(configuration);
synchronized (__parser)
try (ConfigurationParser parser = getParser())
{
_location = null;
setConfig(__parser.parse(source));
_dtd = __parser.getDTD();
setConfig(parser.parse(source));
_dtd = parser.getDTD();
}
}
@ -1939,4 +1918,40 @@ public class XmlConfiguration
throw e;
}
}
private static class ConfigurationParser extends XmlParser implements AutoCloseable
{
private final Pool<ConfigurationParser>.Entry _entry;
private ConfigurationParser(Pool<ConfigurationParser>.Entry entry)
{
_entry = entry;
ClassLoader loader = XmlConfiguration.class.getClassLoader();
URL config60 = loader.getResource("org/eclipse/jetty/xml/configure_6_0.dtd");
URL config76 = loader.getResource("org/eclipse/jetty/xml/configure_7_6.dtd");
URL config90 = loader.getResource("org/eclipse/jetty/xml/configure_9_0.dtd");
URL config93 = loader.getResource("org/eclipse/jetty/xml/configure_9_3.dtd");
redirectEntity("configure.dtd", config90);
redirectEntity("configure_1_0.dtd", config60);
redirectEntity("configure_1_1.dtd", config60);
redirectEntity("configure_1_2.dtd", config60);
redirectEntity("configure_1_3.dtd", config60);
redirectEntity("configure_6_0.dtd", config60);
redirectEntity("configure_7_6.dtd", config76);
redirectEntity("configure_9_0.dtd", config90);
redirectEntity("configure_9_3.dtd", config93);
redirectEntity("http://jetty.mortbay.org/configure.dtd", config93);
redirectEntity("http://jetty.eclipse.org/configure.dtd", config93);
redirectEntity("http://www.eclipse.org/jetty/configure.dtd", config93);
redirectEntity("-//Mort Bay Consulting//DTD Configure//EN", config93);
redirectEntity("-//Jetty//Configure//EN", config93);
}
@Override
public void close()
{
if (_entry != null)
__parsers.release(_entry);
}
}
}

View File

@ -28,6 +28,8 @@ import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -180,4 +182,56 @@ public class RoundRobinConnectionPoolTest extends AbstractTest<TransportScenario
assertThat(remotePorts.get(i - 1), Matchers.not(Matchers.equalTo(candidate)));
}
}
@ParameterizedTest
@ArgumentsSource(TransportProvider.class)
public void testMultiplexWithMaxUsage(Transport transport) throws Exception
{
init(transport);
int multiplex = 1;
if (scenario.transport.isHttp2Based())
multiplex = 2;
int maxMultiplex = multiplex;
int maxUsage = 2;
int maxConnections = 2;
int count = maxConnections * maxMultiplex * maxUsage;
List<Integer> remotePorts = new CopyOnWriteArrayList<>();
scenario.start(new EmptyServerHandler()
{
@Override
protected void service(String target, Request jettyRequest, HttpServletRequest request, HttpServletResponse response)
{
remotePorts.add(request.getRemotePort());
}
});
scenario.client.getTransport().setConnectionPoolFactory(destination ->
{
RoundRobinConnectionPool pool = new RoundRobinConnectionPool(destination, maxConnections, destination, maxMultiplex);
pool.setMaxUsageCount(maxUsage);
return pool;
});
CountDownLatch clientLatch = new CountDownLatch(count);
for (int i = 0; i < count; ++i)
{
scenario.client.newRequest(scenario.newURI())
.path("/" + i)
.timeout(5, TimeUnit.SECONDS)
.send(result ->
{
if (result.getResponse().getStatus() == HttpStatus.OK_200)
clientLatch.countDown();
});
}
assertTrue(clientLatch.await(count, TimeUnit.SECONDS));
assertEquals(count, remotePorts.size());
Map<Integer, Long> results = remotePorts.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
assertEquals(count / maxUsage, results.size(), remotePorts.toString());
assertEquals(1, results.values().stream().distinct().count(), remotePorts.toString());
}
}