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,
|
mavenOpts: mavenOpts,
|
||||||
mavenLocalRepo: localRepo) {
|
mavenLocalRepo: localRepo) {
|
||||||
// Some common Maven command line + provided command line
|
// 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;
|
||||||
import java.nio.channels.FileChannel.MapMode;
|
import java.nio.channels.FileChannel.MapMode;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
|
|
||||||
import javax.servlet.AsyncContext;
|
import javax.servlet.AsyncContext;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -116,7 +115,8 @@ public class FastFileServer
|
||||||
}
|
}
|
||||||
String listing = Resource.newResource(file).getListHTML(
|
String listing = Resource.newResource(file).getListHTML(
|
||||||
request.getRequestURI(),
|
request.getRequestURI(),
|
||||||
request.getPathInfo().lastIndexOf("/") > 0);
|
request.getPathInfo().lastIndexOf("/") > 0,
|
||||||
|
request.getQueryString());
|
||||||
response.setContentType("text/html; charset=utf-8");
|
response.setContentType("text/html; charset=utf-8");
|
||||||
response.getWriter().println(listing);
|
response.getWriter().println(listing);
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -91,6 +91,7 @@ import org.eclipse.jetty.util.SocketAddressResolver;
|
||||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.Assumptions;
|
import org.junit.jupiter.api.Assumptions;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
||||||
import org.junit.jupiter.api.extension.ExtendWith;
|
import org.junit.jupiter.api.extension.ExtendWith;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
|
@ -740,6 +741,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(ScenarioProvider.class)
|
@ArgumentsSource(ScenarioProvider.class)
|
||||||
|
@Tag("ipv6")
|
||||||
public void testSendToIPv6Address(Scenario scenario) throws Exception
|
public void testSendToIPv6Address(Scenario scenario) throws Exception
|
||||||
{
|
{
|
||||||
start(scenario, new EmptyServerHandler());
|
start(scenario, new EmptyServerHandler());
|
||||||
|
@ -1609,6 +1611,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(ScenarioProvider.class)
|
@ArgumentsSource(ScenarioProvider.class)
|
||||||
|
@Tag("ipv6")
|
||||||
public void test_IPv6_Host(Scenario scenario) throws Exception
|
public void test_IPv6_Host(Scenario scenario) throws Exception
|
||||||
{
|
{
|
||||||
start(scenario, new AbstractHandler()
|
start(scenario, new AbstractHandler()
|
||||||
|
|
|
@ -18,13 +18,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client;
|
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.BufferedReader;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
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.server.handler.AbstractHandler;
|
||||||
import org.eclipse.jetty.util.Fields;
|
import org.eclipse.jetty.util.Fields;
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
import org.eclipse.jetty.util.URIUtil;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
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
|
public class HttpClientURITest extends AbstractHttpClientServerTest
|
||||||
{
|
{
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(ScenarioProvider.class)
|
@ArgumentsSource(ScenarioProvider.class)
|
||||||
|
@Tag("ipv6")
|
||||||
public void testIPv6Host(Scenario scenario) throws Exception
|
public void testIPv6Host(Scenario scenario) throws Exception
|
||||||
{
|
{
|
||||||
start(scenario, new EmptyServerHandler());
|
start(scenario, new EmptyServerHandler());
|
||||||
|
|
|
@ -18,12 +18,12 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.client;
|
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.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
public class ProxyConfigurationTest
|
public class ProxyConfigurationTest
|
||||||
{
|
{
|
||||||
@Test
|
@Test
|
||||||
|
@ -68,6 +68,7 @@ public class ProxyConfigurationTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Tag("ipv6")
|
||||||
public void testProxyMatchesWithIncludesAndExcludesIPv6() throws Exception
|
public void testProxyMatchesWithIncludesAndExcludesIPv6() throws Exception
|
||||||
{
|
{
|
||||||
HttpProxy proxy = new HttpProxy("host", 0);
|
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.AppLifeCycle;
|
||||||
import org.eclipse.jetty.deploy.graph.Node;
|
import org.eclipse.jetty.deploy.graph.Node;
|
||||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||||
|
import org.eclipse.jetty.util.Callback;
|
||||||
|
|
||||||
public class StandardDeployer implements AppLifeCycle.Binding
|
public class StandardDeployer implements AppLifeCycle.Binding
|
||||||
{
|
{
|
||||||
|
@ -37,9 +38,10 @@ public class StandardDeployer implements AppLifeCycle.Binding
|
||||||
{
|
{
|
||||||
ContextHandler handler = app.getContextHandler();
|
ContextHandler handler = app.getContextHandler();
|
||||||
if (handler == null)
|
if (handler == null)
|
||||||
{
|
|
||||||
throw new NullPointerException("No Handler created for App: " + app);
|
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.App;
|
||||||
import org.eclipse.jetty.deploy.AppLifeCycle;
|
import org.eclipse.jetty.deploy.AppLifeCycle;
|
||||||
import org.eclipse.jetty.deploy.graph.Node;
|
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.ContextHandler;
|
||||||
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
import org.eclipse.jetty.server.handler.ContextHandlerCollection;
|
||||||
import org.eclipse.jetty.server.handler.HandlerCollection;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
|
||||||
|
|
||||||
public class StandardUndeployer implements AppLifeCycle.Binding
|
public class StandardUndeployer implements AppLifeCycle.Binding
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(StandardUndeployer.class);
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getBindingTargets()
|
public String[] getBindingTargets()
|
||||||
{
|
{
|
||||||
|
@ -42,33 +37,11 @@ public class StandardUndeployer implements AppLifeCycle.Binding
|
||||||
@Override
|
@Override
|
||||||
public void processBinding(Node node, App app) throws Exception
|
public void processBinding(Node node, App app) throws Exception
|
||||||
{
|
{
|
||||||
ContextHandler handler = app.getContextHandler();
|
ContextHandlerCollection contexts = app.getDeploymentManager().getContexts();
|
||||||
ContextHandlerCollection chcoll = app.getDeploymentManager().getContexts();
|
ContextHandler context = app.getContextHandler();
|
||||||
|
Callback.Completable blocker = new Callback.Completable();
|
||||||
recursiveRemoveContext(chcoll,handler);
|
contexts.undeployHandler(context, blocker);
|
||||||
}
|
blocker.get();
|
||||||
|
context.destroy();
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||||
import org.eclipse.jetty.toolchain.test.IO;
|
import org.eclipse.jetty.toolchain.test.IO;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
import org.junit.jupiter.api.condition.DisabledIfSystemProperty;
|
||||||
|
|
||||||
|
@ -473,10 +474,10 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
|
|
||||||
ExecutionException x = assertThrows(ExecutionException.class, () ->
|
ExecutionException x = assertThrows(ExecutionException.class, () ->
|
||||||
client.newRequest("localhost", connector.getLocalPort())
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.idleTimeout(4 * idleTimeout, TimeUnit.MILLISECONDS)
|
.idleTimeout(4 * idleTimeout, TimeUnit.MILLISECONDS)
|
||||||
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
|
.timeout(3 * idleTimeout, TimeUnit.MILLISECONDS)
|
||||||
.send());
|
.send());
|
||||||
assertThat(x.getCause(), instanceOf(EOFException.class));
|
assertThat(x.getCause(), instanceOf(EOFException.class));
|
||||||
|
|
||||||
connector.setIdleTimeout(5 * idleTimeout);
|
connector.setIdleTimeout(5 * idleTimeout);
|
||||||
|
@ -493,6 +494,7 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Tag("ipv6")
|
||||||
public void testSendToIPv6Address() throws Exception
|
public void testSendToIPv6Address() throws Exception
|
||||||
{
|
{
|
||||||
start(new EmptyServerHandler());
|
start(new EmptyServerHandler());
|
||||||
|
@ -595,9 +597,9 @@ public class HttpClientTest extends AbstractHttpClientServerTest
|
||||||
{
|
{
|
||||||
assertThrows(ExecutionException.class, () ->
|
assertThrows(ExecutionException.class, () ->
|
||||||
client.newRequest("localhost", connector.getLocalPort())
|
client.newRequest("localhost", connector.getLocalPort())
|
||||||
.scheme(scheme)
|
.scheme(scheme)
|
||||||
.timeout(60, TimeUnit.SECONDS)
|
.timeout(60, TimeUnit.SECONDS)
|
||||||
.send());
|
.send());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,7 @@ session-store
|
||||||
sessions
|
sessions
|
||||||
|
|
||||||
[files]
|
[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]
|
[xml]
|
||||||
etc/sessions/hazelcast/default.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.hazelcastInstanceName=JETTY_DISTRIBUTED_SESSION_INSTANCE
|
||||||
#jetty.session.hazelcast.configurationLocation=
|
#jetty.session.hazelcast.configurationLocation=
|
||||||
jetty.session.gracePeriod.seconds=3600
|
jetty.session.gracePeriod.seconds=3600
|
||||||
jetty.session.savePeriod.seconds=0
|
jetty.session.savePeriod.seconds=0
|
||||||
|
|
|
@ -13,8 +13,8 @@ session-store
|
||||||
sessions
|
sessions
|
||||||
|
|
||||||
[files]
|
[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
|
||||||
maven://com.hazelcast/hazelcast-client/3.8.2|lib/hazelcast/hazelcast-client-3.8.2.jar
|
maven://com.hazelcast/hazelcast-client/3.9.3|lib/hazelcast/hazelcast-client-3.9.3.jar
|
||||||
|
|
||||||
[xml]
|
[xml]
|
||||||
etc/sessions/hazelcast/remote.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.onlyClient=true
|
||||||
#jetty.session.hazelcast.configurationLocation=
|
#jetty.session.hazelcast.configurationLocation=
|
||||||
jetty.session.gracePeriod.seconds=3600
|
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.eclipse.jetty.util.log.StacklessLogging;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.eclipse.jetty.http.HttpCompliance.Violation.CASE_INSENSITIVE_METHOD;
|
import static org.eclipse.jetty.http.HttpCompliance.Violation.CASE_INSENSITIVE_METHOD;
|
||||||
|
@ -1975,6 +1976,7 @@ public class HttpParserTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Tag("ipv6")
|
||||||
public void testIPv6Host() throws Exception
|
public void testIPv6Host() throws Exception
|
||||||
{
|
{
|
||||||
ByteBuffer buffer = BufferUtil.toBuffer(
|
ByteBuffer buffer = BufferUtil.toBuffer(
|
||||||
|
@ -2056,6 +2058,7 @@ public class HttpParserTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Tag("ipv6")
|
||||||
public void testIPv6HostPort() throws Exception
|
public void testIPv6HostPort() throws Exception
|
||||||
{
|
{
|
||||||
ByteBuffer buffer = BufferUtil.toBuffer(
|
ByteBuffer buffer = BufferUtil.toBuffer(
|
||||||
|
|
|
@ -295,8 +295,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
||||||
}
|
}
|
||||||
catch (Throwable x)
|
catch (Throwable x)
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
LOG.ignore(x);
|
||||||
LOG.debug(x);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -309,8 +308,7 @@ public class ManagedSelector extends ContainerLifeCycle implements Dumpable
|
||||||
}
|
}
|
||||||
catch (Throwable x)
|
catch (Throwable x)
|
||||||
{
|
{
|
||||||
if (LOG.isDebugEnabled())
|
LOG.ignore(x);
|
||||||
LOG.debug(x);
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -389,9 +389,9 @@ abstract public class WriteFlusher
|
||||||
boolean progress = true;
|
boolean progress = true;
|
||||||
while (progress && buffers != null)
|
while (progress && buffers != null)
|
||||||
{
|
{
|
||||||
long before = remaining(buffers);
|
long before = BufferUtil.remaining(buffers);
|
||||||
boolean flushed = _endPoint.flush(buffers);
|
boolean flushed = _endPoint.flush(buffers);
|
||||||
long after = remaining(buffers);
|
long after = BufferUtil.remaining(buffers);
|
||||||
long written = before - after;
|
long written = before - after;
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
|
@ -441,16 +441,6 @@ abstract public class WriteFlusher
|
||||||
return buffers == null ? EMPTY_BUFFERS : buffers;
|
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
|
* 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));
|
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;
|
Boolean result = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
@ -866,7 +872,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
||||||
if (filled < 0)
|
if (filled < 0)
|
||||||
throw new IOException("Broken pipe");
|
throw new IOException("Broken pipe");
|
||||||
}
|
}
|
||||||
return result = false;
|
return result = isEmpty;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new IllegalStateException("Unexpected HandshakeStatus " + status);
|
throw new IllegalStateException("Unexpected HandshakeStatus " + status);
|
||||||
|
@ -895,10 +901,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
||||||
_sslEngine.isOutboundDone());
|
_sslEngine.isOutboundDone());
|
||||||
|
|
||||||
// Was all the data consumed?
|
// Was all the data consumed?
|
||||||
boolean allConsumed = true;
|
isEmpty = BufferUtil.isEmpty(appOuts);
|
||||||
for (ByteBuffer b : appOuts)
|
|
||||||
if (BufferUtil.hasContent(b))
|
|
||||||
allConsumed = false;
|
|
||||||
|
|
||||||
// if we have net bytes, let's try to flush them
|
// if we have net bytes, let's try to flush them
|
||||||
boolean flushed = true;
|
boolean flushed = true;
|
||||||
|
@ -906,7 +909,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
||||||
flushed = getEndPoint().flush(_encryptedOutput);
|
flushed = getEndPoint().flush(_encryptedOutput);
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
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
|
// Now deal with the results returned from the wrap
|
||||||
Status wrap = wrapResult.getStatus();
|
Status wrap = wrapResult.getStatus();
|
||||||
|
@ -919,7 +922,7 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
||||||
if (!flushed)
|
if (!flushed)
|
||||||
return result = false;
|
return result = false;
|
||||||
getEndPoint().shutdownOutput();
|
getEndPoint().shutdownOutput();
|
||||||
if (allConsumed)
|
if (isEmpty)
|
||||||
return result = true;
|
return result = true;
|
||||||
throw new IOException("Broken pipe");
|
throw new IOException("Broken pipe");
|
||||||
}
|
}
|
||||||
|
@ -936,15 +939,20 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
||||||
if (isRenegotiating() && !allowRenegotiate())
|
if (isRenegotiating() && !allowRenegotiate())
|
||||||
{
|
{
|
||||||
getEndPoint().shutdownOutput();
|
getEndPoint().shutdownOutput();
|
||||||
if (allConsumed && BufferUtil.isEmpty(_encryptedOutput))
|
if (isEmpty && BufferUtil.isEmpty(_encryptedOutput))
|
||||||
return result = true;
|
return result = true;
|
||||||
throw new IOException("Broken pipe");
|
throw new IOException("Broken pipe");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!flushed)
|
if (!flushed)
|
||||||
return result = false;
|
return result = false;
|
||||||
if (allConsumed)
|
|
||||||
return result = true;
|
if (isEmpty)
|
||||||
|
{
|
||||||
|
if (wrapResult.getHandshakeStatus() != HandshakeStatus.NEED_WRAP ||
|
||||||
|
wrapResult.bytesProduced() == 0)
|
||||||
|
return result = true;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -1073,14 +1081,15 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
||||||
@Override
|
@Override
|
||||||
public void doShutdownOutput()
|
public void doShutdownOutput()
|
||||||
{
|
{
|
||||||
|
final EndPoint endp = getEndPoint();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
boolean close;
|
boolean close;
|
||||||
boolean flush = false;
|
boolean flush = false;
|
||||||
synchronized (_decryptedEndPoint)
|
synchronized (_decryptedEndPoint)
|
||||||
{
|
{
|
||||||
boolean ishut = getEndPoint().isInputShutdown();
|
boolean ishut = endp.isInputShutdown();
|
||||||
boolean oshut = getEndPoint().isOutputShutdown();
|
boolean oshut = endp.isOutputShutdown();
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("shutdownOutput: {} oshut={}, ishut={} {}", SslConnection.this, oshut, ishut);
|
LOG.debug("shutdownOutput: {} oshut={}, ishut={} {}", SslConnection.this, oshut, ishut);
|
||||||
|
|
||||||
|
@ -1097,16 +1106,28 @@ public class SslConnection extends AbstractConnection implements Connection.Upgr
|
||||||
}
|
}
|
||||||
|
|
||||||
if (flush)
|
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)
|
if (close)
|
||||||
getEndPoint().close();
|
endp.close();
|
||||||
else
|
else
|
||||||
ensureFillInterested();
|
ensureFillInterested();
|
||||||
}
|
}
|
||||||
catch (Throwable x)
|
catch (Throwable x)
|
||||||
{
|
{
|
||||||
LOG.ignore(x);
|
LOG.ignore(x);
|
||||||
getEndPoint().close();
|
endp.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
import java.net.SocketTimeoutException;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.channels.SelectableChannel;
|
import java.nio.channels.SelectableChannel;
|
||||||
import java.nio.channels.SelectionKey;
|
import java.nio.channels.SelectionKey;
|
||||||
|
@ -31,6 +32,7 @@ import java.nio.channels.SocketChannel;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
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.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
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.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
@ -63,6 +67,8 @@ public class SslConnectionTest
|
||||||
private final SslContextFactory _sslCtxFactory = new SslContextFactory.Server();
|
private final SslContextFactory _sslCtxFactory = new SslContextFactory.Server();
|
||||||
protected volatile EndPoint _lastEndp;
|
protected volatile EndPoint _lastEndp;
|
||||||
private volatile boolean _testFill=true;
|
private volatile boolean _testFill=true;
|
||||||
|
private volatile boolean _onXWriteThenShutdown=false;
|
||||||
|
|
||||||
private volatile FutureCallback _writeCallback;
|
private volatile FutureCallback _writeCallback;
|
||||||
protected ServerSocketChannel _connector;
|
protected ServerSocketChannel _connector;
|
||||||
final AtomicInteger _dispatches = new AtomicInteger();
|
final AtomicInteger _dispatches = new AtomicInteger();
|
||||||
|
@ -104,6 +110,7 @@ public class SslConnectionTest
|
||||||
|
|
||||||
static final AtomicInteger __startBlocking = new AtomicInteger();
|
static final AtomicInteger __startBlocking = new AtomicInteger();
|
||||||
static final AtomicInteger __blockFor = new AtomicInteger();
|
static final AtomicInteger __blockFor = new AtomicInteger();
|
||||||
|
static final AtomicBoolean __onIncompleteFlush = new AtomicBoolean();
|
||||||
private static class TestEP extends SocketChannelEndPoint
|
private static class TestEP extends SocketChannelEndPoint
|
||||||
{
|
{
|
||||||
public TestEP(SelectableChannel channel, ManagedSelector selector, SelectionKey key, Scheduler scheduler)
|
public TestEP(SelectableChannel channel, ManagedSelector selector, SelectionKey key, Scheduler scheduler)
|
||||||
|
@ -114,13 +121,14 @@ public class SslConnectionTest
|
||||||
@Override
|
@Override
|
||||||
protected void onIncompleteFlush()
|
protected void onIncompleteFlush()
|
||||||
{
|
{
|
||||||
super.onIncompleteFlush();
|
__onIncompleteFlush.set(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean flush(ByteBuffer... buffers) throws IOException
|
public boolean flush(ByteBuffer... buffers) throws IOException
|
||||||
{
|
{
|
||||||
|
__onIncompleteFlush.set(false);
|
||||||
if (__startBlocking.get()==0 || __startBlocking.decrementAndGet()==0)
|
if (__startBlocking.get()==0 || __startBlocking.decrementAndGet()==0)
|
||||||
{
|
{
|
||||||
if (__blockFor.get()>0 && __blockFor.getAndDecrement()>0)
|
if (__blockFor.get()>0 && __blockFor.getAndDecrement()>0)
|
||||||
|
@ -224,20 +232,23 @@ public class SslConnectionTest
|
||||||
filled=endp.fill(_in);
|
filled=endp.fill(_in);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean shutdown = _onXWriteThenShutdown && BufferUtil.toString(_in).contains("X");
|
||||||
|
|
||||||
// Write everything
|
// Write everything
|
||||||
int l=_in.remaining();
|
int l=_in.remaining();
|
||||||
if (l>0)
|
if (l>0)
|
||||||
{
|
{
|
||||||
FutureCallback blockingWrite= new FutureCallback();
|
FutureCallback blockingWrite= new FutureCallback();
|
||||||
|
|
||||||
endp.write(blockingWrite,_in);
|
endp.write(blockingWrite,_in);
|
||||||
blockingWrite.get();
|
blockingWrite.get();
|
||||||
|
if (shutdown)
|
||||||
|
endp.shutdownOutput();
|
||||||
}
|
}
|
||||||
|
|
||||||
// are we done?
|
// are we done?
|
||||||
if (endp.isInputShutdown())
|
if (endp.isInputShutdown() || shutdown)
|
||||||
{
|
|
||||||
endp.shutdownOutput();
|
endp.shutdownOutput();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(InterruptedException|EofException e)
|
catch(InterruptedException|EofException e)
|
||||||
|
@ -423,7 +434,7 @@ public class SslConnectionTest
|
||||||
public void testBlockedWrite() throws Exception
|
public void testBlockedWrite() throws Exception
|
||||||
{
|
{
|
||||||
startSSL();
|
startSSL();
|
||||||
try (Socket client = newClient())
|
try (SSLSocket client = newClient())
|
||||||
{
|
{
|
||||||
client.setSoTimeout(5000);
|
client.setSoTimeout(5000);
|
||||||
try (SocketChannel server = _connector.accept())
|
try (SocketChannel server = _connector.accept())
|
||||||
|
@ -431,21 +442,78 @@ public class SslConnectionTest
|
||||||
server.configureBlocking(false);
|
server.configureBlocking(false);
|
||||||
_manager.accept(server);
|
_manager.accept(server);
|
||||||
|
|
||||||
__startBlocking.set(5);
|
|
||||||
__blockFor.set(3);
|
|
||||||
|
|
||||||
client.getOutputStream().write("Hello".getBytes(StandardCharsets.UTF_8));
|
client.getOutputStream().write("Hello".getBytes(StandardCharsets.UTF_8));
|
||||||
byte[] buffer = new byte[1024];
|
byte[] buffer = new byte[1024];
|
||||||
int len = client.getInputStream().read(buffer);
|
int len = client.getInputStream().read(buffer);
|
||||||
assertEquals(5, len);
|
|
||||||
assertEquals("Hello", new String(buffer, 0, len, StandardCharsets.UTF_8));
|
assertEquals("Hello", new String(buffer, 0, len, StandardCharsets.UTF_8));
|
||||||
|
|
||||||
|
__startBlocking.set(0);
|
||||||
|
__blockFor.set(2);
|
||||||
_dispatches.set(0);
|
_dispatches.set(0);
|
||||||
client.getOutputStream().write("World".getBytes(StandardCharsets.UTF_8));
|
client.getOutputStream().write("World".getBytes(StandardCharsets.UTF_8));
|
||||||
len = 5;
|
|
||||||
while (len > 0)
|
try
|
||||||
len -= client.getInputStream().read(buffer);
|
{
|
||||||
assertEquals(0, len);
|
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();
|
String line = in.readLine();
|
||||||
if (line == null)
|
if (line == null)
|
||||||
break;
|
break;
|
||||||
// System.err.println(line);
|
|
||||||
count.countDown();
|
count.countDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -488,7 +555,6 @@ public class SslConnectionTest
|
||||||
for (int i = 0; i < LINES; i++)
|
for (int i = 0; i < LINES; i++)
|
||||||
{
|
{
|
||||||
client.getOutputStream().write(("HelloWorld " + i + "\n").getBytes(StandardCharsets.UTF_8));
|
client.getOutputStream().write(("HelloWorld " + i + "\n").getBytes(StandardCharsets.UTF_8));
|
||||||
// System.err.println("wrote");
|
|
||||||
if (i % 1000 == 0)
|
if (i % 1000 == 0)
|
||||||
{
|
{
|
||||||
client.getOutputStream().flush();
|
client.getOutputStream().flush();
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.slf4j</groupId>
|
<groupId>org.slf4j</groupId>
|
||||||
<artifactId>slf4j-simple</artifactId>
|
<artifactId>slf4j-simple</artifactId>
|
||||||
<version>1.7.9</version>
|
<version>${slf4j.version}</version>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.eclipse.jetty.util.B64Code;
|
||||||
import org.eclipse.jetty.util.Callback;
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.Promise;
|
import org.eclipse.jetty.util.Promise;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
@ -88,6 +89,7 @@ public class ConnectHandlerTest extends AbstractConnectHandlerTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@Tag("ipv6")
|
||||||
public void testCONNECTwithIPv6() throws Exception
|
public void testCONNECTwithIPv6() throws Exception
|
||||||
{
|
{
|
||||||
String hostPort = "[::1]:" + serverConnector.getLocalPort();
|
String hostPort = "[::1]:" + serverConnector.getLocalPort();
|
||||||
|
|
|
@ -1618,9 +1618,10 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu
|
||||||
|
|
||||||
if (getServer() != null && (getServer().isStarting() || getServer().isStarted()))
|
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++)
|
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.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.ConcurrentMap;
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
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.server.Request;
|
||||||
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
import org.eclipse.jetty.util.ArrayTernaryTrie;
|
||||||
import org.eclipse.jetty.util.ArrayUtil;
|
import org.eclipse.jetty.util.ArrayUtil;
|
||||||
|
import org.eclipse.jetty.util.Callback;
|
||||||
import org.eclipse.jetty.util.Trie;
|
import org.eclipse.jetty.util.Trie;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
import org.eclipse.jetty.util.annotation.ManagedOperation;
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
import org.eclipse.jetty.util.thread.SerializedExecutor;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/** ContextHandlerCollection.
|
/**
|
||||||
*
|
|
||||||
* This {@link org.eclipse.jetty.server.handler.HandlerCollection} is creates a
|
* This {@link org.eclipse.jetty.server.handler.HandlerCollection} is creates a
|
||||||
* Map of contexts to it's contained handlers based
|
* 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.
|
* 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
|
public class ContextHandlerCollection extends HandlerCollection
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
|
private static final Logger LOG = Log.getLogger(ContextHandlerCollection.class);
|
||||||
|
private final SerializedExecutor _serializedExecutor = new SerializedExecutor();
|
||||||
|
|
||||||
private final ConcurrentMap<ContextHandler,Handler> _contextBranches = new ConcurrentHashMap<>();
|
@Deprecated
|
||||||
private volatile Trie<Map.Entry<String,Branch[]>> _pathBranches;
|
|
||||||
private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
|
private Class<? extends ContextHandler> _contextClass = ContextHandler.class;
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
|
@ -71,43 +71,57 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public ContextHandlerCollection(ContextHandler... contexts)
|
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()
|
public void mapContexts()
|
||||||
{
|
{
|
||||||
_contextBranches.clear();
|
_serializedExecutor.execute(()->
|
||||||
|
|
||||||
Handler[] handlers = getHandlers();
|
|
||||||
if (handlers==null)
|
|
||||||
{
|
{
|
||||||
_pathBranches=new ArrayTernaryTrie<>(false,16);
|
while(true)
|
||||||
return;
|
{
|
||||||
}
|
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
|
// 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)
|
for (Handler handler:handlers)
|
||||||
{
|
{
|
||||||
Branch branch=new Branch(handler);
|
Branch branch=new Branch(handler);
|
||||||
for (String contextPath : branch.getContextPaths())
|
for (String contextPath : branch.getContextPaths())
|
||||||
{
|
{
|
||||||
Branch[] branches=map.get(contextPath);
|
Branch[] branches=path2Branches.get(contextPath);
|
||||||
map.put(contextPath, ArrayUtil.addToArray(branches, branch, Branch.class));
|
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
|
// Sort the branches for each contextPath so those with virtual hosts are considered before those without
|
||||||
for (Map.Entry<String,Branch[]> entry: map.entrySet())
|
for (Map.Entry<String,Branch[]> entry: path2Branches.entrySet())
|
||||||
{
|
{
|
||||||
Branch[] branches=entry.getValue();
|
Branch[] branches=entry.getValue();
|
||||||
Branch[] sorted=new Branch[branches.length];
|
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
|
// Loop until we have a big enough trie to hold all the context paths
|
||||||
int capacity=512;
|
int capacity=512;
|
||||||
Trie<Map.Entry<String,Branch[]>> trie;
|
Mapping mapping;
|
||||||
loop: while(true)
|
loop: while(true)
|
||||||
{
|
{
|
||||||
trie=new ArrayTernaryTrie<>(false,capacity);
|
mapping = new Mapping(handlers, capacity);
|
||||||
for (Map.Entry<String,Branch[]> entry: map.entrySet())
|
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;
|
capacity+=512;
|
||||||
continue loop;
|
continue loop;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break loop;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
{
|
{
|
||||||
for (String ctx : trie.keySet())
|
for (String ctx : mapping._pathBranches.keySet())
|
||||||
LOG.debug("{}->{}",ctx,Arrays.asList(trie.get(ctx).getValue()));
|
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
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||||
{
|
{
|
||||||
Handler[] handlers = getHandlers();
|
Handlers handlers = _handlers.get();
|
||||||
if (handlers==null || handlers.length==0)
|
if (handlers==null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
Mapping mapping = (Mapping)handlers;
|
||||||
HttpChannelState async = baseRequest.getHttpChannelState();
|
HttpChannelState async = baseRequest.getHttpChannelState();
|
||||||
if (async.isAsync())
|
if (async.isAsync())
|
||||||
{
|
{
|
||||||
ContextHandler context=async.getContextHandler();
|
ContextHandler context=async.getContextHandler();
|
||||||
if (context!=null)
|
if (context!=null)
|
||||||
{
|
{
|
||||||
Handler branch = _contextBranches.get(context);
|
Handler branch = mapping._contextBranches.get(context);
|
||||||
|
|
||||||
if (branch==null)
|
if (branch==null)
|
||||||
context.handle(target,baseRequest,request, response);
|
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("/"))
|
if (target.startsWith("/"))
|
||||||
{
|
{
|
||||||
|
Trie<Map.Entry<String,Branch[]>> pathBranches = mapping._pathBranches;
|
||||||
|
if (pathBranches==null)
|
||||||
|
return;
|
||||||
|
|
||||||
int limit = target.length()-1;
|
int limit = target.length()-1;
|
||||||
|
|
||||||
while (limit>=0)
|
while (limit>=0)
|
||||||
{
|
{
|
||||||
// Get best match
|
// 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)
|
if (branches==null)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -227,10 +228,11 @@ public class ContextHandlerCollection extends HandlerCollection
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// This may not work in all circumstances... but then I think it should never be called
|
if (mapping.getHandlers()==null)
|
||||||
for (int i=0;i<handlers.length;i++)
|
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())
|
if ( baseRequest.isHandled())
|
||||||
return;
|
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 contextPath The context path to add
|
||||||
* @param resourceBase the base (root) Resource
|
* @param resourceBase the base (root) Resource
|
||||||
* @return the ContextHandler just added
|
* @return the ContextHandler just added
|
||||||
|
* @deprecated Unused convenience method no longer supported.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public ContextHandler addContext(String contextPath,String resourceBase)
|
public ContextHandler addContext(String contextPath,String resourceBase)
|
||||||
{
|
{
|
||||||
try
|
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
|
* @return The class to use to add new Contexts
|
||||||
|
* @deprecated Unused convenience mechanism not used.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public Class<?> getContextClass()
|
public Class<?> getContextClass()
|
||||||
{
|
{
|
||||||
return _contextClass;
|
return _contextClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @param contextClass The class to use to add new Contexts
|
* @param contextClass The class to use to add new Contexts
|
||||||
|
* @deprecated Unused convenience mechanism not used.
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public void setContextClass(Class<? extends ContextHandler> contextClass)
|
public void setContextClass(Class<? extends ContextHandler> contextClass)
|
||||||
{
|
{
|
||||||
if (contextClass ==null || !(ContextHandler.class.isAssignableFrom(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.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -47,7 +48,7 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
public class HandlerCollection extends AbstractHandlerContainer
|
public class HandlerCollection extends AbstractHandlerContainer
|
||||||
{
|
{
|
||||||
private final boolean _mutableWhenRunning;
|
private final boolean _mutableWhenRunning;
|
||||||
private volatile Handler[] _handlers;
|
protected final AtomicReference<Handlers> _handlers = new AtomicReference<>();
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
public HandlerCollection()
|
public HandlerCollection()
|
||||||
|
@ -71,72 +72,93 @@ public class HandlerCollection extends AbstractHandlerContainer
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @return Returns the handlers.
|
* @return the array of handlers.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
@ManagedAttribute(value="Wrapped handlers", readonly=true)
|
@ManagedAttribute(value="Wrapped handlers", readonly=true)
|
||||||
public Handler[] getHandlers()
|
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)
|
public void setHandlers(Handler[] handlers)
|
||||||
{
|
{
|
||||||
if (!_mutableWhenRunning && isStarted())
|
if (!_mutableWhenRunning && isStarted())
|
||||||
throw new IllegalStateException(STARTED);
|
throw new IllegalStateException(STARTED);
|
||||||
|
|
||||||
if (handlers!=null)
|
while(true)
|
||||||
{
|
{
|
||||||
// check for loops
|
if (updateHandlers(_handlers.get(), newHandlers(handlers)))
|
||||||
for (Handler handler:handlers)
|
break;
|
||||||
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());
|
|
||||||
}
|
}
|
||||||
Handler[] old=_handlers;;
|
|
||||||
_handlers = handlers;
|
|
||||||
updateBeans(old, handlers);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
protected Handlers newHandlers(Handler[] handlers)
|
||||||
* @see Handler#handle(String, Request, HttpServletRequest, HttpServletResponse)
|
{
|
||||||
*/
|
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
|
@Override
|
||||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
||||||
throws IOException, ServletException
|
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
|
try
|
||||||
{
|
{
|
||||||
_handlers[i].handle(target,baseRequest, request, response);
|
handler.handle(target, baseRequest, request, response);
|
||||||
}
|
}
|
||||||
catch(IOException e)
|
catch (IOException | RuntimeException e)
|
||||||
{
|
{
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
catch(RuntimeException e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
throw e;
|
if (mex == null)
|
||||||
}
|
mex = new MultiException();
|
||||||
catch(Exception e)
|
|
||||||
{
|
|
||||||
if (mex==null)
|
|
||||||
mex=new MultiException();
|
|
||||||
mex.add(e);
|
mex.add(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -147,37 +169,54 @@ public class HandlerCollection extends AbstractHandlerContainer
|
||||||
else
|
else
|
||||||
throw new ServletException(mex);
|
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.
|
* 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)
|
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.
|
* 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)
|
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)
|
public void removeHandler(Handler handler)
|
||||||
{
|
{
|
||||||
Handler[] handlers = getHandlers();
|
while(true)
|
||||||
|
{
|
||||||
if (handlers!=null && handlers.length>0 )
|
Handlers old = _handlers.get();
|
||||||
setHandlers(ArrayUtil.removeFromArray(handlers, handler));
|
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())
|
if (!isStopped())
|
||||||
throw new IllegalStateException("!STOPPED");
|
throw new IllegalStateException("!STOPPED");
|
||||||
Handler[] children=getChildHandlers();
|
Handler[] children = getChildHandlers();
|
||||||
setHandlers(null);
|
setHandlers(null);
|
||||||
for (Handler child: children)
|
for (Handler child: children)
|
||||||
child.destroy();
|
child.destroy();
|
||||||
super.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.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
|
@ -18,17 +18,17 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.server;
|
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.server.handler.ErrorHandler;
|
||||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.junit.jupiter.api.Tag;
|
||||||
import org.junit.jupiter.api.Test;
|
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
|
@Test
|
||||||
|
@Tag("ipv6")
|
||||||
public void testIPv6() throws Exception
|
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"+
|
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;
|
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 java.io.IOException;
|
||||||
|
|
||||||
import javax.servlet.AsyncContext;
|
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.Request;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
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
|
public class ContextHandlerCollectionTest
|
||||||
{
|
{
|
||||||
@Test
|
@Test
|
||||||
|
@ -214,7 +213,7 @@ public class ContextHandlerCollectionTest
|
||||||
IsHandledHandler handler = (IsHandledHandler)context.getHandler();
|
IsHandledHandler handler = (IsHandledHandler)context.getHandler();
|
||||||
|
|
||||||
context.setVirtualHosts(contextHosts);
|
context.setVirtualHosts(contextHosts);
|
||||||
// trigger this manually; it's supposed to be called when adding the handler
|
// trigger this manually
|
||||||
handlerCollection.mapContexts();
|
handlerCollection.mapContexts();
|
||||||
|
|
||||||
for(String host : requestHosts)
|
for(String host : requestHosts)
|
||||||
|
|
|
@ -167,7 +167,7 @@ public class ResourceHandlerTest
|
||||||
_local.getResponse("GET /resource/ HTTP/1.0\r\n\r\n"));
|
_local.getResponse("GET /resource/ HTTP/1.0\r\n\r\n"));
|
||||||
assertThat(response.getStatus(),equalTo(200));
|
assertThat(response.getStatus(),equalTo(200));
|
||||||
assertThat(response.getContent(),containsString("jetty-dir.css"));
|
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("big.txt"));
|
||||||
assertThat(response.getContent(),containsString("bigger.txt"));
|
assertThat(response.getContent(),containsString("bigger.txt"));
|
||||||
assertThat(response.getContent(),containsString("directory"));
|
assertThat(response.getContent(),containsString("directory"));
|
||||||
|
|
|
@ -23,7 +23,6 @@ import java.net.URL;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
import javax.servlet.ServletContext;
|
import javax.servlet.ServletContext;
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.UnavailableException;
|
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.Resource;
|
||||||
import org.eclipse.jetty.util.resource.ResourceFactory;
|
import org.eclipse.jetty.util.resource.ResourceFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
/**
|
|
||||||
* The default servlet.
|
* The default servlet.
|
||||||
* <p>
|
* <p>
|
||||||
* This servlet, normally mapped to /, provides the handling for static
|
* 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 If set to true, then static content will be served as
|
||||||
* gzip content encoded if a matching resource is
|
* gzip content encoded if a matching resource is
|
||||||
* found ending with ".gz" (default false)
|
* found ending with ".gz" (default false)
|
||||||
* (deprecated: use precompressed)
|
* (deprecated: use precompressed)
|
||||||
*
|
*
|
||||||
* precompressed If set to a comma separated list of encoding types (that may be
|
* precompressed If set to a comma separated list of encoding types (that may be
|
||||||
* listed in a requests Accept-Encoding header) to file
|
* listed in a requests Accept-Encoding header) to file
|
||||||
* extension mappings to look for and serve. For example:
|
* 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 class DefaultServlet extends HttpServlet implements ResourceFactory, WelcomeFactory
|
||||||
{
|
{
|
||||||
public static final String CONTEXT_INIT = "org.eclipse.jetty.servlet.Default.";
|
public static final String CONTEXT_INIT = "org.eclipse.jetty.servlet.Default.";
|
||||||
|
|
||||||
private static final Logger LOG = Log.getLogger(DefaultServlet.class);
|
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 final ResourceService _resourceService;
|
||||||
private ServletContext _servletContext;
|
private ServletContext _servletContext;
|
||||||
|
@ -165,7 +163,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc
|
||||||
{
|
{
|
||||||
this(new ResourceService());
|
this(new ResourceService());
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
/* ------------------------------------------------------------ */
|
||||||
@Override
|
@Override
|
||||||
public void init()
|
public void init()
|
||||||
|
@ -186,7 +184,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc
|
||||||
_resourceService.setPrecompressedFormats(parsePrecompressedFormats(getInitParameter("precompressed"), getInitBoolean("gzip", false)));
|
_resourceService.setPrecompressedFormats(parsePrecompressedFormats(getInitParameter("precompressed"), getInitBoolean("gzip", false)));
|
||||||
_resourceService.setPathInfoOnly(getInitBoolean("pathInfoOnly",_resourceService.isPathInfoOnly()));
|
_resourceService.setPathInfoOnly(getInitBoolean("pathInfoOnly",_resourceService.isPathInfoOnly()));
|
||||||
_resourceService.setEtags(getInitBoolean("etags",_resourceService.isEtags()));
|
_resourceService.setEtags(getInitBoolean("etags",_resourceService.isEtags()));
|
||||||
|
|
||||||
if ("exact".equals(getInitParameter("welcomeServlets")))
|
if ("exact".equals(getInitParameter("welcomeServlets")))
|
||||||
{
|
{
|
||||||
_welcomeExactServlets=true;
|
_welcomeExactServlets=true;
|
||||||
|
@ -242,8 +240,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory, Welc
|
||||||
String cc=getInitParameter("cacheControl");
|
String cc=getInitParameter("cacheControl");
|
||||||
if (cc!=null)
|
if (cc!=null)
|
||||||
_resourceService.setCacheControl(new PreEncodedHttpField(HttpHeader.CACHE_CONTROL,cc));
|
_resourceService.setCacheControl(new PreEncodedHttpField(HttpHeader.CACHE_CONTROL,cc));
|
||||||
|
|
||||||
|
|
||||||
String resourceCache = getInitParameter("resourceCache");
|
String resourceCache = getInitParameter("resourceCache");
|
||||||
int max_cache_size=getInitInt("maxCacheSize", -2);
|
int max_cache_size=getInitInt("maxCacheSize", -2);
|
||||||
int max_cached_file_size=getInitInt("maxCachedFileSize", -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.setContentFactory(contentFactory);
|
||||||
_resourceService.setWelcomeFactory(this);
|
_resourceService.setWelcomeFactory(this);
|
||||||
|
|
||||||
List<String> gzip_equivalent_file_extensions = new ArrayList<String>();
|
List<String> gzip_equivalent_file_extensions = new ArrayList<String>();
|
||||||
String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
|
String otherGzipExtensions = getInitParameter("otherGzipFileExtensions");
|
||||||
if (otherGzipExtensions != null)
|
if (otherGzipExtensions != null)
|
||||||
|
|
|
@ -157,10 +157,14 @@ public class DefaultServletTest
|
||||||
defholder.setInitParameter("gzip", "false");
|
defholder.setInitParameter("gzip", "false");
|
||||||
|
|
||||||
/* create some content in the docroot */
|
/* 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("two"));
|
||||||
FS.ensureDirExists(docRoot.resolve("three"));
|
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
|
* Intentionally bad request URI. Sending a non-encoded URI with typically
|
||||||
* encoded characters '<', '>', and '"'.
|
* encoded characters '<', '>', and '"'.
|
||||||
|
@ -172,6 +176,16 @@ public class DefaultServletTest
|
||||||
|
|
||||||
String body = response.getContent();
|
String body = response.getContent();
|
||||||
assertThat(body, not(containsString("<script>")));
|
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
|
@Test
|
||||||
|
|
|
@ -35,7 +35,7 @@ public class UnixSocketClient
|
||||||
{
|
{
|
||||||
java.io.File path = new java.io.File("/tmp/jetty.sock");
|
java.io.File path = new java.io.File("/tmp/jetty.sock");
|
||||||
java.io.File content = new java.io.File("/tmp/data.txt");
|
java.io.File content = new java.io.File("/tmp/data.txt");
|
||||||
|
|
||||||
String method = "GET";
|
String method = "GET";
|
||||||
int content_length = 0;
|
int content_length = 0;
|
||||||
String body = null;
|
String body = null;
|
||||||
|
@ -45,12 +45,12 @@ public class UnixSocketClient
|
||||||
body = IO.readToString(content);
|
body = IO.readToString(content);
|
||||||
content_length = body.length();
|
content_length = body.length();
|
||||||
}
|
}
|
||||||
String data = method+" / HTTP/1.1\r\n"
|
String data = method + " / HTTP/1.1\r\n"
|
||||||
+ "Host: unixsock\r\n"
|
+ "Host: unixsock\r\n"
|
||||||
+ "Content-Length: "+content_length+"\r\n"
|
+ "Content-Length: " + content_length + "\r\n"
|
||||||
+ "Connection: close\r\n"
|
+ "Connection: close\r\n"
|
||||||
+ "\r\n";
|
+ "\r\n";
|
||||||
if (body!=null)
|
if (body != null)
|
||||||
data += body;
|
data += body;
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
|
@ -59,18 +59,18 @@ public class UnixSocketClient
|
||||||
UnixSocketChannel channel = UnixSocketChannel.open(address);
|
UnixSocketChannel channel = UnixSocketChannel.open(address);
|
||||||
System.out.println("connected to " + channel.getRemoteSocketAddress());
|
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));
|
InputStreamReader r = new InputStreamReader(Channels.newInputStream(channel));
|
||||||
|
|
||||||
w.print(data);
|
w.print(data);
|
||||||
w.flush();
|
w.flush();
|
||||||
|
|
||||||
CharBuffer result = CharBuffer.allocate(4096);
|
CharBuffer result = CharBuffer.allocate(4096);
|
||||||
String total="";
|
String total = "";
|
||||||
int l = 0;
|
int l = 0;
|
||||||
while (l>=0)
|
while (l >= 0)
|
||||||
{
|
{
|
||||||
if (l>0)
|
if (l > 0)
|
||||||
{
|
{
|
||||||
result.flip();
|
result.flip();
|
||||||
total += result.toString();
|
total += result.toString();
|
||||||
|
|
|
@ -71,18 +71,16 @@ public class UnixSocketTest
|
||||||
server = null;
|
server = null;
|
||||||
httpClient = null;
|
httpClient = null;
|
||||||
String unixSocketTmp = System.getProperty("unix.socket.tmp");
|
String unixSocketTmp = System.getProperty("unix.socket.tmp");
|
||||||
Path unixSocket;
|
|
||||||
if (StringUtil.isNotBlank(unixSocketTmp))
|
if (StringUtil.isNotBlank(unixSocketTmp))
|
||||||
unixSocket = Files.createTempFile(Paths.get(unixSocketTmp), "unix", ".sock");
|
sockFile = Files.createTempFile(Paths.get(unixSocketTmp), "unix", ".sock");
|
||||||
else
|
else
|
||||||
unixSocket = Files.createTempFile("unix", ".sock");
|
sockFile = Files.createTempFile("unix", ".sock");
|
||||||
if (unixSocket.toAbsolutePath().toString().length() > UnixSocketConnector.MAX_UNIX_SOCKET_PATH_LENGTH)
|
if (sockFile.toAbsolutePath().toString().length() > UnixSocketConnector.MAX_UNIX_SOCKET_PATH_LENGTH)
|
||||||
{
|
{
|
||||||
Path tmp = Paths.get("/tmp");
|
Path tmp = Paths.get("/tmp");
|
||||||
assumeTrue(Files.exists(tmp) && Files.isDirectory(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");
|
assertTrue(Files.deleteIfExists(sockFile), "temp sock file cannot be deleted");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,111 +42,111 @@ public class JnrTest
|
||||||
serverChannel.configureBlocking(false);
|
serverChannel.configureBlocking(false);
|
||||||
serverChannel.socket().bind(address);
|
serverChannel.socket().bind(address);
|
||||||
serverChannel.register(serverSelector, SelectionKey.OP_ACCEPT, "SERVER");
|
serverChannel.register(serverSelector, SelectionKey.OP_ACCEPT, "SERVER");
|
||||||
System.err.printf("serverChannel=%s,%n",serverChannel);
|
System.err.printf("serverChannel=%s,%n", serverChannel);
|
||||||
|
|
||||||
UnixSocketChannel client = UnixSocketChannel.open( address );
|
UnixSocketChannel client = UnixSocketChannel.open(address);
|
||||||
Selector clientSelector = NativeSelectorProvider.getInstance().openSelector();
|
Selector clientSelector = NativeSelectorProvider.getInstance().openSelector();
|
||||||
client.configureBlocking(false);
|
client.configureBlocking(false);
|
||||||
SelectionKey clientKey = client.register(clientSelector,0,"client");
|
SelectionKey clientKey = client.register(clientSelector, 0, "client");
|
||||||
|
|
||||||
System.err.printf("client=%s connected=%b pending=%b%n",client,client.isConnected(),client.isConnectionPending());
|
System.err.printf("client=%s connected=%b pending=%b%n", client, client.isConnected(), client.isConnectionPending());
|
||||||
|
|
||||||
int selected = serverSelector.select();
|
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();
|
SelectionKey key = serverSelector.selectedKeys().iterator().next();
|
||||||
serverSelector.selectedKeys().clear();
|
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();
|
UnixSocketChannel server = serverChannel.accept();
|
||||||
server.configureBlocking(false);
|
server.configureBlocking(false);
|
||||||
SelectionKey serverKey = server.register(serverSelector, SelectionKey.OP_READ, "server");
|
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();
|
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);
|
ByteBuffer buffer = ByteBuffer.allocate(32768);
|
||||||
|
|
||||||
buffer.clear();
|
buffer.clear();
|
||||||
int read = server.read(buffer);
|
int read = server.read(buffer);
|
||||||
buffer.flip();
|
buffer.flip();
|
||||||
System.err.printf("server read=%d%n",read);
|
System.err.printf("server read=%d%n", read);
|
||||||
|
|
||||||
selected = clientSelector.selectNow();
|
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)));
|
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();
|
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();
|
key = serverSelector.selectedKeys().iterator().next();
|
||||||
serverSelector.selectedKeys().clear();
|
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();
|
buffer.clear();
|
||||||
read = server.read(buffer);
|
read = server.read(buffer);
|
||||||
buffer.flip();
|
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();
|
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)));
|
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();
|
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);
|
clientKey.interestOps(SelectionKey.OP_READ);
|
||||||
|
|
||||||
selected = clientSelector.selectNow();
|
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();
|
key = clientSelector.selectedKeys().iterator().next();
|
||||||
clientSelector.selectedKeys().clear();
|
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.clear();
|
||||||
read = client.read(buffer);
|
read = client.read(buffer);
|
||||||
buffer.flip();
|
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...");
|
System.err.println("So far so good.... now it gets strange...");
|
||||||
|
|
||||||
// Let's write until flow control hit
|
// Let's write until flow control hit
|
||||||
|
|
||||||
int size = buffer.capacity();
|
int size = buffer.capacity();
|
||||||
Arrays.fill(buffer.array(),0,size,(byte)'X');
|
Arrays.fill(buffer.array(), 0, size, (byte)'X');
|
||||||
long written = 0;
|
long written = 0;
|
||||||
while(true)
|
while (true)
|
||||||
{
|
{
|
||||||
buffer.position(0).limit(size);
|
buffer.position(0).limit(size);
|
||||||
wrote = server.write(buffer);
|
wrote = server.write(buffer);
|
||||||
|
|
||||||
System.err.printf("server wrote %d/%d remaining=%d%n",wrote,size,buffer.remaining());
|
System.err.printf("server wrote %d/%d remaining=%d%n", wrote, size, buffer.remaining());
|
||||||
|
|
||||||
if (buffer.remaining()!=(size-wrote))
|
if (buffer.remaining() != (size - wrote))
|
||||||
System.err.printf("BUG!!!!!!!!!!!!!!!!%n");
|
System.err.printf("BUG!!!!!!!!!!!!!!!!%n");
|
||||||
|
|
||||||
if (wrote==0)
|
if (wrote == 0)
|
||||||
break;
|
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();
|
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();
|
key = clientSelector.selectedKeys().iterator().next();
|
||||||
clientSelector.selectedKeys().clear();
|
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.clear();
|
||||||
buffer.limit(32);
|
buffer.limit(32);
|
||||||
read = client.read(buffer);
|
read = client.read(buffer);
|
||||||
buffer.flip();
|
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();
|
server.close();
|
||||||
client.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">
|
<!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">
|
<Call name="addFirstConnectionFactory">
|
||||||
<Arg>
|
<Arg>
|
||||||
<New class="org.eclipse.jetty.server.ProxyConnectionFactory"/>
|
<New class="org.eclipse.jetty.server.ProxyConnectionFactory"/>
|
||||||
|
|
|
@ -19,19 +19,19 @@ server
|
||||||
etc/jetty-unixsocket.xml
|
etc/jetty-unixsocket.xml
|
||||||
|
|
||||||
[files]
|
[files]
|
||||||
maven://com.github.jnr/jnr-unixsocket/0.8|lib/jnr/jnr-unixsocket-0.8.jar
|
maven://com.github.jnr/jnr-unixsocket/0.20|lib/jnr/jnr-unixsocket-0.20.jar
|
||||||
maven://com.github.jnr/jnr-ffi/2.0.3|lib/jnr/jnr-ffi-2.0.3.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.9|lib/jnr/jffi-1.2.9.jar
|
maven://com.github.jnr/jffi/1.2.17|lib/jnr/jffi-1.2.17.jar
|
||||||
maven://com.github.jnr/jffi/1.2.9/jar/native|lib/jnr/jffi-1.2.9-native.jar
|
maven://com.github.jnr/jffi/1.2.16/jar/native|lib/jnr/jffi-1.2.16-native.jar
|
||||||
maven://org.ow2.asm/asm/5.0.1|lib/jnr/asm-5.0.1.jar
|
maven://org.ow2.asm/asm/7.0|lib/jnr/asm-7.0.jar
|
||||||
maven://org.ow2.asm/asm-commons/5.0.1|lib/jnr/asm-commons-5.0.1.jar
|
maven://org.ow2.asm/asm-commons/7.0|lib/jnr/asm-commons-7.0.jar
|
||||||
maven://org.ow2.asm/asm-analysis/5.0.3|lib/jnr/asm-analysis-5.0.3.jar
|
maven://org.ow2.asm/asm-analysis/7.0|lib/jnr/asm-analysis-7.0.jar
|
||||||
maven://org.ow2.asm/asm-tree/5.0.3|lib/jnr/asm-tree-5.0.3.jar
|
maven://org.ow2.asm/asm-tree/7.0|lib/jnr/asm-tree-7.0.jar
|
||||||
maven://org.ow2.asm/asm-util/5.0.3|lib/jnr/asm-util-5.0.3.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-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-constants/0.9.11|lib/jnr/jnr-constants-0.9.11.jar
|
||||||
maven://com.github.jnr/jnr-enxio/0.9|lib/jnr/jnr-enxio-0.9.jar
|
maven://com.github.jnr/jnr-enxio/0.18|lib/jnr/jnr-enxio-0.18.jar
|
||||||
maven://com.github.jnr/jnr-posix/3.0.12|lib/jnr/jnr-posix-3.0.12.jar
|
maven://com.github.jnr/jnr-posix/3.0.46|lib/jnr/jnr-posix-3.0.46.jar
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
lib/jetty-unixsocket-${jetty.version}.jar
|
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.ssl.SslContextFactory;
|
||||||
import org.eclipse.jetty.util.thread.Scheduler;
|
import org.eclipse.jetty.util.thread.Scheduler;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>A server-side connector for UNIX sockets.</p>
|
||||||
|
*/
|
||||||
@ManagedObject("Connector using UNIX Socket")
|
@ManagedObject("Connector using UNIX Socket")
|
||||||
public class UnixSocketConnector extends AbstractConnector
|
public class UnixSocketConnector extends AbstractConnector
|
||||||
{
|
{
|
||||||
|
@ -67,10 +70,9 @@ public class UnixSocketConnector extends AbstractConnector
|
||||||
private volatile boolean _reuseAddress = true;
|
private volatile boolean _reuseAddress = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP Server Connection.
|
* <p>Constructs a UnixSocketConnector with the default configuration.</p>
|
||||||
* <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</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)
|
public UnixSocketConnector(@Name("server") Server server)
|
||||||
{
|
{
|
||||||
|
@ -78,11 +80,10 @@ public class UnixSocketConnector extends AbstractConnector
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP Server Connection.
|
* <p>Constructs a UnixSocketConnector with the given number of selectors</p>
|
||||||
* <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</p>
|
|
||||||
*
|
*
|
||||||
* @param server The {@link Server} this connector will accept connection for.
|
* @param server the {@link Server} this connector will accept connections 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 selectors the number of selectors, or <=0 for a default value.
|
||||||
*/
|
*/
|
||||||
public UnixSocketConnector(@Name("server") Server server, @Name("selectors") int selectors)
|
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>Constructs a UnixSocketConnector with the given ConnectionFactories.</p>
|
||||||
* <p>Construct a Server Connector with the passed Connection factories.</p>
|
|
||||||
*
|
*
|
||||||
* @param server The {@link Server} this connector will accept connection for.
|
* @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.
|
* @param factories zero or more {@link ConnectionFactory} instances used to create and configure connections.
|
||||||
*/
|
*/
|
||||||
public UnixSocketConnector(@Name("server") Server server, @Name("factories") ConnectionFactory... factories)
|
public UnixSocketConnector(@Name("server") Server server, @Name("factories") ConnectionFactory... factories)
|
||||||
{
|
{
|
||||||
|
@ -102,12 +102,11 @@ public class UnixSocketConnector extends AbstractConnector
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP Server Connection.
|
* <p>Constructs a UnixSocketConnector with the given selectors and ConnectionFactories.</p>
|
||||||
* <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the only factory.</p>
|
|
||||||
*
|
*
|
||||||
* @param server The {@link Server} this connector will accept connection for.
|
* @param server the {@link Server} this connector will accept connections 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 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.
|
* @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)
|
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>Constructs a UnixSocketConnector with the given SslContextFactory.</p>
|
||||||
* <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the primary protocol</p>.
|
|
||||||
*
|
*
|
||||||
* @param server The {@link Server} this connector will accept connection for.
|
* @param server the {@link Server} this connector will accept connections for.
|
||||||
* @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the
|
* @param sslContextFactory when non null a {@link SslConnectionFactory} prepended to the other ConnectionFactories
|
||||||
* list of HTTP Connection Factory.
|
|
||||||
*/
|
*/
|
||||||
public UnixSocketConnector(@Name("server") Server server, @Name("sslContextFactory") SslContextFactory.Server sslContextFactory)
|
public UnixSocketConnector(@Name("server") Server server, @Name("sslContextFactory") SslContextFactory.Server sslContextFactory)
|
||||||
{
|
{
|
||||||
|
@ -128,13 +125,11 @@ public class UnixSocketConnector extends AbstractConnector
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HTTP Server Connection.
|
* <p>Constructs a UnixSocketConnector with the given selectors and SslContextFactory.</p>.
|
||||||
* <p>Construct a ServerConnector with a private instance of {@link HttpConnectionFactory} as the primary protocol</p>.
|
|
||||||
*
|
*
|
||||||
* @param server The {@link Server} this connector will accept connection for.
|
* @param server the {@link Server} this connector will accept connections for.
|
||||||
* @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the
|
* @param sslContextFactory when non null a {@link SslConnectionFactory} prepended to the other ConnectionFactories
|
||||||
* list of HTTP Connection Factory.
|
* @param selectors the number of selectors, or <=0 for a default value.
|
||||||
* @param selectors the number of selector threads, or <=0 for a default value. Selectors notice and schedule established connection that can make IO progress.
|
|
||||||
*/
|
*/
|
||||||
public UnixSocketConnector(@Name("server") Server server, @Name("selectors") int selectors, @Name("sslContextFactory") SslContextFactory.Server sslContextFactory)
|
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 server the {@link Server} this connector will accept connections for.
|
||||||
* @param sslContextFactory If non null, then a {@link SslConnectionFactory} is instantiated and prepended to the
|
* @param sslContextFactory when non null a {@link SslConnectionFactory} prepended to the other ConnectionFactories
|
||||||
* 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 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)
|
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 server the {@link Server} this connector will accept connections for.
|
||||||
* @param executor An executor used to run tasks for handling requests, acceptors and selectors.
|
* @param executor the executor that runs tasks for handling requests, acceptors and selectors.
|
||||||
* If null then use the servers executor
|
* @param scheduler the scheduler used to schedule timed tasks.
|
||||||
* @param scheduler A scheduler used to schedule timeouts. If null then use the servers scheduler
|
* @param bufferPool the ByteBufferPool used to allocate buffers.
|
||||||
* @param bufferPool A ByteBuffer pool used to allocate buffers. If null then create a private pool with default configuration.
|
* @param selectors the number of selectors, or <=0 for a default value.
|
||||||
* @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 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)
|
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);
|
addBean(_manager, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ManagedAttribute
|
@ManagedAttribute("The UNIX socket file name")
|
||||||
public String getUnixSocket()
|
public String getUnixSocket()
|
||||||
{
|
{
|
||||||
return _unixSocket;
|
return _unixSocket;
|
||||||
|
@ -295,7 +288,7 @@ public class UnixSocketConnector extends AbstractConnector
|
||||||
return _acceptChannel;
|
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());
|
return new UnixSocketEndPoint((UnixSocketChannel)channel, selector, key, getScheduler());
|
||||||
}
|
}
|
||||||
|
@ -321,6 +314,7 @@ public class UnixSocketConnector extends AbstractConnector
|
||||||
* @return whether the server socket reuses addresses
|
* @return whether the server socket reuses addresses
|
||||||
* @see ServerSocket#getReuseAddress()
|
* @see ServerSocket#getReuseAddress()
|
||||||
*/
|
*/
|
||||||
|
@ManagedAttribute("Whether the server socket reuses addresses")
|
||||||
public boolean getReuseAddress()
|
public boolean getReuseAddress()
|
||||||
{
|
{
|
||||||
return _reuseAddress;
|
return _reuseAddress;
|
||||||
|
@ -338,9 +332,7 @@ public class UnixSocketConnector extends AbstractConnector
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
return String.format("%s{%s}",
|
return String.format("%s{%s}", super.toString(), _unixSocket);
|
||||||
super.toString(),
|
|
||||||
_unixSocket);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected class UnixSocketConnectorManager extends SelectorManager
|
protected class UnixSocketConnectorManager extends SelectorManager
|
||||||
|
@ -363,15 +355,15 @@ public class UnixSocketConnector extends AbstractConnector
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
UnixSocketEndPoint endPoint = UnixSocketConnector.this.newEndPoint(channel, selector, selectionKey);
|
||||||
endp.setIdleTimeout(getIdleTimeout());
|
endPoint.setIdleTimeout(getIdleTimeout());
|
||||||
return endp;
|
return endPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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);
|
return getDefaultConnectionFactory().newConnection(UnixSocketConnector.this, endpoint);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
|
|
||||||
|
@ -38,32 +37,31 @@ import org.eclipse.jetty.unixsocket.server.UnixSocketConnector;
|
||||||
|
|
||||||
public class UnixSocketProxyServer
|
public class UnixSocketProxyServer
|
||||||
{
|
{
|
||||||
public static void main (String... args) throws Exception
|
public static void main(String... args) throws Exception
|
||||||
{
|
{
|
||||||
Server server = new Server();
|
Server server = new Server();
|
||||||
|
|
||||||
HttpConnectionFactory http = new HttpConnectionFactory();
|
HttpConnectionFactory http = new HttpConnectionFactory();
|
||||||
ProxyConnectionFactory proxy = new ProxyConnectionFactory(http.getProtocol());
|
ProxyConnectionFactory proxy = new ProxyConnectionFactory(http.getProtocol());
|
||||||
UnixSocketConnector connector = new UnixSocketConnector(server,proxy,http);
|
UnixSocketConnector connector = new UnixSocketConnector(server, proxy, http);
|
||||||
server.addConnector(connector);
|
server.addConnector(connector);
|
||||||
|
|
||||||
Path socket = Paths.get(connector.getUnixSocket());
|
Path socket = Paths.get(connector.getUnixSocket());
|
||||||
if (Files.exists(socket))
|
if (Files.exists(socket))
|
||||||
Files.delete(socket);
|
Files.delete(socket);
|
||||||
|
|
||||||
server.setHandler(new AbstractHandler.ErrorDispatchHandler()
|
server.setHandler(new AbstractHandler.ErrorDispatchHandler()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
throws IOException, ServletException
|
|
||||||
{
|
{
|
||||||
int l = 0;
|
int l = 0;
|
||||||
if (request.getContentLength()!=0)
|
if (request.getContentLength() != 0)
|
||||||
{
|
{
|
||||||
InputStream in = request.getInputStream();
|
InputStream in = request.getInputStream();
|
||||||
byte[] buffer = new byte[4096];
|
byte[] buffer = new byte[4096];
|
||||||
int r = 0;
|
int r = 0;
|
||||||
while (r>=0)
|
while (r >= 0)
|
||||||
{
|
{
|
||||||
l += r;
|
l += r;
|
||||||
r = in.read(buffer);
|
r = in.read(buffer);
|
||||||
|
@ -71,21 +69,21 @@ public class UnixSocketProxyServer
|
||||||
}
|
}
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
response.setStatus(200);
|
response.setStatus(200);
|
||||||
response.getWriter().write("Hello World "+new Date() + "\r\n");
|
response.getWriter().write("Hello World " + new Date() + "\r\n");
|
||||||
response.getWriter().write("remote="+request.getRemoteAddr()+":"+request.getRemotePort()+"\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("local =" + request.getLocalAddr() + ":" + request.getLocalPort() + "\r\n");
|
||||||
response.getWriter().write("read ="+l+"\r\n");
|
response.getWriter().write("read =" + l + "\r\n");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
server.start();
|
server.start();
|
||||||
|
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
Thread.sleep(5000);
|
Thread.sleep(5000);
|
||||||
connector.dumpStdErr();
|
connector.dumpStdErr();
|
||||||
}
|
}
|
||||||
|
|
||||||
// server.join();
|
// server.join();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,12 +36,12 @@ import org.eclipse.jetty.unixsocket.server.UnixSocketConnector;
|
||||||
|
|
||||||
public class UnixSocketServer
|
public class UnixSocketServer
|
||||||
{
|
{
|
||||||
public static void main (String... args) throws Exception
|
public static void main(String... args) throws Exception
|
||||||
{
|
{
|
||||||
Server server = new Server();
|
Server server = new Server();
|
||||||
|
|
||||||
HttpConnectionFactory http = new HttpConnectionFactory();
|
HttpConnectionFactory http = new HttpConnectionFactory();
|
||||||
UnixSocketConnector connector = new UnixSocketConnector(server,http);
|
UnixSocketConnector connector = new UnixSocketConnector(server, http);
|
||||||
server.addConnector(connector);
|
server.addConnector(connector);
|
||||||
|
|
||||||
Path socket = Paths.get(connector.getUnixSocket());
|
Path socket = Paths.get(connector.getUnixSocket());
|
||||||
|
@ -51,16 +51,15 @@ public class UnixSocketServer
|
||||||
server.setHandler(new AbstractHandler.ErrorDispatchHandler()
|
server.setHandler(new AbstractHandler.ErrorDispatchHandler()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response)
|
protected void doNonErrorHandle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||||
throws IOException
|
|
||||||
{
|
{
|
||||||
int l = 0;
|
int l = 0;
|
||||||
if (request.getContentLength()!=0)
|
if (request.getContentLength() != 0)
|
||||||
{
|
{
|
||||||
InputStream in = request.getInputStream();
|
InputStream in = request.getInputStream();
|
||||||
byte[] buffer = new byte[4096];
|
byte[] buffer = new byte[4096];
|
||||||
int r = 0;
|
int r = 0;
|
||||||
while (r>=0)
|
while (r >= 0)
|
||||||
{
|
{
|
||||||
l += r;
|
l += r;
|
||||||
r = in.read(buffer);
|
r = in.read(buffer);
|
||||||
|
@ -68,10 +67,10 @@ public class UnixSocketServer
|
||||||
}
|
}
|
||||||
baseRequest.setHandled(true);
|
baseRequest.setHandled(true);
|
||||||
response.setStatus(200);
|
response.setStatus(200);
|
||||||
response.getWriter().write("Hello World "+new Date() + "\r\n");
|
response.getWriter().write("Hello World " + new Date() + "\r\n");
|
||||||
response.getWriter().write("remote="+request.getRemoteAddr()+":"+request.getRemotePort()+"\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("local =" + request.getLocalAddr() + ":" + request.getLocalPort() + "\r\n");
|
||||||
response.getWriter().write("read ="+l+"\r\n");
|
response.getWriter().write("read =" + l + "\r\n");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -253,6 +253,7 @@ public class BufferUtil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------ */
|
||||||
/**
|
/**
|
||||||
* @param buf the buffer to check
|
* @param buf the buffer to check
|
||||||
* @return true if buf is equal to EMPTY_BUFFER
|
* @return true if buf is equal to EMPTY_BUFFER
|
||||||
|
@ -274,6 +275,36 @@ public class BufferUtil
|
||||||
return buf == null || buf.remaining() == 0;
|
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.
|
/** Check for a non null and non empty buffer.
|
||||||
* @param buf the buffer to check
|
* @param buf the buffer to check
|
||||||
|
|
|
@ -310,6 +310,7 @@ public interface Callback extends Invocable
|
||||||
return callback.getInvocationType();
|
return callback.getInvocationType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>A CompletableFuture that is also a Callback.</p>
|
* <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.nio.file.Path;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.B64Code;
|
import org.eclipse.jetty.util.B64Code;
|
||||||
import org.eclipse.jetty.util.IO;
|
import org.eclipse.jetty.util.IO;
|
||||||
import org.eclipse.jetty.util.Loader;
|
import org.eclipse.jetty.util.Loader;
|
||||||
|
import org.eclipse.jetty.util.MultiMap;
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
import org.eclipse.jetty.util.URIUtil;
|
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.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
@ -102,7 +104,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
* Construct a resource from a url.
|
* Construct a resource from a url.
|
||||||
* @param url the url for which to make the resource
|
* @param url the url for which to make the resource
|
||||||
* @param useCaches true enables URLConnection caching if applicable to the type of 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)
|
static Resource newResource(URL url, boolean useCaches)
|
||||||
{
|
{
|
||||||
|
@ -143,8 +145,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
* @throws MalformedURLException Problem accessing URI
|
* @throws MalformedURLException Problem accessing URI
|
||||||
* @return A Resource object.
|
* @return A Resource object.
|
||||||
*/
|
*/
|
||||||
public static Resource newResource(String resource)
|
public static Resource newResource(String resource) throws IOException
|
||||||
throws MalformedURLException, IOException
|
|
||||||
{
|
{
|
||||||
return newResource(resource, __defaultUseCaches);
|
return newResource(resource, __defaultUseCaches);
|
||||||
}
|
}
|
||||||
|
@ -156,10 +157,9 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
* @return A Resource object.
|
* @return A Resource object.
|
||||||
* @throws MalformedURLException Problem accessing URI
|
* @throws MalformedURLException Problem accessing URI
|
||||||
*/
|
*/
|
||||||
public static Resource newResource(String resource, boolean useCaches)
|
public static Resource newResource(String resource, boolean useCaches) throws IOException
|
||||||
throws MalformedURLException, IOException
|
|
||||||
{
|
{
|
||||||
URL url=null;
|
URL url;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Try to format as a URL?
|
// Try to format as a URL?
|
||||||
|
@ -222,8 +222,7 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
* @return The new Resource
|
* @return The new Resource
|
||||||
* @throws IOException Problem accessing resource.
|
* @throws IOException Problem accessing resource.
|
||||||
*/
|
*/
|
||||||
public static Resource newSystemResource(String resource)
|
public static Resource newSystemResource(String resource) throws IOException
|
||||||
throws IOException
|
|
||||||
{
|
{
|
||||||
URL url=null;
|
URL url=null;
|
||||||
// Try to format as a URL?
|
// Try to format as a URL?
|
||||||
|
@ -444,7 +443,6 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
* <p>
|
* <p>
|
||||||
* This method is essentially an alias for {@link #addPath(String)}, but without checked exceptions.
|
* This method is essentially an alias for {@link #addPath(String)}, but without checked exceptions.
|
||||||
* This method satisfied the {@link ResourceFactory} interface.
|
* This method satisfied the {@link ResourceFactory} interface.
|
||||||
* @see org.eclipse.jetty.util.resource.ResourceFactory#getResource(java.lang.String)
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Resource getResource(String path)
|
public Resource getResource(String path)
|
||||||
|
@ -501,65 +499,228 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
* @return String of HTML
|
* @return String of HTML
|
||||||
* @throws IOException if unable to get the list of resources as HTML
|
* @throws IOException if unable to get the list of resources as HTML
|
||||||
*/
|
*/
|
||||||
public String getListHTML(String base,boolean parent)
|
public String getListHTML(String base, boolean parent) throws IOException
|
||||||
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);
|
base=URIUtil.canonicalPath(base);
|
||||||
if (base==null || !isDirectory())
|
if (base==null || !isDirectory())
|
||||||
return null;
|
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);
|
String[] rawListing = list();
|
||||||
buf.append("<HTML><HEAD>");
|
if (rawListing == null)
|
||||||
buf.append("<LINK HREF=\"").append("jetty-dir.css").append("\" REL=\"stylesheet\" TYPE=\"text/css\"/><TITLE>");
|
{
|
||||||
|
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);
|
||||||
buf.append("</TITLE></HEAD><BODY>\n<H1>");
|
buf.append("</title>\n");
|
||||||
buf.append(title);
|
buf.append("</head>\n");
|
||||||
buf.append("</H1>\n<TABLE BORDER=0>\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)
|
if (parent)
|
||||||
{
|
{
|
||||||
buf.append("<TR><TD><A HREF=\"");
|
// Name
|
||||||
buf.append(URIUtil.addEncodedPaths(base,"../"));
|
buf.append("<tr><td class=\"name\"><a href=\"");
|
||||||
buf.append("\">Parent Directory</A></TD><TD></TD><TD></TD></TR>\n");
|
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 dfmt=DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
|
||||||
DateFormat.MEDIUM);
|
DateFormat.MEDIUM);
|
||||||
for (int i=0 ; i< ls.length ; i++)
|
for (Resource item: items)
|
||||||
{
|
{
|
||||||
Resource item = addPath(ls[i]);
|
String name = item.getName();
|
||||||
|
int slashIdx = name.lastIndexOf('/');
|
||||||
buf.append("\n<TR><TD><A HREF=\"");
|
if (slashIdx != -1)
|
||||||
String path=URIUtil.addEncodedPaths(encodedBase,URIUtil.encodePath(ls[i]));
|
{
|
||||||
|
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);
|
buf.append(path);
|
||||||
|
|
||||||
if (item.isDirectory() && !path.endsWith("/"))
|
|
||||||
buf.append(URIUtil.SLASH);
|
|
||||||
|
|
||||||
// URIUtil.encodePath(buf,path);
|
|
||||||
buf.append("\">");
|
buf.append("\">");
|
||||||
buf.append(deTag(ls[i]));
|
buf.append(deTag(name));
|
||||||
buf.append(" ");
|
buf.append(" ");
|
||||||
buf.append("</A></TD><TD ALIGN=right>");
|
buf.append("</a></td>");
|
||||||
buf.append(item.length());
|
|
||||||
buf.append(" bytes </TD><TD>");
|
// Last Modified
|
||||||
|
buf.append("<td class=\"lastmodified\">");
|
||||||
buf.append(dfmt.format(new Date(item.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("</tbody>\n");
|
||||||
buf.append("</BODY></HTML>\n");
|
buf.append("</table>\n");
|
||||||
|
buf.append("</body></html>\n");
|
||||||
|
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -599,21 +760,21 @@ public abstract class Resource implements ResourceFactory, Closeable
|
||||||
char c=raw.charAt(i);
|
char c=raw.charAt(i);
|
||||||
switch(c)
|
switch(c)
|
||||||
{
|
{
|
||||||
case '"':
|
case '"':
|
||||||
buf.append("%22");
|
buf.append("%22");
|
||||||
continue;
|
break;
|
||||||
case '\'':
|
case '\'':
|
||||||
buf.append("%27");
|
buf.append("%27");
|
||||||
continue;
|
break;
|
||||||
case '<':
|
case '<':
|
||||||
buf.append("%3C");
|
buf.append("%3C");
|
||||||
continue;
|
break;
|
||||||
case '>':
|
case '>':
|
||||||
buf.append("%3E");
|
buf.append("%3E");
|
||||||
continue;
|
break;
|
||||||
default:
|
default:
|
||||||
buf.append(c);
|
buf.append(c);
|
||||||
continue;
|
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;
|
package org.eclipse.jetty.util.thread;
|
||||||
|
|
||||||
|
import java.io.Closeable;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -146,6 +147,9 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
|
||||||
@Override
|
@Override
|
||||||
protected void doStop() throws Exception
|
protected void doStop() throws Exception
|
||||||
{
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Stopping {}", this);
|
||||||
|
|
||||||
removeBean(_tryExecutor);
|
removeBean(_tryExecutor);
|
||||||
_tryExecutor = TryExecutor.NO_TRY;
|
_tryExecutor = TryExecutor.NO_TRY;
|
||||||
|
|
||||||
|
@ -163,11 +167,13 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
|
||||||
for (int i = _threadsStarted.get(); i-- > 0; )
|
for (int i = _threadsStarted.get(); i-- > 0; )
|
||||||
jobs.offer(noop);
|
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;
|
long stopby = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2;
|
||||||
for (Thread thread : _threads)
|
for (Thread thread : _threads)
|
||||||
{
|
{
|
||||||
long canwait = TimeUnit.NANOSECONDS.toMillis(stopby - System.nanoTime());
|
long canwait = TimeUnit.NANOSECONDS.toMillis(stopby - System.nanoTime());
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Waiting for {} for {}", thread, canwait);
|
||||||
if (canwait > 0)
|
if (canwait > 0)
|
||||||
thread.join(canwait);
|
thread.join(canwait);
|
||||||
}
|
}
|
||||||
|
@ -177,13 +183,19 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
|
||||||
// interrupt remaining threads
|
// interrupt remaining threads
|
||||||
if (_threadsStarted.get() > 0)
|
if (_threadsStarted.get() > 0)
|
||||||
for (Thread thread : _threads)
|
for (Thread thread : _threads)
|
||||||
|
{
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Interrupting {}", thread);
|
||||||
thread.interrupt();
|
thread.interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
// wait again for the other half of our stop time
|
// wait again for the other half of our stop time
|
||||||
stopby = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2;
|
stopby = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeout) / 2;
|
||||||
for (Thread thread : _threads)
|
for (Thread thread : _threads)
|
||||||
{
|
{
|
||||||
long canwait = TimeUnit.NANOSECONDS.toMillis(stopby - System.nanoTime());
|
long canwait = TimeUnit.NANOSECONDS.toMillis(stopby - System.nanoTime());
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Waiting for {} for {}", thread, canwait);
|
||||||
if (canwait > 0)
|
if (canwait > 0)
|
||||||
thread.join(canwait);
|
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)
|
if (_budget!=null)
|
||||||
_budget.reset();
|
_budget.reset();
|
||||||
|
|
||||||
|
@ -535,6 +566,8 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
|
||||||
thread.setDaemon(isDaemon());
|
thread.setDaemon(isDaemon());
|
||||||
thread.setPriority(getThreadsPriority());
|
thread.setPriority(getThreadsPriority());
|
||||||
thread.setName(_name + "-" + thread.getId());
|
thread.setName(_name + "-" + thread.getId());
|
||||||
|
if (LOG.isDebugEnabled())
|
||||||
|
LOG.debug("Starting {}", thread);
|
||||||
_threads.add(thread);
|
_threads.add(thread);
|
||||||
_lastShrink.set(System.nanoTime());
|
_lastShrink.set(System.nanoTime());
|
||||||
thread.start();
|
thread.start();
|
||||||
|
@ -651,105 +684,93 @@ public class QueuedThreadPool extends ContainerLifeCycle implements SizedThreadP
|
||||||
_tryExecutor);
|
_tryExecutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Runnable idleJobPoll() throws InterruptedException
|
|
||||||
{
|
|
||||||
return _jobs.poll(_idleTimeout, TimeUnit.MILLISECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Runnable _runnable = new Runnable()
|
private Runnable _runnable = new Runnable()
|
||||||
{
|
{
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
boolean shrink = false;
|
boolean idle = false;
|
||||||
boolean ignore = false;
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Runnable job = _jobs.poll();
|
Runnable job = _jobs.poll();
|
||||||
|
|
||||||
if (job != null && _threadsIdle.get() == 0)
|
if (job != null && _threadsIdle.get() == 0)
|
||||||
{
|
|
||||||
startThreads(1);
|
startThreads(1);
|
||||||
}
|
|
||||||
|
|
||||||
loop:
|
while (true)
|
||||||
while (isRunning())
|
|
||||||
{
|
{
|
||||||
// Job loop
|
if (job == null)
|
||||||
while (job != null && isRunning())
|
|
||||||
{
|
{
|
||||||
|
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())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("run {}", job);
|
LOG.debug("run {}", job);
|
||||||
runJob(job);
|
runJob(job);
|
||||||
if (LOG.isDebugEnabled())
|
if (LOG.isDebugEnabled())
|
||||||
LOG.debug("ran {}", job);
|
LOG.debug("ran {}", job);
|
||||||
if (Thread.interrupted())
|
|
||||||
{
|
// Clear interrupted status
|
||||||
ignore = true;
|
Thread.interrupted();
|
||||||
break loop;
|
|
||||||
}
|
|
||||||
job = _jobs.poll();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Idle loop
|
if (!isRunning())
|
||||||
try
|
break;
|
||||||
{
|
|
||||||
_threadsIdle.incrementAndGet();
|
|
||||||
|
|
||||||
while (isRunning() && job == null)
|
job = _jobs.poll();
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (InterruptedException e)
|
catch (InterruptedException e)
|
||||||
{
|
{
|
||||||
ignore = true;
|
|
||||||
LOG.ignore(e);
|
LOG.ignore(e);
|
||||||
}
|
}
|
||||||
catch (Throwable e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
LOG.warn(e);
|
LOG.warn(String.format("Unexpected thread death: %s in %s", this, QueuedThreadPool.this), e);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
if (!shrink && isRunning())
|
if (idle)
|
||||||
{
|
_threadsIdle.decrementAndGet();
|
||||||
if (!ignore)
|
|
||||||
LOG.warn("Unexpected thread death: {} in {}", this, QueuedThreadPool.this);
|
|
||||||
// This is an unexpected thread death!
|
|
||||||
if (_threadsStarted.decrementAndGet() < getMaxThreads())
|
|
||||||
startThreads(1);
|
|
||||||
}
|
|
||||||
removeThread(Thread.currentThread());
|
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;
|
background-color: #FFFFFF;
|
||||||
margin: 10px;
|
margin: 10px;
|
||||||
padding: 5px;
|
padding: 5px;
|
||||||
|
font-family: sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
h1
|
h1.title {
|
||||||
{
|
|
||||||
text-shadow: #000000 -1px -1px 1px;
|
text-shadow: #000000 -1px -1px 1px;
|
||||||
color: #FC390E;
|
color: #FC390E;
|
||||||
font-weight: bold;
|
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;
|
color: #7036be;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
|
@ -21,10 +40,9 @@ a
|
||||||
font-size:inherit;
|
font-size:inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
td
|
td {
|
||||||
{
|
|
||||||
font-style: italic;
|
font-style: italic;
|
||||||
padding: 2px 15px 2px 0px;
|
padding: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -18,22 +18,30 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.util.thread;
|
package org.eclipse.jetty.util.thread;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
import java.io.Closeable;
|
||||||
import org.eclipse.jetty.util.thread.ThreadPool.SizedThreadPool;
|
import java.io.IOException;
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
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.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
|
||||||
import static org.hamcrest.Matchers.is;
|
import static org.hamcrest.Matchers.is;
|
||||||
import static org.junit.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
|
public class QueuedThreadPoolTest extends AbstractThreadPoolTest
|
||||||
{
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(QueuedThreadPoolTest.class);
|
||||||
private final AtomicInteger _jobs=new AtomicInteger();
|
private final AtomicInteger _jobs=new AtomicInteger();
|
||||||
|
|
||||||
private class RunningJob implements Runnable
|
private class RunningJob implements Runnable
|
||||||
|
@ -41,6 +49,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
|
||||||
private final CountDownLatch _run = new CountDownLatch(1);
|
private final CountDownLatch _run = new CountDownLatch(1);
|
||||||
private final CountDownLatch _stopping = new CountDownLatch(1);
|
private final CountDownLatch _stopping = new CountDownLatch(1);
|
||||||
private final CountDownLatch _stopped = new CountDownLatch(1);
|
private final CountDownLatch _stopped = new CountDownLatch(1);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
|
@ -51,7 +60,7 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
|
||||||
}
|
}
|
||||||
catch(Exception e)
|
catch(Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
LOG.debug(e);
|
||||||
}
|
}
|
||||||
finally
|
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
|
@Test
|
||||||
public void testThreadPool() throws Exception
|
public void testThreadPool() throws Exception
|
||||||
{
|
{
|
||||||
|
@ -146,6 +166,58 @@ public class QueuedThreadPoolTest extends AbstractThreadPoolTest
|
||||||
waitForIdle(tp,2);
|
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
|
@Test
|
||||||
public void testShrink() throws Exception
|
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));
|
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());
|
localAddress = InetSocketAddress.createUnresolved(httpRequest.getLocalAddr(), httpRequest.getLocalPort());
|
||||||
localName = httpRequest.getLocalName();
|
localName = httpRequest.getLocalName();
|
||||||
remoteAddress = InetSocketAddress.createUnresolved(httpRequest.getRemoteAddr(), httpRequest.getRemotePort());
|
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>
|
<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 -->
|
<!-- 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.compiler.plugin.version>3.8.0</maven.compiler.plugin.version>
|
||||||
<maven.dependency.plugin.version>3.1.1</maven.dependency.plugin.version>
|
<maven.dependency.plugin.version>3.1.1</maven.dependency.plugin.version>
|
||||||
<maven.resources.plugin.version>3.1.0</maven.resources.plugin.version>
|
<maven.resources.plugin.version>3.1.0</maven.resources.plugin.version>
|
||||||
|
@ -1203,9 +1203,20 @@
|
||||||
</profile>
|
</profile>
|
||||||
<profile>
|
<profile>
|
||||||
<id>ci</id>
|
<id>ci</id>
|
||||||
<modules>
|
<build>
|
||||||
<module>aggregates/jetty-all</module>
|
<pluginManagement>
|
||||||
</modules>
|
<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>
|
||||||
<profile>
|
<profile>
|
||||||
<id>update-version</id>
|
<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'
|
# This is equivalent to 'mvn release:prepare'
|
||||||
if proceedyn "Update project.versions for $VER_RELEASE? (Y/n)" y; then
|
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" \
|
-DoldVersion="$VER_CURRENT" \
|
||||||
-DnewVersion="$VER_RELEASE" \
|
-DnewVersion="$VER_RELEASE" \
|
||||||
-DprocessAllModules=true
|
-DprocessAllModules=true
|
||||||
|
|
|
@ -80,6 +80,12 @@
|
||||||
<artifactId>jetty-test-helper</artifactId>
|
<artifactId>jetty-test-helper</artifactId>
|
||||||
<scope>test</scope>
|
<scope>test</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty</groupId>
|
||||||
|
<artifactId>jetty-unixsocket-client</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
<build>
|
<build>
|
||||||
|
|
|
@ -19,6 +19,8 @@
|
||||||
package org.eclipse.jetty.tests.distribution;
|
package org.eclipse.jetty.tests.distribution;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.util.concurrent.TimeUnit;
|
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.http.HttpStatus;
|
||||||
import org.eclipse.jetty.http2.client.HTTP2Client;
|
import org.eclipse.jetty.http2.client.HTTP2Client;
|
||||||
import org.eclipse.jetty.http2.client.http.HttpClientTransportOverHTTP2;
|
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.Test;
|
||||||
import org.junit.jupiter.api.condition.DisabledOnJre;
|
import org.junit.jupiter.api.condition.DisabledOnJre;
|
||||||
import org.junit.jupiter.api.condition.JRE;
|
import org.junit.jupiter.api.condition.JRE;
|
||||||
|
@ -204,4 +208,53 @@ public class DistributionTests extends AbstractDistributionTest
|
||||||
assertThat(response.getContentAsString(), not(containsString("<%")));
|
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.eclipse.jetty.util.log.StacklessLogging;
|
||||||
import org.hamcrest.Matchers;
|
import org.hamcrest.Matchers;
|
||||||
import org.junit.jupiter.api.Assumptions;
|
import org.junit.jupiter.api.Assumptions;
|
||||||
import org.junit.jupiter.api.Disabled;
|
|
||||||
import org.junit.jupiter.api.Tag;
|
import org.junit.jupiter.api.Tag;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||||
|
@ -394,12 +393,8 @@ public class AsyncIOServletTest extends AbstractTest<AsyncIOServletTest.AsyncTra
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ArgumentsSource(TransportProvider.class)
|
@ArgumentsSource(TransportProvider.class)
|
||||||
@Tag("Unstable")
|
@Tag("Unstable")
|
||||||
@Disabled // TODO fix this test! #2243
|
|
||||||
public void testAsyncWriteClosed(Transport transport) throws Exception
|
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);
|
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";
|
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)
|
@ArgumentsSource(TransportProvider.class)
|
||||||
public void testAsyncWriteLessThanContentLengthFlushed(Transport transport) throws Exception
|
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);
|
init(transport);
|
||||||
|
|
||||||
CountDownLatch complete = new CountDownLatch(1);
|
CountDownLatch complete = new CountDownLatch(1);
|
||||||
|
|
|
@ -180,7 +180,7 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
|
||||||
for (String failure : failures)
|
for (String failure : failures)
|
||||||
logger.info("FAILED: {}", failure);
|
logger.info("FAILED: {}", failure);
|
||||||
|
|
||||||
assertTrue(failures.isEmpty(),failures.toString());
|
assertTrue(failures.isEmpty(), failures.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void test(final CountDownLatch latch, final List<String> failures)
|
private void test(final CountDownLatch latch, final List<String> failures)
|
||||||
|
@ -366,19 +366,18 @@ public class HttpClientLoadTest extends AbstractTest<HttpClientLoadTest.LoadTran
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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)
|
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());
|
unixSocketConnector.setUnixSocket(sockFile.toString());
|
||||||
return unixSocketConnector;
|
return unixSocketConnector;
|
||||||
}
|
}
|
||||||
int cores = ProcessorUtils.availableProcessors();
|
return new ServerConnector(server, null, null, byteBufferPool, 1, selectors, provideServerConnectionFactory(transport));
|
||||||
ByteBufferPool byteBufferPool = new ArrayByteBufferPool();
|
|
||||||
byteBufferPool = new LeakTrackingByteBufferPool(byteBufferPool);
|
|
||||||
return new ServerConnector(server, null, null, byteBufferPool,
|
|
||||||
1, Math.min(1, cores / 2), provideServerConnectionFactory(transport));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -97,7 +97,7 @@ public class RoundRobinConnectionPoolTest extends AbstractTest<TransportScenario
|
||||||
int expected = remotePorts.get(base);
|
int expected = remotePorts.get(base);
|
||||||
int candidate = remotePorts.get(i);
|
int candidate = remotePorts.get(i);
|
||||||
assertThat(scenario.client.dump() + System.lineSeparator() + remotePorts.toString(), expected, Matchers.equalTo(candidate));
|
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)));
|
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 expected = remotePorts.get(base);
|
||||||
int candidate = remotePorts.get(i);
|
int candidate = remotePorts.get(i);
|
||||||
assertThat(scenario.client.dump() + System.lineSeparator() + remotePorts.toString(), expected, Matchers.equalTo(candidate));
|
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)));
|
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.Arrays;
|
||||||
import java.util.EnumSet;
|
import java.util.EnumSet;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
import org.eclipse.jetty.util.StringUtil;
|
||||||
|
import org.junit.jupiter.api.condition.OS;
|
||||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||||
import org.junit.jupiter.params.provider.Arguments;
|
import org.junit.jupiter.params.provider.Arguments;
|
||||||
import org.junit.jupiter.params.provider.ArgumentsProvider;
|
import org.junit.jupiter.params.provider.ArgumentsProvider;
|
||||||
|
@ -35,21 +35,16 @@ public class TransportProvider implements ArgumentsProvider
|
||||||
String transports = System.getProperty(Transport.class.getName());
|
String transports = System.getProperty(Transport.class.getName());
|
||||||
|
|
||||||
if (!StringUtil.isBlank(transports))
|
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.LINUX.isCurrentOs())
|
||||||
// if (OS.IS_UNIX)
|
return Arrays.stream(Transport.values());
|
||||||
// return Transport.values();
|
|
||||||
|
|
||||||
return EnumSet.complementOf(EnumSet.of(Transport.UNIX_SOCKET))
|
return EnumSet.complementOf(EnumSet.of(Transport.UNIX_SOCKET)).stream();
|
||||||
.stream();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception
|
public Stream<? extends Arguments> provideArguments(ExtensionContext context)
|
||||||
{
|
{
|
||||||
return getActiveTransports().map(Arguments::of);
|
return getActiveTransports().map(Arguments::of);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
||||||
import java.lang.management.ManagementFactory;
|
import java.lang.management.ManagementFactory;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
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.unixsocket.server.UnixSocketConnector;
|
||||||
import org.eclipse.jetty.util.BlockingArrayQueue;
|
import org.eclipse.jetty.util.BlockingArrayQueue;
|
||||||
import org.eclipse.jetty.util.SocketAddressResolver;
|
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.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||||
|
|
||||||
public class TransportScenario
|
public class TransportScenario
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(TransportScenario.class);
|
private static final Logger LOG = Log.getLogger(TransportScenario.class);
|
||||||
|
@ -78,25 +82,34 @@ public class TransportScenario
|
||||||
protected String servletPath = "/servlet";
|
protected String servletPath = "/servlet";
|
||||||
protected HttpClient client;
|
protected HttpClient client;
|
||||||
protected Path sockFile;
|
protected Path sockFile;
|
||||||
protected final BlockingQueue<String> requestLog= new BlockingArrayQueue<>();
|
protected final BlockingQueue<String> requestLog = new BlockingArrayQueue<>();
|
||||||
|
|
||||||
public TransportScenario(final Transport transport) throws IOException
|
public TransportScenario(final Transport transport) throws IOException
|
||||||
{
|
{
|
||||||
this.transport = transport;
|
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();
|
Files.delete(sockFile);
|
||||||
sockFile = Files.createTempFile(target,"unix", ".sock" );
|
Path tmp = Paths.get("/tmp");
|
||||||
Files.delete( sockFile );
|
assumeTrue(Files.exists(tmp) && Files.isDirectory(tmp));
|
||||||
|
sockFile = Files.createTempFile(tmp, "unix", ".sock");
|
||||||
}
|
}
|
||||||
|
Files.delete(sockFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Optional<String> getNetworkConnectorLocalPort()
|
public Optional<String> getNetworkConnectorLocalPort()
|
||||||
{
|
{
|
||||||
if (connector instanceof ServerConnector)
|
if (connector instanceof ServerConnector)
|
||||||
{
|
{
|
||||||
ServerConnector serverConnector = (ServerConnector) connector;
|
ServerConnector serverConnector = (ServerConnector)connector;
|
||||||
return Optional.of(Integer.toString(serverConnector.getLocalPort()));
|
return Optional.of(Integer.toString(serverConnector.getLocalPort()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +120,7 @@ public class TransportScenario
|
||||||
{
|
{
|
||||||
if (connector instanceof ServerConnector)
|
if (connector instanceof ServerConnector)
|
||||||
{
|
{
|
||||||
ServerConnector serverConnector = (ServerConnector) connector;
|
ServerConnector serverConnector = (ServerConnector)connector;
|
||||||
return Optional.of(serverConnector.getLocalPort());
|
return Optional.of(serverConnector.getLocalPort());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,12 +145,12 @@ public class TransportScenario
|
||||||
return new HttpClient(transport);
|
return new HttpClient(transport);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Connector newServerConnector(Server server) throws Exception
|
public Connector newServerConnector(Server server)
|
||||||
{
|
{
|
||||||
if (transport == Transport.UNIX_SOCKET)
|
if (transport == Transport.UNIX_SOCKET)
|
||||||
{
|
{
|
||||||
UnixSocketConnector unixSocketConnector = new UnixSocketConnector(server, provideServerConnectionFactory( transport ));
|
UnixSocketConnector unixSocketConnector = new UnixSocketConnector(server, provideServerConnectionFactory(transport));
|
||||||
unixSocketConnector.setUnixSocket( sockFile.toString() );
|
unixSocketConnector.setUnixSocket(sockFile.toString());
|
||||||
return unixSocketConnector;
|
return unixSocketConnector;
|
||||||
}
|
}
|
||||||
return new ServerConnector(server, provideServerConnectionFactory(transport));
|
return new ServerConnector(server, provideServerConnectionFactory(transport));
|
||||||
|
@ -149,10 +162,7 @@ public class TransportScenario
|
||||||
ret.append(getScheme());
|
ret.append(getScheme());
|
||||||
ret.append("://localhost");
|
ret.append("://localhost");
|
||||||
Optional<String> localPort = getNetworkConnectorLocalPort();
|
Optional<String> localPort = getNetworkConnectorLocalPort();
|
||||||
if (localPort.isPresent())
|
localPort.ifPresent(s -> ret.append(':').append(s));
|
||||||
{
|
|
||||||
ret.append(':').append(localPort.get());
|
|
||||||
}
|
|
||||||
return ret.toString();
|
return ret.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,13 +245,13 @@ public class TransportScenario
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result.toArray(new ConnectionFactory[result.size()]);
|
return result.toArray(new ConnectionFactory[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setConnectionIdleTimeout(long idleTimeout)
|
public void setConnectionIdleTimeout(long idleTimeout)
|
||||||
{
|
{
|
||||||
if (connector instanceof AbstractConnector)
|
if (connector instanceof AbstractConnector)
|
||||||
AbstractConnector.class.cast(connector).setIdleTimeout(idleTimeout);
|
((AbstractConnector)connector).setIdleTimeout(idleTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setServerIdleTimeout(long idleTimeout)
|
public void setServerIdleTimeout(long idleTimeout)
|
||||||
|
@ -252,9 +262,10 @@ public class TransportScenario
|
||||||
else
|
else
|
||||||
setConnectionIdleTimeout(idleTimeout);
|
setConnectionIdleTimeout(idleTimeout);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(Handler handler) throws Exception
|
public void start(Handler handler) throws Exception
|
||||||
{
|
{
|
||||||
start(handler,null);
|
start(handler, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void start(Handler handler, Consumer<HttpClient> config) throws Exception
|
public void start(Handler handler, Consumer<HttpClient> config) throws Exception
|
||||||
|
@ -284,7 +295,7 @@ public class TransportScenario
|
||||||
client.setExecutor(clientThreads);
|
client.setExecutor(clientThreads);
|
||||||
client.setSocketAddressResolver(new SocketAddressResolver.Sync());
|
client.setSocketAddressResolver(new SocketAddressResolver.Sync());
|
||||||
|
|
||||||
if (config!=null)
|
if (config != null)
|
||||||
config.accept(client);
|
config.accept(client);
|
||||||
|
|
||||||
client.start();
|
client.start();
|
||||||
|
@ -317,7 +328,7 @@ public class TransportScenario
|
||||||
server.setRequestLog((request, response) ->
|
server.setRequestLog((request, response) ->
|
||||||
{
|
{
|
||||||
int status = response.getCommittedMetaData().getStatus();
|
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);
|
server.setHandler(handler);
|
||||||
|
@ -326,7 +337,7 @@ public class TransportScenario
|
||||||
{
|
{
|
||||||
server.start();
|
server.start();
|
||||||
}
|
}
|
||||||
catch ( Exception e )
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
@ -375,25 +386,25 @@ public class TransportScenario
|
||||||
{
|
{
|
||||||
stopClient();
|
stopClient();
|
||||||
}
|
}
|
||||||
catch (Exception ignore)
|
catch (Exception x)
|
||||||
{
|
{
|
||||||
LOG.ignore(ignore);
|
LOG.ignore(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
stopServer();
|
stopServer();
|
||||||
}
|
}
|
||||||
catch (Exception ignore)
|
catch (Exception x)
|
||||||
{
|
{
|
||||||
LOG.ignore(ignore);
|
LOG.ignore(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sockFile!=null)
|
if (sockFile != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Files.deleteIfExists( sockFile );
|
Files.deleteIfExists(sockFile);
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
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"));
|
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>");
|
out.println("<p><b>Result: "+(fragInitParamResult? "<span class=\"pass\">PASS": "<span class=\"fail\">FAIL")+"</span></p>");
|
||||||
|
|
||||||
|
|
||||||
__HandlesTypes = Arrays.asList( "javax.servlet.GenericServlet",
|
__HandlesTypes = Arrays.asList( "javax.servlet.GenericServlet",
|
||||||
"javax.servlet.http.HttpServlet",
|
"javax.servlet.http.HttpServlet",
|
||||||
"com.acme.test.AsyncListenerServlet",
|
"com.acme.test.AsyncListenerServlet",
|
||||||
|
"com.acme.test.ClassLoaderServlet",
|
||||||
"com.acme.test.AnnotationTest",
|
"com.acme.test.AnnotationTest",
|
||||||
"com.acme.test.RoleAnnotationTest",
|
"com.acme.test.RoleAnnotationTest",
|
||||||
"com.acme.test.MultiPartTest",
|
"com.acme.test.MultiPartTest",
|
||||||
|
|
Loading…
Reference in New Issue