Merged branch 'jetty-9.4.x' into 'jetty-10.0.x'.

This commit is contained in:
Simone Bordet 2019-04-06 13:45:54 +02:00
commit 40c2e76786
54 changed files with 1496 additions and 619 deletions

2
Jenkinsfile vendored
View File

@ -97,7 +97,7 @@ def mavenBuild(jdk, cmdline, mvnName, junitPublishDisabled) {
mavenOpts: mavenOpts,
mavenLocalRepo: localRepo) {
// Some common Maven command line + provided command line
sh "mvn -V -B -T3 -e -fae -Dmaven.test.failure.ignore=true -Djetty.testtracker.log=true $cmdline -Dunix.socket.tmp=" + env.JENKINS_HOME
sh "mvn -Pci -V -B -T3 -e -fae -Dmaven.test.failure.ignore=true -Djetty.testtracker.log=true $cmdline -Dunix.socket.tmp=" + env.JENKINS_HOME
}
}

View File

@ -25,7 +25,6 @@ import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileChannel.MapMode;
import java.nio.file.StandardOpenOption;
import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -116,7 +115,8 @@ public class FastFileServer
}
String listing = Resource.newResource(file).getListHTML(
request.getRequestURI(),
request.getPathInfo().lastIndexOf("/") > 0);
request.getPathInfo().lastIndexOf("/") > 0,
request.getQueryString());
response.setContentType("text/html; charset=utf-8");
response.getWriter().println(listing);
return;

View File

@ -91,6 +91,7 @@ import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
@ -740,6 +741,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
@Tag("ipv6")
public void testSendToIPv6Address(Scenario scenario) throws Exception
{
start(scenario, new EmptyServerHandler());
@ -1609,6 +1611,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
@Tag("ipv6")
public void test_IPv6_Host(Scenario scenario) throws Exception
{
start(scenario, new AbstractHandler()

View File

@ -18,13 +18,6 @@
package org.eclipse.jetty.client;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
@ -52,13 +45,22 @@ import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.util.Fields;
import org.eclipse.jetty.util.URIUtil;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class HttpClientURITest extends AbstractHttpClientServerTest
{
@ParameterizedTest
@ArgumentsSource(ScenarioProvider.class)
@Tag("ipv6")
public void testIPv6Host(Scenario scenario) throws Exception
{
start(scenario, new EmptyServerHandler());

View File

@ -18,12 +18,12 @@
package org.eclipse.jetty.client;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.junit.jupiter.api.Test;
public class ProxyConfigurationTest
{
@Test
@ -68,6 +68,7 @@ public class ProxyConfigurationTest
}
@Test
@Tag("ipv6")
public void testProxyMatchesWithIncludesAndExcludesIPv6() throws Exception
{
HttpProxy proxy = new HttpProxy("host", 0);

View File

@ -22,6 +22,7 @@ import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppLifeCycle;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.Callback;
public class StandardDeployer implements AppLifeCycle.Binding
{
@ -37,9 +38,10 @@ public class StandardDeployer implements AppLifeCycle.Binding
{
ContextHandler handler = app.getContextHandler();
if (handler == null)
{
throw new NullPointerException("No Handler created for App: " + app);
}
app.getDeploymentManager().getContexts().addHandler(handler);
Callback.Completable blocker = new Callback.Completable();
app.getDeploymentManager().getContexts().deployHandler(handler, blocker);
blocker.get();
}
}

View File

@ -21,17 +21,12 @@ package org.eclipse.jetty.deploy.bindings;
import org.eclipse.jetty.deploy.App;
import org.eclipse.jetty.deploy.AppLifeCycle;
import org.eclipse.jetty.deploy.graph.Node;
import org.eclipse.jetty.server.Handler;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.Callback;
public class StandardUndeployer implements AppLifeCycle.Binding
{
private static final Logger LOG = Log.getLogger(StandardUndeployer.class);
@Override
public String[] getBindingTargets()
{
@ -42,33 +37,11 @@ public class StandardUndeployer implements AppLifeCycle.Binding
@Override
public void processBinding(Node node, App app) throws Exception
{
ContextHandler handler = app.getContextHandler();
ContextHandlerCollection chcoll = app.getDeploymentManager().getContexts();
recursiveRemoveContext(chcoll,handler);
}
private void recursiveRemoveContext(HandlerCollection coll, ContextHandler context)
{
Handler children[] = coll.getHandlers();
int originalCount = children.length;
for (int i = 0, n = children.length; i < n; i++)
{
Handler child = children[i];
LOG.debug("Child handler {}",child);
if (child.equals(context))
{
LOG.debug("Removing handler {}",child);
coll.removeHandler(child);
child.destroy();
if (LOG.isDebugEnabled())
LOG.debug("After removal: {} (originally {})",coll.getHandlers().length,originalCount);
}
else if (child instanceof HandlerCollection)
{
recursiveRemoveContext((HandlerCollection)child,context);
}
}
ContextHandlerCollection contexts = app.getDeploymentManager().getContexts();
ContextHandler context = app.getContextHandler();
Callback.Completable blocker = new Callback.Completable();
contexts.undeployHandler(context, blocker);
blocker.get();
context.destroy();
}
}

View File

@ -50,6 +50,7 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
import org.eclipse.jetty.toolchain.test.IO;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
@ -473,10 +474,10 @@ public class HttpClientTest extends AbstractHttpClientServerTest
ExecutionException x = assertThrows(ExecutionException.class, () ->
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.idleTimeout(4 * idleTimeout, TimeUnit.MILLISECONDS)
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
.send());
.scheme(scheme)
.idleTimeout(4 * idleTimeout, TimeUnit.MILLISECONDS)
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
.send());
assertThat(x.getCause(), instanceOf(EOFException.class));
connector.setIdleTimeout(5 * idleTimeout);
@ -493,6 +494,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
}
@Test
@Tag("ipv6")
public void testSendToIPv6Address() throws Exception
{
start(new EmptyServerHandler());
@ -595,9 +597,9 @@ public class HttpClientTest extends AbstractHttpClientServerTest
{
assertThrows(ExecutionException.class, () ->
client.newRequest("localhost", connector.getLocalPort())
.scheme(scheme)
.timeout(60, TimeUnit.SECONDS)
.send());
.scheme(scheme)
.timeout(60, TimeUnit.SECONDS)
.send());
}
}

View File

@ -13,7 +13,7 @@ session-store
sessions
[files]
maven://com.hazelcast/hazelcast/3.8.2|lib/hazelcast/hazelcast-3.8.2.jar
maven://com.hazelcast/hazelcast/3.9.3|lib/hazelcast/hazelcast-3.9.3.jar
[xml]
etc/sessions/hazelcast/default.xml
@ -33,4 +33,4 @@ jetty.session.hazelcast.mapName=jetty-distributed-session-map
jetty.session.hazelcast.hazelcastInstanceName=JETTY_DISTRIBUTED_SESSION_INSTANCE
#jetty.session.hazelcast.configurationLocation=
jetty.session.gracePeriod.seconds=3600
jetty.session.savePeriod.seconds=0
jetty.session.savePeriod.seconds=0

View File

@ -13,8 +13,8 @@ session-store
sessions
[files]
maven://com.hazelcast/hazelcast/3.8.2|lib/hazelcast/hazelcast-3.8.2.jar
maven://com.hazelcast/hazelcast-client/3.8.2|lib/hazelcast/hazelcast-client-3.8.2.jar
maven://com.hazelcast/hazelcast/3.9.3|lib/hazelcast/hazelcast-3.9.3.jar
maven://com.hazelcast/hazelcast-client/3.9.3|lib/hazelcast/hazelcast-client-3.9.3.jar
[xml]
etc/sessions/hazelcast/remote.xml
@ -35,4 +35,4 @@ jetty.session.hazelcast.hazelcastInstanceName=JETTY_DISTRIBUTED_SESSION_INSTANCE
jetty.session.hazelcast.onlyClient=true
#jetty.session.hazelcast.configurationLocation=
jetty.session.gracePeriod.seconds=3600
jetty.session.savePeriod.seconds=0
jetty.session.savePeriod.seconds=0

View File

@ -28,6 +28,7 @@ import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.eclipse.jetty.http.HttpCompliance.Violation.CASE_INSENSITIVE_METHOD;
@ -1975,6 +1976,7 @@ public class HttpParserTest
}
@Test
@Tag("ipv6")
public void testIPv6Host() throws Exception
{
ByteBuffer buffer = BufferUtil.toBuffer(
@ -2056,6 +2058,7 @@ public class HttpParserTest
}
@Test
@Tag("ipv6")
public void testIPv6HostPort() throws Exception
{
ByteBuffer buffer = BufferUtil.toBuffer(

View File

@ -295,8 +295,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug(x);
LOG.ignore(x);
return -1;
}
}
@ -309,8 +308,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
}
catch (Throwable x)
{
if (LOG.isDebugEnabled())
LOG.debug(x);
LOG.ignore(x);
return -1;
}
}

View File

@ -389,9 +389,9 @@ abstract public class WriteFlusher
boolean progress = true;
while (progress && buffers != null)
{
long before = remaining(buffers);
long before = BufferUtil.remaining(buffers);
boolean flushed = _endPoint.flush(buffers);
long after = remaining(buffers);
long after = BufferUtil.remaining(buffers);
long written = before - after;
if (LOG.isDebugEnabled())
@ -441,16 +441,6 @@ abstract public class WriteFlusher
return buffers == null ? EMPTY_BUFFERS : buffers;
}
private long remaining(ByteBuffer[] buffers)
{
if (buffers == null)
return 0;
long result = 0;
for (ByteBuffer buffer : buffers)
result += buffer.remaining();
return result;
}
/**
* Notify the flusher of a failure
*

View File

@ -835,6 +835,12 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
LOG.debug("flush b[{}]={}", i++, BufferUtil.toDetailString(b));
}
// finish of any previous flushes
if (BufferUtil.hasContent(_encryptedOutput) && !getEndPoint().flush(_encryptedOutput))
return false;
boolean isEmpty = BufferUtil.isEmpty(appOuts);
Boolean result = null;
try
{
@ -866,7 +872,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
if (filled < 0)
throw new IOException("Broken pipe");
}
return result = false;
return result = isEmpty;
default:
throw new IllegalStateException("Unexpected HandshakeStatus " + status);
@ -895,10 +901,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
_sslEngine.isOutboundDone());
// Was all the data consumed?
boolean allConsumed = true;
for (ByteBuffer b : appOuts)
if (BufferUtil.hasContent(b))
allConsumed = false;
isEmpty = BufferUtil.isEmpty(appOuts);
// if we have net bytes, let's try to flush them
boolean flushed = true;
@ -906,7 +909,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
flushed = getEndPoint().flush(_encryptedOutput);
if (LOG.isDebugEnabled())
LOG.debug("net flushed={}, ac={}", flushed, allConsumed);
LOG.debug("net flushed={}, ac={}", flushed, isEmpty);
// Now deal with the results returned from the wrap
Status wrap = wrapResult.getStatus();
@ -919,7 +922,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
if (!flushed)
return result = false;
getEndPoint().shutdownOutput();
if (allConsumed)
if (isEmpty)
return result = true;
throw new IOException("Broken pipe");
}
@ -936,15 +939,20 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
if (isRenegotiating() && !allowRenegotiate())
{
getEndPoint().shutdownOutput();
if (allConsumed && BufferUtil.isEmpty(_encryptedOutput))
if (isEmpty && BufferUtil.isEmpty(_encryptedOutput))
return result = true;
throw new IOException("Broken pipe");
}
if (!flushed)
return result = false;
if (allConsumed)
return result = true;
if (isEmpty)
{
if (wrapResult.getHandshakeStatus() != HandshakeStatus.NEED_WRAP ||
wrapResult.bytesProduced() == 0)
return result = true;
}
break;
default:
@ -1073,14 +1081,15 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
@Override
public void doShutdownOutput()
{
final EndPoint endp = getEndPoint();
try
{
boolean close;
boolean flush = false;
synchronized (_decryptedEndPoint)
{
boolean ishut = getEndPoint().isInputShutdown();
boolean oshut = getEndPoint().isOutputShutdown();
boolean ishut = endp.isInputShutdown();
boolean oshut = endp.isOutputShutdown();
if (LOG.isDebugEnabled())
LOG.debug("shutdownOutput: {} oshut={}, ishut={} {}", SslConnection.this, oshut, ishut);
@ -1097,16 +1106,28 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
}
if (flush)
flush(BufferUtil.EMPTY_BUFFER); // Send the TLS close message.
{
if (!flush(BufferUtil.EMPTY_BUFFER) && !close)
{
Thread.yield();
// if we still can't flush, but we are not closing the endpoint,
// let's just flush the encrypted output in the background.
// and continue as if we are closed. The assumption here is that
// the encrypted buffer will contain the entire close handshake
// and that a call to flush(EMPTY_BUFFER) is not needed.
endp.write(Callback.from(() -> {}, t -> endp.close()), _encryptedOutput);
}
}
if (close)
getEndPoint().close();
endp.close();
else
ensureFillInterested();
}
catch (Throwable x)
{
LOG.ignore(x);
getEndPoint().close();
endp.close();
}
}

View File

@ -23,6 +23,7 @@ import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;
import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
@ -31,6 +32,7 @@ import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import javax.net.ssl.SSLEngine;
@ -50,6 +52,8 @@ import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
@ -63,6 +67,8 @@ public class SslConnectionTest
private final SslContextFactory _sslCtxFactory = new SslContextFactory.Server();
protected volatile EndPoint _lastEndp;
private volatile boolean _testFill=true;
private volatile boolean _onXWriteThenShutdown=false;
private volatile FutureCallback _writeCallback;
protected ServerSocketChannel _connector;
final AtomicInteger _dispatches = new AtomicInteger();
@ -104,6 +110,7 @@ public class SslConnectionTest
static final AtomicInteger __startBlocking = new AtomicInteger();
static final AtomicInteger __blockFor = new AtomicInteger();
static final AtomicBoolean __onIncompleteFlush = new AtomicBoolean();
private static class TestEP extends SocketChannelEndPoint
{
public TestEP(SelectableChannel channel, ManagedSelector selector, SelectionKey key, Scheduler scheduler)
@ -114,13 +121,14 @@ public class SslConnectionTest
@Override
protected void onIncompleteFlush()
{
super.onIncompleteFlush();
__onIncompleteFlush.set(true);
}
@Override
public boolean flush(ByteBuffer... buffers) throws IOException
{
__onIncompleteFlush.set(false);
if (__startBlocking.get()==0 || __startBlocking.decrementAndGet()==0)
{
if (__blockFor.get()>0 && __blockFor.getAndDecrement()>0)
@ -224,20 +232,23 @@ public class SslConnectionTest
filled=endp.fill(_in);
}
boolean shutdown = _onXWriteThenShutdown && BufferUtil.toString(_in).contains("X");
// Write everything
int l=_in.remaining();
if (l>0)
{
FutureCallback blockingWrite= new FutureCallback();
endp.write(blockingWrite,_in);
blockingWrite.get();
if (shutdown)
endp.shutdownOutput();
}
// are we done?
if (endp.isInputShutdown())
{
if (endp.isInputShutdown() || shutdown)
endp.shutdownOutput();
}
}
}
catch(InterruptedException|EofException e)
@ -423,7 +434,7 @@ public class SslConnectionTest
public void testBlockedWrite() throws Exception
{
startSSL();
try (Socket client = newClient())
try (SSLSocket client = newClient())
{
client.setSoTimeout(5000);
try (SocketChannel server = _connector.accept())
@ -431,21 +442,78 @@ public class SslConnectionTest
server.configureBlocking(false);
_manager.accept(server);
__startBlocking.set(5);
__blockFor.set(3);
client.getOutputStream().write("Hello".getBytes(StandardCharsets.UTF_8));
byte[] buffer = new byte[1024];
int len = client.getInputStream().read(buffer);
assertEquals(5, len);
assertEquals("Hello", new String(buffer, 0, len, StandardCharsets.UTF_8));
__startBlocking.set(0);
__blockFor.set(2);
_dispatches.set(0);
client.getOutputStream().write("World".getBytes(StandardCharsets.UTF_8));
len = 5;
while (len > 0)
len -= client.getInputStream().read(buffer);
assertEquals(0, len);
try
{
client.setSoTimeout(500);
client.getInputStream().read(buffer);
throw new IllegalStateException();
}
catch(SocketTimeoutException e)
{
}
assertTrue(__onIncompleteFlush.get());
((TestEP)_lastEndp).getWriteFlusher().completeWrite();
len = client.getInputStream().read(buffer);
assertEquals("World", new String(buffer, 0, len, StandardCharsets.UTF_8));
}
}
}
@Test
public void testBlockedClose() throws Exception
{
startSSL();
try (SSLSocket client = newClient())
{
client.setSoTimeout(5000);
try (SocketChannel server = _connector.accept())
{
server.configureBlocking(false);
_manager.accept(server);
//__startBlocking.set(5);
//__blockFor.set(3);
client.getOutputStream().write("Short".getBytes(StandardCharsets.UTF_8));
byte[] buffer = new byte[1024];
int len = client.getInputStream().read(buffer);
assertEquals("Short", new String(buffer, 0, len, StandardCharsets.UTF_8));
_onXWriteThenShutdown=true;
__startBlocking.set(2); // block on the close handshake flush
__blockFor.set(Integer.MAX_VALUE); // > retry loops in SslConnection + 1
client.getOutputStream().write("This is a much longer example with X".getBytes(StandardCharsets.UTF_8));
len = client.getInputStream().read(buffer);
assertEquals("This is a much longer example with X", new String(buffer, 0, len, StandardCharsets.UTF_8));
try
{
client.setSoTimeout(500);
client.getInputStream().read(buffer);
throw new IllegalStateException();
}
catch(SocketTimeoutException e)
{
}
__blockFor.set(0);
assertTrue(__onIncompleteFlush.get());
((TestEP)_lastEndp).getWriteFlusher().completeWrite();
len = client.getInputStream().read(buffer);
assertThat(len, is(len));
}
}
}
@ -475,7 +543,6 @@ public class SslConnectionTest
String line = in.readLine();
if (line == null)
break;
// System.err.println(line);
count.countDown();
}
}
@ -488,7 +555,6 @@ public class SslConnectionTest
for (int i = 0; i < LINES; i++)
{
client.getOutputStream().write(("HelloWorld " + i + "\n").getBytes(StandardCharsets.UTF_8));
// System.err.println("wrote");
if (i % 1000 == 0)
{
client.getOutputStream().flush();

View File

@ -24,7 +24,7 @@
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.9</version>
<version>${slf4j.version}</version>
<scope>test</scope>
</dependency>
<dependency>

View File

@ -47,6 +47,7 @@ import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Promise;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
@ -88,6 +89,7 @@ public class ConnectHandlerTest extends AbstractConnectHandlerTest
}
@Test
@Tag("ipv6")
public void testCONNECTwithIPv6() throws Exception
{
String hostPort = "[::1]:" + serverConnector.getLocalPort();

View File

@ -1618,9 +1618,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
{
Handler[] contextCollections = getServer().getChildHandlersByClass(ContextHandlerCollection.class);
ContextHandlerCollection[] contextCollections =
(ContextHandlerCollection[])getServer().getChildHandlersByClass(ContextHandlerCollection.class);
for (int h = 0; contextCollections != null && h < contextCollections.length; h++)
((ContextHandlerCollection)contextCollections[h]).mapContexts();
contextCollections[h].mapContexts();
}
}

View File

@ -24,8 +24,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -36,15 +35,16 @@ import org.eclipse.jetty.server.HttpChannelState;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.util.ArrayTernaryTrie;
import org.eclipse.jetty.util.ArrayUtil;
import org.eclipse.jetty.util.Callback;
import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.annotation.ManagedObject;
import org.eclipse.jetty.util.annotation.ManagedOperation;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.SerializedExecutor;
/* ------------------------------------------------------------ */
/** ContextHandlerCollection.
*
/**
* This {@link org.eclipse.jetty.server.handler.HandlerCollection} is creates a
* Map of contexts to it's contained handlers based
* on the context path and virtual hosts of any contained {@link org.eclipse.jetty.server.handler.ContextHandler}s.
@ -57,9 +57,9 @@ import org.eclipse.jetty.util.log.Logger;
public class ContextHandlerCollection extends HandlerCollection
{
private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
private final SerializedExecutor _serializedExecutor = new SerializedExecutor();
private final ConcurrentMap<ContextHandler,Handler> _contextBranches = new ConcurrentHashMap<>();
private volatile Trie<Map.Entry<String,Branch[]>> _pathBranches;
@Deprecated
private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
/* ------------------------------------------------------------ */
@ -71,43 +71,57 @@ public class ContextHandlerCollection extends HandlerCollection
/* ------------------------------------------------------------ */
public ContextHandlerCollection(ContextHandler... contexts)
{
super(true,contexts);
super(true);
setHandlers(contexts);
}
/* ------------------------------------------------------------ */
/**
* Remap the context paths.
* Remap the contexts. Normally this is not required as context
* mapping is maintained as a side effect of {@link #setHandlers(Handler[])}
* However, if configuration changes in the deep handler structure (eg contextpath is changed), then
* this call will trigger a remapping.
* This method is mutually excluded from {@link #deployHandler(Handler, Callback)} and
* {@link #undeployHandler(Handler, Callback)}
*/
@ManagedOperation("update the mapping of context path to context")
@ManagedOperation("Update the mapping of context path to context")
public void mapContexts()
{
_contextBranches.clear();
Handler[] handlers = getHandlers();
if (handlers==null)
_serializedExecutor.execute(()->
{
_pathBranches=new ArrayTernaryTrie<>(false,16);
return;
}
while(true)
{
Handlers handlers = _handlers.get();
if (handlers==null)
break;
if (updateHandlers(handlers, newHandlers(handlers.getHandlers())))
break;
}
});
}
/* ------------------------------------------------------------ */
@Override
protected Handlers newHandlers(Handler[] handlers)
{
if (handlers==null || handlers.length==0)
return null;
// Create map of contextPath to handler Branch
Map<String,Branch[]> map = new HashMap<>();
// A branch is a Handler that could contain 0 or more ContextHandlers
Map<String,Branch[]> path2Branches = new HashMap<>();
for (Handler handler:handlers)
{
Branch branch=new Branch(handler);
for (String contextPath : branch.getContextPaths())
{
Branch[] branches=map.get(contextPath);
map.put(contextPath, ArrayUtil.addToArray(branches, branch, Branch.class));
Branch[] branches=path2Branches.get(contextPath);
path2Branches.put(contextPath, ArrayUtil.addToArray(branches, branch, Branch.class));
}
for (ContextHandler context : branch.getContextHandlers())
_contextBranches.putIfAbsent(context, branch.getHandler());
}
// Sort the branches so those with virtual hosts are considered before those without
for (Map.Entry<String,Branch[]> entry: map.entrySet())
// Sort the branches for each contextPath so those with virtual hosts are considered before those without
for (Map.Entry<String,Branch[]> entry: path2Branches.entrySet())
{
Branch[] branches=entry.getValue();
Branch[] sorted=new Branch[branches.length];
@ -123,69 +137,56 @@ public class ContextHandlerCollection extends HandlerCollection
// Loop until we have a big enough trie to hold all the context paths
int capacity=512;
Trie<Map.Entry<String,Branch[]>> trie;
Mapping mapping;
loop: while(true)
{
trie=new ArrayTernaryTrie<>(false,capacity);
for (Map.Entry<String,Branch[]> entry: map.entrySet())
mapping = new Mapping(handlers, capacity);
for (Map.Entry<String,Branch[]> entry: path2Branches.entrySet())
{
if (!trie.put(entry.getKey().substring(1),entry))
if (!mapping._pathBranches.put(entry.getKey().substring(1),entry))
{
capacity+=512;
continue loop;
}
}
break loop;
break;
}
if (LOG.isDebugEnabled())
{
for (String ctx : trie.keySet())
LOG.debug("{}->{}",ctx,Arrays.asList(trie.get(ctx).getValue()));
for (String ctx : mapping._pathBranches.keySet())
LOG.debug("{}->{}",ctx,Arrays.asList(mapping._pathBranches.get(ctx).getValue()));
}
_pathBranches=trie;
// add new context branches to concurrent map
for (Branch[] branches: path2Branches.values())
{
for (Branch branch : branches)
{
for (ContextHandler context : branch.getContextHandlers())
mapping._contextBranches.put(context, branch.getHandler());
}
}
return mapping;
}
/* ------------------------------------------------------------ */
/*
* @see org.eclipse.jetty.server.server.handler.HandlerCollection#setHandlers(org.eclipse.jetty.server.server.Handler[])
*/
@Override
public void setHandlers(Handler[] handlers)
{
super.setHandlers(handlers);
if (isStarted())
mapContexts();
}
/* ------------------------------------------------------------ */
@Override
protected void doStart() throws Exception
{
mapContexts();
super.doStart();
}
/* ------------------------------------------------------------ */
/*
* @see org.eclipse.jetty.server.server.Handler#handle(java.lang.String, javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse, int)
*/
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
Handler[] handlers = getHandlers();
if (handlers==null || handlers.length==0)
Handlers handlers = _handlers.get();
if (handlers==null)
return;
Mapping mapping = (Mapping)handlers;
HttpChannelState async = baseRequest.getHttpChannelState();
if (async.isAsync())
{
ContextHandler context=async.getContextHandler();
if (context!=null)
{
Handler branch = _contextBranches.get(context);
Handler branch = mapping._contextBranches.get(context);
if (branch==null)
context.handle(target,baseRequest,request, response);
@ -195,19 +196,19 @@ public class ContextHandlerCollection extends HandlerCollection
}
}
// data structure which maps a request to a context; first-best match wins
// { context path => [ context ] }
// }
if (target.startsWith("/"))
{
Trie<Map.Entry<String,Branch[]>> pathBranches = mapping._pathBranches;
if (pathBranches==null)
return;
int limit = target.length()-1;
while (limit>=0)
{
// Get best match
Map.Entry<String,Branch[]> branches = _pathBranches.getBest(target,1,limit);
Map.Entry<String,Branch[]> branches = pathBranches.getBest(target,1,limit);
if (branches==null)
break;
@ -227,10 +228,11 @@ public class ContextHandlerCollection extends HandlerCollection
}
else
{
// This may not work in all circumstances... but then I think it should never be called
for (int i=0;i<handlers.length;i++)
if (mapping.getHandlers()==null)
return;
for (int i=0;i<mapping.getHandlers().length;i++)
{
handlers[i].handle(target,baseRequest, request, response);
mapping.getHandlers()[i].handle(target,baseRequest, request, response);
if ( baseRequest.isHandled())
return;
}
@ -238,11 +240,15 @@ public class ContextHandlerCollection extends HandlerCollection
}
/* ------------------------------------------------------------ */
/** Add a context handler.
/**
* Adds a context handler.
*
* @param contextPath The context path to add
* @param resourceBase the base (root) Resource
* @return the ContextHandler just added
* @deprecated Unused convenience method no longer supported.
*/
@Deprecated
public ContextHandler addContext(String contextPath,String resourceBase)
{
try
@ -260,22 +266,90 @@ public class ContextHandlerCollection extends HandlerCollection
}
}
/* ------------------------------------------------------------ */
/**
* Thread safe deploy of a Handler.
* <p>
* This method is the equivalent of {@link #addHandler(Handler)},
* but its execution is non-block and mutually excluded from all
* other calls to {@link #deployHandler(Handler, Callback)} and
* {@link #undeployHandler(Handler, Callback)}.
* The handler may be added after this call returns.
* </p>
* @param handler the handler to deploy
* @param callback Called after handler has been added
*/
public void deployHandler(Handler handler, Callback callback)
{
if (handler.getServer()!=getServer())
handler.setServer(getServer());
_serializedExecutor.execute(new SerializedExecutor.ErrorHandlingTask()
{
@Override
public void run()
{
addHandler(handler);
callback.succeeded();
}
@Override
public void accept(Throwable throwable)
{
callback.failed(throwable);
}
});
}
/* ------------------------------------------------------------ */
/**
* Thread safe undeploy of a Handler.
* <p>
* This method is the equivalent of {@link #removeHandler(Handler)},
* but its execution is non-block and mutually excluded from all
* other calls to {@link #deployHandler(Handler,Callback)} and
* {@link #undeployHandler(Handler,Callback)}.
* The handler may be removed after this call returns.
* </p>
* @param handler The handler to undeploy
* @param callback Called after handler has been removed
*/
public void undeployHandler(Handler handler, Callback callback)
{
_serializedExecutor.execute(new SerializedExecutor.ErrorHandlingTask()
{
@Override
public void run()
{
removeHandler(handler);
callback.succeeded();
}
@Override
public void accept(Throwable throwable)
{
callback.failed(throwable);
}
});
}
/* ------------------------------------------------------------ */
/**
* @return The class to use to add new Contexts
* @deprecated Unused convenience mechanism not used.
*/
@Deprecated
public Class<?> getContextClass()
{
return _contextClass;
}
/* ------------------------------------------------------------ */
/**
* @param contextClass The class to use to add new Contexts
* @deprecated Unused convenience mechanism not used.
*/
@Deprecated
public void setContextClass(Class<? extends ContextHandler> contextClass)
{
if (contextClass ==null || !(ContextHandler.class.isAssignableFrom(contextClass)))
@ -342,5 +416,18 @@ public class ContextHandlerCollection extends HandlerCollection
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
private static class Mapping extends Handlers
{
private final Map<ContextHandler,Handler> _contextBranches = new HashMap<>();
private final Trie<Map.Entry<String,Branch[]>> _pathBranches;
private Mapping(Handler[] handlers, int capacity)
{
super(handlers);
_pathBranches = new ArrayTernaryTrie<>(false, capacity);
}
}
}

View File

@ -21,6 +21,7 @@ package org.eclipse.jetty.server.handler;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@ -47,7 +48,7 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
public class HandlerCollection extends AbstractHandlerContainer
{
private final boolean _mutableWhenRunning;
private volatile Handler[] _handlers;
protected final AtomicReference<Handlers> _handlers = new AtomicReference<>();
/* ------------------------------------------------------------ */
public HandlerCollection()
@ -71,72 +72,93 @@ public class HandlerCollection extends AbstractHandlerContainer
/* ------------------------------------------------------------ */
/**
* @return Returns the handlers.
* @return the array of handlers.
*/
@Override
@ManagedAttribute(value="Wrapped handlers", readonly=true)
public Handler[] getHandlers()
{
return _handlers;
Handlers handlers = _handlers.get();
return handlers==null ? null : handlers._handlers;
}
/* ------------------------------------------------------------ */
/**
* @param handlers The handlers to set.
* @param handlers the array of handlers to set.
*/
public void setHandlers(Handler[] handlers)
{
if (!_mutableWhenRunning && isStarted())
throw new IllegalStateException(STARTED);
if (handlers!=null)
while(true)
{
// check for loops
for (Handler handler:handlers)
if (handler == this || (handler instanceof HandlerContainer &&
Arrays.asList(((HandlerContainer)handler).getChildHandlers()).contains(this)))
throw new IllegalStateException("setHandler loop");
// Set server
for (Handler handler:handlers)
if (handler.getServer()!=getServer())
handler.setServer(getServer());
if (updateHandlers(_handlers.get(), newHandlers(handlers)))
break;
}
Handler[] old=_handlers;;
_handlers = handlers;
updateBeans(old, handlers);
}
/* ------------------------------------------------------------ */
/**
* @see Handler#handle(String, Request, HttpServletRequest, HttpServletResponse)
*/
protected Handlers newHandlers(Handler[] handlers)
{
if (handlers==null || handlers.length==0)
return null;
return new Handlers(handlers);
}
/* ------------------------------------------------------------ */
protected boolean updateHandlers(Handlers old, Handlers handlers)
{
if (handlers!=null)
{
// check for loops
for (Handler handler:handlers._handlers)
if (handler == this || (handler instanceof HandlerContainer &&
Arrays.asList(((HandlerContainer)handler).getChildHandlers()).contains(this)))
throw new IllegalStateException("setHandler loop");
// Set server
for (Handler handler:handlers._handlers)
if (handler.getServer()!=getServer())
handler.setServer(getServer());
}
if (_handlers.compareAndSet(old, handlers))
{
Handler[] oldBeans = old == null ? null : old._handlers;
Handler[] newBeans = handlers == null ? null : handlers._handlers;
updateBeans(oldBeans, newBeans);
return true;
}
return false;
}
/* ------------------------------------------------------------ */
@Override
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
if (_handlers!=null && isStarted())
if (isStarted())
{
MultiException mex=null;
Handlers handlers = _handlers.get();
if (handlers==null)
return;
for (int i=0;i<_handlers.length;i++)
MultiException mex=null;
for (Handler handler : handlers._handlers)
{
try
{
_handlers[i].handle(target,baseRequest, request, response);
handler.handle(target, baseRequest, request, response);
}
catch(IOException e)
catch (IOException | RuntimeException e)
{
throw e;
}
catch(RuntimeException e)
catch (Exception e)
{
throw e;
}
catch(Exception e)
{
if (mex==null)
mex=new MultiException();
if (mex == null)
mex = new MultiException();
mex.add(e);
}
}
@ -147,37 +169,54 @@ public class HandlerCollection extends AbstractHandlerContainer
else
throw new ServletException(mex);
}
}
}
/* ------------------------------------------------------------ */
/* Add a handler.
/**
* Adds a handler.
* This implementation adds the passed handler to the end of the existing collection of handlers.
* @see org.eclipse.jetty.server.server.HandlerContainer#addHandler(org.eclipse.jetty.server.server.Handler)
* If the handler is already added, it is removed and readded
*/
public void addHandler(Handler handler)
{
setHandlers(ArrayUtil.addToArray(getHandlers(), handler, Handler.class));
while(true)
{
Handlers old = _handlers.get();
Handlers handlers = newHandlers(ArrayUtil.addToArray(old==null?null:ArrayUtil.removeFromArray(old._handlers, handler), handler, Handler.class));
if (updateHandlers(old,handlers))
break;
}
}
/* ------------------------------------------------------------ */
/* Prepend a handler.
/**
* Prepends a handler.
* This implementation adds the passed handler to the start of the existing collection of handlers.
* @see org.eclipse.jetty.server.server.HandlerContainer#addHandler(org.eclipse.jetty.server.server.Handler)
*/
public void prependHandler(Handler handler)
{
setHandlers(ArrayUtil.prependToArray(handler, getHandlers(), Handler.class));
while(true)
{
Handlers old = _handlers.get();
Handlers handlers = newHandlers(ArrayUtil.prependToArray(handler, old==null?null:old._handlers, Handler.class));
if (updateHandlers(old,handlers))
break;
}
}
/* ------------------------------------------------------------ */
public void removeHandler(Handler handler)
{
Handler[] handlers = getHandlers();
if (handlers!=null && handlers.length>0 )
setHandlers(ArrayUtil.removeFromArray(handlers, handler));
while(true)
{
Handlers old = _handlers.get();
if (old==null || old._handlers.length==0)
break;
Handlers handlers = newHandlers(ArrayUtil.removeFromArray(old._handlers, handler));
if (updateHandlers(old,handlers))
break;
}
}
/* ------------------------------------------------------------ */
@ -196,10 +235,28 @@ public class HandlerCollection extends AbstractHandlerContainer
{
if (!isStopped())
throw new IllegalStateException("!STOPPED");
Handler[] children=getChildHandlers();
Handler[] children = getChildHandlers();
setHandlers(null);
for (Handler child: children)
child.destroy();
super.destroy();
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
protected static class Handlers
{
private final Handler[] _handlers;
protected Handlers(Handler[] handlers)
{
this._handlers = handlers;
}
public Handler[] getHandlers()
{
return _handlers;
}
}
}

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

View File

@ -18,17 +18,17 @@
package org.eclipse.jetty.server;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertNull;
import org.eclipse.jetty.server.handler.ErrorHandler;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertNull;
/**
*
*/
@ -82,6 +82,7 @@ public class ProxyConnectionTest
}
@Test
@Tag("ipv6")
public void testIPv6() throws Exception
{
String response=_connector.getResponse("PROXY UNKNOWN eeee:eeee:eeee:eeee:eeee:eeee:eeee:eeee ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n"+

View File

@ -18,16 +18,6 @@
package org.eclipse.jetty.server.handler;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
import java.io.IOException;
import javax.servlet.AsyncContext;
@ -41,9 +31,18 @@ import org.eclipse.jetty.server.LocalConnector;
import org.eclipse.jetty.server.Request;
import org.eclipse.jetty.server.Server;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.endsWith;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.startsWith;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;
public class ContextHandlerCollectionTest
{
@Test
@ -214,7 +213,7 @@ public class ContextHandlerCollectionTest
IsHandledHandler handler = (IsHandledHandler)context.getHandler();
context.setVirtualHosts(contextHosts);
// trigger this manually; it's supposed to be called when adding the handler
// trigger this manually
handlerCollection.mapContexts();
for(String host : requestHosts)

View File

@ -167,7 +167,7 @@ public class ResourceHandlerTest
_local.getResponse("GET /resource/ HTTP/1.0\r\n\r\n"));
assertThat(response.getStatus(),equalTo(200));
assertThat(response.getContent(),containsString("jetty-dir.css"));
assertThat(response.getContent(),containsString("<H1>Directory: /resource/"));
assertThat(response.getContent(),containsString("Directory: /resource/"));
assertThat(response.getContent(),containsString("big.txt"));
assertThat(response.getContent(),containsString("bigger.txt"));
assertThat(response.getContent(),containsString("directory"));

View File

@ -23,7 +23,6 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.UnavailableException;
@ -48,8 +47,7 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.resource.Resource;
import org.eclipse.jetty.util.resource.ResourceFactory;
/**
/**
* The default servlet.
* <p>
* This servlet, normally mapped to /, provides the handling for static
@ -79,9 +77,9 @@ import org.eclipse.jetty.util.resource.ResourceFactory;
*
* gzip If set to true, then static content will be served as
* gzip content encoded if a matching resource is
* found ending with ".gz" (default false)
* found ending with ".gz" (default false)
* (deprecated: use precompressed)
*
*
* 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:
@ -131,10 +129,10 @@ import org.eclipse.jetty.util.resource.ResourceFactory;
public class DefaultServlet extends HttpServlet implements ResourceFactory, WelcomeFactory
{
public static final String CONTEXT_INIT = "org.eclipse.jetty.servlet.Default.";
private static final Logger LOG = Log.getLogger(DefaultServlet.class);
private static final long serialVersionUID = 4930458713846881193L;
private static final long serialVersionUID = 4930458713846881193L;
private final ResourceService _resourceService;
private ServletContext _servletContext;
@ -165,7 +163,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc
{
this(new ResourceService());
}
/* ------------------------------------------------------------ */
@Override
public void init()
@ -186,7 +184,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc
_resourceService.setPrecompressedFormats(parsePrecompressedFormats(getInitParameter("precompressed"), getInitBoolean("gzip", false)));
_resourceService.setPathInfoOnly(getInitBoolean("pathInfoOnly",_resourceService.isPathInfoOnly()));
_resourceService.setEtags(getInitBoolean("etags",_resourceService.isEtags()));
if ("exact".equals(getInitParameter("welcomeServlets")))
{
_welcomeExactServlets=true;
@ -242,8 +240,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc
String cc=getInitParameter("cacheControl");
if (cc!=null)
_resourceService.setCacheControl(new PreEncodedHttpField(HttpHeader.CACHE_CONTROL,cc));
String resourceCache = getInitParameter("resourceCache");
int max_cache_size=getInitInt("maxCacheSize", -2);
int max_cached_file_size=getInitInt("maxCachedFileSize", -2);
@ -286,7 +283,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc
}
_resourceService.setContentFactory(contentFactory);
_resourceService.setWelcomeFactory(this);
List<String> gzip_equivalent_file_extensions = new ArrayList<String>();
String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
if (otherGzipExtensions != null)

View File

@ -157,10 +157,14 @@ public class DefaultServletTest
defholder.setInitParameter("gzip", "false");
/* create some content in the docroot */
FS.ensureDirExists(docRoot.resolve("one"));
Path one = docRoot.resolve("one");
FS.ensureDirExists(one);
FS.ensureDirExists(docRoot.resolve("two"));
FS.ensureDirExists(docRoot.resolve("three"));
Path alert = one.resolve("onmouseclick='alert(oops)'");
FS.touch(alert);
/*
* Intentionally bad request URI. Sending a non-encoded URI with typically
* encoded characters '<', '>', and '"'.
@ -172,6 +176,16 @@ public class DefaultServletTest
String body = response.getContent();
assertThat(body, not(containsString("<script>")));
req1 = "GET /context/one/;\"onmouseover='alert(document.location)' HTTP/1.0\r\n" +
"\r\n";
rawResponse = connector.getResponse(req1);
response = HttpTester.parseResponse(rawResponse);
body = response.getContent();
assertThat(body, not(containsString(";\"onmouseover")));
}
@Test

View File

@ -35,7 +35,7 @@ public class UnixSocketClient
{
java.io.File path = new java.io.File("/tmp/jetty.sock");
java.io.File content = new java.io.File("/tmp/data.txt");
String method = "GET";
int content_length = 0;
String body = null;
@ -45,12 +45,12 @@ public class UnixSocketClient
body = IO.readToString(content);
content_length = body.length();
}
String data = method+" / HTTP/1.1\r\n"
+ "Host: unixsock\r\n"
+ "Content-Length: "+content_length+"\r\n"
+ "Connection: close\r\n"
+ "\r\n";
if (body!=null)
String data = method + " / HTTP/1.1\r\n"
+ "Host: unixsock\r\n"
+ "Content-Length: " + content_length + "\r\n"
+ "Connection: close\r\n"
+ "\r\n";
if (body != null)
data += body;
while (true)
@ -59,18 +59,18 @@ public class UnixSocketClient
UnixSocketChannel channel = UnixSocketChannel.open(address);
System.out.println("connected to " + channel.getRemoteSocketAddress());
PrintWriter w = new PrintWriter(new OutputStreamWriter(Channels.newOutputStream(channel),StandardCharsets.ISO_8859_1));
PrintWriter w = new PrintWriter(new OutputStreamWriter(Channels.newOutputStream(channel), StandardCharsets.ISO_8859_1));
InputStreamReader r = new InputStreamReader(Channels.newInputStream(channel));
w.print(data);
w.flush();
CharBuffer result = CharBuffer.allocate(4096);
String total="";
String total = "";
int l = 0;
while (l>=0)
while (l >= 0)
{
if (l>0)
if (l > 0)
{
result.flip();
total += result.toString();

View File

@ -71,18 +71,16 @@ public class UnixSocketTest
server = null;
httpClient = null;
String unixSocketTmp = System.getProperty("unix.socket.tmp");
Path unixSocket;
if (StringUtil.isNotBlank(unixSocketTmp))
unixSocket = Files.createTempFile(Paths.get(unixSocketTmp), "unix", ".sock");
sockFile = Files.createTempFile(Paths.get(unixSocketTmp), "unix", ".sock");
else
unixSocket = Files.createTempFile("unix", ".sock");
if (unixSocket.toAbsolutePath().toString().length() > UnixSocketConnector.MAX_UNIX_SOCKET_PATH_LENGTH)
sockFile = Files.createTempFile("unix", ".sock");
if (sockFile.toAbsolutePath().toString().length() > UnixSocketConnector.MAX_UNIX_SOCKET_PATH_LENGTH)
{
Path tmp = Paths.get("/tmp");
assumeTrue(Files.exists(tmp) && Files.isDirectory(tmp));
unixSocket = Files.createTempFile(tmp, "unix", ".sock");
sockFile = Files.createTempFile(tmp, "unix", ".sock");
}
sockFile = unixSocket;
assertTrue(Files.deleteIfExists(sockFile), "temp sock file cannot be deleted");
}

View File

@ -42,111 +42,111 @@ public class JnrTest
serverChannel.configureBlocking(false);
serverChannel.socket().bind(address);
serverChannel.register(serverSelector, SelectionKey.OP_ACCEPT, "SERVER");
System.err.printf("serverChannel=%s,%n",serverChannel);
UnixSocketChannel client = UnixSocketChannel.open( address );
System.err.printf("serverChannel=%s,%n", serverChannel);
UnixSocketChannel client = UnixSocketChannel.open(address);
Selector clientSelector = NativeSelectorProvider.getInstance().openSelector();
client.configureBlocking(false);
SelectionKey clientKey = client.register(clientSelector,0,"client");
System.err.printf("client=%s connected=%b pending=%b%n",client,client.isConnected(),client.isConnectionPending());
SelectionKey clientKey = client.register(clientSelector, 0, "client");
System.err.printf("client=%s connected=%b pending=%b%n", client, client.isConnected(), client.isConnectionPending());
int selected = serverSelector.select();
System.err.printf("serverSelected=%d %s%n",selected,serverSelector.selectedKeys());
System.err.printf("serverSelected=%d %s%n", selected, serverSelector.selectedKeys());
SelectionKey key = serverSelector.selectedKeys().iterator().next();
serverSelector.selectedKeys().clear();
System.err.printf("key=%s/%s c=%b a=%b r=%b w=%b%n",key,key.attachment(),key.isConnectable(),key.isAcceptable(),key.isReadable(),key.isWritable());
System.err.printf("key=%s/%s c=%b a=%b r=%b w=%b%n", key, key.attachment(), key.isConnectable(), key.isAcceptable(), key.isReadable(), key.isWritable());
UnixSocketChannel server = serverChannel.accept();
server.configureBlocking(false);
SelectionKey serverKey = server.register(serverSelector, SelectionKey.OP_READ, "server");
System.err.printf("server=%s connected=%b pending=%b%n",server,server.isConnected(),server.isConnectionPending());
System.err.printf("server=%s key=%s connected=%b pending=%b%n", server, serverKey, server.isConnected(), server.isConnectionPending());
selected = serverSelector.selectNow();
System.err.printf("serverSelected=%d %s%n",selected,serverSelector.selectedKeys());
System.err.printf("serverSelected=%d %s%n", selected, serverSelector.selectedKeys());
ByteBuffer buffer = ByteBuffer.allocate(32768);
buffer.clear();
int read = server.read(buffer);
buffer.flip();
System.err.printf("server read=%d%n",read);
System.err.printf("server read=%d%n", read);
selected = clientSelector.selectNow();
System.err.printf("clientSelected=%d %s%n",selected,clientSelector.selectedKeys());
System.err.printf("clientSelected=%d %s%n", selected, clientSelector.selectedKeys());
int wrote = client.write(ByteBuffer.wrap("Hello".getBytes(StandardCharsets.ISO_8859_1)));
System.err.printf("client wrote=%d%n",wrote);
System.err.printf("client wrote=%d%n", wrote);
selected = serverSelector.selectNow();
System.err.printf("serverSelected=%d %s%n",selected,serverSelector.selectedKeys());
System.err.printf("serverSelected=%d %s%n", selected, serverSelector.selectedKeys());
key = serverSelector.selectedKeys().iterator().next();
serverSelector.selectedKeys().clear();
System.err.printf("key=%s/%s c=%b a=%b r=%b w=%b ch=%s%n",key,key.attachment(),key.isConnectable(),key.isAcceptable(),key.isReadable(),key.isWritable(),key.channel());
System.err.printf("key=%s/%s c=%b a=%b r=%b w=%b ch=%s%n", key, key.attachment(), key.isConnectable(), key.isAcceptable(), key.isReadable(), key.isWritable(), key.channel());
buffer.clear();
read = server.read(buffer);
buffer.flip();
System.err.printf("server read=%d '%s'%n",read,new String(buffer.array(),0,buffer.limit(),StandardCharsets.ISO_8859_1));
System.err.printf("server read=%d '%s'%n", read, new String(buffer.array(), 0, buffer.limit(), StandardCharsets.ISO_8859_1));
selected = clientSelector.selectNow();
System.err.printf("clientSelected=%d %s%n",selected,clientSelector.selectedKeys());
System.err.printf("clientSelected=%d %s%n", selected, clientSelector.selectedKeys());
wrote = server.write(ByteBuffer.wrap("Ciao!".getBytes(StandardCharsets.ISO_8859_1)));
System.err.printf("server wrote=%d%n",wrote);
System.err.printf("server wrote=%d%n", wrote);
selected = clientSelector.selectNow();
System.err.printf("clientSelected=%d %s%n",selected,clientSelector.selectedKeys());
System.err.printf("clientSelected=%d %s%n", selected, clientSelector.selectedKeys());
clientKey.interestOps(SelectionKey.OP_READ);
selected = clientSelector.selectNow();
System.err.printf("clientSelected=%d %s%n",selected,clientSelector.selectedKeys());
System.err.printf("clientSelected=%d %s%n", selected, clientSelector.selectedKeys());
key = clientSelector.selectedKeys().iterator().next();
clientSelector.selectedKeys().clear();
System.err.printf("key=%s/%s c=%b a=%b r=%b w=%b ch=%s%n",key,key.attachment(),key.isConnectable(),key.isAcceptable(),key.isReadable(),key.isWritable(),key.channel());
System.err.printf("key=%s/%s c=%b a=%b r=%b w=%b ch=%s%n", key, key.attachment(), key.isConnectable(), key.isAcceptable(), key.isReadable(), key.isWritable(), key.channel());
buffer.clear();
read = client.read(buffer);
buffer.flip();
System.err.printf("client read=%d '%s'%n",read,new String(buffer.array(),0,buffer.limit(),StandardCharsets.ISO_8859_1));
System.err.printf("client read=%d '%s'%n", read, new String(buffer.array(), 0, buffer.limit(), StandardCharsets.ISO_8859_1));
System.err.println("So far so good.... now it gets strange...");
// Let's write until flow control hit
int size = buffer.capacity();
Arrays.fill(buffer.array(),0,size,(byte)'X');
Arrays.fill(buffer.array(), 0, size, (byte)'X');
long written = 0;
while(true)
while (true)
{
buffer.position(0).limit(size);
wrote = server.write(buffer);
System.err.printf("server wrote %d/%d remaining=%d%n",wrote,size,buffer.remaining());
if (buffer.remaining()!=(size-wrote))
System.err.printf("server wrote %d/%d remaining=%d%n", wrote, size, buffer.remaining());
if (buffer.remaining() != (size - wrote))
System.err.printf("BUG!!!!!!!!!!!!!!!!%n");
if (wrote==0)
if (wrote == 0)
break;
written+=wrote;
written += wrote;
}
System.err.printf("server wrote %d before flow control%n",written);
System.err.printf("server wrote %d before flow control%n", written);
selected = clientSelector.selectNow();
System.err.printf("clientSelected=%d %s%n",selected,clientSelector.selectedKeys());
System.err.printf("clientSelected=%d %s%n", selected, clientSelector.selectedKeys());
key = clientSelector.selectedKeys().iterator().next();
clientSelector.selectedKeys().clear();
System.err.printf("key=%s/%s c=%b a=%b r=%b w=%b ch=%s%n",key,key.attachment(),key.isConnectable(),key.isAcceptable(),key.isReadable(),key.isWritable(),key.channel());
System.err.printf("key=%s/%s c=%b a=%b r=%b w=%b ch=%s%n", key, key.attachment(), key.isConnectable(), key.isAcceptable(), key.isReadable(), key.isWritable(), key.channel());
buffer.clear();
buffer.limit(32);
read = client.read(buffer);
buffer.flip();
System.err.printf("client read=%d '%s'%n",read,new String(buffer.array(),0,buffer.limit(),StandardCharsets.ISO_8859_1));
System.err.printf("client read=%d '%s'%n", read, new String(buffer.array(), 0, buffer.limit(), StandardCharsets.ISO_8859_1));
server.close();
client.close();

View File

@ -1,7 +1,7 @@
<?xml version="1.0"?>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE Configure PUBLIC "-//Jetty//Configure//EN" "http://www.eclipse.org/jetty/configure_9_3.dtd">
<Configure id="unixSocketConnector" class="org.eclipse.jetty.server.ServerConnector">
<Configure id="unixSocketConnector" class="org.eclipse.jetty.unixsocket.UnixSocketConnector">
<Call name="addFirstConnectionFactory">
<Arg>
<New class="org.eclipse.jetty.server.ProxyConnectionFactory"/>

View File

@ -19,19 +19,19 @@ server
etc/jetty-unixsocket.xml
[files]
maven://com.github.jnr/jnr-unixsocket/0.8|lib/jnr/jnr-unixsocket-0.8.jar
maven://com.github.jnr/jnr-ffi/2.0.3|lib/jnr/jnr-ffi-2.0.3.jar
maven://com.github.jnr/jffi/1.2.9|lib/jnr/jffi-1.2.9.jar
maven://com.github.jnr/jffi/1.2.9/jar/native|lib/jnr/jffi-1.2.9-native.jar
maven://org.ow2.asm/asm/5.0.1|lib/jnr/asm-5.0.1.jar
maven://org.ow2.asm/asm-commons/5.0.1|lib/jnr/asm-commons-5.0.1.jar
maven://org.ow2.asm/asm-analysis/5.0.3|lib/jnr/asm-analysis-5.0.3.jar
maven://org.ow2.asm/asm-tree/5.0.3|lib/jnr/asm-tree-5.0.3.jar
maven://org.ow2.asm/asm-util/5.0.3|lib/jnr/asm-util-5.0.3.jar
maven://com.github.jnr/jnr-unixsocket/0.20|lib/jnr/jnr-unixsocket-0.20.jar
maven://com.github.jnr/jnr-ffi/2.1.9|lib/jnr/jnr-ffi-2.1.9.jar
maven://com.github.jnr/jffi/1.2.17|lib/jnr/jffi-1.2.17.jar
maven://com.github.jnr/jffi/1.2.16/jar/native|lib/jnr/jffi-1.2.16-native.jar
maven://org.ow2.asm/asm/7.0|lib/jnr/asm-7.0.jar
maven://org.ow2.asm/asm-commons/7.0|lib/jnr/asm-commons-7.0.jar
maven://org.ow2.asm/asm-analysis/7.0|lib/jnr/asm-analysis-7.0.jar
maven://org.ow2.asm/asm-tree/7.0|lib/jnr/asm-tree-7.0.jar
maven://org.ow2.asm/asm-util/7.0|lib/jnr/asm-util-7.0.jar
maven://com.github.jnr/jnr-x86asm/1.0.2|lib/jnr/jnr-x86asm-1.0.2.jar
maven://com.github.jnr/jnr-constants/0.8.7|lib/jnr/jnr-constants-0.8.7.jar
maven://com.github.jnr/jnr-enxio/0.9|lib/jnr/jnr-enxio-0.9.jar
maven://com.github.jnr/jnr-posix/3.0.12|lib/jnr/jnr-posix-3.0.12.jar
maven://com.github.jnr/jnr-constants/0.9.11|lib/jnr/jnr-constants-0.9.11.jar
maven://com.github.jnr/jnr-enxio/0.18|lib/jnr/jnr-enxio-0.18.jar
maven://com.github.jnr/jnr-posix/3.0.46|lib/jnr/jnr-posix-3.0.46.jar
[lib]
lib/jetty-unixsocket-${jetty.version}.jar

View File

@ -53,6 +53,9 @@ import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.Scheduler;
/**
* <p>A server-side connector for UNIX sockets.</p>
*/
@ManagedObject("Connector using UNIX Socket")
public class UnixSocketConnector extends AbstractConnector
{
@ -67,10 +70,9 @@ public class UnixSocketConnector extends AbstractConnector
private volatile boolean _reuseAddress = true;
/**
* HTTP Server Connection.
* <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</p>
* <p>Constructs a UnixSocketConnector with the default configuration.</p>
*
* @param server The {@link Server} this connector will accept connection for.
* @param server the {@link Server} this connector will accept connections for.
*/
public UnixSocketConnector(@Name("server") Server server)
{
@ -78,11 +80,10 @@ public class UnixSocketConnector extends AbstractConnector
}
/**
* HTTP Server Connection.
* <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</p>
* <p>Constructs a UnixSocketConnector with the given number of selectors</p>
*
* @param server The {@link Server} this connector will accept connection for.
* @param selectors the number of selector threads, or &lt;=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
* @param server the {@link Server} this connector will accept connections for.
* @param selectors the number of selectors, or &lt;=0 for a default value.
*/
public UnixSocketConnector(@Name("server") Server server, @Name("selectors") int selectors)
{
@ -90,11 +91,10 @@ public class UnixSocketConnector extends AbstractConnector
}
/**
* Generic Server Connection with default configuration.
* <p>Construct a Server Connector with the passed Connection factories.</p>
* <p>Constructs a UnixSocketConnector with the given ConnectionFactories.</p>
*
* @param server The {@link Server} this connector will accept connection for.
* @param factories Zero or more {@link ConnectionFactory} instances used to create and configure connections.
* @param server the {@link Server} this connector will accept connections for.
* @param factories zero or more {@link ConnectionFactory} instances used to create and configure connections.
*/
public UnixSocketConnector(@Name("server") Server server, @Name("factories") ConnectionFactory... factories)
{
@ -102,12 +102,11 @@ public class UnixSocketConnector extends AbstractConnector
}
/**
* HTTP Server Connection.
* <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</p>
* <p>Constructs a UnixSocketConnector with the given selectors and ConnectionFactories.</p>
*
* @param server The {@link Server} this connector will accept connection for.
* @param selectors the number of selector threads, or &lt;=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
* @param factories Zero or more {@link ConnectionFactory} instances used to create and configure connections.
* @param server the {@link Server} this connector will accept connections for.
* @param selectors the number of selectors, or &lt;=0 for a default value.
* @param factories zero or more {@link ConnectionFactory} instances used to create and configure connections.
*/
public UnixSocketConnector(@Name("server") Server server, @Name("selectors") int selectors, @Name("factories") ConnectionFactory... factories)
{
@ -115,12 +114,10 @@ public class UnixSocketConnector extends AbstractConnector
}
/**
* HTTP Server Connection.
* <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the primary protocol</p>.
* <p>Constructs a UnixSocketConnector with the given SslContextFactory.</p>
*
* @param server The {@link Server} this connector will accept connection for.
* @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the
* list of HTTP Connection Factory.
* @param server the {@link Server} this connector will accept connections for.
* @param sslContextFactory when non null a {@link SslConnectionFactory} prepended to the other ConnectionFactories
*/
public UnixSocketConnector(@Name("server") Server server, @Name("sslContextFactory") SslContextFactory.Server sslContextFactory)
{
@ -128,13 +125,11 @@ public class UnixSocketConnector extends AbstractConnector
}
/**
* HTTP Server Connection.
* <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the primary protocol</p>.
* <p>Constructs a UnixSocketConnector with the given selectors and SslContextFactory.</p>.
*
* @param server The {@link Server} this connector will accept connection for.
* @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the
* list of HTTP Connection Factory.
* @param selectors the number of selector threads, or &lt;=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
* @param server the {@link Server} this connector will accept connections for.
* @param sslContextFactory when non null a {@link SslConnectionFactory} prepended to the other ConnectionFactories
* @param selectors the number of selectors, or &lt;=0 for a default value.
*/
public UnixSocketConnector(@Name("server") Server server, @Name("selectors") int selectors, @Name("sslContextFactory") SslContextFactory.Server sslContextFactory)
{
@ -142,12 +137,11 @@ public class UnixSocketConnector extends AbstractConnector
}
/**
* Generic SSL Server Connection.
* <p>Constructs a UnixSocketConnector with the given SslContextFactory and ConnectionFactories.</p>.
*
* @param server The {@link Server} this connector will accept connection for.
* @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the
* list of ConnectionFactories, with the first factory being the default protocol for the SslConnectionFactory.
* @param factories Zero or more {@link ConnectionFactory} instances used to create and configure connections.
* @param server the {@link Server} this connector will accept connections for.
* @param sslContextFactory when non null a {@link SslConnectionFactory} prepended to the other ConnectionFactories
* @param factories zero or more {@link ConnectionFactory} instances used to create and configure connections.
*/
public UnixSocketConnector(@Name("server") Server server, @Name("sslContextFactory") SslContextFactory.Server sslContextFactory, @Name("factories") ConnectionFactory... factories)
{
@ -155,15 +149,14 @@ public class UnixSocketConnector extends AbstractConnector
}
/**
* Generic Server Connection.
* <p>Constructs a UnixSocketConnector with the given parameters.</p>.
*
* @param server The server this connector will be accept connection for.
* @param executor An executor used to run tasks for handling requests, acceptors and selectors.
* If null then use the servers executor
* @param scheduler A scheduler used to schedule timeouts. If null then use the servers scheduler
* @param bufferPool A ByteBuffer pool used to allocate buffers. If null then create a private pool with default configuration.
* @param selectors the number of selector threads, or &lt;=0 for a default value(1). Selectors notice and schedule established connection that can make IO progress.
* @param factories Zero or more {@link ConnectionFactory} instances used to create and configure connections.
* @param server the {@link Server} this connector will accept connections for.
* @param executor the executor that runs tasks for handling requests, acceptors and selectors.
* @param scheduler the scheduler used to schedule timed tasks.
* @param bufferPool the ByteBufferPool used to allocate buffers.
* @param selectors the number of selectors, or &lt;=0 for a default value.
* @param factories zero or more {@link ConnectionFactory} instances used to create and configure connections.
*/
public UnixSocketConnector(@Name("server") Server server, @Name("executor") Executor executor, @Name("scheduler") Scheduler scheduler, @Name("bufferPool") ByteBufferPool bufferPool, @Name("selectors") int selectors, @Name("factories") ConnectionFactory... factories)
{
@ -172,7 +165,7 @@ public class UnixSocketConnector extends AbstractConnector
addBean(_manager, true);
}
@ManagedAttribute
@ManagedAttribute("The UNIX socket file name")
public String getUnixSocket()
{
return _unixSocket;
@ -295,7 +288,7 @@ public class UnixSocketConnector extends AbstractConnector
return _acceptChannel;
}
protected UnixSocketEndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey key) throws IOException
protected UnixSocketEndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey key)
{
return new UnixSocketEndPoint((UnixSocketChannel)channel, selector, key, getScheduler());
}
@ -321,6 +314,7 @@ public class UnixSocketConnector extends AbstractConnector
* @return whether the server socket reuses addresses
* @see ServerSocket#getReuseAddress()
*/
@ManagedAttribute("Whether the server socket reuses addresses")
public boolean getReuseAddress()
{
return _reuseAddress;
@ -338,9 +332,7 @@ public class UnixSocketConnector extends AbstractConnector
@Override
public String toString()
{
return String.format("%s{%s}",
super.toString(),
_unixSocket);
return String.format("%s{%s}", super.toString(), _unixSocket);
}
protected class UnixSocketConnectorManager extends SelectorManager
@ -363,15 +355,15 @@ public class UnixSocketConnector extends AbstractConnector
}
@Override
protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey selectionKey) throws IOException
protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey selectionKey)
{
UnixSocketEndPoint endp = UnixSocketConnector.this.newEndPoint(channel, selector, selectionKey);
endp.setIdleTimeout(getIdleTimeout());
return endp;
UnixSocketEndPoint endPoint = UnixSocketConnector.this.newEndPoint(channel, selector, selectionKey);
endPoint.setIdleTimeout(getIdleTimeout());
return endPoint;
}
@Override
public Connection newConnection(SelectableChannel channel, EndPoint endpoint, Object attachment) throws IOException
public Connection newConnection(SelectableChannel channel, EndPoint endpoint, Object attachment)
{
return getDefaultConnectionFactory().newConnection(UnixSocketConnector.this, endpoint);
}

View File

@ -25,7 +25,6 @@ import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@ -38,32 +37,31 @@ import org.eclipse.jetty.unixsocket.server.UnixSocketConnector;
public class UnixSocketProxyServer
{
public static void main (String... args) throws Exception
public static void main(String... args) throws Exception
{
Server server = new Server();
HttpConnectionFactory http = new HttpConnectionFactory();
ProxyConnectionFactory proxy = new ProxyConnectionFactory(http.getProtocol());
UnixSocketConnector connector = new UnixSocketConnector(server,proxy,http);
UnixSocketConnector connector = new UnixSocketConnector(server, proxy, http);
server.addConnector(connector);
Path socket = Paths.get(connector.getUnixSocket());
if (Files.exists(socket))
Files.delete(socket);
server.setHandler(new AbstractHandler.ErrorDispatchHandler()
{
@Override
protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
int l = 0;
if (request.getContentLength()!=0)
if (request.getContentLength() != 0)
{
InputStream in = request.getInputStream();
byte[] buffer = new byte[4096];
int r = 0;
while (r>=0)
while (r >= 0)
{
l += r;
r = in.read(buffer);
@ -71,21 +69,21 @@ public class UnixSocketProxyServer
}
baseRequest.setHandled(true);
response.setStatus(200);
response.getWriter().write("Hello World "+new Date() + "\r\n");
response.getWriter().write("remote="+request.getRemoteAddr()+":"+request.getRemotePort()+"\r\n");
response.getWriter().write("local ="+request.getLocalAddr()+":"+request.getLocalPort()+"\r\n");
response.getWriter().write("read ="+l+"\r\n");
response.getWriter().write("Hello World " + new Date() + "\r\n");
response.getWriter().write("remote=" + request.getRemoteAddr() + ":" + request.getRemotePort() + "\r\n");
response.getWriter().write("local =" + request.getLocalAddr() + ":" + request.getLocalPort() + "\r\n");
response.getWriter().write("read =" + l + "\r\n");
}
});
server.start();
while (true)
{
Thread.sleep(5000);
connector.dumpStdErr();
}
// server.join();
}
}

