Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-11.0.x
This commit is contained in:
commit
7de53ea445
|
@ -11,7 +11,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.core.server.internal;
|
||||
package org.eclipse.jetty.websocket.core.server;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
|
@ -19,9 +19,14 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||
import jakarta.servlet.http.HttpServletResponse;
|
||||
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;
|
||||
}
|
|
@ -34,7 +34,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;
|
||||
|
@ -124,6 +123,11 @@ public class WebSocketMappings implements Dumpable, LifeCycle.Listener
|
|||
this.components = components;
|
||||
}
|
||||
|
||||
public Handshaker getHandshaker()
|
||||
{
|
||||
return handshaker;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void lifeCycleStopping(LifeCycle context)
|
||||
{
|
||||
|
@ -176,7 +180,7 @@ public class WebSocketMappings implements Dumpable, LifeCycle.Listener
|
|||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,11 +18,17 @@ import java.util.function.Function;
|
|||
|
||||
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<WebSocketNegotiation, FrameHandler> negotiate)
|
||||
{
|
||||
return from(negotiate, null);
|
||||
|
@ -40,6 +46,16 @@ public interface WebSocketNegotiator extends Configuration.Customizer
|
|||
};
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -42,6 +42,7 @@ import org.eclipse.jetty.websocket.core.internal.ExtensionStack;
|
|||
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;
|
||||
|
|
|
@ -32,6 +32,11 @@ 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);
|
||||
|
|
|
@ -19,6 +19,7 @@ import jakarta.servlet.http.HttpServletRequest;
|
|||
import jakarta.servlet.http.HttpServletResponse;
|
||||
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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
@ -21,6 +22,8 @@ import java.util.concurrent.Executor;
|
|||
import java.util.function.Consumer;
|
||||
|
||||
import jakarta.servlet.ServletContext;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
|
@ -32,10 +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.FrameHandlerFactory;
|
||||
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;
|
||||
|
@ -69,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);
|
||||
|
@ -82,7 +90,8 @@ public class JettyWebSocketServerContainer extends ContainerLifeCycle implements
|
|||
|
||||
private final ServletContextHandler contextHandler;
|
||||
private final WebSocketMappings webSocketMappings;
|
||||
private final FrameHandlerFactory frameHandlerFactory;
|
||||
private final WebSocketComponents components;
|
||||
private final JettyServerFrameHandlerFactory frameHandlerFactory;
|
||||
private final Executor executor;
|
||||
private final Configuration.ConfigurationCustomizer customizer = new Configuration.ConfigurationCustomizer();
|
||||
|
||||
|
@ -95,21 +104,14 @@ 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;
|
||||
|
||||
// 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);
|
||||
|
@ -145,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()
|
||||
{
|
||||
|
|
|
@ -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");
|
||||
|
||||
|
|
|
@ -14,9 +14,6 @@
|
|||
package org.eclipse.jetty.websocket.server.internal;
|
||||
|
||||
import jakarta.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;
|
||||
|
@ -24,13 +21,14 @@ 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.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)
|
||||
|
@ -46,16 +44,4 @@ public class JettyServerFrameHandlerFactory extends JettyWebSocketFrameHandlerFa
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// 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 jakarta.servlet.ServletConfig;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.http.HttpServlet;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.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.server.JettyWebSocketCreator;
|
||||
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 JettyWebSocketServerContainer container;
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig config) throws ServletException
|
||||
{
|
||||
super.init(config);
|
||||
container = JettyWebSocketServerContainer.getContainer(getServletContext());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
|
||||
{
|
||||
JettyWebSocketCreator creator = (req, resp) -> new EchoSocket();
|
||||
container.upgrade(creator, request, response);
|
||||
}
|
||||
}
|
||||
|
||||
@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<Session> 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));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue