diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
index d53b4417dab..c6df710c723 100644
--- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletContextHandler.java
@@ -722,6 +722,16 @@ public class ServletContextHandler extends ContextHandler
_objFactory.destroy(filter);
}
+ public static ServletContextHandler getServletContextHandler(ServletContext context)
+ {
+ ContextHandler handler = getContextHandler(context);
+ if (handler == null)
+ return null;
+ if (handler instanceof ServletContextHandler)
+ return (ServletContextHandler) handler;
+ return null;
+ }
+
/* ------------------------------------------------------------ */
public static class JspPropertyGroup implements JspPropertyGroupDescriptor
{
diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ContainerInitializer.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ContainerInitializer.java
new file mode 100644
index 00000000000..648248f4401
--- /dev/null
+++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/listener/ContainerInitializer.java
@@ -0,0 +1,184 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.servlet.listener;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.function.Consumer;
+import javax.servlet.ServletContainerInitializer;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+/**
+ * Utility Methods for manual execution of {@link javax.servlet.ServletContainerInitializer} when
+ * using Embedded Jetty.
+ */
+public final class ContainerInitializer
+{
+ /**
+ * Utility Method to allow for manual execution of {@link javax.servlet.ServletContainerInitializer} when
+ * using Embedded Jetty.
+ *
+ *
+ * ServletContextHandler context = new ServletContextHandler();
+ * ServletContainerInitializer corpSci = new MyCorporateSCI();
+ * context.addEventListener(ContainerInitializer.asContextListener(corpSci));
+ *
+ *
+ *
+ * The {@link ServletContainerInitializer} will have its {@link ServletContainerInitializer#onStartup(Set, ServletContext)}
+ * method called with the manually configured list of {@code Set> c} set.
+ * In other words, this usage does not perform bytecode or annotation scanning against the classes in
+ * your {@code ServletContextHandler} or {@code WebAppContext}.
+ *
+ *
+ * @param sci the {@link ServletContainerInitializer} to call
+ * @return the {@link ServletContextListener} wrapping the SCI
+ * @see ServletContainerInitializerServletContextListener#addClasses(Class[])
+ * @see ServletContainerInitializerServletContextListener#addClasses(String...)
+ */
+ public static ServletContainerInitializerServletContextListener asContextListener(ServletContainerInitializer sci)
+ {
+ return new ServletContainerInitializerServletContextListener(sci);
+ }
+
+ public static class ServletContainerInitializerServletContextListener implements ServletContextListener
+ {
+ private final ServletContainerInitializer sci;
+ private Set classNames;
+ private Set> classes = new HashSet<>();
+ private Consumer afterStartupConsumer;
+
+ public ServletContainerInitializerServletContextListener(ServletContainerInitializer sci)
+ {
+ this.sci = sci;
+ }
+
+ /**
+ * Add classes to be passed to the {@link ServletContainerInitializer#onStartup(Set, ServletContext)} call.
+ *
+ * Note that these classes will be loaded using the context classloader for the ServletContext
+ * initialization phase.
+ *
+ *
+ * @param classNames the class names to load and pass into the {@link ServletContainerInitializer#onStartup(Set, ServletContext)} call
+ * @return this configured {@link ServletContainerInitializerServletContextListener} instance.
+ */
+ public ServletContainerInitializerServletContextListener addClasses(String... classNames)
+ {
+ if (this.classNames == null)
+ {
+ this.classNames = new HashSet<>();
+ }
+ this.classNames.addAll(Arrays.asList(classNames));
+ return this;
+ }
+
+ /**
+ * Add classes to be passed to the {@link ServletContainerInitializer#onStartup(Set, ServletContext)} call.
+ *
+ * Note that these classes will exist on the classloader that was used to call this method.
+ * If you want the classes to be loaded using the context classloader for the ServletContext
+ * then use the String form of the classes via the {@link #addClasses(String...)} method.
+ *
+ *
+ * @param classes the classes to pass into the {@link ServletContainerInitializer#onStartup(Set, ServletContext)} call
+ * @return this configured {@link ServletContainerInitializerServletContextListener} instance.
+ */
+ public ServletContainerInitializerServletContextListener addClasses(Class>... classes)
+ {
+ this.classes.addAll(Arrays.asList(classes));
+ return this;
+ }
+
+ /**
+ * Add a optional consumer to execute once the {@link ServletContainerInitializer#onStartup(Set, ServletContext)} has
+ * been called successfully.
+ *
+ * This would be for actions to perform on a ServletContext once this specific SCI has completed
+ * its execution. Actions that would require specific configurations that the SCI provides to be present on the
+ * ServletContext to function properly.
+ *
+ *
+ * This consumer is typically used for Embedded Jetty users to configure Jetty for their specific needs.
+ *
+ *
+ * @param consumer the consumer to execute after the SCI has executed
+ * @return this configured {@link ServletContainerInitializerServletContextListener} instance.
+ */
+ public ServletContainerInitializerServletContextListener afterStartup(Consumer consumer)
+ {
+ this.afterStartupConsumer = consumer;
+ return this;
+ }
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce)
+ {
+ ServletContext servletContext = sce.getServletContext();
+ try
+ {
+ sci.onStartup(getClasses(), servletContext);
+ if (afterStartupConsumer != null)
+ {
+ afterStartupConsumer.accept(servletContext);
+ }
+ }
+ catch (RuntimeException rte)
+ {
+ throw rte;
+ }
+ catch (Throwable cause)
+ {
+ throw new RuntimeException(cause);
+ }
+ }
+
+ public Set> getClasses()
+ {
+ if (classNames != null && !classNames.isEmpty())
+ {
+ ClassLoader cl = Thread.currentThread().getContextClassLoader();
+
+ for (String className : classNames)
+ {
+ try
+ {
+ Class> clazz = cl.loadClass(className);
+ classes.add(clazz);
+ }
+ catch (ClassNotFoundException e)
+ {
+ throw new RuntimeException("Unable to find class: " + className, e);
+ }
+ }
+ }
+
+ return classes;
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce)
+ {
+ // ignore
+ }
+ }
+}
diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/RestartContextTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/RestartContextTest.java
new file mode 100644
index 00000000000..fd390547208
--- /dev/null
+++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/RestartContextTest.java
@@ -0,0 +1,212 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.jsr356.server;
+
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import javax.websocket.DeploymentException;
+import javax.websocket.OnMessage;
+import javax.websocket.server.ServerEndpoint;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+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.api.Session;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+import org.eclipse.jetty.websocket.api.util.WSURI;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+public class RestartContextTest
+{
+ private Server server;
+ private WebSocketClient client;
+
+ @BeforeEach
+ public void startClient() throws Exception
+ {
+ client = new WebSocketClient();
+ client.start();
+ }
+
+ @AfterEach
+ public void stopClient() throws Exception
+ {
+ client.stop();
+ }
+
+ @AfterEach
+ public void startServer() throws Exception
+ {
+ server.stop();
+ }
+
+ @Test
+ public void testStartStopStart_ServletContextListener() throws Exception
+ {
+ server = new Server();
+
+ ServerConnector connector = new ServerConnector(server);
+ connector.setPort(0);
+ server.addConnector(connector);
+
+ // Setup Context
+ ServletContextHandler context = new ServletContextHandler();
+ context.setContextPath("/");
+ WebSocketServerContainerInitializer.configure(context, null);
+ // late initialization via my own ServletContextListener
+ context.addEventListener(new AddEndpointListener());
+
+ // Setup handler tree
+ HandlerList handlers = new HandlerList();
+ handlers.addHandler(context);
+ handlers.addHandler(new DefaultHandler());
+
+ // Add handler tree to server
+ server.setHandler(handlers);
+
+ // Start server
+ server.start();
+
+ // verify functionality
+ verifyWebSocketEcho(server.getURI().resolve("/echo"));
+
+ // Stop server
+ server.stop();
+
+ // Start server (again)
+ server.start();
+
+ // verify functionality (again)
+ verifyWebSocketEcho(server.getURI().resolve("/echo"));
+ }
+
+ @Test
+ public void testStartStopStart_Configurator() throws Exception
+ {
+ server = new Server();
+
+ ServerConnector connector = new ServerConnector(server);
+ connector.setPort(0);
+ server.addConnector(connector);
+
+ // Setup Context
+ ServletContextHandler context = new ServletContextHandler();
+ context.setContextPath("/");
+ WebSocketServerContainerInitializer.configure(context, (servletContext, serverContainer) -> {
+ // Add endpoint via configurator
+ serverContainer.addEndpoint(EchoEndpoint.class);
+ });
+
+ // Setup handler tree
+ HandlerList handlers = new HandlerList();
+ handlers.addHandler(context);
+ handlers.addHandler(new DefaultHandler());
+
+ // Add handler tree to server
+ server.setHandler(handlers);
+
+ // Start server
+ server.start();
+
+ // verify functionality
+ verifyWebSocketEcho(server.getURI().resolve("/echo"));
+
+ // Stop server
+ server.stop();
+
+ // Start server (again)
+ server.start();
+
+ // verify functionality (again)
+ verifyWebSocketEcho(server.getURI().resolve("/echo"));
+ }
+
+ private void verifyWebSocketEcho(URI endpointUri) throws URISyntaxException, IOException, ExecutionException, InterruptedException
+ {
+ ClientEndpoint endpoint = new ClientEndpoint();
+ Future fut = client.connect(endpoint, WSURI.toWebsocket(endpointUri));
+ try(Session session = fut.get())
+ {
+ session.getRemote().sendString("Test Echo");
+ String msg = endpoint.messages.poll(5, TimeUnit.SECONDS);
+ assertThat("msg", msg, is("Test Echo"));
+ }
+ }
+
+ public static class AddEndpointListener implements ServletContextListener
+ {
+ @Override
+ public void contextInitialized(ServletContextEvent sce)
+ {
+ ServerContainer container = (ServerContainer) sce.getServletContext().getAttribute(javax.websocket.server.ServerContainer.class.getName());
+ try
+ {
+ container.addEndpoint(EchoEndpoint.class);
+ }
+ catch (DeploymentException e)
+ {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce)
+ {
+ }
+ }
+
+ @ServerEndpoint("/echo")
+ public static class EchoEndpoint
+ {
+ @OnMessage
+ public String onMessage(String msg)
+ {
+ return msg;
+ }
+ }
+
+ @WebSocket
+ public static class ClientEndpoint
+ {
+ public LinkedBlockingQueue messages = new LinkedBlockingQueue<>();
+
+ @OnWebSocketMessage
+ public void onMessage(String msg)
+ {
+ this.messages.offer(msg);
+ }
+ }
+}
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 4dd48323d34..83834672c51 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
@@ -22,11 +22,9 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Supplier;
-
import javax.servlet.ServletContext;
import javax.websocket.DeploymentException;
import javax.websocket.EndpointConfig;
-import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
@@ -51,7 +49,7 @@ import org.eclipse.jetty.websocket.servlet.WebSocketMapping;
@ManagedObject("JSR356 Server Container")
public class JavaxWebSocketServerContainer extends JavaxWebSocketClientContainer implements javax.websocket.server.ServerContainer, LifeCycle.Listener
{
- public static final String JAVAX_WEBSOCKET_CONTAINER_ATTRIBUTE = ServerContainer.class.getName();
+ public static final String JAVAX_WEBSOCKET_CONTAINER_ATTRIBUTE = javax.websocket.server.ServerContainer.class.getName();
private static final Logger LOG = Log.getLogger(JavaxWebSocketServerContainer.class);
public static JavaxWebSocketServerContainer ensureContainer(ServletContext servletContext)
diff --git a/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServletContainerInitializer.java b/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServletContainerInitializer.java
index c642f5fbf91..6342444da2f 100644
--- a/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServletContainerInitializer.java
+++ b/jetty-websocket/javax-websocket-server/src/main/java/org/eclipse/jetty/websocket/javax/server/JavaxWebSocketServletContainerInitializer.java
@@ -27,11 +27,13 @@ import javax.servlet.annotation.HandlesTypes;
import javax.websocket.DeploymentException;
import javax.websocket.Endpoint;
import javax.websocket.server.ServerApplicationConfig;
+import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.listener.ContainerInitializer;
import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
@@ -43,8 +45,13 @@ import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter;
@HandlesTypes({ ServerApplicationConfig.class, ServerEndpoint.class, Endpoint.class })
public class JavaxWebSocketServletContainerInitializer implements ServletContainerInitializer
{
+ /**
+ * The ServletContext attribute key name for the
+ * ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
+ */
+ public static final String ATTR_JAVAX_SERVER_CONTAINER = javax.websocket.server.ServerContainer.class.getName();
+
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(JavaxWebSocketServletContainerInitializer.class);
@@ -81,32 +88,89 @@ public class JavaxWebSocketServletContainerInitializer implements ServletContain
return null;
}
- public static JavaxWebSocketServerContainer configureContext(ServletContextHandler context)
+ public interface Configurator
{
- WebSocketComponents components = WebSocketComponents.ensureWebSocketComponents(context.getServletContext());
- FilterHolder filterHolder = WebSocketUpgradeFilter.ensureFilter(context.getServletContext());
- WebSocketMapping mapping = WebSocketMapping.ensureMapping(context.getServletContext(), WebSocketMapping.DEFAULT_KEY);
- JavaxWebSocketServerContainer container = JavaxWebSocketServerContainer.ensureContainer(context.getServletContext());
+ void accept(ServletContext servletContext, ServerContainer serverContainer) throws DeploymentException;
+ }
- if (LOG.isDebugEnabled())
- LOG.debug("configureContext {} {} {} {}", mapping, components, filterHolder, container);
+ /**
+ * Configure the {@link ServletContextHandler} to call {@link JavaxWebSocketServletContainerInitializer#onStartup(Set, ServletContext)}
+ * during the {@link ServletContext} initialization phase.
+ *
+ * @param context the context to add listener to
+ * @param configurator the lambda that is called to allow the {@link ServerContainer} to
+ * be configured during the {@link ServletContext} initialization phase
+ */
+ public static void configure(ServletContextHandler context, Configurator configurator)
+ {
+ // In this embedded-jetty usage, allow ServletContext.addListener() to
+ // add other ServletContextListeners (such as the ContextDestroyListener) after
+ // the initialization phase is over. (important for this SCI to function)
+ context.getServletContext().setExtendedListenerTypes(true);
- return container;
+ context.addEventListener(ContainerInitializer.asContextListener(new JavaxWebSocketServletContainerInitializer())
+ .afterStartup((servletContext) ->
+ {
+ ServerContainer serverContainer = (ServerContainer)servletContext.getAttribute(ATTR_JAVAX_SERVER_CONTAINER);
+ if (configurator != null)
+ {
+ try
+ {
+ configurator.accept(servletContext, serverContainer);
+ }
+ catch (DeploymentException e)
+ {
+ throw new RuntimeException("Failed to deploy WebSocket Endpoint", e);
+ }
+ }
+ }));
+ }
+
+ /**
+ * Immediately initialize the {@link ServletContext} with the default (and empty) {@link ServerContainer}.
+ *
+ *
+ * This method is typically called from {@link #onStartup(Set, ServletContext)} itself or from
+ * another dependent {@link ServletContainerInitializer} that requires minimal setup to
+ * be performed.
+ *
+ *
+ * This method SHOULD NOT BE CALLED by users of Jetty.
+ * Use the {@link #configure(ServletContextHandler, Configurator)} method instead.
+ *
+ *
+ * There is no enablement check here, and no automatic deployment of endpoints at this point
+ * in time. It merely sets up the {@link ServletContext} so with the basics needed to start
+ * configuring for `javax.websocket.server` based endpoints.
+ *
+ *
+ * @param context the context to work with
+ * @return the default {@link ServerContainer} for this context
+ */
+ public static JavaxWebSocketServerContainer initialize(ServletContextHandler context)
+ {
+ JavaxWebSocketServerContainer serverContainer = (JavaxWebSocketServerContainer) context.getAttribute(ATTR_JAVAX_SERVER_CONTAINER);
+ if(serverContainer == null)
+ {
+ WebSocketComponents components = WebSocketComponents.ensureWebSocketComponents(context.getServletContext());
+ FilterHolder filterHolder = WebSocketUpgradeFilter.ensureFilter(context.getServletContext());
+ WebSocketMapping mapping = WebSocketMapping.ensureMapping(context.getServletContext(), WebSocketMapping.DEFAULT_KEY);
+ serverContainer = JavaxWebSocketServerContainer.ensureContainer(context.getServletContext());
+
+ if (LOG.isDebugEnabled())
+ LOG.debug("configureContext {} {} {} {}", mapping, components, filterHolder, serverContainer);
+ }
+ return serverContainer;
}
@Override
public void onStartup(Set> c, ServletContext context) throws ServletException
{
Boolean enableKey = isEnabledViaContext(context, ENABLE_KEY);
- Boolean deprecatedEnabledKey = isEnabledViaContext(context, DEPRECATED_ENABLE_KEY);
- if (deprecatedEnabledKey != null)
- LOG.warn("Deprecated parameter used: " + DEPRECATED_ENABLE_KEY);
boolean websocketEnabled = true;
if (enableKey != null)
websocketEnabled = enableKey;
- else if (deprecatedEnabledKey != null)
- websocketEnabled = deprecatedEnabledKey;
if (!websocketEnabled)
{
@@ -114,7 +178,8 @@ public class JavaxWebSocketServletContainerInitializer implements ServletContain
return;
}
- JavaxWebSocketServerContainer container = configureContext(ServletContextHandler.getServletContextHandler(context,"Javax WebSocket SCI"));
+ ServletContextHandler servletContextHandler = ServletContextHandler.getServletContextHandler(context,"Javax WebSocket SCI");
+ JavaxWebSocketServerContainer container = initialize(servletContextHandler);
try (ThreadClassLoaderScope scope = new ThreadClassLoaderScope(context.getClassLoader()))
{
diff --git a/jetty-websocket/javax-websocket-server/src/test/java/org/eclipse/jetty/websocket/javax/server/WebSocketServerExamplesTest.java b/jetty-websocket/javax-websocket-server/src/test/java/org/eclipse/jetty/websocket/javax/server/WebSocketServerExamplesTest.java
index ceade33a518..56438ccfb47 100644
--- a/jetty-websocket/javax-websocket-server/src/test/java/org/eclipse/jetty/websocket/javax/server/WebSocketServerExamplesTest.java
+++ b/jetty-websocket/javax-websocket-server/src/test/java/org/eclipse/jetty/websocket/javax/server/WebSocketServerExamplesTest.java
@@ -22,7 +22,6 @@ import java.net.URI;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
-
import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.ContainerProvider;
@@ -32,7 +31,6 @@ import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
-import javax.websocket.server.ServerContainer;
import org.eclipse.jetty.security.ConstraintMapping;
import org.eclipse.jetty.security.ConstraintSecurityHandler;
@@ -110,10 +108,12 @@ public class WebSocketServerExamplesTest
_context.setSecurityHandler(getSecurityHandler("user", "password", "testRealm"));
_server.setHandler(_context);
- ServerContainer serverContainer = JavaxWebSocketServletContainerInitializer.configureContext(_context);
- serverContainer.addEndpoint(MyAuthedSocket.class);
- serverContainer.addEndpoint(StreamingEchoSocket.class);
- serverContainer.addEndpoint(GetHttpSessionSocket.class);
+ JavaxWebSocketServletContainerInitializer.configure(_context, (context, container) ->
+ {
+ container.addEndpoint(MyAuthedSocket.class);
+ container.addEndpoint(StreamingEchoSocket.class);
+ container.addEndpoint(GetHttpSessionSocket.class);
+ });
_server.start();
System.setProperty("org.eclipse.jetty.websocket.port", Integer.toString(connector.getLocalPort()));
@@ -125,11 +125,12 @@ public class WebSocketServerExamplesTest
_server.stop();
}
- private static SecurityHandler getSecurityHandler(String username, String password, String realm) {
+ private static SecurityHandler getSecurityHandler(String username, String password, String realm)
+ {
HashLoginService loginService = new HashLoginService();
UserStore userStore = new UserStore();
- userStore.addUser(username, Credential.getCredential(password), new String[] {"websocket"});
+ userStore.addUser(username, Credential.getCredential(password), new String[]{"websocket"});
loginService.setUserStore(userStore);
loginService.setName(realm);
@@ -154,11 +155,11 @@ public class WebSocketServerExamplesTest
public void testMyAuthedSocket() throws Exception
{
//HttpClient is configured for BasicAuthentication with the XmlHttpClientProvider
- URI uri = URI.create("ws://localhost:"+connector.getLocalPort()+"/secured/socket");
+ URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/secured/socket");
WebSocketContainer clientContainer = ContainerProvider.getWebSocketContainer();
ClientSocket clientEndpoint = new ClientSocket();
- try(Session session = clientContainer.connectToServer(clientEndpoint, uri))
+ try (Session session = clientContainer.connectToServer(clientEndpoint, uri))
{
session.getBasicRemote().sendText("hello world");
}
@@ -171,11 +172,11 @@ public class WebSocketServerExamplesTest
@Test
public void testStreamingEchoSocket() throws Exception
{
- URI uri = URI.create("ws://localhost:"+connector.getLocalPort()+"/echo");
+ URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/echo");
WebSocketContainer clientContainer = ContainerProvider.getWebSocketContainer();
ClientSocket clientEndpoint = new ClientSocket();
- try(Session session = clientContainer.connectToServer(clientEndpoint, uri))
+ try (Session session = clientContainer.connectToServer(clientEndpoint, uri))
{
session.getBasicRemote().sendText("hello world");
}
@@ -188,11 +189,11 @@ public class WebSocketServerExamplesTest
@Test
public void testGetHttpSessionSocket() throws Exception
{
- URI uri = URI.create("ws://localhost:"+connector.getLocalPort()+"/example");
+ URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/example");
WebSocketContainer clientContainer = ContainerProvider.getWebSocketContainer();
ClientSocket clientEndpoint = new ClientSocket();
- try(Session session = clientContainer.connectToServer(clientEndpoint, uri))
+ try (Session session = clientContainer.connectToServer(clientEndpoint, uri))
{
session.getBasicRemote().sendText("hello world");
}
diff --git a/jetty-websocket/javax-websocket-server/src/test/java/org/eclipse/jetty/websocket/javax/server/browser/JsrBrowserDebugTool.java b/jetty-websocket/javax-websocket-server/src/test/java/org/eclipse/jetty/websocket/javax/server/browser/JsrBrowserDebugTool.java
index 21563f1ed7c..ccb71039385 100644
--- a/jetty-websocket/javax-websocket-server/src/test/java/org/eclipse/jetty/websocket/javax/server/browser/JsrBrowserDebugTool.java
+++ b/jetty-websocket/javax-websocket-server/src/test/java/org/eclipse/jetty/websocket/javax/server/browser/JsrBrowserDebugTool.java
@@ -19,14 +19,10 @@
package org.eclipse.jetty.websocket.javax.server.browser;
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;
@@ -37,7 +33,6 @@ import org.eclipse.jetty.servlet.ServletHolder;
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.JavaxWebSocketServletContainerInitializer;
/**
@@ -68,7 +63,6 @@ public class JsrBrowserDebugTool
JsrBrowserDebugTool tool = new JsrBrowserDebugTool();
tool.setupServer(port);
tool.server.start();
- tool.server.dumpStdErr();
LOG.info("Server available at {}", tool.server.getURI());
tool.server.join();
}
@@ -80,13 +74,10 @@ public class JsrBrowserDebugTool
private Server server;
- private JavaxWebSocketServerContainer setupServer(int port)
- throws DeploymentException, ServletException, URISyntaxException, MalformedURLException, IOException
+ private void setupServer(int port) throws URISyntaxException, IOException
{
server = new Server();
- server.setDumpAfterStart(true);
-
HttpConfiguration httpConf = new HttpConfiguration();
httpConf.setSendServerVersion(true);
@@ -107,10 +98,9 @@ public class JsrBrowserDebugTool
holder.setInitParameter("dirAllowed", "true");
server.setHandler(context);
- JavaxWebSocketServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(context);
- container.addEndpoint(JsrBrowserSocket.class);
+ JavaxWebSocketServletContainerInitializer.configure(context,
+ (servletContext, container) -> container.addEndpoint(JsrBrowserSocket.class));
LOG.info("{} setup on port {}", this.getClass().getName(), port);
- return container;
}
}
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 3c14f340cda..9d0042dfb67 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
@@ -21,7 +21,6 @@ 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;
@@ -168,7 +167,7 @@ public class LocalServer extends ContainerLifeCycle implements LocalFuzzer.Provi
{
servletContextHandler = new ServletContextHandler(server, "/", true, false);
servletContextHandler.setContextPath("/");
- serverContainer = JavaxWebSocketServletContainerInitializer.configureContext(servletContextHandler);
+ JavaxWebSocketServletContainerInitializer.configure(servletContextHandler, null);
serverContainer.addSessionListener(trackingListener);
configureServletContextHandler(servletContextHandler);
return servletContextHandler;
@@ -283,6 +282,15 @@ public class LocalServer extends ContainerLifeCycle implements LocalFuzzer.Provi
public ServerContainer getServerContainer()
{
+ if (!servletContextHandler.isRunning())
+ {
+ throw new IllegalStateException("Cannot access ServerContainer when ServletContextHandler isn't running");
+ }
+
+ if (serverContainer == null)
+ {
+ serverContainer = (JavaxWebSocketServerContainer)servletContextHandler.getAttribute(JavaxWebSocketServerContainer.JAVAX_WEBSOCKET_CONTAINER_ATTRIBUTE);
+ }
return serverContainer;
}
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 8c2112de515..aa95b86edb3 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
@@ -21,16 +21,12 @@ 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.JavaxWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.javax.tests.Fuzzer;
import org.eclipse.jetty.websocket.javax.tests.LocalServer;
import org.junit.jupiter.api.AfterAll;
@@ -69,16 +65,9 @@ public class QuotesDecoderTextStreamTest
@BeforeAll
public static void startServer() throws Exception
{
- server = new LocalServer()
- {
- @Override
- protected void configureServletContextHandler(ServletContextHandler context) throws Exception
- {
- ServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(context);
- container.addEndpoint(QuotesEchoStringSocket.class);
- }
- };
+ server = new LocalServer();
server.start();
+ server.getServerContainer().addEndpoint(QuotesEchoStringSocket.class);
}
@AfterAll
diff --git a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/AbstractJavaxWebSocketServerFrameHandlerTest.java b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/AbstractJavaxWebSocketServerFrameHandlerTest.java
index f9d78ffd3e9..563f25e847b 100644
--- a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/AbstractJavaxWebSocketServerFrameHandlerTest.java
+++ b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/AbstractJavaxWebSocketServerFrameHandlerTest.java
@@ -20,7 +20,6 @@ package org.eclipse.jetty.websocket.javax.tests.server;
import java.util.HashMap;
import java.util.Map;
-
import javax.websocket.EndpointConfig;
import org.eclipse.jetty.server.Server;
@@ -45,14 +44,14 @@ public abstract class AbstractJavaxWebSocketServerFrameHandlerTest
server = new Server();
context = new ServletContextHandler();
server.setHandler(context);
- container = JavaxWebSocketServletContainerInitializer.configureContext(context);
+ JavaxWebSocketServletContainerInitializer.configure(context, null);
server.start();
+ container = (JavaxWebSocketServerContainer)context.getServletContext().getAttribute(JavaxWebSocketServerContainer.JAVAX_WEBSOCKET_CONTAINER_ATTRIBUTE);
}
@AfterAll
public static void stopContainer() throws Exception
{
- container.stop();
server.stop();
}
diff --git a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/DeploymentExceptionTest.java b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/DeploymentExceptionTest.java
index ca16d869bed..052d8f87e3f 100644
--- a/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/DeploymentExceptionTest.java
+++ b/jetty-websocket/javax-websocket-tests/src/test/java/org/eclipse/jetty/websocket/javax/tests/server/DeploymentExceptionTest.java
@@ -21,8 +21,8 @@ package org.eclipse.jetty.websocket.javax.tests.server;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
-
import javax.websocket.DeploymentException;
+import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;
import org.eclipse.jetty.server.Handler;
@@ -30,7 +30,6 @@ import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.handler.HandlerCollection;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.javax.common.util.InvalidSignatureException;
-import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServerContainer;
import org.eclipse.jetty.websocket.javax.server.JavaxWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.javax.tests.server.sockets.InvalidCloseIntSocket;
import org.eclipse.jetty.websocket.javax.tests.server.sockets.InvalidErrorErrorSocket;
@@ -98,14 +97,14 @@ public class DeploymentExceptionTest
{
ServletContextHandler context = new ServletContextHandler();
context.setServer(server);
- JavaxWebSocketServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(context);
- context.addBean(container);
+ JavaxWebSocketServletContainerInitializer.configure(context, null);
contexts.addHandler(context);
try
{
context.start();
- Exception e = assertThrows(DeploymentException.class, () -> container.addEndpoint(pojo));
+ ServerContainer serverContainer = (ServerContainer)context.getServletContext().getAttribute(ServerContainer.class.getName());
+ Exception e = assertThrows(DeploymentException.class, () -> serverContainer.addEndpoint(pojo));
assertThat(e.getCause(), instanceOf(InvalidSignatureException.class));
}
finally
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 b47cb19030b..cfe1ff2c2df 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
@@ -24,14 +24,12 @@ 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;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;
-import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpointConfig;
import org.eclipse.jetty.server.Server;
@@ -79,10 +77,12 @@ public class MemoryUsageTest
connector = new ServerConnector(server);
server.addConnector(connector);
- ServletContextHandler context = new ServletContextHandler(server, "/", true, false);
- ServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(context);
- ServerEndpointConfig config = ServerEndpointConfig.Builder.create(BasicEndpoint.class, "/").build();
- container.addEndpoint(config);
+ ServletContextHandler contextHandler = new ServletContextHandler(server, "/", true, false);
+ JavaxWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
+ {
+ ServerEndpointConfig config = ServerEndpointConfig.Builder.create(BasicEndpoint.class, "/").build();
+ container.addEndpoint(config);
+ });
server.start();
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 b0aaa08d5ad..68f1b1fb8c9 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
@@ -21,21 +21,17 @@ package org.eclipse.jetty.websocket.javax.tests.server;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
-
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
-import javax.websocket.server.ServerContainer;
import javax.websocket.server.ServerEndpoint;
-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;
@@ -111,17 +107,10 @@ public class PartialEchoTest
@BeforeAll
public static void startServer() throws Exception
{
- server = new LocalServer()
- {
- @Override
- protected void configureServletContextHandler(ServletContextHandler context) throws Exception
- {
- ServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(context);
- container.addEndpoint(PartialTextSocket.class);
- container.addEndpoint(PartialTextSessionSocket.class);
- }
- };
+ server = new LocalServer();
server.start();
+ server.getServerContainer().addEndpoint(PartialTextSocket.class);
+ server.getServerContainer().addEndpoint(PartialTextSessionSocket.class);
}
@AfterAll
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 ae2345ce60c..13cb8c0201c 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
@@ -26,7 +26,6 @@ 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;
import javax.servlet.http.HttpServletRequest;
@@ -141,6 +140,12 @@ public class WebSocketServerContainerExecutorTest
}
}
+ private Executor getJavaxServerContainerExecutor(ServletContextHandler servletContextHandler)
+ {
+ JavaxWebSocketServerContainer serverContainer = (JavaxWebSocketServerContainer)servletContextHandler.getServletContext().getAttribute(JavaxWebSocketServerContainer.JAVAX_WEBSOCKET_CONTAINER_ATTRIBUTE);
+ return serverContainer.getExecutor();
+ }
+
@Test
public void testClientExecutor() throws Exception
{
@@ -160,15 +165,16 @@ public class WebSocketServerContainerExecutorTest
// Using JSR356 Server Techniques to connectToServer()
contextHandler.addServlet(ServerConnectServlet.class, "/connect");
- javax.websocket.server.ServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(contextHandler);
- container.addEndpoint(EchoSocket.class);
+ JavaxWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
+ container.addEndpoint(EchoSocket.class));
+
try
{
server.start();
String response = GET(server.getURI().resolve("/connect"));
assertThat("Response", response, startsWith("Connected to ws://"));
- Executor containerExecutor = ((JavaxWebSocketServerContainer)container).getExecutor();
+ Executor containerExecutor = getJavaxServerContainerExecutor(contextHandler);
assertThat(containerExecutor, sameInstance(executor));
}
finally
@@ -189,15 +195,15 @@ public class WebSocketServerContainerExecutorTest
// Using JSR356 Server Techniques to connectToServer()
contextHandler.addServlet(ServerConnectServlet.class, "/connect");
- javax.websocket.server.ServerContainer container = JavaxWebSocketServletContainerInitializer.configureContext(contextHandler);
- container.addEndpoint(EchoSocket.class);
+ JavaxWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
+ container.addEndpoint(EchoSocket.class));
try
{
server.start();
String response = GET(server.getURI().resolve("/connect"));
assertThat("Response", response, startsWith("Connected to ws://"));
- Executor containerExecutor = ((JavaxWebSocketServerContainer)container).getExecutor();
+ Executor containerExecutor = getJavaxServerContainerExecutor(contextHandler);
assertThat(containerExecutor, sameInstance(executor));
}
finally
diff --git a/jetty-websocket/jetty-websocket-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java b/jetty-websocket/jetty-websocket-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java
index fe80f1978ee..6d406dc8d7c 100644
--- a/jetty-websocket/jetty-websocket-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java
+++ b/jetty-websocket/jetty-websocket-api/src/main/java/org/eclipse/jetty/websocket/api/RemoteEndpoint.java
@@ -238,6 +238,7 @@ public interface RemoteEndpoint
* Get the InetSocketAddress for the established connection.
*
* @return the InetSocketAddress for the established connection. (or null, if there is none)
+ * @deprecated use {@link #getRemoteAddress()} instead
*/
@Deprecated
InetSocketAddress getInetSocketAddress();
diff --git a/jetty-websocket/jetty-websocket-server/src/main/java/org/eclipse/jetty/websocket/server/config/JettyWebSocketServletContainerInitializer.java b/jetty-websocket/jetty-websocket-server/src/main/java/org/eclipse/jetty/websocket/server/config/JettyWebSocketServletContainerInitializer.java
index 6bd66a3e817..98409e3af4e 100644
--- a/jetty-websocket/jetty-websocket-server/src/main/java/org/eclipse/jetty/websocket/server/config/JettyWebSocketServletContainerInitializer.java
+++ b/jetty-websocket/jetty-websocket-server/src/main/java/org/eclipse/jetty/websocket/server/config/JettyWebSocketServletContainerInitializer.java
@@ -19,13 +19,12 @@
package org.eclipse.jetty.websocket.server.config;
import java.util.Set;
-
import javax.servlet.ServletContainerInitializer;
import javax.servlet.ServletContext;
-import javax.servlet.ServletException;
import org.eclipse.jetty.servlet.FilterHolder;
import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.listener.ContainerInitializer;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.core.WebSocketComponents;
@@ -40,7 +39,56 @@ public class JettyWebSocketServletContainerInitializer implements ServletContain
{
private static final Logger LOG = Log.getLogger(JettyWebSocketServletContainerInitializer.class);
- public static JettyWebSocketServerContainer configureContext(ServletContextHandler context)
+ public interface Configurator
+ {
+ void accept(ServletContext servletContext, JettyWebSocketServerContainer container);
+ }
+
+ /**
+ * Configure the {@link ServletContextHandler} to call the {@link JettyWebSocketServletContainerInitializer}
+ * during the {@link ServletContext} initialization phase.
+ *
+ * @param context the context to add listener to.
+ * @param configurator a lambda that is called to allow the {@link WebSocketMapping} to
+ * be configured during {@link ServletContext} initialization phase
+ */
+ public static void configure(ServletContextHandler context, Configurator configurator)
+ {
+ context.addEventListener(
+ ContainerInitializer
+ .asContextListener(new JettyWebSocketServletContainerInitializer())
+ .afterStartup((servletContext) ->
+ {
+ if (configurator != null)
+ {
+ JettyWebSocketServerContainer container = (JettyWebSocketServerContainer)servletContext
+ .getAttribute(JettyWebSocketServerContainer.JETTY_WEBSOCKET_CONTAINER_ATTRIBUTE);
+ configurator.accept(servletContext, container);
+ }
+ }));
+ }
+
+ /**
+ * Immediately initialize the {@link ServletContextHandler} with the default {@link JettyWebSocketServerContainer}.
+ *
+ *
+ * This method is typically called from {@link #onStartup(Set, ServletContext)} itself or from
+ * another dependent {@link ServletContainerInitializer} that requires minimal setup to
+ * be performed.
+ *
+ *
+ * This method SHOULD NOT BE CALLED by users of Jetty.
+ * Use the {@link #configure(ServletContextHandler, Configurator)} method instead.
+ *
+ *
+ * This will return the default {@link JettyWebSocketServerContainer} if already initialized,
+ * and not create a new {@link JettyWebSocketServerContainer} each time it is called.
+ *
+ *
+ * @param context the context to work with
+ * @return the default {@link JettyWebSocketServerContainer}
+ */
+ public static JettyWebSocketServerContainer initialize(ServletContextHandler context)
{
WebSocketComponents components = WebSocketComponents.ensureWebSocketComponents(context.getServletContext());
FilterHolder filterHolder = WebSocketUpgradeFilter.ensureFilter(context.getServletContext());
@@ -54,9 +102,9 @@ public class JettyWebSocketServletContainerInitializer implements ServletContain
}
@Override
- public void onStartup(Set> c, ServletContext context) throws ServletException
+ public void onStartup(Set> c, ServletContext context)
{
- ServletContextHandler contextHandler = ServletContextHandler.getServletContextHandler(context,"Jetty WebSocket SCI");
- JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
+ ServletContextHandler contextHandler = ServletContextHandler.getServletContextHandler(context, "Jetty WebSocket SCI");
+ JettyWebSocketServletContainerInitializer.initialize(contextHandler);
}
}
diff --git a/jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java b/jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java
index 4bbb232a59a..472649a476f 100644
--- a/jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java
+++ b/jetty-websocket/jetty-websocket-server/src/test/java/org/eclipse/jetty/websocket/server/browser/BrowserDebugTool.java
@@ -19,12 +19,10 @@
package org.eclipse.jetty.websocket.server.browser;
import java.io.IOException;
-import java.net.URISyntaxException;
import java.nio.file.Path;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
-
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -92,7 +90,8 @@ public class BrowserDebugTool
return connector.getLocalPort();
}
- public void prepare(int port) throws IOException, URISyntaxException {
+ public void prepare(int port)
+ {
server = new Server();
connector = new ServerConnector(server);
connector.setPort(port);
@@ -100,7 +99,7 @@ public class BrowserDebugTool
ServletContextHandler context = new ServletContextHandler();
- JettyWebSocketServletContainerInitializer.configureContext(context);
+ JettyWebSocketServletContainerInitializer.configure(context, null);
context.setContextPath("/");
Resource staticResourceBase = findStaticResources();
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/AnnoMaxMessageEndpoint.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/AnnoMaxMessageEndpoint.java
new file mode 100644
index 00000000000..ad6d06b37d3
--- /dev/null
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/AnnoMaxMessageEndpoint.java
@@ -0,0 +1,36 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+
+@SuppressWarnings("unused")
+@WebSocket(maxTextMessageSize = 100*1024)
+public class AnnoMaxMessageEndpoint
+{
+ @OnWebSocketMessage
+ public void onMessage(Session session, String msg) throws IOException
+ {
+ session.getRemote().sendString(msg);
+ }
+}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/ConnectMessageEndpoint.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/ConnectMessageEndpoint.java
new file mode 100644
index 00000000000..d9ff09a4645
--- /dev/null
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/ConnectMessageEndpoint.java
@@ -0,0 +1,36 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+
+@SuppressWarnings("unused")
+@WebSocket
+public class ConnectMessageEndpoint
+{
+ @OnWebSocketConnect
+ public void onConnect(Session session) throws IOException
+ {
+ session.getRemote().sendString("Greeting from onConnect");
+ }
+}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/EchoSocket.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/EchoSocket.java
index 602951959be..eac981f385b 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/EchoSocket.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/EchoSocket.java
@@ -24,6 +24,7 @@ import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.WriteCallback;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+@SuppressWarnings("unused")
@WebSocket
public class EchoSocket extends EventSocket
{
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/GetAuthHeaderEndpoint.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/GetAuthHeaderEndpoint.java
new file mode 100644
index 00000000000..8e6130841cf
--- /dev/null
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/GetAuthHeaderEndpoint.java
@@ -0,0 +1,38 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests;
+
+import java.io.IOException;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+
+@SuppressWarnings("unused")
+@WebSocket
+public class GetAuthHeaderEndpoint
+{
+ @OnWebSocketConnect
+ public void onConnect(Session session) throws IOException
+ {
+ String authHeaderName = "Authorization";
+ String authHeaderValue = session.getUpgradeRequest().getHeader(authHeaderName);
+ session.getRemote().sendString("Header[" + authHeaderName + "]=" + authHeaderValue);
+ }
+}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketExtensionConfigTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketExtensionConfigTest.java
index cf53f289c4d..d981760f999 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketExtensionConfigTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketExtensionConfigTest.java
@@ -36,7 +36,6 @@ import org.eclipse.jetty.websocket.api.extensions.ExtensionConfig;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.JettyUpgradeListener;
import org.eclipse.jetty.websocket.client.WebSocketClient;
-import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -66,25 +65,26 @@ public class JettyWebSocketExtensionConfigTest
contextHandler.setContextPath("/");
server.setHandler(contextHandler);
- JettyWebSocketServerContainer container = JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
- container.addMapping("/", (req, resp)->
- {
- assertEquals(req.getExtensions().stream().filter(e -> e.getName().equals("permessage-deflate")).count(), 1);
- assertEquals(resp.getExtensions().stream().filter(e -> e.getName().equals("permessage-deflate")).count(), 1);
+ JettyWebSocketServletContainerInitializer.configure(contextHandler,
+ (context, container) -> container.addMapping("/", (req, resp) ->
+ {
+ assertEquals(req.getExtensions().stream().filter(e -> e.getName().equals("permessage-deflate")).count(), 1);
+ assertEquals(resp.getExtensions().stream().filter(e -> e.getName().equals("permessage-deflate")).count(), 1);
- ExtensionConfig nonRequestedExtension = ExtensionConfig.parse("identity");
- assertNotNull(nonRequestedExtension);
+ ExtensionConfig nonRequestedExtension = ExtensionConfig.parse("identity");
+ assertNotNull(nonRequestedExtension);
- assertThrows(IllegalArgumentException.class,
- ()->resp.setExtensions(List.of(nonRequestedExtension)),
+ assertThrows(IllegalArgumentException.class,
+ () -> resp.setExtensions(List.of(nonRequestedExtension)),
"should not allow extensions not requested");
- // Check identity extension was not added because it was not requested
- assertEquals(resp.getExtensions().stream().filter(config -> config.getName().equals("identity")).count(), 0);
- assertEquals(resp.getExtensions().stream().filter(e -> e.getName().equals("permessage-deflate")).count(), 1);
+ // Check identity extension was not added because it was not requested
+ assertEquals(resp.getExtensions().stream().filter(config -> config.getName().equals("identity")).count(), 0);
+ assertEquals(resp.getExtensions().stream().filter(e -> e.getName().equals("permessage-deflate")).count(), 1);
+
+ return new EchoSocket();
+ }));
- return new EchoSocket();
- });
server.start();
client = new WebSocketClient();
@@ -101,7 +101,7 @@ public class JettyWebSocketExtensionConfigTest
@Test
public void testJettyExtensionConfig() throws Exception
{
- URI uri = URI.create("ws://localhost:"+connector.getLocalPort()+"/filterPath");
+ URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/filterPath");
EventSocket socket = new EventSocket();
UpgradeRequest request = new ClientUpgradeRequest();
@@ -115,7 +115,7 @@ public class JettyWebSocketExtensionConfigTest
{
String extensions = response.getHeaders().get(HttpHeader.SEC_WEBSOCKET_EXTENSIONS);
- if("permessage-deflate".equals(extensions))
+ if ("permessage-deflate".equals(extensions))
correctResponseExtensions.countDown();
else
throw new IllegalStateException("Unexpected Negotiated Extensions: " + extensions);
@@ -123,7 +123,7 @@ public class JettyWebSocketExtensionConfigTest
};
CompletableFuture connect = client.connect(socket, uri, request, listener);
- try(Session session = connect.get(5, TimeUnit.SECONDS))
+ try (Session session = connect.get(5, TimeUnit.SECONDS))
{
session.getRemote().sendString("hello world");
}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketFilterTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketFilterTest.java
index e90163f9211..52e1110b2c3 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketFilterTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketFilterTest.java
@@ -27,7 +27,6 @@ import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient;
-import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -54,8 +53,8 @@ public class JettyWebSocketFilterTest
contextHandler.setContextPath("/");
server.setHandler(contextHandler);
- JettyWebSocketServerContainer container = JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
- container.addMapping("/", (req, resp)->new EchoSocket());
+ JettyWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
+ container.addMapping("/", (req, resp) -> new EchoSocket()));
server.start();
client = new WebSocketClient();
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketNegotiationTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketNegotiationTest.java
index 827c0b32573..6222679ace6 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketNegotiationTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketNegotiationTest.java
@@ -32,7 +32,6 @@ import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
import org.eclipse.jetty.websocket.client.WebSocketClient;
-import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
@@ -75,8 +74,9 @@ public class JettyWebSocketNegotiationTest
@Test
public void testBadRequest() throws Exception
{
- JettyWebSocketServerContainer container = JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
- container.addMapping("/", (req, resp)->new EchoSocket());
+ JettyWebSocketServletContainerInitializer.configure(contextHandler,
+ (context, container) -> container.addMapping("/", (req, resp)->new EchoSocket()));
+
URI uri = URI.create("ws://localhost:"+connector.getLocalPort()+"/filterPath");
EventSocket socket = new EventSocket();
@@ -93,12 +93,12 @@ public class JettyWebSocketNegotiationTest
@Test
public void testServerError() throws Exception
{
- JettyWebSocketServerContainer container = JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
- container.addMapping("/", (req, resp)->
- {
- resp.setAcceptedSubProtocol("errorSubProtocol");
- return new EchoSocket();
- });
+ JettyWebSocketServletContainerInitializer.configure(contextHandler, (context, container) ->
+ container.addMapping("/", (req, resp) ->
+ {
+ resp.setAcceptedSubProtocol("errorSubProtocol");
+ return new EchoSocket();
+ }));
URI uri = URI.create("ws://localhost:"+connector.getLocalPort()+"/filterPath");
EventSocket socket = new EventSocket();
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketServletTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketServletTest.java
index fcd25934e6d..746ec976ae8 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketServletTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/JettyWebSocketServletTest.java
@@ -66,7 +66,7 @@ public class JettyWebSocketServletTest
contextHandler.addServlet(MyWebSocketServlet.class, "/servletPath");
- JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
+ JettyWebSocketServletContainerInitializer.configure(contextHandler, null);
server.start();
client = new WebSocketClient();
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/ParamsEndpoint.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/ParamsEndpoint.java
new file mode 100644
index 00000000000..bd4470465f1
--- /dev/null
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/ParamsEndpoint.java
@@ -0,0 +1,49 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
+import org.eclipse.jetty.websocket.api.annotations.WebSocket;
+
+@SuppressWarnings("unused")
+@WebSocket
+public class ParamsEndpoint
+{
+ @OnWebSocketConnect
+ public void onConnect(Session session) throws IOException
+ {
+ Map> params = session.getUpgradeRequest().getParameterMap();
+ StringBuilder msg = new StringBuilder();
+
+ for (String key : params.keySet())
+ {
+ msg.append("Params[").append(key).append("]=");
+ msg.append(params.get(key).stream().collect(Collectors.joining(", ", "[", "]")));
+ msg.append("\n");
+ }
+
+ session.getRemote().sendString(msg.toString());
+ }
+}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/SimpleStatusServlet.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/SimpleStatusServlet.java
new file mode 100644
index 00000000000..300439a10d4
--- /dev/null
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/SimpleStatusServlet.java
@@ -0,0 +1,41 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests;
+
+import java.io.IOException;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class SimpleStatusServlet extends HttpServlet
+{
+ private final int statusCode;
+
+ public SimpleStatusServlet(int statusCode)
+ {
+ this.statusCode = statusCode;
+ }
+
+ @Override
+ protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
+ {
+ resp.setStatus(this.statusCode);
+ }
+}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java
index 8ce13faaa43..54d454ca315 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/SuspendResumeTest.java
@@ -85,7 +85,7 @@ public class SuspendResumeTest
server.setHandler(contextHandler);
contextHandler.addServlet(new ServletHolder(new UpgradeServlet()), "/suspend");
- JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
+ JettyWebSocketServletContainerInitializer.configure(contextHandler, null);
server.start();
client.start();
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketServletExamplesTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketServletExamplesTest.java
index b3c897272eb..0990c087a25 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketServletExamplesTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketServletExamplesTest.java
@@ -73,7 +73,7 @@ public class WebSocketServletExamplesTest
_context.addServlet(MyAdvancedEchoServlet.class, "/advancedEcho");
_context.addServlet(MyAuthedServlet.class, "/authed");
- JettyWebSocketServletContainerInitializer.configureContext(_context);
+ JettyWebSocketServletContainerInitializer.configure(_context, null);
_server.start();
}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStatsTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStatsTest.java
index 0c527954a11..28aab5dfaf1 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStatsTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/WebSocketStatsTest.java
@@ -34,7 +34,6 @@ import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.websocket.api.Session;
-import org.eclipse.jetty.websocket.api.StatusCode;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.core.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
@@ -97,7 +96,7 @@ public class WebSocketStatsTest
contextHandler.addServlet(MyWebSocketServlet.class, "/testPath");
server.setHandler(contextHandler);
- JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
+ JettyWebSocketServletContainerInitializer.configure(contextHandler, null);
client = new WebSocketClient();
server.start();
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/BadNetworkTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/BadNetworkTest.java
index 1aba249b7b6..11f7cb431b3 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/BadNetworkTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/BadNetworkTest.java
@@ -90,7 +90,7 @@ public class BadNetworkTest
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
- JettyWebSocketServletContainerInitializer.configureContext(context);
+ JettyWebSocketServletContainerInitializer.configure(context, null);
server.start();
}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientCloseTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientCloseTest.java
index eba846f319d..d52ec3e75cc 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientCloseTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientCloseTest.java
@@ -143,7 +143,7 @@ public class ClientCloseTest
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
- JettyWebSocketServletContainerInitializer.configureContext(context);
+ JettyWebSocketServletContainerInitializer.configure(context, null);
server.start();
}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConfigTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConfigTest.java
index 1f45f214211..eac483fc5b5 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConfigTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConfigTest.java
@@ -38,7 +38,6 @@ import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.core.internal.WebSocketConnection;
import org.eclipse.jetty.websocket.core.internal.WebSocketCoreSession;
-import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.tests.EchoSocket;
import org.eclipse.jetty.websocket.tests.EventSocket;
@@ -83,8 +82,9 @@ public class ClientConfigTest
contextHandler.setContextPath("/");
server.setHandler(contextHandler);
- JettyWebSocketServerContainer container = JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
- container.addMapping("/", (req, resp)->serverSocket);
+ JettyWebSocketServletContainerInitializer.configure(contextHandler,
+ (context, container) -> container.addMapping("/", (req, resp) -> serverSocket));
+
server.start();
client = new WebSocketClient();
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConnectTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConnectTest.java
new file mode 100644
index 00000000000..514c043152b
--- /dev/null
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientConnectTest.java
@@ -0,0 +1,399 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests.client;
+
+import java.net.ConnectException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.ServerSocket;
+import java.net.SocketTimeoutException;
+import java.net.URI;
+import java.time.Duration;
+import java.util.EnumSet;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
+import javax.servlet.DispatcherType;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.servlet.ServletHolder;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.UpgradeException;
+import org.eclipse.jetty.websocket.api.util.WSURI;
+import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
+import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter;
+import org.eclipse.jetty.websocket.tests.CloseTrackingEndpoint;
+import org.eclipse.jetty.websocket.tests.EchoSocket;
+import org.eclipse.jetty.websocket.tests.GetAuthHeaderEndpoint;
+import org.eclipse.jetty.websocket.tests.SimpleStatusServlet;
+import org.hamcrest.Matcher;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.anyOf;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.instanceOf;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.fail;
+
+/**
+ * Various connect condition testing
+ */
+@SuppressWarnings("Duplicates")
+public class ClientConnectTest
+{
+ private Server server;
+ private WebSocketClient client;
+
+ @SuppressWarnings("unchecked")
+ private E assertExpectedError(ExecutionException e, CloseTrackingEndpoint wsocket, Matcher errorMatcher)
+ {
+ // Validate thrown cause
+ Throwable cause = e.getCause();
+
+ assertThat("ExecutionException.cause", cause, errorMatcher);
+
+ // Validate websocket captured cause
+ Throwable capcause = wsocket.error.get();
+ assertThat("Error", capcause, notNullValue());
+ assertThat("Error", capcause, errorMatcher);
+
+ // Validate that websocket didn't see an open event
+ assertThat("Open Latch", wsocket.openLatch.getCount(), is(1L));
+
+ // Return the captured cause
+ return (E)capcause;
+ }
+
+ @BeforeEach
+ public void startClient() throws Exception
+ {
+ client = new WebSocketClient();
+ client.setConnectTimeout(TimeUnit.SECONDS.toMillis(3));
+ client.setIdleTimeout(Duration.ofSeconds(10));
+ client.start();
+ }
+
+ @BeforeEach
+ public void startServer() throws Exception
+ {
+ server = new Server();
+
+ ServerConnector connector = new ServerConnector(server);
+ connector.setPort(0);
+ server.addConnector(connector);
+
+ ServletContextHandler context = new ServletContextHandler();
+ context.setContextPath("/");
+
+ JettyWebSocketServletContainerInitializer.configure(context,
+ (servletContext, configuration) ->
+ {
+ configuration.setIdleTimeout(Duration.ofSeconds(10));
+ configuration.addMapping("/echo", (req, resp) ->
+ {
+ if (req.hasSubProtocol("echo"))
+ resp.setAcceptedSubProtocol("echo");
+ return new EchoSocket();
+ });
+ configuration.addMapping("/get-auth-header", (req, resp) -> new GetAuthHeaderEndpoint());
+ });
+
+ context.addFilter(WebSocketUpgradeFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
+
+ context.addServlet(new ServletHolder(new SimpleStatusServlet(404)), "/bogus");
+ context.addServlet(new ServletHolder(new SimpleStatusServlet(200)), "/a-okay");
+ context.addServlet(new ServletHolder(new InvalidUpgradeServlet()), "/invalid-upgrade/*");
+
+ server.setHandler(context);
+
+ server.start();
+ }
+
+ @AfterEach
+ public void stopClient() throws Exception
+ {
+ client.stop();
+ }
+
+ @AfterEach
+ public void stopServer() throws Exception
+ {
+ server.stop();
+ }
+
+ @Test
+ public void testUpgradeRequest() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+ client.setIdleTimeout(Duration.ofSeconds(10));
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo"));
+ ClientUpgradeRequest request = new ClientUpgradeRequest();
+ request.setSubProtocols("echo");
+ Future future = client.connect(cliSock, wsUri);
+
+ try (Session sess = future.get(30, TimeUnit.SECONDS))
+ {
+ assertThat("Connect.UpgradeRequest", sess.getUpgradeRequest(), notNullValue());
+ assertThat("Connect.UpgradeResponse", sess.getUpgradeResponse(), notNullValue());
+ }
+ }
+
+ @Test
+ public void testUpgradeWithAuthorizationHeader() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/get-auth-header"));
+ ClientUpgradeRequest request = new ClientUpgradeRequest();
+ // actual value for this test is irrelevant, its important that this
+ // header actually be sent with a value (the value specified)
+ String authHeaderValue = "Basic YWxhZGRpbjpvcGVuc2VzYW1l";
+ request.setHeader("Authorization", authHeaderValue);
+ Future future = client.connect(cliSock, wsUri, request);
+
+ try (Session sess = future.get(5, TimeUnit.SECONDS))
+ {
+ // Test client side
+ String cliAuthValue = sess.getUpgradeRequest().getHeader("Authorization");
+ assertThat("Client Request Authorization Value", cliAuthValue, is(authHeaderValue));
+
+ // wait for response from server
+ String received = cliSock.messageQueue.poll(5, TimeUnit.SECONDS);
+ assertThat("Message", received, containsString("Header[Authorization]=" + authHeaderValue));
+ }
+ }
+
+ @Test
+ public void testBadHandshake() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/bogus"));
+ Future future = client.connect(cliSock, wsUri);
+
+ // The attempt to get upgrade response future should throw error
+ ExecutionException e = assertThrows(ExecutionException.class,
+ () -> future.get(5, TimeUnit.SECONDS));
+
+ UpgradeException ue = assertExpectedError(e, cliSock, instanceOf(UpgradeException.class));
+ assertThat("UpgradeException.requestURI", ue.getRequestURI(), notNullValue());
+ assertThat("UpgradeException.requestURI", ue.getRequestURI().toASCIIString(), is(wsUri.toASCIIString()));
+ assertThat("UpgradeException.responseStatusCode", ue.getResponseStatusCode(), is(404));
+ }
+
+ @Test
+ public void testBadHandshake_GetOK() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/a-okay"));
+ Future future = client.connect(cliSock, wsUri);
+
+ // The attempt to get upgrade response future should throw error
+ ExecutionException e = assertThrows(ExecutionException.class,
+ () -> future.get(5, TimeUnit.SECONDS));
+
+ UpgradeException ue = assertExpectedError(e, cliSock, instanceOf(UpgradeException.class));
+ assertThat("UpgradeException.requestURI", ue.getRequestURI(), notNullValue());
+ assertThat("UpgradeException.requestURI", ue.getRequestURI().toASCIIString(), is(wsUri.toASCIIString()));
+ assertThat("UpgradeException.responseStatusCode", ue.getResponseStatusCode(), is(200));
+ }
+
+ @Test
+ public void testBadHandshake_GetOK_WithSecWebSocketAccept() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/invalid-upgrade/only-accept"));
+ Future future = client.connect(cliSock, wsUri);
+
+ // The attempt to get upgrade response future should throw error
+ ExecutionException e = assertThrows(ExecutionException.class,
+ () -> future.get(5, TimeUnit.SECONDS));
+
+ UpgradeException ue = assertExpectedError(e, cliSock, instanceOf(UpgradeException.class));
+ assertThat("UpgradeException.requestURI", ue.getRequestURI(), notNullValue());
+ assertThat("UpgradeException.requestURI", ue.getRequestURI().toASCIIString(), is(wsUri.toASCIIString()));
+ assertThat("UpgradeException.responseStatusCode", ue.getResponseStatusCode(), is(200));
+ }
+
+ @Test
+ public void testBadHandshake_SwitchingProtocols_InvalidConnectionHeader() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/invalid-upgrade/close-connection"));
+ Future future = client.connect(cliSock, wsUri);
+
+ // The attempt to get upgrade response future should throw error
+ ExecutionException e = assertThrows(ExecutionException.class,
+ () -> future.get(5, TimeUnit.SECONDS));
+
+ UpgradeException ue = assertExpectedError(e, cliSock, instanceOf(UpgradeException.class));
+ assertThat("UpgradeException.requestURI", ue.getRequestURI(), notNullValue());
+ assertThat("UpgradeException.requestURI", ue.getRequestURI().toASCIIString(), is(wsUri.toASCIIString()));
+ assertThat("UpgradeException.responseStatusCode", ue.getResponseStatusCode(), is(101));
+ }
+
+ @Test
+ public void testBadHandshake_SwitchingProtocols_NoConnectionHeader() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/invalid-upgrade/missing-connection"));
+ Future future = client.connect(cliSock, wsUri);
+
+ // The attempt to get upgrade response future should throw error
+ ExecutionException e = assertThrows(ExecutionException.class,
+ () -> future.get(5, TimeUnit.SECONDS));
+
+ UpgradeException ue = assertExpectedError(e, cliSock, instanceOf(UpgradeException.class));
+ assertThat("UpgradeException.requestURI", ue.getRequestURI(), notNullValue());
+ assertThat("UpgradeException.requestURI", ue.getRequestURI().toASCIIString(), is(wsUri.toASCIIString()));
+ assertThat("UpgradeException.responseStatusCode", ue.getResponseStatusCode(), is(101));
+ }
+
+ @Test
+ public void testBadUpgrade() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/invalid-upgrade/rubbish-accept"));
+ Future future = client.connect(cliSock, wsUri);
+
+ // The attempt to get upgrade response future should throw error
+ ExecutionException e = assertThrows(ExecutionException.class,
+ () -> future.get(5, TimeUnit.SECONDS));
+
+ UpgradeException ue = assertExpectedError(e, cliSock, instanceOf(UpgradeException.class));
+ assertThat("UpgradeException.responseStatusCode", ue.getResponseStatusCode(), is(101));
+ }
+
+ @Test
+ public void testConnectionNotAccepted() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ try (ServerSocket serverSocket = new ServerSocket())
+ {
+ InetAddress addr = InetAddress.getByName("localhost");
+ InetSocketAddress endpoint = new InetSocketAddress(addr, 0);
+ serverSocket.bind(endpoint, 1);
+ int port = serverSocket.getLocalPort();
+
+ URI wsUri = URI.create(String.format("ws://%s:%d/", addr.getHostAddress(), port));
+ Future future = client.connect(cliSock, wsUri);
+
+ // Intentionally not accept incoming socket.
+ // serverSocket.accept();
+
+ try
+ {
+ future.get(8, TimeUnit.SECONDS);
+ fail("Should have Timed Out");
+ }
+ catch (ExecutionException e)
+ {
+ // Passing Path (active session wait timeout)
+ assertExpectedError(e, cliSock, instanceOf(UpgradeException.class));
+ }
+ catch (TimeoutException e)
+ {
+ // Passing Path
+ }
+ }
+ }
+
+ @Test
+ public void testConnectionRefused() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ // Intentionally bad port with nothing listening on it
+ URI wsUri = new URI("ws://127.0.0.1:1");
+
+ try
+ {
+ Future future = client.connect(cliSock, wsUri);
+
+ // The attempt to get upgrade response future should throw error
+ future.get(5, TimeUnit.SECONDS);
+ fail("Expected ExecutionException -> ConnectException");
+ }
+ catch (ConnectException e)
+ {
+ Throwable t = cliSock.error.get();
+ assertThat("Error Queue[0]", t, instanceOf(ConnectException.class));
+ }
+ catch (ExecutionException e)
+ {
+ assertExpectedError(e, cliSock,
+ anyOf(
+ instanceOf(UpgradeException.class),
+ instanceOf(SocketTimeoutException.class),
+ instanceOf(ConnectException.class)));
+ }
+ }
+
+ @Test
+ public void testConnectionTimeout_Concurrent() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ try (ServerSocket serverSocket = new ServerSocket())
+ {
+ InetAddress addr = InetAddress.getByName("localhost");
+ InetSocketAddress endpoint = new InetSocketAddress(addr, 0);
+ serverSocket.bind(endpoint, 1);
+ int port = serverSocket.getLocalPort();
+ URI wsUri = URI.create(String.format("ws://%s:%d/", addr.getHostAddress(), port));
+ Future future = client.connect(cliSock, wsUri);
+
+ // Accept the connection, but do nothing on it (no response, no upgrade, etc)
+ serverSocket.accept();
+
+ // The attempt to get upgrade response future should throw error
+ Exception e = assertThrows(Exception.class,
+ () -> future.get(5, TimeUnit.SECONDS));
+
+ if (e instanceof ExecutionException)
+ {
+ assertExpectedError((ExecutionException)e, cliSock, anyOf(
+ instanceOf(ConnectException.class),
+ instanceOf(UpgradeException.class)
+ ));
+ }
+ else
+ {
+ assertThat("Should have been a TimeoutException", e, instanceOf(TimeoutException.class));
+ }
+ }
+ }
+}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientSessionsTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientSessionsTest.java
index 87a0675e06e..bf1eae3cdd7 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientSessionsTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/ClientSessionsTest.java
@@ -83,7 +83,7 @@ public class ClientSessionsTest
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
- JettyWebSocketServletContainerInitializer.configureContext(context);
+ JettyWebSocketServletContainerInitializer.configure(context, null);
server.start();
}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/InvalidUpgradeServlet.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/InvalidUpgradeServlet.java
new file mode 100644
index 00000000000..1b4fc2d642b
--- /dev/null
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/InvalidUpgradeServlet.java
@@ -0,0 +1,68 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests.client;
+
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+import org.eclipse.jetty.http.HttpHeader;
+import org.eclipse.jetty.websocket.core.internal.WebSocketCore;
+
+public class InvalidUpgradeServlet extends HttpServlet
+{
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+ {
+ String pathInfo = req.getPathInfo();
+ if (pathInfo.contains("only-accept"))
+ {
+ // Force 200 response, no response body content, incomplete websocket response headers, no actual upgrade for this test
+ resp.setStatus(HttpServletResponse.SC_OK);
+ String key = req.getHeader(HttpHeader.SEC_WEBSOCKET_KEY.toString());
+ resp.setHeader(HttpHeader.SEC_WEBSOCKET_ACCEPT.toString(), WebSocketCore.hashKey(key));
+ }
+ else if (pathInfo.contains("close-connection"))
+ {
+ // Force 101 response, with invalid Connection header, invalid handshake
+ resp.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
+ String key = req.getHeader(HttpHeader.SEC_WEBSOCKET_KEY.toString());
+ resp.setHeader(HttpHeader.CONNECTION.toString(), "close");
+ resp.setHeader(HttpHeader.SEC_WEBSOCKET_ACCEPT.toString(), WebSocketCore.hashKey(key));
+ }
+ else if (pathInfo.contains("missing-connection"))
+ {
+ // Force 101 response, with no Connection header, invalid handshake
+ resp.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
+ String key = req.getHeader(HttpHeader.SEC_WEBSOCKET_KEY.toString());
+ // Intentionally leave out Connection header
+ resp.setHeader(HttpHeader.SEC_WEBSOCKET_ACCEPT.toString(), WebSocketCore.hashKey(key));
+ }
+ else if (pathInfo.contains("rubbish-accept"))
+ {
+ // Force 101 response, with no Connection header, invalid handshake
+ resp.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
+ resp.setHeader(HttpHeader.SEC_WEBSOCKET_ACCEPT.toString(), "rubbish");
+ }
+ else
+ {
+ resp.setStatus(500);
+ }
+ }
+}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/SlowClientTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/SlowClientTest.java
index ed7bacb39f6..8848364f852 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/SlowClientTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/SlowClientTest.java
@@ -86,7 +86,7 @@ public class SlowClientTest
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
- JettyWebSocketServletContainerInitializer.configureContext(context);
+ JettyWebSocketServletContainerInitializer.configure(context, null);
server.start();
}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/WebSocketClientTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/WebSocketClientTest.java
new file mode 100644
index 00000000000..e96b46d3ab6
--- /dev/null
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/client/WebSocketClientTest.java
@@ -0,0 +1,333 @@
+//
+// ========================================================================
+// Copyright (c) 1995-2019 Mort Bay Consulting Pty. Ltd.
+// ------------------------------------------------------------------------
+// All rights reserved. This program and the accompanying materials
+// are made available under the terms of the Eclipse Public License v1.0
+// and Apache License v2.0 which accompanies this distribution.
+//
+// The Eclipse Public License is available at
+// http://www.eclipse.org/legal/epl-v10.html
+//
+// The Apache License v2.0 is available at
+// http://www.opensource.org/licenses/apache2.0.php
+//
+// You may elect to redistribute this code under either of these licenses.
+// ========================================================================
+//
+
+package org.eclipse.jetty.websocket.tests.client;
+
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.time.Duration;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import javax.servlet.DispatcherType;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.server.ServerConnector;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.util.StringUtil;
+import org.eclipse.jetty.websocket.api.RemoteEndpoint;
+import org.eclipse.jetty.websocket.api.Session;
+import org.eclipse.jetty.websocket.api.UpgradeRequest;
+import org.eclipse.jetty.websocket.api.util.WSURI;
+import org.eclipse.jetty.websocket.client.ClientUpgradeRequest;
+import org.eclipse.jetty.websocket.client.WebSocketClient;
+import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
+import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter;
+import org.eclipse.jetty.websocket.tests.AnnoMaxMessageEndpoint;
+import org.eclipse.jetty.websocket.tests.CloseTrackingEndpoint;
+import org.eclipse.jetty.websocket.tests.ConnectMessageEndpoint;
+import org.eclipse.jetty.websocket.tests.EchoSocket;
+import org.eclipse.jetty.websocket.tests.ParamsEndpoint;
+import org.eclipse.jetty.websocket.tests.util.FutureWriteCallback;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.hamcrest.Matchers.greaterThan;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.not;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class WebSocketClientTest
+{
+ private Server server;
+ private WebSocketClient client;
+
+ @BeforeEach
+ public void startClient() throws Exception
+ {
+ client = new WebSocketClient();
+ client.start();
+ }
+
+ @BeforeEach
+ public void startServer() throws Exception
+ {
+ server = new Server();
+
+ ServerConnector connector = new ServerConnector(server);
+ connector.setPort(0);
+ server.addConnector(connector);
+
+ ServletContextHandler context = new ServletContextHandler();
+ context.setContextPath("/");
+
+ JettyWebSocketServletContainerInitializer.configure(context,
+ (servletContext, configuration) ->
+ {
+ configuration.setIdleTimeout(Duration.ofSeconds(10));
+ configuration.addMapping("/echo", (req, resp) ->
+ {
+ if (req.hasSubProtocol("echo"))
+ resp.setAcceptedSubProtocol("echo");
+ return new EchoSocket();
+ });
+ configuration.addMapping("/anno-max-message", (req, resp) -> new AnnoMaxMessageEndpoint());
+ configuration.addMapping("/connect-msg", (req, resp) -> new ConnectMessageEndpoint());
+ configuration.addMapping("/get-params", (req, resp) -> new ParamsEndpoint());
+ });
+
+ context.addFilter(WebSocketUpgradeFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
+
+ server.setHandler(context);
+
+ server.start();
+ }
+
+ @AfterEach
+ public void stopClient() throws Exception
+ {
+ client.stop();
+ }
+
+ @AfterEach
+ public void stopServer() throws Exception
+ {
+ server.stop();
+ }
+
+ @Test
+ public void testAddExtension_NotInstalled() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ client.setIdleTimeout(Duration.ofSeconds(10));
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo"));
+ ClientUpgradeRequest request = new ClientUpgradeRequest();
+ request.setSubProtocols("echo");
+ request.addExtensions("x-bad");
+
+ assertThrows(IllegalArgumentException.class, () ->
+ {
+ // Should trigger failure on bad extension
+ client.connect(cliSock, wsUri, request);
+ });
+ }
+
+ @Test
+ public void testBasicEcho_FromClient() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ client.setIdleTimeout(Duration.ofSeconds(10));
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo"));
+ ClientUpgradeRequest request = new ClientUpgradeRequest();
+ request.setSubProtocols("echo");
+ Future future = client.connect(cliSock, wsUri, request);
+
+ try (Session sess = future.get(30, TimeUnit.SECONDS))
+ {
+ assertThat("Session", sess, notNullValue());
+ assertThat("Session.open", sess.isOpen(), is(true));
+ assertThat("Session.upgradeRequest", sess.getUpgradeRequest(), notNullValue());
+ assertThat("Session.upgradeResponse", sess.getUpgradeResponse(), notNullValue());
+
+ Collection sessions = client.getOpenSessions();
+ assertThat("client.sessions.size", sessions.size(), is(1));
+
+ RemoteEndpoint remote = cliSock.getSession().getRemote();
+ remote.sendString("Hello World!");
+
+ // wait for response from server
+ String received = cliSock.messageQueue.poll(5, TimeUnit.SECONDS);
+ assertThat("Message", received, containsString("Hello World"));
+ }
+ }
+
+ @Test
+ public void testBasicEcho_UsingCallback() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ client.setIdleTimeout(Duration.ofSeconds(10));
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo"));
+ ClientUpgradeRequest request = new ClientUpgradeRequest();
+ request.setSubProtocols("echo");
+ Future future = client.connect(cliSock, wsUri, request);
+
+ try (Session sess = future.get(5, TimeUnit.SECONDS))
+ {
+ assertThat("Session", sess, notNullValue());
+ assertThat("Session.open", sess.isOpen(), is(true));
+ assertThat("Session.upgradeRequest", sess.getUpgradeRequest(), notNullValue());
+ assertThat("Session.upgradeResponse", sess.getUpgradeResponse(), notNullValue());
+
+ Collection sessions = client.getOpenSessions();
+ assertThat("client.sessions.size", sessions.size(), is(1));
+
+ FutureWriteCallback callback = new FutureWriteCallback();
+
+ cliSock.getSession().getRemote().sendString("Hello World!", callback);
+ callback.get(5, TimeUnit.SECONDS);
+
+ // wait for response from server
+ String received = cliSock.messageQueue.poll(5, TimeUnit.SECONDS);
+ assertThat("Message", received, containsString("Hello World"));
+ }
+ }
+
+ @Test
+ public void testBasicEcho_FromServer() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ client.setIdleTimeout(Duration.ofSeconds(10));
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/connect-msg"));
+ Future future = client.connect(cliSock, wsUri);
+
+ try (Session sess = future.get(5, TimeUnit.SECONDS))
+ {
+ // Validate connect
+ assertThat("Session", sess, notNullValue());
+ assertThat("Session.open", sess.isOpen(), is(true));
+ assertThat("Session.upgradeRequest", sess.getUpgradeRequest(), notNullValue());
+ assertThat("Session.upgradeResponse", sess.getUpgradeResponse(), notNullValue());
+
+ // wait for message from server
+ String received = cliSock.messageQueue.poll(5, TimeUnit.SECONDS);
+ assertThat("Message", received, containsString("Greeting from onConnect"));
+ }
+ }
+
+ @Test
+ public void testLocalRemoteAddress() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ client.setIdleTimeout(Duration.ofSeconds(10));
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/echo"));
+ ClientUpgradeRequest request = new ClientUpgradeRequest();
+ request.setSubProtocols("echo");
+ Future future = client.connect(cliSock, wsUri, request);
+
+ try (Session sess = future.get(5, TimeUnit.SECONDS))
+ {
+ Assertions.assertTrue(cliSock.openLatch.await(1, TimeUnit.SECONDS));
+
+ InetSocketAddress local = (InetSocketAddress)cliSock.getSession().getLocalAddress();
+ InetSocketAddress remote = (InetSocketAddress)cliSock.getSession().getRemoteAddress();
+
+ assertThat("Local Socket Address", local, notNullValue());
+ assertThat("Remote Socket Address", remote, notNullValue());
+
+ // Hard to validate (in a portable unit test) the local address that was used/bound in the low level Jetty Endpoint
+ assertThat("Local Socket Address / Host", local.getAddress().getHostAddress(), notNullValue());
+ assertThat("Local Socket Address / Port", local.getPort(), greaterThan(0));
+
+ String uriHostAddress = InetAddress.getByName(wsUri.getHost()).getHostAddress();
+ assertThat("Remote Socket Address / Host", remote.getAddress().getHostAddress(), is(uriHostAddress));
+ assertThat("Remote Socket Address / Port", remote.getPort(), greaterThan(0));
+ }
+ }
+
+ /**
+ * Ensure that @WebSocket(maxTextMessageSize = 100*1024)
behaves as expected.
+ *
+ * @throws Exception on test failure
+ */
+ @Test
+ public void testMaxMessageSize() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ client.setMaxTextMessageSize(100 * 1024);
+ client.setIdleTimeout(Duration.ofSeconds(10));
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/anno-max-message"));
+ Future future = client.connect(cliSock, wsUri);
+
+ try (Session sess = future.get(5, TimeUnit.SECONDS))
+ {
+ assertThat("Session", sess, notNullValue());
+ assertThat("Session.open", sess.isOpen(), is(true));
+
+ // Create string that is larger than default size of 64k
+ // but smaller than maxMessageSize of 100k
+ int size = 80 * 1024;
+ byte buf[] = new byte[size];
+ Arrays.fill(buf,(byte)'x');
+ String msg = StringUtil.toUTF8String(buf,0,buf.length);
+
+ sess.getRemote().sendString(msg);
+
+ // wait for message from server
+ String received = cliSock.messageQueue.poll(5, TimeUnit.SECONDS);
+ assertThat("Message", received.length(), is(size));
+ }
+ }
+
+ @Test
+ public void testParameterMap() throws Exception
+ {
+ CloseTrackingEndpoint cliSock = new CloseTrackingEndpoint();
+
+ client.setMaxTextMessageSize(100 * 1024);
+ client.setIdleTimeout(Duration.ofSeconds(10));
+
+ URI wsUri = WSURI.toWebsocket(server.getURI().resolve("/get-params?snack=cashews&amount=handful&brand=off"));
+ ClientUpgradeRequest request = new ClientUpgradeRequest();
+ Future future = client.connect(cliSock, wsUri, request);
+
+ try (Session sess = future.get(5, TimeUnit.SECONDS))
+ {
+ UpgradeRequest req = sess.getUpgradeRequest();
+ assertThat("Upgrade Request", req, notNullValue());
+
+ Map> parameterMap = req.getParameterMap();
+ assertThat("Parameter Map", parameterMap, notNullValue());
+
+ assertThat("Parameter[snack]", parameterMap.get("snack"), is(Arrays.asList(new String[]{"cashews"})));
+ assertThat("Parameter[amount]", parameterMap.get("amount"), is(Arrays.asList(new String[]{"handful"})));
+ assertThat("Parameter[brand]", parameterMap.get("brand"), is(Arrays.asList(new String[]{"off"})));
+
+ assertThat("Parameter[cost]", parameterMap.get("cost"), nullValue());
+
+ // wait for message from server indicating what it sees
+ String received = cliSock.messageQueue.poll(5, TimeUnit.SECONDS);
+ assertThat("Parameter[snack]", received, containsString("Params[snack]=[cashews]"));
+ assertThat("Parameter[amount]", received, containsString("Params[amount]=[handful]"));
+ assertThat("Parameter[brand]", received, containsString("Params[brand]=[off]"));
+ assertThat("Parameter[cost]", received, not(containsString("Params[cost]=")));
+ }
+ }
+}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerCloseTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerCloseTest.java
index 12647f2c3ba..5a7c7a6ae5d 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerCloseTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerCloseTest.java
@@ -89,7 +89,7 @@ public class ServerCloseTest
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
- JettyWebSocketServletContainerInitializer.configureContext(context);
+ JettyWebSocketServletContainerInitializer.configure(context, null);
server.start();
}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerConfigTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerConfigTest.java
index 9f8ba9a1ccf..6a743890e0c 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerConfigTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/ServerConfigTest.java
@@ -42,7 +42,6 @@ import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.core.internal.WebSocketConnection;
import org.eclipse.jetty.websocket.core.internal.WebSocketCoreSession;
-import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer;
import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
@@ -187,12 +186,14 @@ public class ServerConfigTest
contextHandler.addServlet(new ServletHolder(new WebSocketSessionConfigServlet()), "/sessionConfig");
server.setHandler(contextHandler);
- JettyWebSocketServerContainer container = JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
- container.setIdleTimeout(Duration.ofMillis(idleTimeout));
- container.setMaxTextMessageSize(maxMessageSize);
- container.setMaxBinaryMessageSize(maxMessageSize);
- container.setInputBufferSize(inputBufferSize);
- container.addMapping("/containerConfig", (req, resp)->standardEndpoint);
+ JettyWebSocketServletContainerInitializer.configure(contextHandler, (context, container) -> {
+ container.setIdleTimeout(Duration.ofMillis(idleTimeout));
+ container.setMaxTextMessageSize(maxMessageSize);
+ container.setMaxBinaryMessageSize(maxMessageSize);
+ container.setInputBufferSize(inputBufferSize);
+ container.addMapping("/containerConfig", (req, resp)->standardEndpoint);
+ });
+
server.start();
client = new WebSocketClient();
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/SlowServerTest.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/SlowServerTest.java
index e1976b8f9c0..ba63acdda4c 100644
--- a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/SlowServerTest.java
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/server/SlowServerTest.java
@@ -86,7 +86,7 @@ public class SlowServerTest
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
- JettyWebSocketServletContainerInitializer.configureContext(context);
+ JettyWebSocketServletContainerInitializer.configure(context, null);
server.start();
}
diff --git a/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/util/FutureWriteCallback.java b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/util/FutureWriteCallback.java
new file mode 100644
index 00000000000..7c8af9a8d2c
--- /dev/null
+++ b/jetty-websocket/jetty-websocket-tests/src/test/java/org/eclipse/jetty/websocket/tests/util/FutureWriteCallback.java
@@ -0,0 +1,32 @@
+package org.eclipse.jetty.websocket.tests.util;
+
+import java.util.concurrent.Future;
+
+import org.eclipse.jetty.util.FutureCallback;
+import org.eclipse.jetty.util.log.Log;
+import org.eclipse.jetty.util.log.Logger;
+import org.eclipse.jetty.websocket.api.WriteCallback;
+
+/**
+ * Allows events to a {@link WriteCallback} to drive a {@link Future} for the user.
+ */
+public class FutureWriteCallback extends FutureCallback implements WriteCallback
+{
+ private static final Logger LOG = Log.getLogger(FutureWriteCallback.class);
+
+ @Override
+ public void writeFailed(Throwable cause)
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug(".writeFailed",cause);
+ failed(cause);
+ }
+
+ @Override
+ public void writeSuccess()
+ {
+ if (LOG.isDebugEnabled())
+ LOG.debug(".writeSuccess");
+ succeeded();
+ }
+}
diff --git a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketUpgradeFilter.java b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketUpgradeFilter.java
index 368a4b3b47d..bdc8eb09208 100644
--- a/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketUpgradeFilter.java
+++ b/jetty-websocket/websocket-servlet/src/main/java/org/eclipse/jetty/websocket/servlet/WebSocketUpgradeFilter.java
@@ -21,7 +21,6 @@ package org.eclipse.jetty.websocket.servlet;
import java.io.IOException;
import java.time.Duration;
import java.util.EnumSet;
-
import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
@@ -90,6 +89,22 @@ public class WebSocketUpgradeFilter implements Filter, Dumpable
return null;
}
+ /**
+ * Configure the default WebSocketUpgradeFilter.
+ *
+ *
+ * This will return the default {@link WebSocketUpgradeFilter} on the
+ * provided {@link ServletContext}, creating the filter if necessary.
+ *
+ *
+ * The default {@link WebSocketUpgradeFilter} is also available via
+ * the {@link ServletContext} attribute named {@code org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter}
+ *
+ *
+ * @param servletContext the {@link ServletContext} to use
+ * @return the configured default {@link WebSocketUpgradeFilter} instance
+ * @throws ServletException if the filer cannot be configured
+ */
public static FilterHolder ensureFilter(ServletContext servletContext)
{
FilterHolder existingFilter = WebSocketUpgradeFilter.getFilter(servletContext);