View File

@ -36,12 +36,12 @@ import org.eclipse.jetty.unixsocket.server.UnixSocketConnector;
public class UnixSocketServer
{
public static void main (String... args) throws Exception
public static void main(String... args) throws Exception
{
Server server = new Server();
HttpConnectionFactory http = new HttpConnectionFactory();
UnixSocketConnector connector = new UnixSocketConnector(server,http);
UnixSocketConnector connector = new UnixSocketConnector(server, http);
server.addConnector(connector);
Path socket = Paths.get(connector.getUnixSocket());
@ -51,16 +51,15 @@ public class UnixSocketServer
server.setHandler(new AbstractHandler.ErrorDispatchHandler()
{
@Override
protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
throws IOException
protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
{
int l = 0;
if (request.getContentLength()!=0)
if (request.getContentLength() != 0)
{
InputStream in = request.getInputStream();
byte[] buffer = new byte[4096];
int r = 0;
while (r>=0)
while (r >= 0)
{
l += r;
r = in.read(buffer);
@ -68,10 +67,10 @@ public class UnixSocketServer
}
baseRequest.setHandled(true);
response.setStatus(200);
response.getWriter().write("Hello World "+new Date() + "\r\n");
response.getWriter().write("remote="+request.getRemoteAddr()+":"+request.getRemotePort()+"\r\n");
response.getWriter().write("local ="+request.getLocalAddr()+":"+request.getLocalPort()+"\r\n");
response.getWriter().write("read ="+l+"\r\n");
response.getWriter().write("Hello World " + new Date() + "\r\n");
response.getWriter().write("remote=" + request.getRemoteAddr() + ":" + request.getRemotePort() + "\r\n");
response.getWriter().write("local =" + request.getLocalAddr() + ":" + request.getLocalPort() + "\r\n");
response.getWriter().write("read =" + l + "\r\n");
}
});

View File

@ -253,6 +253,7 @@ public class BufferUtil
}
}
/* ------------------------------------------------------------ */
/**
* @param buf the buffer to check
* @return true if buf is equal to EMPTY_BUFFER
@ -274,6 +275,36 @@ public class BufferUtil
return buf == null || buf.remaining() == 0;
}
/* ------------------------------------------------------------ */
/** Check for an empty or null buffers.
* @param buf the buffer to check
* @return true if the buffer is null or empty.
*/
public static boolean isEmpty(ByteBuffer[] buf)
{
if (buf==null || buf.length==0)
return true;
for (ByteBuffer b : buf)
if (b!=null && b.hasRemaining())
return false;
return true;
}
/* ------------------------------------------------------------ */
/** Get the remaining bytes in 0 or more buffers.
* @param buf the buffers to check
* @return number of bytes remaining in all buffers.
*/
public static long remaining(ByteBuffer... buf)
{
long remaining = 0;
if (buf!=null)
for (ByteBuffer b : buf)
if (b!=null)
remaining += b.remaining();
return remaining;
}
/* ------------------------------------------------------------ */
/** Check for a non null and non empty buffer.
* @param buf the buffer to check

View File

@ -310,6 +310,7 @@ public interface Callback extends Invocable
return callback.getInvocationType();
}
}
/**
* <p>A CompletableFuture that is also a Callback.</p>
*/

View File

@ -31,15 +31,17 @@ import java.nio.channels.ReadableByteChannel;
import java.nio.file.Path;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.eclipse.jetty.util.B64Code;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.Loader;
import org.eclipse.jetty.util.MultiMap;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.URIUtil;
import org.eclipse.jetty.util.UrlEncoded;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@ -102,7 +104,7 @@ public abstract class Resource implements ResourceFactory, Closeable
* Construct a resource from a url.
* @param url the url for which to make the resource
* @param useCaches true enables URLConnection caching if applicable to the type of resource
* @return
* @return a new resource from the given URL
*/
static Resource newResource(URL url, boolean useCaches)
{
@ -143,8 +145,7 @@ public abstract class Resource implements ResourceFactory, Closeable
* @throws MalformedURLException Problem accessing URI
* @return A Resource object.
*/
public static Resource newResource(String resource)
throws MalformedURLException, IOException
public static Resource newResource(String resource) throws IOException
{
return newResource(resource, __defaultUseCaches);
}
@ -156,10 +157,9 @@ public abstract class Resource implements ResourceFactory, Closeable
* @return A Resource object.
* @throws MalformedURLException Problem accessing URI
*/
public static Resource newResource(String resource, boolean useCaches)
throws MalformedURLException, IOException
public static Resource newResource(String resource, boolean useCaches) throws IOException
{
URL url=null;
URL url;
try
{
// Try to format as a URL?
@ -222,8 +222,7 @@ public abstract class Resource implements ResourceFactory, Closeable
* @return The new Resource
* @throws IOException Problem accessing resource.
*/
public static Resource newSystemResource(String resource)
throws IOException
public static Resource newSystemResource(String resource) throws IOException
{
URL url=null;
// Try to format as a URL?
@ -444,7 +443,6 @@ public abstract class Resource implements ResourceFactory, Closeable
* <p>
* This method is essentially an alias for {@link #addPath(String)}, but without checked exceptions.
* This method satisfied the {@link ResourceFactory} interface.
* @see org.eclipse.jetty.util.resource.ResourceFactory#getResource(java.lang.String)
*/
@Override
public Resource getResource(String path)
@ -501,65 +499,228 @@ public abstract class Resource implements ResourceFactory, Closeable
* @return String of HTML
* @throws IOException if unable to get the list of resources as HTML
*/
public String getListHTML(String base,boolean parent)
throws IOException
public String getListHTML(String base, boolean parent) throws IOException
{
return getListHTML(base, parent, null);
}
/** Get the resource list as a HTML directory listing.
* @param base The base URL
* @param parent True if the parent directory should be included
* @param query query params
* @return String of HTML
*/
public String getListHTML(String base, boolean parent, String query) throws IOException
{
base=URIUtil.canonicalPath(base);
if (base==null || !isDirectory())
return null;
String[] ls = list();
if (ls==null)
return null;
Arrays.sort(ls);
String decodedBase = URIUtil.decodePath(base);
String title = "Directory: "+deTag(decodedBase);
StringBuilder buf=new StringBuilder(4096);
buf.append("<HTML><HEAD>");
buf.append("<LINK HREF=\"").append("jetty-dir.css").append("\" REL=\"stylesheet\" TYPE=\"text/css\"/><TITLE>");
String[] rawListing = list();
if (rawListing == null)
{
return null;
}
boolean sortOrderAscending = true;
String sortColumn = "N"; // name (or "M" for Last Modified, or "S" for Size)
// check for query
if (query != null)
{
MultiMap<String> params = new MultiMap<>();
UrlEncoded.decodeUtf8To(query, 0, query.length(), params);
String paramO = params.getString("O");
String paramC = params.getString("C");
if (StringUtil.isNotBlank(paramO))
{
if (paramO.equals("A"))
{
sortOrderAscending = true;
}
else if (paramO.equals("D"))
{
sortOrderAscending = false;
}
}
if (StringUtil.isNotBlank(paramC))
{
if (paramC.equals("N") || paramC.equals("M") || paramC.equals("S"))
{
sortColumn = paramC;
}
}
}
// Gather up entries
List<Resource> items = new ArrayList<>();
for (String l : rawListing)
{
Resource item = addPath(l);
items.add(item);
}
// Perform sort
if (sortColumn.equals("M"))
{
items.sort(ResourceCollators.byLastModified(sortOrderAscending));
}
else if (sortColumn.equals("S"))
{
items.sort(ResourceCollators.bySize(sortOrderAscending));
}
else
{
items.sort(ResourceCollators.byName(sortOrderAscending));
}
String decodedBase = URIUtil.decodePath(base);
String title = "Directory: " + deTag(decodedBase);
StringBuilder buf = new StringBuilder(4096);
// Doctype Declaration (HTML5)
buf.append("<!DOCTYPE html>\n");
buf.append("<html lang=\"en\">\n");
// HTML Header
buf.append("<head>\n");
buf.append("<meta charset=\"utf-8\">\n");
buf.append("<link href=\"jetty-dir.css\" rel=\"stylesheet\" />\n");
buf.append("<title>");
buf.append(title);
buf.append("</TITLE></HEAD><BODY>\n<H1>");
buf.append(title);
buf.append("</H1>\n<TABLE BORDER=0>\n");
buf.append("</title>\n");
buf.append("</head>\n");
// HTML Body
buf.append("<body>\n");
buf.append("<h1 class=\"title\">").append(title).append("</h1>\n");
// HTML Table
final String ARROW_DOWN = "&nbsp; &#8681;";
final String ARROW_UP = "&nbsp; &#8679;";
String arrow;
String order;
buf.append("<table class=\"listing\">\n");
buf.append("<thead>\n");
arrow = "";
order = "A";
if (sortColumn.equals("N"))
{
if(sortOrderAscending)
{
order = "D";
arrow = ARROW_UP;
}
else
{
order = "A";
arrow = ARROW_DOWN;
}
}
buf.append("<tr><th class=\"name\"><a href=\"?C=N&O=").append(order).append("\">");
buf.append("Name").append(arrow);
buf.append("</a></th>");
arrow = "";
order = "A";
if (sortColumn.equals("M"))
{
if(sortOrderAscending)
{
order = "D";
arrow = ARROW_UP;
}
else
{
order = "A";
arrow = ARROW_DOWN;
}
}
buf.append("<th class=\"lastmodified\"><a href=\"?C=M&O=").append(order).append("\">");
buf.append("Last Modified").append(arrow);
buf.append("</a></th>");
arrow = "";
order = "A";
if (sortColumn.equals("S"))
{
if(sortOrderAscending)
{
order = "D";
arrow = ARROW_UP;
}
else
{
order = "A";
arrow = ARROW_DOWN;
}
}
buf.append("<th class=\"size\"><a href=\"?C=S&O=").append(order).append("\">");
buf.append("Size").append(arrow);
buf.append("</a></th></tr>\n");
buf.append("</thead>\n");
buf.append("<tbody>\n");
String encodedBase = hrefEncodeURI(base);
if (parent)
{
buf.append("<TR><TD><A HREF=\"");
buf.append(URIUtil.addEncodedPaths(base,"../"));
buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
// Name
buf.append("<tr><td class=\"name\"><a href=\"");
buf.append(URIUtil.addPaths(encodedBase,"../"));
buf.append("\">Parent Directory</a></td>");
// Last Modified
buf.append("<td class=\"lastmodified\">-</td>");
// Size
buf.append("<td>-</td>");
buf.append("</tr>\n");
}
String encodedBase = hrefEncodeURI(base);
DateFormat dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
DateFormat.MEDIUM);
for (int i=0 ; i< ls.length ; i++)
for (Resource item: items)
{
Resource item = addPath(ls[i]);
buf.append("\n<TR><TD><A HREF=\"");
String path=URIUtil.addEncodedPaths(encodedBase,URIUtil.encodePath(ls[i]));
String name = item.getName();
int slashIdx = name.lastIndexOf('/');
if (slashIdx != -1)
{
name = name.substring(slashIdx + 1);
}
if (item.isDirectory() && !name.endsWith("/"))
{
name += URIUtil.SLASH;
}
// Name
buf.append("<tr><td class=\"name\"><a href=\"");
String path=URIUtil.addEncodedPaths(encodedBase,URIUtil.encodePath(name));
buf.append(path);
if (item.isDirectory() && !path.endsWith("/"))
buf.append(URIUtil.SLASH);
// URIUtil.encodePath(buf,path);
buf.append("\">");
buf.append(deTag(ls[i]));
buf.append(deTag(name));
buf.append("&nbsp;");
buf.append("</A></TD><TD ALIGN=right>");
buf.append(item.length());
buf.append(" bytes&nbsp;</TD><TD>");
buf.append("</a></td>");
// Last Modified
buf.append("<td class=\"lastmodified\">");
buf.append(dfmt.format(new Date(item.lastModified())));
buf.append("</TD></TR>");
buf.append("</td>");
// Size
buf.append("<td class=\"size\">");
buf.append(String.format("%,d", item.length()));
buf.append(" bytes&nbsp;</td></tr>\n");
}
buf.append("</TABLE>\n");
buf.append("</BODY></HTML>\n");
buf.append("</tbody>\n");
buf.append("</table>\n");
buf.append("</body></html>\n");
return buf.toString();
}
@ -599,21 +760,21 @@ public abstract class Resource implements ResourceFactory, Closeable
char c=raw.charAt(i);
switch(c)
{
case '"':
buf.append("%22");
continue;
case '\'':
buf.append("%27");
continue;
case '<':
buf.append("%3C");
continue;
case '>':
buf.append("%3E");
continue;
default:
buf.append(c);
continue;
case '"':
buf.append("%22");
break;
case '\'':
buf.append("%27");
break;
case '<':
buf.append("%3C");
break;
case '>':
buf.append("%3E");
break;
default:
buf.append(c);
break;
}
}

View File

@ -0,0 +1,91 @@
//
// ========================================================================
// Copyright (c) 1995-2019 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.resource;
import java.text.Collator;
import java.util.Collections;
import java.util.Comparator;
import java.util.Locale;
public class ResourceCollators
{
private static Comparator<? super Resource> BY_NAME_ASCENDING =
new Comparator<>()
{
private final Collator collator = Collator.getInstance(Locale.ENGLISH);
@Override
public int compare(Resource o1, Resource o2)
{
return collator.compare(o1.getName(), o2.getName());
}
};
private static Comparator<? super Resource> BY_NAME_DESCENDING =
Collections.reverseOrder(BY_NAME_ASCENDING);
private static Comparator<? super Resource> BY_LAST_MODIFIED_ASCENDING =
Comparator.comparingLong(Resource::lastModified);
private static Comparator<? super Resource> BY_LAST_MODIFIED_DESCENDING =
Collections.reverseOrder(BY_LAST_MODIFIED_ASCENDING);
private static Comparator<? super Resource> BY_SIZE_ASCENDING =
Comparator.comparingLong(Resource::length);
private static Comparator<? super Resource> BY_SIZE_DESCENDING =
Collections.reverseOrder(BY_SIZE_ASCENDING);
public static Comparator<? super Resource> byLastModified(boolean sortOrderAscending)
{
if (sortOrderAscending)
{
return BY_LAST_MODIFIED_ASCENDING;
}
else
{
return BY_LAST_MODIFIED_DESCENDING;
}
}
public static Comparator<? super Resource> byName(boolean sortOrderAscending)
{
if (sortOrderAscending)
{
return BY_NAME_ASCENDING;
}
else
{
return BY_NAME_DESCENDING;
}
}
public static Comparator<? super Resource> bySize(boolean sortOrderAscending)
{
if (sortOrderAscending)
{
return BY_SIZE_ASCENDING;
}
else
{
return BY_SIZE_DESCENDING;
}
}
}

View File

@ -18,6 +18,7 @@
package org.eclipse.jetty.util.thread;
import java.io.Closeable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -146,6 +147,9 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
@Override
protected void doStop() throws Exception
{
if (LOG.isDebugEnabled())
LOG.debug("Stopping {}", this);
removeBean(_tryExecutor);
_tryExecutor = TryExecutor.NO_TRY;
@ -163,11 +167,13 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
for (int i = _threadsStarted.get(); i-- > 0; )
jobs.offer(noop);
// try to jobs complete naturally for half our stop time
// try to let jobs complete naturally for half our stop time
long stopby = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2;
for (Thread thread : _threads)
{
long canwait = TimeUnit.NANOSECONDS.toMillis(stopby - System.nanoTime());
if (LOG.isDebugEnabled())
LOG.debug("Waiting for {} for {}", thread, canwait);
if (canwait > 0)
thread.join(canwait);
}
@ -177,13 +183,19 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
// interrupt remaining threads
if (_threadsStarted.get() > 0)
for (Thread thread : _threads)
{
if (LOG.isDebugEnabled())
LOG.debug("Interrupting {}", thread);
thread.interrupt();
}
// wait again for the other half of our stop time
stopby = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2;
for (Thread thread : _threads)
{
long canwait = TimeUnit.NANOSECONDS.toMillis(stopby - System.nanoTime());
if (LOG.isDebugEnabled())
LOG.debug("Waiting for {} for {}", thread, canwait);
if (canwait > 0)
thread.join(canwait);
}
@ -213,6 +225,25 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
}
}
// Close any un-executed jobs
while (!_jobs.isEmpty())
{
Runnable job = _jobs.poll();
if (job instanceof Closeable)
{
try
{
((Closeable)job).close();
}
catch (Throwable t)
{
LOG.warn(t);
}
}
else if (job != noop)
LOG.warn("Stopped without executing or closing {}", job);
}
if (_budget!=null)
_budget.reset();
@ -535,6 +566,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
thread.setDaemon(isDaemon());
thread.setPriority(getThreadsPriority());
thread.setName(_name + "-" + thread.getId());
if (LOG.isDebugEnabled())
LOG.debug("Starting {}", thread);
_threads.add(thread);
_lastShrink.set(System.nanoTime());
thread.start();
@ -651,105 +684,93 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
_tryExecutor);
}
private Runnable idleJobPoll() throws InterruptedException
{
return _jobs.poll(_idleTimeout, TimeUnit.MILLISECONDS);
}
private Runnable _runnable = new Runnable()
{
@Override
public void run()
{
boolean shrink = false;
boolean ignore = false;
boolean idle = false;
try
{
Runnable job = _jobs.poll();
if (job != null && _threadsIdle.get() == 0)
{
startThreads(1);
}
loop:
while (isRunning())
while (true)
{
// Job loop
while (job != null && isRunning())
if (job == null)
{
if (!idle)
{
idle = true;
_threadsIdle.incrementAndGet();
}
if (_idleTimeout <= 0)
job = _jobs.take();
else
{
// maybe we should shrink?
int size = _threadsStarted.get();
if (size > _minThreads)
{
long last = _lastShrink.get();
long now = System.nanoTime();
if (last == 0 || (now - last) > TimeUnit.MILLISECONDS.toNanos(_idleTimeout))
{
if (_lastShrink.compareAndSet(last, now))
break;
}
}
job = _jobs.poll(_idleTimeout, TimeUnit.MILLISECONDS);
}
}
// run job
if (job != null)
{
if (idle)
{
idle = false;
if (_threadsIdle.decrementAndGet() == 0)
startThreads(1);
}
if (LOG.isDebugEnabled())
LOG.debug("run {}", job);
runJob(job);
if (LOG.isDebugEnabled())
LOG.debug("ran {}", job);
if (Thread.interrupted())
{
ignore = true;
break loop;
}
job = _jobs.poll();
// Clear interrupted status
Thread.interrupted();
}
// Idle loop
try
{
_threadsIdle.incrementAndGet();
if (!isRunning())
break;
while (isRunning() && job == null)
{
if (_idleTimeout <= 0)
job = _jobs.take();
else
{
// maybe we should shrink?
final int size = _threadsStarted.get();
if (size > _minThreads)
{
long last = _lastShrink.get();
long now = System.nanoTime();
if (last == 0 || (now - last) > TimeUnit.MILLISECONDS.toNanos(_idleTimeout))
{
if (_lastShrink.compareAndSet(last, now) && _threadsStarted.compareAndSet(size, size - 1))
{
shrink = true;
break loop;
}
}
}
job = idleJobPoll();
}
}
}
finally
{
if (_threadsIdle.decrementAndGet() == 0)
{
startThreads(1);
}
}
job = _jobs.poll();
}
}
catch (InterruptedException e)
{
ignore = true;
LOG.ignore(e);
}
catch (Throwable e)
{
LOG.warn(e);
LOG.warn(String.format("Unexpected thread death: %s in %s", this, QueuedThreadPool.this), e);
}
finally
{
if (!shrink && isRunning())
{
if (!ignore)
LOG.warn("Unexpected thread death: {} in {}", this, QueuedThreadPool.this);
// This is an unexpected thread death!
if (_threadsStarted.decrementAndGet() < getMaxThreads())
startThreads(1);
}
if (idle)
_threadsIdle.decrementAndGet();
removeThread(Thread.currentThread());
if (_threadsStarted.decrementAndGet() < getMinThreads())
startThreads(1);
}
}
};

View File

@ -0,0 +1,111 @@
//
// ========================================================================
// Copyright (c) 1995-2019 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 java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import org.eclipse.jetty.util.log.Log;
/**
* An executor than ensurers serial execution of submitted tasks.
* <p>
* Callers of this execute will never block in the executor, but they may
* be required to either execute the task they submit or tasks submitted
* by other threads whilst they are executing tasks.
* </p>
* <p>
* This class was inspired by the public domain class
* <a href="https://github.com/jroper/reactive-streams-servlet/blob/master/reactive-streams-servlet/src/main/java/org/reactivestreams/servlet/NonBlockingMutexExecutor.java">NonBlockingMutexExecutor</a>
* </p>
*/
public class SerializedExecutor implements Executor
{
private final AtomicReference<Link> _tail = new AtomicReference<>();
@Override
public void execute(Runnable task)
{
Link link = new Link(task);
Link lastButOne = _tail.getAndSet(link);
if (lastButOne==null)
run(link);
else
lastButOne._next.lazySet(link);
}
protected void onError(Runnable task, Throwable t)
{
if (task instanceof ErrorHandlingTask)
((ErrorHandlingTask)task).accept(t);
Log.getLogger(task.getClass()).warn(t);
}
private void run(Link link)
{
while(link!=null)
{
try
{
link._task.run();
}
catch (Throwable t)
{
onError(link._task, t);
}
finally
{
// Are we the current the last Link?
if (_tail.compareAndSet(link, null))
link = null;
else
{
// not the last task, so its next link will eventually be set
Link next = link._next.get();
while (next == null)
{
Thread.yield(); // Thread.onSpinWait();
next = link._next.get();
}
link = next;
}
}
}
}
private class Link
{
private final Runnable _task;
private final AtomicReference<Link> _next = new AtomicReference<>();
public Link(Runnable task)
{
_task = task;
}
}
/**
* Error handling task
* <p>If a submitted task implements this interface, it will be passed
* any exceptions thrown when running the task.</p>
*/
public interface ErrorHandlingTask extends Runnable, Consumer<Throwable>
{}
}

View File

@ -1,19 +1,38 @@
body
{
body {
background-color: #FFFFFF;
margin: 10px;
padding: 5px;
font-family: sans-serif;
}
h1
{
h1.title {
text-shadow: #000000 -1px -1px 1px;
color: #FC390E;
font-weight: bold;
}
a
{
table.listing {
border: 0px;
}
thead a {
color: blue;
}
thead th {
border-bottom: black 1px solid;
}
.name, .lastmodified {
text-align: left;
padding-right: 15px;
}
.size {
text-align: right;
}
a {
color: #7036be;
font-weight: bold;
font-style: normal;
@ -21,10 +40,9 @@ a
font-size:inherit;
}
td
{
td {
font-style: italic;
padding: 2px 15px 2px 0px;
padding: 2px;
}

View File

@ -18,22 +18,30 @@
package org.eclipse.jetty.util.thread;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
import org.junit.jupiter.api.Test;
import java.io.Closeable;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.is;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class QueuedThreadPoolTest extends AbstractThreadPoolTest
{
private static final Logger LOG = Log.getLogger(QueuedThreadPoolTest.class);
private final AtomicInteger _jobs=new AtomicInteger();
private class RunningJob implements Runnable
@ -41,6 +49,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
private final CountDownLatch _run = new CountDownLatch(1);
private final CountDownLatch _stopping = new CountDownLatch(1);
private final CountDownLatch _stopped = new CountDownLatch(1);
@Override
public void run()
{
@ -51,7 +60,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
}
catch(Exception e)
{
e.printStackTrace();
LOG.debug(e);
}
finally
{
@ -69,6 +78,17 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
}
}
private class CloseableJob extends RunningJob implements Closeable
{
private final CountDownLatch _closed = new CountDownLatch(1);
@Override
public void close() throws IOException
{
_closed.countDown();
}
}
@Test
public void testThreadPool() throws Exception
{
@ -146,6 +166,58 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
waitForIdle(tp,2);
}
@Test
public void testLifeCycleStop() throws Exception
{
QueuedThreadPool tp= new QueuedThreadPool();
tp.setName("TestPool");
tp.setMinThreads(1);
tp.setMaxThreads(2);
tp.setIdleTimeout(900);
tp.setStopTimeout(500);
tp.setThreadsPriority(Thread.NORM_PRIORITY-1);
tp.start();
// min threads started
waitForThreads(tp,1);
waitForIdle(tp,1);
// Run job0 and job1
RunningJob job0=new RunningJob();
RunningJob job1=new RunningJob();
tp.execute(job0);
tp.execute(job1);
// Add a more jobs (which should not be run)
RunningJob job2=new RunningJob();
CloseableJob job3=new CloseableJob();
RunningJob job4=new RunningJob();
tp.execute(job2);
tp.execute(job3);
tp.execute(job4);
// Wait until the first 2 start running
waitForThreads(tp,2);
waitForIdle(tp,0);
// Queue should be empty after thread pool is stopped
tp.stop();
assertThat(tp.getQueue().size(), is(0));
// First 2 jobs closed by InterruptedException
assertThat(job0._stopped.await(200, TimeUnit.MILLISECONDS), is(true));
assertThat(job1._stopped.await(200, TimeUnit.MILLISECONDS), is(true));
// Verify RunningJobs in the queue have not been run
assertThat(job2._run.await(200, TimeUnit.MILLISECONDS), is(false));
assertThat(job4._run.await(200, TimeUnit.MILLISECONDS), is(false));
// Verify ClosableJobs have not been run but have been closed
assertThat(job4._run.await(200, TimeUnit.MILLISECONDS), is(false));
assertThat(job3._closed.await(200, TimeUnit.MILLISECONDS), is(true));
}
@Test
public void testShrink() throws Exception
{

View File

@ -0,0 +1,92 @@
//
// ========================================================================
// Copyright (c) 1995-2019 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 java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Test;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class SerializedExecutorTest
{
@Test
public void test() throws Exception
{
int threads = 64;
int loops = 1000;
int depth = 100;
AtomicInteger ran = new AtomicInteger();
AtomicBoolean running = new AtomicBoolean();
SerializedExecutor executor = new SerializedExecutor();
CountDownLatch start = new CountDownLatch(1);
CountDownLatch stop = new CountDownLatch(threads);
Random random = new Random();
for (int t = threads; t-- > 0; )
{
new Thread(() ->
{
try
{
start.await();
for (int l = loops; l-- > 0; )
{
final AtomicInteger d = new AtomicInteger(depth);
executor.execute(new Runnable()
{
@Override
public void run()
{
ran.incrementAndGet();
if (!running.compareAndSet(false, true))
throw new IllegalStateException();
if (d.decrementAndGet() > 0)
executor.execute(this);
if (!running.compareAndSet(true, false))
throw new IllegalStateException();
}
});
Thread.sleep(random.nextInt(5));
}
}
catch (Throwable th)
{
th.printStackTrace();
}
finally
{
stop.countDown();
}
}).start();
}
start.countDown();
assertTrue(stop.await(30, TimeUnit.SECONDS));
assertThat(ran.get(), Matchers.is(threads * loops * depth));
}
}

View File

@ -130,6 +130,12 @@ public class UpgradeHttpServletRequest implements HttpServletRequest
attributes.put(name, httpRequest.getAttribute(name));
}
Enumeration<Locale> localeElements = httpRequest.getLocales();
while (localeElements.hasMoreElements())
{
locales.add(localeElements.nextElement());
}
localAddress = InetSocketAddress.createUnresolved(httpRequest.getLocalAddr(), httpRequest.getLocalPort());
localName = httpRequest.getLocalName();
remoteAddress = InetSocketAddress.createUnresolved(httpRequest.getRemoteAddr(), httpRequest.getRemotePort());

19
pom.xml
View File

@ -47,7 +47,7 @@
<jetty.surefire.argLine>-Dfile.encoding=UTF-8 -Duser.language=en -Duser.region=US -showversion -Xmx1g -Xms1g -Xlog:gc:stderr:time,level,tags</jetty.surefire.argLine>
<!-- some maven plugins versions -->
<maven.surefire.version>3.0.0-M2</maven.surefire.version>
<maven.surefire.version>3.0.0-M3</maven.surefire.version>
<maven.compiler.plugin.version>3.8.0</maven.compiler.plugin.version>
<maven.dependency.plugin.version>3.1.1</maven.dependency.plugin.version>
<maven.resources.plugin.version>3.1.0</maven.resources.plugin.version>
@ -1203,9 +1203,20 @@
</profile>
<profile>
<id>ci</id>
<modules>
<module>aggregates/jetty-all</module>
</modules>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven.surefire.version}</version>
<configuration>
<excludedGroups>ipv6</excludedGroups>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
<profile>
<id>update-version</id>

View File

@ -141,7 +141,7 @@ if proceedyn "Are you sure you want to release using above? (y/N)" n; then
# This is equivalent to 'mvn release:prepare'
if proceedyn "Update project.versions for $VER_RELEASE? (Y/n)" y; then
mvn org.codehaus.mojo:versions-maven-plugin:2.5:set \
mvn org.codehaus.mojo:versions-maven-plugin:2.7:set \
-DoldVersion="$VER_CURRENT" \
-DnewVersion="$VER_RELEASE" \
-DprocessAllModules=true

View File

@ -80,6 +80,12 @@
<artifactId>jetty-test-helper</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-unixsocket-client</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>

View File

@ -19,6 +19,8 @@
package org.eclipse.jetty.tests.distribution;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.concurrent.TimeUnit;
@ -27,6 +29,8 @@ import org.eclipse.jetty.client.api.ContentResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http2.client.HTTP2Client;
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
import org.eclipse.jetty.unixsocket.client.HttpClientTransportOverUnixSockets;
import org.eclipse.jetty.util.StringUtil;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.DisabledOnJre;
import org.junit.jupiter.api.condition.JRE;
@ -204,4 +208,53 @@ public class DistributionTests extends AbstractDistributionTest
assertThat(response.getContentAsString(), not(containsString("<%")));
}
}
@Test
public void unixSocket() throws Exception
{
Path sockFile;
String unixSocketTmp = System.getProperty("unix.socket.tmp");
if( StringUtil.isNotBlank(unixSocketTmp))
{
sockFile = Files.createTempFile(Paths.get(unixSocketTmp), "unix", ".sock");
} else {
sockFile = Files.createTempFile("unix", ".sock");
}
assertTrue(Files.deleteIfExists(sockFile), "temp sock file cannot be deleted");
String jettyVersion = System.getProperty("jettyVersion");
DistributionTester distribution = DistributionTester.Builder.newInstance()
.jettyVersion(jettyVersion)
.mavenLocalRepository(System.getProperty("mavenRepoPath"))
.build();
String[] args1 = {
"--create-startd",
"--add-to-start=unixsocket-http,deploy,jsp",
"--approve-all-licenses"
};
try (DistributionTester.Run run1 = distribution.start(args1))
{
assertTrue(run1.awaitFor(5, TimeUnit.SECONDS));
assertEquals(0, run1.getExitValue());
File war = distribution.resolveArtifact("org.eclipse.jetty.tests:test-simple-webapp:war:" + jettyVersion);
distribution.installWarFile(war, "test");
try (DistributionTester.Run run2 = distribution.start("jetty.unixsocket="+sockFile.toString()))
{
assertTrue(run2.awaitConsoleLogsFor("Started @", 10, TimeUnit.SECONDS));
startHttpClient(() -> new HttpClient(new HttpClientTransportOverUnixSockets(sockFile.toString())));
ContentResponse response = client.GET("http://localhost/test/index.jsp");
assertEquals(HttpStatus.OK_200, response.getStatus());
assertThat(response.getContentAsString(), containsString("Hello"));
assertThat(response.getContentAsString(), not(containsString("<%")));
}
} finally {
Files.deleteIfExists(sockFile);
}
}
}

View File

@ -76,7 +76,6 @@ import org.eclipse.jetty.util.FuturePromise;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.hamcrest.Matchers;
import org.junit.jupiter.api.Assumptions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ArgumentsSource;
@ -394,12 +393,8 @@ public class AsyncIOServletTest extends AbstractTest<AsyncIOServletTest.AsyncTra
@ParameterizedTest
@ArgumentsSource(TransportProvider.class)
@Tag("Unstable")
@Disabled // TODO fix this test! #2243
public void testAsyncWriteClosed(Transport transport) throws Exception
{
// TODO work out why this test fails for UNIX_SOCKET
Assumptions.assumeFalse(transport==Transport.UNIX_SOCKET);
init(transport);
String text = "Now is the winter of our discontent. How Now Brown Cow. The quick brown fox jumped over the lazy dog.\n";
@ -465,8 +460,6 @@ public class AsyncIOServletTest extends AbstractTest<AsyncIOServletTest.AsyncTra
@ArgumentsSource(TransportProvider.class)
public void testAsyncWriteLessThanContentLengthFlushed(Transport transport) throws Exception
{
// TODO work out why this test fails for UNIX_SOCKET
Assumptions.assumeFalse(transport==Transport.UNIX_SOCKET);
init(transport);
CountDownLatch complete = new CountDownLatch(1);

View File

@ -180,7 +180,7 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
for (String failure : failures)
logger.info("FAILED: {}", failure);
assertTrue(failures.isEmpty(),failures.toString());
assertTrue(failures.isEmpty(), failures.toString());
}
private void test(final CountDownLatch latch, final List<String> failures)
@ -366,19 +366,18 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
}
@Override
public Connector newServerConnector( Server server)
public Connector newServerConnector(Server server)
{
int selectors = Math.min(1, ProcessorUtils.availableProcessors() / 2);
ByteBufferPool byteBufferPool = new ArrayByteBufferPool();
byteBufferPool = new LeakTrackingByteBufferPool(byteBufferPool);
if (transport == Transport.UNIX_SOCKET)
{
UnixSocketConnector unixSocketConnector = new UnixSocketConnector(server, provideServerConnectionFactory(transport));
UnixSocketConnector unixSocketConnector = new UnixSocketConnector(server, null, null, byteBufferPool, selectors, provideServerConnectionFactory(transport));
unixSocketConnector.setUnixSocket(sockFile.toString());
return unixSocketConnector;
}
int cores = ProcessorUtils.availableProcessors();
ByteBufferPool byteBufferPool = new ArrayByteBufferPool();
byteBufferPool = new LeakTrackingByteBufferPool(byteBufferPool);
return new ServerConnector(server, null, null, byteBufferPool,
1, Math.min(1, cores / 2), provideServerConnectionFactory(transport));
return new ServerConnector(server, null, null, byteBufferPool, 1, selectors, provideServerConnectionFactory(transport));
}
@Override

View File

@ -97,7 +97,7 @@ public class RoundRobinConnectionPoolTest extends AbstractTest<TransportScenario
int expected = remotePorts.get(base);
int candidate = remotePorts.get(i);
assertThat(scenario.client.dump() + System.lineSeparator() + remotePorts.toString(), expected, Matchers.equalTo(candidate));
if (i > 0)
if (transport != Transport.UNIX_SOCKET && i > 0)
assertThat(remotePorts.get(i - 1), Matchers.not(Matchers.equalTo(candidate)));
}
}
@ -187,7 +187,7 @@ public class RoundRobinConnectionPoolTest extends AbstractTest<TransportScenario
int expected = remotePorts.get(base);
int candidate = remotePorts.get(i);
assertThat(scenario.client.dump() + System.lineSeparator() + remotePorts.toString(), expected, Matchers.equalTo(candidate));
if (i > 0)
if (transport != Transport.UNIX_SOCKET && i > 0)
assertThat(remotePorts.get(i - 1), Matchers.not(Matchers.equalTo(candidate)));
}
}

