From 0a944ac0a9f1c1e7be5f62d1d36be1273f26c71f Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Fri, 8 Jan 2021 22:47:55 +1100 Subject: [PATCH 1/3] Issue #5866 - allow programmatic upgrades with websocket-core Signed-off-by: Lachlan Roberts --- .../{internal => }/CreatorNegotiator.java | 13 +- .../server/{internal => }/Handshaker.java | 9 +- .../core/server/WebSocketMappings.java | 6 +- .../server/internal/AbstractHandshaker.java | 1 + .../server/internal/HandshakerSelector.java | 1 + .../server/JettyWebSocketServerContainer.java | 15 +-- .../server/JettyWebSocketServlet.java | 1 - .../JettyServerFrameHandlerFactory.java | 24 +--- .../ProgrammaticWebSocketUpgradeTest.java | 127 ++++++++++++++++++ 9 files changed, 152 insertions(+), 45 deletions(-) rename jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/{internal => }/CreatorNegotiator.java (85%) rename jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/{internal => }/Handshaker.java (83%) create mode 100644 jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/CreatorNegotiator.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/CreatorNegotiator.java similarity index 85% rename from jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/CreatorNegotiator.java rename to jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/CreatorNegotiator.java index 713a8768bfa9..0263dde7e335 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/CreatorNegotiator.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/CreatorNegotiator.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.websocket.core.server.internal; +package org.eclipse.jetty.websocket.core.server; import java.io.IOException; import java.util.concurrent.atomic.AtomicReference; @@ -20,18 +20,17 @@ import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.websocket.core.FrameHandler; -import org.eclipse.jetty.websocket.core.server.FrameHandlerFactory; -import org.eclipse.jetty.websocket.core.server.ServerUpgradeRequest; -import org.eclipse.jetty.websocket.core.server.ServerUpgradeResponse; -import org.eclipse.jetty.websocket.core.server.WebSocketCreator; -import org.eclipse.jetty.websocket.core.server.WebSocketNegotiation; -import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator; public class CreatorNegotiator extends WebSocketNegotiator.AbstractNegotiator { private final WebSocketCreator creator; private final FrameHandlerFactory factory; + public CreatorNegotiator(WebSocketCreator creator, FrameHandlerFactory factory) + { + this(creator, factory, null); + } + public CreatorNegotiator(WebSocketCreator creator, FrameHandlerFactory factory, Customizer customizer) { super(customizer); diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/Handshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/Handshaker.java similarity index 83% rename from jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/Handshaker.java rename to jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/Handshaker.java index 8bc3380306e3..ee4e78357db7 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/Handshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/Handshaker.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.websocket.core.server.internal; +package org.eclipse.jetty.websocket.core.server; import java.io.IOException; import javax.servlet.http.HttpServletRequest; @@ -19,9 +19,14 @@ import org.eclipse.jetty.websocket.core.Configuration; import org.eclipse.jetty.websocket.core.WebSocketComponents; -import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator; +import org.eclipse.jetty.websocket.core.server.internal.HandshakerSelector; public interface Handshaker { + static Handshaker newInstance() + { + return new HandshakerSelector(); + } + boolean upgradeRequest(WebSocketNegotiator negotiator, HttpServletRequest request, HttpServletResponse response, WebSocketComponents components, Configuration.Customizer defaultCustomizer) throws IOException; } diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java index 80670ce21bc6..f0e0e43126b3 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java @@ -33,8 +33,6 @@ import org.eclipse.jetty.websocket.core.FrameHandler; import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.exception.WebSocketException; -import org.eclipse.jetty.websocket.core.server.internal.CreatorNegotiator; -import org.eclipse.jetty.websocket.core.server.internal.Handshaker; import org.eclipse.jetty.websocket.core.server.internal.HandshakerSelector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -54,14 +52,14 @@ public class WebSocketMappings implements Dumpable, LifeCycle.Listener private static final Logger LOG = LoggerFactory.getLogger(WebSocketMappings.class); public static final String WEBSOCKET_MAPPING_ATTRIBUTE = WebSocketMappings.class.getName(); - public static WebSocketMappings getWebSocketNegotiator(ServletContext servletContext) + public static WebSocketMappings getMappings(ServletContext servletContext) { return (WebSocketMappings)servletContext.getAttribute(WEBSOCKET_MAPPING_ATTRIBUTE); } public static WebSocketMappings ensureMappings(ServletContext servletContext) { - WebSocketMappings mapping = getWebSocketNegotiator(servletContext); + WebSocketMappings mapping = getMappings(servletContext); if (mapping == null) { mapping = new WebSocketMappings(WebSocketServerComponents.getWebSocketComponents(servletContext)); diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java index 80623e44b053..a3cd1db8b433 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/AbstractHandshaker.java @@ -42,6 +42,7 @@ import org.eclipse.jetty.websocket.core.internal.Negotiated; import org.eclipse.jetty.websocket.core.internal.WebSocketConnection; import org.eclipse.jetty.websocket.core.internal.WebSocketCoreSession; +import org.eclipse.jetty.websocket.core.server.Handshaker; import org.eclipse.jetty.websocket.core.server.WebSocketNegotiation; import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator; import org.slf4j.Logger; diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/HandshakerSelector.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/HandshakerSelector.java index da2f0a1e896d..f5e97e69d8b0 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/HandshakerSelector.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/HandshakerSelector.java @@ -19,6 +19,7 @@ import org.eclipse.jetty.websocket.core.Configuration; import org.eclipse.jetty.websocket.core.WebSocketComponents; +import org.eclipse.jetty.websocket.core.server.Handshaker; import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator; /** diff --git a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java index 412cf92054e3..427256b609d4 100644 --- a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java +++ b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java @@ -34,7 +34,6 @@ import org.eclipse.jetty.websocket.core.Configuration; import org.eclipse.jetty.websocket.core.exception.WebSocketException; import org.eclipse.jetty.websocket.core.internal.util.ReflectUtils; -import org.eclipse.jetty.websocket.core.server.FrameHandlerFactory; import org.eclipse.jetty.websocket.core.server.WebSocketMappings; import org.eclipse.jetty.websocket.server.config.JettyWebSocketServletContainerInitializer; import org.eclipse.jetty.websocket.server.internal.DelegatedServerUpgradeRequest; @@ -82,7 +81,7 @@ public static JettyWebSocketServerContainer ensureContainer(ServletContext servl private final ServletContextHandler contextHandler; private final WebSocketMappings webSocketMappings; - private final FrameHandlerFactory frameHandlerFactory; + private final JettyServerFrameHandlerFactory frameHandlerFactory; private final Executor executor; private final Configuration.ConfigurationCustomizer customizer = new Configuration.ConfigurationCustomizer(); @@ -100,16 +99,8 @@ public static JettyWebSocketServerContainer ensureContainer(ServletContext servl this.contextHandler = contextHandler; this.webSocketMappings = webSocketMappings; this.executor = executor; - - // Ensure there is a FrameHandlerFactory - JettyServerFrameHandlerFactory factory = contextHandler.getBean(JettyServerFrameHandlerFactory.class); - if (factory == null) - { - factory = new JettyServerFrameHandlerFactory(this); - contextHandler.addManaged(factory); - contextHandler.addEventListener(factory); - } - frameHandlerFactory = factory; + this.frameHandlerFactory = new JettyServerFrameHandlerFactory(this); + addBean(frameHandlerFactory); addSessionListener(sessionTracker); addBean(sessionTracker); diff --git a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServlet.java b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServlet.java index dd2bb3e5b745..aa41bed1f22a 100644 --- a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServlet.java +++ b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServlet.java @@ -113,7 +113,6 @@ public abstract class JettyWebSocketServlet extends HttpServlet private FrameHandlerFactory getFactory() { JettyServerFrameHandlerFactory frameHandlerFactory = JettyServerFrameHandlerFactory.getFactory(getServletContext()); - if (frameHandlerFactory == null) throw new IllegalStateException("JettyServerFrameHandlerFactory not found"); diff --git a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/JettyServerFrameHandlerFactory.java b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/JettyServerFrameHandlerFactory.java index 4313bacd96ca..353a47b3e927 100644 --- a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/JettyServerFrameHandlerFactory.java +++ b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/internal/JettyServerFrameHandlerFactory.java @@ -15,9 +15,6 @@ import javax.servlet.ServletContext; -import org.eclipse.jetty.server.handler.ContextHandler; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.util.component.LifeCycle; import org.eclipse.jetty.websocket.api.WebSocketContainer; import org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandler; import org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandlerFactory; @@ -25,13 +22,14 @@ import org.eclipse.jetty.websocket.core.server.FrameHandlerFactory; import org.eclipse.jetty.websocket.core.server.ServerUpgradeRequest; import org.eclipse.jetty.websocket.core.server.ServerUpgradeResponse; +import org.eclipse.jetty.websocket.server.JettyWebSocketServerContainer; -public class JettyServerFrameHandlerFactory extends JettyWebSocketFrameHandlerFactory implements FrameHandlerFactory, LifeCycle.Listener +public class JettyServerFrameHandlerFactory extends JettyWebSocketFrameHandlerFactory implements FrameHandlerFactory { - public static JettyServerFrameHandlerFactory getFactory(ServletContext context) + public static JettyServerFrameHandlerFactory getFactory(ServletContext servletContext) { - ServletContextHandler contextHandler = ServletContextHandler.getServletContextHandler(context, "JettyServerFrameHandlerFactory"); - return contextHandler.getBean(JettyServerFrameHandlerFactory.class); + JettyWebSocketServerContainer container = JettyWebSocketServerContainer.getContainer(servletContext); + return (container == null) ? null : container.getBean(JettyServerFrameHandlerFactory.class); } public JettyServerFrameHandlerFactory(WebSocketContainer container) @@ -47,16 +45,4 @@ public FrameHandler newFrameHandler(Object websocketPojo, ServerUpgradeRequest u frameHandler.setUpgradeResponse(new DelegatedServerUpgradeResponse(upgradeResponse)); return frameHandler; } - - @Override - public void lifeCycleStopping(LifeCycle context) - { - ContextHandler contextHandler = (ContextHandler)context; - JettyServerFrameHandlerFactory factory = contextHandler.getBean(JettyServerFrameHandlerFactory.class); - if (factory != null) - { - contextHandler.removeBean(factory); - LifeCycle.stop(factory); - } - } } diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java new file mode 100644 index 000000000000..aa4a8d4d57ca --- /dev/null +++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java @@ -0,0 +1,127 @@ +// +// ======================================================================== +// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others. +// +// This program and the accompanying materials are made available under the +// terms of the Eclipse Public License v. 2.0 which is available at +// https://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0 +// which is available at https://www.apache.org/licenses/LICENSE-2.0. +// +// SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 +// ======================================================================== +// + +package org.eclipse.jetty.websocket.tests; + +import java.io.IOException; +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; +import javax.servlet.http.HttpServletResponse; + +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.client.WebSocketClient; +import org.eclipse.jetty.websocket.core.WebSocketComponents; +import org.eclipse.jetty.websocket.core.server.CreatorNegotiator; +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.config.JettyWebSocketServletContainerInitializer; +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; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class ProgrammaticWebSocketUpgradeTest +{ + private Server server; + private ServerConnector connector; + private WebSocketClient client; + private ServletContextHandler contextHandler; + + @BeforeEach + public void before() throws Exception + { + client = new WebSocketClient(); + server = new Server(); + connector = new ServerConnector(server); + server.addConnector(connector); + + contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS); + contextHandler.setContextPath("/"); + contextHandler.addServlet(new ServletHolder(new CustomUpgradeServlet()), "/"); + server.setHandler(contextHandler); + + JettyWebSocketServletContainerInitializer.configure(contextHandler, null); + + server.start(); + client.start(); + } + + @AfterEach + public void stop() throws Exception + { + client.stop(); + server.stop(); + } + + public static class CustomUpgradeServlet extends HttpServlet + { + private final Handshaker handshaker = Handshaker.newInstance(); + private FrameHandlerFactory frameHandlerFactory; + private WebSocketComponents components; + + @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); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException + { + WebSocketCreator creator = (req, resp) -> new EchoSocket(); + WebSocketNegotiator negotiator = new CreatorNegotiator(creator, frameHandlerFactory); + handshaker.upgradeRequest(negotiator, request, response, components, null); + } + } + + @Test + public void testProgrammaticWebSocketUpgrade() throws Exception + { + // Test we can upgrade to websocket and send a message. + URI uri = URI.create("ws://localhost:" + connector.getLocalPort() + "/path"); + EventSocket socket = new EventSocket(); + CompletableFuture connect = client.connect(socket, uri); + try (Session session = connect.get(5, TimeUnit.SECONDS)) + { + session.getRemote().sendString("hello world"); + } + assertTrue(socket.closeLatch.await(10, TimeUnit.SECONDS)); + + String msg = socket.textMessages.poll(); + assertThat(msg, is("hello world")); + + // WebSocketUpgradeFilter should not have been added. + assertThat(contextHandler.getServletHandler().getFilters().length, is(0)); + } +} From 78707fffde3407da14bd88cd77eac4bf29bd947d Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 13 Jan 2021 09:55:19 +1100 Subject: [PATCH 2/3] CreatorNegotiator should remain private, use from on WebSocketNegotiator Signed-off-by: Lachlan Roberts --- .../websocket/core/server/WebSocketMappings.java | 3 ++- .../core/server/WebSocketNegotiator.java | 16 ++++++++++++++++ .../server/{ => internal}/CreatorNegotiator.java | 8 +++++++- .../tests/ProgrammaticWebSocketUpgradeTest.java | 3 +-- 4 files changed, 26 insertions(+), 4 deletions(-) rename jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/{ => internal}/CreatorNegotiator.java (85%) diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java index f0e0e43126b3..ba3034ba6d37 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java @@ -33,6 +33,7 @@ import org.eclipse.jetty.websocket.core.FrameHandler; import org.eclipse.jetty.websocket.core.WebSocketComponents; import org.eclipse.jetty.websocket.core.exception.WebSocketException; +import org.eclipse.jetty.websocket.core.server.internal.CreatorNegotiator; import org.eclipse.jetty.websocket.core.server.internal.HandshakerSelector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -174,7 +175,7 @@ public WebSocketCreator getWebSocketCreator(PathSpec pathSpec) */ public void addMapping(PathSpec pathSpec, WebSocketCreator creator, FrameHandlerFactory factory, Configuration.Customizer customizer) throws WebSocketException { - mappings.put(pathSpec, new CreatorNegotiator(creator, factory, customizer)); + mappings.put(pathSpec, WebSocketNegotiator.from(creator, factory, customizer)); } /** diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketNegotiator.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketNegotiator.java index 729038b71018..f06bfef6f610 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketNegotiator.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketNegotiator.java @@ -18,11 +18,17 @@ import org.eclipse.jetty.websocket.core.Configuration; import org.eclipse.jetty.websocket.core.FrameHandler; +import org.eclipse.jetty.websocket.core.server.internal.CreatorNegotiator; public interface WebSocketNegotiator extends Configuration.Customizer { FrameHandler negotiate(WebSocketNegotiation negotiation) throws IOException; + @Override + default void customize(Configuration configurable) + { + } + static WebSocketNegotiator from(Function negotiate) { return from(negotiate, null); @@ -40,6 +46,16 @@ public FrameHandler negotiate(WebSocketNegotiation negotiation) }; } + static WebSocketNegotiator from(WebSocketCreator creator, FrameHandlerFactory factory) + { + return from(creator, factory, null); + } + + static WebSocketNegotiator from(WebSocketCreator creator, FrameHandlerFactory factory, Configuration.Customizer customizer) + { + return new CreatorNegotiator(creator, factory, customizer); + } + abstract class AbstractNegotiator extends Configuration.ConfigurationCustomizer implements WebSocketNegotiator { final Configuration.Customizer customizer; diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/CreatorNegotiator.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/CreatorNegotiator.java similarity index 85% rename from jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/CreatorNegotiator.java rename to jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/CreatorNegotiator.java index 0263dde7e335..66c2853d6efa 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/CreatorNegotiator.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/internal/CreatorNegotiator.java @@ -11,7 +11,7 @@ // ======================================================================== // -package org.eclipse.jetty.websocket.core.server; +package org.eclipse.jetty.websocket.core.server.internal; import java.io.IOException; import java.util.concurrent.atomic.AtomicReference; @@ -20,6 +20,12 @@ import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.websocket.core.FrameHandler; +import org.eclipse.jetty.websocket.core.server.FrameHandlerFactory; +import org.eclipse.jetty.websocket.core.server.ServerUpgradeRequest; +import org.eclipse.jetty.websocket.core.server.ServerUpgradeResponse; +import org.eclipse.jetty.websocket.core.server.WebSocketCreator; +import org.eclipse.jetty.websocket.core.server.WebSocketNegotiation; +import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator; public class CreatorNegotiator extends WebSocketNegotiator.AbstractNegotiator { diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java index aa4a8d4d57ca..996d9c18adb6 100644 --- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java +++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java @@ -31,7 +31,6 @@ 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.CreatorNegotiator; import org.eclipse.jetty.websocket.core.server.FrameHandlerFactory; import org.eclipse.jetty.websocket.core.server.Handshaker; import org.eclipse.jetty.websocket.core.server.WebSocketCreator; @@ -100,7 +99,7 @@ public void init(ServletConfig config) throws ServletException protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { WebSocketCreator creator = (req, resp) -> new EchoSocket(); - WebSocketNegotiator negotiator = new CreatorNegotiator(creator, frameHandlerFactory); + WebSocketNegotiator negotiator = WebSocketNegotiator.from(creator, frameHandlerFactory); handshaker.upgradeRequest(negotiator, request, response, components, null); } } From 0d48b8b991c8b098a28c15bb79ad79a0ef237f3e Mon Sep 17 00:00:00 2001 From: Lachlan Roberts Date: Wed, 13 Jan 2021 10:45:45 +1100 Subject: [PATCH 3/3] Issue #5866 - add Programmatic WebSocket Upgrade to Jetty API Signed-off-by: Lachlan Roberts --- .../core/server/WebSocketMappings.java | 5 +++ .../server/JettyWebSocketServerContainer.java | 32 +++++++++++++++++-- .../ProgrammaticWebSocketUpgradeTest.java | 22 +++---------- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java index ba3034ba6d37..cb4236712a7a 100644 --- a/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java +++ b/jetty-websocket/websocket-core-server/src/main/java/org/eclipse/jetty/websocket/core/server/WebSocketMappings.java @@ -123,6 +123,11 @@ public WebSocketMappings(WebSocketComponents components) this.components = components; } + public Handshaker getHandshaker() + { + return handshaker; + } + @Override public void lifeCycleStopping(LifeCycle context) { diff --git a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java index 427256b609d4..4b4af5bdb7bb 100644 --- a/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java +++ b/jetty-websocket/websocket-jetty-server/src/main/java/org/eclipse/jetty/websocket/server/JettyWebSocketServerContainer.java @@ -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.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.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 static JettyWebSocketServerContainer ensureContainer(ServletContext servl // 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 static JettyWebSocketServerContainer ensureContainer(ServletContext servl 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 static JettyWebSocketServerContainer ensureContainer(ServletContext servl * @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 void addMapping(String pathSpec, final Class endpointClass) }); } + /** + * 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() { diff --git a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java index 996d9c18adb6..5c0dae99c131 100644 --- a/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java +++ b/jetty-websocket/websocket-jetty-tests/src/test/java/org/eclipse/jetty/websocket/tests/ProgrammaticWebSocketUpgradeTest.java @@ -18,7 +18,6 @@ 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.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 void stop() throws Exception 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); } }