Merge remote-tracking branch 'origin/jetty-10.0.x' into jetty-10.0.x-3012-Compliance

This commit is contained in:
Greg Wilkins 2019-03-13 14:59:02 +11:00
commit c7f66521cb
49 changed files with 745 additions and 747 deletions

View File

@ -633,7 +633,6 @@
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.3.0.Final</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.cdi</groupId>

View File

@ -10,7 +10,6 @@
<url>http://www.eclipse.org/jetty</url>
<properties>
<bundle-symbolic-name>${project.groupId}.infinispan</bundle-symbolic-name>
<infinispan.version>9.1.0.Final</infinispan.version>
</properties>
<build>
<defaultGoal>install</defaultGoal>
@ -43,7 +42,7 @@
<dependency>
<groupId>org.infinispan.protostream</groupId>
<artifactId>protostream</artifactId>
<version>4.1.0.Final</version>
<version>4.2.2.Final</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
@ -53,13 +52,17 @@
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-client-hotrod</artifactId>
<version>9.1.0.Final</version>
<version>${infinispan.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-remote-query-client</artifactId>
<version>9.1.0.Final</version>
<version>${infinispan.version}</version>
<scope>provided</scope>
</dependency>
</dependencies>

View File

@ -16,14 +16,10 @@
// ========================================================================
//
import org.eclipse.jetty.websocket.api.extensions.Extension;
module org.eclipse.jetty.websocket.jetty.api
{
exports org.eclipse.jetty.websocket.api;
exports org.eclipse.jetty.websocket.api.annotations;
exports org.eclipse.jetty.websocket.api.extensions;
exports org.eclipse.jetty.websocket.api.util;
uses Extension;
}

View File

@ -18,11 +18,8 @@
package org.eclipse.jetty.websocket.api;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.api.extensions.OutgoingFrames;
/**
* The possible batch modes when invoking {@link OutgoingFrames#outgoingFrame(Frame, WriteCallback, BatchMode)}.
* The possible batch modes when enqueuing outgoing frames.
*/
public enum BatchMode
{

View File

@ -16,7 +16,7 @@
// ========================================================================
//
package org.eclipse.jetty.websocket.api.extensions;
package org.eclipse.jetty.websocket.api;
import java.nio.ByteBuffer;

View File

@ -18,8 +18,6 @@
package org.eclipse.jetty.websocket.api;
import org.eclipse.jetty.websocket.api.extensions.Frame;
/**
* WebSocket Frame Listener interface for incoming WebSocket frames.
*/

View File

@ -18,22 +18,23 @@
package org.eclipse.jetty.websocket.api.annotations;
import org.eclipse.jetty.websocket.api.Session;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.eclipse.jetty.websocket.api.Frame;
import org.eclipse.jetty.websocket.api.Session;
/**
* (ADVANCED) Annotation for tagging methods to receive frame events.
* <p>
* Acceptable method patterns.<br>
* Note: {@code methodName} can be any name you want to use.
* <ol>
* <li><code>public void methodName({@link org.eclipse.jetty.websocket.api.extensions.Frame} frame)</code></li>
* <li><code>public void methodName({@link Session} session, {@link org.eclipse.jetty.websocket.api.extensions.Frame} frame)</code></li>
* <li><code>public void methodName({@link Frame} frame)</code></li>
* <li><code>public void methodName({@link Session} session, {@link Frame} frame)</code></li>
* </ol>
*/
@Documented

View File

@ -1,84 +0,0 @@
//
// ========================================================================
// 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.api.extensions;
/**
* Interface for WebSocket Extensions.
* <p>
* That {@link Frame}s are passed through the Extension via the {@link IncomingFrames} and {@link OutgoingFrames} interfaces
*/
public interface Extension extends IncomingFrames, OutgoingFrames
{
/**
* The active configuration for this extension.
*
* @return the configuration for this extension. never null.
*/
public ExtensionConfig getConfig();
/**
* The {@code Sec-WebSocket-Extensions} name for this extension.
* <p>
* Also known as the <a href="https://tools.ietf.org/html/rfc6455#section-9.1">{@code extension-token} per Section 9.1. Negotiating Extensions</a>.
*
* @return the name of the extension
*/
public String getName();
/**
* Used to indicate that the extension makes use of the RSV1 bit of the base websocket framing.
* <p>
* This is used to adjust validation during parsing, as well as a checkpoint against 2 or more extensions all simultaneously claiming ownership of RSV1.
*
* @return true if extension uses RSV1 for its own purposes.
*/
public abstract boolean isRsv1User();
/**
* Used to indicate that the extension makes use of the RSV2 bit of the base websocket framing.
* <p>
* This is used to adjust validation during parsing, as well as a checkpoint against 2 or more extensions all simultaneously claiming ownership of RSV2.
*
* @return true if extension uses RSV2 for its own purposes.
*/
public abstract boolean isRsv2User();
/**
* Used to indicate that the extension makes use of the RSV3 bit of the base websocket framing.
* <p>
* This is used to adjust validation during parsing, as well as a checkpoint against 2 or more extensions all simultaneously claiming ownership of RSV3.
*
* @return true if extension uses RSV3 for its own purposes.
*/
public abstract boolean isRsv3User();
/**
* Set the next {@link IncomingFrames} to call in the chain.
*
* @param nextIncoming the next incoming extension
*/
public void setNextIncomingFrames(IncomingFrames nextIncoming);
/**
* Set the next {@link OutgoingFrames} to call in the chain.
*
* @param nextOutgoing the next outgoing extension
*/
public void setNextOutgoingFrames(OutgoingFrames nextOutgoing);
}

View File

@ -1,81 +0,0 @@
//
// ========================================================================
// 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.api.extensions;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
public abstract class ExtensionFactory implements Iterable<Class<? extends Extension>>
{
private Map<String, Class<? extends Extension>> availableExtensions;
public ExtensionFactory()
{
ServiceLoader<Extension> extensionLoader = ServiceLoader.load(Extension.class);
availableExtensions = new HashMap<>();
for (Extension ext : extensionLoader)
{
if (ext != null)
{
availableExtensions.put(ext.getName(), ext.getClass());
}
}
}
public Map<String, Class<? extends Extension>> getAvailableExtensions()
{
return availableExtensions;
}
public Class<? extends Extension> getExtension(String name)
{
return availableExtensions.get(name);
}
public Set<String> getExtensionNames()
{
return availableExtensions.keySet();
}
public boolean isAvailable(String name)
{
return availableExtensions.containsKey(name);
}
@Override
public Iterator<Class<? extends Extension>> iterator()
{
return availableExtensions.values().iterator();
}
public abstract Extension newInstance(ExtensionConfig config);
public void register(String name, Class<? extends Extension> extension)
{
availableExtensions.put(name, extension);
}
public void unregister(String name)
{
availableExtensions.remove(name);
}
}

View File

@ -1,38 +0,0 @@
//
// ========================================================================
// 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.api.extensions;
/**
* Interface for dealing with Incoming Frames.
*/
public interface IncomingFrames
{
public void incomingError(Throwable t);
/**
* Process the incoming frame.
* <p>
* Note: if you need to hang onto any information from the frame, be sure
* to copy it, as the information contained in the Frame will be released
* and/or reused by the implementation.
*
* @param frame the frame to process
*/
public void incomingFrame(Frame frame);
}

View File

@ -1,44 +0,0 @@
//
// ========================================================================
// 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.api.extensions;
import org.eclipse.jetty.websocket.api.BatchMode;
import org.eclipse.jetty.websocket.api.WriteCallback;
/**
* Interface for dealing with frames outgoing to (eventually) the network layer.
*/
public interface OutgoingFrames
{
/**
* A frame, and optional callback, intended for the network layer.
* <p>
* Note: the frame can undergo many transformations in the various
* layers and extensions present in the implementation.
* <p>
* If you are implementing a mutation, you are obliged to handle
* the incoming WriteCallback appropriately.
*
* @param frame the frame to eventually write to the network layer.
* @param callback the callback to notify when the frame is written.
* @param batchMode the batch mode requested by the sender.
*/
void outgoingFrame(Frame frame, WriteCallback callback, BatchMode batchMode);
}

View File

@ -18,11 +18,11 @@
package org.eclipse.jetty.websocket.common;
import org.eclipse.jetty.websocket.core.Frame;
import java.nio.ByteBuffer;
public class JettyWebSocketFrame implements org.eclipse.jetty.websocket.api.extensions.Frame
import org.eclipse.jetty.websocket.core.Frame;
public class JettyWebSocketFrame implements org.eclipse.jetty.websocket.api.Frame
{
private final Frame frame;

View File

@ -71,7 +71,7 @@ public class JettyWebSocketFrameHandler implements FrameHandler
private MessageSink textSink;
private MessageSink binarySink;
private MessageSink activeMessageSink;
private WebSocketSessionImpl session;
private WebSocketSession session;
public JettyWebSocketFrameHandler(WebSocketContainer container,
Object endpointInstance,
@ -108,7 +108,7 @@ public class JettyWebSocketFrameHandler implements FrameHandler
this.customizer = customizer;
}
public WebSocketSessionImpl getSession()
public WebSocketSession getSession()
{
return session;
}
@ -120,7 +120,7 @@ public class JettyWebSocketFrameHandler implements FrameHandler
{
customizer.customize(coreSession);
session = new WebSocketSessionImpl(coreSession, this, upgradeRequest, upgradeResponse);
session = new WebSocketSession(coreSession, this, upgradeRequest, upgradeResponse);
frameHandle = JettyWebSocketFrameHandlerFactory.bindTo(frameHandle, session);
openHandle = JettyWebSocketFrameHandlerFactory.bindTo(openHandle, session);

View File

@ -36,6 +36,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.websocket.api.Frame;
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.UpgradeRequest;
@ -280,7 +281,7 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
// Frame Listener
if (WebSocketFrameListener.class.isAssignableFrom(endpointClass))
{
Method frameMethod = ReflectUtils.findMethod(endpointClass, "onWebSocketFrame", org.eclipse.jetty.websocket.api.extensions.Frame.class);
Method frameMethod = ReflectUtils.findMethod(endpointClass, "onWebSocketFrame", Frame.class);
MethodHandle frame = toMethodHandle(lookup, frameMethod);
metadata.setFrameHandler(frame, frameMethod);
}
@ -349,7 +350,7 @@ public class JettyWebSocketFrameHandlerFactory extends ContainerLifeCycle
{
assertSignatureValid(endpointClass, onmethod, OnWebSocketFrame.class);
final InvokerUtils.Arg SESSION = new InvokerUtils.Arg(Session.class);
final InvokerUtils.Arg FRAME = new InvokerUtils.Arg(org.eclipse.jetty.websocket.api.extensions.Frame.class).required();
final InvokerUtils.Arg FRAME = new InvokerUtils.Arg(Frame.class).required();
MethodHandle methodHandle = InvokerUtils.mutatedInvoker(endpointClass, onmethod, SESSION, FRAME);
metadata.setFrameHandler(methodHandle, onmethod);
}

View File

@ -1,28 +0,0 @@
//
// ========================================================================
// 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.common;
public interface SessionListener
{
void onCreated(WebSocketSessionImpl session);
void onOpened(WebSocketSessionImpl session);
void onClosed(WebSocketSessionImpl session);
}

View File

@ -36,13 +36,13 @@ public class SessionTracker extends AbstractLifeCycle implements WebSocketSessio
}
@Override
public void onWebSocketSessionOpened(WebSocketSessionImpl session)
public void onWebSocketSessionOpened(WebSocketSession session)
{
sessions.add(session);
}
@Override
public void onWebSocketSessionClosed(WebSocketSessionImpl session)
public void onWebSocketSessionClosed(WebSocketSession session)
{
sessions.remove(session);
}

View File

@ -37,16 +37,16 @@ import org.eclipse.jetty.websocket.api.UpgradeResponse;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.core.FrameHandler;
public class WebSocketSessionImpl extends AbstractLifeCycle implements Session, Dumpable
public class WebSocketSession extends AbstractLifeCycle implements Session, Dumpable
{
private static final Logger LOG = Log.getLogger(WebSocketSessionImpl.class);
private static final Logger LOG = Log.getLogger(WebSocketSession.class);
private final FrameHandler.CoreSession coreSession;
private final JettyWebSocketFrameHandler frameHandler;
private final JettyWebSocketRemoteEndpoint remoteEndpoint;
private final UpgradeRequest upgradeRequest;
private final UpgradeResponse upgradeResponse;
public WebSocketSessionImpl(
public WebSocketSession(
FrameHandler.CoreSession coreSession,
JettyWebSocketFrameHandler frameHandler,
UpgradeRequest upgradeRequest,
@ -254,6 +254,6 @@ public class WebSocketSessionImpl extends AbstractLifeCycle implements Session,
@Override
public String toString()
{
return String.format("WebSocketSessionImpl[%s,to=%s,%s,%s]", getBehavior(), getIdleTimeout(), coreSession, frameHandler);
return String.format("WebSocketSession[%s,to=%s,%s,%s]", getBehavior(), getIdleTimeout(), coreSession, frameHandler);
}
}

View File

@ -23,7 +23,7 @@ package org.eclipse.jetty.websocket.common;
*/
public interface WebSocketSessionListener
{
void onWebSocketSessionOpened(WebSocketSessionImpl session);
void onWebSocketSessionOpened(WebSocketSession session);
void onWebSocketSessionClosed(WebSocketSessionImpl session);
void onWebSocketSessionClosed(WebSocketSession session);
}

View File

@ -18,9 +18,9 @@
package org.eclipse.jetty.websocket.common.endpoints.annotated;
import org.eclipse.jetty.websocket.api.Frame;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.extensions.Frame;
@WebSocket
public class FrameSocket

View File

@ -18,9 +18,9 @@
package org.eclipse.jetty.websocket.common.endpoints.listeners;
import org.eclipse.jetty.websocket.api.Frame;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketFrameListener;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.common.EventQueue;
import org.eclipse.jetty.websocket.common.util.TextUtil;
import org.eclipse.jetty.websocket.core.CloseStatus;

View File

@ -19,8 +19,8 @@
package org.eclipse.jetty.websocket.server;
import java.util.concurrent.CompletableFuture;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
@ -29,7 +29,6 @@ import org.eclipse.jetty.websocket.common.JettyWebSocketFrameHandlerFactory;
import org.eclipse.jetty.websocket.common.WebSocketContainer;
import org.eclipse.jetty.websocket.core.FrameHandler;
import org.eclipse.jetty.websocket.server.internal.DelegatedJettyServletUpgradeRequest;
import org.eclipse.jetty.websocket.server.internal.JettyWebSocketServerContainer;
import org.eclipse.jetty.websocket.server.internal.UpgradeResponseAdapter;
import org.eclipse.jetty.websocket.servlet.FrameHandlerFactory;
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
@ -39,23 +38,6 @@ public class JettyServerFrameHandlerFactory
extends JettyWebSocketFrameHandlerFactory
implements FrameHandlerFactory, LifeCycle.Listener
{
public static JettyServerFrameHandlerFactory ensureFactory(ServletContext servletContext)
throws ServletException
{
ContextHandler contextHandler = ServletContextHandler.getServletContextHandler(servletContext, "Jetty Websocket");
JettyServerFrameHandlerFactory factory = contextHandler.getBean(JettyServerFrameHandlerFactory.class);
if (factory == null)
{
JettyWebSocketServerContainer container = new JettyWebSocketServerContainer(contextHandler);
servletContext.setAttribute(WebSocketContainer.class.getName(), container);
factory = new JettyServerFrameHandlerFactory(container);
contextHandler.addManaged(factory);
contextHandler.addLifeCycleListener(factory);
}
return factory;
}
public JettyServerFrameHandlerFactory(WebSocketContainer container)
{
super(container);

View File

@ -0,0 +1,231 @@
//
// ========================================================================
// 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.server;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import javax.servlet.ServletContext;
import org.eclipse.jetty.http.pathmap.PathSpec;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.util.component.ContainerLifeCycle;
import org.eclipse.jetty.util.component.LifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
import org.eclipse.jetty.websocket.common.SessionTracker;
import org.eclipse.jetty.websocket.common.WebSocketContainer;
import org.eclipse.jetty.websocket.common.WebSocketSessionListener;
import org.eclipse.jetty.websocket.core.FrameHandler;
import org.eclipse.jetty.websocket.core.WebSocketComponents;
import org.eclipse.jetty.websocket.core.WebSocketException;
import org.eclipse.jetty.websocket.servlet.FrameHandlerFactory;
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
import org.eclipse.jetty.websocket.servlet.WebSocketMapping;
public class JettyWebSocketServerContainer extends ContainerLifeCycle implements WebSocketContainer, WebSocketPolicy, LifeCycle.Listener
{
public static JettyWebSocketServerContainer ensureContainer(ServletContext servletContext)
{
ServletContextHandler contextHandler = ServletContextHandler.getServletContextHandler(servletContext, "Javax Websocket");
if (contextHandler.getServer() == null)
throw new IllegalStateException("Server has not been set on the ServletContextHandler");
JettyWebSocketServerContainer container = contextHandler.getBean(JettyWebSocketServerContainer.class);
if (container == null)
{
// Find Pre-Existing executor
Executor executor = (Executor)servletContext.getAttribute("org.eclipse.jetty.server.Executor");
if (executor == null)
executor = contextHandler.getServer().getThreadPool();
// Create the Jetty ServerContainer implementation
container = new JettyWebSocketServerContainer(
contextHandler,
WebSocketMapping.ensureMapping(servletContext, WebSocketMapping.DEFAULT_KEY),
WebSocketComponents.ensureWebSocketComponents(servletContext), executor);
servletContext.setAttribute(WebSocketContainer.class.getName(), container);
contextHandler.addManaged(container);
contextHandler.addLifeCycleListener(container);
}
return container;
}
private final static Logger LOG = Log.getLogger(JettyWebSocketServerContainer.class);
private final WebSocketMapping webSocketMapping;
private final WebSocketComponents webSocketComponents;
private final FrameHandlerFactory frameHandlerFactory;
private final Executor executor;
private final FrameHandler.ConfigurationCustomizer customizer = new FrameHandler.ConfigurationCustomizer();
private final List<WebSocketSessionListener> sessionListeners = new ArrayList<>();
private final SessionTracker sessionTracker = new SessionTracker();
/**
* Main entry point for {@link JettyWebSocketServletContainerInitializer}.
*
* @param webSocketMapping the {@link WebSocketMapping} that this container belongs to
* @param webSocketComponents the {@link WebSocketComponents} instance to use
* @param executor the {@link Executor} to use
*/
public JettyWebSocketServerContainer(ServletContextHandler contextHandler, WebSocketMapping webSocketMapping, WebSocketComponents webSocketComponents, Executor executor)
{
this.webSocketMapping = webSocketMapping;
this.webSocketComponents = webSocketComponents;
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.addLifeCycleListener(factory);
}
frameHandlerFactory = factory;
addSessionListener(sessionTracker);
}
public void addMapping(String pathSpec, WebSocketCreator creator)
{
PathSpec ps = WebSocketMapping.parsePathSpec(pathSpec);
if (webSocketMapping.getMapping(ps) != null)
throw new WebSocketException("Duplicate WebSocket Mapping for PathSpec");
webSocketMapping.addMapping(ps, creator, frameHandlerFactory, customizer);
}
@Override
public Executor getExecutor()
{
return this.executor;
}
@Override
public void addSessionListener(WebSocketSessionListener listener)
{
sessionListeners.add(listener);
}
@Override
public boolean removeSessionListener(WebSocketSessionListener listener)
{
return sessionListeners.remove(listener);
}
@Override
public void notifySessionListeners(Consumer<WebSocketSessionListener> consumer)
{
for (WebSocketSessionListener listener : sessionListeners)
{
try
{
consumer.accept(listener);
}
catch (Throwable x)
{
LOG.info("Exception while invoking listener " + listener, x);
}
}
}
@Override
public Collection<Session> getOpenSessions()
{
return sessionTracker.getSessions();
}
@Override
public WebSocketBehavior getBehavior()
{
return WebSocketBehavior.SERVER;
}
@Override
public Duration getIdleTimeout()
{
return customizer.getIdleTimeout();
}
@Override
public int getInputBufferSize()
{
return customizer.getInputBufferSize();
}
@Override
public int getOutputBufferSize()
{
return customizer.getOutputBufferSize();
}
@Override
public long getMaxBinaryMessageSize()
{
return customizer.getMaxBinaryMessageSize();
}
@Override
public long getMaxTextMessageSize()
{
return customizer.getMaxTextMessageSize();
}
@Override
public void setIdleTimeout(Duration duration)
{
customizer.setIdleTimeout(duration);
}
@Override
public void setInputBufferSize(int size)
{
customizer.setInputBufferSize(size);
}
@Override
public void setOutputBufferSize(int size)
{
customizer.setOutputBufferSize(size);
}
@Override
public void setMaxBinaryMessageSize(long size)
{
customizer.setMaxBinaryMessageSize(size);
}
@Override
public void setMaxTextMessageSize(long size)
{
customizer.setMaxTextMessageSize(size);
}
}

View File

@ -18,17 +18,19 @@
package org.eclipse.jetty.websocket.server;
import java.util.Collections;
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.util.component.AbstractLifeCycle;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.core.WebSocketComponents;
import org.eclipse.jetty.websocket.servlet.WebSocketMapping;
import org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter;
/**
* ServletContext configuration for Jetty Native WebSockets API.
@ -37,45 +39,23 @@ public class JettyWebSocketServletContainerInitializer implements ServletContain
{
private static final Logger LOG = Log.getLogger(JettyWebSocketServletContainerInitializer.class);
public static class JettyWebSocketEmbeddedStarter extends AbstractLifeCycle implements ServletContextHandler.ServletContainerInitializerCaller
public static JettyWebSocketServerContainer configureContext(ServletContextHandler context)
{
private ServletContainerInitializer sci;
private ServletContextHandler context;
WebSocketComponents components = WebSocketComponents.ensureWebSocketComponents(context.getServletContext());
FilterHolder filterHolder = WebSocketUpgradeFilter.ensureFilter(context.getServletContext());
WebSocketMapping mapping = WebSocketMapping.ensureMapping(context.getServletContext(), WebSocketMapping.DEFAULT_KEY);
JettyWebSocketServerContainer container = JettyWebSocketServerContainer.ensureContainer(context.getServletContext());
public JettyWebSocketEmbeddedStarter(ServletContainerInitializer sci, ServletContextHandler context)
{
this.sci = sci;
this.context = context;
}
if (LOG.isDebugEnabled())
LOG.debug("configureContext {} {} {} {}", container, mapping, filterHolder, components);
public void doStart()
{
try
{
Set<Class<?>> c = Collections.emptySet();
sci.onStartup(c, context.getServletContext());
}
catch (Exception e)
{
throw new RuntimeException(e);
}
}
}
public static void configure(ServletContextHandler contextHandler)
{
JettyWebSocketServletContainerInitializer sci = new JettyWebSocketServletContainerInitializer();
JettyWebSocketEmbeddedStarter starter = new JettyWebSocketEmbeddedStarter(sci, contextHandler);
contextHandler.addBean(starter);
return container;
}
@Override
public void onStartup(Set<Class<?>> c, ServletContext servletContext) throws ServletException
public void onStartup(Set<Class<?>> c, ServletContext context) throws ServletException
{
WebSocketComponents components = WebSocketComponents.ensureWebSocketComponents(servletContext);
JettyServerFrameHandlerFactory factory = JettyServerFrameHandlerFactory.ensureFactory(servletContext);
if (LOG.isDebugEnabled())
LOG.debug("onStartup {} {}", components, factory);
ServletContextHandler contextHandler = ServletContextHandler.getServletContextHandler(context,"Jetty WebSocket SCI");
JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
}
}

View File

@ -1,99 +0,0 @@
//
// ========================================================================
// 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.server.internal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import org.eclipse.jetty.server.handler.ContextHandler;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.util.thread.QueuedThreadPool;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.common.SessionTracker;
import org.eclipse.jetty.websocket.common.WebSocketContainer;
import org.eclipse.jetty.websocket.common.WebSocketSessionListener;
public class JettyWebSocketServerContainer implements WebSocketContainer
{
private final static Logger LOG = Log.getLogger(JettyWebSocketServerContainer.class);
private final Executor executor;
private final List<WebSocketSessionListener> sessionListeners = new ArrayList<>();
private final SessionTracker sessionTracker = new SessionTracker();
public JettyWebSocketServerContainer(ContextHandler handler)
{
Executor executor = (Executor) handler
.getAttribute("org.eclipse.jetty.server.Executor");
if (executor == null)
{
executor = handler.getServer().getThreadPool();
}
if (executor == null)
{
executor = new QueuedThreadPool(); // default settings
}
this.executor = executor;
addSessionListener(sessionTracker);
handler.addBean(sessionTracker);
}
@Override
public Executor getExecutor()
{
return this.executor;
}
@Override
public void addSessionListener(WebSocketSessionListener listener)
{
sessionListeners.add(listener);
}
@Override
public boolean removeSessionListener(WebSocketSessionListener listener)
{
return sessionListeners.remove(listener);
}
@Override
public void notifySessionListeners(Consumer<WebSocketSessionListener> consumer)
{
for (WebSocketSessionListener listener : sessionListeners)
{
try
{
consumer.accept(listener);
}
catch (Throwable x)
{
LOG.info("Exception while invoking listener " + listener, x);
}
}
}
@Override
public Collection<Session> getOpenSessions()
{
return sessionTracker.getSessions();
}
}

View File

@ -101,7 +101,7 @@ public class BrowserDebugTool
ServletContextHandler context = new ServletContextHandler();
JettyWebSocketServletContainerInitializer.configure(context);
JettyWebSocketServletContainerInitializer.configureContext(context);
context.setContextPath("/");
Resource staticResourceBase = findStaticResources();

View File

@ -40,11 +40,8 @@ import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketFrame;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.api.extensions.Frame;
import org.eclipse.jetty.websocket.core.OpCode;
@WebSocket
public class BrowserSocket

View File

@ -29,7 +29,7 @@ import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
import org.eclipse.jetty.websocket.common.WebSocketSessionImpl;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.core.internal.WebSocketChannel;
import org.eclipse.jetty.websocket.core.internal.WebSocketConnection;
import org.hamcrest.Matcher;
@ -117,9 +117,9 @@ public class CloseTrackingEndpoint extends WebSocketAdapter
public EndPoint getEndPoint()
{
Session session = getSession();
assertThat("Session type", session, instanceOf(WebSocketSessionImpl.class));
assertThat("Session type", session, instanceOf(WebSocketSession.class));
WebSocketSessionImpl wsSession = (WebSocketSessionImpl) session;
WebSocketSession wsSession = (WebSocketSession) session;
WebSocketChannel wsChannel = (WebSocketChannel) wsSession.getCoreSession();
WebSocketConnection wsConnection = wsChannel.getConnection();

View File

@ -0,0 +1,97 @@
//
// ========================================================================
// 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.concurrent.BlockingQueue;
import java.util.concurrent.CountDownLatch;
import org.eclipse.jetty.util.BlockingArrayQueue;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
@WebSocket
public class EventSocket
{
private static Logger LOG = Log.getLogger(EventSocket.class);
protected Session session;
private String behavior;
public volatile Throwable failure = null;
public BlockingQueue<String> receivedMessages = new BlockingArrayQueue<>();
public CountDownLatch open = new CountDownLatch(1);
public CountDownLatch error = new CountDownLatch(1);
public CountDownLatch closed = new CountDownLatch(1);
@OnWebSocketConnect
public void onOpen(Session session)
{
this.session = session;
behavior = session.getPolicy().getBehavior().name();
LOG.info("{} onOpen(): {}", toString(), session);
open.countDown();
}
@OnWebSocketMessage
public void onMessage(String message) throws IOException
{
LOG.info("{} onMessage(): {}", toString(), message);
receivedMessages.offer(message);
}
@OnWebSocketClose
public void onClose(int statusCode, String reason)
{
LOG.info("{} onClose(): {}:{}", toString(), statusCode, reason);
closed.countDown();
}
@OnWebSocketError
public void onError(Throwable cause)
{
LOG.info("{} onError(): {}", toString(), cause);
failure = cause;
error.countDown();
}
@Override
public String toString()
{
return String.format("[%s@%s]", behavior, Integer.toHexString(hashCode()));
}
@WebSocket
public static class EchoSocket extends EventSocket
{
@Override
public void onMessage(String message) throws IOException
{
super.onMessage(message);
session.getRemote().sendStringByFuture(message);
}
}
}

View File

@ -0,0 +1,87 @@
//
// ========================================================================
// 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.net.URI;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.Server;
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.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 JettyWebSocketFilterTest
{
Server server;
WebSocketClient client;
@BeforeEach
public void start() throws Exception
{
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8080);
server.addConnector(connector);
ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
contextHandler.setContextPath("/");
server.setHandler(contextHandler);
JettyWebSocketServerContainer container = JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
container.addMapping("/", (req, resp)->new EventSocket.EchoSocket());
server.start();
client = new WebSocketClient();
client.start();
}
@AfterEach
public void stop() throws Exception
{
client.stop();
server.stop();
}
@Test
public void test() throws Exception
{
URI uri = URI.create("ws://localhost:8080/filterPath");
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.closed.await(10, TimeUnit.SECONDS));
String msg = socket.receivedMessages.poll();
assertThat(msg, is("hello world"));
}
}

View File

@ -0,0 +1,99 @@
//
// ========================================================================
// 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.net.URI;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.Server;
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.JettyWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
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 JettyWebSocketServletTest
{
public static class MyWebSocketServlet extends WebSocketServlet
{
@Override
public void configure(WebSocketServletFactory factory)
{
factory.addMapping("/",(req, resp)->new EventSocket.EchoSocket());
}
}
Server server;
WebSocketClient client;
@BeforeEach
public void start() throws Exception
{
server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8080);
server.addConnector(connector);
ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
contextHandler.setContextPath("/");
server.setHandler(contextHandler);
contextHandler.addServlet(MyWebSocketServlet.class, "/servletPath");
JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
server.start();
client = new WebSocketClient();
client.start();
}
@AfterEach
public void stop() throws Exception
{
client.stop();
server.stop();
}
@Test
public void test() throws Exception
{
URI uri = URI.create("ws://localhost:8080/servletPath");
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.closed.await(10, TimeUnit.SECONDS));
String msg = socket.receivedMessages.poll();
assertThat(msg, is("hello world"));
}
}

View File

@ -1,146 +0,0 @@
//
// ========================================================================
// 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.net.URI;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.server.ServerConnector;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketMessage;
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class JettyWebSocketTest
{
@WebSocket
public static class EventSocket
{
CountDownLatch closed = new CountDownLatch(1);
String behavior;
@OnWebSocketConnect
public void onOpen(Session sess)
{
behavior = sess.getPolicy().getBehavior().name();
System.err.println(toString() + " Socket Connected: " + sess);
}
@OnWebSocketMessage
public void onMessage(String message)
{
System.err.println(toString() + " Received TEXT message: " + message);
}
@OnWebSocketClose
public void onClose(int statusCode, String reason)
{
System.err.println(toString() + " Socket Closed: " + statusCode + ":" + reason);
closed.countDown();
}
@OnWebSocketError
public void onError(Throwable cause)
{
cause.printStackTrace(System.err);
}
@Override
public String toString()
{
return String.format("[%s@%s]", behavior, Integer.toHexString(hashCode()));
}
}
public static class MyWebSocketServlet extends WebSocketServlet
{
@Override
public void configure(WebSocketServletFactory factory)
{
factory.addMapping("/",(req, resp)->new EventSocket());
}
}
@Test
public void test() throws Exception
{
Server server = new Server();
ServerConnector connector = new ServerConnector(server);
connector.setPort(8080);
server.addConnector(connector);
ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
contextHandler.setContextPath("/");
server.setHandler(contextHandler);
contextHandler.addServlet(MyWebSocketServlet.class, "/testPath1");
contextHandler.addServlet(MyWebSocketServlet.class, "/testPath2");
try
{
JettyWebSocketServletContainerInitializer.configure(contextHandler);
server.start();
WebSocketClient client = new WebSocketClient();
client.start();
URI uri = URI.create("ws://localhost:8080/testPath1");
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.closed.await(10, TimeUnit.SECONDS));
uri = URI.create("ws://localhost:8080/testPath2");
socket = new EventSocket();
connect = client.connect(socket, uri);
try(Session session = connect.get(5, TimeUnit.SECONDS))
{
session.getRemote().sendString("hello world");
}
assertTrue(socket.closed.await(10, TimeUnit.SECONDS));
server.stop();
}
catch (Throwable t)
{
t.printStackTrace();
}
}
}

View File

@ -115,7 +115,7 @@ public class WebSocketServletExamplesTest
_context.addServlet(MyAdvancedEchoServlet.class, "/advancedEcho");
_context.addServlet(MyAuthedServlet.class, "/authed");
JettyWebSocketServletContainerInitializer.configure(_context);
JettyWebSocketServletContainerInitializer.configureContext(_context);
_server.start();
}

View File

@ -147,7 +147,7 @@ public class WebSocketStatsTest
contextHandler.addServlet(MyWebSocketServlet.class, "/testPath");
server.setHandler(contextHandler);
JettyWebSocketServletContainerInitializer.configure(contextHandler);
JettyWebSocketServletContainerInitializer.configureContext(contextHandler);
client = new WebSocketClient();
server.start();

View File

@ -74,7 +74,6 @@ public class BadNetworkTest
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
JettyWebSocketServletContainerInitializer.configure(context);
context.setContextPath("/");
ServletHolder holder = new ServletHolder(new WebSocketServlet()
{
@ -92,6 +91,7 @@ public class BadNetworkTest
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
JettyWebSocketServletContainerInitializer.configureContext(context);
server.start();
}

View File

@ -38,6 +38,8 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
import org.eclipse.jetty.websocket.api.CloseException;
import org.eclipse.jetty.websocket.api.Frame;
import org.eclipse.jetty.websocket.api.MessageTooLargeException;
import org.eclipse.jetty.websocket.api.Session;
import org.eclipse.jetty.websocket.api.StatusCode;
@ -118,7 +120,6 @@ public class ClientCloseTest
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
JettyWebSocketServletContainerInitializer.configure(context);
context.setContextPath("/");
ServletHolder holder = new ServletHolder(new WebSocketServlet()
{
@ -136,6 +137,7 @@ public class ClientCloseTest
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
JettyWebSocketServletContainerInitializer.configureContext(context);
server.start();
}
@ -427,7 +429,7 @@ public class ClientCloseTest
}
@Override
public void onWebSocketFrame(org.eclipse.jetty.websocket.api.extensions.Frame frame)
public void onWebSocketFrame(Frame frame)
{
if (frame.getOpCode() == OpCode.CLOSE)
{

View File

@ -22,7 +22,7 @@ import java.util.concurrent.CountDownLatch;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.websocket.client.WebSocketClient;
import org.eclipse.jetty.websocket.common.WebSocketSessionImpl;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.WebSocketSessionListener;
import static java.util.concurrent.TimeUnit.SECONDS;
@ -64,12 +64,12 @@ public class ClientOpenSessionTracker implements Connection.Listener, WebSocketS
}
@Override
public void onWebSocketSessionOpened(WebSocketSessionImpl session)
public void onWebSocketSessionOpened(WebSocketSession session)
{
}
@Override
public void onWebSocketSessionClosed(WebSocketSessionImpl session)
public void onWebSocketSessionClosed(WebSocketSession session)
{
this.closeSessionLatch.countDown();
}

View File

@ -37,7 +37,7 @@ import org.eclipse.jetty.websocket.api.StatusCode;
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.common.WebSocketSessionImpl;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.common.WebSocketSessionListener;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
@ -68,7 +68,6 @@ public class ClientSessionsTest
server.addConnector(connector);
ServletContextHandler context = new ServletContextHandler();
JettyWebSocketServletContainerInitializer.configure(context);
context.setContextPath("/");
ServletHolder holder = new ServletHolder(new WebSocketServlet()
{
@ -86,6 +85,7 @@ public class ClientSessionsTest
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
JettyWebSocketServletContainerInitializer.configureContext(context);
server.start();
}
@ -105,12 +105,12 @@ public class ClientSessionsTest
client.addSessionListener(new WebSocketSessionListener() {
@Override
public void onWebSocketSessionOpened(WebSocketSessionImpl session)
public void onWebSocketSessionOpened(WebSocketSession session)
{
}
@Override
public void onWebSocketSessionClosed(WebSocketSessionImpl session)
public void onWebSocketSessionClosed(WebSocketSession session)
{
onSessionCloseLatch.countDown();
}

View File

@ -81,13 +81,13 @@ public class SlowClientTest
}
});
context.addServlet(websocket, "/ws");
JettyWebSocketServletContainerInitializer.configure(context);
HandlerList handlers = new HandlerList();
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
JettyWebSocketServletContainerInitializer.configureContext(context);
server.start();
}

View File

@ -56,6 +56,7 @@ public abstract class AbstractCloseEndpoint extends WebSocketAdapter
@Override
public void onWebSocketError(Throwable cause)
{
LOG.debug("onWebSocketError({})", cause.getClass().getSimpleName());
errors.offer(cause);
}

View File

@ -35,7 +35,7 @@ import org.eclipse.jetty.websocket.api.StatusCode;
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.common.WebSocketSessionImpl;
import org.eclipse.jetty.websocket.common.WebSocketSession;
import org.eclipse.jetty.websocket.core.internal.WebSocketChannel;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletContainerInitializer;
import org.eclipse.jetty.websocket.servlet.WebSocketServlet;
@ -84,13 +84,13 @@ public class ServerCloseTest
}
});
context.addServlet(closeEndpoint, "/ws");
JettyWebSocketServletContainerInitializer.configure(context);
HandlerList handlers = new HandlerList();
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
JettyWebSocketServletContainerInitializer.configureContext(context);
server.start();
}
@ -212,7 +212,7 @@ public class ServerCloseTest
Future<Session> futSession = client.connect(clientEndpoint, wsUri, request);
Session session = null;
try(StacklessLogging ignore = new StacklessLogging(WebSocketSessionImpl.class))
try(StacklessLogging ignore = new StacklessLogging(WebSocketSession.class))
{
session = futSession.get(5, SECONDS);
@ -241,9 +241,9 @@ public class ServerCloseTest
@Test
public void testOpenSessionCleanup() throws Exception
{
fastFail();
fastClose();
dropConnection();
//fastFail();
//fastClose();
//dropConnection();
ClientUpgradeRequest request = new ClientUpgradeRequest();
request.setSubProtocols("container");
@ -253,7 +253,7 @@ public class ServerCloseTest
Future<Session> futSession = client.connect(clientEndpoint, wsUri, request);
Session session = null;
try(StacklessLogging ignore = new StacklessLogging(WebSocketSessionImpl.class))
try(StacklessLogging ignore = new StacklessLogging(WebSocketSession.class))
{
session = futSession.get(5, SECONDS);

View File

@ -81,13 +81,13 @@ public class SlowServerTest
}
});
context.addServlet(websocket, "/ws");
JettyWebSocketServletContainerInitializer.configure(context);
HandlerList handlers = new HandlerList();
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler());
server.setHandler(handlers);
JettyWebSocketServletContainerInitializer.configureContext(context);
server.start();
}

View File

@ -20,6 +20,7 @@
# org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.Slf4jLog
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
org.eclipse.jetty.LEVEL=WARN
org.eclipse.jetty.websocket.tests.LEVEL=DEBUG
# org.eclipse.jetty.util.log.stderr.LONG=true
# org.eclipse.jetty.server.AbstractConnector.LEVEL=DEBUG
# org.eclipse.jetty.io.WriteFlusher.LEVEL=DEBUG

View File

@ -61,11 +61,11 @@ public class WebSocketMapping implements Dumpable, LifeCycle.Listener
{
private static final Logger LOG = Log.getLogger(WebSocketMapping.class);
public static WebSocketMapping ensureMapping(ServletContext servletContext, String mappingKey)
public static WebSocketMapping getMapping(ServletContext servletContext, String mappingKey)
{
ContextHandler contextHandler = ContextHandler.getContextHandler(servletContext);
Object mappingObject = contextHandler.getAttribute(mappingKey);
if (mappingObject!=null)
{
if (WebSocketMapping.class.isInstance(mappingObject))
@ -73,104 +73,24 @@ public class WebSocketMapping implements Dumpable, LifeCycle.Listener
else
throw new IllegalStateException(
String.format("ContextHandler attribute %s is not of type WebSocketMapping: {%s}",
mappingKey, mappingObject.toString()));
}
else
{
WebSocketMapping mapping = new WebSocketMapping(WebSocketComponents.ensureWebSocketComponents(servletContext));
contextHandler.setAttribute(mappingKey, mapping);
return mapping;
mappingKey, mappingObject.toString()));
}
return null;
}
public static final String DEFAULT_KEY = "org.eclipse.jetty.websocket.servlet.WebSocketMapping";
private final PathMappings<Negotiator> mappings = new PathMappings<>();
private final WebSocketComponents components;
private final Handshaker handshaker = Handshaker.newInstance();
public WebSocketMapping()
public static WebSocketMapping ensureMapping(ServletContext servletContext, String mappingKey)
{
this(new WebSocketComponents());
}
ContextHandler contextHandler = ContextHandler.getContextHandler(servletContext);
WebSocketMapping mapping = getMapping(servletContext, mappingKey);
public WebSocketMapping(WebSocketComponents components)
{
this.components = components;
}
@Override
public void lifeCycleStopping(LifeCycle context)
{
ContextHandler contextHandler = (ContextHandler) context;
WebSocketMapping mapping = contextHandler.getBean(WebSocketMapping.class);
if (mapping == this)
{
contextHandler.removeBean(mapping);
mappings.reset();
}
}
/**
* Manually add a WebSocket mapping.
* <p>
* If mapping is added before this configuration is started, then it is persisted through
* stop/start of this configuration's lifecycle. Otherwise it will be removed when
* this configuration is stopped.
* </p>
*
* @param pathSpec the pathspec to respond on
* @param creator the websocket creator to activate on the provided mapping.
* @param factory the factory to use to create a FrameHandler for the websocket
* @param customizer the customizer to use to customize the WebSocket session.
*/
public void addMapping(PathSpec pathSpec, WebSocketCreator creator, FrameHandlerFactory factory, FrameHandler.Customizer customizer)
throws WebSocketException
{
// TODO evaluate why this can't be done
//if (getMapping(pathSpec) != null)
// throw new WebSocketException("Duplicate WebSocket Mapping for PathSpec");
mappings.put(pathSpec, new Negotiator(creator, factory, customizer));
}
public WebSocketCreator getMapping(PathSpec pathSpec)
{
Negotiator cn = mappings.get(pathSpec);
return cn == null?null:cn.getWebSocketCreator();
}
public boolean removeMapping(PathSpec pathSpec)
{
return mappings.remove(pathSpec);
}
@Override
public String dump()
{
return Dumpable.dump(this);
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
Dumpable.dumpObjects(out, indent, this, mappings);
}
/**
* Get the matching {@link MappedResource} for the provided target.
*
* @param target the target path
* @return the matching resource, or null if no match.
*/
public WebSocketNegotiator getMatchedNegotiator(String target, Consumer<PathSpec> pathSpecConsumer)
{
MappedResource<Negotiator> mapping = this.mappings.getMatch(target);
if (mapping == null)
return null;
{
mapping = new WebSocketMapping(WebSocketComponents.ensureWebSocketComponents(servletContext));
contextHandler.setAttribute(mappingKey, mapping);
}
pathSpecConsumer.accept(mapping.getPathSpec());
return mapping.getResource();
return mapping;
}
/**
@ -212,6 +132,91 @@ public class WebSocketMapping implements Dumpable, LifeCycle.Listener
throw new IllegalArgumentException("Unrecognized path spec syntax [" + rawSpec + "]");
}
public static final String DEFAULT_KEY = "org.eclipse.jetty.websocket.servlet.WebSocketMapping";
private final PathMappings<Negotiator> mappings = new PathMappings<>();
private final WebSocketComponents components;
private final Handshaker handshaker = Handshaker.newInstance();
public WebSocketMapping()
{
this(new WebSocketComponents());
}
public WebSocketMapping(WebSocketComponents components)
{
this.components = components;
}
@Override
public void lifeCycleStopping(LifeCycle context)
{
ContextHandler contextHandler = (ContextHandler) context;
WebSocketMapping mapping = contextHandler.getBean(WebSocketMapping.class);
if (mapping == this)
{
contextHandler.removeBean(mapping);
mappings.reset();
}
}
@Override
public String dump()
{
return Dumpable.dump(this);
}
@Override
public void dump(Appendable out, String indent) throws IOException
{
Dumpable.dumpObjects(out, indent, this, mappings);
}
/**
* Manually add a WebSocket mapping.
* <p>
* If mapping is added before this configuration is started, then it is persisted through
* stop/start of this configuration's lifecycle. Otherwise it will be removed when
* this configuration is stopped.
* </p>
*
* @param pathSpec the pathspec to respond on
* @param creator the websocket creator to activate on the provided mapping.
* @param factory the factory to use to create a FrameHandler for the websocket
* @param customizer the customizer to use to customize the WebSocket session.
*/
public void addMapping(PathSpec pathSpec, WebSocketCreator creator, FrameHandlerFactory factory, FrameHandler.Customizer customizer) throws WebSocketException
{
mappings.put(pathSpec, new Negotiator(creator, factory, customizer));
}
public WebSocketCreator getMapping(PathSpec pathSpec)
{
Negotiator cn = mappings.get(pathSpec);
return cn == null?null:cn.getWebSocketCreator();
}
public boolean removeMapping(PathSpec pathSpec)
{
return mappings.remove(pathSpec);
}
/**
* Get the matching {@link MappedResource} for the provided target.
*
* @param target the target path
* @return the matching resource, or null if no match.
*/
public WebSocketNegotiator getMatchedNegotiator(String target, Consumer<PathSpec> pathSpecConsumer)
{
MappedResource<Negotiator> mapping = this.mappings.getMatch(target);
if (mapping == null)
return null;
pathSpecConsumer.accept(mapping.getPathSpec());
return mapping.getResource();
}
public boolean upgrade(HttpServletRequest request, HttpServletResponse response, FrameHandler.Customizer defaultCustomizer)
{
try
@ -241,9 +246,7 @@ public class WebSocketMapping implements Dumpable, LifeCycle.Listener
}
catch (Exception e)
{
if (LOG.isDebugEnabled())
LOG.debug("Unable to upgrade: "+e);
LOG.ignore(e);
LOG.warn("Error during upgrade: ", e);
}
return false;
}

View File

@ -189,9 +189,10 @@ public abstract class WebSocketServlet extends HttpServlet
@Override
public void addMapping(PathSpec pathSpec, WebSocketCreator creator)
{
// TODO a bit fragile. This code knows that only the JettyFHF is added directly as a been
ServletContext servletContext = getServletContext();
ContextHandler contextHandler = ServletContextHandler.getServletContextHandler(servletContext, "WebSocketServlet");
// TODO: a bit fragile, this code knows that only the JettyFHF is added as a bean
FrameHandlerFactory frameHandlerFactory = contextHandler.getBean(FrameHandlerFactory.class);
if (frameHandlerFactory==null)

View File

@ -21,39 +21,54 @@ package org.eclipse.jetty.websocket.servlet;
import java.time.Duration;
import org.eclipse.jetty.http.pathmap.PathSpec;
import org.eclipse.jetty.websocket.core.FrameHandler;
import org.eclipse.jetty.websocket.core.WebSocketExtensionRegistry;
public interface WebSocketServletFactory
public interface WebSocketServletFactory extends FrameHandler.Configuration
{
WebSocketExtensionRegistry getExtensionRegistry();
@Override
Duration getIdleTimeout();
@Override
void setIdleTimeout(Duration duration);
@Override
int getInputBufferSize();
@Override
void setInputBufferSize(int bufferSize);
@Override
long getMaxFrameSize();
@Override
void setMaxFrameSize(long maxFrameSize);
@Override
long getMaxBinaryMessageSize();
@Override
void setMaxBinaryMessageSize(long bufferSize);
@Override
long getMaxTextMessageSize();
@Override
void setMaxTextMessageSize(long bufferSize);
@Override
int getOutputBufferSize();
@Override
void setOutputBufferSize(int bufferSize);
@Override
boolean isAutoFragment();
@Override
void setAutoFragment(boolean autoFragment);
void addMapping(String pathSpec, WebSocketCreator creator);

View File

@ -21,6 +21,7 @@ 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;
@ -82,7 +83,7 @@ public class WebSocketUpgradeFilter implements Filter, Dumpable
for (FilterHolder holder : servletHandler.getFilters())
{
if (holder.getInitParameter(MAPPING_INIT_PARAM) != null)
if (holder.getInitParameter(MAPPING_ATTRIBUTE_INIT_PARAM) != null)
return holder;
}
@ -100,7 +101,7 @@ public class WebSocketUpgradeFilter implements Filter, Dumpable
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
FilterHolder holder = new FilterHolder(new WebSocketUpgradeFilter());
holder.setName(name);
holder.setInitParameter(MAPPING_INIT_PARAM, WebSocketMapping.DEFAULT_KEY);
holder.setInitParameter(MAPPING_ATTRIBUTE_INIT_PARAM, WebSocketMapping.DEFAULT_KEY);
holder.setAsyncSupported(true);
ServletHandler servletHandler = ContextHandler.getContextHandler(servletContext).getChildHandlerByClass(ServletHandler.class);
@ -110,7 +111,7 @@ public class WebSocketUpgradeFilter implements Filter, Dumpable
return holder;
}
public final static String MAPPING_INIT_PARAM = "org.eclipse.jetty.websocket.servlet.WebSocketMapping.key";
public final static String MAPPING_ATTRIBUTE_INIT_PARAM = "org.eclipse.jetty.websocket.servlet.WebSocketMapping.key";
private final FrameHandler.ConfigurationCustomizer defaultCustomizer = new FrameHandler.ConfigurationCustomizer();
private WebSocketMapping mapping;
@ -157,7 +158,7 @@ public class WebSocketUpgradeFilter implements Filter, Dumpable
{
final ServletContext context = config.getServletContext();
String mappingKey = config.getInitParameter(MAPPING_INIT_PARAM);
String mappingKey = config.getInitParameter(MAPPING_ATTRIBUTE_INIT_PARAM);
if (mappingKey != null)
mapping = WebSocketMapping.ensureMapping(context, mappingKey);
else
@ -193,4 +194,9 @@ public class WebSocketUpgradeFilter implements Filter, Dumpable
if (autoFragment != null)
defaultCustomizer.setAutoFragment(Boolean.parseBoolean(autoFragment));
}
@Override
public void destroy()
{
}
}

View File

@ -27,6 +27,7 @@
<jetty-test-policy.version>1.2</jetty-test-policy.version>
<servlet.api.version>4.0.2</servlet.api.version>
<jsp.version>9.0.14.1</jsp.version>
<infinispan.version>9.4.8.Final</infinispan.version>
<!-- default values are unsupported, but required to be defined for reactor sanity reasons -->
<alpn.version>undefined</alpn.version>
<conscrypt.version>1.4.1</conscrypt.version>
@ -1031,6 +1032,11 @@
<artifactId>slf4j-simple</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.3.2.Final</version>
</dependency>
<dependency>
<groupId>com.github.jnr</groupId>
<artifactId>jnr-unixsocket</artifactId>

View File

@ -106,19 +106,19 @@
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
<version>9.1.0.Final</version>
<version>${infinispan.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-client-hotrod</artifactId>
<version>9.1.0.Final</version>
<version>${infinispan.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-remote-query-client</artifactId>
<version>9.1.0.Final</version>
<version>${infinispan.version}</version>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -22,9 +22,12 @@ package org.eclipse.jetty.server.session;
import org.eclipse.jetty.session.infinispan.InfinispanSessionDataStoreFactory;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@Disabled("Disabled due to Infinispan not supporting JDK 11, see eclipse/jetty.project#3024")
/**
* ClusteredSessionScavengingTest
*
*/
public class ClusteredSessionScavengingTest extends AbstractClusteredSessionScavengingTest
{
public InfinispanTestSupport _testSupport;
@ -44,6 +47,18 @@ public class ClusteredSessionScavengingTest extends AbstractClusteredSessionScav
_testSupport.teardown();
}
@Override
@Test
public void testClusteredScavenge()
throws Exception
{
super.testClusteredScavenge();
}
/**
* @see org.eclipse.jetty.server.session.AbstractTestBase#createSessionDataStoreFactory()
*/
@Override
public SessionDataStoreFactory createSessionDataStoreFactory()
{