View File

@ -20,10 +20,10 @@ package org.eclipse.jetty.http.client;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.Set;
import java.util.stream.Stream;
import org.eclipse.jetty.util.StringUtil;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.ExtensionContext;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.ArgumentsProvider;
@ -35,21 +35,16 @@ public class TransportProvider implements ArgumentsProvider
String transports = System.getProperty(Transport.class.getName());
if (!StringUtil.isBlank(transports))
{
return Arrays.stream(transports.split("\\s*,\\s*"))
.map(Transport::valueOf);
}
return Arrays.stream(transports.split("\\s*,\\s*")).map(Transport::valueOf);
// TODO #2014 too many test failures, don't test unix socket client for now.
// if (OS.IS_UNIX)
// return Transport.values();
if (OS.LINUX.isCurrentOs())
return Arrays.stream(Transport.values());
return EnumSet.complementOf(EnumSet.of(Transport.UNIX_SOCKET))
.stream();
return EnumSet.complementOf(EnumSet.of(Transport.UNIX_SOCKET)).stream();
}
@Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception
public Stream<? extends Arguments> provideArguments(ExtensionContext context)
{
return getActiveTransports().map(Arguments::of);
}

View File

@ -22,6 +22,7 @@ import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@ -60,11 +61,14 @@ import org.eclipse.jetty.unixsocket.client.HttpClientTransportOverUnixSockets;
import org.eclipse.jetty.unixsocket.server.UnixSocketConnector;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.SocketAddressResolver;
import org.eclipse.jetty.util.StringUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import static org.junit.jupiter.api.Assumptions.assumeTrue;
public class TransportScenario
{
private static final Logger LOG = Log.getLogger(TransportScenario.class);
@ -78,25 +82,34 @@ public class TransportScenario
protected String servletPath = "/servlet";
protected HttpClient client;
protected Path sockFile;
protected final BlockingQueue<String> requestLog= new BlockingArrayQueue<>();
protected final BlockingQueue<String> requestLog = new BlockingArrayQueue<>();
public TransportScenario(final Transport transport) throws IOException
{
this.transport = transport;
if(sockFile == null || !Files.exists( sockFile ))
Path unixSocketTmp;
String tmpProp = System.getProperty("unix.socket.tmp");
if (StringUtil.isBlank(tmpProp))
unixSocketTmp = MavenTestingUtils.getTargetPath();
else
unixSocketTmp = Paths.get(tmpProp);
sockFile = Files.createTempFile(unixSocketTmp, "unix", ".sock");
if (sockFile.toAbsolutePath().toString().length() > UnixSocketConnector.MAX_UNIX_SOCKET_PATH_LENGTH)
{
Path target = MavenTestingUtils.getTargetPath();
sockFile = Files.createTempFile(target,"unix", ".sock" );
Files.delete( sockFile );
Files.delete(sockFile);
Path tmp = Paths.get("/tmp");
assumeTrue(Files.exists(tmp) && Files.isDirectory(tmp));
sockFile = Files.createTempFile(tmp, "unix", ".sock");
}
Files.delete(sockFile);
}
public Optional<String> getNetworkConnectorLocalPort()
{
if (connector instanceof ServerConnector)
{
ServerConnector serverConnector = (ServerConnector) connector;
ServerConnector serverConnector = (ServerConnector)connector;
return Optional.of(Integer.toString(serverConnector.getLocalPort()));
}
@ -107,7 +120,7 @@ public class TransportScenario
{
if (connector instanceof ServerConnector)
{
ServerConnector serverConnector = (ServerConnector) connector;
ServerConnector serverConnector = (ServerConnector)connector;
return Optional.of(serverConnector.getLocalPort());
}
@ -132,12 +145,12 @@ public class TransportScenario
return new HttpClient(transport);
}
public Connector newServerConnector(Server server) throws Exception
public Connector newServerConnector(Server server)
{
if (transport == Transport.UNIX_SOCKET)
{
UnixSocketConnector unixSocketConnector = new UnixSocketConnector(server, provideServerConnectionFactory( transport ));
unixSocketConnector.setUnixSocket( sockFile.toString() );
UnixSocketConnector unixSocketConnector = new UnixSocketConnector(server, provideServerConnectionFactory(transport));
unixSocketConnector.setUnixSocket(sockFile.toString());
return unixSocketConnector;
}
return new ServerConnector(server, provideServerConnectionFactory(transport));
@ -149,10 +162,7 @@ public class TransportScenario
ret.append(getScheme());
ret.append("://localhost");
Optional<String> localPort = getNetworkConnectorLocalPort();
if (localPort.isPresent())
{
ret.append(':').append(localPort.get());
}
localPort.ifPresent(s -> ret.append(':').append(s));
return ret.toString();
}
@ -235,13 +245,13 @@ public class TransportScenario
throw new IllegalArgumentException();
}
}
return result.toArray(new ConnectionFactory[result.size()]);
return result.toArray(new ConnectionFactory[0]);
}
public void setConnectionIdleTimeout(long idleTimeout)
{
if (connector instanceof AbstractConnector)
AbstractConnector.class.cast(connector).setIdleTimeout(idleTimeout);
((AbstractConnector)connector).setIdleTimeout(idleTimeout);
}
public void setServerIdleTimeout(long idleTimeout)
@ -252,9 +262,10 @@ public class TransportScenario
else
setConnectionIdleTimeout(idleTimeout);
}
public void start(Handler handler) throws Exception
{
start(handler,null);
start(handler, null);
}
public void start(Handler handler, Consumer<HttpClient> config) throws Exception
@ -284,7 +295,7 @@ public class TransportScenario
client.setExecutor(clientThreads);
client.setSocketAddressResolver(new SocketAddressResolver.Sync());
if (config!=null)
if (config != null)
config.accept(client);
client.start();
@ -317,7 +328,7 @@ public class TransportScenario
server.setRequestLog((request, response) ->
{
int status = response.getCommittedMetaData().getStatus();
requestLog.offer(String.format("%s %s %s %03d",request.getMethod(),request.getRequestURI(),request.getProtocol(),status));
requestLog.offer(String.format("%s %s %s %03d", request.getMethod(), request.getRequestURI(), request.getProtocol(), status));
});
server.setHandler(handler);
@ -326,7 +337,7 @@ public class TransportScenario
{
server.start();
}
catch ( Exception e )
catch (Exception e)
{
e.printStackTrace();
}
@ -375,25 +386,25 @@ public class TransportScenario
{
stopClient();
}
catch (Exception ignore)
catch (Exception x)
{
LOG.ignore(ignore);
LOG.ignore(x);
}
try
{
stopServer();
}
catch (Exception ignore)
catch (Exception x)
{
LOG.ignore(ignore);
LOG.ignore(x);
}
if (sockFile!=null)
if (sockFile != null)
{
try
{
Files.deleteIfExists( sockFile );
Files.deleteIfExists(sockFile);
}
catch (IOException e)
{
@ -401,6 +412,4 @@ public class TransportScenario
}
}
}
}

View File

@ -208,10 +208,10 @@ public class AnnotationTest extends HttpServlet
boolean fragInitParamResult = "123".equals(config.getInitParameter("extra1")) && "345".equals(config.getInitParameter("extra2"));
out.println("<p><b>Result: "+(fragInitParamResult? "<span class=\"pass\">PASS": "<span class=\"fail\">FAIL")+"</span></p>");
__HandlesTypes = Arrays.asList( "javax.servlet.GenericServlet",
"javax.servlet.http.HttpServlet",
"com.acme.test.AsyncListenerServlet",
"com.acme.test.ClassLoaderServlet",
"com.acme.test.AnnotationTest",
"com.acme.test.RoleAnnotationTest",
"com.acme.test.MultiPartTest",