diff --git a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java index 5864f928389..9d35954972c 100644 --- a/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java +++ b/examples/embedded/src/main/java/org/eclipse/jetty/embedded/WebSocketJsrServer.java @@ -27,7 +27,7 @@ import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.handler.DefaultHandler; import org.eclipse.jetty.server.handler.HandlerList; import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainerInitializer; +import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServletContainerInitializer; /** * Example of setting up a javax.websocket server with Jetty embedded @@ -59,7 +59,7 @@ public class WebSocketJsrServer handlers.addHandler(context); // Enable javax.websocket configuration for the context - ServerContainer wsContainer = JavaxWebSocketServerContainerInitializer + ServerContainer wsContainer = JavaxWebSocketServletContainerInitializer .configureContext(context); // Add your websockets to the container diff --git a/jetty-util/src/main/java/org/eclipse/jetty/util/Callback.java b/jetty-util/src/main/java/org/eclipse/jetty/util/Callback.java index 7eea663e59a..0f1740f05e6 100644 --- a/jetty-util/src/main/java/org/eclipse/jetty/util/Callback.java +++ b/jetty-util/src/main/java/org/eclipse/jetty/util/Callback.java @@ -128,7 +128,18 @@ public interface Callback extends Invocable } }; } - + + static Callback from(Runnable completed) + { + return new Completing() + { + public void completed() + { + completed.run(); + } + }; + } + class Completing implements Callback { @Override diff --git a/jetty-websocket/javax-websocket-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java b/jetty-websocket/javax-websocket-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java index ff70a34a422..d63666d2a26 100644 --- a/jetty-websocket/javax-websocket-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java +++ b/jetty-websocket/javax-websocket-common/src/main/java/org/eclipse/jetty/websocket/javax/common/JavaxWebSocketSession.java @@ -18,24 +18,6 @@ package org.eclipse.jetty.websocket.javax.common; -import org.eclipse.jetty.util.SharedBlockingCallback; -import org.eclipse.jetty.util.component.AbstractLifeCycle; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.websocket.core.ExtensionConfig; -import org.eclipse.jetty.websocket.core.FrameHandler; -import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders; -import org.eclipse.jetty.websocket.javax.common.encoders.AvailableEncoders; -import org.eclipse.jetty.websocket.javax.common.util.ReflectUtils; - -import javax.websocket.CloseReason; -import javax.websocket.EndpointConfig; -import javax.websocket.Extension; -import javax.websocket.MessageHandler; -import javax.websocket.RemoteEndpoint.Async; -import javax.websocket.RemoteEndpoint.Basic; -import javax.websocket.Session; -import javax.websocket.WebSocketContainer; import java.io.IOException; import java.net.URI; import java.security.Principal; @@ -48,6 +30,25 @@ import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; +import javax.websocket.CloseReason; +import javax.websocket.EndpointConfig; +import javax.websocket.Extension; +import javax.websocket.MessageHandler; +import javax.websocket.RemoteEndpoint.Async; +import javax.websocket.RemoteEndpoint.Basic; +import javax.websocket.Session; +import javax.websocket.WebSocketContainer; + +import org.eclipse.jetty.util.SharedBlockingCallback; +import org.eclipse.jetty.util.component.AbstractLifeCycle; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.websocket.core.ExtensionConfig; +import org.eclipse.jetty.websocket.core.FrameHandler; +import org.eclipse.jetty.websocket.javax.common.decoders.AvailableDecoders; +import org.eclipse.jetty.websocket.javax.common.encoders.AvailableEncoders; +import org.eclipse.jetty.websocket.javax.common.util.ReflectUtils; + /** * Client Session for the JSR. */ @@ -535,7 +536,7 @@ public class JavaxWebSocketSession extends AbstractLifeCycle implements javax.we @Override public boolean isOpen() { - return coreSession.isOpen(); + return coreSession.isOutputOpen(); } /** diff --git a/jetty-websocket/javax-websocket-server/src/main/java/module-info.java b/jetty-websocket/javax-websocket-server/src/main/java/module-info.java index 52c8ed8a5c0..ec26f527b23 100644 --- a/jetty-websocket/javax-websocket-server/src/main/java/module-info.java +++ b/jetty-websocket/javax-websocket-server/src/main/java/module-info.java @@ -22,7 +22,7 @@ import javax.websocket.server.ServerEndpointConfig; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.websocket.javax.server.ContainerDefaultConfigurator; import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketConfiguration; -import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainerInitializer; +import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServletContainerInitializer; module org.eclipse.jetty.websocket.javax.server { @@ -42,7 +42,7 @@ module org.eclipse.jetty.websocket.javax.server requires org.eclipse.jetty.websocket.javax.client; requires org.eclipse.jetty.websocket.servlet; - provides ServletContainerInitializer with JavaxWebSocketServerContainerInitializer; + provides ServletContainerInitializer with JavaxWebSocketServletContainerInitializer; provides ServerEndpointConfig.Configurator with ContainerDefaultConfigurator; provides Configuration with JavaxWebSocketConfiguration; } diff --git a/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServerContainer.java b/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServerContainer.java index 18ac6e302c1..908dcf06680 100644 --- a/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServerContainer.java +++ b/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServerContainer.java @@ -89,10 +89,10 @@ public class JavaxWebSocketServerContainer if (container==null) { // Find Pre-Existing (Shared?) HttpClient and/or executor - HttpClient httpClient = (HttpClient)servletContext.getAttribute(JavaxWebSocketServerContainerInitializer.HTTPCLIENT_ATTRIBUTE); + HttpClient httpClient = (HttpClient)servletContext.getAttribute(JavaxWebSocketServletContainerInitializer.HTTPCLIENT_ATTRIBUTE); if (httpClient == null) httpClient = (HttpClient)contextHandler.getServer() - .getAttribute(JavaxWebSocketServerContainerInitializer.HTTPCLIENT_ATTRIBUTE); + .getAttribute(JavaxWebSocketServletContainerInitializer.HTTPCLIENT_ATTRIBUTE); Executor executor = httpClient == null?null:httpClient.getExecutor(); if (executor == null) @@ -124,7 +124,7 @@ public class JavaxWebSocketServerContainer private List deferredEndpointConfigs; /** - * Main entry point for {@link JavaxWebSocketServerContainerInitializer}. + * Main entry point for {@link JavaxWebSocketServletContainerInitializer}. * @param webSocketMapping the {@link WebSocketMapping} that this container belongs to * @param httpClient the {@link HttpClient} instance to use */ diff --git a/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServerContainerInitializer.java b/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServletContainerInitializer.java similarity index 98% rename from jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServerContainerInitializer.java rename to jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServletContainerInitializer.java index e58567575a5..b3fdf6ef028 100644 --- a/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServerContainerInitializer.java +++ b/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServletContainerInitializer.java @@ -41,12 +41,12 @@ import org.eclipse.jetty.websocket.servlet.WebSocketMapping; import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter; @HandlesTypes({ ServerApplicationConfig.class, ServerEndpoint.class, Endpoint.class }) -public class JavaxWebSocketServerContainerInitializer implements ServletContainerInitializer +public class JavaxWebSocketServletContainerInitializer implements ServletContainerInitializer { public static final String ENABLE_KEY = "org.eclipse.jetty.websocket.javax"; public static final String DEPRECATED_ENABLE_KEY = "org.eclipse.jetty.websocket.jsr356"; public static final String HTTPCLIENT_ATTRIBUTE = "org.eclipse.jetty.websocket.javax.HttpClient"; - private static final Logger LOG = Log.getLogger(JavaxWebSocketServerContainerInitializer.class); + private static final Logger LOG = Log.getLogger(JavaxWebSocketServletContainerInitializer.class); /** * Test a ServletContext for {@code init-param} or {@code attribute} at {@code keyName} for diff --git a/jetty-websocket/javax-websocket-server/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer b/jetty-websocket/javax-websocket-server/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer index e75020de778..86c5fef3534 100644 --- a/jetty-websocket/javax-websocket-server/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer +++ b/jetty-websocket/javax-websocket-server/src/main/resources/META-INF/services/javax.servlet.ServletContainerInitializer @@ -1 +1 @@ -org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainerInitializer \ No newline at end of file +org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServletContainerInitializer \ No newline at end of file diff --git a/jetty-websocket/javax-websocket-server/src/test/java/examples/JsrBrowserDebugTool.java b/jetty-websocket/javax-websocket-server/src/test/java/examples/JsrBrowserDebugTool.java index 372f51647e2..75afd0e50cf 100644 --- a/jetty-websocket/javax-websocket-server/src/test/java/examples/JsrBrowserDebugTool.java +++ b/jetty-websocket/javax-websocket-server/src/test/java/examples/JsrBrowserDebugTool.java @@ -18,6 +18,15 @@ package examples; +import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Objects; + +import javax.servlet.ServletException; +import javax.websocket.DeploymentException; + import org.eclipse.jetty.server.HttpConfiguration; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.Server; @@ -29,15 +38,7 @@ 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.websocket.javax.server.JavaxWebSocketServerContainer; -import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainerInitializer; - -import javax.servlet.ServletException; -import javax.websocket.DeploymentException; -import java.io.IOException; -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; -import java.util.Objects; +import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServletContainerInitializer; /** * Tool to help debug JSR based websocket circumstances reported around browsers. @@ -106,7 +107,7 @@ public class JsrBrowserDebugTool holder.setInitParameter("dirAllowed", "true"); server.setHandler(context); - JavaxWebSocketServerContainer container = JavaxWebSocketServerContainerInitializer.configureContext(context); + JavaxWebSocketServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(context); container.addEndpoint(JsrBrowserSocket.class); LOG.info("{} setup on port {}", this.getClass().getName(), port); diff --git a/jetty-websocket/javax-websocket-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/LocalServer.java b/jetty-websocket/javax-websocket-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/LocalServer.java index 5870de2fc23..c1099963f54 100644 --- a/jetty-websocket/javax-websocket-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/LocalServer.java +++ b/jetty-websocket/javax-websocket-tests/src/main/java/org/eclipse/jetty/websocket/javax/tests/LocalServer.java @@ -18,6 +18,16 @@ package org.eclipse.jetty.websocket.javax.tests; +import java.net.URI; +import java.util.Map; +import java.util.function.BiConsumer; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.websocket.OnMessage; +import javax.websocket.server.ServerContainer; +import javax.websocket.server.ServerEndpoint; + import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.pathmap.PathSpec; import org.eclipse.jetty.io.ByteBufferPool; @@ -38,23 +48,12 @@ import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.websocket.core.FrameHandler; import org.eclipse.jetty.websocket.core.internal.Parser; -import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainerInitializer; +import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServletContainerInitializer; import org.eclipse.jetty.websocket.servlet.WebSocketCreator; -import org.eclipse.jetty.websocket.servlet.WebSocketMapping; import org.eclipse.jetty.websocket.servlet.WebSocketServlet; import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import javax.websocket.OnMessage; -import javax.websocket.server.ServerContainer; -import javax.websocket.server.ServerEndpoint; -import java.net.URI; -import java.util.Map; -import java.util.function.BiConsumer; - public class LocalServer extends ContainerLifeCycle implements LocalFuzzer.Provider { @@ -172,7 +171,7 @@ public class LocalServer extends ContainerLifeCycle implements LocalFuzzer.Provi { servletContextHandler = new ServletContextHandler(server, "/", true, false); servletContextHandler.setContextPath("/"); - serverContainer = JavaxWebSocketServerContainerInitializer.configureContext(servletContextHandler); + serverContainer = JavaxWebSocketServletContainerInitializer.configureContext(servletContextHandler); configureServletContextHandler(servletContextHandler); return servletContextHandler; } diff --git a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/client/misbehaving/MisbehavingClassTest.java b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/client/misbehaving/MisbehavingClassTest.java index a2e57690c41..a7dff0dcaa4 100644 --- a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/client/misbehaving/MisbehavingClassTest.java +++ b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/client/misbehaving/MisbehavingClassTest.java @@ -18,6 +18,13 @@ package org.eclipse.jetty.websocket.javax.tests.client.misbehaving; +import java.io.IOException; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; + +import javax.websocket.ContainerProvider; +import javax.websocket.WebSocketContainer; + import org.eclipse.jetty.util.log.StacklessLogging; import org.eclipse.jetty.websocket.core.internal.WebSocketChannel; import org.eclipse.jetty.websocket.javax.tests.CoreServer; @@ -25,12 +32,6 @@ import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import javax.websocket.ContainerProvider; -import javax.websocket.WebSocketContainer; -import java.io.IOException; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.is; diff --git a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/quotes/QuotesDecoderTextStreamTest.java b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/quotes/QuotesDecoderTextStreamTest.java index 94e98821acf..8c2112de515 100644 --- a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/quotes/QuotesDecoderTextStreamTest.java +++ b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/quotes/QuotesDecoderTextStreamTest.java @@ -18,24 +18,25 @@ package org.eclipse.jetty.websocket.javax.tests.quotes; +import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + +import javax.websocket.OnMessage; +import javax.websocket.server.ServerContainer; +import javax.websocket.server.ServerEndpoint; + import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.websocket.core.CloseStatus; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; -import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainerInitializer; +import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServletContainerInitializer; import org.eclipse.jetty.websocket.javax.tests.Fuzzer; import org.eclipse.jetty.websocket.javax.tests.LocalServer; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import javax.websocket.OnMessage; -import javax.websocket.server.ServerContainer; -import javax.websocket.server.ServerEndpoint; -import java.util.List; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.allOf; import static org.hamcrest.Matchers.containsString; @@ -73,7 +74,7 @@ public class QuotesDecoderTextStreamTest @Override protected void configureServletContextHandler(ServletContextHandler context) throws Exception { - ServerContainer container = JavaxWebSocketServerContainerInitializer.configureContext(context); + ServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(context); container.addEndpoint(QuotesEchoStringSocket.class); } }; diff --git a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/MemoryUsageTest.java b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/MemoryUsageTest.java index c3aa25b98d7..b47cb19030b 100644 --- a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/MemoryUsageTest.java +++ b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/MemoryUsageTest.java @@ -18,15 +18,12 @@ package org.eclipse.jetty.websocket.javax.tests.server; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainerInitializer; -import org.junit.jupiter.api.AfterEach; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.condition.EnabledOnJre; -import org.junit.jupiter.api.condition.JRE; +import java.lang.management.ManagementFactory; +import java.lang.management.MemoryMXBean; +import java.lang.management.MemoryUsage; +import java.net.URI; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import javax.websocket.ContainerProvider; import javax.websocket.Endpoint; @@ -36,12 +33,16 @@ import javax.websocket.Session; import javax.websocket.WebSocketContainer; import javax.websocket.server.ServerContainer; import javax.websocket.server.ServerEndpointConfig; -import java.lang.management.ManagementFactory; -import java.lang.management.MemoryMXBean; -import java.lang.management.MemoryUsage; -import java.net.URI; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; + +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServletContainerInitializer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.condition.EnabledOnJre; +import org.junit.jupiter.api.condition.JRE; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.lessThan; @@ -79,7 +80,7 @@ public class MemoryUsageTest server.addConnector(connector); ServletContextHandler context = new ServletContextHandler(server, "/", true, false); - ServerContainer container = JavaxWebSocketServerContainerInitializer.configureContext(context); + ServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(context); ServerEndpointConfig config = ServerEndpointConfig.Builder.create(BasicEndpoint.class, "/").build(); container.addEndpoint(config); diff --git a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/PartialEchoTest.java b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/PartialEchoTest.java index 352da20d1be..b0aaa08d5ad 100644 --- a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/PartialEchoTest.java +++ b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/PartialEchoTest.java @@ -18,18 +18,9 @@ package org.eclipse.jetty.websocket.javax.tests.server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.util.log.Log; -import org.eclipse.jetty.util.log.Logger; -import org.eclipse.jetty.websocket.core.CloseStatus; -import org.eclipse.jetty.websocket.core.Frame; -import org.eclipse.jetty.websocket.core.OpCode; -import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainerInitializer; -import org.eclipse.jetty.websocket.javax.tests.Fuzzer; -import org.eclipse.jetty.websocket.javax.tests.LocalServer; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; import javax.websocket.OnError; import javax.websocket.OnMessage; @@ -37,9 +28,19 @@ import javax.websocket.OnOpen; import javax.websocket.Session; import javax.websocket.server.ServerContainer; import javax.websocket.server.ServerEndpoint; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; + +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; +import org.eclipse.jetty.websocket.core.CloseStatus; +import org.eclipse.jetty.websocket.core.Frame; +import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServletContainerInitializer; +import org.eclipse.jetty.websocket.javax.tests.Fuzzer; +import org.eclipse.jetty.websocket.javax.tests.LocalServer; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; /** * Sends raw TEXT or BINARY messages to server. @@ -115,7 +116,7 @@ public class PartialEchoTest @Override protected void configureServletContextHandler(ServletContextHandler context) throws Exception { - ServerContainer container = JavaxWebSocketServerContainerInitializer.configureContext(context); + ServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(context); container.addEndpoint(PartialTextSocket.class); container.addEndpoint(PartialTextSessionSocket.class); } diff --git a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/WebSocketServerContainerExecutorTest.java b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/WebSocketServerContainerExecutorTest.java index 61764e687a2..edaaa030aef 100644 --- a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/WebSocketServerContainerExecutorTest.java +++ b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/WebSocketServerContainerExecutorTest.java @@ -18,15 +18,14 @@ package org.eclipse.jetty.websocket.javax.tests.server; -import org.eclipse.jetty.client.HttpClient; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.util.IO; -import org.eclipse.jetty.util.thread.QueuedThreadPool; -import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainer; -import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainerInitializer; -import org.eclipse.jetty.websocket.javax.tests.WSURI; -import org.junit.jupiter.api.Test; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringWriter; +import java.net.HttpURLConnection; +import java.net.URI; +import java.nio.charset.StandardCharsets; +import java.util.concurrent.Executor; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; @@ -40,16 +39,18 @@ import javax.websocket.OnMessage; import javax.websocket.Session; import javax.websocket.WebSocketContainer; import javax.websocket.server.ServerEndpoint; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.StringWriter; -import java.net.HttpURLConnection; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.Executor; -import static org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainerInitializer.HTTPCLIENT_ATTRIBUTE; +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.eclipse.jetty.util.IO; +import org.eclipse.jetty.util.thread.QueuedThreadPool; +import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainer; +import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServletContainerInitializer; +import org.eclipse.jetty.websocket.javax.tests.WSURI; +import org.junit.jupiter.api.Test; + +import static org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServletContainerInitializer.HTTPCLIENT_ATTRIBUTE; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.startsWith; import static org.hamcrest.MatcherAssert.assertThat; @@ -159,7 +160,7 @@ public class WebSocketServerContainerExecutorTest // Using JSR356 Server Techniques to connectToServer() contextHandler.addServlet(ServerConnectServlet.class, "/connect"); - javax.websocket.server.ServerContainer container = JavaxWebSocketServerContainerInitializer.configureContext(contextHandler); + javax.websocket.server.ServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(contextHandler); container.addEndpoint(EchoSocket.class); try { @@ -188,7 +189,7 @@ public class WebSocketServerContainerExecutorTest // Using JSR356 Server Techniques to connectToServer() contextHandler.addServlet(ServerConnectServlet.class, "/connect"); - javax.websocket.server.ServerContainer container = JavaxWebSocketServerContainerInitializer.configureContext(contextHandler); + javax.websocket.server.ServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(contextHandler); container.addEndpoint(EchoSocket.class); try { @@ -218,7 +219,7 @@ public class WebSocketServerContainerExecutorTest // Using JSR356 Server Techniques to connectToServer() contextHandler.addServlet(ServerConnectServlet.class, "/connect"); - javax.websocket.server.ServerContainer container = JavaxWebSocketServerContainerInitializer.configureContext(contextHandler); + javax.websocket.server.ServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(contextHandler); container.addEndpoint(EchoSocket.class); try { diff --git a/jetty-websocket/jetty-websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java b/jetty-websocket/jetty-websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java index 60f103b7357..a9267c39c13 100644 --- a/jetty-websocket/jetty-websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java +++ b/jetty-websocket/jetty-websocket-client/src/main/java/org/eclipse/jetty/websocket/client/WebSocketClient.java @@ -18,6 +18,18 @@ package org.eclipse.jetty.websocket.client; +import java.io.IOException; +import java.net.CookieStore; +import java.net.SocketAddress; +import java.net.URI; +import java.time.Duration; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.ThreadLocalRandom; + import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.util.DecoratedObjectFactory; @@ -34,18 +46,6 @@ import org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandlerFactory; import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry; import org.eclipse.jetty.websocket.core.client.WebSocketCoreClient; -import java.io.IOException; -import java.net.CookieStore; -import java.net.SocketAddress; -import java.net.URI; -import java.time.Duration; -import java.util.Collection; -import java.util.Collections; -import java.util.HashSet; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.ThreadLocalRandom; - public class WebSocketClient extends ContainerLifeCycle implements WebSocketPolicy { private final WebSocketCoreClient coreClient; @@ -93,9 +93,7 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli public CompletableFuture connect(Object websocket, URI toUri) throws IOException { - ClientUpgradeRequestImpl upgradeRequest = new ClientUpgradeRequestImpl(this, coreClient, null, toUri, websocket); - coreClient.connect(upgradeRequest); - return upgradeRequest.getFutureSession(); + return connect(websocket, toUri, null); } /** @@ -107,7 +105,7 @@ public class WebSocketClient extends ContainerLifeCycle implements WebSocketPoli * @return the future for the session, available on success of connect * @throws IOException if unable to connect */ - public CompletableFuture connect(Object websocket, URI toUri, org.eclipse.jetty.websocket.api.UpgradeRequest request) throws IOException + public CompletableFuture connect(Object websocket, URI toUri, UpgradeRequest request) throws IOException { ClientUpgradeRequestImpl upgradeRequest = new ClientUpgradeRequestImpl(this, coreClient, request, toUri, websocket); coreClient.connect(upgradeRequest); diff --git a/jetty-websocket/jetty-websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionImpl.java b/jetty-websocket/jetty-websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionImpl.java index eff766bc247..58f02e6f479 100644 --- a/jetty-websocket/jetty-websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionImpl.java +++ b/jetty-websocket/jetty-websocket-common/src/main/java/org/eclipse/jetty/websocket/common/WebSocketSessionImpl.java @@ -18,6 +18,11 @@ package org.eclipse.jetty.websocket.common; +import java.io.IOException; +import java.net.SocketAddress; +import java.time.Duration; +import java.util.Objects; + import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.websocket.api.CloseStatus; import org.eclipse.jetty.websocket.api.Session; @@ -27,11 +32,6 @@ import org.eclipse.jetty.websocket.api.UpgradeResponse; import org.eclipse.jetty.websocket.api.WebSocketBehavior; import org.eclipse.jetty.websocket.core.FrameHandler; -import java.io.IOException; -import java.net.SocketAddress; -import java.time.Duration; -import java.util.Objects; - public class WebSocketSessionImpl implements Session, Dumpable { private final FrameHandler.CoreSession coreSession; @@ -160,7 +160,7 @@ public class WebSocketSessionImpl implements Session, Dumpable @Override public boolean isOpen() { - return remoteEndpoint.getCoreSession().isOpen(); + return remoteEndpoint.getCoreSession().isOutputOpen(); } @Override diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/CloseStatus.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/CloseStatus.java index 604ee09edda..4e9f30de364 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/CloseStatus.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/CloseStatus.java @@ -18,14 +18,14 @@ package org.eclipse.jetty.websocket.core; -import org.eclipse.jetty.util.BufferUtil; -import org.eclipse.jetty.util.Utf8Appendable; -import org.eclipse.jetty.util.Utf8StringBuilder; - import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.Arrays; +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.util.Utf8Appendable; +import org.eclipse.jetty.util.Utf8StringBuilder; + /** * Representation of a WebSocket Close (status code & reason) */ @@ -162,6 +162,15 @@ public class CloseStatus return; } + public static CloseStatus getCloseStatus(Frame frame) + { + if (frame instanceof CloseStatus.Supplier) + return ((CloseStatus.Supplier)frame).getCloseStatus(); + if (frame.getOpCode()==OpCode.CLOSE) + return new CloseStatus(frame); + return null; + } + public int getCode() { return code; @@ -184,7 +193,7 @@ public class CloseStatus int len = 2; // status code - byte reasonBytes[] = null; + byte[] reasonBytes = null; if (reason != null) { @@ -198,7 +207,7 @@ public class CloseStatus ByteBuffer buf = BufferUtil.allocate(len); BufferUtil.flipToFill(buf); buf.put((byte)((statusCode >>> 8) & 0xFF)); - buf.put((byte)((statusCode >>> 0) & 0xFF)); + buf.put((byte)(statusCode & 0xFF)); if ((reasonBytes != null) && (reasonBytes.length > 0)) { @@ -265,19 +274,19 @@ public class CloseStatus public Frame toFrame() { - return toFrame(code, reason); + if (isTransmittableStatusCode(code)) + return new CloseFrame(this, OpCode.CLOSE, true, asPayloadBuffer(code, reason)); + return new CloseFrame(this, OpCode.CLOSE); } public static Frame toFrame(int closeStatus) { - return toFrame(closeStatus, null); + return new CloseStatus(closeStatus).toFrame(); } public static Frame toFrame(int closeStatus, String reason) { - if (isTransmittableStatusCode(closeStatus)) - return new Frame(OpCode.CLOSE, true, asPayloadBuffer(closeStatus, reason)); - return new Frame(OpCode.CLOSE); + return new CloseStatus(closeStatus, reason).toFrame(); } public static String codeString(int closeStatus) @@ -324,4 +333,27 @@ public class CloseStatus return String.format("{%04d=%s,%s}", code, codeString(code), reason); } + public interface Supplier + { + CloseStatus getCloseStatus(); + } + + class CloseFrame extends Frame implements CloseStatus.Supplier + { + public CloseFrame(CloseStatus closeStatus, byte opcode) + { + super(opcode); + } + + public CloseFrame(CloseStatus closeStatus, byte opCode, boolean fin, ByteBuffer payload) + { + super(opCode, fin, payload); + } + + @Override + public CloseStatus getCloseStatus() + { + return CloseStatus.this; + } + } } diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/FrameHandler.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/FrameHandler.java index 2ab1908c355..80ef69c586d 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/FrameHandler.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/FrameHandler.java @@ -270,7 +270,7 @@ public interface FrameHandler extends IncomingFrames /** * @return True if the websocket is open outbound */ - boolean isOpen(); + boolean isOutputOpen(); /** * If using BatchMode.ON or BatchMode.AUTO, trigger a flush of enqueued / batched frames. @@ -374,7 +374,7 @@ public interface FrameHandler extends IncomingFrames } @Override - public boolean isOpen() + public boolean isOutputOpen() { return false; } diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/ExtensionStack.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/ExtensionStack.java index 0df60eedf94..fa903261f14 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/ExtensionStack.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/ExtensionStack.java @@ -19,18 +19,15 @@ package org.eclipse.jetty.websocket.core.internal; import java.io.IOException; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.ListIterator; -import java.util.Queue; import java.util.stream.Collectors; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.util.Callback; import org.eclipse.jetty.util.DecoratedObjectFactory; -import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.annotation.ManagedAttribute; import org.eclipse.jetty.util.annotation.ManagedObject; import org.eclipse.jetty.util.component.Dumpable; @@ -51,8 +48,6 @@ public class ExtensionStack implements IncomingFrames, OutgoingFrames, Dumpable { private static final Logger LOG = Log.getLogger(ExtensionStack.class); - private final Queue entries = new ArrayDeque<>(); - private final IteratingCallback flusher = new Flusher(); private final WebSocketExtensionRegistry factory; private List extensions; private IncomingFrames incoming; @@ -198,14 +193,12 @@ public class ExtensionStack implements IncomingFrames, OutgoingFrames, Dumpable { if (outgoing == null) throw new IllegalStateException(); - FrameEntry entry = new FrameEntry(frame, callback, batch); if (LOG.isDebugEnabled()) - LOG.debug("Queuing {}", entry); - offerEntry(entry); - flusher.iterate(); + LOG.debug("Extending out {} {} {}", frame, callback, batch); + outgoing.sendFrame(frame, callback, batch); } - public void connect(IncomingFrames incoming, OutgoingFrames outgoing, WebSocketChannel webSocketChannel) + public void initialize(IncomingFrames incoming, OutgoingFrames outgoing, WebSocketChannel webSocketChannel) { if (extensions == null) throw new IllegalStateException(); @@ -224,30 +217,6 @@ public class ExtensionStack implements IncomingFrames, OutgoingFrames, Dumpable extension.setWebSocketChannel(webSocketChannel); } - private void offerEntry(FrameEntry entry) - { - synchronized (this) - { - entries.offer(entry); - } - } - - private FrameEntry pollEntry() - { - synchronized (this) - { - return entries.poll(); - } - } - - private int getQueueSize() - { - synchronized (this) - { - return entries.size(); - } - } - @Override public String dump() { @@ -263,16 +232,14 @@ public class ExtensionStack implements IncomingFrames, OutgoingFrames, Dumpable @Override public String dumpSelf() { - return String.format("%s@%x[size=%d,queueSize=%d]", getClass().getSimpleName(), hashCode(), extensions.size(), getQueueSize()); + return String.format("%s@%x[size=%d]", getClass().getSimpleName(), hashCode(), extensions.size()); } @Override public String toString() { StringBuilder s = new StringBuilder(); - s.append("ExtensionStack["); - s.append("queueSize=").append(getQueueSize()); - s.append(",extensions="); + s.append("ExtensionStack[extensions="); if (extensions == null) { s.append(""); @@ -304,94 +271,4 @@ public class ExtensionStack implements IncomingFrames, OutgoingFrames, Dumpable s.append("]"); return s.toString(); } - - private class Flusher extends IteratingCallback implements Callback - { - private FrameEntry current; - - @Override - protected Action process() throws Exception - { - current = pollEntry(); - if (current == null) - { - if (LOG.isDebugEnabled()) - LOG.debug("Entering IDLE"); - return Action.IDLE; - } - if (LOG.isDebugEnabled()) - LOG.debug("Processing {}", current); - outgoing.sendFrame(current.frame, this, current.batch); - return Action.SCHEDULED; - } - - @Override - protected void onCompleteSuccess() - { - // This IteratingCallback never completes. - throw new IllegalStateException("This IteratingCallback should never complete."); - } - - @Override - protected void onCompleteFailure(Throwable x) - { - // This IteratingCallback never fails. - // The callback are those provided by WriteCallback (implemented - // below) and even in case of writeFailed() we call succeeded(). - throw new IllegalStateException("This IteratingCallback should never fail."); - } - - @Override - public void succeeded() - { - // Notify first then call succeeded(), otherwise - // write callbacks may be invoked out of order. - notifyCallbackSuccess(current.callback); - super.succeeded(); - } - - @Override - public void failed(Throwable cause) - { - // Notify first, the call succeeded() to drain the queue. - // We don't want to call failed(x) because that will put - // this flusher into a final state that cannot be exited, - // and the failure of a frame may not mean that the whole - // connection is now invalid. - notifyCallbackFailure(current.callback, cause); - super.succeeded(); - } - - private void notifyCallbackSuccess(Callback callback) - { - try - { - if (callback != null) - callback.succeeded(); - } - catch (Throwable x) - { - LOG.debug("Exception while notifying success of callback " + callback, x); - } - } - - private void notifyCallbackFailure(Callback callback, Throwable failure) - { - try - { - if (callback != null) - callback.failed(failure); - } - catch (Throwable x) - { - LOG.debug("Exception while notifying failure of callback " + callback, x); - } - } - - @Override - public String toString() - { - return "ExtensionStack$Flusher[" + (extensions == null?-1:extensions.size()) + "]"; - } - } } diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java index a1039932f48..211c50c9668 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/FrameFlusher.java @@ -18,6 +18,14 @@ package org.eclipse.jetty.websocket.core.internal; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayDeque; +import java.util.ArrayList; +import java.util.Deque; +import java.util.List; +import java.util.Objects; + import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.util.BufferUtil; @@ -28,14 +36,6 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.OpCode; -import java.nio.ByteBuffer; -import java.nio.channels.ClosedChannelException; -import java.util.ArrayDeque; -import java.util.ArrayList; -import java.util.Deque; -import java.util.List; -import java.util.Objects; - public class FrameFlusher extends IteratingCallback { public static final Frame FLUSH_FRAME = new Frame(OpCode.BINARY); @@ -49,8 +49,6 @@ public class FrameFlusher extends IteratingCallback private final Deque queue = new ArrayDeque<>(); private final List entries; private final List buffers; - private boolean closed; - private Throwable terminated; private ByteBuffer batchBuffer = null; public FrameFlusher(ByteBufferPool bufferPool, Generator generator, EndPoint endPoint, int bufferSize, int maxGather) @@ -67,25 +65,26 @@ public class FrameFlusher extends IteratingCallback public void enqueue(Frame frame, Callback callback, boolean batch) { Entry entry = new Entry(frame, callback, batch); - - Throwable closed; + byte opCode = frame.getOpCode(); synchronized (this) { - closed = terminated; - if (closed == null) - { - byte opCode = frame.getOpCode(); - if (opCode == OpCode.PING || opCode == OpCode.PONG) - queue.offerFirst(entry); - else - queue.offerLast(entry); - } + if (opCode == OpCode.PING || opCode == OpCode.PONG) + queue.offerFirst(entry); + else + queue.offerLast(entry); } + } - if (closed == null) - iterate(); - else - notifyCallbackFailure(callback, closed); + public void onClose() + { + Throwable cause = null; + synchronized (this) + { + if (!queue.isEmpty()) + cause = new IOException("Closed"); + } + if (cause!=null) + onCompleteFailure(cause); } @Override @@ -102,12 +101,6 @@ public class FrameFlusher extends IteratingCallback if (succeedEntries() && batchBuffer != null) BufferUtil.clear(batchBuffer); - if (closed) - return Action.SUCCEEDED; - - if (terminated != null) - throw terminated; - while (!queue.isEmpty() && entries.size() <= maxGather) { Entry entry = queue.poll(); @@ -167,7 +160,7 @@ public class FrameFlusher extends IteratingCallback } if (LOG.isDebugEnabled()) - LOG.debug("{} processed {} entries flush=%b batch=%s: {}", + LOG.debug("{} processed {} entries flush={} batch={}: {}", this, entries.size(), flush, @@ -220,10 +213,7 @@ public class FrameFlusher extends IteratingCallback notifyCallbackSuccess(entry.callback); entry.release(); if (entry.frame.getOpCode() == OpCode.CLOSE) - { - terminate(new ClosedChannelException(), true); endPoint.shutdownOutput(); - } } entries.clear(); return hadEntries; @@ -233,13 +223,8 @@ public class FrameFlusher extends IteratingCallback public void onCompleteFailure(Throwable failure) { releaseAggregate(); - - Throwable closed; synchronized (this) { - closed = terminated; - if (closed == null) - terminated = failure; entries.addAll(queue); queue.clear(); } @@ -261,22 +246,6 @@ public class FrameFlusher extends IteratingCallback } } - public void terminate(Throwable cause, boolean close) - { - Throwable reason; - synchronized (this) - { - closed = close; - reason = terminated; - if (reason == null) - terminated = cause; - } - if (LOG.isDebugEnabled()) - LOG.debug("{} {}", reason == null?"Terminating":"Terminated", this); - if (reason == null && !close) - iterate(); - } - protected void notifyCallbackSuccess(Callback callback) { try @@ -312,12 +281,10 @@ public class FrameFlusher extends IteratingCallback @Override public String toString() { - return String.format("%s@%x[queueSize=%d,aggregate=%s,terminated=%s]", - getClass().getSimpleName(), - hashCode(), + return String.format("%s[queueSize=%d,aggregate=%s]", + super.toString(), getQueueSize(), - BufferUtil.toDetailString(batchBuffer), - terminated); + BufferUtil.toDetailString(batchBuffer)); } private class Entry extends FrameEntry @@ -353,7 +320,7 @@ public class FrameFlusher extends IteratingCallback @Override public String toString() { - return String.format("%s[%s,%s,%b,%s]", getClass().getSimpleName(), frame, callback, batch, terminated); + return String.format("%s{%s,%s,%b}", getClass().getSimpleName(), frame, callback, batch); } } } diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/Parser.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/Parser.java index edd8fcc3958..04d9fb5cbb3 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/Parser.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/Parser.java @@ -32,6 +32,7 @@ import org.eclipse.jetty.websocket.core.WebSocketException; import java.io.Closeable; import java.nio.ByteBuffer; +import java.util.function.Supplier; /** * Parsing of a frames in WebSocket land. @@ -374,7 +375,7 @@ public class Parser .format("Parser@%x[s=%s,c=%d,o=0x%x,m=%s,l=%d]", hashCode(), state, cursor, firstByte, mask == null?"-":TypeUtil.toHexString(mask), payloadLength); } - public class ParsedFrame extends Frame implements Closeable + public class ParsedFrame extends Frame implements Closeable, CloseStatus.Supplier { final CloseStatus closeStatus; final boolean releaseable; @@ -404,6 +405,7 @@ public class Parser bufferPool.release(getPayload()); } + @Override public CloseStatus getCloseStatus() { return closeStatus; diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketChannel.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketChannel.java index 14cb3d1a796..ec4e158044a 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketChannel.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketChannel.java @@ -18,9 +18,21 @@ package org.eclipse.jetty.websocket.core.internal; +import java.io.IOException; +import java.net.SocketAddress; +import java.net.SocketTimeoutException; +import java.net.URI; +import java.time.Duration; +import java.util.ArrayDeque; +import java.util.List; +import java.util.Map; +import java.util.Queue; +import java.util.concurrent.Executor; +import java.util.concurrent.TimeoutException; + import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.EofException; import org.eclipse.jetty.util.Callback; +import org.eclipse.jetty.util.IteratingCallback; import org.eclipse.jetty.util.Utf8Appendable; import org.eclipse.jetty.util.component.Dumpable; import org.eclipse.jetty.util.log.Log; @@ -40,16 +52,6 @@ import org.eclipse.jetty.websocket.core.WebSocketConstants; import org.eclipse.jetty.websocket.core.WebSocketTimeoutException; import org.eclipse.jetty.websocket.core.internal.Parser.ParsedFrame; -import java.io.IOException; -import java.net.SocketAddress; -import java.net.SocketException; -import java.net.SocketTimeoutException; -import java.net.URI; -import java.time.Duration; -import java.util.List; -import java.util.Map; -import java.util.concurrent.Executor; - /** * The Core WebSocket Session. */ @@ -59,11 +61,11 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio private final static CloseStatus NO_CODE = new CloseStatus(CloseStatus.NO_CODE); private final Behavior behavior; - private final WebSocketChannelState state = new WebSocketChannelState(); + private final WebSocketChannelState channelState = new WebSocketChannelState(); private final FrameHandler handler; private final Negotiated negotiated; private final boolean demanding; - private final FrameSequence outgoingSequence = new FrameSequence(); + private final Flusher flusher = new Flusher(); private WebSocketConnection connection; private boolean autoFragment = WebSocketConstants.DEFAULT_AUTO_FRAGMENT; @@ -81,7 +83,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio this.behavior = behavior; this.negotiated = negotiated; this.demanding = handler.isDemanding(); - negotiated.getExtensions().connect(new IncomingState(), new OutgoingState(), this); + negotiated.getExtensions().initialize(new IncomingAdaptor(), new OutgoingAdaptor(), this); } /** @@ -155,7 +157,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio if (frame.getOpCode() == OpCode.CLOSE) { if (!(frame instanceof ParsedFrame)) // already check in parser - new CloseStatus(frame.getPayload()); + CloseStatus.getCloseStatus(frame); // return ignored as get used to validate there is a closeStatus } } else @@ -237,9 +239,14 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio } @Override - public boolean isOpen() + public boolean isOutputOpen() { - return state.isOutOpen(); + return channelState.isOutputOpen(); + } + + public boolean isClosed() + { + return channelState.isClosed(); } public void setWebSocketConnection(WebSocketConnection connection) @@ -273,44 +280,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio private void close(CloseStatus closeStatus, Callback callback, boolean batch) { - if (state.onCloseOut(closeStatus)) - { - callback = new Callback.Nested(callback) - { - @Override - public void completed() - { - try - { - handler.onClosed(state.getCloseStatus()); - } - catch (Throwable e) - { - try - { - handler.onError(e); - } - catch (Throwable e2) - { - e.addSuppressed(e2); - LOG.warn(e); - } - } - finally - { - connection.close(); - } - } - }; - } - - if (LOG.isDebugEnabled()) - { - LOG.debug("close({}, {}, {})", closeStatus, callback, batch); - } - - Frame frame = closeStatus.toFrame(); - negotiated.getExtensions().sendFrame(frame, callback, batch); + sendFrame(closeStatus.toFrame(), callback, batch); } @Override @@ -321,99 +291,94 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio public void onClosed(Throwable cause) { - onClosed(cause, new CloseStatus(CloseStatus.NO_CLOSE, cause == null?null:cause.toString())); + CloseStatus closeStatus = new CloseStatus(CloseStatus.NO_CLOSE, cause == null?null:cause.toString()); + if (channelState.onClosed(closeStatus)) + closeConnection(cause, closeStatus); } - public void onClosed(Throwable cause, CloseStatus closeStatus) + public void closeConnection(Throwable cause, CloseStatus closeStatus) { - if (state.onClosed(closeStatus)) - { - connection.cancelDemand(); + connection.cancelDemand(); - // Forward Errors to Local WebSocket EndPoint + // Forward Errors to Local WebSocket EndPoint + if (cause!=null) + { try { handler.onError(cause); } catch (Throwable e) { - cause.addSuppressed(e); + if (e != cause) + cause.addSuppressed(e); LOG.warn(cause); } - - try - { - handler.onClosed(closeStatus); - } - catch (Exception e) - { - LOG.warn(e); - } - } - } - - /** - * Process an Error event seen by the Session and/or Connection - * - * @param cause the cause - */ - public void processError(Throwable cause) - { - CloseStatus closeStatus; - - if (cause instanceof Utf8Appendable.NotUtf8Exception) - { - closeStatus = new CloseStatus(CloseStatus.BAD_PAYLOAD, cause.getMessage()); - } - else if (cause instanceof SocketTimeoutException) - { - // A path often seen in Windows - closeStatus = new CloseStatus(CloseStatus.SHUTDOWN, cause.getMessage()); - } - else if (cause instanceof IOException) - { - closeStatus = new CloseStatus(CloseStatus.PROTOCOL, cause.getMessage()); - } - else if (cause instanceof SocketException) - { - // A path unique to Unix - closeStatus = new CloseStatus(CloseStatus.SHUTDOWN, cause.getMessage()); - } - else if (cause instanceof CloseException) - { - CloseException ce = (CloseException)cause; - closeStatus = new CloseStatus(ce.getStatusCode(), ce.getMessage()); - } - else if (cause instanceof WebSocketTimeoutException) - { - closeStatus = new CloseStatus(CloseStatus.SHUTDOWN, cause.getMessage()); - } - else - { - LOG.warn("Unhandled Error (closing connection)", cause); - - // Exception on end-user WS-Endpoint. - // Fast-fail & close connection with reason. - int statusCode = CloseStatus.SERVER_ERROR; - if (behavior == Behavior.CLIENT) - statusCode = CloseStatus.POLICY_VIOLATION; - - closeStatus = new CloseStatus(statusCode, cause.getMessage()); } try { - // TODO can we avoid the illegal state exception in outClosed - close(closeStatus, Callback.NOOP, false); + handler.onClosed(closeStatus); } - catch (IllegalStateException e) + catch (Throwable e) { - if (cause == null) - cause = e; - else - cause.addSuppressed(e); + LOG.warn(e); } - onClosed(cause, closeStatus); + + if (connection.getEndPoint().isOpen()) + connection.close(); + } + + AbnormalCloseStatus abnormalCloseStatusFor(Throwable cause) + { + int code; + if (cause instanceof ProtocolException) + code = CloseStatus.PROTOCOL; + else if (cause instanceof CloseException) + code = ((CloseException)cause).getStatusCode(); + else if (cause instanceof Utf8Appendable.NotUtf8Exception) + code = CloseStatus.BAD_PAYLOAD; + else if (cause instanceof WebSocketTimeoutException || cause instanceof TimeoutException || cause instanceof SocketTimeoutException) + code = CloseStatus.SHUTDOWN; + else if (behavior == Behavior.CLIENT) + code = CloseStatus.POLICY_VIOLATION; + else + code = CloseStatus.SERVER_ERROR; + + return new AbnormalCloseStatus(code, cause.getMessage()); + } + + /** + * Process an Error that originated from the connection. + * For protocol causes, send and abnormal close frame + * otherwise just close the connection. + * + * @param cause the cause + */ + public void processConnectionError(Throwable cause) + { + if (LOG.isDebugEnabled()) + LOG.debug("processConnectionError {} {}", this, cause); + + CloseStatus closeStatus = abnormalCloseStatusFor(cause); + + if (closeStatus.getCode() == CloseStatus.PROTOCOL) + close(closeStatus, Callback.NOOP, false); + else if (channelState.onClosed(closeStatus)) + closeConnection(cause, closeStatus); + } + + /** + * Process an Error that originated from the handler. + * Send an abnormal close frame to ensure connection is closed. + * + * @param cause the cause + */ + public void processHandlerError(Throwable cause) + { + if (LOG.isDebugEnabled()) + LOG.debug("processHandlerError {} {}", this, cause); + + close(abnormalCloseStatusFor(cause), Callback.NOOP, false); } /** @@ -427,32 +392,32 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio try { // Upgrade success - state.onConnected(); + channelState.onConnected(); if (LOG.isDebugEnabled()) LOG.debug("ConnectionState: Transition to CONNECTED"); - try - { - // Open connection and handler - state.onOpen(); - handler.onOpen(this); - if (!demanding) - connection.demand(1); + // Open connection and handler + channelState.onOpen(); + handler.onOpen(this); + if (!demanding) + connection.demand(1); - if (LOG.isDebugEnabled()) - LOG.debug("ConnectionState: Transition to OPEN"); - } - catch (Throwable t) - { - LOG.warn("Error during OPEN", t); - // TODO: this must trigger onError AND onClose - processError(new CloseException(CloseStatus.SERVER_ERROR, t)); - } + if (LOG.isDebugEnabled()) + LOG.debug("ConnectionState: Transition to OPEN"); } catch (Throwable t) { - processError(t); // Handle error + LOG.warn("Error during OPEN", t); + try + { + handler.onError(t); + } + catch (Exception e) + { + t.addSuppressed(e); + } + processHandlerError(new CloseException(CloseStatus.SERVER_ERROR, t)); } } @@ -499,7 +464,6 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio try { assertValidOutgoing(frame); - outgoingSequence.check(frame.getOpCode(), frame.isFin()); } catch (Throwable ex) { @@ -507,20 +471,60 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio return; } - if (frame.getOpCode() == OpCode.CLOSE) + try { - close(new CloseStatus(frame.getPayload()), callback, batch); + synchronized(flusher) + { + boolean closeConnection = channelState.onOutgoingFrame(frame); + + if (frame.getOpCode() == OpCode.CLOSE) + { + if (LOG.isDebugEnabled()) + LOG.debug("close({}, {}, {})", CloseStatus.getCloseStatus(frame), callback, batch); + + if (closeConnection) + { + callback = new Callback.Nested(callback) + { + @Override + public void completed() + { + closeConnection(null, channelState.getCloseStatus()); + } + }; + } + } + + flusher.queue.offer(new FrameEntry(frame, callback, batch)); + } + flusher.iterate(); } - else + catch (Throwable ex) { - negotiated.getExtensions().sendFrame(frame, callback, batch); + try + { + callback.failed(ex); + } + finally + { + if (frame.getOpCode() == OpCode.CLOSE) + { + CloseStatus closeStatus = CloseStatus.getCloseStatus(frame); + if (closeStatus instanceof AbnormalCloseStatus) + closeConnection(null, closeStatus); + } + } } } @Override public void flush(Callback callback) { - negotiated.getExtensions().sendFrame(FrameFlusher.FLUSH_FRAME, callback, false); + synchronized(flusher) + { + flusher.queue.offer(new FrameEntry(FrameFlusher.FLUSH_FRAME, callback, false)); + } + flusher.iterate(); } @Override @@ -602,7 +606,7 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio maxTextMessageSize = maxSize; } - private class IncomingState extends FrameSequence implements IncomingFrames + private class IncomingAdaptor implements IncomingFrames { @Override public void onFrame(Frame frame, Callback callback) @@ -611,59 +615,52 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio { if (LOG.isDebugEnabled()) LOG.debug("receiveFrame({}, {}) - connectionState={}, handler={}", - frame, callback, state, handler); + frame, callback, channelState, handler); - check(frame.getOpCode(), frame.isFin()); - if (state.isInOpen()) + boolean closeConnection = channelState.onIncomingFrame(frame); + + // Handle inbound close + if (frame.getOpCode() == OpCode.CLOSE) { - // Handle inbound close - if (frame.getOpCode() == OpCode.CLOSE) + connection.cancelDemand(); + if (closeConnection) { - connection.cancelDemand(); - CloseStatus closeStatus = ((ParsedFrame)frame).getCloseStatus(); - if (state.onCloseIn(closeStatus)) - { - callback = new Callback.Nested(callback) - { - @Override - public void completed() - { - handler.onClosed(state.getCloseStatus()); - connection.close(); - } - }; - handler.onFrame(frame, callback); - return; - } - callback = new Callback.Nested(callback) { @Override public void completed() { - // was a close sent by the handler? - if (state.isOutOpen()) - { - // No! - if (LOG.isDebugEnabled()) - LOG.debug("ConnectionState: sending close response {}", closeStatus); - - close(closeStatus.getCode(), closeStatus.getReason(), Callback.NOOP); - return; - } + handler.onClosed(channelState.getCloseStatus()); + connection.close(); } }; + handler.onFrame(frame, callback); + return; } - // Handle the frame - handler.onFrame(frame, callback); - } - else - { - if (LOG.isDebugEnabled()) - LOG.debug("Discarding post EOF frame - {}", frame); - callback.failed(new EofException()); + callback = new Callback.Nested(callback) + { + @Override + public void completed() + { + if (channelState.isOutputOpen()) + { + CloseStatus closeStatus = CloseStatus.getCloseStatus(frame); + + if (LOG.isDebugEnabled()) + LOG.debug("ConnectionState: sending close response {}", closeStatus); + + // this may race with a rare application close but errors are ignored + if (closeStatus==null) + closeStatus = CloseStatus.NO_CODE_STATUS; + close(closeStatus.getCode(), closeStatus.getReason(), Callback.NOOP); + } + } + }; } + + // Handle the frame + handler.onFrame(frame, callback); } catch (Throwable t) { @@ -672,14 +669,14 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio } } - private class OutgoingState implements OutgoingFrames + private class OutgoingAdaptor implements OutgoingFrames { @Override public void sendFrame(Frame frame, Callback callback, boolean batch) { try { - connection.sendFrame(frame, callback, batch); + connection.enqueueFrame(frame, callback, batch); } catch (ProtocolException e) { @@ -742,9 +739,10 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio @Override public String toString() { - return String.format("WSChannel@%x{%s,%s,af=%b,i/o=%d/%d,fs=%d}->%s", + return String.format("WSChannel@%x{%s,%s,%s,af=%b,i/o=%d/%d,fs=%d}->%s", hashCode(), - state, + behavior, + channelState, negotiated, autoFragment, inputBufferSize, @@ -752,4 +750,67 @@ public class WebSocketChannel implements IncomingFrames, FrameHandler.CoreSessio maxFrameSize, handler); } + + static class AbnormalCloseStatus extends CloseStatus + { + public AbnormalCloseStatus(int statusCode, String reasonPhrase) + { + super(statusCode, reasonPhrase); + } + } + + private class Flusher extends IteratingCallback + { + private final Queue queue = new ArrayDeque<>(); + FrameEntry entry; + + @Override + protected Action process() throws Throwable + { + synchronized (this) + { + entry = queue.poll(); + } + if (entry==null) + return Action.IDLE; + + negotiated.getExtensions().sendFrame(entry.frame, this, entry.batch); + return Action.SCHEDULED; + } + + @Override + public void succeeded() + { + entry.callback.succeeded(); + super.succeeded(); + } + + @Override + protected void onCompleteFailure(Throwable cause) + { + entry.callback.failed(cause); + Queue entries; + synchronized (this) + { + entries = new ArrayDeque<>(queue); + queue.clear(); + } + entries.forEach(e-> failEntry(cause, e)); + } + + private void failEntry(Throwable cause, FrameEntry e) + { + try + { + e.callback.failed(cause); + } + catch(Throwable x) + { + if (cause != x) + cause.addSuppressed(x); + LOG.warn(cause); + } + } + } + } diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketChannelState.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketChannelState.java index 376f6f6560c..815959e940a 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketChannelState.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketChannelState.java @@ -19,140 +19,212 @@ package org.eclipse.jetty.websocket.core.internal; import org.eclipse.jetty.websocket.core.CloseStatus; - -import java.util.concurrent.atomic.AtomicReference; +import org.eclipse.jetty.websocket.core.Frame; +import org.eclipse.jetty.websocket.core.OpCode; +import org.eclipse.jetty.websocket.core.ProtocolException; /** * Atomic Connection State */ public class WebSocketChannelState { - private static class State + enum State { - final String name; - final boolean inOpen; - final boolean outOpen; - final CloseStatus closeStatus; - - State(String name, boolean inOpen, boolean outOpen, CloseStatus closeStatus) - { - this.name = name; - this.inOpen = inOpen; - this.outOpen = outOpen; - this.closeStatus = closeStatus; - } - - @Override - public String toString() - { - return String.format("%s{i=%b o=%b c=%d}", name, inOpen, outOpen, closeStatus == null?-1:closeStatus.getCode()); - } + CONNECTING, + CONNECTED, + OPEN, + ISHUT, + OSHUT, + CLOSED } - private static final State CONNECTING = new State("CONNECTING", false, false, null); - private static final State CONNECTED = new State("CONNECTED", true, true, null); - private static final State OPEN = new State("OPEN", true, true, null); - - private AtomicReference state = new AtomicReference<>(CONNECTING); + private State _channelState = State.CONNECTING; + private byte _incomingContinuation = OpCode.UNDEFINED; + private byte _outgoingContinuation = OpCode.UNDEFINED; + CloseStatus _closeStatus = null; public void onConnected() { - if (!state.compareAndSet(CONNECTING, CONNECTED)) - throw new IllegalStateException(state.get().toString()); + synchronized (this) + { + if (_channelState != State.CONNECTING) + throw new IllegalStateException(_channelState.toString()); + + _channelState = State.CONNECTED; + } } public void onOpen() { - if (!state.compareAndSet(CONNECTED, OPEN)) - throw new IllegalStateException(state.get().toString()); + synchronized (this) + { + if (_channelState != State.CONNECTED) + throw new IllegalStateException(_channelState.toString()); + + _channelState = State.OPEN; + } } @Override public String toString() { - return state.get().toString(); + return String.format("%s@%x{%s,i=%s,o=%s,c=%s}",getClass().getSimpleName(),hashCode(), + _channelState, + OpCode.name(_incomingContinuation), + OpCode.name(_outgoingContinuation), + _closeStatus); + } + + + public State getState() + { + synchronized (this) + { + return _channelState; + } } public boolean isClosed() { - State s = state.get(); - return !s.inOpen && !s.outOpen; + return getState()==State.CLOSED; } - public boolean isInOpen() + public boolean isInputOpen() { - return state.get().inOpen; + State state = getState(); + return (state==State.OPEN || state==State.OSHUT); } - public boolean isOutOpen() + public boolean isOutputOpen() { - return state.get().outOpen; + State state = getState(); + return (state==State.OPEN || state==State.ISHUT); } public CloseStatus getCloseStatus() { - return state.get().closeStatus; - } - - public boolean onCloseIn(CloseStatus closeStatus) - { - while (true) + synchronized (this) { - State s = state.get(); - - if (!s.inOpen) - throw new IllegalStateException(state.get().toString()); - - if (s.outOpen) - { - State closedIn = new State("ICLOSED", false, true, closeStatus); - if (state.compareAndSet(s, closedIn)) - return false; - } - else - { - State closed = new State("CLOSED", false, false, closeStatus); - if (state.compareAndSet(s, closed)) - return true; - } - } - } - - public boolean onCloseOut(CloseStatus closeStatus) - { - while (true) - { - State s = state.get(); - - if (!s.outOpen) - throw new IllegalStateException(state.get().toString()); - - if (s.inOpen) - { - State closedOut = new State("OCLOSED", true, false, closeStatus); - if (state.compareAndSet(s, closedOut)) - return false; - } - else - { - State closed = new State("CLOSED", false, false, closeStatus); - if (state.compareAndSet(s, closed)) - return true; - } + return _closeStatus; } } public boolean onClosed(CloseStatus closeStatus) { - while (true) + synchronized (this) { - State s = state.get(); - if (!s.outOpen && !s.inOpen) + if (_channelState == State.CLOSED) return false; - State newState = new State("CLOSED", false, false, closeStatus); - if (state.compareAndSet(s, newState)) - return true; + _closeStatus = closeStatus; + _channelState = State.CLOSED; + return true; + } + } + + public boolean onOutgoingFrame(Frame frame) throws ProtocolException + { + byte opcode = frame.getOpCode(); + boolean fin = frame.isFin(); + + synchronized (this) + { + if (!isOutputOpen()) + { + if (opcode == OpCode.CLOSE && CloseStatus.getCloseStatus(frame) instanceof WebSocketChannel.AbnormalCloseStatus) + _channelState = State.CLOSED; + throw new IllegalStateException(_channelState.toString()); + } + + if (opcode == OpCode.CLOSE) + { + _closeStatus = CloseStatus.getCloseStatus(frame); + if (_closeStatus instanceof WebSocketChannel.AbnormalCloseStatus) + { + _channelState = State.CLOSED; + return true; + } + + switch (_channelState) + { + case OPEN: + _channelState = State.OSHUT; + return false; + + case ISHUT: + _channelState = State.CLOSED; + return true; + + default: + throw new IllegalStateException(_channelState.toString()); + } + } + else if (frame.isDataFrame()) + { + _outgoingContinuation = checkDataSequence(opcode, fin, _outgoingContinuation); + } + } + + return false; + } + + public boolean onIncomingFrame(Frame frame) throws ProtocolException + { + byte opcode = frame.getOpCode(); + boolean fin = frame.isFin(); + + synchronized (this) + { + if (!isInputOpen()) + throw new IllegalStateException(_channelState.toString()); + + if (opcode == OpCode.CLOSE) + { + _closeStatus = CloseStatus.getCloseStatus(frame); + + switch (_channelState) + { + case OPEN: + _channelState = State.ISHUT; + return false; + case OSHUT: + _channelState = State.CLOSED; + return true; + default: + throw new IllegalStateException(_channelState.toString()); + } + } + else if (frame.isDataFrame()) + { + _incomingContinuation = checkDataSequence(opcode, fin, _incomingContinuation); + } + } + + return false; + } + + + private static byte checkDataSequence(byte opcode, boolean fin, byte lastOpCode) throws ProtocolException + { + switch (opcode) + { + case OpCode.TEXT: + case OpCode.BINARY: + if (lastOpCode != OpCode.UNDEFINED) + throw new ProtocolException("DataFrame before fin==true"); + if (!fin) + return opcode; + return OpCode.UNDEFINED; + + case OpCode.CONTINUATION: + if (lastOpCode == OpCode.UNDEFINED) + throw new ProtocolException("CONTINUATION after fin==true"); + if (fin) + return OpCode.UNDEFINED; + return lastOpCode; + + default: + return lastOpCode; } } } diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java index 2a961ccd098..3d5757542f7 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/core/internal/WebSocketConnection.java @@ -18,6 +18,13 @@ package org.eclipse.jetty.websocket.core.internal; +import java.io.IOException; +import java.net.InetSocketAddress; +import java.nio.ByteBuffer; +import java.util.Objects; +import java.util.Random; +import java.util.concurrent.Executor; + import org.eclipse.jetty.io.AbstractConnection; import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.Connection; @@ -31,21 +38,13 @@ import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.websocket.core.Behavior; import org.eclipse.jetty.websocket.core.Frame; import org.eclipse.jetty.websocket.core.MessageTooLargeException; -import org.eclipse.jetty.websocket.core.OutgoingFrames; import org.eclipse.jetty.websocket.core.ProtocolException; import org.eclipse.jetty.websocket.core.WebSocketTimeoutException; -import java.io.IOException; -import java.net.InetSocketAddress; -import java.nio.ByteBuffer; -import java.util.Objects; -import java.util.Random; -import java.util.concurrent.Executor; - /** * Provides the implementation of {@link org.eclipse.jetty.io.Connection} that is suitable for WebSocket */ -public class WebSocketConnection extends AbstractConnection implements Connection.UpgradeTo, Dumpable, OutgoingFrames, Runnable +public class WebSocketConnection extends AbstractConnection implements Connection.UpgradeTo, Dumpable, Runnable { private final Logger LOG = Log.getLogger(this.getClass()); @@ -168,23 +167,44 @@ public class WebSocketConnection extends AbstractConnection implements Connectio if (LOG.isDebugEnabled()) LOG.debug("onClose() of physical connection"); - // TODO review all close paths - IOException e = new IOException("Closed"); - flusher.terminate(e, true); - channel.onClosed(e); + if (!channel.isClosed()) + { + IOException e = new IOException("Closed"); + channel.onClosed(e); + } + flusher.onClose(); + super.onClose(); } + @Override public boolean onIdleExpired() { if (LOG.isDebugEnabled()) LOG.debug("onIdleExpired()"); - channel.processError(new WebSocketTimeoutException("Connection Idle Timeout")); + // treat as a handler error because socket is still open + channel.processHandlerError(new WebSocketTimeoutException("Connection Idle Timeout")); return true; } + /** + * Event for no activity on connection (read or write) + * + * @return true to signal that the endpoint must be closed, false to keep the endpoint open + */ + @Override + protected boolean onReadTimeout(Throwable timeout) + { + if (LOG.isDebugEnabled()) + LOG.debug("onReadTimeout()"); + + // treat as a handler error because socket is still open + channel.processHandlerError(new WebSocketTimeoutException("Timeout on Read", timeout)); + return false; + } + protected void onFrame(Parser.ParsedFrame frame) { if (LOG.isDebugEnabled()) @@ -221,7 +241,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio referenced.release(); // notify session & endpoint - channel.processError(cause); + channel.processHandlerError(cause); } }); } @@ -433,7 +453,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio LOG.warn(t.toString()); BufferUtil.clear(networkBuffer.getBuffer()); releaseNetworkBuffer(); - channel.processError(t); + channel.processConnectionError(t); } } @@ -478,18 +498,6 @@ public class WebSocketConnection extends AbstractConnection implements Connectio super.onOpen(); } - /** - * Event for no activity on connection (read or write) - * - * @return true to signal that the endpoint must be closed, false to keep the endpoint open - */ - @Override - protected boolean onReadTimeout(Throwable timeout) - { - channel.processError(new WebSocketTimeoutException("Timeout on Read", timeout)); - return false; - } - @Override public void setInputBufferSize(int inputBufferSize) { @@ -577,8 +585,13 @@ public class WebSocketConnection extends AbstractConnection implements Connectio setInitialBuffer(prefilled); } - @Override - public void sendFrame(Frame frame, Callback callback, boolean batch) + /** + * Enqueue a Frame to be sent. + * @param frame The frame to queue + * @param callback The callback to call once the frame is sent + * @param batch True if batch mode is to be used + */ + void enqueueFrame(Frame frame, Callback callback, boolean batch) { if (channel.getBehavior() == Behavior.CLIENT) { @@ -588,6 +601,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio wsf.setMask(mask); } flusher.enqueue(frame, callback, batch); + flusher.iterate(); } private class Flusher extends FrameFlusher @@ -601,7 +615,7 @@ public class WebSocketConnection extends AbstractConnection implements Connectio public void onCompleteFailure(Throwable x) { super.onCompleteFailure(x); - channel.processError(x); + channel.processConnectionError(x); } } } diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/WebSocketCloseTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/WebSocketCloseTest.java index 84484d5406c..1bd0b5478cc 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/WebSocketCloseTest.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/WebSocketCloseTest.java @@ -18,6 +18,11 @@ package org.eclipse.jetty.websocket.core; +import java.net.Socket; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; @@ -39,11 +44,6 @@ import org.eclipse.jetty.websocket.core.server.internal.RFC6455Handshaker; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Test; -import java.net.Socket; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; - import static org.eclipse.jetty.util.Callback.NOOP; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.containsString; @@ -65,7 +65,7 @@ public class WebSocketCloseTest extends WebSocketTester enum State { - OPEN, ICLOSED, OCLOSED + OPEN, ISHUT, OSHUT } @AfterEach @@ -93,7 +93,7 @@ public class WebSocketCloseTest extends WebSocketTester break; } - case ICLOSED: + case ISHUT: { TestFrameHandler serverHandler = new TestFrameHandler(); @@ -109,12 +109,12 @@ public class WebSocketCloseTest extends WebSocketTester assertNotNull(frame); assertThat(new CloseStatus(frame.getPayload()).getCode(), is(CloseStatus.NORMAL)); - assertThat(server.handler.getCoreSession().toString(), containsString("ICLOSED")); - LOG.info("Server: ICLOSED"); + assertThat(server.handler.getCoreSession().toString(), containsString("ISHUT")); + LOG.info("Server: ISHUT"); break; } - case OCLOSED: + case OSHUT: { TestFrameHandler serverHandler = new TestFrameHandler(); @@ -129,8 +129,8 @@ public class WebSocketCloseTest extends WebSocketTester assertNotNull(frame); assertThat(new CloseStatus(frame.getPayload()).getCode(), is(CloseStatus.NORMAL)); - assertThat(server.handler.getCoreSession().toString(), containsString("OCLOSED")); - LOG.info("Server: OCLOSED"); + assertThat(server.handler.getCoreSession().toString(), containsString("OSHUT")); + LOG.info("Server: OSHUT"); break; } @@ -140,7 +140,7 @@ public class WebSocketCloseTest extends WebSocketTester @Test public void serverClose_ICLOSED() throws Exception { - setup(State.ICLOSED); + setup(State.ISHUT); server.handler.receivedCallback.poll().succeeded(); Frame frame = receiveFrame(client.getInputStream()); @@ -154,7 +154,7 @@ public class WebSocketCloseTest extends WebSocketTester @Test public void serverDifferentClose_ICLOSED() throws Exception { - setup(State.ICLOSED); + setup(State.ISHUT); server.sendFrame(CloseStatus.toFrame(CloseStatus.SHUTDOWN)); server.handler.receivedCallback.poll().succeeded(); @@ -171,7 +171,7 @@ public class WebSocketCloseTest extends WebSocketTester { try (StacklessLogging stackless = new StacklessLogging(WebSocketChannel.class)) { - setup(State.ICLOSED); + setup(State.ISHUT); server.handler.receivedCallback.poll().failed(new Exception("test failure")); Frame frame = receiveFrame(client.getInputStream()); @@ -186,7 +186,7 @@ public class WebSocketCloseTest extends WebSocketTester @Test public void clientClose_OCLOSED() throws Exception { - setup(State.OCLOSED); + setup(State.OSHUT); server.handler.getCoreSession().demand(1); client.getOutputStream().write(RawFrameBuilder.buildClose(new CloseStatus(CloseStatus.NORMAL), true)); assertNotNull(server.handler.receivedFrames.poll(10, TimeUnit.SECONDS)); @@ -201,7 +201,7 @@ public class WebSocketCloseTest extends WebSocketTester @Test public void clientDifferentClose_OCLOSED() throws Exception { - setup(State.OCLOSED); + setup(State.OSHUT); server.handler.getCoreSession().demand(1); client.getOutputStream().write(RawFrameBuilder.buildClose(new CloseStatus(CloseStatus.BAD_PAYLOAD), true)); assertNotNull(server.handler.receivedFrames.poll(10, TimeUnit.SECONDS)); @@ -218,7 +218,7 @@ public class WebSocketCloseTest extends WebSocketTester { try (StacklessLogging stackless = new StacklessLogging(WebSocketChannel.class)) { - setup(State.OCLOSED); + setup(State.OSHUT); server.handler.getCoreSession().demand(1); client.getOutputStream().write(RawFrameBuilder.buildClose(new CloseStatus(CloseStatus.NORMAL), true)); assertNotNull(server.handler.receivedFrames.poll(10, TimeUnit.SECONDS)); @@ -246,7 +246,7 @@ public class WebSocketCloseTest extends WebSocketTester @Test public void clientSendsBadFrame_OCLOSED() throws Exception { - setup(State.OCLOSED); + setup(State.OSHUT); client.getOutputStream().write(RawFrameBuilder.buildFrame(OpCode.PONG, "pong frame not masked", false)); server.handler.getCoreSession().demand(1); @@ -258,7 +258,7 @@ public class WebSocketCloseTest extends WebSocketTester @Test public void clientSendsBadFrame_ICLOSED() throws Exception { - setup(State.ICLOSED); + setup(State.ISHUT); client.getOutputStream().write(RawFrameBuilder.buildFrame(OpCode.PONG, "pong frame not masked", false)); assertFalse(server.handler.closed.await(250, TimeUnit.MILLISECONDS)); @@ -286,7 +286,7 @@ public class WebSocketCloseTest extends WebSocketTester @Test public void clientAborts_OCLOSED() throws Exception { - setup(State.OCLOSED); + setup(State.OSHUT); client.close(); assertFalse(server.handler.closed.await(250, TimeUnit.MILLISECONDS)); @@ -299,7 +299,7 @@ public class WebSocketCloseTest extends WebSocketTester @Test public void clientAborts_ICLOSED() throws Exception { - setup(State.ICLOSED); + setup(State.ISHUT); client.close(); assertFalse(server.handler.closed.await(250, TimeUnit.MILLISECONDS)); @@ -330,7 +330,7 @@ public class WebSocketCloseTest extends WebSocketTester @Test public void onFrameThrows_OCLOSED() throws Exception { - setup(State.OCLOSED); + setup(State.OSHUT); client.getOutputStream().write(RawFrameBuilder.buildFrame(OpCode.BINARY, "binary", true)); @@ -478,7 +478,7 @@ public class WebSocketCloseTest extends WebSocketTester public boolean isOpen() { - return handler.getCoreSession().isOpen(); + return handler.getCoreSession().isOutputOpen(); } } } diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/client/WebSocketClientServerTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/client/WebSocketClientServerTest.java index 4e0ecedf18c..f564272c260 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/client/WebSocketClientServerTest.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/client/WebSocketClientServerTest.java @@ -18,6 +18,11 @@ package org.eclipse.jetty.websocket.core.client; +import java.net.URI; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; @@ -45,11 +50,6 @@ import org.hamcrest.Matchers; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.net.URI; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; - import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -208,7 +208,7 @@ public class WebSocketClientServerTest public boolean isOpen() { - return handler.getCoreSession().isOpen(); + return handler.getCoreSession().isOutputOpen(); } } @@ -272,7 +272,7 @@ public class WebSocketClientServerTest public boolean isOpen() { - return handler.getCoreSession().isOpen(); + return handler.getCoreSession().isOutputOpen(); } } diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ExtensionStackTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ExtensionStackTest.java index a35121e05f2..d5276089157 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ExtensionStackTest.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ExtensionStackTest.java @@ -18,6 +18,9 @@ package org.eclipse.jetty.websocket.core.extensions; +import java.util.ArrayList; +import java.util.List; + import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.io.MappedByteBufferPool; import org.eclipse.jetty.util.DecoratedObjectFactory; @@ -35,9 +38,6 @@ import org.eclipse.jetty.websocket.core.internal.IdentityExtension; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; -import java.util.ArrayList; -import java.util.List; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.junit.jupiter.api.Assertions.assertEquals; @@ -80,7 +80,7 @@ public class ExtensionStackTest // Setup Listeners IncomingFrames session = new IncomingFramesCapture(); OutgoingFrames connection = new OutgoingFramesCapture(); - stack.connect(session, connection, null); + stack.initialize(session, connection, null); // Dump LOG.debug("{}", stack.dump()); @@ -104,7 +104,7 @@ public class ExtensionStackTest // Setup Listeners IncomingFrames session = new IncomingFramesCapture(); OutgoingFrames connection = new OutgoingFramesCapture(); - stack.connect(session, connection, null); + stack.initialize(session, connection, null); // Dump LOG.debug("{}", stack.dump()); diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ValidationExtensionTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ValidationExtensionTest.java index 62c3463a862..901982f5209 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ValidationExtensionTest.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/extensions/ValidationExtensionTest.java @@ -18,6 +18,10 @@ package org.eclipse.jetty.websocket.core.extensions; +import java.net.Socket; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; + import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.NetworkConnector; import org.eclipse.jetty.server.Server; @@ -44,10 +48,6 @@ import org.eclipse.jetty.websocket.core.server.WebSocketUpgradeHandler; import org.eclipse.jetty.websocket.core.server.internal.RFC6455Handshaker; import org.junit.jupiter.api.Test; -import java.net.Socket; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; - import static org.eclipse.jetty.util.Callback.NOOP; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; @@ -218,7 +218,7 @@ public class ValidationExtensionTest extends WebSocketTester public boolean isOpen() { - return handler.getCoreSession().isOpen(); + return handler.getCoreSession().isOutputOpen(); } } } diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/server/WebSocketServerTest.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/server/WebSocketServerTest.java index 86f8f0c7142..b48d4be1dcf 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/server/WebSocketServerTest.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/core/server/WebSocketServerTest.java @@ -18,6 +18,12 @@ package org.eclipse.jetty.websocket.core.server; +import java.net.Socket; +import java.nio.ByteBuffer; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + import org.eclipse.jetty.io.ByteBufferPool; import org.eclipse.jetty.server.HttpConnectionFactory; import org.eclipse.jetty.server.NetworkConnector; @@ -46,12 +52,6 @@ import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.jupiter.api.Test; -import java.net.Socket; -import java.nio.ByteBuffer; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; @@ -547,7 +547,7 @@ public class WebSocketServerTest extends WebSocketTester public boolean isOpen() { - return handler.getCoreSession().isOpen(); + return handler.getCoreSession().isOutputOpen(); } } } diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketMapping.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketMapping.java index fc49b0fa6b2..b16505505cb 100644 --- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketMapping.java +++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketMapping.java @@ -18,12 +18,8 @@ package org.eclipse.jetty.websocket.servlet; -import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE; - import java.io.IOException; import java.net.URISyntaxException; -import java.util.HashSet; -import java.util.Set; import java.util.function.Consumer; import javax.servlet.ServletContext; @@ -52,6 +48,8 @@ import org.eclipse.jetty.websocket.core.server.Handshaker; import org.eclipse.jetty.websocket.core.server.Negotiation; import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator; +import static javax.servlet.http.HttpServletResponse.SC_SERVICE_UNAVAILABLE; + /** * Mapping of pathSpec to a tupple of {@link WebSocketCreator}, {@link FrameHandlerFactory} and * {@link org.eclipse.jetty.websocket.core.FrameHandler.Customizer}. @@ -84,7 +82,6 @@ public class WebSocketMapping implements Dumpable, LifeCycle.Listener } private final PathMappings mappings = new PathMappings<>(); - private final Set frameHandlerFactories = new HashSet<>(); private final Handshaker handshaker = Handshaker.newInstance(); private DecoratedObjectFactory objectFactory; @@ -185,12 +182,6 @@ public class WebSocketMapping implements Dumpable, LifeCycle.Listener return this.objectFactory; } - public void addFrameHandlerFactory(FrameHandlerFactory webSocketServletFrameHandlerFactory) - { - // TODO should this be done by a ServiceLoader? - this.frameHandlerFactories.add(webSocketServletFrameHandlerFactory); - } - /** * Get the matching {@link MappedResource} for the provided target. *