Issue #5866 - add Programmatic WebSocket Upgrade to Jetty API

Signed-off-by: Lachlan Roberts <lachlan@webtide.com>
This commit is contained in:
Lachlan Roberts 2021-01-13 10:45:45 +11:00
parent 78707fffde
commit 0d48b8b991
3 changed files with 40 additions and 19 deletions

View File

@ -123,6 +123,11 @@ public class WebSocketMappings implements Dumpable, LifeCycle.Listener
this.components = components; this.components = components;
} }
public Handshaker getHandshaker()
{
return handshaker;
}
@Override @Override
public void lifeCycleStopping(LifeCycle context) public void lifeCycleStopping(LifeCycle context)
{ {

View File

@ -13,6 +13,7 @@
package org.eclipse.jetty.websocket.server; package org.eclipse.jetty.websocket.server;
import java.io.IOException;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
@ -20,6 +21,8 @@ import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.pathmap.PathSpec; import org.eclipse.jetty.http.pathmap.PathSpec;
import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletContextHandler;
@ -32,9 +35,14 @@ import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.api.WebSocketSessionListener; import org.eclipse.jetty.websocket.api.WebSocketSessionListener;
import org.eclipse.jetty.websocket.common.SessionTracker; import org.eclipse.jetty.websocket.common.SessionTracker;
import org.eclipse.jetty.websocket.core.Configuration; import org.eclipse.jetty.websocket.core.Configuration;
import org.eclipse.jetty.websocket.core.WebSocketComponents;
import org.eclipse.jetty.websocket.core.exception.WebSocketException; import org.eclipse.jetty.websocket.core.exception.WebSocketException;
import org.eclipse.jetty.websocket.core.internal.util.ReflectUtils; import org.eclipse.jetty.websocket.core.internal.util.ReflectUtils;
import org.eclipse.jetty.websocket.core.server.Handshaker;
import org.eclipse.jetty.websocket.core.server.WebSocketCreator;
import org.eclipse.jetty.websocket.core.server.WebSocketMappings; import org.eclipse.jetty.websocket.core.server.WebSocketMappings;
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
import org.eclipse.jetty.websocket.core.server.WebSocketServerComponents;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.server.internal.DelegatedServerUpgradeRequest; import org.eclipse.jetty.websocket.server.internal.DelegatedServerUpgradeRequest;
import org.eclipse.jetty.websocket.server.internal.DelegatedServerUpgradeResponse; import org.eclipse.jetty.websocket.server.internal.DelegatedServerUpgradeResponse;
@ -68,7 +76,8 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements
// Create the Jetty ServerContainer implementation // Create the Jetty ServerContainer implementation
WebSocketMappings mappings = WebSocketMappings.ensureMappings(servletContext); WebSocketMappings mappings = WebSocketMappings.ensureMappings(servletContext);
container = new JettyWebSocketServerContainer(contextHandler, mappings, executor); WebSocketComponents components = WebSocketServerComponents.getWebSocketComponents(servletContext);
container = new JettyWebSocketServerContainer(contextHandler, mappings, components, executor);
servletContext.setAttribute(JETTY_WEBSOCKET_CONTAINER_ATTRIBUTE, container); servletContext.setAttribute(JETTY_WEBSOCKET_CONTAINER_ATTRIBUTE, container);
contextHandler.addManaged(container); contextHandler.addManaged(container);
contextHandler.addEventListener(container); contextHandler.addEventListener(container);
@ -81,6 +90,7 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements
private final ServletContextHandler contextHandler; private final ServletContextHandler contextHandler;
private final WebSocketMappings webSocketMappings; private final WebSocketMappings webSocketMappings;
private final WebSocketComponents components;
private final JettyServerFrameHandlerFactory frameHandlerFactory; private final JettyServerFrameHandlerFactory frameHandlerFactory;
private final Executor executor; private final Executor executor;
private final Configuration.ConfigurationCustomizer customizer = new Configuration.ConfigurationCustomizer(); private final Configuration.ConfigurationCustomizer customizer = new Configuration.ConfigurationCustomizer();
@ -94,10 +104,11 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements
* @param webSocketMappings the {@link WebSocketMappings} that this container belongs to * @param webSocketMappings the {@link WebSocketMappings} that this container belongs to
* @param executor the {@link Executor} to use * @param executor the {@link Executor} to use
*/ */
JettyWebSocketServerContainer(ServletContextHandler contextHandler, WebSocketMappings webSocketMappings, Executor executor) JettyWebSocketServerContainer(ServletContextHandler contextHandler, WebSocketMappings webSocketMappings, WebSocketComponents components, Executor executor)
{ {
this.contextHandler = contextHandler; this.contextHandler = contextHandler;
this.webSocketMappings = webSocketMappings; this.webSocketMappings = webSocketMappings;
this.components = components;
this.executor = executor; this.executor = executor;
this.frameHandlerFactory = new JettyServerFrameHandlerFactory(this); this.frameHandlerFactory = new JettyServerFrameHandlerFactory(this);
addBean(frameHandlerFactory); addBean(frameHandlerFactory);
@ -136,6 +147,23 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements
}); });
} }
/**
* An immediate programmatic WebSocket upgrade that does not register a mapping or create a {@link WebSocketUpgradeFilter}.
* @param creator the WebSocketCreator to use.
* @param request the HttpServletRequest.
* @param response the HttpServletResponse.
* @return true if the connection was successfully upgraded to WebSocket.
* @throws IOException if an I/O error occurs.
*/
public boolean upgrade(JettyWebSocketCreator creator, HttpServletRequest request, HttpServletResponse response) throws IOException
{
WebSocketCreator coreCreator = (req, resp) -> creator.createWebSocket(new DelegatedServerUpgradeRequest(req), new DelegatedServerUpgradeResponse(resp));
WebSocketNegotiator negotiator = WebSocketNegotiator.from(coreCreator, frameHandlerFactory, customizer);
Handshaker handshaker = webSocketMappings.getHandshaker();
return handshaker.upgradeRequest(negotiator, request, response, components, null);
}
@Override @Override
public Executor getExecutor() public Executor getExecutor()
{ {

View File

@ -18,7 +18,6 @@ import java.net.URI;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.servlet.ServletConfig; import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException; import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -30,12 +29,7 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder; import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.websocket.api.Session; import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.client.WebSocketClient; import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.server.JettyWebSocketCreator;
import org.eclipse.jetty.websocket.core.server.FrameHandlerFactory;
import org.eclipse.jetty.websocket.core.server.Handshaker;
import org.eclipse.jetty.websocket.core.server.WebSocketCreator;
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
import org.eclipse.jetty.websocket.core.server.WebSocketServerComponents;
import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer; import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.AfterEach;
@ -81,26 +75,20 @@ public class ProgrammaticWebSocketUpgradeTest
public static class CustomUpgradeServlet extends HttpServlet public static class CustomUpgradeServlet extends HttpServlet
{ {
private final Handshaker handshaker = Handshaker.newInstance(); private JettyWebSocketServerContainer container;
private FrameHandlerFactory frameHandlerFactory;
private WebSocketComponents components;
@Override @Override
public void init(ServletConfig config) throws ServletException public void init(ServletConfig config) throws ServletException
{ {
super.init(config); super.init(config);
ServletContext servletContext = getServletContext(); container = JettyWebSocketServerContainer.getContainer(getServletContext());
JettyWebSocketServerContainer container = JettyWebSocketServerContainer.getContainer(servletContext);
components = WebSocketServerComponents.getWebSocketComponents(servletContext);
frameHandlerFactory = container.getBean(FrameHandlerFactory.class);
} }
@Override @Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{ {
WebSocketCreator creator = (req, resp) -> new EchoSocket(); JettyWebSocketCreator creator = (req, resp) -> new EchoSocket();
WebSocketNegotiator negotiator = WebSocketNegotiator.from(creator, frameHandlerFactory); container.upgrade(creator, request, response);
handshaker.upgradeRequest(negotiator, request, response, components, null);
} }
} }