Merge remote-tracking branch 'origin/jetty-9.4.x' into jetty-9.4.x-3132-DumpTool
This commit is contained in:
commit
cff6121c45
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue