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;
}
public Handshaker getHandshaker()
{
return handshaker;
}
@Override
public void lifeCycleStopping(LifeCycle context)
{

View File

@ -13,6 +13,7 @@
package org.eclipse.jetty.websocket.server;
import java.io.IOException;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
@ -20,6 +21,8 @@ import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
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.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.common.SessionTracker;
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.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.WebSocketNegotiator;
import org.eclipse.jetty.websocket.core.server.WebSocketServerComponents;
import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.server.internal.DelegatedServerUpgradeRequest;
import org.eclipse.jetty.websocket.server.internal.DelegatedServerUpgradeResponse;
@ -68,7 +76,8 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements
// Create the Jetty ServerContainer implementation
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);
contextHandler.addManaged(container);
contextHandler.addEventListener(container);
@ -81,6 +90,7 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements
private final ServletContextHandler contextHandler;
private final WebSocketMappings webSocketMappings;
private final WebSocketComponents components;
private final JettyServerFrameHandlerFactory frameHandlerFactory;
private final Executor executor;
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 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.webSocketMappings = webSocketMappings;
this.components = components;
this.executor = executor;
this.frameHandlerFactory = new JettyServerFrameHandlerFactory(this);
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
public Executor getExecutor()
{

View File

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