Issue #1090 - Delaying role of WebSocketUpgradeFilter till .init()
+ WebSocketServerContainerInitializer now initializes the MappedWebSocketCreator, WebSocketServerFactory, and ServerContainer directly, but puts the components needed by WebSocketUpgradeFilter into ServletContext attributes that it can obtain in its own init() + Added optional org.eclipse.jetty.websocket.jsr356.addDynamicFilter init-param/attribute key to disable/enable the automatic addition of the WebSocketUpgradeFilter during JSR356 init (to allow WEB-INF/web.xml specified filter to operate instead) + Added WebSocketServerContainerINitializer.isEnabledViaContext() to make testing the init-param and attributes more generic
This commit is contained in:
parent
2295bd59e3
commit
e6b3216708
|
@ -20,6 +20,7 @@ package org.eclipse.jetty.websocket.jsr356.server.deploy;
|
|||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletContainerInitializer;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
|
@ -30,12 +31,18 @@ import javax.websocket.server.ServerApplicationConfig;
|
|||
import javax.websocket.server.ServerEndpoint;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.ServerContainer;
|
||||
import org.eclipse.jetty.websocket.server.DefaultMappedWebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
|
||||
import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
|
||||
|
||||
@HandlesTypes(
|
||||
|
@ -43,28 +50,87 @@ import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
|
|||
public class WebSocketServerContainerInitializer implements ServletContainerInitializer
|
||||
{
|
||||
public static final String ENABLE_KEY = "org.eclipse.jetty.websocket.jsr356";
|
||||
public static final String ADD_DYNAMIC_FILTER_KEY = "org.eclipse.jetty.websocket.jsr356.addDynamicFilter";
|
||||
private static final Logger LOG = Log.getLogger(WebSocketServerContainerInitializer.class);
|
||||
|
||||
|
||||
/**
|
||||
* Jetty Native approach.
|
||||
* <p>
|
||||
* Note: this will add the Upgrade filter to the existing list, with no regard for order. It will just be tacked onto the end of the list.
|
||||
* Test a ServletContext for {@code init-param} or {@code attribute} at {@code keyName} for
|
||||
* true or false setting that determines if the specified feature is enabled (or not).
|
||||
*
|
||||
* @param context the context to search
|
||||
* @param keyName the key name
|
||||
* @param defValue the default value, if the value is not specified in the context
|
||||
* @return the value for the feature key
|
||||
*/
|
||||
public static boolean isEnabledViaContext(ServletContext context, String keyName, boolean defValue)
|
||||
{
|
||||
// Try context parameters first
|
||||
String cp = context.getInitParameter(keyName);
|
||||
|
||||
if(cp != null)
|
||||
{
|
||||
if (TypeUtil.isTrue(cp))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TypeUtil.isFalse(cp))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return defValue;
|
||||
}
|
||||
|
||||
// Next, try attribute on context
|
||||
Object enable = context.getAttribute(ENABLE_KEY);
|
||||
|
||||
if(enable != null)
|
||||
{
|
||||
if (TypeUtil.isTrue(enable))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TypeUtil.isFalse(enable))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return defValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Embedded Jetty approach for non-bytecode scanning.
|
||||
*/
|
||||
public static ServerContainer configureContext(ServletContextHandler context) throws ServletException
|
||||
{
|
||||
// Create Filter
|
||||
WebSocketUpgradeFilter filter = WebSocketUpgradeFilter.configureContext(context);
|
||||
// Create Basic components
|
||||
WebSocketPolicy policy = WebSocketPolicy.newServerPolicy();
|
||||
ByteBufferPool bufferPool = new MappedByteBufferPool();
|
||||
MappedWebSocketCreator creator = new DefaultMappedWebSocketCreator();
|
||||
WebSocketServerFactory factory = new WebSocketServerFactory(policy, bufferPool);
|
||||
|
||||
// Create the Jetty ServerContainer implementation
|
||||
ServerContainer jettyContainer = new ServerContainer(filter,filter.getFactory(),context.getServer().getThreadPool());
|
||||
ServerContainer jettyContainer = new ServerContainer(creator,factory,context.getServer().getThreadPool());
|
||||
context.addBean(jettyContainer);
|
||||
|
||||
context.setAttribute(WebSocketUpgradeFilter.CREATOR_KEY, creator);
|
||||
context.setAttribute(WebSocketUpgradeFilter.FACTORY_KEY, factory);
|
||||
|
||||
// Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
|
||||
context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer);
|
||||
|
||||
|
||||
// Create Filter
|
||||
if(isEnabledViaContext(context.getServletContext(), ADD_DYNAMIC_FILTER_KEY, true))
|
||||
{
|
||||
WebSocketUpgradeFilter.configureContext(context);
|
||||
}
|
||||
|
||||
return jettyContainer;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #configureContext(ServletContextHandler)} instead
|
||||
*/
|
||||
|
@ -74,56 +140,10 @@ public class WebSocketServerContainerInitializer implements ServletContainerInit
|
|||
return configureContext(jettyContext);
|
||||
}
|
||||
|
||||
private boolean isEnabled(Set<Class<?>> c, ServletContext context)
|
||||
{
|
||||
// Try context parameters first
|
||||
String cp = context.getInitParameter(ENABLE_KEY);
|
||||
if(TypeUtil.isTrue(cp))
|
||||
{
|
||||
// forced on
|
||||
return true;
|
||||
}
|
||||
|
||||
if(TypeUtil.isFalse(cp))
|
||||
{
|
||||
// forced off
|
||||
LOG.warn("JSR-356 support disabled via parameter on context {} - {}",context.getContextPath(),context);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Next, try attribute on context
|
||||
Object enable = context.getAttribute(ENABLE_KEY);
|
||||
|
||||
if(TypeUtil.isTrue(enable))
|
||||
{
|
||||
// forced on
|
||||
return true;
|
||||
}
|
||||
|
||||
if (TypeUtil.isFalse(enable))
|
||||
{
|
||||
// forced off
|
||||
LOG.warn("JSR-356 support disabled via attribute on context {} - {}",context.getContextPath(),context);
|
||||
return false;
|
||||
}
|
||||
|
||||
// if not forced on or off, determine behavior based on annotations.
|
||||
if (c.isEmpty())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("No JSR-356 annotations or interfaces discovered. JSR-356 support disabled",context.getContextPath(),context);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext context) throws ServletException
|
||||
{
|
||||
if(!isEnabled(c,context))
|
||||
if(!isEnabledViaContext(context, ENABLE_KEY, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -146,18 +166,27 @@ public class WebSocketServerContainerInitializer implements ServletContainerInit
|
|||
try
|
||||
{
|
||||
Thread.currentThread().setContextClassLoader(context.getClassLoader());
|
||||
|
||||
|
||||
// Create the Jetty ServerContainer implementation
|
||||
ServerContainer jettyContainer = configureContext(jettyContext);
|
||||
|
||||
// Store a reference to the ServerContainer per javax.websocket spec 1.0 final section 6.4 Programmatic Server Deployment
|
||||
context.setAttribute(javax.websocket.server.ServerContainer.class.getName(),jettyContainer);
|
||||
|
||||
if (c.isEmpty())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("No JSR-356 annotations or interfaces discovered");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Found {} classes",c.size());
|
||||
}
|
||||
|
||||
|
||||
// Now process the incoming classes
|
||||
Set<Class<? extends Endpoint>> discoveredExtendedEndpoints = new HashSet<>();
|
||||
Set<Class<?>> discoveredAnnotatedEndpoints = new HashSet<>();
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 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.jsr356.server;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.notNullValue;
|
||||
import static org.hamcrest.CoreMatchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.test.LeakTrackingBufferPoolRule;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoSocket;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Testing the use of an alternate {@link org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter}
|
||||
* defined in the WEB-INF/web.xml
|
||||
*/
|
||||
public class AltFilterTest
|
||||
{
|
||||
@Rule
|
||||
public TestingDir testdir = new TestingDir();
|
||||
|
||||
@Rule
|
||||
public LeakTrackingBufferPoolRule bufferPool = new LeakTrackingBufferPoolRule("Test");
|
||||
|
||||
@Test
|
||||
public void testEcho() throws Exception
|
||||
{
|
||||
WSServer wsb = new WSServer(testdir,"app");
|
||||
wsb.copyWebInf("alt-filter-web.xml");
|
||||
// the endpoint (extends javax.websocket.Endpoint)
|
||||
wsb.copyClass(BasicEchoSocket.class);
|
||||
|
||||
try
|
||||
{
|
||||
wsb.start();
|
||||
URI uri = wsb.getServerBaseURI();
|
||||
|
||||
WebAppContext webapp = wsb.createWebAppContext();
|
||||
wsb.deployWebapp(webapp);
|
||||
|
||||
FilterHolder filterWebXml = webapp.getServletHandler().getFilter("wsuf-test");
|
||||
assertThat("Filter[wsuf-test]", filterWebXml, notNullValue());
|
||||
|
||||
FilterHolder filterSCI = webapp.getServletHandler().getFilter("Jetty_WebSocketUpgradeFilter");
|
||||
assertThat("Filter[Jetty_WebSocketUpgradeFilter]", filterSCI, nullValue());
|
||||
|
||||
WebSocketClient client = new WebSocketClient(bufferPool);
|
||||
try
|
||||
{
|
||||
client.start();
|
||||
JettyEchoSocket clientEcho = new JettyEchoSocket();
|
||||
Future<Session> future = client.connect(clientEcho,uri.resolve("echo"));
|
||||
// wait for connect
|
||||
future.get(1,TimeUnit.SECONDS);
|
||||
clientEcho.sendMessage("Hello Echo");
|
||||
Queue<String> msgs = clientEcho.awaitMessages(1);
|
||||
Assert.assertEquals("Expected message","Hello Echo",msgs.poll());
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
wsb.stop();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -58,21 +58,18 @@ public class ExtensionStackProcessingTest
|
|||
{
|
||||
private Server server;
|
||||
private ServerConnector connector;
|
||||
private ExtensionFactory serverExtensionFactory;
|
||||
private WebSocketContainer client;
|
||||
|
||||
private ServletContextHandler servletContextHandler;
|
||||
|
||||
@Before
|
||||
public void prepare() throws Exception
|
||||
{
|
||||
server = new Server();
|
||||
connector = new ServerConnector(server);
|
||||
server.addConnector(connector);
|
||||
|
||||
ServletContextHandler context = new ServletContextHandler(server, "/", true, false);
|
||||
ServerContainer container = WebSocketServerContainerInitializer.configureContext(context);
|
||||
|
||||
WebSocketUpgradeFilter filter = (WebSocketUpgradeFilter)context.getAttribute(WebSocketUpgradeFilter.class.getName());
|
||||
serverExtensionFactory = filter.getFactory().getExtensionFactory();
|
||||
|
||||
servletContextHandler = new ServletContextHandler(server, "/", true, false);
|
||||
ServerContainer container = WebSocketServerContainerInitializer.configureContext(servletContextHandler);
|
||||
|
||||
ServerEndpointConfig config = ServerEndpointConfig.Builder.create(BasicEchoEndpoint.class, "/").build();
|
||||
container.addEndpoint(config);
|
||||
|
@ -88,11 +85,18 @@ public class ExtensionStackProcessingTest
|
|||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
private void assumeDeflateFrameAvailable()
|
||||
{
|
||||
WebSocketUpgradeFilter filter = (WebSocketUpgradeFilter)servletContextHandler.getAttribute(WebSocketUpgradeFilter.class.getName());
|
||||
ExtensionFactory serverExtensionFactory = filter.getFactory().getExtensionFactory();
|
||||
Assume.assumeTrue("Server has permessage-deflate extension registered",serverExtensionFactory.isAvailable("permessage-deflate"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeflateFrameExtension() throws Exception
|
||||
{
|
||||
Assume.assumeTrue("Server has deflate-frame extension registered",serverExtensionFactory.isAvailable("deflate-frame"));
|
||||
assumeDeflateFrameAvailable();
|
||||
|
||||
ClientEndpointConfig config = ClientEndpointConfig.Builder.create()
|
||||
.extensions(Arrays.<Extension>asList(new JsrExtension("deflate-frame")))
|
||||
|
@ -140,7 +144,7 @@ public class ExtensionStackProcessingTest
|
|||
@Test
|
||||
public void testPerMessageDeflateExtension() throws Exception
|
||||
{
|
||||
Assume.assumeTrue("Server has permessage-deflate extension registered",serverExtensionFactory.isAvailable("permessage-deflate"));
|
||||
assumeDeflateFrameAvailable();
|
||||
|
||||
ClientEndpointConfig config = ClientEndpointConfig.Builder.create()
|
||||
.extensions(Arrays.<Extension>asList(new JsrExtension("permessage-deflate")))
|
||||
|
|
|
@ -18,6 +18,8 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.jsr356.server;
|
||||
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
|
@ -47,8 +49,6 @@ import org.eclipse.jetty.webapp.WebInfConfiguration;
|
|||
import org.eclipse.jetty.webapp.WebXmlConfiguration;
|
||||
import org.junit.Assert;
|
||||
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
/**
|
||||
* Utility to build out exploded directory WebApps, in the /target/tests/ directory, for testing out servers that use javax.websocket endpoints.
|
||||
* <p>
|
||||
|
@ -151,6 +151,11 @@ public class WSServer
|
|||
{
|
||||
return serverUri;
|
||||
}
|
||||
|
||||
public Server getServer()
|
||||
{
|
||||
return server;
|
||||
}
|
||||
|
||||
public File getWebAppDir()
|
||||
{
|
||||
|
|
|
@ -25,7 +25,7 @@ import javax.websocket.server.ServerContainer;
|
|||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
/**
|
||||
* Example of adding a server WebSocket (extending {@link Endpoint}) programmatically via config
|
||||
* Example of adding a server WebSocket (extending {@link javax.websocket.Endpoint}) programmatically via config
|
||||
*/
|
||||
public class BasicEchoEndpointConfigContextListener implements ServletContextListener
|
||||
{
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app
|
||||
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
|
||||
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
|
||||
version="3.1">
|
||||
|
||||
<context-param>
|
||||
<param-name>org.eclipse.jetty.websocket.jsr356.addDynamicFilter</param-name>
|
||||
<param-value>false</param-value>
|
||||
</context-param>
|
||||
|
||||
<filter>
|
||||
<filter-name>wsuf-test</filter-name>
|
||||
<filter-class>org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter</filter-class>
|
||||
</filter>
|
||||
|
||||
<filter-mapping>
|
||||
<filter-name>wsuf-test</filter-name>
|
||||
<url-pattern>/echo/*</url-pattern>
|
||||
</filter-mapping>
|
||||
</web-app>
|
|
@ -0,0 +1,40 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2016 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 org.eclipse.jetty.websocket.server.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
||||
public class DefaultMappedWebSocketCreator implements MappedWebSocketCreator
|
||||
{
|
||||
private final PathMappings<WebSocketCreator> mappings = new PathMappings<>();
|
||||
|
||||
@Override
|
||||
public void addMapping(PathSpec spec, WebSocketCreator creator)
|
||||
{
|
||||
this.mappings.put(spec, creator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathMappings<WebSocketCreator> getMappings()
|
||||
{
|
||||
return this.mappings;
|
||||
}
|
||||
}
|
|
@ -57,48 +57,35 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
{
|
||||
public static final String CONTEXT_ATTRIBUTE_KEY = "contextAttributeKey";
|
||||
private static final Logger LOG = Log.getLogger(WebSocketUpgradeFilter.class);
|
||||
|
||||
|
||||
public static WebSocketUpgradeFilter configureContext(ServletContextHandler context) throws ServletException
|
||||
{
|
||||
// Prevent double configure
|
||||
WebSocketUpgradeFilter filter = (WebSocketUpgradeFilter)context.getAttribute(WebSocketUpgradeFilter.class.getName());
|
||||
WebSocketUpgradeFilter filter = (WebSocketUpgradeFilter) context.getAttribute(WebSocketUpgradeFilter.class.getName());
|
||||
if (filter != null)
|
||||
{
|
||||
return filter;
|
||||
}
|
||||
|
||||
// Use WEB-INF/web.xml instantiated version, if present
|
||||
FilterHolder filters[] = context.getServletHandler().getFilters();
|
||||
for (FilterHolder registeredFilter: filters)
|
||||
{
|
||||
if (WebSocketUpgradeFilter.class.isAssignableFrom(registeredFilter.getClass()))
|
||||
{
|
||||
WebSocketUpgradeFilter wsuf = (WebSocketUpgradeFilter) registeredFilter.getFilter();
|
||||
// need async supported
|
||||
context.setAttribute(WebSocketUpgradeFilter.class.getName(), wsuf);
|
||||
return wsuf;
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamically add filter
|
||||
filter = new WebSocketUpgradeFilter();
|
||||
filter.setToAttribute(context, WebSocketUpgradeFilter.class.getName());
|
||||
|
||||
|
||||
String name = "Jetty_WebSocketUpgradeFilter";
|
||||
String pathSpec = "/*";
|
||||
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
|
||||
|
||||
|
||||
FilterHolder fholder = new FilterHolder(filter);
|
||||
fholder.setName(name);
|
||||
fholder.setAsyncSupported(true);
|
||||
fholder.setInitParameter(CONTEXT_ATTRIBUTE_KEY,WebSocketUpgradeFilter.class.getName());
|
||||
context.addFilter(fholder,pathSpec,dispatcherTypes);
|
||||
|
||||
fholder.setInitParameter(CONTEXT_ATTRIBUTE_KEY, WebSocketUpgradeFilter.class.getName());
|
||||
context.addFilter(fholder, pathSpec, dispatcherTypes);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Adding [{}] {} mapped to {} to {}",name,filter,pathSpec,context);
|
||||
LOG.debug("Adding [{}] {} mapped to {} to {}", name, filter, pathSpec, context);
|
||||
}
|
||||
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
|
@ -109,12 +96,12 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
public static WebSocketUpgradeFilter configureContext(ServletContext context) throws ServletException
|
||||
{
|
||||
ContextHandler handler = ContextHandler.getContextHandler(context);
|
||||
|
||||
|
||||
if (handler == null)
|
||||
{
|
||||
throw new ServletException("Not running on Jetty, WebSocket support unavailable");
|
||||
}
|
||||
|
||||
|
||||
if (!(handler instanceof ServletContextHandler))
|
||||
{
|
||||
throw new ServletException("Not running in Jetty ServletContextHandler, WebSocket support via " + WebSocketUpgradeFilter.class.getName() + " unavailable");
|
||||
|
@ -122,40 +109,41 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
|
||||
return configureContext((ServletContextHandler) handler);
|
||||
}
|
||||
|
||||
private final WebSocketServerFactory factory;
|
||||
private final PathMappings<WebSocketCreator> pathmap = new PathMappings<>();
|
||||
|
||||
public static final String CREATOR_KEY = "org.eclipse.jetty.websocket.server.creator";
|
||||
public static final String FACTORY_KEY = "org.eclipse.jetty.websocket.server.factory";
|
||||
|
||||
private final WebSocketPolicy policy;
|
||||
private final ByteBufferPool bufferPool;
|
||||
private WebSocketServerFactory factory;
|
||||
private MappedWebSocketCreator mappedWebSocketCreator;
|
||||
private String fname;
|
||||
private boolean alreadySetToAttribute = false;
|
||||
|
||||
|
||||
public WebSocketUpgradeFilter()
|
||||
{
|
||||
this(WebSocketPolicy.newServerPolicy(),new MappedByteBufferPool());
|
||||
this(WebSocketPolicy.newServerPolicy(), new MappedByteBufferPool());
|
||||
}
|
||||
|
||||
|
||||
public WebSocketUpgradeFilter(WebSocketPolicy policy, ByteBufferPool bufferPool)
|
||||
{
|
||||
factory = new WebSocketServerFactory(policy,bufferPool);
|
||||
addBean(factory,true);
|
||||
this.policy = policy;
|
||||
this.bufferPool = bufferPool;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void addMapping(PathSpec spec, WebSocketCreator creator)
|
||||
{
|
||||
// TODO: ideally should throw warning/error if attempting to map to something
|
||||
// not covered by the filter pathSpec/url-patterns. But how do we get the filter mappings
|
||||
// in a sane way? and then apply them to the PathSpec object properly?
|
||||
pathmap.put(spec,creator);
|
||||
mappedWebSocketCreator.addMapping(spec, creator);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
factory.cleanup();
|
||||
pathmap.reset();
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
|
||||
{
|
||||
|
@ -163,10 +151,10 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
{
|
||||
// no factory, cannot operate
|
||||
LOG.debug("WebSocketUpgradeFilter is not operational - no WebSocketServletFactory configured");
|
||||
chain.doFilter(request,response);
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
HttpServletRequest httpreq = (HttpServletRequest) request;
|
||||
|
@ -178,7 +166,7 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Since this is a filter, we need to be smart about determining the target path
|
||||
String contextPath = httpreq.getContextPath();
|
||||
String target = httpreq.getRequestURI();
|
||||
|
@ -186,37 +174,37 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
{
|
||||
target = target.substring(contextPath.length());
|
||||
}
|
||||
|
||||
MappedResource<WebSocketCreator> resource = pathmap.getMatch(target);
|
||||
|
||||
MappedResource<WebSocketCreator> resource = mappedWebSocketCreator.getMappings().getMatch(target);
|
||||
if (resource == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("WebSocket Upgrade on {} has no associated endpoint", target);
|
||||
LOG.debug("PathMappings: {}", pathmap.dump());
|
||||
LOG.debug("PathMappings: {}", mappedWebSocketCreator.getMappings().dump());
|
||||
}
|
||||
// no match.
|
||||
chain.doFilter(request, response);
|
||||
return;
|
||||
}
|
||||
|
||||
if(LOG.isDebugEnabled())
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("WebSocket Upgrade detected on {} for endpoint {}", target, resource);
|
||||
}
|
||||
|
||||
|
||||
WebSocketCreator creator = resource.getResource();
|
||||
|
||||
|
||||
// Store PathSpec resource mapping as request attribute
|
||||
httpreq.setAttribute(PathSpec.class.getName(), resource.getPathSpec());
|
||||
|
||||
|
||||
// We have an upgrade request
|
||||
if (factory.acceptWebSocket(creator, httpreq, httpresp))
|
||||
{
|
||||
// We have a socket instance created
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// If we reach this point, it means we had an incoming request to upgrade
|
||||
// but it was either not a proper websocket upgrade, or it was possibly rejected
|
||||
// due to incoming request constraints (controlled by WebSocketCreator)
|
||||
|
@ -234,69 +222,82 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
LOG.debug("Not a HttpServletRequest, skipping WebSocketUpgradeFilter");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// not an Upgrade request
|
||||
chain.doFilter(request,response);
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String dump()
|
||||
{
|
||||
return ContainerLifeCycle.dump(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
out.append(indent).append(" +- pathmap=").append(pathmap.toString()).append("\n");
|
||||
pathmap.dump(out,indent + " ");
|
||||
out.append(indent).append(" +- pathmap=").append(mappedWebSocketCreator.toString()).append("\n");
|
||||
mappedWebSocketCreator.getMappings().dump(out, indent + " ");
|
||||
}
|
||||
|
||||
|
||||
public WebSocketServerFactory getFactory()
|
||||
{
|
||||
return factory;
|
||||
}
|
||||
|
||||
|
||||
@ManagedAttribute(value = "mappings", readonly = true)
|
||||
@Override
|
||||
public PathMappings<WebSocketCreator> getMappings()
|
||||
{
|
||||
return pathmap;
|
||||
return mappedWebSocketCreator.getMappings();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig config) throws ServletException
|
||||
{
|
||||
fname = config.getFilterName();
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
WebSocketPolicy policy = factory.getPolicy();
|
||||
|
||||
mappedWebSocketCreator = (MappedWebSocketCreator) config.getServletContext().getAttribute(CREATOR_KEY);
|
||||
if (mappedWebSocketCreator == null)
|
||||
{
|
||||
mappedWebSocketCreator = new DefaultMappedWebSocketCreator();
|
||||
}
|
||||
|
||||
factory = (WebSocketServerFactory) config.getServletContext().getAttribute(FACTORY_KEY);
|
||||
if (factory == null)
|
||||
{
|
||||
factory = new WebSocketServerFactory(policy, bufferPool);
|
||||
}
|
||||
addBean(factory, true);
|
||||
|
||||
// TODO: Policy isn't from attributes
|
||||
|
||||
String max = config.getInitParameter("maxIdleTime");
|
||||
if (max != null)
|
||||
{
|
||||
policy.setIdleTimeout(Long.parseLong(max));
|
||||
factory.getPolicy().setIdleTimeout(Long.parseLong(max));
|
||||
}
|
||||
|
||||
|
||||
max = config.getInitParameter("maxTextMessageSize");
|
||||
if (max != null)
|
||||
{
|
||||
policy.setMaxTextMessageSize(Integer.parseInt(max));
|
||||
factory.getPolicy().setMaxTextMessageSize(Integer.parseInt(max));
|
||||
}
|
||||
|
||||
|
||||
max = config.getInitParameter("maxBinaryMessageSize");
|
||||
if (max != null)
|
||||
{
|
||||
policy.setMaxBinaryMessageSize(Integer.parseInt(max));
|
||||
factory.getPolicy().setMaxBinaryMessageSize(Integer.parseInt(max));
|
||||
}
|
||||
|
||||
|
||||
max = config.getInitParameter("inputBufferSize");
|
||||
if (max != null)
|
||||
{
|
||||
policy.setInputBufferSize(Integer.parseInt(max));
|
||||
factory.getPolicy().setInputBufferSize(Integer.parseInt(max));
|
||||
}
|
||||
|
||||
|
||||
String key = config.getInitParameter(CONTEXT_ATTRIBUTE_KEY);
|
||||
if (key == null)
|
||||
{
|
||||
|
@ -305,7 +306,7 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
}
|
||||
|
||||
setToAttribute(config.getServletContext(), key);
|
||||
|
||||
|
||||
factory.start();
|
||||
}
|
||||
catch (Exception x)
|
||||
|
@ -316,45 +317,45 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
|
||||
private void setToAttribute(ServletContextHandler context, String key) throws ServletException
|
||||
{
|
||||
if(alreadySetToAttribute)
|
||||
if (alreadySetToAttribute)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.getAttribute(key) != null)
|
||||
{
|
||||
throw new ServletException(WebSocketUpgradeFilter.class.getName() +
|
||||
throw new ServletException(WebSocketUpgradeFilter.class.getName() +
|
||||
" is defined twice for the same context attribute key '" + key
|
||||
+ "'. Make sure you have different init-param '" +
|
||||
+ "'. Make sure you have different init-param '" +
|
||||
CONTEXT_ATTRIBUTE_KEY + "' values set");
|
||||
}
|
||||
context.setAttribute(key,this);
|
||||
|
||||
context.setAttribute(key, this);
|
||||
|
||||
alreadySetToAttribute = true;
|
||||
}
|
||||
|
||||
|
||||
public void setToAttribute(ServletContext context, String key) throws ServletException
|
||||
{
|
||||
if(alreadySetToAttribute)
|
||||
if (alreadySetToAttribute)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (context.getAttribute(key) != null)
|
||||
{
|
||||
throw new ServletException(WebSocketUpgradeFilter.class.getName() +
|
||||
throw new ServletException(WebSocketUpgradeFilter.class.getName() +
|
||||
" is defined twice for the same context attribute key '" + key
|
||||
+ "'. Make sure you have different init-param '" +
|
||||
+ "'. Make sure you have different init-param '" +
|
||||
CONTEXT_ATTRIBUTE_KEY + "' values set");
|
||||
}
|
||||
context.setAttribute(key,this);
|
||||
|
||||
context.setAttribute(key, this);
|
||||
|
||||
alreadySetToAttribute = true;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[factory=%s,pathmap=%s]",this.getClass().getSimpleName(),factory,pathmap);
|
||||
return String.format("%s[factory=%s,creator=%s]", this.getClass().getSimpleName(), factory, mappedWebSocketCreator);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue