From 36340c48988de24376c4d0c36e8b596012ba475b Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 8 Jan 2014 14:54:39 -0700 Subject: [PATCH 01/54] Minor tweaks to leak detector stuff --- .../org/eclipse/jetty/io/LeakTrackingByteBufferPool.java | 9 +++++---- .../main/java/org/eclipse/jetty/util/LeakDetector.java | 1 - 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/jetty-io/src/main/java/org/eclipse/jetty/io/LeakTrackingByteBufferPool.java b/jetty-io/src/main/java/org/eclipse/jetty/io/LeakTrackingByteBufferPool.java index 7cc33fbe25a..fef0adfdf45 100644 --- a/jetty-io/src/main/java/org/eclipse/jetty/io/LeakTrackingByteBufferPool.java +++ b/jetty-io/src/main/java/org/eclipse/jetty/io/LeakTrackingByteBufferPool.java @@ -37,6 +37,7 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By LeakTrackingByteBufferPool.this.leaked(leakInfo); } }; + private final ByteBufferPool delegate; public LeakTrackingByteBufferPool(ByteBufferPool delegate) @@ -51,7 +52,7 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By { ByteBuffer buffer = delegate.acquire(size, direct); if (!leakDetector.acquired(buffer)) - LOG.info("ByteBuffer {}@{} not tracked", buffer, System.identityHashCode(buffer)); + LOG.warn("ByteBuffer {}@{} not tracked", buffer, System.identityHashCode(buffer)); return buffer; } @@ -59,12 +60,12 @@ public class LeakTrackingByteBufferPool extends ContainerLifeCycle implements By public void release(ByteBuffer buffer) { if (!leakDetector.released(buffer)) - LOG.info("ByteBuffer {}@{} released but not acquired", buffer, System.identityHashCode(buffer)); + LOG.warn("ByteBuffer {}@{} released but not acquired", buffer, System.identityHashCode(buffer)); delegate.release(buffer); } - protected void leaked(LeakDetector.LeakInfo leakInfo) + protected void leaked(LeakDetector.LeakInfo leakInfo) { - LOG.info("ByteBuffer " + leakInfo.getResourceDescription() + " leaked at:", leakInfo.getStackFrames()); + LOG.warn("ByteBuffer " + leakInfo.getResourceDescription() + " leaked at:", leakInfo.getStackFrames()); } } diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/LeakDetector.java b/jetty-util/src/main/java/org/eclipse/jetty/util/LeakDetector.java index b0ac94e9b4d..33ffe6f924d 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/LeakDetector.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/LeakDetector.java @@ -20,7 +20,6 @@ package org.eclipse.jetty.util; import java.lang.ref.PhantomReference; import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; From 29dec203ba037e081b90214f321f63ab6710ce46 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Wed, 8 Jan 2014 14:54:57 -0700 Subject: [PATCH 02/54] Turning on some leak detection in websocket testing --- .../server/AnnotatedServerEndpointTest.java | 8 +- .../jsr356/server/BasicEndpointTest.java | 7 +- .../jsr356/server/IdleTimeoutTest.java | 7 +- .../jsr356/server/LargeAnnotatedTest.java | 7 +- .../jsr356/server/LargeContainerTest.java | 7 +- .../jsr356/server/OnMessageReturnTest.java | 7 +- .../websocket/jsr356/server/SessionTest.java | 8 +- .../websocket/client/WebSocketClient.java | 12 ++- .../websocket/client/BadNetworkTest.java | 7 +- .../websocket/client/ClientConnectTest.java | 7 +- .../websocket/client/examples/TestClient.java | 7 +- .../common/GeneratorParserRoundtripTest.java | 10 ++- .../websocket/common/WebSocketFrameTest.java | 19 +++-- .../common/WebSocketRemoteEndpointTest.java | 9 ++- .../websocket/common/events/EventCapture.java | 6 +- .../common/events/EventDriverTest.java | 17 ++-- .../extensions/AbstractExtensionTest.java | 17 ++-- .../common/extensions/ExtensionStackTest.java | 7 +- .../extensions/FragmentExtensionTest.java | 23 +++--- .../compress/DeflateFrameExtensionTest.java | 16 ++-- .../PerMessageDeflateExtensionTest.java | 11 ++- .../common/io/LocalWebSocketConnection.java | 13 ++- .../common/io/LocalWebSocketSession.java | 5 +- .../message/MessageInputStreamTest.java | 13 ++- .../message/MessageOutputStreamTest.java | 7 +- .../common/message/MessageWriterTest.java | 7 +- .../common/test/BlockheadClient.java | 1 + .../common/test/BlockheadServer.java | 1 + .../common/test/LeakTrackingBufferPool.java | 80 +++++++++++++++++++ .../websocket/common/test/UnitGenerator.java | 8 +- .../websocket/common/test/UnitParser.java | 8 +- .../websocket/server/WebSocketHandler.java | 9 ++- .../server/WebSocketServerFactory.java | 11 ++- .../server/WebSocketUpgradeFilter.java | 9 ++- .../WebSocketUpgradeHandlerWrapper.java | 9 ++- .../server/WebSocketOverSSLTest.java | 11 ++- .../websocket/server/ab/AbstractABCase.java | 15 ++-- 37 files changed, 328 insertions(+), 98 deletions(-) create mode 100644 jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/LeakTrackingBufferPool.java diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointTest.java index dc65e1c7273..3791fb00cb4 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/AnnotatedServerEndpointTest.java @@ -26,11 +26,13 @@ import java.util.Queue; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.jsr356.server.samples.beans.DateDecoder; import org.eclipse.jetty.websocket.jsr356.server.samples.beans.TimeEncoder; import org.eclipse.jetty.websocket.jsr356.server.samples.echo.ConfiguredEchoSocket; @@ -38,6 +40,7 @@ import org.eclipse.jetty.websocket.jsr356.server.samples.echo.EchoSocketConfigur import org.junit.AfterClass; import org.junit.Assert; import org.junit.BeforeClass; +import org.junit.Rule; import org.junit.Test; /** @@ -45,6 +48,9 @@ import org.junit.Test; */ public class AnnotatedServerEndpointTest { + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + private static WSServer server; @BeforeClass @@ -72,7 +78,7 @@ public class AnnotatedServerEndpointTest private void assertResponse(String message, String... expectedTexts) throws Exception { - WebSocketClient client = new WebSocketClient(); + WebSocketClient client = new WebSocketClient(bufferPool); try { client.start(); diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BasicEndpointTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BasicEndpointTest.java index 176f36d27f8..03359878e97 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BasicEndpointTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/BasicEndpointTest.java @@ -23,10 +23,12 @@ import java.util.Queue; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoEndpoint; import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoEndpointConfigContextListener; import org.junit.Assert; @@ -42,6 +44,9 @@ public class BasicEndpointTest @Rule public TestingDir testdir = new TestingDir(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + @Test public void testEcho() throws Exception { @@ -61,7 +66,7 @@ public class BasicEndpointTest wsb.deployWebapp(webapp); // wsb.dump(); - WebSocketClient client = new WebSocketClient(); + WebSocketClient client = new WebSocketClient(bufferPool); try { client.start(); diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/IdleTimeoutTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/IdleTimeoutTest.java index 8e735ef23a0..db85dc06b78 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/IdleTimeoutTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/IdleTimeoutTest.java @@ -29,6 +29,7 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.util.log.Log; @@ -36,6 +37,7 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.IdleTimeoutContextListener; import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.OnOpenIdleTimeoutEndpoint; import org.eclipse.jetty.websocket.jsr356.server.samples.idletimeout.OnOpenIdleTimeoutSocket; @@ -51,6 +53,9 @@ public class IdleTimeoutTest @Rule public TestingDir testdir = new TestingDir(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + private static WSServer server; @BeforeClass @@ -80,7 +85,7 @@ public class IdleTimeoutTest private void assertConnectionTimeout(URI uri) throws Exception, IOException, InterruptedException, ExecutionException, TimeoutException { - WebSocketClient client = new WebSocketClient(); + WebSocketClient client = new WebSocketClient(bufferPool); try { client.start(); diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeAnnotatedTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeAnnotatedTest.java index 3db7247cc7d..f663fc210a8 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeAnnotatedTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeAnnotatedTest.java @@ -25,10 +25,12 @@ import java.util.Queue; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.jsr356.server.samples.echo.LargeEchoConfiguredSocket; import org.junit.Assert; import org.junit.Rule; @@ -42,6 +44,9 @@ public class LargeAnnotatedTest @Rule public TestingDir testdir = new TestingDir(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + @Test public void testEcho() throws Exception { @@ -58,7 +63,7 @@ public class LargeAnnotatedTest wsb.deployWebapp(webapp); // wsb.dump(); - WebSocketClient client = new WebSocketClient(); + WebSocketClient client = new WebSocketClient(bufferPool); try { client.getPolicy().setMaxTextMessageSize(128*1024); diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeContainerTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeContainerTest.java index 3c47b6e1582..130f7f17ae9 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeContainerTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/LargeContainerTest.java @@ -25,10 +25,12 @@ import java.util.Queue; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.jsr356.server.samples.echo.LargeEchoDefaultSocket; import org.junit.Assert; import org.junit.Rule; @@ -42,6 +44,9 @@ public class LargeContainerTest @Rule public TestingDir testdir = new TestingDir(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + @Test public void testEcho() throws Exception { @@ -58,7 +63,7 @@ public class LargeContainerTest wsb.deployWebapp(webapp); // wsb.dump(); - WebSocketClient client = new WebSocketClient(); + WebSocketClient client = new WebSocketClient(bufferPool); try { client.getPolicy().setMaxTextMessageSize(128*1024); diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnMessageReturnTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnMessageReturnTest.java index ec3def09d7e..5185b6acdf2 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnMessageReturnTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/OnMessageReturnTest.java @@ -23,10 +23,12 @@ import java.util.Queue; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.jsr356.server.samples.echo.EchoReturnEndpoint; import org.junit.Assert; import org.junit.Rule; @@ -37,6 +39,9 @@ public class OnMessageReturnTest @Rule public TestingDir testdir = new TestingDir(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + @Test public void testEchoReturn() throws Exception { @@ -53,7 +58,7 @@ public class OnMessageReturnTest wsb.deployWebapp(webapp); wsb.dump(); - WebSocketClient client = new WebSocketClient(); + WebSocketClient client = new WebSocketClient(bufferPool); try { client.start(); diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java index 9453d3a54fc..24e8cff6a92 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java @@ -28,14 +28,17 @@ import java.util.Queue; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.toolchain.test.MavenTestingUtils; import org.eclipse.jetty.webapp.WebAppContext; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.junit.After; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -95,6 +98,9 @@ public class SessionTest return cases; } + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + private final Case testcase; private WSServer server; private URI serverUri; @@ -127,7 +133,7 @@ public class SessionTest private void assertResponse(String requestPath, String requestMessage, String expectedResponse) throws Exception { - WebSocketClient client = new WebSocketClient(); + WebSocketClient client = new WebSocketClient(bufferPool); try { client.start(); diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java index 6d2144dd569..b9309f8db82 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java @@ -94,6 +94,11 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen { this(null,executor); } + + public WebSocketClient(ByteBufferPool bufferPool) + { + this(null,null,bufferPool); + } public WebSocketClient(SslContextFactory sslContextFactory) { @@ -101,11 +106,16 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen } public WebSocketClient(SslContextFactory sslContextFactory, Executor executor) + { + this(sslContextFactory,executor,new MappedByteBufferPool()); + } + + public WebSocketClient(SslContextFactory sslContextFactory, Executor executor, ByteBufferPool bufferPool) { this.executor = executor; this.sslContextFactory = sslContextFactory; this.policy = WebSocketPolicy.newClientPolicy(); - this.bufferPool = new MappedByteBufferPool(); + this.bufferPool = bufferPool; this.extensionRegistry = new WebSocketExtensionFactory(policy,bufferPool); this.masker = new RandomMasker(); this.eventDriverFactory = new EventDriverFactory(policy); diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/BadNetworkTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/BadNetworkTest.java index 7a342787512..19b740a7e19 100644 --- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/BadNetworkTest.java +++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/BadNetworkTest.java @@ -22,10 +22,12 @@ import java.net.URI; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.common.test.BlockheadServer; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection; import org.junit.After; import org.junit.Before; @@ -41,13 +43,16 @@ public class BadNetworkTest @Rule public TestTracker tt = new TestTracker(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + private BlockheadServer server; private WebSocketClient client; @Before public void startClient() throws Exception { - client = new WebSocketClient(); + client = new WebSocketClient(bufferPool); client.getPolicy().setIdleTimeout(250); client.start(); } diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientConnectTest.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientConnectTest.java index d4b269f1987..5b41986c2f8 100644 --- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientConnectTest.java +++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/ClientConnectTest.java @@ -33,12 +33,14 @@ import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.toolchain.test.OS; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.UpgradeException; import org.eclipse.jetty.websocket.common.AcceptHash; import org.eclipse.jetty.websocket.common.test.BlockheadServer; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.common.test.BlockheadServer.ServerConnection; import org.junit.After; import org.junit.Assert; @@ -55,6 +57,9 @@ public class ClientConnectTest @Rule public TestTracker tt = new TestTracker(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + private final int timeout = 500; private BlockheadServer server; private WebSocketClient client; @@ -86,7 +91,7 @@ public class ClientConnectTest @Before public void startClient() throws Exception { - client = new WebSocketClient(); + client = new WebSocketClient(bufferPool); client.setConnectTimeout(timeout); client.start(); } diff --git a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/examples/TestClient.java b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/examples/TestClient.java index dfa9fdf7b8c..fe4a99f8899 100644 --- a/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/examples/TestClient.java +++ b/jetty-websocket/websocket-client/src/test/java/org/eclipse/jetty/websocket/client/examples/TestClient.java @@ -29,11 +29,13 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.WebSocketAdapter; import org.eclipse.jetty.websocket.client.ClientUpgradeRequest; import org.eclipse.jetty.websocket.client.WebSocketClient; import org.eclipse.jetty.websocket.common.OpCode; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; /** * This is not a general purpose websocket client. It's only for testing the websocket server and is hardwired to a specific draft version of the protocol. @@ -95,6 +97,8 @@ public class TestClient private static final Random __random = new Random(); + private static LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("TestClient",new MappedByteBufferPool()); + private final String _host; private final int _port; private final String _protocol; @@ -172,7 +176,7 @@ public class TestClient } TestClient[] client = new TestClient[clients]; - WebSocketClient wsclient = new WebSocketClient(); + WebSocketClient wsclient = new WebSocketClient(bufferPool); try { wsclient.start(); @@ -250,6 +254,7 @@ public class TestClient wsclient.stop(); } + bufferPool.assertNoLeaks(); } private static void usage(String[] args) diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorParserRoundtripTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorParserRoundtripTest.java index 5b4e6789eec..5b6ba00e93b 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorParserRoundtripTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/GeneratorParserRoundtripTest.java @@ -18,27 +18,30 @@ package org.eclipse.jetty.websocket.common; -import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.*; import java.nio.ByteBuffer; import java.util.Arrays; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.common.frames.TextFrame; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; public class GeneratorParserRoundtripTest { + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + @Test public void testParserAndGenerator() throws Exception { WebSocketPolicy policy = WebSocketPolicy.newClientPolicy(); - ByteBufferPool bufferPool = new MappedByteBufferPool(); Generator gen = new Generator(policy,bufferPool); Parser parser = new Parser(policy,bufferPool); IncomingFramesCapture capture = new IncomingFramesCapture(); @@ -77,7 +80,6 @@ public class GeneratorParserRoundtripTest @Test public void testParserAndGeneratorMasked() throws Exception { - ByteBufferPool bufferPool = new MappedByteBufferPool(); Generator gen = new Generator(WebSocketPolicy.newClientPolicy(),bufferPool); Parser parser = new Parser(WebSocketPolicy.newServerPolicy(),bufferPool); IncomingFramesCapture capture = new IncomingFramesCapture(); diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketFrameTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketFrameTest.java index 3e864a5eaf2..c359a6a3460 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketFrameTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketFrameTest.java @@ -18,11 +18,10 @@ package org.eclipse.jetty.websocket.common; -import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.*; import java.nio.ByteBuffer; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.websocket.api.StatusCode; @@ -31,15 +30,20 @@ import org.eclipse.jetty.websocket.api.extensions.Frame; import org.eclipse.jetty.websocket.common.frames.CloseFrame; import org.eclipse.jetty.websocket.common.frames.PingFrame; import org.eclipse.jetty.websocket.common.frames.TextFrame; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.common.util.Hex; import org.junit.Assert; -import org.junit.BeforeClass; +import org.junit.Before; +import org.junit.Rule; import org.junit.Test; public class WebSocketFrameTest { - private static Generator strictGenerator; - private static Generator laxGenerator; + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + + private Generator strictGenerator; + private Generator laxGenerator; private ByteBuffer generateWholeFrame(Generator generator, Frame frame) { @@ -49,11 +53,10 @@ public class WebSocketFrameTest return buf; } - @BeforeClass - public static void initGenerator() + @Before + public void initGenerator() { WebSocketPolicy policy = WebSocketPolicy.newServerPolicy(); - ByteBufferPool bufferPool = new MappedByteBufferPool(); strictGenerator = new Generator(policy,bufferPool); laxGenerator = new Generator(policy,bufferPool,false); } diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpointTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpointTest.java index 2c5336eaf67..a78b7e68703 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpointTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/WebSocketRemoteEndpointTest.java @@ -23,7 +23,9 @@ import static org.hamcrest.Matchers.containsString; import java.io.IOException; import java.nio.ByteBuffer; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.websocket.common.io.LocalWebSocketConnection; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.common.test.OutgoingFramesCapture; import org.junit.Assert; import org.junit.Rule; @@ -35,10 +37,13 @@ public class WebSocketRemoteEndpointTest @Rule public TestName testname = new TestName(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + @Test public void testTextBinaryText() throws IOException { - LocalWebSocketConnection conn = new LocalWebSocketConnection(testname); + LocalWebSocketConnection conn = new LocalWebSocketConnection(testname,bufferPool); OutgoingFramesCapture outgoing = new OutgoingFramesCapture(); WebSocketRemoteEndpoint remote = new WebSocketRemoteEndpoint(conn,outgoing); conn.connect(); @@ -68,7 +73,7 @@ public class WebSocketRemoteEndpointTest @Test public void testTextPingText() throws IOException { - LocalWebSocketConnection conn = new LocalWebSocketConnection(testname); + LocalWebSocketConnection conn = new LocalWebSocketConnection(testname,bufferPool); OutgoingFramesCapture outgoing = new OutgoingFramesCapture(); WebSocketRemoteEndpoint remote = new WebSocketRemoteEndpoint(conn,outgoing); conn.connect(); diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventCapture.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventCapture.java index 6fa5df377e3..38451bb3835 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventCapture.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventCapture.java @@ -25,11 +25,15 @@ import static org.hamcrest.Matchers.startsWith; import java.util.regex.Pattern; import org.eclipse.jetty.toolchain.test.EventQueue; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.junit.Assert; @SuppressWarnings("serial") public class EventCapture extends EventQueue { + private static final Logger LOG = Log.getLogger(EventCapture.class); + public static class Assertable { private final String event; @@ -63,7 +67,7 @@ public class EventCapture extends EventQueue public void add(String format, Object... args) { String msg = String.format(format,args); - System.err.printf("### EVENT: %s%n",msg); + LOG.debug("EVENT: {}",msg); super.offer(msg); } diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventDriverTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventDriverTest.java index dce54a91f81..fd837b45279 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventDriverTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/events/EventDriverTest.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.common.events; import java.io.IOException; import java.util.concurrent.TimeoutException; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.websocket.api.StatusCode; import org.eclipse.jetty.websocket.api.WebSocketException; import org.eclipse.jetty.websocket.api.WebSocketPolicy; @@ -30,6 +31,7 @@ import org.eclipse.jetty.websocket.common.frames.BinaryFrame; import org.eclipse.jetty.websocket.common.frames.PingFrame; import org.eclipse.jetty.websocket.common.frames.TextFrame; import org.eclipse.jetty.websocket.common.io.LocalWebSocketSession; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TestName; @@ -46,6 +48,9 @@ public class EventDriverTest @Rule public TestName testname = new TestName(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + private Frame makeBinaryFrame(String content, boolean fin) { return new BinaryFrame().setPayload(content).setFin(fin); @@ -57,7 +62,7 @@ public class EventDriverTest AdapterConnectCloseSocket socket = new AdapterConnectCloseSocket(); EventDriver driver = wrap(socket); - try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver)) + try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool)) { conn.open(); driver.incomingFrame(new CloseInfo(StatusCode.NORMAL).asFrame()); @@ -74,7 +79,7 @@ public class EventDriverTest AnnotatedBinaryArraySocket socket = new AnnotatedBinaryArraySocket(); EventDriver driver = wrap(socket); - try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver)) + try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool)) { conn.open(); driver.incomingFrame(makeBinaryFrame("Hello World",true)); @@ -93,7 +98,7 @@ public class EventDriverTest AnnotatedTextSocket socket = new AnnotatedTextSocket(); EventDriver driver = wrap(socket); - try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver)) + try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool)) { conn.open(); driver.incomingError(new WebSocketException("oof")); @@ -112,7 +117,7 @@ public class EventDriverTest AnnotatedFramesSocket socket = new AnnotatedFramesSocket(); EventDriver driver = wrap(socket); - try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver)) + try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool)) { conn.open(); driver.incomingFrame(new PingFrame().setPayload("PING")); @@ -136,7 +141,7 @@ public class EventDriverTest AnnotatedBinaryStreamSocket socket = new AnnotatedBinaryStreamSocket(); EventDriver driver = wrap(socket); - try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver)) + try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool)) { conn.open(); driver.incomingFrame(makeBinaryFrame("Hello World",true)); @@ -155,7 +160,7 @@ public class EventDriverTest ListenerBasicSocket socket = new ListenerBasicSocket(); EventDriver driver = wrap(socket); - try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver)) + try (LocalWebSocketSession conn = new LocalWebSocketSession(testname,driver,bufferPool)) { conn.start(); conn.open(); diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtensionTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtensionTest.java index 9801cd1e9e0..97e3bf04cf5 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtensionTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/AbstractExtensionTest.java @@ -18,10 +18,10 @@ package org.eclipse.jetty.websocket.common.extensions; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.websocket.api.WebSocketPolicy; -import org.junit.BeforeClass; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; +import org.junit.Before; import org.junit.Rule; import org.junit.rules.TestName; @@ -29,15 +29,16 @@ public abstract class AbstractExtensionTest { @Rule public TestName testname = new TestName(); + + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); - private static ByteBufferPool bufferPool; - protected static ExtensionTool clientExtensions; - protected static ExtensionTool serverExtensions; + protected ExtensionTool clientExtensions; + protected ExtensionTool serverExtensions; - @BeforeClass - public static void init() + @Before + public void init() { - bufferPool = new MappedByteBufferPool(); clientExtensions = new ExtensionTool(WebSocketPolicy.newClientPolicy(),bufferPool); serverExtensions = new ExtensionTool(WebSocketPolicy.newServerPolicy(),bufferPool); } diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStackTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStackTest.java index 6a38af6c364..1863169f7e5 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStackTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/ExtensionStackTest.java @@ -24,7 +24,6 @@ import java.util.ArrayList; import java.util.List; import org.eclipse.jetty.io.ArrayByteBufferPool; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.websocket.api.WebSocketPolicy; @@ -32,12 +31,17 @@ import org.eclipse.jetty.websocket.api.extensions.Extension; import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig; import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory; import org.eclipse.jetty.websocket.common.extensions.identity.IdentityExtension; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; public class ExtensionStackTest { private static final Logger LOG = Log.getLogger(ExtensionStackTest.class); + + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new ArrayByteBufferPool()); @SuppressWarnings("unchecked") private T assertIsExtension(String msg, Object obj, Class clazz) @@ -53,7 +57,6 @@ public class ExtensionStackTest private ExtensionStack createExtensionStack() { WebSocketPolicy policy = WebSocketPolicy.newClientPolicy(); - ByteBufferPool bufferPool = new ArrayByteBufferPool(); ExtensionFactory factory = new WebSocketExtensionFactory(policy,bufferPool); return new ExtensionStack(factory); } diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/FragmentExtensionTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/FragmentExtensionTest.java index 9243332a918..b022c38e4aa 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/FragmentExtensionTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/FragmentExtensionTest.java @@ -18,7 +18,7 @@ package org.eclipse.jetty.websocket.common.extensions; -import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.*; import java.io.IOException; import java.nio.ByteBuffer; @@ -40,12 +40,17 @@ import org.eclipse.jetty.websocket.common.frames.PingFrame; import org.eclipse.jetty.websocket.common.frames.TextFrame; import org.eclipse.jetty.websocket.common.test.ByteBufferAssert; import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.common.test.OutgoingFramesCapture; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; public class FragmentExtensionTest { + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + /** * Verify that incoming frames are passed thru without modification */ @@ -55,7 +60,7 @@ public class FragmentExtensionTest IncomingFramesCapture capture = new IncomingFramesCapture(); FragmentExtension ext = new FragmentExtension(); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); ext.setPolicy(WebSocketPolicy.newClientPolicy()); ExtensionConfig config = ExtensionConfig.parse("fragment;maxLength=4"); ext.setConfig(config); @@ -107,7 +112,7 @@ public class FragmentExtensionTest IncomingFramesCapture capture = new IncomingFramesCapture(); FragmentExtension ext = new FragmentExtension(); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); ext.setPolicy(WebSocketPolicy.newServerPolicy()); ExtensionConfig config = ExtensionConfig.parse("fragment;maxLength=4"); ext.setConfig(config); @@ -142,7 +147,7 @@ public class FragmentExtensionTest OutgoingFramesCapture capture = new OutgoingFramesCapture(); FragmentExtension ext = new FragmentExtension(); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); ext.setPolicy(WebSocketPolicy.newServerPolicy()); ExtensionConfig config = ExtensionConfig.parse("fragment;maxLength=20"); ext.setConfig(config); @@ -173,7 +178,7 @@ public class FragmentExtensionTest expectedFrames.add(new TextFrame().setPayload("-- Albert Einstein").setFin(true)); - capture.dump(); + // capture.dump(); int len = expectedFrames.size(); capture.assertFrameCount(len); @@ -186,8 +191,8 @@ public class FragmentExtensionTest WebSocketFrame actualFrame = frames.get(i); WebSocketFrame expectedFrame = expectedFrames.get(i); - System.out.printf("actual: %s%n",actualFrame); - System.out.printf("expect: %s%n",expectedFrame); + // System.out.printf("actual: %s%n",actualFrame); + // System.out.printf("expect: %s%n",expectedFrame); // Validate Frame Assert.assertThat(prefix + ".opcode",actualFrame.getOpCode(),is(expectedFrame.getOpCode())); @@ -214,7 +219,7 @@ public class FragmentExtensionTest OutgoingFramesCapture capture = new OutgoingFramesCapture(); FragmentExtension ext = new FragmentExtension(); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); ext.setPolicy(WebSocketPolicy.newServerPolicy()); ExtensionConfig config = ExtensionConfig.parse("fragment"); ext.setConfig(config); @@ -278,7 +283,7 @@ public class FragmentExtensionTest OutgoingFramesCapture capture = new OutgoingFramesCapture(); FragmentExtension ext = new FragmentExtension(); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); ext.setPolicy(WebSocketPolicy.newServerPolicy()); ExtensionConfig config = ExtensionConfig.parse("fragment;maxLength=4"); ext.setConfig(config); diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateFrameExtensionTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateFrameExtensionTest.java index 55a00cd2302..d8dc65e351b 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateFrameExtensionTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/DeflateFrameExtensionTest.java @@ -28,7 +28,6 @@ import java.util.List; import java.util.zip.Deflater; import java.util.zip.Inflater; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.StringUtil; @@ -45,19 +44,24 @@ import org.eclipse.jetty.websocket.common.extensions.ExtensionTool.Tester; import org.eclipse.jetty.websocket.common.frames.TextFrame; import org.eclipse.jetty.websocket.common.test.ByteBufferAssert; import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.common.test.OutgoingNetworkBytesCapture; import org.eclipse.jetty.websocket.common.test.UnitParser; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; public class DeflateFrameExtensionTest extends AbstractExtensionTest { + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + private void assertIncoming(byte[] raw, String... expectedTextDatas) { WebSocketPolicy policy = WebSocketPolicy.newClientPolicy(); DeflateFrameExtension ext = new DeflateFrameExtension(); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); ext.setPolicy(policy); ExtensionConfig config = ExtensionConfig.parse("deflate-frame"); @@ -101,13 +105,12 @@ public class DeflateFrameExtensionTest extends AbstractExtensionTest WebSocketPolicy policy = WebSocketPolicy.newClientPolicy(); DeflateFrameExtension ext = new DeflateFrameExtension(); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); ext.setPolicy(policy); ExtensionConfig config = ExtensionConfig.parse("deflate-frame"); ext.setConfig(config); - ByteBufferPool bufferPool = new MappedByteBufferPool(); boolean validating = true; Generator generator = new Generator(policy,bufferPool,validating); generator.configureFromExtensions(Collections.singletonList(ext)); @@ -235,7 +238,7 @@ public class DeflateFrameExtensionTest extends AbstractExtensionTest private void init(DeflateFrameExtension ext) { ext.setConfig(new ExtensionConfig(ext.getName())); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); } @Test @@ -290,11 +293,10 @@ public class DeflateFrameExtensionTest extends AbstractExtensionTest WebSocketPolicy policy = WebSocketPolicy.newClientPolicy(); DeflateFrameExtension ext = new DeflateFrameExtension(); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); ext.setPolicy(policy); ext.setConfig(new ExtensionConfig(ext.getName())); - ByteBufferPool bufferPool = new MappedByteBufferPool(); boolean validating = true; Generator generator = new Generator(policy,bufferPool,validating); generator.configureFromExtensions(Collections.singletonList(ext)); diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtensionTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtensionTest.java index 199a61befac..8d14481b4eb 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtensionTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/extensions/compress/PerMessageDeflateExtensionTest.java @@ -40,8 +40,10 @@ import org.eclipse.jetty.websocket.common.frames.PingFrame; import org.eclipse.jetty.websocket.common.frames.TextFrame; import org.eclipse.jetty.websocket.common.test.ByteBufferAssert; import org.eclipse.jetty.websocket.common.test.IncomingFramesCapture; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.common.test.OutgoingFramesCapture; import org.junit.Assert; +import org.junit.Rule; import org.junit.Test; /** @@ -51,6 +53,9 @@ import org.junit.Test; */ public class PerMessageDeflateExtensionTest extends AbstractExtensionTest { + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + /** * Decode payload example as seen in draft-ietf-hybi-permessage-compression-15. *

@@ -206,7 +211,7 @@ public class PerMessageDeflateExtensionTest extends AbstractExtensionTest public void testIncomingPing() { PerMessageDeflateExtension ext = new PerMessageDeflateExtension(); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); ext.setPolicy(WebSocketPolicy.newServerPolicy()); ExtensionConfig config = ExtensionConfig.parse("permessage-deflate"); ext.setConfig(config); @@ -243,7 +248,7 @@ public class PerMessageDeflateExtensionTest extends AbstractExtensionTest public void testIncomingUncompressedFrames() { PerMessageDeflateExtension ext = new PerMessageDeflateExtension(); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); ext.setPolicy(WebSocketPolicy.newServerPolicy()); ExtensionConfig config = ExtensionConfig.parse("permessage-deflate"); ext.setConfig(config); @@ -298,7 +303,7 @@ public class PerMessageDeflateExtensionTest extends AbstractExtensionTest public void testOutgoingPing() throws IOException { PerMessageDeflateExtension ext = new PerMessageDeflateExtension(); - ext.setBufferPool(new MappedByteBufferPool()); + ext.setBufferPool(bufferPool); ext.setPolicy(WebSocketPolicy.newServerPolicy()); ExtensionConfig config = ExtensionConfig.parse("permessage-deflate"); ext.setConfig(config); diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java index 2b5f4aec961..8d7b094ef69 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketConnection.java @@ -22,7 +22,6 @@ import java.net.InetSocketAddress; import java.util.concurrent.Executor; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.ExecutorThreadPool; @@ -49,22 +48,22 @@ public class LocalWebSocketConnection implements LogicalConnection, IncomingFram private IncomingFrames incoming; private IOState ioState = new IOState(); - public LocalWebSocketConnection() + public LocalWebSocketConnection(ByteBufferPool bufferPool) { - this("anon"); + this("anon",bufferPool); } - public LocalWebSocketConnection(String id) + public LocalWebSocketConnection(String id, ByteBufferPool bufferPool) { this.id = id; - this.bufferPool = new MappedByteBufferPool(); + this.bufferPool = bufferPool; this.executor = new ExecutorThreadPool(); this.ioState.addListener(this); } - public LocalWebSocketConnection(TestName testname) + public LocalWebSocketConnection(TestName testname, ByteBufferPool bufferPool) { - this(testname.getMethodName()); + this(testname.getMethodName(),bufferPool); } @Override diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketSession.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketSession.java index 1de1ced590c..6bf9224d1f6 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketSession.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/io/LocalWebSocketSession.java @@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.common.io; import java.net.URI; +import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.websocket.common.SessionListener; import org.eclipse.jetty.websocket.common.WebSocketSession; import org.eclipse.jetty.websocket.common.events.EventDriver; @@ -31,9 +32,9 @@ public class LocalWebSocketSession extends WebSocketSession private String id; private OutgoingFramesCapture outgoingCapture; - public LocalWebSocketSession(TestName testname, EventDriver driver) + public LocalWebSocketSession(TestName testname, EventDriver driver, ByteBufferPool bufferPool) { - super(URI.create("ws://localhost/LocalWebSocketSesssion/" + testname.getMethodName()),driver,new LocalWebSocketConnection(testname), new SessionListener[0]); + super(URI.create("ws://localhost/LocalWebSocketSesssion/" + testname.getMethodName()),driver,new LocalWebSocketConnection(testname,bufferPool), new SessionListener[0]); this.id = testname.getMethodName(); outgoingCapture = new OutgoingFramesCapture(); setOutgoingHandler(outgoingCapture); diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageInputStreamTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageInputStreamTest.java index 91cb416d0b7..e1dfc5f9d87 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageInputStreamTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageInputStreamTest.java @@ -27,8 +27,10 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.websocket.common.io.LocalWebSocketConnection; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; @@ -39,10 +41,13 @@ public class MessageInputStreamTest @Rule public TestName testname = new TestName(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + @Test(timeout=10000) public void testBasicAppendRead() throws IOException { - LocalWebSocketConnection conn = new LocalWebSocketConnection(testname); + LocalWebSocketConnection conn = new LocalWebSocketConnection(testname,bufferPool); try (MessageInputStream stream = new MessageInputStream(conn)) { @@ -65,7 +70,7 @@ public class MessageInputStreamTest @Test(timeout=5000) public void testBlockOnRead() throws Exception { - LocalWebSocketConnection conn = new LocalWebSocketConnection(testname); + LocalWebSocketConnection conn = new LocalWebSocketConnection(testname,bufferPool); try (MessageInputStream stream = new MessageInputStream(conn)) { @@ -116,7 +121,7 @@ public class MessageInputStreamTest @Test(timeout=10000) public void testBlockOnReadInitial() throws IOException { - LocalWebSocketConnection conn = new LocalWebSocketConnection(testname); + LocalWebSocketConnection conn = new LocalWebSocketConnection(testname,bufferPool); try (MessageInputStream stream = new MessageInputStream(conn)) { @@ -155,7 +160,7 @@ public class MessageInputStreamTest @Test(timeout=10000) public void testReadByteNoBuffersClosed() throws IOException { - LocalWebSocketConnection conn = new LocalWebSocketConnection(testname); + LocalWebSocketConnection conn = new LocalWebSocketConnection(testname,bufferPool); try (MessageInputStream stream = new MessageInputStream(conn)) { diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageOutputStreamTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageOutputStreamTest.java index 408b216e349..feb4a859928 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageOutputStreamTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageOutputStreamTest.java @@ -24,6 +24,7 @@ import static org.hamcrest.Matchers.is; import java.util.Arrays; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -33,6 +34,7 @@ import org.eclipse.jetty.websocket.common.events.EventDriver; import org.eclipse.jetty.websocket.common.events.EventDriverFactory; import org.eclipse.jetty.websocket.common.io.FramePipes; import org.eclipse.jetty.websocket.common.io.LocalWebSocketSession; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -50,6 +52,9 @@ public class MessageOutputStreamTest @Rule public TestName testname = new TestName(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + private WebSocketPolicy policy; private TrackingSocket socket; private LocalWebSocketSession session; @@ -77,7 +82,7 @@ public class MessageOutputStreamTest socket = new TrackingSocket("remote"); OutgoingFrames socketPipe = FramePipes.to(factory.wrap(socket)); - session = new LocalWebSocketSession(testname,driver); + session = new LocalWebSocketSession(testname,driver,bufferPool); session.setPolicy(policy); // talk to our remote socket diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageWriterTest.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageWriterTest.java index ec1ae223b0c..43f68ecf00a 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageWriterTest.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/message/MessageWriterTest.java @@ -22,6 +22,7 @@ import static org.hamcrest.Matchers.is; import java.util.Arrays; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -31,6 +32,7 @@ import org.eclipse.jetty.websocket.common.events.EventDriver; import org.eclipse.jetty.websocket.common.events.EventDriverFactory; import org.eclipse.jetty.websocket.common.io.FramePipes; import org.eclipse.jetty.websocket.common.io.LocalWebSocketSession; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.junit.After; import org.junit.Assert; import org.junit.Before; @@ -48,6 +50,9 @@ public class MessageWriterTest @Rule public TestName testname = new TestName(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + private WebSocketPolicy policy; private TrackingSocket socket; private LocalWebSocketSession session; @@ -75,7 +80,7 @@ public class MessageWriterTest socket = new TrackingSocket("remote"); OutgoingFrames socketPipe = FramePipes.to(factory.wrap(socket)); - session = new LocalWebSocketSession(testname,driver); + session = new LocalWebSocketSession(testname,driver,bufferPool); session.setPolicy(policy); // talk to our remote socket diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java index 21ab83e6050..349b7b49e20 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadClient.java @@ -133,6 +133,7 @@ public class BlockheadClient implements IncomingFrames, OutgoingFrames, Connecti LOG.debug("WebSocket URI: {}",destWebsocketURI); LOG.debug(" HTTP URI: {}",destHttpURI); + // This is a blockhead client, no point tracking leaks on this object. this.bufferPool = new MappedByteBufferPool(8192); this.generator = new Generator(policy,bufferPool); this.parser = new Parser(policy,bufferPool); diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java index c1c46a80cf7..aa23cbde085 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/BlockheadServer.java @@ -102,6 +102,7 @@ public class BlockheadServer this.policy = WebSocketPolicy.newServerPolicy(); this.policy.setMaxBinaryMessageSize(100000); this.policy.setMaxTextMessageSize(100000); + // This is a blockhead server connection, no point tracking leaks on this object. this.bufferPool = new MappedByteBufferPool(BUFFER_SIZE); this.parser = new Parser(policy,bufferPool); this.parseCount = new AtomicInteger(0); diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/LeakTrackingBufferPool.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/LeakTrackingBufferPool.java new file mode 100644 index 00000000000..76172d978ce --- /dev/null +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/LeakTrackingBufferPool.java @@ -0,0 +1,80 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.websocket.common.test; + +import static org.hamcrest.Matchers.*; + +import java.nio.ByteBuffer; +import java.util.concurrent.atomic.AtomicInteger; + +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.LeakTrackingByteBufferPool; +import org.eclipse.jetty.util.LeakDetector; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.junit.Assert; +import org.junit.rules.TestRule; +import org.junit.runner.Description; +import org.junit.runners.model.Statement; + +public class LeakTrackingBufferPool extends LeakTrackingByteBufferPool implements TestRule +{ + private static final Logger LOG = Log.getLogger(LeakTrackingBufferPool.class); + private final String id; + private AtomicInteger leakCount = new AtomicInteger(0); + + public LeakTrackingBufferPool(String id, ByteBufferPool delegate) + { + super(delegate); + this.id = id; + } + + @Override + protected void leaked(LeakDetector.LeakInfo leakInfo) + { + String msg = String.format("%s ByteBuffer %s leaked at:",id,leakInfo.getResourceDescription()); + LOG.warn(msg,leakInfo.getStackFrames()); + leakCount.incrementAndGet(); + } + + public void assertNoLeaks() + { + Assert.assertThat("Leak Count for [" + id + "]",leakCount.get(),is(0)); + } + + public void clearTracking() + { + leakCount.set(0); + } + + @Override + public Statement apply(final Statement statement, Description description) + { + return new Statement() + { + @Override + public void evaluate() throws Throwable + { + clearTracking(); + statement.evaluate(); + assertNoLeaks(); + } + }; + } +} diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitGenerator.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitGenerator.java index c4628982c46..1dcf19d9d6b 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitGenerator.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitGenerator.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.websocket.common.test; import java.nio.ByteBuffer; import java.util.List; +import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.log.Log; @@ -122,6 +123,11 @@ public class UnitGenerator extends Generator public UnitGenerator() { - super(WebSocketPolicy.newServerPolicy(),new MappedByteBufferPool()); + super(WebSocketPolicy.newServerPolicy(),new LeakTrackingBufferPool("UnitGenerator",new MappedByteBufferPool())); + } + + public UnitGenerator(ByteBufferPool bufferPool) + { + super(WebSocketPolicy.newServerPolicy(),bufferPool); } } diff --git a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitParser.java b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitParser.java index ac15144b0db..d6a34eaee20 100644 --- a/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitParser.java +++ b/jetty-websocket/websocket-common/src/test/java/org/eclipse/jetty/websocket/common/test/UnitParser.java @@ -20,7 +20,6 @@ package org.eclipse.jetty.websocket.common.test; import java.nio.ByteBuffer; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.util.log.StacklessLogging; import org.eclipse.jetty.websocket.api.WebSocketPolicy; @@ -33,14 +32,9 @@ public class UnitParser extends Parser this(WebSocketPolicy.newServerPolicy()); } - public UnitParser(ByteBufferPool bufferPool, WebSocketPolicy policy) - { - super(policy,bufferPool); - } - public UnitParser(WebSocketPolicy policy) { - this(new MappedByteBufferPool(),policy); + super(policy,new LeakTrackingBufferPool("UnitParser",new MappedByteBufferPool())); } private void parsePartial(ByteBuffer buf, int numBytes) diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java index bd026ced602..daf0c588a3f 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketHandler.java @@ -24,6 +24,8 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.websocket.api.WebSocketBehavior; @@ -55,10 +57,15 @@ public abstract class WebSocketHandler extends HandlerWrapper private final WebSocketServletFactory webSocketFactory; public WebSocketHandler() + { + this(new MappedByteBufferPool()); + } + + public WebSocketHandler(ByteBufferPool bufferPool) { WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER); configurePolicy(policy); - webSocketFactory = new WebSocketServerFactory(policy); + webSocketFactory = new WebSocketServerFactory(policy, bufferPool); addBean(webSocketFactory); } diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java index 6e2e624ffbe..46ab21e95d9 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java @@ -96,6 +96,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc private final String supportedVersions; private final WebSocketPolicy defaultPolicy; private final EventDriverFactory eventDriverFactory; + private final ByteBufferPool bufferPool; private final WebSocketExtensionFactory extensionFactory; private List sessionFactories; private Set openSessions = new CopyOnWriteArraySet<>(); @@ -111,6 +112,11 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc { this(policy,new MappedByteBufferPool()); } + + public WebSocketServerFactory(ByteBufferPool bufferPool) + { + this(WebSocketPolicy.newServerPolicy(),bufferPool); + } public WebSocketServerFactory(WebSocketPolicy policy, ByteBufferPool bufferPool) { @@ -121,7 +127,8 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc this.defaultPolicy = policy; this.eventDriverFactory = new EventDriverFactory(defaultPolicy); - this.extensionFactory = new WebSocketExtensionFactory(defaultPolicy,bufferPool); + this.bufferPool = bufferPool; + this.extensionFactory = new WebSocketExtensionFactory(defaultPolicy,this.bufferPool); this.sessionFactories = new ArrayList<>(); this.sessionFactories.add(new WebSocketSessionFactory(this)); this.creator = this; @@ -228,7 +235,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc @Override public WebSocketServletFactory createFactory(WebSocketPolicy policy) { - return new WebSocketServerFactory(policy); + return new WebSocketServerFactory(policy,bufferPool); } private WebSocketSession createSession(URI requestURI, EventDriver websocket, LogicalConnection connection) diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java index de3acbad635..76b7b06ad06 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeFilter.java @@ -31,6 +31,8 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.util.annotation.ManagedAttribute; @@ -77,7 +79,12 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter public WebSocketUpgradeFilter(WebSocketPolicy policy) { - factory = new WebSocketServerFactory(policy); + this(policy, new MappedByteBufferPool()); + } + + public WebSocketUpgradeFilter(WebSocketPolicy policy, ByteBufferPool bufferPool) + { + factory = new WebSocketServerFactory(policy,bufferPool); addBean(factory,true); } diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java index df406a3eb7d..c4bcc17ba2c 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketUpgradeHandlerWrapper.java @@ -24,6 +24,8 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.server.Request; import org.eclipse.jetty.server.handler.HandlerWrapper; import org.eclipse.jetty.websocket.server.pathmap.PathMappings; @@ -38,7 +40,12 @@ public class WebSocketUpgradeHandlerWrapper extends HandlerWrapper implements Ma public WebSocketUpgradeHandlerWrapper() { - factory = new WebSocketServerFactory(); + this(new MappedByteBufferPool()); + } + + public WebSocketUpgradeHandlerWrapper(ByteBufferPool bufferPool) + { + factory = new WebSocketServerFactory(bufferPool); } @Override diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java index f70c723806b..34b5e40ac31 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/WebSocketOverSSLTest.java @@ -24,10 +24,12 @@ import java.net.URI; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.toolchain.test.EventQueue; import org.eclipse.jetty.toolchain.test.TestTracker; import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.client.WebSocketClient; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.server.helper.CaptureSocket; import org.eclipse.jetty.websocket.server.helper.SessionServlet; import org.junit.AfterClass; @@ -41,6 +43,9 @@ public class WebSocketOverSSLTest @Rule public TestTracker tracker = new TestTracker(); + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + private static SimpleServletServer server; @BeforeClass @@ -64,7 +69,7 @@ public class WebSocketOverSSLTest public void testEcho() throws Exception { Assert.assertThat("server scheme",server.getServerUri().getScheme(),is("wss")); - WebSocketClient client = new WebSocketClient(server.getSslContextFactory()); + WebSocketClient client = new WebSocketClient(server.getSslContextFactory(),null,bufferPool); try { client.start(); @@ -102,7 +107,7 @@ public class WebSocketOverSSLTest public void testServerSessionIsSecure() throws Exception { Assert.assertThat("server scheme",server.getServerUri().getScheme(),is("wss")); - WebSocketClient client = new WebSocketClient(server.getSslContextFactory()); + WebSocketClient client = new WebSocketClient(server.getSslContextFactory(),null,bufferPool); try { client.setConnectTimeout(3000); @@ -140,7 +145,7 @@ public class WebSocketOverSSLTest public void testServerSessionRequestURI() throws Exception { Assert.assertThat("server scheme",server.getServerUri().getScheme(),is("wss")); - WebSocketClient client = new WebSocketClient(server.getSslContextFactory()); + WebSocketClient client = new WebSocketClient(server.getSslContextFactory(),null,bufferPool); try { client.setConnectTimeout(3000); diff --git a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java index 22609df6740..1b282b93026 100644 --- a/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java +++ b/jetty-websocket/websocket-server/src/test/java/org/eclipse/jetty/websocket/server/ab/AbstractABCase.java @@ -21,7 +21,6 @@ package org.eclipse.jetty.websocket.server.ab; import java.net.URI; import java.nio.ByteBuffer; -import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.StringUtil; @@ -31,9 +30,11 @@ import org.eclipse.jetty.websocket.common.Generator; import org.eclipse.jetty.websocket.common.OpCode; import org.eclipse.jetty.websocket.common.WebSocketFrame; import org.eclipse.jetty.websocket.common.test.Fuzzed; +import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPool; import org.eclipse.jetty.websocket.common.test.RawFrameBuilder; import org.eclipse.jetty.websocket.server.SimpleServletServer; import org.junit.AfterClass; +import org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import org.junit.rules.TestName; @@ -73,15 +74,17 @@ public abstract class AbstractABCase implements Fuzzed protected static final byte[] MASK = { 0x12, 0x34, 0x56, 0x78 }; - protected static Generator strictGenerator; - protected static Generator laxGenerator; + protected Generator strictGenerator; + protected Generator laxGenerator; protected static SimpleServletServer server; - @BeforeClass - public static void initGenerators() + @Rule + public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); + + @Before + public void initGenerators() { WebSocketPolicy policy = WebSocketPolicy.newClientPolicy(); - ByteBufferPool bufferPool = new MappedByteBufferPool(); strictGenerator = new Generator(policy,bufferPool,true); laxGenerator = new Generator(policy,bufferPool,false); } From f59b3db0e434b3ac5d9294444e8a8d4187dedf54 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Fri, 10 Jan 2014 16:25:38 +1100 Subject: [PATCH 03/54] 424562 JDBCSessionManager.setNodeIdInSessionId(true) does not work --- .../org/eclipse/jetty/server/session/JDBCSessionManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java index 784a20ff420..576cd07cd7c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java @@ -963,7 +963,7 @@ public class JDBCSessionManager extends AbstractSessionManager long now = System.currentTimeMillis(); connection.setAutoCommit(true); statement.setString(1, rowId); //rowId - statement.setString(2, session.getId()); //session id + statement.setString(2, session.getClusterId()); //session id statement.setString(3, session.getCanonicalContext()); //context path statement.setString(4, session.getVirtualHost()); //first vhost statement.setString(5, getSessionIdManager().getWorkerName());//my node id @@ -1011,7 +1011,7 @@ public class JDBCSessionManager extends AbstractSessionManager { long now = System.currentTimeMillis(); connection.setAutoCommit(true); - statement.setString(1, data.getId()); + statement.setString(1, data.getClusterId()); statement.setString(2, getSessionIdManager().getWorkerName());//my node id statement.setLong(3, data.getAccessed());//accessTime statement.setLong(4, data.getLastAccessedTime()); //lastAccessTime From a8b74ea9afb5fd46597ac55c4ea6014fceea13c0 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Mon, 13 Jan 2014 19:50:41 +1100 Subject: [PATCH 04/54] 425275 org.eclipse.jetty.osgi.annotations.AnnotationConfiguration.BundleParserTask.getStatistic() returns null when debug is enabled. --- .../annotations/AnnotationConfiguration.java | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java index 1f13fc1bf34..322aec55e7a 100644 --- a/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java +++ b/jetty-osgi/jetty-osgi-boot/src/main/java/org/eclipse/jetty/osgi/annotations/AnnotationConfiguration.java @@ -25,6 +25,8 @@ import org.eclipse.jetty.annotations.AnnotationParser.Handler; import org.eclipse.jetty.annotations.ClassNameResolver; import org.eclipse.jetty.osgi.boot.OSGiWebappConstants; import org.eclipse.jetty.osgi.boot.utils.internal.PackageAdminServiceTracker; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.resource.Resource; import org.eclipse.jetty.webapp.WebAppContext; import org.osgi.framework.Bundle; @@ -37,12 +39,14 @@ import org.osgi.framework.Constants; */ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.AnnotationConfiguration { + private static final Logger LOG = Log.getLogger(org.eclipse.jetty.annotations.AnnotationConfiguration.class); + public class BundleParserTask extends ParserTask { public BundleParserTask (AnnotationParser parser, Sethandlers, Resource resource, ClassNameResolver resolver) { - super(parser, handlers, resource, resolver); + super(parser, handlers, resource, resolver); } public Void call() throws Exception @@ -51,7 +55,11 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot { org.eclipse.jetty.osgi.annotations.AnnotationParser osgiAnnotationParser = (org.eclipse.jetty.osgi.annotations.AnnotationParser)_parser; Bundle bundle = osgiAnnotationParser.getBundle(_resource); + if (_stat != null) + _stat.start(); osgiAnnotationParser.parse(_handlers, bundle, _resolver); + if (_stat != null) + _stat.end(); } return null; } @@ -178,7 +186,12 @@ public class AnnotationConfiguration extends org.eclipse.jetty.annotations.Annot ClassNameResolver classNameResolver = createClassNameResolver(context); if (_parserTasks != null) - _parserTasks.add(new BundleParserTask(parser, handlers, bundleRes, classNameResolver)); + { + BundleParserTask task = new BundleParserTask(parser, handlers, bundleRes, classNameResolver); + _parserTasks.add(task); + if (LOG.isDebugEnabled()) + task.setStatistic(new TimeStatistic()); + } } /** From 1cad68b263188c9d4ecfb7e427f7711c73ff6c87 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Mon, 13 Jan 2014 19:13:19 -0700 Subject: [PATCH 05/54] Windows file mod (0755 to 0655) changes --- jetty-ant/pom.xml | 0 .../main/java/org/eclipse/jetty/ant/types/SystemProperties.java | 0 jetty-ant/src/main/resources/tasks.properties | 0 jetty-distribution/src/main/resources/bin/jetty-cygwin.sh | 0 jetty-distribution/src/main/resources/bin/jetty-xinetd.sh | 0 jetty-distribution/src/main/resources/bin/jetty.sh | 0 .../java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java | 0 jetty-util/src/test/resources/TestData/test/META-INF/MANIFEST.MF | 0 jetty-util/src/test/resources/TestData/test/alphabet | 0 jetty-util/src/test/resources/TestData/test/numbers | 0 jetty-util/src/test/resources/TestData/test/subdir/alphabet | 0 jetty-util/src/test/resources/TestData/test/subdir/numbers | 0 .../src/test/resources/TestData/test/subdir/subsubdir/alphabet | 0 .../src/test/resources/TestData/test/subdir/subsubdir/numbers | 0 settings.xml | 0 .../test-jetty-webapp/src/main/webapp/cgi-bin/hello.sh | 0 16 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 jetty-ant/pom.xml mode change 100755 => 100644 jetty-ant/src/main/java/org/eclipse/jetty/ant/types/SystemProperties.java mode change 100755 => 100644 jetty-ant/src/main/resources/tasks.properties mode change 100755 => 100644 jetty-distribution/src/main/resources/bin/jetty-cygwin.sh mode change 100755 => 100644 jetty-distribution/src/main/resources/bin/jetty-xinetd.sh mode change 100755 => 100644 jetty-distribution/src/main/resources/bin/jetty.sh mode change 100755 => 100644 jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java mode change 100755 => 100644 jetty-util/src/test/resources/TestData/test/META-INF/MANIFEST.MF mode change 100755 => 100644 jetty-util/src/test/resources/TestData/test/alphabet mode change 100755 => 100644 jetty-util/src/test/resources/TestData/test/numbers mode change 100755 => 100644 jetty-util/src/test/resources/TestData/test/subdir/alphabet mode change 100755 => 100644 jetty-util/src/test/resources/TestData/test/subdir/numbers mode change 100755 => 100644 jetty-util/src/test/resources/TestData/test/subdir/subsubdir/alphabet mode change 100755 => 100644 jetty-util/src/test/resources/TestData/test/subdir/subsubdir/numbers mode change 100755 => 100644 settings.xml mode change 100755 => 100644 tests/test-webapps/test-jetty-webapp/src/main/webapp/cgi-bin/hello.sh diff --git a/jetty-ant/pom.xml b/jetty-ant/pom.xml old mode 100755 new mode 100644 diff --git a/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/SystemProperties.java b/jetty-ant/src/main/java/org/eclipse/jetty/ant/types/SystemProperties.java old mode 100755 new mode 100644 diff --git a/jetty-ant/src/main/resources/tasks.properties b/jetty-ant/src/main/resources/tasks.properties old mode 100755 new mode 100644 diff --git a/jetty-distribution/src/main/resources/bin/jetty-cygwin.sh b/jetty-distribution/src/main/resources/bin/jetty-cygwin.sh old mode 100755 new mode 100644 diff --git a/jetty-distribution/src/main/resources/bin/jetty-xinetd.sh b/jetty-distribution/src/main/resources/bin/jetty-xinetd.sh old mode 100755 new mode 100644 diff --git a/jetty-distribution/src/main/resources/bin/jetty.sh b/jetty-distribution/src/main/resources/bin/jetty.sh old mode 100755 new mode 100644 diff --git a/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java b/jetty-util-ajax/src/main/java/org/eclipse/jetty/util/ajax/JSONObjectConvertor.java old mode 100755 new mode 100644 diff --git a/jetty-util/src/test/resources/TestData/test/META-INF/MANIFEST.MF b/jetty-util/src/test/resources/TestData/test/META-INF/MANIFEST.MF old mode 100755 new mode 100644 diff --git a/jetty-util/src/test/resources/TestData/test/alphabet b/jetty-util/src/test/resources/TestData/test/alphabet old mode 100755 new mode 100644 diff --git a/jetty-util/src/test/resources/TestData/test/numbers b/jetty-util/src/test/resources/TestData/test/numbers old mode 100755 new mode 100644 diff --git a/jetty-util/src/test/resources/TestData/test/subdir/alphabet b/jetty-util/src/test/resources/TestData/test/subdir/alphabet old mode 100755 new mode 100644 diff --git a/jetty-util/src/test/resources/TestData/test/subdir/numbers b/jetty-util/src/test/resources/TestData/test/subdir/numbers old mode 100755 new mode 100644 diff --git a/jetty-util/src/test/resources/TestData/test/subdir/subsubdir/alphabet b/jetty-util/src/test/resources/TestData/test/subdir/subsubdir/alphabet old mode 100755 new mode 100644 diff --git a/jetty-util/src/test/resources/TestData/test/subdir/subsubdir/numbers b/jetty-util/src/test/resources/TestData/test/subdir/subsubdir/numbers old mode 100755 new mode 100644 diff --git a/settings.xml b/settings.xml old mode 100755 new mode 100644 diff --git a/tests/test-webapps/test-jetty-webapp/src/main/webapp/cgi-bin/hello.sh b/tests/test-webapps/test-jetty-webapp/src/main/webapp/cgi-bin/hello.sh old mode 100755 new mode 100644 From 3a16944fd719d62bf614c23a192c8ecf46729a8a Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 14 Jan 2014 11:35:08 -0700 Subject: [PATCH 06/54] Fixing path based assertions to be OS neutral (windows) --- .../org/eclipse/jetty/start/BaseHomeTest.java | 299 +++++----- .../jetty/start/ConfigurationAssert.java | 548 +++++++++--------- 2 files changed, 428 insertions(+), 419 deletions(-) diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java b/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java index ce37b575c5d..ef3ac686e2b 100644 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/BaseHomeTest.java @@ -1,145 +1,154 @@ -// -// ======================================================================== -// Copyright (c) 1995-2014 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.start; - -import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.startsWith; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.jetty.toolchain.test.IO; -import org.eclipse.jetty.toolchain.test.MavenTestingUtils; -import org.junit.Assert; -import org.junit.Test; - -public class BaseHomeTest -{ - private void assertFileList(BaseHome hb, String message, List expected, List files) - { - List actual = new ArrayList<>(); - for (File file : files) - { - actual.add(hb.toShortForm(file)); - } - Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray())); - } - - @Test - public void testGetFile_OnlyHome() throws IOException - { - File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); - File baseDir = null; - - BaseHome hb = new BaseHome(homeDir,baseDir); - File startIni = hb.getFile("/start.ini"); - - String ref = hb.toShortForm(startIni); - Assert.assertThat("Reference",ref,startsWith("${jetty.home}")); - - String contents = IO.readToString(startIni); - Assert.assertThat("Contents",contents,containsString("Home Ini")); - } - - @Test - public void testListFiles_OnlyHome() throws IOException - { - File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); - File baseDir = null; - - BaseHome hb = new BaseHome(homeDir,baseDir); - List files = hb.listFiles("/start.d"); - - List expected = new ArrayList<>(); - expected.add("${jetty.home}/start.d/jmx.ini"); - expected.add("${jetty.home}/start.d/jndi.ini"); - expected.add("${jetty.home}/start.d/jsp.ini"); - expected.add("${jetty.home}/start.d/logging.ini"); - expected.add("${jetty.home}/start.d/ssl.ini"); - - assertFileList(hb,"Files found",expected,files); - } - - @Test - public void testListFiles_Filtered_OnlyHome() throws IOException - { - File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); - File baseDir = null; - - BaseHome hb = new BaseHome(homeDir,baseDir); - List files = hb.listFiles("/start.d",new FS.IniFilter()); - - List expected = new ArrayList<>(); - expected.add("${jetty.home}/start.d/jmx.ini"); - expected.add("${jetty.home}/start.d/jndi.ini"); - expected.add("${jetty.home}/start.d/jsp.ini"); - expected.add("${jetty.home}/start.d/logging.ini"); - expected.add("${jetty.home}/start.d/ssl.ini"); - - assertFileList(hb,"Files found",expected,files); - } - - @Test - public void testListFiles_Both() throws IOException - { - File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); - File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base"); - - BaseHome hb = new BaseHome(homeDir,baseDir); - List files = hb.listFiles("/start.d"); - - List expected = new ArrayList<>(); - expected.add("${jetty.base}/start.d/jmx.ini"); - expected.add("${jetty.home}/start.d/jndi.ini"); - expected.add("${jetty.home}/start.d/jsp.ini"); - expected.add("${jetty.base}/start.d/logging.ini"); - expected.add("${jetty.home}/start.d/ssl.ini"); - expected.add("${jetty.base}/start.d/myapp.ini"); - - assertFileList(hb,"Files found",expected,files); - } - - @Test - public void testDefault() throws IOException - { - BaseHome bh = new BaseHome(); - Assert.assertThat("Home",bh.getHome(),notNullValue()); - Assert.assertThat("Base",bh.getBase(),notNullValue()); - } - - @Test - public void testGetFile_Both() throws IOException - { - File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); - File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base"); - - BaseHome hb = new BaseHome(homeDir,baseDir); - File startIni = hb.getFile("/start.ini"); - - String ref = hb.toShortForm(startIni); - Assert.assertThat("Reference",ref,startsWith("${jetty.base}")); - - String contents = IO.readToString(startIni); - Assert.assertThat("Contents",contents,containsString("Base Ini")); - } -} +// +// ======================================================================== +// Copyright (c) 1995-2014 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.start; + +import static org.hamcrest.Matchers.*; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.toolchain.test.IO; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.junit.Assert; +import org.junit.Test; + +public class BaseHomeTest +{ + private void assertFileList(BaseHome hb, String message, List expected, List files) + { + List actual = new ArrayList<>(); + for (File file : files) + { + actual.add(hb.toShortForm(file)); + } + Assert.assertThat(message + ": " + Main.join(actual,", "),actual,containsInAnyOrder(expected.toArray())); + } + + private void toOsSeparators(List expected) + { + for (int i = 0; i < expected.size(); i++) + { + String fixed = FS.separators(expected.get(i)); + expected.set(i,fixed); + } + } + + @Test + public void testGetFile_OnlyHome() throws IOException + { + File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); + File baseDir = null; + + BaseHome hb = new BaseHome(homeDir,baseDir); + File startIni = hb.getFile("/start.ini"); + + String ref = hb.toShortForm(startIni); + Assert.assertThat("Reference",ref,startsWith("${jetty.home}")); + + String contents = IO.readToString(startIni); + Assert.assertThat("Contents",contents,containsString("Home Ini")); + } + + @Test + public void testListFiles_OnlyHome() throws IOException + { + File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); + File baseDir = null; + + BaseHome hb = new BaseHome(homeDir,baseDir); + List files = hb.listFiles("/start.d"); + + List expected = new ArrayList<>(); + expected.add("${jetty.home}/start.d/jmx.ini"); + expected.add("${jetty.home}/start.d/jndi.ini"); + expected.add("${jetty.home}/start.d/jsp.ini"); + expected.add("${jetty.home}/start.d/logging.ini"); + expected.add("${jetty.home}/start.d/ssl.ini"); + toOsSeparators(expected); + + assertFileList(hb,"Files found",expected,files); + } + + @Test + public void testListFiles_Filtered_OnlyHome() throws IOException + { + File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); + File baseDir = null; + + BaseHome hb = new BaseHome(homeDir,baseDir); + List files = hb.listFiles("/start.d",new FS.IniFilter()); + + List expected = new ArrayList<>(); + expected.add("${jetty.home}/start.d/jmx.ini"); + expected.add("${jetty.home}/start.d/jndi.ini"); + expected.add("${jetty.home}/start.d/jsp.ini"); + expected.add("${jetty.home}/start.d/logging.ini"); + expected.add("${jetty.home}/start.d/ssl.ini"); + toOsSeparators(expected); + + assertFileList(hb,"Files found",expected,files); + } + + @Test + public void testListFiles_Both() throws IOException + { + File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); + File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base"); + + BaseHome hb = new BaseHome(homeDir,baseDir); + List files = hb.listFiles("/start.d"); + + List expected = new ArrayList<>(); + expected.add("${jetty.base}/start.d/jmx.ini"); + expected.add("${jetty.home}/start.d/jndi.ini"); + expected.add("${jetty.home}/start.d/jsp.ini"); + expected.add("${jetty.base}/start.d/logging.ini"); + expected.add("${jetty.home}/start.d/ssl.ini"); + expected.add("${jetty.base}/start.d/myapp.ini"); + toOsSeparators(expected); + + assertFileList(hb,"Files found",expected,files); + } + + @Test + public void testDefault() throws IOException + { + BaseHome bh = new BaseHome(); + Assert.assertThat("Home",bh.getHome(),notNullValue()); + Assert.assertThat("Base",bh.getBase(),notNullValue()); + } + + @Test + public void testGetFile_Both() throws IOException + { + File homeDir = MavenTestingUtils.getTestResourceDir("hb.1/home"); + File baseDir = MavenTestingUtils.getTestResourceDir("hb.1/base"); + + BaseHome hb = new BaseHome(homeDir,baseDir); + File startIni = hb.getFile("/start.ini"); + + String ref = hb.toShortForm(startIni); + Assert.assertThat("Reference",ref,startsWith("${jetty.base}")); + + String contents = IO.readToString(startIni); + Assert.assertThat("Contents",contents,containsString("Base Ini")); + } +} diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java b/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java index f54a3ce01d6..f3ba3a86df0 100644 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/ConfigurationAssert.java @@ -1,274 +1,274 @@ -// -// ======================================================================== -// Copyright (c) 1995-2014 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.start; - -import static org.hamcrest.Matchers.*; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -import org.eclipse.jetty.start.Props.Prop; -import org.eclipse.jetty.toolchain.test.MavenTestingUtils; -import org.junit.Assert; - -public class ConfigurationAssert -{ - /** - * Given a provided StartArgs, assert that the configuration it has determined is valid based on values in a assert text file. - * - * @param baseHome - * the BaseHome used. Access it via {@link Main#getBaseHome()} - * @param args - * the StartArgs that has been processed via {@link Main#processCommandLine(String[])} - * @param filename - * the filename of the assertion values - * @throws IOException - */ - public static void assertConfiguration(BaseHome baseHome, StartArgs args, String filename) throws FileNotFoundException, IOException - { - File testResourcesDir = MavenTestingUtils.getTestResourcesDir(); - File file = MavenTestingUtils.getTestResourceFile(filename); - TextFile textFile = new TextFile(file); - - // Validate XMLs (order is important) - List expectedXmls = new ArrayList<>(); - for (String line : textFile) - { - if (line.startsWith("XML|")) - { - expectedXmls.add(getValue(line)); - } - } - List actualXmls = new ArrayList<>(); - for (File xml : args.getXmlFiles()) - { - actualXmls.add(shorten(baseHome,xml,testResourcesDir)); - } - assertOrdered("XML Resolution Order",expectedXmls,actualXmls); - - // Validate LIBs (order is not important) - List expectedLibs = new ArrayList<>(); - for (String line : textFile) - { - if (line.startsWith("LIB|")) - { - expectedLibs.add(getValue(line)); - } - } - List actualLibs = new ArrayList<>(); - for (File path : args.getClasspath()) - { - actualLibs.add(shorten(baseHome,path,testResourcesDir)); - } - assertContainsUnordered("Libs",expectedLibs,actualLibs); - - // Validate PROPERTIES (order is not important) - Set expectedProperties = new HashSet<>(); - for (String line : textFile) - { - if (line.startsWith("PROP|")) - { - expectedProperties.add(getValue(line)); - } - } - List actualProperties = new ArrayList<>(); - for (Prop prop : args.getProperties()) - { - String name = prop.key; - if ("jetty.home".equals(name) || "jetty.base".equals(name) || prop.origin.equals(Props.ORIGIN_SYSPROP)) - { - // strip these out from assertion, to make assertions easier. - continue; - } - actualProperties.add(prop.key + "=" + args.getProperties().expand(prop.value)); - } - assertContainsUnordered("Properties",expectedProperties,actualProperties); - - // Validate Downloads - List expectedDownloads = new ArrayList<>(); - for (String line : textFile) - { - if (line.startsWith("DOWNLOAD|")) - { - expectedDownloads.add(getValue(line)); - } - } - List actualDownloads = new ArrayList<>(); - for (FileArg darg : args.getFiles()) - { - if (darg.uri != null) - { - actualDownloads.add(String.format("%s:%s",darg.uri,darg.location)); - } - } - assertContainsUnordered("Downloads",expectedDownloads,actualDownloads); - - // Validate Files/Dirs creation - List expectedFiles = new ArrayList<>(); - for(String line: textFile) - { - if(line.startsWith("FILE|")) - { - expectedFiles.add(getValue(line)); - } - } - List actualFiles = new ArrayList<>(); - for(FileArg farg: args.getFiles()) - { - if(farg.uri == null) - { - actualFiles.add(farg.location); - } - } - assertContainsUnordered("Files/Dirs",expectedFiles,actualFiles); - } - - private static String shorten(BaseHome baseHome, File path, File testResourcesDir) - { - String value = baseHome.toShortForm(path); - if (value.startsWith(testResourcesDir.getAbsolutePath())) - { - int len = testResourcesDir.getAbsolutePath().length(); - value = "${maven-test-resources}" + value.substring(len); - } - return value; - } - - private static void assertContainsUnordered(String msg, Collection expectedSet, Collection actualSet) - { - // same size? - boolean mismatch = expectedSet.size() != actualSet.size(); - - // test content - Set missing = new HashSet<>(); - for (String expected : expectedSet) - { - if (!actualSet.contains(expected)) - { - missing.add(expected); - } - } - - if (mismatch || missing.size() > 0) - { - // build up detailed error message - StringWriter message = new StringWriter(); - PrintWriter err = new PrintWriter(message); - - err.printf("%s: Assert Contains (Unordered)",msg); - if (mismatch) - { - err.print(" [size mismatch]"); - } - if (missing.size() >= 0) - { - err.printf(" [%d entries missing]",missing.size()); - } - err.println(); - err.printf("Actual Entries (size: %d)%n",actualSet.size()); - for (String actual : actualSet) - { - char indicator = expectedSet.contains(actual)?' ':'>'; - err.printf("%s| %s%n",indicator,actual); - } - err.printf("Expected Entries (size: %d)%n",expectedSet.size()); - for (String expected : expectedSet) - { - char indicator = actualSet.contains(expected)?' ':'>'; - err.printf("%s| %s%n",indicator,expected); - } - err.flush(); - Assert.fail(message.toString()); - } - } - - private static void assertOrdered(String msg, List expectedList, List actualList) - { - // same size? - boolean mismatch = expectedList.size() != actualList.size(); - - // test content - List badEntries = new ArrayList<>(); - int min = Math.min(expectedList.size(),actualList.size()); - int max = Math.max(expectedList.size(),actualList.size()); - for (int i = 0; i < min; i++) - { - if (!expectedList.get(i).equals(actualList.get(i))) - { - badEntries.add(i); - } - } - for (int i = min; i < max; i++) - { - badEntries.add(i); - } - - if (mismatch || badEntries.size() > 0) - { - // build up detailed error message - StringWriter message = new StringWriter(); - PrintWriter err = new PrintWriter(message); - - err.printf("%s: Assert Contains (Unordered)",msg); - if (mismatch) - { - err.print(" [size mismatch]"); - } - if (badEntries.size() >= 0) - { - err.printf(" [%d entries not matched]",badEntries.size()); - } - err.println(); - err.printf("Actual Entries (size: %d)%n",actualList.size()); - for (int i = 0; i < actualList.size(); i++) - { - String actual = actualList.get(i); - char indicator = badEntries.contains(i)?'>':' '; - err.printf("%s[%d] %s%n",indicator,i,actual); - } - - err.printf("Expected Entries (size: %d)%n",expectedList.size()); - for (int i = 0; i < expectedList.size(); i++) - { - String expected = expectedList.get(i); - char indicator = badEntries.contains(i)?'>':' '; - err.printf("%s[%d] %s%n",indicator,i,expected); - } - err.flush(); - Assert.fail(message.toString()); - } - } - - private static String getValue(String arg) - { - int idx = arg.indexOf('|'); - Assert.assertThat("Expecting '|' sign in [" + arg + "]",idx,greaterThanOrEqualTo(0)); - String value = arg.substring(idx + 1).trim(); - Assert.assertThat("Expecting Value after '|' in [" + arg + "]",value.length(),greaterThan(0)); - return value; - } -} +// +// ======================================================================== +// Copyright (c) 1995-2014 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.start; + +import static org.hamcrest.Matchers.*; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import org.eclipse.jetty.start.Props.Prop; +import org.eclipse.jetty.toolchain.test.MavenTestingUtils; +import org.junit.Assert; + +public class ConfigurationAssert +{ + /** + * Given a provided StartArgs, assert that the configuration it has determined is valid based on values in a assert text file. + * + * @param baseHome + * the BaseHome used. Access it via {@link Main#getBaseHome()} + * @param args + * the StartArgs that has been processed via {@link Main#processCommandLine(String[])} + * @param filename + * the filename of the assertion values + * @throws IOException + */ + public static void assertConfiguration(BaseHome baseHome, StartArgs args, String filename) throws FileNotFoundException, IOException + { + File testResourcesDir = MavenTestingUtils.getTestResourcesDir(); + File file = MavenTestingUtils.getTestResourceFile(filename); + TextFile textFile = new TextFile(file); + + // Validate XMLs (order is important) + List expectedXmls = new ArrayList<>(); + for (String line : textFile) + { + if (line.startsWith("XML|")) + { + expectedXmls.add(FS.separators(getValue(line))); + } + } + List actualXmls = new ArrayList<>(); + for (File xml : args.getXmlFiles()) + { + actualXmls.add(shorten(baseHome,xml,testResourcesDir)); + } + assertOrdered("XML Resolution Order",expectedXmls,actualXmls); + + // Validate LIBs (order is not important) + List expectedLibs = new ArrayList<>(); + for (String line : textFile) + { + if (line.startsWith("LIB|")) + { + expectedLibs.add(FS.separators(getValue(line))); + } + } + List actualLibs = new ArrayList<>(); + for (File path : args.getClasspath()) + { + actualLibs.add(shorten(baseHome,path,testResourcesDir)); + } + assertContainsUnordered("Libs",expectedLibs,actualLibs); + + // Validate PROPERTIES (order is not important) + Set expectedProperties = new HashSet<>(); + for (String line : textFile) + { + if (line.startsWith("PROP|")) + { + expectedProperties.add(getValue(line)); + } + } + List actualProperties = new ArrayList<>(); + for (Prop prop : args.getProperties()) + { + String name = prop.key; + if ("jetty.home".equals(name) || "jetty.base".equals(name) || prop.origin.equals(Props.ORIGIN_SYSPROP)) + { + // strip these out from assertion, to make assertions easier. + continue; + } + actualProperties.add(prop.key + "=" + args.getProperties().expand(prop.value)); + } + assertContainsUnordered("Properties",expectedProperties,actualProperties); + + // Validate Downloads + List expectedDownloads = new ArrayList<>(); + for (String line : textFile) + { + if (line.startsWith("DOWNLOAD|")) + { + expectedDownloads.add(getValue(line)); + } + } + List actualDownloads = new ArrayList<>(); + for (FileArg darg : args.getFiles()) + { + if (darg.uri != null) + { + actualDownloads.add(String.format("%s:%s",darg.uri,darg.location)); + } + } + assertContainsUnordered("Downloads",expectedDownloads,actualDownloads); + + // Validate Files/Dirs creation + List expectedFiles = new ArrayList<>(); + for(String line: textFile) + { + if(line.startsWith("FILE|")) + { + expectedFiles.add(getValue(line)); + } + } + List actualFiles = new ArrayList<>(); + for(FileArg farg: args.getFiles()) + { + if(farg.uri == null) + { + actualFiles.add(farg.location); + } + } + assertContainsUnordered("Files/Dirs",expectedFiles,actualFiles); + } + + private static String shorten(BaseHome baseHome, File path, File testResourcesDir) + { + String value = baseHome.toShortForm(path); + if (value.startsWith(testResourcesDir.getAbsolutePath())) + { + int len = testResourcesDir.getAbsolutePath().length(); + value = "${maven-test-resources}" + value.substring(len); + } + return value; + } + + private static void assertContainsUnordered(String msg, Collection expectedSet, Collection actualSet) + { + // same size? + boolean mismatch = expectedSet.size() != actualSet.size(); + + // test content + Set missing = new HashSet<>(); + for (String expected : expectedSet) + { + if (!actualSet.contains(expected)) + { + missing.add(expected); + } + } + + if (mismatch || missing.size() > 0) + { + // build up detailed error message + StringWriter message = new StringWriter(); + PrintWriter err = new PrintWriter(message); + + err.printf("%s: Assert Contains (Unordered)",msg); + if (mismatch) + { + err.print(" [size mismatch]"); + } + if (missing.size() >= 0) + { + err.printf(" [%d entries missing]",missing.size()); + } + err.println(); + err.printf("Actual Entries (size: %d)%n",actualSet.size()); + for (String actual : actualSet) + { + char indicator = expectedSet.contains(actual)?' ':'>'; + err.printf("%s| %s%n",indicator,actual); + } + err.printf("Expected Entries (size: %d)%n",expectedSet.size()); + for (String expected : expectedSet) + { + char indicator = actualSet.contains(expected)?' ':'>'; + err.printf("%s| %s%n",indicator,expected); + } + err.flush(); + Assert.fail(message.toString()); + } + } + + private static void assertOrdered(String msg, List expectedList, List actualList) + { + // same size? + boolean mismatch = expectedList.size() != actualList.size(); + + // test content + List badEntries = new ArrayList<>(); + int min = Math.min(expectedList.size(),actualList.size()); + int max = Math.max(expectedList.size(),actualList.size()); + for (int i = 0; i < min; i++) + { + if (!expectedList.get(i).equals(actualList.get(i))) + { + badEntries.add(i); + } + } + for (int i = min; i < max; i++) + { + badEntries.add(i); + } + + if (mismatch || badEntries.size() > 0) + { + // build up detailed error message + StringWriter message = new StringWriter(); + PrintWriter err = new PrintWriter(message); + + err.printf("%s: Assert Contains (Unordered)",msg); + if (mismatch) + { + err.print(" [size mismatch]"); + } + if (badEntries.size() >= 0) + { + err.printf(" [%d entries not matched]",badEntries.size()); + } + err.println(); + err.printf("Actual Entries (size: %d)%n",actualList.size()); + for (int i = 0; i < actualList.size(); i++) + { + String actual = actualList.get(i); + char indicator = badEntries.contains(i)?'>':' '; + err.printf("%s[%d] %s%n",indicator,i,actual); + } + + err.printf("Expected Entries (size: %d)%n",expectedList.size()); + for (int i = 0; i < expectedList.size(); i++) + { + String expected = expectedList.get(i); + char indicator = badEntries.contains(i)?'>':' '; + err.printf("%s[%d] %s%n",indicator,i,expected); + } + err.flush(); + Assert.fail(message.toString()); + } + } + + private static String getValue(String arg) + { + int idx = arg.indexOf('|'); + Assert.assertThat("Expecting '|' sign in [" + arg + "]",idx,greaterThanOrEqualTo(0)); + String value = arg.substring(idx + 1).trim(); + Assert.assertThat("Expecting Value after '|' in [" + arg + "]",value.length(),greaterThan(0)); + return value; + } +} From dec67094bfbf6ef70011a8299c3e6b77b975ac83 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 14 Jan 2014 11:43:21 -0700 Subject: [PATCH 07/54] 425696 - start.jar --add-to-start={module} results in error --- jetty-start/src/main/java/org/eclipse/jetty/start/Props.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java index 6c3b48bf2b1..838cf4644c9 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Props.java @@ -98,8 +98,9 @@ public final class Props implements Iterable if (props.isEmpty()) { - // This is a stupid programming error, we should have something, even system properties - throw new PropsException("Props is empty: no properties declared!?"); + // nothing to expand + // this situation can occur from --add-to-startd on a new blank base directory + return str; } Pattern pat = Pattern.compile("(?<=[^$]|^)(\\$\\{[^}]*\\})"); From 99b5b61b0b87e9e6bffb80a7ba349f85b34e901a Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 14 Jan 2014 21:30:28 +0100 Subject: [PATCH 08/54] Simplified handling of "Connection: close" response header. --- .../client/http/HttpChannelOverHTTP.java | 29 ++++--------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java index d473b619c52..76d99d74bda 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpChannelOverHTTP.java @@ -18,8 +18,6 @@ package org.eclipse.jetty.client.http; -import java.util.Enumeration; - import org.eclipse.jetty.client.HttpChannel; import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.api.Result; @@ -79,28 +77,13 @@ public class HttpChannelOverHTTP extends HttpChannel public void exchangeTerminated(Result result) { super.exchangeTerminated(result); - - if (result.isSucceeded()) - { - HttpFields responseHeaders = result.getResponse().getHeaders(); - Enumeration values = responseHeaders.getValues(HttpHeader.CONNECTION.asString(), ","); - if (values != null) - { - while (values.hasMoreElements()) - { - if (HttpHeaderValue.CLOSE.asString().equalsIgnoreCase(values.nextElement())) - { - connection.close(); - return; - } - } - } - connection.release(); - } - else - { + boolean close = result.isFailed(); + HttpFields responseHeaders = result.getResponse().getHeaders(); + close |= responseHeaders.contains(HttpHeader.CONNECTION, HttpHeaderValue.CLOSE.asString()); + if (close) connection.close(); - } + else + connection.release(); } @Override From c06e65e798e064cfe588fc3b219e3412dd05d89b Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 14 Jan 2014 21:31:15 +0100 Subject: [PATCH 09/54] Improved dump() output. --- .../java/org/eclipse/jetty/spdy/client/SPDYClient.java | 7 +++++++ .../jetty/spdy/server/SPDYServerConnectionFactory.java | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java index 3f05b1bd9ef..678aa2aac60 100644 --- a/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java +++ b/jetty-spdy/spdy-client/src/main/java/org/eclipse/jetty/spdy/client/SPDYClient.java @@ -392,6 +392,13 @@ public class SPDYClient return Collections.unmodifiableCollection(sessions); } + @Override + protected void dumpThis(Appendable out) throws IOException + { + super.dumpThis(out); + dump(out, "", sessions); + } + private class ClientSelectorManager extends SelectorManager { private ClientSelectorManager(Executor executor, Scheduler scheduler) diff --git a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnectionFactory.java b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnectionFactory.java index adcfc2ef5cb..36eaec1a0a8 100644 --- a/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnectionFactory.java +++ b/jetty-spdy/spdy-server/src/main/java/org/eclipse/jetty/spdy/server/SPDYServerConnectionFactory.java @@ -18,6 +18,7 @@ package org.eclipse.jetty.spdy.server; +import java.io.IOException; import java.util.Collection; import java.util.Collections; import java.util.Queue; @@ -182,6 +183,13 @@ public class SPDYServerConnectionFactory extends AbstractConnectionFactory return Collections.unmodifiableCollection(sessions); } + @Override + protected void dumpThis(Appendable out) throws IOException + { + super.dumpThis(out); + dump(out, "", sessions); + } + private class ServerSPDYConnection extends SPDYConnection implements Runnable { private final ServerSessionFrameListener listener; From 9f786402fd9a196817f8db791c89b1d7a03455a9 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 14 Jan 2014 21:32:07 +0100 Subject: [PATCH 10/54] Added TODO, reminding to notify failure outside sync blocks. --- .../spdy-core/src/main/java/org/eclipse/jetty/spdy/Flusher.java | 1 + 1 file changed, 1 insertion(+) diff --git a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Flusher.java b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Flusher.java index 31d762fc0e1..6fb5a78cc1b 100644 --- a/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Flusher.java +++ b/jetty-spdy/spdy-core/src/main/java/org/eclipse/jetty/spdy/Flusher.java @@ -180,6 +180,7 @@ public class Flusher // Has the stream been reset for this data frame ? if (stream != null && stream.isReset() && frameBytes instanceof StandardSession.DataFrameBytes) { + // TODO: notify from within sync block ! frameBytes.failed(new StreamException(frameBytes.getStream().getId(), StreamStatus.INVALID_STREAM, "Stream: " + frameBytes.getStream() + " is reset!")); continue; From 1286363dbf16c5e05c6d47c983bad33e3e8eb1ac Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 14 Jan 2014 21:34:34 +0100 Subject: [PATCH 11/54] Improved SPDY load test. --- .../spdy/server/SynDataReplyDataLoadTest.java | 33 +++++++++++-------- .../test/resources/jetty-logging.properties | 2 +- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SynDataReplyDataLoadTest.java b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SynDataReplyDataLoadTest.java index e1a0f9ca63b..a9342e809ac 100644 --- a/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SynDataReplyDataLoadTest.java +++ b/jetty-spdy/spdy-server/src/test/java/org/eclipse/jetty/spdy/server/SynDataReplyDataLoadTest.java @@ -57,12 +57,12 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.util.thread.Scheduler; import org.junit.Assert; -import org.junit.Test; import org.junit.Ignore; +import org.junit.Test; public class SynDataReplyDataLoadTest extends AbstractTest { - private static final int TIMEOUT = 60000; + private static final int TIMEOUT = 60 * 1000; private static final Logger logger = Log.getLogger(SynDataReplyDataLoadTest.class); @Test(timeout = TIMEOUT) @@ -104,14 +104,20 @@ public class SynDataReplyDataLoadTest extends AbstractTest }; } }; + + short spdyVersion = SPDY.V2; + long idleTimeout = 2 * TIMEOUT; + server = newServer(); connector = new ServerConnector(server, null, null, serverBufferPool, 1, - Runtime.getRuntime().availableProcessors() / 2, new SPDYServerConnectionFactory(SPDY.V3, listener)); + Math.max(1, Runtime.getRuntime().availableProcessors() / 2), + new SPDYServerConnectionFactory(spdyVersion, listener)); + connector.setIdleTimeout(idleTimeout); QueuedThreadPool clientExecutor = new QueuedThreadPool(); clientExecutor.setName(clientExecutor.getName() + "-client"); - clientFactory = new SPDYClient.Factory(clientExecutor, null, clientBufferPool, null, 30000); - final Session session = startClient(SPDY.V3, startServer(SPDY.V3, listener), null); + clientFactory = new SPDYClient.Factory(clientExecutor, null, clientBufferPool, null, idleTimeout); + final Session session = startClient(spdyVersion, startServer(spdyVersion, listener), null); final Thread testThread = Thread.currentThread(); Runnable timeout = new Runnable() @@ -162,7 +168,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest } }); } - Scheduler.Task timeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS); + Scheduler.Task syncTimeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS); { long begin = System.nanoTime(); List> futures = threadPool.invokeAll(tasks); @@ -172,7 +178,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest long end = System.nanoTime(); System.err.printf("SYN+GET+DATA+GET completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin)); } - timeoutTask.cancel(); + syncTimeoutTask.cancel(); tasks.clear(); for (int i = 0; i < count; ++i) @@ -187,7 +193,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest } }); } - timeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS); + Scheduler.Task asyncTimeoutTask = clientFactory.getScheduler().schedule(timeout, TIMEOUT / 2, TimeUnit.MILLISECONDS); { long begin = System.nanoTime(); List> futures = threadPool.invokeAll(tasks); @@ -197,7 +203,8 @@ public class SynDataReplyDataLoadTest extends AbstractTest long end = System.nanoTime(); System.err.printf("SYN+COMPLETED+DATA completed in %d ms%n", TimeUnit.NANOSECONDS.toMillis(end - begin)); } - timeoutTask.cancel(); + asyncTimeoutTask.cancel(); + threadPool.shutdown(); Assert.assertEquals(0, leaks.get()); @@ -206,7 +213,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest private void synCompletedData(Session session, Fields headers, int iterations) throws Exception { final Map counter = new ConcurrentHashMap<>(iterations); - final CountDownLatch latch = new CountDownLatch(2 * iterations); + final CountDownLatch requestsLatch = new CountDownLatch(2 * iterations); for (int i = 0; i < iterations; ++i) { final AtomicInteger count = new AtomicInteger(2); @@ -218,7 +225,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest public void onReply(Stream stream, ReplyInfo replyInfo) { Assert.assertEquals(2, count.getAndDecrement()); - latch.countDown(); + requestsLatch.countDown(); } @Override @@ -230,7 +237,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest { Assert.assertEquals(1, count.getAndDecrement()); counter.remove(index); - latch.countDown(); + requestsLatch.countDown(); } } }, new Promise.Adapter() @@ -244,7 +251,7 @@ public class SynDataReplyDataLoadTest extends AbstractTest } ); } - Assert.assertTrue(latch.await(iterations, TimeUnit.SECONDS)); + Assert.assertTrue(requestsLatch.await(iterations, TimeUnit.SECONDS)); Assert.assertTrue(counter.toString(), counter.isEmpty()); } diff --git a/jetty-spdy/spdy-server/src/test/resources/jetty-logging.properties b/jetty-spdy/spdy-server/src/test/resources/jetty-logging.properties index 5250a08562a..ead13ec1970 100644 --- a/jetty-spdy/spdy-server/src/test/resources/jetty-logging.properties +++ b/jetty-spdy/spdy-server/src/test/resources/jetty-logging.properties @@ -1,2 +1,2 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog -org.eclipse.jetty.spdy.LEVEL=WARN +#org.eclipse.jetty.spdy.LEVEL=DEBUG From 10dd14d34eb2117bb2fb0f91f3c55658dee44224 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 14 Jan 2014 22:10:28 +0100 Subject: [PATCH 12/54] Reorganization of the code in preview of a larger refactoring. --- .../org/eclipse/jetty/server/HttpInput.java | 136 ++++++++---------- .../eclipse/jetty/server/QueuedHttpInput.java | 63 ++++---- 2 files changed, 87 insertions(+), 112 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java index 04c151e5f6e..7506e6010f9 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java @@ -19,7 +19,6 @@ package org.eclipse.jetty.server; import java.io.IOException; - import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; @@ -30,37 +29,19 @@ import org.eclipse.jetty.util.log.Logger; /** *

{@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.

- *

{@link HttpInput} holds a queue of items passed to it by calls to {@link #content(T)}.

- *

{@link HttpInput} stores the items directly; if the items contain byte buffers, it does not copy them - * but simply holds references to the item, thus the caller must organize for those buffers to valid while - * held by this class.

- *

To assist the caller, subclasses may override methods {@link #onContentQueued(T)}, - * {@link #onContentConsumed(T)} and {@link #onAllContentConsumed()} that can be implemented so that the - * caller will know when buffers are queued and consumed.

- */ -/** - * @author gregw - * - * @param - */ -/** - * @author gregw - * - * @param */ public abstract class HttpInput extends ServletInputStream implements Runnable { private final static Logger LOG = Log.getLogger(HttpInput.class); private final byte[] _oneByteBuffer = new byte[1]; - private HttpChannelState _channelState; - private Throwable _onError; - private ReadListener _listener; - private boolean _notReady; - - protected State _state = BLOCKING; - private State _eof=null; private final Object _lock; + private HttpChannelState _channelState; + private ReadListener _listener; + private Throwable _onError; + private boolean _notReady; + private State _state = BLOCKING; + private State _eof; private long _contentRead; protected HttpInput() @@ -70,7 +51,15 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl protected HttpInput(Object lock) { - _lock=lock==null?this:lock; + _lock = lock == null ? this : lock; + } + + public void init(HttpChannelState state) + { + synchronized (lock()) + { + _channelState = state; + } } public final Object lock() @@ -89,43 +78,6 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl } } - /** - * Access the next content to be consumed from. Returning the next item does not consume it - * and it may be returned multiple times until it is consumed. Calls to {@link #get(Object, byte[], int, int)} - * or {@link #consume(Object, int)} are required to consume data from the content. - * @return Content or null if none available. - * @throws IOException - */ - protected abstract T nextContent() throws IOException; - - /** - * A convenience method to call nextContent and to check the return value, which if null then the - * a check is made for EOF and the state changed accordingly. - * @see #nextContent() - * @return Content or null if none available. - * @throws IOException - */ - protected T getNextContent() throws IOException - { - T content=nextContent(); - - if (content==null && _eof!=null) - { - LOG.debug("{} eof {}",this,_eof); - _state=_eof; - _eof=null; - } - - return content; - } - - @Override - public int read() throws IOException - { - int read = read(_oneByteBuffer, 0, 1); - return read < 0 ? -1 : 0xff & _oneByteBuffer[0]; - } - @Override public int available() { @@ -143,6 +95,13 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl } } + @Override + public int read() throws IOException + { + int read = read(_oneByteBuffer, 0, 1); + return read < 0 ? -1 : 0xff & _oneByteBuffer[0]; + } + @Override public int read(byte[] b, int off, int len) throws IOException { @@ -171,6 +130,36 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl return l; } + /** + * A convenience method to call nextContent and to check the return value, which if null then the + * a check is made for EOF and the state changed accordingly. + * @see #nextContent() + * @return Content or null if none available. + * @throws IOException + */ + protected T getNextContent() throws IOException + { + T content=nextContent(); + + if (content==null && _eof!=null) + { + LOG.debug("{} eof {}",this,_eof); + _state=_eof; + _eof=null; + } + + return content; + } + + /** + * Access the next content to be consumed from. Returning the next item does not consume it + * and it may be returned multiple times until it is consumed. Calls to {@link #get(Object, byte[], int, int)} + * or {@link #consume(Object, int)} are required to consume data from the content. + * @return Content or null if none available. + * @throws IOException + */ + protected abstract T nextContent() throws IOException; + protected abstract int remaining(T item); protected abstract int get(T item, byte[] buffer, int offset, int length); @@ -178,10 +167,15 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl protected abstract void consume(T item, int length); protected abstract void blockForContent() throws IOException; + + /** Add some content to the input stream + * @param item + */ + public abstract void content(T item); protected boolean onAsyncRead() { - if (_listener==null) + if (_listener == null) return false; _channelState.onReadPossible(); return true; @@ -194,11 +188,6 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl return _contentRead; } } - - /** Add some content to the input stream - * @param item - */ - public abstract void content(T item); /** This method should be called to signal to the HttpInput @@ -442,13 +431,4 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl return "EOF"; } }; - - public void init(HttpChannelState state) - { - synchronized (lock()) - { - _channelState=state; - } - } - } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/QueuedHttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/QueuedHttpInput.java index c5320ef95f5..cadae5fe189 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/QueuedHttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/QueuedHttpInput.java @@ -20,7 +20,6 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.io.InterruptedIOException; - import javax.servlet.ServletInputStream; import org.eclipse.jetty.util.ArrayQueue; @@ -44,8 +43,34 @@ public abstract class QueuedHttpInput extends HttpInput private final ArrayQueue _inputQ = new ArrayQueue<>(lock()); public QueuedHttpInput() - {} + { + } + /** Add some content to the input stream + * @param item + */ + public void content(T item) + { + // The buffer is not copied here. This relies on the caller not recycling the buffer + // until the it is consumed. The onContentConsumed and onAllContentConsumed() callbacks are + // the signals to the caller that the buffers can be recycled. + + synchronized (lock()) + { + boolean empty=_inputQ.isEmpty(); + + _inputQ.add(item); + + if (empty) + { + if (!onAsyncRead()) + lock().notify(); + } + + LOG.debug("{} queued {}", this, item); + } + } + public void recycle() { synchronized (lock()) @@ -84,13 +109,11 @@ public abstract class QueuedHttpInput extends HttpInput return item; } - protected abstract void onContentConsumed(T item); - protected void blockForContent() throws IOException { synchronized (lock()) { - while (_inputQ.isEmpty() && !_state.isEOF()) + while (_inputQ.isEmpty() && !isFinished()) { try { @@ -105,41 +128,14 @@ public abstract class QueuedHttpInput extends HttpInput } } + protected abstract void onContentConsumed(T item); - /* ------------------------------------------------------------ */ /** Called by this HttpInput to signal all available content has been consumed */ protected void onAllContentConsumed() { } - /* ------------------------------------------------------------ */ - /** Add some content to the input stream - * @param item - */ - public void content(T item) - { - // The buffer is not copied here. This relies on the caller not recycling the buffer - // until the it is consumed. The onContentConsumed and onAllContentConsumed() callbacks are - // the signals to the caller that the buffers can be recycled. - - synchronized (lock()) - { - boolean empty=_inputQ.isEmpty(); - - _inputQ.add(item); - - if (empty) - { - if (!onAsyncRead()) - lock().notify(); - } - - LOG.debug("{} queued {}", this, item); - } - } - - public void earlyEOF() { synchronized (lock()) @@ -157,5 +153,4 @@ public abstract class QueuedHttpInput extends HttpInput lock().notify(); } } - } From e4cac732cf1edee3d477f0db987822464068e718 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 14 Jan 2014 22:48:06 +0100 Subject: [PATCH 13/54] Updated build profiles to JDK 7u51, along with NPN modules. --- .../src/main/config/modules/npn/npn-1.7.0_51.mod | 9 +++++++++ .../usecases/home/modules/npn/npn-1.7.0_51.mod | 9 +++++++++ pom.xml | 12 ++++++++++++ 3 files changed, 30 insertions(+) create mode 100644 jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_51.mod create mode 100644 jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_51.mod diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_51.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_51.mod new file mode 100644 index 00000000000..a0676260448 --- /dev/null +++ b/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_51.mod @@ -0,0 +1,9 @@ +[name] +npn-boot + +[files] +http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar:lib/npn/npn-boot-1.1.6.v20130911.jar + +[ini-template] +--exec +-Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar diff --git a/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_51.mod b/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_51.mod new file mode 100644 index 00000000000..a0676260448 --- /dev/null +++ b/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_51.mod @@ -0,0 +1,9 @@ +[name] +npn-boot + +[files] +http://central.maven.org/maven2/org/mortbay/jetty/npn/npn-boot/1.1.6.v20130911/npn-boot-1.1.6.v20130911.jar:lib/npn/npn-boot-1.1.6.v20130911.jar + +[ini-template] +--exec +-Xbootclasspath/p:lib/npn/npn-boot-1.1.6.v20130911.jar diff --git a/pom.xml b/pom.xml index 0452976bd4a..258bb0c6de4 100644 --- a/pom.xml +++ b/pom.xml @@ -908,6 +908,18 @@ 1.1.6.v20130911 + + 7u51 + + + java.version + 1.7.0_51 + + + + 1.1.6.v20130911 + + From 1ff01f36ebe1a1c63dc3a7f352892bb7c0113e83 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 14 Jan 2014 15:09:57 -0700 Subject: [PATCH 14/54] Fixing SessionTest failures on windows due to file locking --- .../eclipse/jetty/websocket/jsr356/server/SessionTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java index 24e8cff6a92..7df2b464e61 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/SessionTest.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Queue; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.servlet.DefaultServlet; @@ -102,6 +103,7 @@ public class SessionTest public LeakTrackingBufferPool bufferPool = new LeakTrackingBufferPool("Test",new MappedByteBufferPool()); private final Case testcase; + private final static AtomicInteger ID = new AtomicInteger(0); private WSServer server; private URI serverUri; @@ -113,7 +115,7 @@ public class SessionTest @Before public void startServer() throws Exception { - server = new WSServer(MavenTestingUtils.getTargetTestingDir(SessionTest.class.getSimpleName()),"app"); + server = new WSServer(MavenTestingUtils.getTargetTestingDir(SessionTest.class.getSimpleName() + "-" + ID.incrementAndGet()),"app"); server.copyWebInf("empty-web.xml"); server.copyClass(SessionInfoSocket.class); server.copyClass(SessionAltConfig.class); From 93013b36ddc4549b81129d403364a79314d1044b Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 14 Jan 2014 22:50:25 +0100 Subject: [PATCH 15/54] 425703 - Review [Queued]HttpInput. Fixed synchronization. Fixed notification of callbacks outside sync blocks. Added isEOF() method to allow correct implementation of blockForContent(). Remove unused callback onAllContentConsumed(). --- .../org/eclipse/jetty/server/HttpInput.java | 268 +++++++++++------- .../eclipse/jetty/server/QueuedHttpInput.java | 85 +++--- .../jetty/server/QueuedHttpInputTest.java | 33 +++ 3 files changed, 231 insertions(+), 155 deletions(-) create mode 100644 jetty-server/src/test/java/org/eclipse/jetty/server/QueuedHttpInputTest.java diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java index 7506e6010f9..a66ed167b5c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java @@ -19,6 +19,7 @@ package org.eclipse.jetty.server; import java.io.IOException; +import java.util.Objects; import javax.servlet.ReadListener; import javax.servlet.ServletInputStream; @@ -28,7 +29,12 @@ import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** - *

{@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.

+ * {@link HttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}. + *

+ * Content may arrive in patterns such as [content(), content(), messageComplete()] so that this class + * maintains two states: the content state that tells whether there is content to consume and the EOF + * state that tells whether an EOF has arrived. + * Only once the content has been consumed the content state is moved to the EOF state. */ public abstract class HttpInput extends ServletInputStream implements Runnable { @@ -40,8 +46,8 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl private ReadListener _listener; private Throwable _onError; private boolean _notReady; - private State _state = BLOCKING; - private State _eof; + private State _contentState = STREAM; + private State _eofState; private long _contentRead; protected HttpInput() @@ -71,10 +77,12 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl { synchronized (lock()) { - _state = BLOCKING; - _eof=null; - _onError=null; - _contentRead=0; + _listener = null; + _onError = null; + _notReady = false; + _contentState = STREAM; + _eofState = null; + _contentRead = 0; } } @@ -86,7 +94,7 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl synchronized (lock()) { T item = getNextContent(); - return item==null?0:remaining(item); + return item == null ? 0 : remaining(item); } } catch (IOException e) @@ -99,84 +107,111 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl public int read() throws IOException { int read = read(_oneByteBuffer, 0, 1); - return read < 0 ? -1 : 0xff & _oneByteBuffer[0]; + return read < 0 ? -1 : _oneByteBuffer[0] & 0xFF; } @Override public int read(byte[] b, int off, int len) throws IOException { - T item = null; - int l; synchronized (lock()) { - // System.err.printf("read s=%s q=%d e=%s%n",_state,_inputQ.size(),_eof); - - // Get the current head of the input Q - item = getNextContent(); - - // If we have no item + T item = getNextContent(); if (item == null) { - _state.waitForContent(this); - item=getNextContent(); - if (item==null) - return _state.noContent(); + _contentState.waitForContent(this); + item = getNextContent(); + if (item == null) + return _contentState.noContent(); } - - l=get(item, b, off, len); - _contentRead+=l; - + int l = get(item, b, off, len); + _contentRead += l; + return l; } - return l; } - + /** * A convenience method to call nextContent and to check the return value, which if null then the * a check is made for EOF and the state changed accordingly. - * @see #nextContent() + * * @return Content or null if none available. * @throws IOException + * @see #nextContent() */ protected T getNextContent() throws IOException { - T content=nextContent(); - - if (content==null && _eof!=null) + T content = nextContent(); + if (content == null) { - LOG.debug("{} eof {}",this,_eof); - _state=_eof; - _eof=null; + synchronized (lock()) + { + if (_eofState != null) + { + LOG.debug("{} eof {}", this, _eofState); + _contentState = _eofState; + } + } } - return content; } /** * Access the next content to be consumed from. Returning the next item does not consume it - * and it may be returned multiple times until it is consumed. Calls to {@link #get(Object, byte[], int, int)} + * and it may be returned multiple times until it is consumed. + *

+ * Calls to {@link #get(Object, byte[], int, int)} * or {@link #consume(Object, int)} are required to consume data from the content. - * @return Content or null if none available. - * @throws IOException + * + * @return the content or null if none available. + * @throws IOException if retrieving the content fails */ protected abstract T nextContent() throws IOException; + /** + * @param item the content + * @return how many bytes remain in the given content + */ protected abstract int remaining(T item); + /** + * Copies the given content into the given byte buffer. + * + * @param item the content to copy from + * @param buffer the buffer to copy into + * @param offset the buffer offset to start copying from + * @param length the space available in the buffer + * @return the number of bytes actually copied + */ protected abstract int get(T item, byte[] buffer, int offset, int length); + /** + * Consumes the given content. + * + * @param item the content to consume + * @param length the number of bytes to consume + */ protected abstract void consume(T item, int length); + /** + * Blocks until some content or some end-of-file event arrives. + * + * @throws IOException if the wait is interrupted + */ protected abstract void blockForContent() throws IOException; - - /** Add some content to the input stream - * @param item + + /** + * Adds some content to this input stream. + * + * @param item the content to add */ public abstract void content(T item); protected boolean onAsyncRead() { - if (_listener == null) - return false; + synchronized (lock()) + { + if (_listener == null) + return false; + } _channelState.onReadPossible(); return true; } @@ -189,9 +224,10 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl } } - - /** This method should be called to signal to the HttpInput - * that an EOF has arrived before all the expected content. + /** + * This method should be called to signal that an EOF has been + * detected before all the expected content arrived. + *

* Typically this will result in an EOFException being thrown * from a subsequent read rather than a -1 return. */ @@ -199,28 +235,34 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl { synchronized (lock()) { - if (_eof==null || !_eof.isEOF()) + if (!isEOF()) { LOG.debug("{} early EOF", this); - _eof=EARLY_EOF; - if (_listener!=null) - _channelState.onReadPossible(); + _eofState = EARLY_EOF; + if (_listener == null) + return; } } + _channelState.onReadPossible(); } + /** + * This method should be called to signal that all the expected + * content arrived. + */ public void messageComplete() { synchronized (lock()) { - if (_eof==null || !_eof.isEOF()) + if (!isEOF()) { LOG.debug("{} EOF", this); - _eof=EOF; - if (_listener!=null) - _channelState.onReadPossible(); + _eofState = EOF; + if (_listener == null) + return; } } + _channelState.onReadPossible(); } public void consumeAll() @@ -232,10 +274,10 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl while (!isFinished()) { T item = getNextContent(); - if (item==null) - _state.waitForContent(this); + if (item == null) + _contentState.waitForContent(this); else - consume(item,remaining(item)); + consume(item, remaining(item)); } } catch (IOException e) @@ -245,35 +287,46 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl } } + /** + * @return whether an EOF has been detected, even though there may be content to consume. + */ + public boolean isEOF() + { + synchronized (lock()) + { + return _eofState != null && _eofState.isEOF(); + } + } + @Override public boolean isFinished() { synchronized (lock()) { - return _state.isEOF(); + return _contentState.isEOF(); } } @Override public boolean isReady() { + boolean finished; synchronized (lock()) { - if (_listener==null) + if (_listener == null) return true; - int available = available(); - if (available>0) + if (available() > 0) return true; - if (!_notReady) - { - _notReady=true; - if (_state.isEOF()) - _channelState.onReadPossible(); - else - unready(); - } - return false; + if (_notReady) + return false; + _notReady = true; + finished = isFinished(); } + if (finished) + _channelState.onReadPossible(); + else + unready(); + return false; } protected void unready() @@ -283,80 +336,79 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl @Override public void setReadListener(ReadListener readListener) { - if (readListener==null) - throw new NullPointerException("readListener==null"); + readListener = Objects.requireNonNull(readListener); synchronized (lock()) { - if (_state!=BLOCKING) - throw new IllegalStateException("state="+_state); - _state=ASYNC; - _listener=readListener; - _notReady=true; - - _channelState.onReadPossible(); + if (_contentState != STREAM) + throw new IllegalStateException("state=" + _contentState); + _contentState = ASYNC; + _listener = readListener; + _notReady = true; } + _channelState.onReadPossible(); } public void failed(Throwable x) { synchronized (lock()) { - if (_onError==null) + if (_onError == null) LOG.warn(x); else - _onError=x; + _onError = x; } } @Override public void run() { - final boolean available; + final Throwable error; + final ReadListener listener; + boolean available = false; final boolean eof; - final Throwable x; synchronized (lock()) { - if (!_notReady || _listener==null) + if (!_notReady || _listener == null) return; - x=_onError; - T item; + error = _onError; + listener = _listener; + try { - item = getNextContent(); + T item = getNextContent(); + available = item != null && remaining(item) > 0; } - catch(Exception e) + catch (Exception e) { - item=null; failed(e); } - available= item!=null && remaining(item)>0; - eof = !available && _state.isEOF(); - _notReady=!available&&!eof; + eof = !available && isFinished(); + _notReady = !available && !eof; } try { - if (x!=null) - _listener.onError(x); + if (error != null) + listener.onError(error); else if (available) - _listener.onDataAvailable(); + listener.onDataAvailable(); else if (eof) - _listener.onAllDataRead(); + listener.onAllDataRead(); else unready(); } - catch(Throwable e) + catch (Throwable e) { LOG.warn(e.toString()); LOG.debug(e); - _listener.onError(e); + listener.onError(e); } } - protected static class State + protected static abstract class State { public void waitForContent(HttpInput in) throws IOException { @@ -373,26 +425,28 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl } } - protected static final State BLOCKING= new State() + protected static final State STREAM = new State() { @Override - public void waitForContent(HttpInput in) throws IOException + public void waitForContent(HttpInput input) throws IOException { - in.blockForContent(); + input.blockForContent(); } + public String toString() { - return "OPEN"; + return "STREAM"; } }; - protected static final State ASYNC= new State() + protected static final State ASYNC = new State() { @Override public int noContent() throws IOException { return 0; } + @Override public String toString() { @@ -400,25 +454,27 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl } }; - protected static final State EARLY_EOF= new State() + protected static final State EARLY_EOF = new State() { @Override public int noContent() throws IOException { throw new EofException(); } + @Override public boolean isEOF() { return true; } + public String toString() { return "EARLY_EOF"; } }; - protected static final State EOF= new State() + protected static final State EOF = new State() { @Override public boolean isEOF() diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/QueuedHttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/QueuedHttpInput.java index cadae5fe189..881fbfa0842 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/QueuedHttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/QueuedHttpInput.java @@ -20,70 +20,59 @@ package org.eclipse.jetty.server; import java.io.IOException; import java.io.InterruptedIOException; -import javax.servlet.ServletInputStream; import org.eclipse.jetty.util.ArrayQueue; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; /** - *

{@link QueuedHttpInput} provides an implementation of {@link ServletInputStream} for {@link HttpChannel}.

- *

{@link QueuedHttpInput} holds a queue of items passed to it by calls to {@link #content(Object)}.

- *

{@link QueuedHttpInput} stores the items directly; if the items contain byte buffers, it does not copy them + * {@link QueuedHttpInput} holds a queue of items passed to it by calls to {@link #content(Object)}. + *

+ * {@link QueuedHttpInput} stores the items directly; if the items contain byte buffers, it does not copy them * but simply holds references to the item, thus the caller must organize for those buffers to valid while - * held by this class.

- *

To assist the caller, subclasses may override methods {@link #onAsyncRead()}, - * {@link #onContentConsumed(Object)} and {@link #onAllContentConsumed()} that can be implemented so that the - * caller will know when buffers are queued and consumed.

+ * held by this class. + *

+ * To assist the caller, subclasses may override methods {@link #onAsyncRead()}, {@link #onContentConsumed(Object)} + * that can be implemented so that the caller will know when buffers are queued and consumed. */ public abstract class QueuedHttpInput extends HttpInput { private final static Logger LOG = Log.getLogger(QueuedHttpInput.class); private final ArrayQueue _inputQ = new ArrayQueue<>(lock()); - + public QueuedHttpInput() { } - /** Add some content to the input stream - * @param item - */ public void content(T item) { // The buffer is not copied here. This relies on the caller not recycling the buffer - // until the it is consumed. The onContentConsumed and onAllContentConsumed() callbacks are + // until the it is consumed. The onContentConsumed and onAllContentConsumed() callbacks are // the signals to the caller that the buffers can be recycled. - + synchronized (lock()) { - boolean empty=_inputQ.isEmpty(); - + boolean wasEmpty = _inputQ.isEmpty(); _inputQ.add(item); - - if (empty) + LOG.debug("{} queued {}", this, item); + if (wasEmpty) { if (!onAsyncRead()) lock().notify(); } - - LOG.debug("{} queued {}", this, item); } } - + public void recycle() { synchronized (lock()) { - T item = _inputQ.peekUnsafe(); + T item = _inputQ.pollUnsafe(); while (item != null) { - _inputQ.pollUnsafe(); onContentConsumed(item); - - item = _inputQ.peekUnsafe(); - if (item == null) - onAllContentConsumed(); + item = _inputQ.pollUnsafe(); } super.recycle(); } @@ -92,28 +81,27 @@ public abstract class QueuedHttpInput extends HttpInput @Override protected T nextContent() { - T item = _inputQ.peekUnsafe(); - - // Skip empty items at the head of the queue - while (item != null && remaining(item) == 0) + synchronized (lock()) { - _inputQ.pollUnsafe(); - onContentConsumed(item); - LOG.debug("{} consumed {}", this, item); - item = _inputQ.peekUnsafe(); - - // If that was the last item then notify - if (item==null) - onAllContentConsumed(); + // Items are removed only when they are fully consumed. + T item = _inputQ.peekUnsafe(); + // Skip consumed items at the head of the queue. + while (item != null && remaining(item) == 0) + { + _inputQ.pollUnsafe(); + onContentConsumed(item); + LOG.debug("{} consumed {}", this, item); + item = _inputQ.peekUnsafe(); + } + return item; } - return item; } - + protected void blockForContent() throws IOException { synchronized (lock()) { - while (_inputQ.isEmpty() && !isFinished()) + while (_inputQ.isEmpty() && !isFinished() && !isEOF()) { try { @@ -128,13 +116,12 @@ public abstract class QueuedHttpInput extends HttpInput } } - protected abstract void onContentConsumed(T item); - - /** Called by this HttpInput to signal all available content has been consumed + /** + * Callback that signals that the given content has been consumed. + * + * @param item the consumed content */ - protected void onAllContentConsumed() - { - } + protected abstract void onContentConsumed(T item); public void earlyEOF() { @@ -144,7 +131,7 @@ public abstract class QueuedHttpInput extends HttpInput lock().notify(); } } - + public void messageComplete() { synchronized (lock()) diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/QueuedHttpInputTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/QueuedHttpInputTest.java new file mode 100644 index 00000000000..864d1a98d39 --- /dev/null +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/QueuedHttpInputTest.java @@ -0,0 +1,33 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.server; + +import org.junit.Test; + +public class QueuedHttpInputTest +{ + @Test + public void testNoContentMessageComplete() throws Exception + { + ByteBufferQueuedHttpInput input = new ByteBufferQueuedHttpInput(); + input.messageComplete(); + + input.getNextContent(); + } +} From 1e2e3fc7f2a10c563d2f7c1afe64599d1e4849be Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Fri, 17 Jan 2014 14:36:08 +1100 Subject: [PATCH 16/54] 425837 Upgrade to jstl 1.2.2 --- jetty-distribution/pom.xml | 34 ++++++++++- jetty-jsp/pom.xml | 4 +- jetty-osgi/jetty-osgi-boot-jsp/pom.xml | 56 +++++++++---------- .../TestJettyOSGiBootWebAppAsService.java | 6 +- .../osgi/test/TestJettyOSGiBootWithJsp.java | 10 +++- pom.xml | 23 ++++++-- 6 files changed, 92 insertions(+), 41 deletions(-) diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index 3bace0fa420..87fb71427a8 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -440,11 +440,38 @@ org.eclipse.jetty.orbit,org.glassfish.web, org.glassfish, javax.el, javax.servlet.jsp, org.eclipse.jetty.toolchain - javax.servlet.jsp.jstl,org.apache.taglibs.standard.glassfish,org.eclipse.jdt.core, javax.servlet.jsp-api, javax.servlet.jsp, jetty-jsp-jdt, javax.el-api, javax.el + org.eclipse.jdt.core, javax.servlet.jsp-api, javax.servlet.jsp, jetty-jsp-jdt, javax.el-api, javax.el jar ${assembly-directory}/lib/jsp + + copy-jstl-api + generate-resources + + copy-dependencies + + + org.eclipse.jetty.orbit + javax.servlet.jsp.jstl + true + jar + ${assembly-directory}/lib/jsp + + + + copy-jstl-impl + generate-resources + + copy-dependencies + + + org.glassfish.web + javax.servlet.jsp.jstl + jar + ${assembly-directory}/lib/jsp + + copy-jaspi-deps generate-resources @@ -571,9 +598,10 @@ org.eclipse.jetty.orbit javax.security.auth.message + - org.eclipse.jetty.orbit - org.apache.taglibs.standard.glassfish + org.glassfish.web + javax.servlet.jsp.jstl diff --git a/jetty-jsp/pom.xml b/jetty-jsp/pom.xml index f29b71402e3..b2225207478 100644 --- a/jetty-jsp/pom.xml +++ b/jetty-jsp/pom.xml @@ -62,8 +62,8 @@ - org.eclipse.jetty.orbit - org.apache.taglibs.standard.glassfish + org.glassfish.web + javax.servlet.jsp.jstl diff --git a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml index 940b7d8a7e3..60050a3a702 100644 --- a/jetty-osgi/jetty-osgi-boot-jsp/pom.xml +++ b/jetty-osgi/jetty-osgi-boot-jsp/pom.xml @@ -100,10 +100,10 @@ javax.servlet.jsp;version="[2.3,2.4)", javax.servlet.jsp.el;version="[2.3,2.4)", javax.servlet.jsp.tagext;version="[2.3,2.4)", - javax.servlet.jsp.jstl.core;version="1.2.0";resolution:=optional, - javax.servlet.jsp.jstl.fmt;version="1.2.0";resolution:=optional, - javax.servlet.jsp.jstl.sql;version="1.2.0";resolution:=optional, - javax.servlet.jsp.jstl.tlv;version="1.2.0";resolution:=optional, + javax.servlet.jsp.jstl.core;version="1.2";resolution:=optional, + javax.servlet.jsp.jstl.fmt;version="1.2";resolution:=optional, + javax.servlet.jsp.jstl.sql;version="1.2";resolution:=optional, + javax.servlet.jsp.jstl.tlv;version="1.2";resolution:=optional, org.apache.jasper;version="[2.3.2,2.4)";resolution:=optional, org.apache.jasper.compiler;version="[2.3.2,2.4)";resolution:=optional, org.apache.jasper.compiler.tagplugin;version="[2.3.2,2.4)";resolution:=optional, @@ -114,29 +114,29 @@ org.apache.jasper.util;version="[2.3.2,2.4)";resolution:=optional, org.apache.jasper.xmlparser;version="[2.3.2,2.4)";resolution:=optional, org.glassfish.jsp.api;version="[2.3.2,2.4)";resolution:=optional, - org.apache.taglibs.standard;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.extra.spath;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.functions;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.lang.jstl;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.lang.jstl.parser;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.lang.jstl.test;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.lang.jstl.test.beans;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.lang.support;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.resources;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.common.core;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.common.fmt;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.common.sql;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.common.xml;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.el.core;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.el.fmt;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.el.sql;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.el.xml;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.rt.core;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.rt.fmt;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.rt.sql;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tag.rt.xml;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tei;version="1.2.0";resolution:=optional, - org.apache.taglibs.standard.tlv;version="1.2.0";resolution:=optional, + org.apache.taglibs.standard;version="1.2";resolution:=optional, + org.apache.taglibs.standard.extra.spath;version="1.2";resolution:=optional, + org.apache.taglibs.standard.functions;version="1.2";resolution:=optional, + org.apache.taglibs.standard.lang.jstl;version="1.2";resolution:=optional, + org.apache.taglibs.standard.lang.jstl.parser;version="1.2";resolution:=optional, + org.apache.taglibs.standard.lang.jstl.test;version="1.2";resolution:=optional, + org.apache.taglibs.standard.lang.jstl.test.beans;version="1.2";resolution:=optional, + org.apache.taglibs.standard.lang.support;version="1.2";resolution:=optional, + org.apache.taglibs.standard.resources;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.common.core;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.common.fmt;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.common.sql;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.common.xml;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.el.core;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.el.fmt;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.el.sql;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.el.xml;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.rt.core;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.rt.fmt;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.rt.sql;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tag.rt.xml;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tei;version="1.2";resolution:=optional, + org.apache.taglibs.standard.tlv;version="1.2";resolution:=optional, org.osgi.*, org.xml.*;resolution:=optional, org.xml.sax.*;resolution:=optional, @@ -144,7 +144,7 @@ org.w3c.dom;resolution:=optional, org.w3c.dom.ls;resolution:=optional, javax.xml.parser;resolution:=optional - + <_nouses>true org.apache.jasper.*;version="2.3" diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java index 1f6b1db9511..b43279c07a9 100644 --- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java +++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWebAppAsService.java @@ -77,6 +77,10 @@ public class TestJettyOSGiBootWebAppAsService options.add(CoreOptions.junitBundles()); options.addAll(configureJettyHomeAndPort("jetty-selector.xml")); options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*")); + options.add(CoreOptions.systemPackages("com.sun.org.apache.xalan.internal.res","com.sun.org.apache.xml.internal.utils", + "com.sun.org.apache.xml.internal.utils", "com.sun.org.apache.xpath.internal", + "com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects")); + options.addAll(TestJettyOSGiBootCore.coreJettyDependencies()); String logLevel = "WARN"; @@ -123,7 +127,7 @@ public class TestJettyOSGiBootWebAppAsService //jsp bundles res.add(mavenBundle().groupId("javax.servlet.jsp").artifactId("javax.servlet.jsp-api").versionAsInProject()); res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("javax.servlet.jsp.jstl").versionAsInProject()); - res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.apache.taglibs.standard.glassfish").versionAsInProject()); + res.add(mavenBundle().groupId("org.glassfish.web").artifactId("javax.servlet.jsp.jstl").versionAsInProject()); res.add(mavenBundle().groupId("org.glassfish").artifactId("javax.el").versionAsInProject()); res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.eclipse.jdt.core").versionAsInProject()); res.add(mavenBundle().groupId("org.eclipse.jetty.toolchain").artifactId("jetty-jsp-fragment").versionAsInProject().noStart()); diff --git a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java index 5a4aa94c6ae..34ce9d11a3d 100644 --- a/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java +++ b/jetty-osgi/test-jetty-osgi/src/test/java/org/eclipse/jetty/osgi/test/TestJettyOSGiBootWithJsp.java @@ -42,6 +42,7 @@ import org.ops4j.pax.exam.CoreOptions; import org.ops4j.pax.exam.Option; import org.ops4j.pax.exam.junit.Configuration; import org.ops4j.pax.exam.junit.JUnit4TestRunner; +import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; /** @@ -70,6 +71,10 @@ public class TestJettyOSGiBootWithJsp options.add(CoreOptions.junitBundles()); options.addAll(configureJettyHomeAndPort("jetty-selector.xml")); options.add(CoreOptions.bootDelegationPackages("org.xml.sax", "org.xml.*", "org.w3c.*", "javax.xml.*")); + options.add(CoreOptions.systemPackages("com.sun.org.apache.xalan.internal.res","com.sun.org.apache.xml.internal.utils", + "com.sun.org.apache.xml.internal.utils", "com.sun.org.apache.xpath.internal", + "com.sun.org.apache.xpath.internal.jaxp", "com.sun.org.apache.xpath.internal.objects")); + options.addAll(TestJettyOSGiBootCore.coreJettyDependencies()); String logLevel = "WARN"; @@ -139,7 +144,7 @@ public class TestJettyOSGiBootWithJsp //jetty jsp bundles res.add(mavenBundle().groupId("javax.servlet.jsp").artifactId("javax.servlet.jsp-api").versionAsInProject()); res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("javax.servlet.jsp.jstl").versionAsInProject()); - res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.apache.taglibs.standard.glassfish").versionAsInProject()); + res.add(mavenBundle().groupId("org.glassfish.web").artifactId("javax.servlet.jsp.jstl").versionAsInProject()); res.add(mavenBundle().groupId("org.glassfish").artifactId("javax.el").versionAsInProject()); res.add(mavenBundle().groupId("org.eclipse.jetty.orbit").artifactId("org.eclipse.jdt.core").versionAsInProject()); res.add(mavenBundle().groupId("org.eclipse.jetty.toolchain").artifactId("jetty-jsp-fragment").versionAsInProject().noStart()); @@ -155,7 +160,7 @@ public class TestJettyOSGiBootWithJsp @Test public void assertAllBundlesActiveOrResolved() { - TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext); + TestOSGiUtil.assertAllBundlesActiveOrResolved(bundleContext); } // at the moment can't run httpservice with jsp at the same time. @@ -167,6 +172,7 @@ public class TestJettyOSGiBootWithJsp TestOSGiUtil.testHttpServiceGreetings(bundleContext, "http", TestJettyOSGiBootCore.DEFAULT_JETTY_HTTP_PORT); } + @Test public void testJspDump() throws Exception { diff --git a/pom.xml b/pom.xml index 258bb0c6de4..f07f29983c3 100644 --- a/pom.xml +++ b/pom.xml @@ -531,17 +531,30 @@ - org.eclipse.jetty.orbit - org.apache.taglibs.standard.glassfish - 1.2.0.v201112081803 + org.glassfish.web + javax.servlet.jsp.jstl + 1.2.2 - org.eclipse.jetty.orbit - javax.servlet + javax.servlet.jsp.jstl + jstl-api + + + javax.servlet + servlet-api + + + javax.servlet.jsp + jsp-api + + + javax.el + el-api + org.eclipse.jetty.orbit javax.servlet.jsp.jstl From f735fe834b5c85c195cfb0e3a6314639d799f198 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Sat, 18 Jan 2014 08:31:43 +1100 Subject: [PATCH 17/54] 425998 JDBCSessionIdManager fails to create maxinterval column --- .../org/eclipse/jetty/server/session/JDBCSessionIdManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java index e4e75355794..3075493eb1c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionIdManager.java @@ -299,7 +299,7 @@ public class JDBCSessionIdManager extends AbstractSessionIdManager if (_dbAdaptor == null) throw new IllegalStateException ("No DBAdaptor"); String longType = _dbAdaptor.getLongType(); - return "alter table "+getTableName()+" add "+longType+" not null default "+MAX_INTERVAL_NOT_SET; + return "alter table "+getTableName()+" add "+getMaxIntervalColumn()+" "+longType+" not null default "+MAX_INTERVAL_NOT_SET; } private void checkNotNull(String s) From 5f7a34e5909259d125164c75e398bc26557b6417 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Mon, 20 Jan 2014 10:40:05 +1100 Subject: [PATCH 18/54] 408167 Complex object as session attribute not necessarily persisted. --- .../server/session/JDBCSessionManager.java | 35 +++++++++++++------ .../server/session/DirtyAttributeTest.java | 6 ++-- 2 files changed, 28 insertions(+), 13 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java index 576cd07cd7c..4a11552fec0 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java @@ -95,49 +95,49 @@ public class JDBCSessionManager extends AbstractSessionManager /** * If dirty, session needs to be (re)persisted */ - private boolean _dirty=false; + protected boolean _dirty=false; /** * Time in msec since the epoch that a session cookie was set for this session */ - private long _cookieSet; + protected long _cookieSet; /** * Time in msec since the epoch that the session will expire */ - private long _expiryTime; + protected long _expiryTime; /** * Time in msec since the epoch that the session was last persisted */ - private long _lastSaved; + protected long _lastSaved; /** * Unique identifier of the last node to host the session */ - private String _lastNode; + protected String _lastNode; /** * Virtual host for context (used to help distinguish 2 sessions with same id on different contexts) */ - private String _virtualHost; + protected String _virtualHost; /** * Unique row in db for session */ - private String _rowId; + protected String _rowId; /** * Mangled context name (used to help distinguish 2 sessions with same id on different contexts) */ - private String _canonicalContext; + protected String _canonicalContext; /** @@ -246,7 +246,8 @@ public class JDBCSessionManager extends AbstractSessionManager @Override public void setAttribute (String name, Object value) { - _dirty = (updateAttribute(name, value) || _dirty); + updateAttribute(name, value); + _dirty = true; } @Override @@ -769,6 +770,20 @@ public class JDBCSessionManager extends AbstractSessionManager { return new Session(request); } + + + /** + * @param sessionId + * @param rowId + * @param created + * @param accessed + * @param maxInterval + * @return + */ + protected AbstractSession newSession (String sessionId, String rowId, long created, long accessed, long maxInterval) + { + return new Session(sessionId, rowId, created, accessed, maxInterval); + } /* ------------------------------------------------------------ */ /** Remove session from manager @@ -892,7 +907,7 @@ public class JDBCSessionManager extends AbstractSessionManager { maxInterval = getMaxInactiveInterval(); //if value not saved for maxInactiveInterval, use current value from sessionmanager } - session = new Session(id, result.getString(_sessionTableSchema.getRowIdColumn()), + session = (Session)newSession(id, result.getString(_sessionTableSchema.getRowIdColumn()), result.getLong(_sessionTableSchema.getCreateTimeColumn()), result.getLong(_sessionTableSchema.getAccessTimeColumn()), maxInterval); diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java index f06180ee90f..871ea43ee42 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java @@ -58,7 +58,7 @@ public class DirtyAttributeTest public static int SCAVENGE = 1; @Test - public void testDiryWrite() throws Exception + public void testDirtyWrite() throws Exception { AbstractTestServer server = new JdbcTestServer(0,INACTIVE,SCAVENGE); @@ -98,8 +98,8 @@ public class DirtyAttributeTest request.header("Cookie", sessionCookie); response = request.send(); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - A_VALUE.assertPassivatesEquals(1); - A_VALUE.assertActivatesEquals(1); + A_VALUE.assertPassivatesEquals(2); + A_VALUE.assertActivatesEquals(2); A_VALUE.assertBindsEquals(1); A_VALUE.assertUnbindsEquals(0); From 62666dc344fd5fc18a71ccd222ad1a13e288a5d2 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 20 Jan 2014 11:03:57 +1100 Subject: [PATCH 19/54] 425638 Fixed monitor module/xml typos --- jetty-monitor/src/main/config/etc/jetty-monitor.xml | 4 ++-- jetty-monitor/src/main/config/modules/monitor.mod | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/jetty-monitor/src/main/config/etc/jetty-monitor.xml b/jetty-monitor/src/main/config/etc/jetty-monitor.xml index 6a866dda28c..61270b5bd51 100644 --- a/jetty-monitor/src/main/config/etc/jetty-monitor.xml +++ b/jetty-monitor/src/main/config/etc/jetty-monitor.xml @@ -14,8 +14,8 @@ diff --git a/jetty-monitor/src/main/config/modules/monitor.mod b/jetty-monitor/src/main/config/modules/monitor.mod index 67f006d0c4c..09132c7b2ca 100644 --- a/jetty-monitor/src/main/config/modules/monitor.mod +++ b/jetty-monitor/src/main/config/modules/monitor.mod @@ -7,7 +7,7 @@ server client [lib] -lib/jetty-monitor-${jetty.version}.jar +lib/monitor/jetty-monitor-${jetty.version}.jar [xml] -etc/jetty-monitor.xml \ No newline at end of file +etc/jetty-monitor.xml From 49ac6de01126a437ee9678d6e10a2ae5836a0d2a Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Mon, 20 Jan 2014 13:28:50 +1100 Subject: [PATCH 20/54] 425930 JDBC Session Manager constantly reloading session if save intervall expired once --- .../server/session/JDBCSessionManager.java | 7 +- .../server/session/SaveIntervalTest.java | 179 ++++++++++++++++++ 2 files changed, 185 insertions(+), 1 deletion(-) create mode 100644 tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java index 4a11552fec0..e48a3b61303 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/JDBCSessionManager.java @@ -495,6 +495,7 @@ public class JDBCSessionManager extends AbstractSessionManager " interval="+(_saveIntervalSec * 1000L)); else LOG.debug("getSession("+idInCluster+"): in session map, "+ + " hashcode="+memSession.hashCode()+ " now="+now+ " lastSaved="+(memSession==null?0:memSession._lastSaved)+ " interval="+(_saveIntervalSec * 1000L)+ @@ -566,7 +567,11 @@ public class JDBCSessionManager extends AbstractSessionManager } else + { + //the session loaded from the db and the one in memory are the same, so keep using the one in memory + session = memSession; LOG.debug("getSession({}): Session not stale {}", idInCluster,session); + } } else { @@ -1100,7 +1105,7 @@ public class JDBCSessionManager extends AbstractSessionManager data.setLastSaved(now); } if (LOG.isDebugEnabled()) - LOG.debug("Updated access time session id="+data.getId()); + LOG.debug("Updated access time session id="+data.getId()+" with lastsaved="+data.getLastSaved()); } diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java new file mode 100644 index 00000000000..d965d687841 --- /dev/null +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java @@ -0,0 +1,179 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.server.session; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.servlet.ServletHolder; +import org.junit.Test; +import org.junit.Ignore; + +/** + * SaveIntervalTest + * + * Checks to see that potentially stale sessions that have not + * changed are not always reloaded from the datase. + * + * This test is Ignored because it takes a little while to run. + * + */ +public class SaveIntervalTest +{ + public static int INACTIVE = 90; //sec + public static int SCAVENGE = 100; //sec + public static int SAVE = 10; //sec + + + @Ignore + @Test + public void testSaveInterval() throws Exception + { + AbstractTestServer server = new JdbcTestServer(0,INACTIVE,SCAVENGE); + + ServletContextHandler ctxA = server.addContext("/mod"); + ServletHolder holder = new ServletHolder(); + TestSaveIntervalServlet servlet = new TestSaveIntervalServlet(); + holder.setServlet(servlet); + ctxA.addServlet(holder, "/test"); + ((JDBCSessionManager)ctxA.getSessionHandler().getSessionManager()).setSaveInterval(SAVE); + server.start(); + int port=server.getPort(); + try + { + HttpClient client = new HttpClient(); + client.start(); + try + { + // Perform a request to create a session + ContentResponse response = client.GET("http://localhost:" + port + "/mod/test?action=create"); + assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + assertTrue(sessionCookie != null); + // Mangle the cookie, replacing Path with $Path, etc. + sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); + long lastSaved = ((JDBCSessionManager.Session)servlet._session).getLastSaved(); + + + //do another request to change the session attribute + Request request = client.newRequest("http://localhost:" + port + "/mod/test?action=set"); + request.header("Cookie", sessionCookie); + response = request.send(); + assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + long tmp = ((JDBCSessionManager.Session)servlet._session).getLastSaved(); + assertNotEquals(lastSaved, tmp); //set of attribute will cause save to db + lastSaved = tmp; + + //do nothing for just a bit longer than the save interval to ensure + //session will be checked against database on next request + Thread.currentThread().sleep((SAVE+2)*1000); + + + //do another request to access the session, this will cause session to be initially + //checked against db. On exit of request, the access time will need updating, so the + //session will be saved to db. + request = client.newRequest("http://localhost:" + port + "/mod/test?action=tickle"); + request.header("Cookie", sessionCookie); + response = request.send(); + assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + tmp = ((JDBCSessionManager.Session)servlet._session).getLastSaved(); + assertNotEquals(lastSaved, tmp); + lastSaved = tmp; + + //wait a little and do another request to access the session + Thread.currentThread().sleep((SAVE/2)*1000); + + //do another request to access the session. This time, the save interval has not + //expired, so we should NOT see a debug trace of loading stale session. Nor should + //the exit of the request cause a save of the updated access time. + request = client.newRequest("http://localhost:" + port + "/mod/test?action=tickle"); + request.header("Cookie", sessionCookie); + response = request.send(); + assertEquals(HttpServletResponse.SC_OK,response.getStatus()); + tmp = ((JDBCSessionManager.Session)servlet._session).getLastSaved(); + assertEquals(lastSaved, tmp); //the save interval did not expire, so update to the access time will not have been persisted + } + finally + { + client.stop(); + } + } + finally + { + server.stop(); + } + } + + public static class TestSaveIntervalServlet extends HttpServlet + { + public HttpSession _session; + + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + String action = request.getParameter("action"); + + + if ("create".equals(action)) + { + HttpSession session = request.getSession(true); + System.err.println("CREATE: Session id="+session.getId()); + _session = session; + return; + } + + if ("set".equals(action)) + { + HttpSession session = request.getSession(false); + if (session == null) + throw new ServletException("Session is null for action=change"); + + System.err.println("SET: Session id="+session.getId()); + session.setAttribute("aaa", "12345"); + assertEquals(_session.getId(), session.getId()); + return; + } + + if ("tickle".equals(action)) + { + HttpSession session = request.getSession(false); + if (session == null) + throw new ServletException("Session does not exist"); + System.err.println("TICKLE: Session id="+session.getId()); + assertEquals(_session.getId(), session.getId()); + return; + } + } + } + +} From 44858c2428cb98fea2f79336fdd5aa5907766451 Mon Sep 17 00:00:00 2001 From: Jesse McConnell Date: Wed, 22 Jan 2014 16:54:51 -0600 Subject: [PATCH 21/54] [Bug 423421] remove org.slf4j and org.ow2.asm from jetty-all artifact --- aggregates/jetty-all/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/aggregates/jetty-all/pom.xml b/aggregates/jetty-all/pom.xml index 46b77d0cecb..e092809c80a 100644 --- a/aggregates/jetty-all/pom.xml +++ b/aggregates/jetty-all/pom.xml @@ -2,7 +2,7 @@ org.eclipse.jetty jetty-project - 9.1.0-SNAPSHOT + 9.1.2-SNAPSHOT ../../pom.xml 4.0.0 @@ -25,7 +25,7 @@ **/MANIFEST.MF,javax/** javax - javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn + javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn,org.slf4j,org.ow2.asm ${project.build.directory}/classes false true @@ -43,7 +43,7 @@ META-INF/**,**/Servlet3Continuation*,**/Jetty6Continuation* org.eclipse.jetty,org.eclipse.jetty.websocket javax - javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn + javax,org.eclipse.jetty.orbit,org.mortbay.jetty.npn,org.slf4j,org.ow2.asm ${project.build.directory}/sources true true From d2ab0ed50696e227e6f0e2b9924deba01a827e26 Mon Sep 17 00:00:00 2001 From: Jesse McConnell Date: Wed, 22 Jan 2014 16:57:36 -0600 Subject: [PATCH 22/54] Bug 426250 jetty-all should be deployed on release --- pom.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pom.xml b/pom.xml index f07f29983c3..586a18a9122 100644 --- a/pom.xml +++ b/pom.xml @@ -665,14 +665,12 @@ > mvn -N site:sshdeploy (for ssh users w/passphrase and ssh-agent) --> - ci From 2db90757edfd61c009bda22c08d89fc5da661c53 Mon Sep 17 00:00:00 2001 From: Jesse McConnell Date: Thu, 23 Jan 2014 10:36:29 -0600 Subject: [PATCH 23/54] Bug 426481 fix < java 1.7.0_10 npn files --- .../main/config/modules/npn/{npn-1.7.0_4.mod => npn-1.7.0_04.mod} | 0 .../main/config/modules/npn/{npn-1.7.0_5.mod => npn-1.7.0_05.mod} | 0 .../main/config/modules/npn/{npn-1.7.0_6.mod => npn-1.7.0_06.mod} | 0 .../main/config/modules/npn/{npn-1.7.0_7.mod => npn-1.7.0_07.mod} | 0 .../main/config/modules/npn/{npn-1.7.0_9.mod => npn-1.7.0_09.mod} | 0 .../home/modules/npn/{npn-1.7.0_4.mod => npn-1.7.0_04.mod} | 0 .../home/modules/npn/{npn-1.7.0_5.mod => npn-1.7.0_05.mod} | 0 .../home/modules/npn/{npn-1.7.0_6.mod => npn-1.7.0_06.mod} | 0 .../home/modules/npn/{npn-1.7.0_7.mod => npn-1.7.0_07.mod} | 0 .../home/modules/npn/{npn-1.7.0_9.mod => npn-1.7.0_09.mod} | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename jetty-spdy/spdy-http-server/src/main/config/modules/npn/{npn-1.7.0_4.mod => npn-1.7.0_04.mod} (100%) rename jetty-spdy/spdy-http-server/src/main/config/modules/npn/{npn-1.7.0_5.mod => npn-1.7.0_05.mod} (100%) rename jetty-spdy/spdy-http-server/src/main/config/modules/npn/{npn-1.7.0_6.mod => npn-1.7.0_06.mod} (100%) rename jetty-spdy/spdy-http-server/src/main/config/modules/npn/{npn-1.7.0_7.mod => npn-1.7.0_07.mod} (100%) rename jetty-spdy/spdy-http-server/src/main/config/modules/npn/{npn-1.7.0_9.mod => npn-1.7.0_09.mod} (100%) rename jetty-start/src/test/resources/usecases/home/modules/npn/{npn-1.7.0_4.mod => npn-1.7.0_04.mod} (100%) rename jetty-start/src/test/resources/usecases/home/modules/npn/{npn-1.7.0_5.mod => npn-1.7.0_05.mod} (100%) rename jetty-start/src/test/resources/usecases/home/modules/npn/{npn-1.7.0_6.mod => npn-1.7.0_06.mod} (100%) rename jetty-start/src/test/resources/usecases/home/modules/npn/{npn-1.7.0_7.mod => npn-1.7.0_07.mod} (100%) rename jetty-start/src/test/resources/usecases/home/modules/npn/{npn-1.7.0_9.mod => npn-1.7.0_09.mod} (100%) diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_4.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_04.mod similarity index 100% rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_4.mod rename to jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_04.mod diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_5.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_05.mod similarity index 100% rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_5.mod rename to jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_05.mod diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_6.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_06.mod similarity index 100% rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_6.mod rename to jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_06.mod diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_7.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_07.mod similarity index 100% rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_7.mod rename to jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_07.mod diff --git a/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_9.mod b/jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_09.mod similarity index 100% rename from jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_9.mod rename to jetty-spdy/spdy-http-server/src/main/config/modules/npn/npn-1.7.0_09.mod diff --git a/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_4.mod b/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_04.mod similarity index 100% rename from jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_4.mod rename to jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_04.mod diff --git a/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_5.mod b/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_05.mod similarity index 100% rename from jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_5.mod rename to jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_05.mod diff --git a/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_6.mod b/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_06.mod similarity index 100% rename from jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_6.mod rename to jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_06.mod diff --git a/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_7.mod b/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_07.mod similarity index 100% rename from jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_7.mod rename to jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_07.mod diff --git a/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_9.mod b/jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_09.mod similarity index 100% rename from jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_9.mod rename to jetty-start/src/test/resources/usecases/home/modules/npn/npn-1.7.0_09.mod From 7ce6aee3042f12258aee68cc5837d876bc27f96f Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 27 Jan 2014 18:57:24 +0100 Subject: [PATCH 24/54] 426739 - Response with Connection: keep-alive truncated. Fixed copy/paste error. Also removed unnecessary constant. --- .../org/eclipse/jetty/http/HttpGenerator.java | 5 +- .../jetty/http/HttpGeneratorServerTest.java | 51 +++++++++++-------- 2 files changed, 33 insertions(+), 23 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java index a158716382a..684570bc933 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java @@ -825,7 +825,7 @@ public class HttpGenerator header.put(CONNECTION_KEEP_ALIVE); else { - header.put(CONNECTION_KEEP_ALIVE,0,CONNECTION_CLOSE.length-2); + header.put(CONNECTION_KEEP_ALIVE,0,CONNECTION_KEEP_ALIVE.length-2); header.put((byte)','); header.put(StringUtil.getBytes(connection.toString())); header.put(CRLF); @@ -833,7 +833,7 @@ public class HttpGenerator } else if (connection!=null) { - header.put(CONNECTION_); + header.put(HttpHeader.CONNECTION.getBytesColonSpace()); header.put(StringUtil.getBytes(connection.toString())); header.put(CRLF); } @@ -872,7 +872,6 @@ public class HttpGenerator private static final byte[] CONTENT_LENGTH_0 = StringUtil.getBytes("Content-Length: 0\015\012"); private static final byte[] CONNECTION_KEEP_ALIVE = StringUtil.getBytes("Connection: keep-alive\015\012"); private static final byte[] CONNECTION_CLOSE = StringUtil.getBytes("Connection: close\015\012"); - private static final byte[] CONNECTION_ = StringUtil.getBytes("Connection: "); private static final byte[] HTTP_1_1_SPACE = StringUtil.getBytes(HttpVersion.HTTP_1_1+" "); private static final byte[] CRLF = StringUtil.getBytes("\015\012"); private static final byte[] TRANSFER_ENCODING_CHUNKED = StringUtil.getBytes("Transfer-Encoding: chunked\015\012"); diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java index be054169e46..6c2ebdca339 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java @@ -18,6 +18,15 @@ package org.eclipse.jetty.http; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jetty.http.HttpGenerator.ResponseInfo; +import org.eclipse.jetty.util.BufferUtil; +import org.junit.Assert; +import org.junit.Test; + import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.equalTo; @@ -28,14 +37,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; - -import org.eclipse.jetty.http.HttpGenerator.ResponseInfo; -import org.eclipse.jetty.util.BufferUtil; -import org.junit.Test; - public class HttpGeneratorServerTest { private class Handler implements HttpParser.ResponseHandler @@ -365,8 +366,7 @@ public class HttpGeneratorServerTest HttpGenerator gen = new HttpGenerator(); - HttpGenerator.Result - result = gen.generateResponse(null, null, null, null, true); + HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true); assertEquals(HttpGenerator.Result.NEED_INFO, result); assertEquals(HttpGenerator.State.START, gen.getState()); @@ -399,8 +399,7 @@ public class HttpGeneratorServerTest HttpGenerator gen = new HttpGenerator(); - HttpGenerator.Result - result = gen.generateResponse(null, null, null, null, true); + HttpGenerator.Result result = gen.generateResponse(null, null, null, null, true); assertEquals(HttpGenerator.Result.NEED_INFO, result); assertEquals(HttpGenerator.State.START, gen.getState()); @@ -495,9 +494,7 @@ public class HttpGeneratorServerTest ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. "); HttpGenerator gen = new HttpGenerator(); - HttpGenerator.Result - - result = gen.generateResponse(null, null, null, content0, false); + HttpGenerator.Result result = gen.generateResponse(null, null, null, content0, false); assertEquals(HttpGenerator.Result.NEED_INFO, result); assertEquals(HttpGenerator.State.START, gen.getState()); @@ -540,15 +537,12 @@ public class HttpGeneratorServerTest @Test public void test100ThenResponseWithContent() throws Exception { - ByteBuffer header = BufferUtil.allocate(4096); ByteBuffer content0 = BufferUtil.toBuffer("Hello World! "); ByteBuffer content1 = BufferUtil.toBuffer("The quick brown fox jumped over the lazy dog. "); HttpGenerator gen = new HttpGenerator(); - HttpGenerator.Result - - result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, null, null, null, false); + HttpGenerator.Result result = gen.generateResponse(HttpGenerator.CONTINUE_100_INFO, null, null, null, false); assertEquals(HttpGenerator.Result.NEED_HEADER, result); assertEquals(HttpGenerator.State.START, gen.getState()); @@ -563,7 +557,6 @@ public class HttpGeneratorServerTest assertThat(out, containsString("HTTP/1.1 100 Continue")); - result = gen.generateResponse(null, null, null, content0, false); assertEquals(HttpGenerator.Result.NEED_INFO, result); assertEquals(HttpGenerator.State.START, gen.getState()); @@ -603,4 +596,22 @@ public class HttpGeneratorServerTest assertThat(out, containsString("Content-Length: 59")); assertThat(out, containsString("\r\n\r\nHello World! The quick brown fox jumped over the lazy dog. ")); } + + @Test + public void testConnectionKeepAliveWithAdditionalCustomValue() throws Exception + { + HttpGenerator generator = new HttpGenerator(); + + HttpFields fields = new HttpFields(); + fields.put(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE); + String customValue = "test"; + fields.add(HttpHeader.CONNECTION, customValue); + ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_0, fields, -1, 200, "OK", false); + ByteBuffer header = BufferUtil.allocate(4096); + HttpGenerator.Result result = generator.generateResponse(info, header, null, null, true); + Assert.assertSame(HttpGenerator.Result.FLUSH, result); + String headers = BufferUtil.toString(header); + Assert.assertTrue(headers.contains(HttpHeaderValue.KEEP_ALIVE.asString())); + Assert.assertTrue(headers.contains(customValue)); + } } From 5ed1a9dfb4b335728d9f13a6d90ef9d526fb14d5 Mon Sep 17 00:00:00 2001 From: Joakim Erdfelt Date: Tue, 28 Jan 2014 11:57:44 -0700 Subject: [PATCH 25/54] Adding parameterized version of HttpGeneratorServerTest.testHTTP() --- .../jetty/http/HttpGeneratorServerTRTest.java | 376 ++++++++++++++++++ 1 file changed, 376 insertions(+) create mode 100644 jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTRTest.java diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTRTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTRTest.java new file mode 100644 index 00000000000..a3f543780a3 --- /dev/null +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTRTest.java @@ -0,0 +1,376 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.http; + +import static org.hamcrest.Matchers.*; +import static org.junit.Assert.*; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.eclipse.jetty.util.BufferUtil; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameter; +import org.junit.runners.Parameterized.Parameters; + +@RunWith(Parameterized.class) +public class HttpGeneratorServerTRTest +{ + private static class TR + { + private HttpFields _fields = new HttpFields(); + private final String _body; + private final int _code; + String _connection; + int _contentLength; + String _contentType; + private final boolean _head; + String _other; + String _te; + + private TR(int code, String contentType, int contentLength, String content, boolean head) + { + _code = code; + _contentType = contentType; + _contentLength = contentLength; + _other = "value"; + _body = content; + _head = head; + } + + private String build(int version, HttpGenerator gen, String reason, String connection, String te, int nchunks) throws Exception + { + String response = ""; + _connection = connection; + _te = te; + + if (_contentType != null) + _fields.put("Content-Type",_contentType); + if (_contentLength >= 0) + _fields.put("Content-Length","" + _contentLength); + if (_connection != null) + _fields.put("Connection",_connection); + if (_te != null) + _fields.put("Transfer-Encoding",_te); + if (_other != null) + _fields.put("Other",_other); + + ByteBuffer source = _body == null?null:BufferUtil.toBuffer(_body); + ByteBuffer[] chunks = new ByteBuffer[nchunks]; + ByteBuffer content = null; + int c = 0; + if (source != null) + { + for (int i = 0; i < nchunks; i++) + { + chunks[i] = source.duplicate(); + chunks[i].position(i * (source.capacity() / nchunks)); + if (i > 0) + chunks[i - 1].limit(chunks[i].position()); + } + content = chunks[c++]; + // System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content)); + } + ByteBuffer header = null; + ByteBuffer chunk = null; + HttpGenerator.ResponseInfo info = null; + + loop: while (true) + { + // if we have unwritten content + if (source != null && content != null && content.remaining() == 0 && c < nchunks) + { + content = chunks[c++]; + // System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content)); + } + + // Generate + boolean last = !BufferUtil.hasContent(content); + + HttpGenerator.Result result = gen.generateResponse(info,header,chunk,content,last); + + switch (result) + { + case NEED_INFO: + info = new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version),_fields,_contentLength,_code,reason,_head); + continue; + + case NEED_HEADER: + header = BufferUtil.allocate(2048); + continue; + + case NEED_CHUNK: + chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE); + continue; + + case FLUSH: + if (BufferUtil.hasContent(header)) + { + response += BufferUtil.toString(header); + header.position(header.limit()); + } + if (BufferUtil.hasContent(chunk)) + { + response += BufferUtil.toString(chunk); + chunk.position(chunk.limit()); + } + if (BufferUtil.hasContent(content)) + { + response += BufferUtil.toString(content); + content.position(content.limit()); + } + break; + + case CONTINUE: + continue; + + case SHUTDOWN_OUT: + break; + + case DONE: + break loop; + } + } + return response; + } + + @Override + public String toString() + { + return "[" + _code + "," + _contentType + "," + _contentLength + "," + (_body == null?"null":"content") + "]"; + } + + public HttpFields getHttpFields() + { + return _fields; + } + } + + private class Handler implements HttpParser.ResponseHandler + { + private final List _hdr = new ArrayList<>(); + private final List _val = new ArrayList<>(); + private int _status; + private HttpVersion _version; + + @Override + public boolean content(ByteBuffer ref) + { + if (_content == null) + _content = ""; + _content += BufferUtil.toString(ref); + ref.position(ref.limit()); + return false; + } + + @Override + public void earlyEOF() + { + } + + @Override + public boolean headerComplete() + { + _content = null; + return false; + } + + @Override + public boolean messageComplete() + { + return true; + } + + @Override + public boolean parsedHeader(HttpField field) + { + _hdr.add(field.getName()); + _val.add(field.getValue()); + return false; + } + + @Override + public boolean startResponse(HttpVersion version, int status, String reason) + { + _version = version; + _status = status; + _reason = reason; + return false; + } + + @Override + public void badMessage(int status, String reason) + { + throw new IllegalStateException(reason); + } + + @Override + public int getHeaderCacheSize() + { + return 256; + } + } + + public final static String CONTENT = "The quick brown fox jumped over the lazy dog.\nNow is the time for all good men to come to the aid of the party\nThe moon is blue to a fish in love.\n"; + + private static class Run + { + public static Run[] as(TR tr, int ver, int chunks, ConnectionType connection) + { + Run run = new Run(); + run.tr = tr; + run.httpVersion = ver; + run.chunks = chunks; + run.connection = connection; + return new Run[] { run }; + } + + TR tr; + ConnectionType connection; + int httpVersion; + int chunks; + + @Override + public String toString() + { + return String.format("tr=%s,ver=%d,chunks=%d,connection=%s",tr,httpVersion,chunks,connection.name()); + } + } + + private enum ConnectionType + { + NONE(null, 9, 10, 11), + KEEP_ALIVE("keep-alive", 9, 10, 11), + CLOSE("close", 9, 10, 11), + TE_CLOSE("TE, close", 11); + + private String val; + private int[] supportedHttpVersions; + + private ConnectionType(String val, int... supportedHttpVersions) + { + this.val = val; + this.supportedHttpVersions = supportedHttpVersions; + } + + public boolean isSupportedByHttp(int version) + { + for (int supported : supportedHttpVersions) + { + if (supported == version) + { + return true; + } + } + return false; + } + } + + @Parameters(name = "{0}") + public static Collection data() + { + TR[] trs = { + /* 0 */new TR(200,null,-1,null,false), + /* 1 */new TR(200,null,-1,CONTENT,false), + /* 2 */new TR(200,null,CONTENT.length(),null,true), + /* 3 */new TR(200,null,CONTENT.length(),CONTENT,false), + /* 4 */new TR(200,"text/html",-1,null,true), + /* 5 */new TR(200,"text/html",-1,CONTENT,false), + /* 6 */new TR(200,"text/html",CONTENT.length(),null,true), + /* 7 */new TR(200,"text/html",CONTENT.length(),CONTENT,false), }; + + List data = new ArrayList<>(); + + // For each test result + for (TR tr : trs) + { + // Loop over HTTP versions + for (int v = 9; v <= 11; v++) + { + // Loop over chunks + for (int chunks = 1; chunks <= 6; chunks++) + { + // Loop over Connection values + for (ConnectionType connection : ConnectionType.values()) + { + if (connection.isSupportedByHttp(v)) + { + data.add(Run.as(tr,v,chunks,connection)); + } + } + } + } + } + + return data; + } + + @Parameter(value = 0) + public Run run; + + private String _content; + private String _reason; + + @Test + public void testHTTP() throws Exception + { + Handler handler = new Handler(); + + HttpGenerator gen = new HttpGenerator(); + + String t = run.toString(); + + run.tr.getHttpFields().clear(); + + String response = run.tr.build(run.httpVersion,gen,"OK\r\nTest",run.connection.val,null,run.chunks); + + if (run.httpVersion == 9) + { + assertFalse(t,gen.isPersistent()); + if (run.tr._body != null) + assertEquals(t,run.tr._body,response); + return; + } + + HttpParser parser = new HttpParser(handler); + parser.setHeadResponse(run.tr._head); + + parser.parseNext(BufferUtil.toBuffer(response)); + + if (run.tr._body != null) + assertEquals(t,run.tr._body,this._content); + + if (run.httpVersion == 10) + assertTrue(t,gen.isPersistent() || run.tr._contentLength >= 0 || run.connection == ConnectionType.CLOSE || run.connection == ConnectionType.NONE); + else + assertTrue(t,gen.isPersistent() || run.connection == ConnectionType.CLOSE || run.connection == ConnectionType.TE_CLOSE); + + if (run.httpVersion > 9) + assertEquals("OK??Test",_reason); + + if (_content == null) + assertTrue(t,run.tr._body == null); + else + assertThat(t,run.tr._contentLength,either(equalTo(_content.length())).or(equalTo(-1))); + } +} From cbdfd87d78cafd0b7d6809e95055828887f79b3c Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Wed, 29 Jan 2014 11:19:45 +0100 Subject: [PATCH 26/54] 426870 - HTTP 1.0 Request with Connection: keep-alive and response content hangs. Fixed HttpGenerator to stay in the EOF_CONTENT mode if such case is detected (while before it was moving to NO_CONTENT mode). By staying in EOF_CONTENT mode the generator is made non-persistent and eventually the connection is closed, signaling the end-of-content to the client. --- .../client/http/HttpConnectionOverHTTP.java | 2 +- .../eclipse/jetty/client/HttpClientTest.java | 83 +++++++++++++++++++ .../org/eclipse/jetty/http/HttpGenerator.java | 16 ++-- .../jetty/http/HttpGeneratorServerTest.java | 2 +- 4 files changed, 93 insertions(+), 10 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java index b1163d2492e..32d7799887c 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java @@ -76,7 +76,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec fillInterested(); } - protected boolean isClosed() + public boolean isClosed() { return closed.get(); } diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java index 40426b5b927..87c03e43f7a 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java @@ -48,6 +48,7 @@ import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.ContentProvider; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Destination; @@ -58,12 +59,16 @@ import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.client.http.HttpDestinationOverHTTP; import org.eclipse.jetty.client.util.BufferingResponseListener; import org.eclipse.jetty.client.util.BytesContentProvider; +import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.server.handler.AbstractHandler; import org.eclipse.jetty.toolchain.test.TestingDir; import org.eclipse.jetty.toolchain.test.annotation.Slow; +import org.eclipse.jetty.util.FuturePromise; import org.eclipse.jetty.util.IO; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.junit.Assert; @@ -1073,4 +1078,82 @@ public class HttpClientTest extends AbstractHttpClientServerTest Assert.assertEquals(200, response.getStatus()); } + + @Test + public void testHTTP10WithKeepAliveAndContentLength() throws Exception + { + start(new AbstractHandler() + { + @Override + public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + // Send the headers at this point, then write the content + byte[] content = "TEST".getBytes("UTF-8"); + response.setContentLength(content.length); + response.flushBuffer(); + response.getOutputStream().write(content); + } + }); + + ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) + .scheme(scheme) + .version(HttpVersion.HTTP_1_0) + .header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()) + .timeout(5, TimeUnit.SECONDS) + .send(); + + Assert.assertEquals(200, response.getStatus()); + Assert.assertTrue(response.getHeaders().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())); + } + + @Test + public void testHTTP10WithKeepAliveAndNoContentLength() throws Exception + { + start(new AbstractHandler() + { + @Override + public void handle(String target, org.eclipse.jetty.server.Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException + { + // Send the headers at this point, then write the content + response.flushBuffer(); + response.getOutputStream().print("TEST"); + } + }); + + FuturePromise promise = new FuturePromise<>(); + Destination destination = client.getDestination(scheme, "localhost", connector.getLocalPort()); + destination.newConnection(promise); + try (Connection connection = promise.get(5, TimeUnit.SECONDS)) + { + long timeout = 5000; + Request request = client.newRequest(destination.getHost(), destination.getPort()) + .scheme(destination.getScheme()) + .version(HttpVersion.HTTP_1_0) + .header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()) + .timeout(timeout, TimeUnit.MILLISECONDS); + + FutureResponseListener listener = new FutureResponseListener(request); + connection.send(request, listener); + ContentResponse response = listener.get(2 * timeout, TimeUnit.MILLISECONDS); + + Assert.assertEquals(200, response.getStatus()); + Assert.assertTrue(((HttpConnectionOverHTTP)connection).isClosed()); + } + } + + @Test + public void testHTTP10WithKeepAliveAndNoContent() throws Exception + { + start(new EmptyServerHandler()); + + ContentResponse response = client.newRequest("localhost", connector.getLocalPort()) + .scheme(scheme) + .version(HttpVersion.HTTP_1_0) + .header(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString()) + .timeout(5, TimeUnit.SECONDS) + .send(); + + Assert.assertEquals(200, response.getStatus()); + Assert.assertTrue(response.getHeaders().contains(HttpHeader.CONNECTION, HttpHeaderValue.KEEP_ALIVE.asString())); + } } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java index 684570bc933..e99d3cc8153 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java @@ -105,7 +105,6 @@ public class HttpGenerator _persistent = null; _contentPrepared = 0; _needCRLF = false; - _noContent=false; } /* ------------------------------------------------------------ */ @@ -744,13 +743,14 @@ public class HttpGenerator } else { - // No idea, so we must assume that a body is coming - _endOfContent = (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal() ) ? EndOfContent.EOF_CONTENT : EndOfContent.CHUNKED_CONTENT; - if (response!=null && _endOfContent==EndOfContent.EOF_CONTENT) - { - _endOfContent=EndOfContent.NO_CONTENT; - _noContent=true; - } + // No idea, so we must assume that a body is coming. + _endOfContent = EndOfContent.CHUNKED_CONTENT; + // HTTP 1.0 does not understand chunked content, so we must use EOF content. + // For a request with HTTP 1.0 & Connection: keep-alive + // we *must* close the connection, otherwise the client + // has no way to detect the end of the content. + if (!isPersistent() || _info.getHttpVersion().ordinal() < HttpVersion.HTTP_1_1.ordinal()) + _endOfContent = EndOfContent.EOF_CONTENT; } break; diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java index 6c2ebdca339..9a9d4002b52 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java @@ -294,7 +294,7 @@ public class HttpGeneratorServerTest assertEquals(t, tr[r]._body, this._content); if (v == 10) - assertTrue(t, gen.isPersistent() || tr[r]._contentLength >= 0 || c == 2 || c == 0); + assertTrue(t, gen.isPersistent() || tr[r]._contentLength >= 0 || c == 2 || c == 1 || c == 0); else assertTrue(t, gen.isPersistent() || c == 2 || c == 3); From 3486046f1e0cf38745d3055d449552420acec59a Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Wed, 29 Jan 2014 18:26:32 +0100 Subject: [PATCH 27/54] 426870 - HTTP 1.0 Request with Connection: keep-alive and response content hangs. Refactored tests. --- ....java => HttpGeneratorServerHTTPTest.java} | 198 ++++++------ .../jetty/http/HttpGeneratorServerTest.java | 300 +----------------- 2 files changed, 106 insertions(+), 392 deletions(-) rename jetty-http/src/test/java/org/eclipse/jetty/http/{HttpGeneratorServerTRTest.java => HttpGeneratorServerHTTPTest.java} (72%) diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTRTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java similarity index 72% rename from jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTRTest.java rename to jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java index a3f543780a3..2925f3e41f2 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTRTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java @@ -18,12 +18,10 @@ package org.eclipse.jetty.http; -import static org.hamcrest.Matchers.*; -import static org.junit.Assert.*; - import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collection; +import java.util.EnumSet; import java.util.List; import org.eclipse.jetty.util.BufferUtil; @@ -33,22 +31,77 @@ import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; +import static org.hamcrest.Matchers.either; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; + @RunWith(Parameterized.class) -public class HttpGeneratorServerTRTest +public class HttpGeneratorServerHTTPTest { - private static class TR + @Parameter(value = 0) + public Run run; + private String _content; + private String _reason; + + @Test + public void testHTTP() throws Exception + { + Handler handler = new Handler(); + + HttpGenerator gen = new HttpGenerator(); + + String t = run.toString(); + + run.result.getHttpFields().clear(); + + String response = run.result.build(run.httpVersion, gen, "OK\r\nTest", run.connection.val, null, run.chunks); + + if (run.httpVersion == 9) + { + assertFalse(t, gen.isPersistent()); + if (run.result._body != null) + assertEquals(t, run.result._body, response); + return; + } + + HttpParser parser = new HttpParser(handler); + parser.setHeadResponse(run.result._head); + + parser.parseNext(BufferUtil.toBuffer(response)); + + if (run.result._body != null) + assertEquals(t, run.result._body, this._content); + + if (run.httpVersion == 10) + assertTrue(t, gen.isPersistent() || run.result._contentLength >= 0 || EnumSet.of(ConnectionType.CLOSE, ConnectionType.KEEP_ALIVE, ConnectionType.NONE).contains(run.connection)); + else + assertTrue(t, gen.isPersistent() || EnumSet.of(ConnectionType.CLOSE, ConnectionType.TE_CLOSE).contains(run.connection)); + + if (run.httpVersion > 9) + assertEquals("OK??Test", _reason); + + if (_content == null) + assertTrue(t, run.result._body == null); + else + assertThat(t, run.result._contentLength, either(equalTo(_content.length())).or(equalTo(-1))); + } + + private static class Result { private HttpFields _fields = new HttpFields(); private final String _body; private final int _code; - String _connection; - int _contentLength; - String _contentType; + private String _connection; + private int _contentLength; + private String _contentType; private final boolean _head; - String _other; - String _te; + private String _other; + private String _te; - private TR(int code, String contentType, int contentLength, String content, boolean head) + private Result(int code, String contentType, int contentLength, String content, boolean head) { _code = code; _contentType = contentType; @@ -65,17 +118,17 @@ public class HttpGeneratorServerTRTest _te = te; if (_contentType != null) - _fields.put("Content-Type",_contentType); + _fields.put("Content-Type", _contentType); if (_contentLength >= 0) - _fields.put("Content-Length","" + _contentLength); + _fields.put("Content-Length", "" + _contentLength); if (_connection != null) - _fields.put("Connection",_connection); + _fields.put("Connection", _connection); if (_te != null) - _fields.put("Transfer-Encoding",_te); + _fields.put("Transfer-Encoding", _te); if (_other != null) - _fields.put("Other",_other); + _fields.put("Other", _other); - ByteBuffer source = _body == null?null:BufferUtil.toBuffer(_body); + ByteBuffer source = _body == null ? null : BufferUtil.toBuffer(_body); ByteBuffer[] chunks = new ByteBuffer[nchunks]; ByteBuffer content = null; int c = 0; @@ -89,30 +142,27 @@ public class HttpGeneratorServerTRTest chunks[i - 1].limit(chunks[i].position()); } content = chunks[c++]; - // System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content)); } ByteBuffer header = null; ByteBuffer chunk = null; HttpGenerator.ResponseInfo info = null; - loop: while (true) + loop: + while (true) { // if we have unwritten content if (source != null && content != null && content.remaining() == 0 && c < nchunks) - { content = chunks[c++]; - // System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content)); - } // Generate boolean last = !BufferUtil.hasContent(content); - HttpGenerator.Result result = gen.generateResponse(info,header,chunk,content,last); + HttpGenerator.Result result = gen.generateResponse(info, header, chunk, content, last); switch (result) { case NEED_INFO: - info = new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version),_fields,_contentLength,_code,reason,_head); + info = new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version), _fields, _contentLength, _code, reason, _head); continue; case NEED_HEADER: @@ -157,7 +207,7 @@ public class HttpGeneratorServerTRTest @Override public String toString() { - return "[" + _code + "," + _contentType + "," + _contentLength + "," + (_body == null?"null":"content") + "]"; + return "[" + _code + "," + _contentType + "," + _contentLength + "," + (_body == null ? "null" : "content") + "]"; } public HttpFields getHttpFields() @@ -168,11 +218,6 @@ public class HttpGeneratorServerTRTest private class Handler implements HttpParser.ResponseHandler { - private final List _hdr = new ArrayList<>(); - private final List _val = new ArrayList<>(); - private int _status; - private HttpVersion _version; - @Override public boolean content(ByteBuffer ref) { @@ -204,16 +249,12 @@ public class HttpGeneratorServerTRTest @Override public boolean parsedHeader(HttpField field) { - _hdr.add(field.getName()); - _val.add(field.getValue()); return false; } @Override public boolean startResponse(HttpVersion version, int status, String reason) { - _version = version; - _status = status; _reason = reason; return false; } @@ -235,25 +276,25 @@ public class HttpGeneratorServerTRTest private static class Run { - public static Run[] as(TR tr, int ver, int chunks, ConnectionType connection) + public static Run[] as(Result result, int ver, int chunks, ConnectionType connection) { Run run = new Run(); - run.tr = tr; + run.result = result; run.httpVersion = ver; run.chunks = chunks; run.connection = connection; - return new Run[] { run }; + return new Run[]{run}; } - TR tr; - ConnectionType connection; - int httpVersion; - int chunks; + private Result result; + private ConnectionType connection; + private int httpVersion; + private int chunks; @Override public String toString() { - return String.format("tr=%s,ver=%d,chunks=%d,connection=%s",tr,httpVersion,chunks,connection.name()); + return String.format("result=%s,version=%d,chunks=%d,connection=%s", result, httpVersion, chunks, connection.name()); } } @@ -289,20 +330,21 @@ public class HttpGeneratorServerTRTest @Parameters(name = "{0}") public static Collection data() { - TR[] trs = { - /* 0 */new TR(200,null,-1,null,false), - /* 1 */new TR(200,null,-1,CONTENT,false), - /* 2 */new TR(200,null,CONTENT.length(),null,true), - /* 3 */new TR(200,null,CONTENT.length(),CONTENT,false), - /* 4 */new TR(200,"text/html",-1,null,true), - /* 5 */new TR(200,"text/html",-1,CONTENT,false), - /* 6 */new TR(200,"text/html",CONTENT.length(),null,true), - /* 7 */new TR(200,"text/html",CONTENT.length(),CONTENT,false), }; + Result[] results = { + new Result(200, null, -1, null, false), + new Result(200, null, -1, CONTENT, false), + new Result(200, null, CONTENT.length(), null, true), + new Result(200, null, CONTENT.length(), CONTENT, false), + new Result(200, "text/html", -1, null, true), + new Result(200, "text/html", -1, CONTENT, false), + new Result(200, "text/html", CONTENT.length(), null, true), + new Result(200, "text/html", CONTENT.length(), CONTENT, false) + }; List data = new ArrayList<>(); // For each test result - for (TR tr : trs) + for (Result result : results) { // Loop over HTTP versions for (int v = 9; v <= 11; v++) @@ -315,62 +357,12 @@ public class HttpGeneratorServerTRTest { if (connection.isSupportedByHttp(v)) { - data.add(Run.as(tr,v,chunks,connection)); + data.add(Run.as(result, v, chunks, connection)); } } } } } - return data; } - - @Parameter(value = 0) - public Run run; - - private String _content; - private String _reason; - - @Test - public void testHTTP() throws Exception - { - Handler handler = new Handler(); - - HttpGenerator gen = new HttpGenerator(); - - String t = run.toString(); - - run.tr.getHttpFields().clear(); - - String response = run.tr.build(run.httpVersion,gen,"OK\r\nTest",run.connection.val,null,run.chunks); - - if (run.httpVersion == 9) - { - assertFalse(t,gen.isPersistent()); - if (run.tr._body != null) - assertEquals(t,run.tr._body,response); - return; - } - - HttpParser parser = new HttpParser(handler); - parser.setHeadResponse(run.tr._head); - - parser.parseNext(BufferUtil.toBuffer(response)); - - if (run.tr._body != null) - assertEquals(t,run.tr._body,this._content); - - if (run.httpVersion == 10) - assertTrue(t,gen.isPersistent() || run.tr._contentLength >= 0 || run.connection == ConnectionType.CLOSE || run.connection == ConnectionType.NONE); - else - assertTrue(t,gen.isPersistent() || run.connection == ConnectionType.CLOSE || run.connection == ConnectionType.TE_CLOSE); - - if (run.httpVersion > 9) - assertEquals("OK??Test",_reason); - - if (_content == null) - assertTrue(t,run.tr._body == null); - else - assertThat(t,run.tr._contentLength,either(equalTo(_content.length())).or(equalTo(-1))); - } } diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java index 9a9d4002b52..a65b3028806 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerTest.java @@ -19,8 +19,6 @@ package org.eclipse.jetty.http; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; import org.eclipse.jetty.http.HttpGenerator.ResponseInfo; import org.eclipse.jetty.util.BufferUtil; @@ -28,301 +26,25 @@ import org.junit.Assert; import org.junit.Test; import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.either; -import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; public class HttpGeneratorServerTest { - private class Handler implements HttpParser.ResponseHandler - { - @Override - public boolean content(ByteBuffer ref) - { - if (_content == null) - _content = ""; - _content += BufferUtil.toString(ref); - ref.position(ref.limit()); - return false; - } - - @Override - public void earlyEOF() - { - } - - @Override - public boolean headerComplete() - { - _content = null; - return false; - } - - @Override - public boolean messageComplete() - { - return true; - } - - @Override - public boolean parsedHeader(HttpField field) - { - _hdr.add(field.getName()); - _val.add(field.getValue()); - return false; - } - - @Override - public boolean startResponse(HttpVersion version, int status, String reason) - { - _version = version; - _status = status; - _reason = reason; - return false; - } - - @Override - public void badMessage(int status, String reason) - { - throw new IllegalStateException(reason); - } - - @Override - public int getHeaderCacheSize() - { - return 256; - } - } - - private static class TR - { - private HttpFields _fields = new HttpFields(); - private final String _body; - private final int _code; - String _connection; - int _contentLength; - String _contentType; - private final boolean _head; - String _other; - String _te; - - private TR(int code, String contentType, int contentLength, String content, boolean head) - { - _code = code; - _contentType = contentType; - _contentLength = contentLength; - _other = "value"; - _body = content; - _head = head; - } - - private String build(int version, HttpGenerator gen, String reason, String connection, String te, int nchunks) throws Exception - { - String response = ""; - _connection = connection; - _te = te; - - if (_contentType != null) - _fields.put("Content-Type", _contentType); - if (_contentLength >= 0) - _fields.put("Content-Length", "" + _contentLength); - if (_connection != null) - _fields.put("Connection", _connection); - if (_te != null) - _fields.put("Transfer-Encoding", _te); - if (_other != null) - _fields.put("Other", _other); - - ByteBuffer source = _body == null ? null : BufferUtil.toBuffer(_body); - ByteBuffer[] chunks = new ByteBuffer[nchunks]; - ByteBuffer content = null; - int c = 0; - if (source != null) - { - for (int i = 0; i < nchunks; i++) - { - chunks[i] = source.duplicate(); - chunks[i].position(i * (source.capacity() / nchunks)); - if (i > 0) - chunks[i - 1].limit(chunks[i].position()); - } - content = chunks[c++]; - // System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content)); - } - ByteBuffer header = null; - ByteBuffer chunk = null; - HttpGenerator.ResponseInfo info = null; - - loop: - while (true) - { - // if we have unwritten content - if (source != null && content != null && content.remaining() == 0 && c < nchunks) - { - content = chunks[c++]; - // System.err.printf("content %d %s%n",c,BufferUtil.toDetailString(content)); - } - - // Generate - boolean last = !BufferUtil.hasContent(content); - - HttpGenerator.Result result = gen.generateResponse(info, header, chunk, content, last); - - switch (result) - { - case NEED_INFO: - info = new HttpGenerator.ResponseInfo(HttpVersion.fromVersion(version), _fields, _contentLength, _code, reason, _head); - continue; - - case NEED_HEADER: - header = BufferUtil.allocate(2048); - continue; - - case NEED_CHUNK: - chunk = BufferUtil.allocate(HttpGenerator.CHUNK_SIZE); - continue; - - case FLUSH: - if (BufferUtil.hasContent(header)) - { - response += BufferUtil.toString(header); - header.position(header.limit()); - } - if (BufferUtil.hasContent(chunk)) - { - response += BufferUtil.toString(chunk); - chunk.position(chunk.limit()); - } - if (BufferUtil.hasContent(content)) - { - response += BufferUtil.toString(content); - content.position(content.limit()); - } - break; - - case CONTINUE: - continue; - - case SHUTDOWN_OUT: - break; - - case DONE: - break loop; - } - } - return response; - } - - @Override - public String toString() - { - return "[" + _code + "," + _contentType + "," + _contentLength + "," + (_body == null ? "null" : "content") + "]"; - } - - public HttpFields getHttpFields() - { - return _fields; - } - } - - public final static String[] connections = {null, "keep-alive", "close", "TE, close"}; - public final static String CONTENT = "The quick brown fox jumped over the lazy dog.\nNow is the time for all good men to come to the aid of the party\nThe moon is blue to a fish in love.\n"; - - private final List _hdr = new ArrayList<>(); - private final List _val = new ArrayList<>(); - private String _content; - private String _reason; - private int _status; - private HttpVersion _version; - private final TR[] tr = - { - /* 0 */ new TR(200, null, -1, null, false), - /* 1 */ new TR(200, null, -1, CONTENT, false), - /* 2 */ new TR(200, null, CONTENT.length(), null, true), - /* 3 */ new TR(200, null, CONTENT.length(), CONTENT, false), - /* 4 */ new TR(200, "text/html", -1, null, true), - /* 5 */ new TR(200, "text/html", -1, CONTENT, false), - /* 6 */ new TR(200, "text/html", CONTENT.length(), null, true), - /* 7 */ new TR(200, "text/html", CONTENT.length(), CONTENT, false), - }; - - @Test - public void testHTTP() throws Exception - { - Handler handler = new Handler(); - - // Loop over HTTP versions - for (int v = 9; v <= 11; v++) - { - // For each test result - for (int r = 0; r < tr.length; r++) - { - HttpGenerator gen = new HttpGenerator(); - - // Loop over chunks - for (int chunks = 1; chunks <= 6; chunks++) - { - // Loop over Connection values - for (int c = 0; c < (v == 11 ? connections.length : (connections.length - 1)); c++) - { - String t = "v=" + v + ",chunks=" + chunks + ",connection=" + connections[c] + ",tr=" + r + "=" + tr[r]; - - gen.reset(); - tr[r].getHttpFields().clear(); - - String response = tr[r].build(v, gen, "OK\r\nTest", connections[c], null, chunks); - - if (v == 9) - { - assertFalse(t, gen.isPersistent()); - if (tr[r]._body != null) - assertEquals(t, tr[r]._body, response); - continue; - } - - HttpParser parser = new HttpParser(handler); - parser.setHeadResponse(tr[r]._head); - - parser.parseNext(BufferUtil.toBuffer(response)); - - if (tr[r]._body != null) - assertEquals(t, tr[r]._body, this._content); - - if (v == 10) - assertTrue(t, gen.isPersistent() || tr[r]._contentLength >= 0 || c == 2 || c == 1 || c == 0); - else - assertTrue(t, gen.isPersistent() || c == 2 || c == 3); - - if (v > 9) - assertEquals("OK??Test", _reason); - - if (_content == null) - assertTrue(t, tr[r]._body == null); - else - assertThat(t, tr[r]._contentLength, either(equalTo(_content.length())).or(equalTo(-1))); - } - } - } - } - } - @Test public void testSendServerXPoweredBy() throws Exception { ByteBuffer header = BufferUtil.allocate(8096); ResponseInfo info = new ResponseInfo(HttpVersion.HTTP_1_1, new HttpFields(), -1, 200, null, false); HttpFields fields = new HttpFields(); - fields.add(HttpHeader.SERVER,"SomeServer"); - fields.add(HttpHeader.X_POWERED_BY,"SomePower"); + fields.add(HttpHeader.SERVER, "SomeServer"); + fields.add(HttpHeader.X_POWERED_BY, "SomePower"); ResponseInfo infoF = new ResponseInfo(HttpVersion.HTTP_1_1, fields, -1, 200, null, false); String head; - - HttpGenerator gen = new HttpGenerator(true,true); + + HttpGenerator gen = new HttpGenerator(true, true); gen.generateResponse(info, header, null, null, true); head = BufferUtil.toString(header); BufferUtil.clear(header); @@ -339,8 +61,8 @@ public class HttpGeneratorServerTest assertThat(head, containsString("X-Powered-By: Jetty(9.x.x)")); assertThat(head, containsString("X-Powered-By: SomePower")); gen.reset(); - - gen = new HttpGenerator(false,false); + + gen = new HttpGenerator(false, false); gen.generateResponse(info, header, null, null, true); head = BufferUtil.toString(header); BufferUtil.clear(header); @@ -356,7 +78,7 @@ public class HttpGeneratorServerTest assertThat(head, containsString("Server: SomeServer")); assertThat(head, not(containsString("X-Powered-By: Jetty(9.x.x)"))); assertThat(head, containsString("X-Powered-By: SomePower")); - gen.reset(); + gen.reset(); } @Test @@ -453,7 +175,7 @@ public class HttpGeneratorServerTest out += BufferUtil.toString(content0); BufferUtil.clear(content0); - result = gen.generateResponse(null,null,chunk, content1, false); + result = gen.generateResponse(null, null, chunk, content1, false); assertEquals(HttpGenerator.Result.FLUSH, result); assertEquals(HttpGenerator.State.COMMITTED, gen.getState()); out += BufferUtil.toString(chunk); @@ -461,17 +183,17 @@ public class HttpGeneratorServerTest out += BufferUtil.toString(content1); BufferUtil.clear(content1); - result = gen.generateResponse(null,null,chunk, null, true); + result = gen.generateResponse(null, null, chunk, null, true); assertEquals(HttpGenerator.Result.CONTINUE, result); assertEquals(HttpGenerator.State.COMPLETING, gen.getState()); - result = gen.generateResponse(null,null,chunk, null, true); + result = gen.generateResponse(null, null, chunk, null, true); assertEquals(HttpGenerator.Result.FLUSH, result); assertEquals(HttpGenerator.State.COMPLETING, gen.getState()); out += BufferUtil.toString(chunk); BufferUtil.clear(chunk); - result = gen.generateResponse(null,null,chunk, null, true); + result = gen.generateResponse(null, null, chunk, null, true); assertEquals(HttpGenerator.Result.DONE, result); assertEquals(HttpGenerator.State.END, gen.getState()); From a09d05b5cb548abf32c2dd0998d3e79df21b11d8 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Thu, 30 Jan 2014 15:52:15 +1100 Subject: [PATCH 28/54] protected ServletHandler API --- .../main/java/org/eclipse/jetty/servlet/ServletHandler.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java index 8062db76116..ebdf3c8a5fb 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java @@ -319,7 +319,7 @@ public class ServletHandler extends ScopedHandler } /* ------------------------------------------------------------ */ - IdentityService getIdentityService() + protected IdentityService getIdentityService() { return _identityService; } @@ -647,7 +647,7 @@ public class ServletHandler extends ScopedHandler } /* ------------------------------------------------------------ */ - private FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder) + protected FilterChain getFilterChain(Request baseRequest, String pathInContext, ServletHolder servletHolder) { String key=pathInContext==null?servletHolder.getName():pathInContext; int dispatch = FilterMapping.dispatch(baseRequest.getDispatcherType()); @@ -735,7 +735,7 @@ public class ServletHandler extends ScopedHandler } /* ------------------------------------------------------------ */ - private void invalidateChainsCache() + protected void invalidateChainsCache() { if (_chainLRU[FilterMapping.REQUEST]!=null) { From d4368d1018fc123f6ca6104aeba2067604def604 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Fri, 31 Jan 2014 16:24:00 +1100 Subject: [PATCH 29/54] 427068 ServletContext.getClassLoader should only check privileges if a SecurityManager exists --- .../jetty/server/handler/ContextHandler.java | 47 +++++++++++++++++-- 1 file changed, 42 insertions(+), 5 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java index 896d6db39ac..3330ddabee7 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/handler/ContextHandler.java @@ -21,6 +21,8 @@ package org.eclipse.jetty.server.handler; import java.io.File; import java.io.IOException; import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; @@ -2258,11 +2260,46 @@ public class ContextHandler extends ScopedHandler implements Attributes, Gracefu } - @Override - public ClassLoader getClassLoader() - { - AccessController.checkPermission(new RuntimePermission("getClassLoader")); - return _classLoader; + @Override + public ClassLoader getClassLoader() + { + if (!_enabled) + throw new UnsupportedOperationException(); + + //no security manager just return the classloader + if (System.getSecurityManager() == null) + return _classLoader; + else + { + //check to see if the classloader of the caller is the same as the context + //classloader, or a parent of it + try + { + Class reflect = Loader.loadClass(getClass(), "sun.reflect.Reflection"); + Method getCallerClass = reflect.getMethod("getCallerClass", Integer.TYPE); + Class caller = (Class)getCallerClass.invoke(null, 2); + + boolean ok = false; + ClassLoader callerLoader = caller.getClassLoader(); + while (!ok && callerLoader != null) + { + if (callerLoader == _classLoader) + ok = true; + else + callerLoader = callerLoader.getParent(); + } + + if (ok) + return _classLoader; + } + catch (Exception e) + { + LOG.warn("Unable to check classloader of caller",e); + } + + AccessController.checkPermission(new RuntimePermission("getClassLoader")); + return _classLoader; + } } @Override From 8cc9d71e20053f6aa4d360ba08d4d3e0738bd833 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Mon, 3 Feb 2014 17:59:31 +1100 Subject: [PATCH 30/54] 426750 Handle autoclose on async writes --- .../org/eclipse/jetty/server/HttpOutput.java | 43 +++++++++++++------ .../eclipse/jetty/server/HttpOutputTest.java | 19 +++++++- 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java index 45eacba982f..1b676da6534 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java @@ -680,20 +680,39 @@ public class HttpOutput extends ServletOutputStream implements Runnable { Throwable th=_onError; _onError=null; - _writeListener.onError(th); + _writeListener.onError(new IOException(th)); close(); } - if (_state.get()==OutputState.READY) + switch(_state.get()) { - try - { - _writeListener.onWritePossible(); - } - catch (Throwable e) - { - _writeListener.onError(e); - close(); - } + case READY: + try + { + _writeListener.onWritePossible(); + } + catch (Throwable e) + { + _writeListener.onError(e); + close(); + } + break; + + case CLOSED: + try + { + // even though a write is not possible, because a close has + // occurred, we need to call onWritePossible to tell async + // producer that the last write completed. + _writeListener.onWritePossible(); + } + catch (Throwable e) + { + _writeListener.onError(e); + } + break; + + default: + } } @@ -722,7 +741,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable break; case CLOSED: - _onError=new EofException("Closed"); + _channel.getState().onWritePossible(); break; default: diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java index ead2774b8fa..4102b474582 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/HttpOutputTest.java @@ -348,7 +348,7 @@ public class HttpOutputTest assertThat(response,containsString("Content-Length")); assertThat(response,containsString("400\tThis is a big file")); } - + @Test public void testWriteLargeKnown() throws Exception { @@ -546,6 +546,22 @@ public class HttpOutputTest assertThat(response,Matchers.not(containsString("Content-Length"))); assertThat(response,containsString("400\tThis is a big file")); } + + @Test + public void testAsyncWriteSimpleKnown() throws Exception + { + final Resource big = Resource.newClassPathResource("simple/simple.txt"); + + _handler._async=true; + _handler._writeLengthIfKnown=true; + _handler._content=BufferUtil.toBuffer(big,false); + _handler._arrayBuffer=new byte[4000]; + + String response=_connector.getResponses("GET / HTTP/1.0\nHost: localhost:80\n\n"); + assertThat(response,containsString("HTTP/1.1 200 OK")); + assertThat(response,containsString("Content-Length: 11")); + assertThat(response,containsString("simple text")); + } static class ContentHandler extends AbstractHandler { @@ -664,7 +680,6 @@ public class HttpOutputTest BufferUtil.flipToFlush(_byteBuffer,0); out.write(_byteBuffer); } - Assert.assertFalse(out.isReady()); } @Override From 3f5a30282df58b3fc28f0d48a726efd77f0b326a Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 3 Feb 2014 11:52:51 +0100 Subject: [PATCH 31/54] Made sure that the number of selectors cannot be zero. --- .../src/main/java/org/eclipse/jetty/server/ServerConnector.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java index c48aa5cd7f7..a7d5fe0f2d8 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java @@ -208,7 +208,7 @@ public class ServerConnector extends AbstractNetworkConnector @Name("factories") ConnectionFactory... factories) { super(server,executor,scheduler,bufferPool,acceptors,factories); - _manager = new ServerConnectorManager(getExecutor(), getScheduler(), selectors >= 0 ? selectors : Runtime.getRuntime().availableProcessors()); + _manager = new ServerConnectorManager(getExecutor(), getScheduler(), selectors > 0 ? selectors : Runtime.getRuntime().availableProcessors()); addBean(_manager, true); } From 5e3c8821bb3ac838903cefac80080c404ac647e8 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 3 Feb 2014 12:05:56 +0100 Subject: [PATCH 32/54] If ClientContainer is being stopped, then we can unregister it from the ShutdownThread. This happens when ClientContainer is added as a managed bean to a ContainerLifeCycle and the container is stopped. --- .../org/eclipse/jetty/websocket/jsr356/ClientContainer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java index 87de5edf6be..4e847b5067e 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/ClientContainer.java @@ -30,7 +30,6 @@ import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Future; - import javax.websocket.ClientEndpoint; import javax.websocket.ClientEndpointConfig; import javax.websocket.DeploymentException; @@ -187,6 +186,7 @@ public class ClientContainer extends ContainerLifeCycle implements WebSocketCont @Override protected void doStop() throws Exception { + ShutdownThread.deregister(this); endpointClientMetadataCache.clear(); super.doStop(); } From cfe248c67beea260f21a73bca019e3241f2cf11d Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 3 Feb 2014 12:49:58 +0100 Subject: [PATCH 33/54] 427128 - Cookies are not sent to the server. Implemented/fixed handling of cookies sent by client to server. --- .../websocket/jsr356/JsrUpgradeListener.java | 20 ++- .../jetty/websocket/jsr356/CookiesTest.java | 122 ++++++++++++++++++ .../jetty/websocket/api/UpgradeRequest.java | 3 +- .../client/ClientUpgradeRequest.java | 14 +- .../websocket/client/WebSocketClient.java | 2 +- 5 files changed, 151 insertions(+), 10 deletions(-) create mode 100644 jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java diff --git a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrUpgradeListener.java b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrUpgradeListener.java index a17656038f1..968f74fa494 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrUpgradeListener.java +++ b/jetty-websocket/javax-websocket-client-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/JsrUpgradeListener.java @@ -18,9 +18,9 @@ package org.eclipse.jetty.websocket.jsr356; +import java.net.HttpCookie; import java.util.List; import java.util.Map; - import javax.websocket.ClientEndpointConfig.Configurator; import org.eclipse.jetty.websocket.api.UpgradeRequest; @@ -46,7 +46,23 @@ public class JsrUpgradeListener implements UpgradeListener Map> headers = request.getHeaders(); configurator.beforeRequest(headers); - request.setHeaders(headers); + + // Handle cookies + for (String name : headers.keySet()) + { + if ("cookie".equalsIgnoreCase(name)) + { + List values = headers.get(name); + if (values != null) + { + for (String cookie : values) + { + List cookies = HttpCookie.parse(cookie); + request.getCookies().addAll(cookies); + } + } + } + } } @Override diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java new file mode 100644 index 00000000000..0bf9abd42b7 --- /dev/null +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java @@ -0,0 +1,122 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.websocket.jsr356; + +import java.net.HttpCookie; +import java.net.URI; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import javax.websocket.ClientEndpointConfig; +import javax.websocket.ContainerProvider; +import javax.websocket.Endpoint; +import javax.websocket.EndpointConfig; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; + +import org.eclipse.jetty.server.Handler; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.server.handler.ContextHandler; +import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest; +import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse; +import org.junit.After; +import org.junit.Assert; +import org.junit.Test; + +public class CookiesTest +{ + private Server server; + private ServerConnector connector; + + protected void startServer(Handler handler) throws Exception + { + server = new Server(); + connector = new ServerConnector(server); + server.addConnector(connector); + + ContextHandler context = new ContextHandler(); + context.setContextPath("/"); + context.setHandler(handler); + server.setHandler(context); + + server.start(); + } + + @After + public void stopServer() throws Exception + { + server.stop(); + } + + @Test + public void testCookiesAreSentToServer() throws Exception + { + final String cookieName = "name"; + final String cookieValue = "value"; + final String cookieString = cookieName + "=" + cookieValue; + startServer(new EchoHandler() + { + @Override + public Object createWebSocket(ServletUpgradeRequest request, ServletUpgradeResponse response) + { + List cookies = request.getCookies(); + Assert.assertNotNull(cookies); + Assert.assertEquals(1, cookies.size()); + HttpCookie cookie = cookies.get(0); + Assert.assertEquals(cookieName, cookie.getName()); + Assert.assertEquals(cookieValue, cookie.getValue()); + + Map> headers = request.getHeaders(); + // Test case insensitivity + Assert.assertTrue(headers.containsKey("cookie")); + List values = headers.get("Cookie"); + Assert.assertNotNull(values); + Assert.assertEquals(1, values.size()); + Assert.assertEquals(cookieString, values.get(0)); + + return super.createWebSocket(request, response); + } + }); + + WebSocketContainer container = ContainerProvider.getWebSocketContainer(); + + ClientEndpointConfig.Builder builder = ClientEndpointConfig.Builder.create(); + builder.configurator(new ClientEndpointConfig.Configurator() + { + @Override + public void beforeRequest(Map> headers) + { + headers.put("Cookie", Collections.singletonList(cookieString)); + } + }); + ClientEndpointConfig config = builder.build(); + + Endpoint endPoint = new Endpoint() + { + @Override + public void onOpen(Session session, EndpointConfig config) + { + } + }; + + Session session = container.connectToServer(endPoint, config, URI.create("ws://localhost:" + connector.getLocalPort())); + session.close(); + } +} diff --git a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeRequest.java b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeRequest.java index bbbe21f7b1c..c76dd5419ac 100644 --- a/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeRequest.java +++ b/jetty-websocket/websocket-api/src/main/java/org/eclipse/jetty/websocket/api/UpgradeRequest.java @@ -262,7 +262,8 @@ public class UpgradeRequest public void setCookies(List cookies) { - this.cookies = cookies; + this.cookies.clear(); + this.cookies.addAll(cookies); } public void setExtensions(List configs) diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java index c042e96defd..15d2f37e046 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/ClientUpgradeRequest.java @@ -24,10 +24,10 @@ import java.net.URI; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.TreeSet; import java.util.concurrent.ThreadLocalRandom; import org.eclipse.jetty.util.B64Code; @@ -44,15 +44,17 @@ import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig; */ public class ClientUpgradeRequest extends UpgradeRequest { - private final static Logger LOG = Log.getLogger(ClientUpgradeRequest.class); - private final static int MAX_KEYS = -1; // maximum number of parameter keys to decode + private static final Logger LOG = Log.getLogger(ClientUpgradeRequest.class); + private static final int MAX_KEYS = -1; // maximum number of parameter keys to decode private static final Set FORBIDDEN_HEADERS; static { - // headers not allowed to be set in ClientUpgradeRequest.headers - FORBIDDEN_HEADERS = new HashSet<>(); + // Headers not allowed to be set in ClientUpgradeRequest.headers. + FORBIDDEN_HEADERS = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + // Cookies are handled explicitly, avoid to add them twice. FORBIDDEN_HEADERS.add("cookie"); + // Headers that cannot be set by applications. FORBIDDEN_HEADERS.add("upgrade"); FORBIDDEN_HEADERS.add("host"); FORBIDDEN_HEADERS.add("connection"); @@ -176,7 +178,7 @@ public class ClientUpgradeRequest extends UpgradeRequest { if (FORBIDDEN_HEADERS.contains(key)) { - LOG.warn("Skipping forbidden header - {}",key); + LOG.debug("Skipping forbidden header - {}",key); continue; // skip } request.append(key).append(": "); diff --git a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java index b9309f8db82..8af85198edd 100644 --- a/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java +++ b/jetty-websocket/websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java @@ -264,7 +264,7 @@ public class WebSocketClient extends ContainerLifeCycle implements SessionListen } super.doStop(); - LOG.info("Stopped {}",this); + LOG.debug("Stopped {}",this); } /** From 5d9360e343b5b9ef1752050f00ec1d6179cc40a2 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 3 Feb 2014 15:31:51 +0100 Subject: [PATCH 34/54] 427254 - Cookies are not sent to the client. Introduced ServletUpgradeResponse.complete(), called when the response is about to be sent to the client, to transfer the headers stored in the superclass (UpgradeResponse.headers) into the HttpServletResponse. --- .../jetty/websocket/jsr356/CookiesTest.java | 57 +++++++++++++++++++ .../websocket/server/HandshakeRFC6455.java | 2 +- .../server/WebSocketServerFactory.java | 3 +- .../servlet/ServletUpgradeResponse.java | 48 ++++++++-------- 4 files changed, 83 insertions(+), 27 deletions(-) diff --git a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java index 0bf9abd42b7..35b272782b3 100644 --- a/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java +++ b/jetty-websocket/javax-websocket-client-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/CookiesTest.java @@ -27,6 +27,7 @@ import javax.websocket.ClientEndpointConfig; import javax.websocket.ContainerProvider; import javax.websocket.Endpoint; import javax.websocket.EndpointConfig; +import javax.websocket.HandshakeResponse; import javax.websocket.Session; import javax.websocket.WebSocketContainer; @@ -119,4 +120,60 @@ public class CookiesTest Session session = container.connectToServer(endPoint, config, URI.create("ws://localhost:" + connector.getLocalPort())); session.close(); } + + @Test + public void testCookiesAreSentToClient() throws Exception + { + final String cookieName = "name"; + final String cookieValue = "value"; + final String cookieDomain = "domain"; + final String cookiePath = "/path"; + startServer(new EchoHandler() + { + @Override + public Object createWebSocket(ServletUpgradeRequest request, ServletUpgradeResponse response) + { + String cookieString = cookieName + "=" + cookieValue + ";Domain=" + cookieDomain + ";Path=" + cookiePath; + response.getHeaders().put("Set-Cookie", Collections.singletonList(cookieString)); + return super.createWebSocket(request, response); + } + }); + + WebSocketContainer container = ContainerProvider.getWebSocketContainer(); + + ClientEndpointConfig.Builder builder = ClientEndpointConfig.Builder.create(); + builder.configurator(new ClientEndpointConfig.Configurator() + { + @Override + public void afterResponse(HandshakeResponse response) + { + Map> headers = response.getHeaders(); + // Test case insensitivity + Assert.assertTrue(headers.containsKey("set-cookie")); + List values = headers.get("Set-Cookie"); + Assert.assertNotNull(values); + Assert.assertEquals(1, values.size()); + + List cookies = HttpCookie.parse(values.get(0)); + Assert.assertEquals(1, cookies.size()); + HttpCookie cookie = cookies.get(0); + Assert.assertEquals(cookieName, cookie.getName()); + Assert.assertEquals(cookieValue, cookie.getValue()); + Assert.assertEquals(cookieDomain, cookie.getDomain()); + Assert.assertEquals(cookiePath, cookie.getPath()); + } + }); + ClientEndpointConfig config = builder.build(); + + Endpoint endPoint = new Endpoint() + { + @Override + public void onOpen(Session session, EndpointConfig config) + { + } + }; + + Session session = container.connectToServer(endPoint, config, URI.create("ws://localhost:" + connector.getLocalPort())); + session.close(); + } } diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/HandshakeRFC6455.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/HandshakeRFC6455.java index eed5fb71a7d..c13f58de850 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/HandshakeRFC6455.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/HandshakeRFC6455.java @@ -19,7 +19,6 @@ package org.eclipse.jetty.websocket.server; import java.io.IOException; - import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig; @@ -65,5 +64,6 @@ public class HandshakeRFC6455 implements WebSocketHandshake } response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS); + response.complete(); } } diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java index 46ab21e95d9..c7c233615e3 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java @@ -32,7 +32,6 @@ import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.Executor; - import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -187,7 +186,7 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc if (websocketPojo == null) { // no creation, sorry - response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + sockresp.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE, "Endpoint Creation Failed"); return false; } diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java index 90943de51ea..450376d5c12 100644 --- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java +++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/ServletUpgradeResponse.java @@ -20,7 +20,7 @@ package org.eclipse.jetty.websocket.servlet; import java.io.IOException; import java.util.List; - +import java.util.Map; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.websocket.api.UpgradeResponse; @@ -31,26 +31,24 @@ import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig; */ public class ServletUpgradeResponse extends UpgradeResponse { - private HttpServletResponse resp; + private HttpServletResponse response; private boolean extensionsNegotiated = false; private boolean subprotocolNegotiated = false; - public ServletUpgradeResponse(HttpServletResponse resp) + public ServletUpgradeResponse(HttpServletResponse response) { - super(); - this.resp = resp; - } - - @Override - public void addHeader(String name, String value) - { - this.resp.addHeader(name,value); + this.response = response; } @Override public int getStatusCode() { - return this.resp.getStatus(); + return response.getStatus(); + } + + public void setStatus(int status) + { + response.setStatus(status); } @Override @@ -61,7 +59,7 @@ public class ServletUpgradeResponse extends UpgradeResponse public boolean isCommitted() { - return this.resp.isCommitted(); + return response.isCommitted(); } public boolean isExtensionsNegotiated() @@ -77,14 +75,16 @@ public class ServletUpgradeResponse extends UpgradeResponse public void sendError(int statusCode, String message) throws IOException { setSuccess(false); - this.resp.sendError(statusCode,message); + complete(); + response.sendError(statusCode, message); } @Override public void sendForbidden(String message) throws IOException { setSuccess(false); - resp.sendError(HttpServletResponse.SC_FORBIDDEN,message); + complete(); + response.sendError(HttpServletResponse.SC_FORBIDDEN, message); } @Override @@ -101,15 +101,15 @@ public class ServletUpgradeResponse extends UpgradeResponse extensionsNegotiated = true; } - @Override - public void setHeader(String name, String value) + public void complete() { - this.resp.setHeader(name,value); + // Transfer all headers to the real HTTP response + for (Map.Entry> entry : getHeaders().entrySet()) + { + for (String value : entry.getValue()) + { + response.addHeader(entry.getKey(), value); + } + } } - - public void setStatus(int status) - { - this.resp.setStatus(status); - } - } From 4844508c5fde59d1353bf4ab15f0ac5f4d1c1ce5 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Tue, 4 Feb 2014 19:25:02 +1100 Subject: [PATCH 35/54] 427245 StackOverflowError when session cannot be de-idled from disk --- .../jetty/server/session/HashedSession.java | 22 +++++++- .../jetty/server/session/IdleSessionTest.java | 51 +++++++++++++++++-- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java index 14dce7f38cb..dc599cc5dc2 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/session/HashedSession.java @@ -50,6 +50,12 @@ public class HashedSession extends AbstractSession * due to serialization failures that are most likely caused by user * data stored in the session that is not serializable. */ private transient boolean _saveFailed = false; + + /** + * True if an attempt has been made to de-idle a session and it failed. Once + * true, the session will not be attempted to be de-idled again. + */ + private transient boolean _deIdleFailed = false; /* ------------------------------------------------------------- */ protected HashedSession(HashSessionManager hashSessionManager, HttpServletRequest request) @@ -68,7 +74,7 @@ public class HashedSession extends AbstractSession /* ------------------------------------------------------------- */ protected void checkValid() { - if (_hashSessionManager._idleSavePeriodMs!=0) + if (!_deIdleFailed && _hashSessionManager._idleSavePeriodMs!=0) deIdle(); super.checkValid(); } @@ -196,7 +202,7 @@ public class HashedSession extends AbstractSession /* ------------------------------------------------------------ */ public synchronized void deIdle() { - if (isIdled()) + if (isIdled() && !_deIdleFailed) { // Access now to prevent race with idling period access(System.currentTimeMillis()); @@ -225,6 +231,7 @@ public class HashedSession extends AbstractSession } catch (Exception e) { + deIdleFailed(); LOG.warn("Problem de-idling session " + super.getId(), e); if (fis != null) IO.close(fis);//Must ensure closed before invalidate invalidate(); @@ -265,4 +272,15 @@ public class HashedSession extends AbstractSession _saveFailed = true; } + /* ------------------------------------------------------------ */ + public synchronized void deIdleFailed() + { + _deIdleFailed = true; + } + + /* ------------------------------------------------------------ */ + public synchronized boolean isDeIdleFailed() + { + return _deIdleFailed; + } } diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java index 20d3351e845..4bcfdd86bb7 100644 --- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java +++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java @@ -38,6 +38,8 @@ import org.eclipse.jetty.server.SessionManager; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.StdErrLog; import org.junit.Test; @@ -49,6 +51,7 @@ import org.junit.Test; */ public class IdleSessionTest { + public class IdleHashTestServer extends HashTestServer { private int _idlePeriod; @@ -108,7 +111,8 @@ public class IdleSessionTest int inactivePeriod = 200; int scavengePeriod = 3; int idlePeriod = 5; - + ((StdErrLog)Log.getLogger(org.eclipse.jetty.server.session.HashedSession.class)).setHideStacks(true); + System.setProperty("org.eclipse.jetty.STACKS", "false"); File storeDir = new File (System.getProperty("java.io.tmpdir"), "idle-test"); storeDir.deleteOnExit(); @@ -135,10 +139,10 @@ public class IdleSessionTest sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); //and wait until the session should be idled out - pause(scavengePeriod * 2); + pause(idlePeriod * 2); //check that the file exists - checkSessionIdled(storeDir); + checkSessionIdled(storeDir, getSessionId(sessionCookie)); //make another request to de-idle the session Request request = client.newRequest(url + "?action=test"); @@ -149,6 +153,23 @@ public class IdleSessionTest //check session de-idled checkSessionDeIdled(storeDir); + //wait again for the session to be idled + pause(idlePeriod * 2); + + //check that it is + checkSessionIdled(storeDir, getSessionId(sessionCookie)); + + + //delete the file + File idleFile = getIdleFile(storeDir, getSessionId(sessionCookie)); + assertTrue(idleFile.exists()); + assertTrue(idleFile.delete()); + + //make a request + request = client.newRequest(url + "?action=testfail"); + request.getHeaders().add("Cookie", sessionCookie); + response2 = request.send(); + assertEquals(HttpServletResponse.SC_OK,response2.getStatus()); } finally { @@ -158,13 +179,14 @@ public class IdleSessionTest } - public void checkSessionIdled (File sessionDir) + public void checkSessionIdled (File sessionDir, String sessionId) { assertNotNull(sessionDir); assertTrue(sessionDir.exists()); String[] files = sessionDir.list(); assertNotNull(files); assertEquals(1, files.length); + assertEquals(sessionId, files[0]); } @@ -176,7 +198,23 @@ public class IdleSessionTest assertNotNull(files); assertEquals(0, files.length); } + + public File getIdleFile (File sessionDir, String sessionId) + { + assertNotNull(sessionDir); + assertTrue(sessionDir.exists()); + String[] files = sessionDir.list(); + assertNotNull(files); + return new File(sessionDir, files[0]); + } + public String getSessionId (String sessionCookie) + { + assertNotNull(sessionCookie); + String sessionId = sessionCookie.substring(11); + sessionId = sessionId.substring(0, sessionId.indexOf(';')); + return sessionId; + } public static class TestServlet extends HttpServlet { @@ -201,6 +239,11 @@ public class IdleSessionTest assertEquals("test", session.getAttribute("test")); assertTrue(!((HashedSession)session).isIdled()); } + else if ("testfail".equals(action)) + { + HttpSession session = request.getSession(false); + assertTrue(session == null); + } } } } From ba318ccbd366219bd834734e8e877d4a60892074 Mon Sep 17 00:00:00 2001 From: Jesse McConnell Date: Tue, 4 Feb 2014 14:09:04 -0600 Subject: [PATCH 36/54] [426003] active modules with missing dependencies fail accordingly, non-active with missing deps fail silently --- .../java/org/eclipse/jetty/start/Main.java | 2 +- .../java/org/eclipse/jetty/start/Modules.java | 60 +++++++++++++++---- .../org/eclipse/jetty/start/TestUseCases.java | 13 ++++ .../assert-enable-spdy-bad-npn-version.txt | 16 +++++ .../usecases/assert-missing-npn-version.txt | 28 +++++++++ .../start.ini | 12 ++++ .../base.missing.npn.version/start.ini | 12 ++++ 7 files changed, 129 insertions(+), 14 deletions(-) create mode 100644 jetty-start/src/test/resources/usecases/assert-enable-spdy-bad-npn-version.txt create mode 100644 jetty-start/src/test/resources/usecases/assert-missing-npn-version.txt create mode 100644 jetty-start/src/test/resources/usecases/base.enable.spdy.bad.npn.version/start.ini create mode 100644 jetty-start/src/test/resources/usecases/base.missing.npn.version/start.ini diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java index fd93832bf2e..792c4afdc86 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Main.java @@ -572,7 +572,7 @@ public class Main args.setAllModules(modules); List activeModules = modules.resolveEnabled(); - + // 7) Lib & XML Expansion / Resolution args.expandModules(baseHome,activeModules); diff --git a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java index d8004f57654..95bd8076f46 100644 --- a/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java +++ b/jetty-start/src/main/java/org/eclipse/jetty/start/Modules.java @@ -39,6 +39,13 @@ import java.util.regex.Pattern; public class Modules implements Iterable { private Map modules = new HashMap<>(); + /* + * modules that may appear in the resolved graph but are undefined in the module system + * + * ex: modules/npn/npn-1.7.0_01.mod (property expansion resolves to non-existent file) + */ + private Set missingModules = new HashSet(); + private int maxDepth = -1; private Set asNameSet(Set moduleSet) @@ -110,7 +117,7 @@ public class Modules implements Iterable if (parent == null) { - System.err.printf("WARNING: module not found [%s]%n",parentName); + StartLog.debug("module not found [%s]%n",parentName); } else { @@ -124,7 +131,7 @@ public class Modules implements Iterable Module optional = get(optionalParentName); if (optional == null) { - System.err.printf("WARNING: module not found [%s]%n",optionalParentName); + StartLog.debug("optional module not found [%s]%n",optionalParentName); } else if (optional.isEnabled()) { @@ -285,12 +292,12 @@ public class Modules implements Iterable } } - private void findParents(Module module, Set ret) + private void findParents(Module module, Map ret) { - ret.add(module); + ret.put(module.getName(), module); for (Module parent : module.getParentEdges()) { - ret.add(parent); + ret.put(parent.getName(), parent); findParents(parent,ret); } } @@ -370,7 +377,7 @@ public class Modules implements Iterable } // load missing post-expanded dependent modules - boolean done = false; + boolean done = false; while (!done) { done = true; @@ -380,7 +387,7 @@ public class Modules implements Iterable { for (String parent : m.getParentNames()) { - if (modules.containsKey(parent)) + if (modules.containsKey(parent) || missingModules.contains(parent)) { continue; // found. skip it. } @@ -392,8 +399,16 @@ public class Modules implements Iterable for (String missingParent : missingParents) { File file = basehome.getFile("modules/" + missingParent + ".mod"); - Module module = registerModule(basehome,args,file); - updateParentReferencesTo(module); + if ( FS.canReadFile(file) ) + { + Module module = registerModule(basehome,args,file); + updateParentReferencesTo(module); + } + else + { + StartLog.debug("Missing module definition: [ Mod: %s | File: %s]", missingParent, file); + missingModules.add(missingParent); + } } } } @@ -425,7 +440,7 @@ public class Modules implements Iterable */ public List resolveEnabled() { - Set active = new HashSet(); + Map active = new HashMap(); for (Module module : modules.values()) { @@ -435,18 +450,37 @@ public class Modules implements Iterable } } + /* + * check against the missing modules + * + * Ex: npn should match anything under npn/ + */ + for ( String missing : missingModules ) + { + for (String activeModule: active.keySet()) + { + if ( missing.startsWith(activeModule) ) + { + StartLog.warn("** Unable to continue, required dependency missing. [%s]", missing); + StartLog.warn("** As configured, Jetty is unable to start due to a missing enabled module dependency."); + StartLog.warn("** This may be due to a transitive dependency akin to spdy on npn, which resolves based on the JDK in use."); + return Collections.emptyList(); + } + } + } + List ordered = new ArrayList<>(); - ordered.addAll(active); + ordered.addAll(active.values()); Collections.sort(ordered,new Module.DepthComparator()); return ordered; } public Set resolveParentModulesOf(String moduleName) { - Set ret = new HashSet<>(); + Map ret = new HashMap<>(); Module module = get(moduleName); findParents(module,ret); - return asNameSet(ret); + return ret.keySet(); } private String toIndent(int depth) diff --git a/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java b/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java index f94b33ddd49..ec419cfc432 100644 --- a/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java +++ b/jetty-start/src/test/java/org/eclipse/jetty/start/TestUseCases.java @@ -61,11 +61,24 @@ public class TestUseCases assertUseCase("home","base.jmx","assert-jmx.txt"); } + @Test + public void testWithMissingNpnVersion() throws Exception + { + assertUseCase("home","base.missing.npn.version","assert-missing-npn-version.txt","java.version=1.7.0_01"); + } + @Test public void testWithSpdy() throws Exception { assertUseCase("home","base.enable.spdy","assert-enable-spdy.txt","java.version=1.7.0_21"); } + + @Test + public void testWithSpdyBadNpnVersion() throws Exception + { + //StartLog.enableDebug(); + assertUseCase("home","base.enable.spdy.bad.npn.version","assert-enable-spdy-bad-npn-version.txt","java.version=1.7.0_01"); + } @Test public void testWithDatabase() throws Exception diff --git a/jetty-start/src/test/resources/usecases/assert-enable-spdy-bad-npn-version.txt b/jetty-start/src/test/resources/usecases/assert-enable-spdy-bad-npn-version.txt new file mode 100644 index 00000000000..84c8867d38c --- /dev/null +++ b/jetty-start/src/test/resources/usecases/assert-enable-spdy-bad-npn-version.txt @@ -0,0 +1,16 @@ +# The XMLs we expect (order is important) + +# The LIBs we expect (order is irrelevant) + +# The Properties we expect (order is irrelevant) +PROP|jetty.port=9090 +PROP|jetty.keystore=etc/keystore +PROP|jetty.keystore.password=friendly +PROP|jetty.keymanager.password=icecream +PROP|jetty.truststore=etc/keystore +PROP|jetty.truststore.password=sundae +PROP|java.version=1.7.0_01 + +# The Downloads + +# The Bootlib diff --git a/jetty-start/src/test/resources/usecases/assert-missing-npn-version.txt b/jetty-start/src/test/resources/usecases/assert-missing-npn-version.txt new file mode 100644 index 00000000000..884767af895 --- /dev/null +++ b/jetty-start/src/test/resources/usecases/assert-missing-npn-version.txt @@ -0,0 +1,28 @@ +# The XMLs we expect (order is important) +XML|${jetty.home}/etc/jetty-jmx.xml +XML|${jetty.home}/etc/jetty.xml +XML|${jetty.home}/etc/jetty-http.xml + +# The LIBs we expect (order is irrelevant) +LIB|${jetty.home}/lib/jetty-continuation-TEST.jar +LIB|${jetty.home}/lib/jetty-http-TEST.jar +LIB|${jetty.home}/lib/jetty-io-TEST.jar +LIB|${jetty.home}/lib/jetty-jmx-TEST.jar +LIB|${jetty.home}/lib/jetty-schemas-3.1.jar +LIB|${jetty.home}/lib/jetty-server-TEST.jar +LIB|${jetty.home}/lib/jetty-util-TEST.jar +LIB|${jetty.home}/lib/jetty-xml-TEST.jar +LIB|${jetty.home}/lib/servlet-api-3.1.jar + +# The Properties we expect (order is irrelevant) +PROP|jetty.port=9090 +PROP|jetty.keystore=etc/keystore +PROP|jetty.keystore.password=friendly +PROP|jetty.keymanager.password=icecream +PROP|jetty.truststore=etc/keystore +PROP|jetty.truststore.password=sundae +PROP|java.version=1.7.0_01 + +# The Downloads + +# The Bootlib diff --git a/jetty-start/src/test/resources/usecases/base.enable.spdy.bad.npn.version/start.ini b/jetty-start/src/test/resources/usecases/base.enable.spdy.bad.npn.version/start.ini new file mode 100644 index 00000000000..13fa5089f33 --- /dev/null +++ b/jetty-start/src/test/resources/usecases/base.enable.spdy.bad.npn.version/start.ini @@ -0,0 +1,12 @@ + +--module=server,http,jmx,spdy + +jetty.port=9090 + +# Some SSL keystore configuration +jetty.keystore=etc/keystore +jetty.keystore.password=friendly +jetty.keymanager.password=icecream +jetty.truststore=etc/keystore +jetty.truststore.password=sundae + diff --git a/jetty-start/src/test/resources/usecases/base.missing.npn.version/start.ini b/jetty-start/src/test/resources/usecases/base.missing.npn.version/start.ini new file mode 100644 index 00000000000..29159616d2c --- /dev/null +++ b/jetty-start/src/test/resources/usecases/base.missing.npn.version/start.ini @@ -0,0 +1,12 @@ + +--module=server,http,jmx + +jetty.port=9090 + +# Some SSL keystore configuration +jetty.keystore=etc/keystore +jetty.keystore.password=friendly +jetty.keymanager.password=icecream +jetty.truststore=etc/keystore +jetty.truststore.password=sundae + From 772505623471ef140b69d0911fafc38dcefb0baa Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 5 Feb 2014 17:34:22 +1100 Subject: [PATCH 37/54] 426750 isReady() returns true at EOF --- .../org/eclipse/jetty/server/HttpInput.java | 4 +- .../org/eclipse/jetty/server/HttpOutput.java | 105 +++++++----- .../jetty/servlet/AsyncServletIOTest.java | 149 +++++++++++++++++- 3 files changed, 215 insertions(+), 43 deletions(-) diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java index a66ed167b5c..771ff2c47ec 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpInput.java @@ -313,7 +313,9 @@ public abstract class HttpInput extends ServletInputStream implements Runnabl boolean finished; synchronized (lock()) { - if (_listener == null) + if (_contentState.isEOF()) + return true; + if (_listener == null ) return true; if (available() > 0) return true; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java index 1b676da6534..99a94235d12 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java @@ -63,13 +63,15 @@ public class HttpOutput extends ServletOutputStream implements Runnable private volatile Throwable _onError; /* - ACTION OPEN ASYNC READY PENDING UNREADY - ------------------------------------------------------------------------------- - setWriteListener() READY->owp ise ise ise ise - write() OPEN ise PENDING wpe wpe - flush() OPEN ise PENDING wpe wpe - isReady() OPEN:true READY:true READY:true UNREADY:false UNREADY:false - write completed - - - ASYNC READY->owp + ACTION OPEN ASYNC READY PENDING UNREADY CLOSED + ----------------------------------------------------------------------------------------------------- + setWriteListener() READY->owp ise ise ise ise ise + write() OPEN ise PENDING wpe wpe eof + flush() OPEN ise PENDING wpe wpe eof + close() CLOSED CLOSED CLOSED CLOSED wpe CLOSED + isReady() OPEN:true READY:true READY:true UNREADY:false UNREADY:false CLOSED:true + write completed - - - ASYNC READY->owp - + */ enum OutputState { OPEN, ASYNC, READY, PENDING, UNREADY, CLOSED } private final AtomicReference _state=new AtomicReference<>(OutputState.OPEN); @@ -131,48 +133,66 @@ public class HttpOutput extends ServletOutputStream implements Runnable @Override public void close() { - OutputState state=_state.get(); - while(state!=OutputState.CLOSED) + loop: while(true) { - if (_state.compareAndSet(state,OutputState.CLOSED)) + OutputState state=_state.get(); + switch (state) { - try - { - write(BufferUtil.hasContent(_aggregate)?_aggregate:BufferUtil.EMPTY_BUFFER,!_channel.getResponse().isIncluding()); - } - catch(IOException e) - { - LOG.debug(e); - _channel.failed(); - } - releaseBuffer(); - return; + case CLOSED: + break loop; + + case UNREADY: + throw new WritePendingException(); // TODO ? + + default: + if (_state.compareAndSet(state,OutputState.CLOSED)) + { + try + { + write(BufferUtil.hasContent(_aggregate)?_aggregate:BufferUtil.EMPTY_BUFFER,!_channel.getResponse().isIncluding()); + } + catch(IOException e) + { + LOG.debug(e); + _channel.failed(); + } + releaseBuffer(); + return; + } } - state=_state.get(); } } /* Called to indicated that the output is already closed and the state needs to be updated to match */ void closed() { - OutputState state=_state.get(); - while(state!=OutputState.CLOSED) + loop: while(true) { - if (_state.compareAndSet(state,OutputState.CLOSED)) + OutputState state=_state.get(); + switch (state) { - try - { - _channel.getResponse().closeOutput(); - } - catch(IOException e) - { - LOG.debug(e); - _channel.failed(); - } - releaseBuffer(); - return; + case CLOSED: + break loop; + + case UNREADY: + throw new WritePendingException(); // TODO ? + + default: + if (_state.compareAndSet(state,OutputState.CLOSED)) + { + try + { + _channel.getResponse().closeOutput(); + } + catch(IOException e) + { + LOG.debug(e); + _channel.failed(); + } + releaseBuffer(); + return; + } } - state=_state.get(); } } @@ -667,8 +687,9 @@ public class HttpOutput extends ServletOutputStream implements Runnable return false; case UNREADY: return false; + case CLOSED: - return false; + return true; } } } @@ -683,6 +704,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable _writeListener.onError(new IOException(th)); close(); } + switch(_state.get()) { case READY: @@ -700,6 +722,7 @@ public class HttpOutput extends ServletOutputStream implements Runnable case CLOSED: try { + new Throwable().printStackTrace(); // even though a write is not possible, because a close has // occurred, we need to call onWritePossible to tell async // producer that the last write completed. @@ -716,6 +739,11 @@ public class HttpOutput extends ServletOutputStream implements Runnable } } + @Override + public String toString() + { + return String.format("%s@%x{%s}",this.getClass().getSimpleName(),hashCode(),_state.get()); + } private abstract class AsyncICB extends IteratingCallback { @@ -741,7 +769,6 @@ public class HttpOutput extends ServletOutputStream implements Runnable break; case CLOSED: - _channel.getState().onWritePossible(); break; default: diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java index a5a0554a55f..e38d39f68c3 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java @@ -32,10 +32,13 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import javax.servlet.AsyncContext; +import javax.servlet.AsyncEvent; +import javax.servlet.AsyncListener; import javax.servlet.ReadListener; import javax.servlet.ServletException; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; +import javax.servlet.ServletResponse; import javax.servlet.WriteListener; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; @@ -55,7 +58,9 @@ import org.junit.Test; // TODO need these on SPDY as well! public class AsyncServletIOTest { - protected AsyncIOServlet _servlet=new AsyncIOServlet(); + protected AsyncIOServlet _servlet0=new AsyncIOServlet(); + protected AsyncIOServlet2 _servlet2=new AsyncIOServlet2(); + protected int _port; protected Server _server = new Server(); @@ -74,9 +79,16 @@ public class AsyncServletIOTest context.setContextPath("/ctx"); _server.setHandler(context); _servletHandler=context.getServletHandler(); - ServletHolder holder=new ServletHolder(_servlet); + + + ServletHolder holder=new ServletHolder(_servlet0); holder.setAsyncSupported(true); _servletHandler.addServletWithMapping(holder,"/path/*"); + + ServletHolder holder2=new ServletHolder(_servlet2); + holder.setAsyncSupported(true); + _servletHandler.addServletWithMapping(holder2,"/path2/*"); + _server.start(); _port=_connector.getLocalPort(); @@ -146,6 +158,56 @@ public class AsyncServletIOTest process("Hello!!!\r\n",10); } + + @Test + public void testAsync2() throws Exception + { + StringBuilder request = new StringBuilder(512); + request.append("GET /ctx/path2/info HTTP/1.1\r\n") + .append("Host: localhost\r\n") + .append("Connection: close\r\n") + .append("\r\n"); + + int port=_port; + List list = new ArrayList<>(); + try (Socket socket = new Socket("localhost",port);) + { + socket.setSoTimeout(1000000); + OutputStream out = socket.getOutputStream(); + out.write(request.toString().getBytes("ISO-8859-1")); + + BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()),102400); + + // response line + String line = in.readLine(); + // System.err.println("resp: "+line); + Assert.assertThat(line,Matchers.startsWith("HTTP/1.1 200 OK")); + + // Skip headers + while (line!=null) + { + line = in.readLine(); + // System.err.println("line: "+line); + if (line.length()==0) + break; + } + + // Get body slowly + while (true) + { + line = in.readLine(); + // System.err.println("body: "+line); + if (line==null) + break; + list.add(line); + } + } + + Assert.assertEquals(list.get(0),"data"); + Assert.assertEquals(_servlet2.completed.get(),1); + } + + protected void assertContains(String content,String response) { @@ -298,7 +360,7 @@ public class AsyncServletIOTest throw new IllegalStateException(); // System.err.println("ODA"); - while (in.isReady()) + while (in.isReady() && !in.isFinished()) { _oda.incrementAndGet(); int len=in.read(_buf); @@ -374,4 +436,85 @@ public class AsyncServletIOTest }); } } + + + + public class AsyncIOServlet2 extends HttpServlet + { + public AtomicInteger completed = new AtomicInteger(0); + + @Override + public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException + { + new SampleAsycListener(request,response); + } + + class SampleAsycListener implements WriteListener, AsyncListener + { + final ServletResponse response; + final ServletOutputStream servletOutputStream; + final AsyncContext asyncContext; + + SampleAsycListener(HttpServletRequest request,HttpServletResponse response) throws IOException + { + asyncContext = request.startAsync(); + asyncContext.setTimeout(10000L); + asyncContext.addListener(this); + servletOutputStream = response.getOutputStream(); + servletOutputStream.setWriteListener(this); + this.response=response; + } + + volatile boolean written=false; + @Override + public void onWritePossible() throws IOException + { + if (!written) + { + written=true; + response.setContentLength(5); + servletOutputStream.write("data\n".getBytes()); + } + + if (servletOutputStream.isReady()) + { + asyncContext.complete(); + } + } + + @Override + public void onError(final Throwable t) + { + t.printStackTrace(); + asyncContext.complete(); + } + + @Override + public void onComplete(final AsyncEvent event) throws IOException + { + completed.incrementAndGet(); + } + + @Override + public void onTimeout(final AsyncEvent event) throws IOException + { + asyncContext.complete(); + } + + @Override + public void onError(final AsyncEvent event) throws IOException + { + asyncContext.complete(); + } + + @Override + public void onStartAsync(AsyncEvent event) throws IOException + { + + } + + } + } + + } From f147362915d309596b7b5dfb9d07a7495c9d5bf6 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Wed, 5 Feb 2014 21:30:06 +0100 Subject: [PATCH 38/54] 427512 - ReadPendingException in case of HTTP Proxy tunnelling. Fixed by marking the old HttpConnection as "soft closed", that is make it so that isClosed() returns true but the underlying EndPoint is not closed. This allows the HttpReceiver to skip the registration for fill interest, so that the ReadPendingException is not thrown. --- .../src/main/java/org/eclipse/jetty/client/HttpProxy.java | 6 +++++- .../eclipse/jetty/client/http/HttpConnectionOverHTTP.java | 7 ++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java index c47a37ab9e4..0faf25e9ea2 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/HttpProxy.java @@ -27,6 +27,7 @@ import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Request; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.api.Result; +import org.eclipse.jetty.client.http.HttpConnectionOverHTTP; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpScheme; @@ -179,9 +180,12 @@ public class HttpProxy extends ProxyConfiguration.Proxy HttpDestination destination = (HttpDestination)context.get(HttpClientTransport.HTTP_DESTINATION_CONTEXT_KEY); HttpClient client = destination.getHttpClient(); ClientConnectionFactory sslConnectionFactory = new SslClientConnectionFactory(client.getSslContextFactory(), client.getByteBufferPool(), client.getExecutor(), connectionFactory); - org.eclipse.jetty.io.Connection oldConnection = endPoint.getConnection(); + HttpConnectionOverHTTP oldConnection = (HttpConnectionOverHTTP)endPoint.getConnection(); org.eclipse.jetty.io.Connection newConnection = sslConnectionFactory.newConnection(endPoint, context); Helper.replaceConnection(oldConnection, newConnection); + // Avoid setting fill interest in the old Connection, + // without closing the underlying EndPoint. + oldConnection.softClose(); LOG.debug("HTTP tunnel established: {} over {}", oldConnection, newConnection); } catch (Throwable x) diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java index 32d7799887c..219a56ee047 100644 --- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java +++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpConnectionOverHTTP.java @@ -120,7 +120,7 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec @Override public void close() { - if (closed.compareAndSet(false, true)) + if (softClose()) { getHttpDestination().close(this); getEndPoint().shutdownOutput(); @@ -130,6 +130,11 @@ public class HttpConnectionOverHTTP extends AbstractConnection implements Connec } } + public boolean softClose() + { + return closed.compareAndSet(false, true); + } + @Override public String toString() { From 8e03498b0d9544875f8134b0c9dbc8f843591d79 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Wed, 5 Feb 2014 23:05:23 +0100 Subject: [PATCH 39/54] Renamed NetworkTrafficSelectChannelConnector to NetworkTrafficServerConnector and deprecated the old class. --- .../server/NetworkTrafficServerConnector.java | 93 +++++++++++++++++++ .../NetworkTrafficSelectChannelConnector.java | 60 ++---------- .../server/NetworkTrafficListenerTest.java | 32 +++---- 3 files changed, 118 insertions(+), 67 deletions(-) create mode 100644 jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java new file mode 100644 index 00000000000..24ac7e47446 --- /dev/null +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/NetworkTrafficServerConnector.java @@ -0,0 +1,93 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 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.server; + +import java.io.IOException; +import java.nio.channels.SelectionKey; +import java.nio.channels.SocketChannel; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.Executor; + +import org.eclipse.jetty.io.ByteBufferPool; +import org.eclipse.jetty.io.NetworkTrafficListener; +import org.eclipse.jetty.io.NetworkTrafficSelectChannelEndPoint; +import org.eclipse.jetty.io.SelectChannelEndPoint; +import org.eclipse.jetty.io.SelectorManager; +import org.eclipse.jetty.util.ssl.SslContextFactory; +import org.eclipse.jetty.util.thread.Scheduler; + +/** + *

A specialized version of {@link ServerConnector} that supports {@link NetworkTrafficListener}s.

+ *

{@link NetworkTrafficListener}s can be added and removed dynamically before and after this connector has + * been started without causing {@link java.util.ConcurrentModificationException}s.

+ */ +public class NetworkTrafficServerConnector extends ServerConnector +{ + private final List listeners = new CopyOnWriteArrayList<>(); + + public NetworkTrafficServerConnector(Server server) + { + this(server, null, null, null, 0, 0, new HttpConnectionFactory()); + } + + public NetworkTrafficServerConnector(Server server, ConnectionFactory connectionFactory, SslContextFactory sslContextFactory) + { + super(server, sslContextFactory, connectionFactory); + } + + public NetworkTrafficServerConnector(Server server, ConnectionFactory connectionFactory) + { + super(server, connectionFactory); + } + + public NetworkTrafficServerConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, ConnectionFactory... factories) + { + super(server, executor, scheduler, pool, acceptors, selectors, factories); + } + + public NetworkTrafficServerConnector(Server server, SslContextFactory sslContextFactory) + { + super(server, sslContextFactory); + } + + /** + * @param listener the listener to add + */ + public void addNetworkTrafficListener(NetworkTrafficListener listener) + { + listeners.add(listener); + } + + /** + * @param listener the listener to remove + */ + public void removeNetworkTrafficListener(NetworkTrafficListener listener) + { + listeners.remove(listener); + } + + @Override + protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectorManager.ManagedSelector selectSet, SelectionKey key) throws IOException + { + NetworkTrafficSelectChannelEndPoint endPoint = new NetworkTrafficSelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout(), listeners); + endPoint.notifyOpened(); + return endPoint; + } +} diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java index 56c661d5a7c..f4c595834a8 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/nio/NetworkTrafficSelectChannelConnector.java @@ -18,83 +18,43 @@ package org.eclipse.jetty.server.nio; -import java.io.IOException; -import java.nio.channels.SelectionKey; -import java.nio.channels.SocketChannel; -import java.util.ConcurrentModificationException; -import java.util.List; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Executor; import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.NetworkTrafficListener; -import org.eclipse.jetty.io.NetworkTrafficSelectChannelEndPoint; -import org.eclipse.jetty.io.SelectChannelEndPoint; -import org.eclipse.jetty.io.SelectorManager; import org.eclipse.jetty.server.ConnectionFactory; -import org.eclipse.jetty.server.HttpConnectionFactory; +import org.eclipse.jetty.server.NetworkTrafficServerConnector; import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.Scheduler; /** - *

A specialized version of {@link ServerConnector} that supports {@link NetworkTrafficListener}s.

- *

{@link NetworkTrafficListener}s can be added and removed dynamically before and after this connector has - * been started without causing {@link ConcurrentModificationException}s.

+ * @deprecated use {@link org.eclipse.jetty.server.NetworkTrafficServerConnector} instead. */ -public class NetworkTrafficSelectChannelConnector extends ServerConnector +@Deprecated +public class NetworkTrafficSelectChannelConnector extends NetworkTrafficServerConnector { - private final List listeners = new CopyOnWriteArrayList(); - public NetworkTrafficSelectChannelConnector(Server server) { - this(server,null,null,null,0,0,new HttpConnectionFactory()); + super(server); } public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory, SslContextFactory sslContextFactory) { - super(server,sslContextFactory,connectionFactory); + super(server, connectionFactory, sslContextFactory); } public NetworkTrafficSelectChannelConnector(Server server, ConnectionFactory connectionFactory) { - super(server,connectionFactory); + super(server, connectionFactory); } - public NetworkTrafficSelectChannelConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, - ConnectionFactory... factories) + public NetworkTrafficSelectChannelConnector(Server server, Executor executor, Scheduler scheduler, ByteBufferPool pool, int acceptors, int selectors, ConnectionFactory... factories) { - super(server,executor,scheduler,pool,acceptors,selectors,factories); + super(server, executor, scheduler, pool, acceptors, selectors, factories); } public NetworkTrafficSelectChannelConnector(Server server, SslContextFactory sslContextFactory) { - super(server,sslContextFactory); + super(server, sslContextFactory); } - - /** - * @param listener the listener to add - */ - public void addNetworkTrafficListener(NetworkTrafficListener listener) - { - listeners.add(listener); - } - - /** - * @param listener the listener to remove - */ - public void removeNetworkTrafficListener(NetworkTrafficListener listener) - { - listeners.remove(listener); - } - - @Override - protected SelectChannelEndPoint newEndPoint(SocketChannel channel, SelectorManager.ManagedSelector selectSet, SelectionKey key) throws IOException - { - NetworkTrafficSelectChannelEndPoint endPoint = new NetworkTrafficSelectChannelEndPoint(channel, selectSet, key, getScheduler(), getIdleTimeout(), listeners); - endPoint.notifyOpened(); - return endPoint; - } - } diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java index 744806e4db7..679df516243 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/NetworkTrafficListenerTest.java @@ -18,9 +18,6 @@ package org.eclipse.jetty.server; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; @@ -31,7 +28,6 @@ import java.nio.charset.StandardCharsets; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; - import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; @@ -39,25 +35,27 @@ import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.io.NetworkTrafficListener; import org.eclipse.jetty.server.handler.AbstractHandler; -import org.eclipse.jetty.server.nio.NetworkTrafficSelectChannelConnector; import org.eclipse.jetty.util.BufferUtil; import org.junit.After; import org.junit.Ignore; import org.junit.Test; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + @Ignore public class NetworkTrafficListenerTest { private static final byte END_OF_CONTENT = '~'; private Server server; - private NetworkTrafficSelectChannelConnector connector; + private NetworkTrafficServerConnector connector; public void initConnector(Handler handler) throws Exception { server = new Server(); - connector = new NetworkTrafficSelectChannelConnector(server); + connector = new NetworkTrafficServerConnector(server); connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendDateHeader(false); connector.getConnectionFactory(HttpConfiguration.ConnectionFactory.class).getHttpConfiguration().setSendServerVersion(false); server.addConnector(connector); @@ -121,9 +119,9 @@ public class NetworkTrafficListenerTest } }); - final AtomicReference incomingData = new AtomicReference(); + final AtomicReference incomingData = new AtomicReference<>(); final CountDownLatch incomingLatch = new CountDownLatch(1); - final AtomicReference outgoingData = new AtomicReference(""); + final AtomicReference outgoingData = new AtomicReference<>(""); final CountDownLatch outgoingLatch = new CountDownLatch(1); connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter() { @@ -186,9 +184,9 @@ public class NetworkTrafficListenerTest } }); - final AtomicReference incomingData = new AtomicReference(); + final AtomicReference incomingData = new AtomicReference<>(); final CountDownLatch incomingLatch = new CountDownLatch(1); - final AtomicReference outgoingData = new AtomicReference(""); + final AtomicReference outgoingData = new AtomicReference<>(""); final CountDownLatch outgoingLatch = new CountDownLatch(2); connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter() { @@ -253,9 +251,9 @@ public class NetworkTrafficListenerTest } }); - final AtomicReference incomingData = new AtomicReference(); + final AtomicReference incomingData = new AtomicReference<>(); final CountDownLatch incomingLatch = new CountDownLatch(1); - final AtomicReference outgoingData = new AtomicReference(""); + final AtomicReference outgoingData = new AtomicReference<>(""); final CountDownLatch outgoingLatch = new CountDownLatch(4); connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter() { @@ -319,9 +317,9 @@ public class NetworkTrafficListenerTest } }); - final AtomicReference incomingData = new AtomicReference(); + final AtomicReference incomingData = new AtomicReference<>(); final CountDownLatch incomingLatch = new CountDownLatch(1); - final AtomicReference outgoingData = new AtomicReference(""); + final AtomicReference outgoingData = new AtomicReference<>(""); final CountDownLatch outgoingLatch = new CountDownLatch(1); connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter() { @@ -393,8 +391,8 @@ public class NetworkTrafficListenerTest } }); - final AtomicReference incomingData = new AtomicReference(""); - final AtomicReference outgoingData = new AtomicReference(""); + final AtomicReference incomingData = new AtomicReference<>(""); + final AtomicReference outgoingData = new AtomicReference<>(""); final CountDownLatch outgoingLatch = new CountDownLatch(1); connector.addNetworkTrafficListener(new NetworkTrafficListener.Adapter() { From c5c553260ff6ec3192b5cd255d85fddea109ca0f Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Thu, 6 Feb 2014 10:30:36 +1100 Subject: [PATCH 40/54] 424171 Old javax.activation jar interferes with email sending --- jetty-distribution/pom.xml | 23 ++++++++++------------- jetty-maven-plugin/pom.xml | 4 ++-- 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/jetty-distribution/pom.xml b/jetty-distribution/pom.xml index 87fb71427a8..c2566304e6b 100644 --- a/jetty-distribution/pom.xml +++ b/jetty-distribution/pom.xml @@ -427,7 +427,7 @@ org.eclipse.jetty.orbit - javax.activation,javax.mail.glassfish + javax.mail.glassfish jar ${assembly-directory}/lib/jndi @@ -578,27 +578,24 @@ - - javax.annotation - javax.annotation-api - - - org.eclipse.jetty.orbit - javax.activation - org.eclipse.jetty.orbit javax.mail.glassfish - - javax.transaction - javax.transaction-api - org.eclipse.jetty.orbit javax.security.auth.message + + javax.annotation + javax.annotation-api + + + javax.transaction + javax.transaction-api + + org.glassfish.web javax.servlet.jsp.jstl diff --git a/jetty-maven-plugin/pom.xml b/jetty-maven-plugin/pom.xml index 9fabfc6aed3..5ef3a1526e5 100644 --- a/jetty-maven-plugin/pom.xml +++ b/jetty-maven-plugin/pom.xml @@ -125,11 +125,11 @@ jetty-jsp ${project.version} - + javax.transaction javax.transaction-api From 51aedd23937fd9a0d5227fc43e86af38ccc19f58 Mon Sep 17 00:00:00 2001 From: Jan Bartel Date: Thu, 6 Feb 2014 12:06:47 +1100 Subject: [PATCH 41/54] 426358 NPE generating temp dir name if no resourceBase or war --- .../org/eclipse/jetty/webapp/WebInfConfiguration.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java index 4e82ac133b1..1dc3d12fd92 100644 --- a/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java +++ b/jetty-webapp/src/main/java/org/eclipse/jetty/webapp/WebInfConfiguration.java @@ -397,6 +397,9 @@ public class WebInfConfiguration extends AbstractConfiguration web_app = context.newResource(war); else web_app=context.getBaseResource(); + + if (web_app == null) + throw new IllegalStateException("No resourceBase or war set for context"); // Accept aliases for WAR files if (web_app.getAlias() != null) @@ -603,7 +606,7 @@ public class WebInfConfiguration extends AbstractConfiguration if (resource == null) { if (context.getWar()==null || context.getWar().length()==0) - resource=context.newResource(context.getResourceBase()); + throw new IllegalStateException("No resourceBase or war set for context"); // Set dir or WAR resource = context.newResource(context.getWar()); @@ -621,7 +624,8 @@ public class WebInfConfiguration extends AbstractConfiguration } catch (Exception e) { - LOG.warn("Can't generate resourceBase as part of webapp tmp dir name", e); + LOG.warn("Can't generate resourceBase as part of webapp tmp dir name: " + e); + LOG.debug(e); } //Context name From 5edf2799e90c3b0beb6cb538e514ed15895cb35b Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Thu, 6 Feb 2014 11:25:13 +0100 Subject: [PATCH 42/54] Made test more robust. The response may arrive on client before the server notifies the complete listeners, so the assert on client side was spuriously failing to check the completion on server side. --- .../jetty/servlet/AsyncServletIOTest.java | 47 ++++++------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java index e38d39f68c3..2d887b1e0b8 100644 --- a/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java +++ b/jetty-servlet/src/test/java/org/eclipse/jetty/servlet/AsyncServletIOTest.java @@ -18,8 +18,6 @@ package org.eclipse.jetty.servlet; -import static org.junit.Assert.assertEquals; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @@ -28,9 +26,10 @@ import java.net.Socket; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; - import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; @@ -55,14 +54,14 @@ import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import static org.junit.Assert.assertEquals; + // TODO need these on SPDY as well! public class AsyncServletIOTest { protected AsyncIOServlet _servlet0=new AsyncIOServlet(); protected AsyncIOServlet2 _servlet2=new AsyncIOServlet2(); - protected int _port; - protected Server _server = new Server(); protected ServletHandler _servletHandler; protected ServerConnector _connector; @@ -75,7 +74,7 @@ public class AsyncServletIOTest _connector = new ServerConnector(_server,new HttpConnectionFactory(http_config)); _server.setConnectors(new Connector[]{ _connector }); - ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SECURITY|ServletContextHandler.NO_SESSIONS); + ServletContextHandler context = new ServletContextHandler(); context.setContextPath("/ctx"); _server.setHandler(context); _servletHandler=context.getServletHandler(); @@ -157,7 +156,6 @@ public class AsyncServletIOTest { process("Hello!!!\r\n",10); } - @Test public void testAsync2() throws Exception @@ -170,7 +168,7 @@ public class AsyncServletIOTest int port=_port; List list = new ArrayList<>(); - try (Socket socket = new Socket("localhost",port);) + try (Socket socket = new Socket("localhost",port)) { socket.setSoTimeout(1000000); OutputStream out = socket.getOutputStream(); @@ -204,19 +202,7 @@ public class AsyncServletIOTest } Assert.assertEquals(list.get(0),"data"); - Assert.assertEquals(_servlet2.completed.get(),1); - } - - - - protected void assertContains(String content,String response) - { - Assert.assertThat(response,Matchers.containsString(content)); - } - - protected void assertNotContains(String content,String response) - { - Assert.assertThat(response,Matchers.not(Matchers.containsString(content))); + Assert.assertTrue(_servlet2.completed.await(5, TimeUnit.SECONDS)); } public synchronized List process(String content,int... writes) throws Exception @@ -245,14 +231,14 @@ public class AsyncServletIOTest .append("Connection: close\r\n"); if (content!=null) - request.append("Content-Length: "+content.length+"\r\n") + request.append("Content-Length: ").append(content.length).append("\r\n") .append("Content-Type: text/plain\r\n"); request.append("\r\n"); int port=_port; List list = new ArrayList<>(); - try (Socket socket = new Socket("localhost",port);) + try (Socket socket = new Socket("localhost",port)) { socket.setSoTimeout(1000000); OutputStream out = socket.getOutputStream(); @@ -328,9 +314,9 @@ public class AsyncServletIOTest private static final long serialVersionUID = -8161977157098646562L; public AsyncIOServlet() - {} + { + } - /* ------------------------------------------------------------ */ @Override public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { @@ -436,12 +422,10 @@ public class AsyncServletIOTest }); } } - - public class AsyncIOServlet2 extends HttpServlet { - public AtomicInteger completed = new AtomicInteger(0); + public CountDownLatch completed = new CountDownLatch(1); @Override public void doGet(final HttpServletRequest request, final HttpServletResponse response) throws IOException @@ -454,6 +438,7 @@ public class AsyncServletIOTest final ServletResponse response; final ServletOutputStream servletOutputStream; final AsyncContext asyncContext; + volatile boolean written=false; SampleAsycListener(HttpServletRequest request,HttpServletResponse response) throws IOException { @@ -465,7 +450,6 @@ public class AsyncServletIOTest this.response=response; } - volatile boolean written=false; @Override public void onWritePossible() throws IOException { @@ -492,7 +476,7 @@ public class AsyncServletIOTest @Override public void onComplete(final AsyncEvent event) throws IOException { - completed.incrementAndGet(); + completed.countDown(); } @Override @@ -512,9 +496,6 @@ public class AsyncServletIOTest { } - } } - - } From 9356ab46da5a4748b5900b0550265731e8bbb280 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Thu, 6 Feb 2014 11:56:25 +0100 Subject: [PATCH 43/54] Made test more robust. The check for the connection being closed may fail spuriously because the notification of CompleteListener happens before the connection is closed. --- .../test/java/org/eclipse/jetty/client/HttpClientTest.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java index 87c03e43f7a..f7ccdfcf63b 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/HttpClientTest.java @@ -1137,6 +1137,10 @@ public class HttpClientTest extends AbstractHttpClientServerTest ContentResponse response = listener.get(2 * timeout, TimeUnit.MILLISECONDS); Assert.assertEquals(200, response.getStatus()); + // The parser notifies end-of-content and therefore the CompleteListener + // before closing the connection, so we need to wait before checking + // that the connection is closed to avoid races. + Thread.sleep(1000); Assert.assertTrue(((HttpConnectionOverHTTP)connection).isClosed()); } } From 4d4b0c42e7d76feaa886924e8af9765ea466e9de Mon Sep 17 00:00:00 2001 From: Jesse McConnell Date: Thu, 6 Feb 2014 09:14:28 -0600 Subject: [PATCH 44/54] [Bug 427570] externalize common http config to start.ini --- jetty-server/src/main/config/etc/jetty.xml | 11 +++++------ jetty-server/src/main/config/modules/server.mod | 12 ++++++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/jetty-server/src/main/config/etc/jetty.xml b/jetty-server/src/main/config/etc/jetty.xml index b88ceb217da..31601680ac5 100644 --- a/jetty-server/src/main/config/etc/jetty.xml +++ b/jetty-server/src/main/config/etc/jetty.xml @@ -79,13 +79,12 @@ https - 32768 - 8192 - 8192 - true - false + + + + + 512 -