Merged branch 'jetty-9.4.x' into 'jetty-10.0.x'.
This commit is contained in:
commit
40c2e76786
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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"+
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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"));
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 <=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 <=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 <=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 <=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 <=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 <=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 <=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 <=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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -310,6 +310,7 @@ public interface Callback extends Invocable
|
|||
return callback.getInvocationType();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>A CompletableFuture that is also a Callback.</p>
|
||||
*/
|
||||
|
|
|
@ -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 = " ⇩";
|
||||
final String ARROW_UP = " ⇧";
|
||||
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(" ");
|
||||
buf.append("</A></TD><TD ALIGN=right>");
|
||||
buf.append(item.length());
|
||||
buf.append(" bytes </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 </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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
{}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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
19
pom.xml
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue