Merge branch 'jetty-9.3.x' into jetty-9.4.x
This commit is contained in:
commit
1003969468
|
@ -26,20 +26,22 @@ import java.io.OutputStream;
|
|||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
/**
|
||||
* RolloverFileOutputStream
|
||||
*
|
||||
* RolloverFileOutputStream.
|
||||
*
|
||||
* <p>
|
||||
* This output stream puts content in a file that is rolled over every 24 hours.
|
||||
* The filename must include the string "yyyy_mm_dd", which is replaced with the
|
||||
* actual date when creating and rolling over the file.
|
||||
*
|
||||
* </p>
|
||||
* <p>
|
||||
* Old files are retained for a number of days before being deleted.
|
||||
* </p>
|
||||
*/
|
||||
public class RolloverFileOutputStream extends FilterOutputStream
|
||||
{
|
||||
|
@ -51,6 +53,7 @@ public class RolloverFileOutputStream extends FilterOutputStream
|
|||
final static int ROLLOVER_FILE_RETAIN_DAYS = 31;
|
||||
|
||||
private RollTask _rollTask;
|
||||
private Calendar midnight;
|
||||
private SimpleDateFormat _fileBackupFormat;
|
||||
private SimpleDateFormat _fileDateFormat;
|
||||
|
||||
|
@ -172,19 +175,27 @@ public class RolloverFileOutputStream extends FilterOutputStream
|
|||
|
||||
_rollTask=new RollTask();
|
||||
|
||||
Calendar now = Calendar.getInstance();
|
||||
now.setTimeZone(zone);
|
||||
|
||||
GregorianCalendar midnight =
|
||||
new GregorianCalendar(now.get(Calendar.YEAR),
|
||||
now.get(Calendar.MONTH),
|
||||
now.get(Calendar.DAY_OF_MONTH),
|
||||
23,0);
|
||||
midnight.setTimeZone(zone);
|
||||
midnight.add(Calendar.HOUR,1);
|
||||
__rollover.scheduleAtFixedRate(_rollTask,midnight.getTime(),1000L*60*60*24);
|
||||
midnight = Calendar.getInstance();
|
||||
midnight.setTimeZone(zone);
|
||||
// set to midnight
|
||||
midnight.set(Calendar.HOUR, 0);
|
||||
midnight.set(Calendar.MINUTE, 0);
|
||||
midnight.set(Calendar.SECOND, 0);
|
||||
midnight.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
scheduleNextRollover();
|
||||
}
|
||||
}
|
||||
|
||||
private void scheduleNextRollover()
|
||||
{
|
||||
// Increment to next day.
|
||||
// Using Calendar.add(DAY, 1) takes in account Daylights Savings
|
||||
// differences, and still maintains the "midnight" settings for
|
||||
// Hour, Minute, Second, Milliseconds
|
||||
midnight.add(Calendar.DAY_OF_MONTH, 1);
|
||||
__rollover.schedule(_rollTask,midnight.getTime());
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public String getFilename()
|
||||
|
@ -254,7 +265,9 @@ public class RolloverFileOutputStream extends FilterOutputStream
|
|||
{
|
||||
if (_retainDays>0)
|
||||
{
|
||||
long now = System.currentTimeMillis();
|
||||
Calendar now = Calendar.getInstance();
|
||||
now.add(Calendar.DAY_OF_MONTH, (-1)*_retainDays);
|
||||
long expired = now.getTimeInMillis();
|
||||
|
||||
File file= new File(_filename);
|
||||
File dir = new File(file.getParent());
|
||||
|
@ -272,9 +285,10 @@ public class RolloverFileOutputStream extends FilterOutputStream
|
|||
if(fn.startsWith(prefix)&&fn.indexOf(suffix,prefix.length())>=0)
|
||||
{
|
||||
File f = new File(dir,fn);
|
||||
long date = f.lastModified();
|
||||
if ( ((now-date)/(1000*60*60*24))>_retainDays)
|
||||
f.delete();
|
||||
if(f.lastModified() < expired)
|
||||
{
|
||||
f.delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -297,8 +311,6 @@ public class RolloverFileOutputStream extends FilterOutputStream
|
|||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/**
|
||||
*/
|
||||
@Override
|
||||
public void close()
|
||||
throws IOException
|
||||
|
@ -316,8 +328,6 @@ public class RolloverFileOutputStream extends FilterOutputStream
|
|||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
/* ------------------------------------------------------------ */
|
||||
private class RollTask extends TimerTask
|
||||
{
|
||||
|
@ -327,13 +337,13 @@ public class RolloverFileOutputStream extends FilterOutputStream
|
|||
try
|
||||
{
|
||||
RolloverFileOutputStream.this.setFile();
|
||||
RolloverFileOutputStream.this.scheduleNextRollover();
|
||||
RolloverFileOutputStream.this.removeOldFiles();
|
||||
|
||||
}
|
||||
catch(IOException e)
|
||||
{
|
||||
// Cannot log this exception to a LOG, as RolloverFOS can be used by logging
|
||||
e.printStackTrace();
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,13 +33,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.DecoratedObjectFactory;
|
||||
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(
|
||||
|
@ -47,13 +52,11 @@ 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);
|
||||
|
||||
|
||||
/**
|
||||
* DestroyListener
|
||||
*
|
||||
*
|
||||
*/
|
||||
public static class ContextDestroyListener implements ServletContextListener
|
||||
{
|
||||
|
@ -80,107 +83,97 @@ public class WebSocketServerContainerInitializer implements ServletContainerInit
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @param context the servlet context handler
|
||||
* @return the created websocket server container
|
||||
* @throws ServletException if unable to create the websocket server container
|
||||
* 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());
|
||||
context.addBean(jettyContainer, true);
|
||||
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);
|
||||
|
||||
return jettyContainer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Servlet 3.1 approach.
|
||||
* <p>
|
||||
* This will use Servlet 3.1 techniques on the {@link ServletContext} to add a filter at the start of the filter chain.
|
||||
*
|
||||
* @param context the servlet context
|
||||
* @param jettyContext the jetty servlet context handler
|
||||
* @return the created websocket server container
|
||||
* @throws ServletException if unable to create the websocket server container
|
||||
*/
|
||||
public static ServerContainer configureContext(ServletContext context, ServletContextHandler jettyContext) throws ServletException
|
||||
{
|
||||
|
||||
// Create Filter
|
||||
WebSocketUpgradeFilter filter = WebSocketUpgradeFilter.configureContext(context);
|
||||
|
||||
// Create the Jetty ServerContainer implementation
|
||||
ServerContainer jettyContainer = new ServerContainer(filter,filter.getFactory(),jettyContext.getServer().getThreadPool());
|
||||
jettyContext.addBean(jettyContainer, true);
|
||||
|
||||
// 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(isEnabledViaContext(context.getServletContext(), ADD_DYNAMIC_FILTER_KEY, true))
|
||||
{
|
||||
WebSocketUpgradeFilter.configureContext(context);
|
||||
}
|
||||
|
||||
return jettyContainer;
|
||||
}
|
||||
|
||||
private boolean isEnabled(Set<Class<?>> c, ServletContext context)
|
||||
/**
|
||||
* @deprecated use {@link #configureContext(ServletContextHandler)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public static ServerContainer configureContext(ServletContext context, ServletContextHandler jettyContext) throws ServletException
|
||||
{
|
||||
// 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;
|
||||
return configureContext(jettyContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartup(Set<Class<?>> c, ServletContext context) throws ServletException
|
||||
{
|
||||
if(!isEnabled(c,context))
|
||||
if(!isEnabledViaContext(context, ENABLE_KEY, true))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -203,26 +196,26 @@ public class WebSocketServerContainerInitializer implements ServletContainerInit
|
|||
try
|
||||
{
|
||||
Thread.currentThread().setContextClassLoader(context.getClassLoader());
|
||||
|
||||
|
||||
// Create the Jetty ServerContainer implementation
|
||||
ServerContainer jettyContainer = configureContext(context,jettyContext);
|
||||
|
||||
context.addListener(new ContextDestroyListener()); //make sure context is cleaned up when the context stops
|
||||
|
||||
// Establish the DecoratedObjectFactory thread local
|
||||
// for various ServiceLoader initiated components to use.
|
||||
DecoratedObjectFactory instantiator = (DecoratedObjectFactory)context.getAttribute(DecoratedObjectFactory.ATTR);
|
||||
if (instantiator == null)
|
||||
ServerContainer jettyContainer = configureContext(jettyContext);
|
||||
|
||||
context.addListener(new ContextDestroyListener()); // make sure context is cleaned up when the context stops
|
||||
|
||||
if (c.isEmpty())
|
||||
{
|
||||
LOG.info("Using WebSocket local DecoratedObjectFactory - none found in ServletContext");
|
||||
instantiator = new DecoratedObjectFactory();
|
||||
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<>();
|
||||
|
@ -312,9 +305,7 @@ public class WebSocketServerContainerInitializer implements ServletContainerInit
|
|||
throw new ServletException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
} finally {
|
||||
Thread.currentThread().setContextClassLoader(old);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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")))
|
||||
|
|
|
@ -151,6 +151,11 @@ public class WSServer
|
|||
{
|
||||
return serverUri;
|
||||
}
|
||||
|
||||
public Server getServer()
|
||||
{
|
||||
return server;
|
||||
}
|
||||
|
||||
public File getWebAppDir()
|
||||
{
|
||||
|
|
|
@ -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>
|
|
@ -221,9 +221,13 @@ public abstract class AbstractEventDriver extends AbstractLifeCycle implements I
|
|||
public void openSession(WebSocketSession session)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("openSession({})",session);
|
||||
{
|
||||
LOG.debug("openSession({})", session);
|
||||
LOG.debug("objectFactory={}", session.getContainerScope().getObjectFactory());
|
||||
}
|
||||
this.session = session;
|
||||
this.session.getContainerScope().getObjectFactory().decorate(this.websocket);
|
||||
|
||||
try
|
||||
{
|
||||
this.onConnect();
|
||||
|
|
|
@ -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.http.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.http.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;
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@ import javax.servlet.DispatcherType;
|
|||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.FilterRegistration;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
|
@ -40,6 +39,7 @@ import org.eclipse.jetty.http.pathmap.RegexPathSpec;
|
|||
import org.eclipse.jetty.http.pathmap.ServletPathSpec;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.servlet.ServletContextHandler;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
|
@ -59,11 +59,11 @@ 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;
|
||||
|
@ -72,77 +72,71 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
// 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);
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
public static WebSocketUpgradeFilter configureContext(ServletContext context) throws ServletException
|
||||
{
|
||||
// Prevent double configure
|
||||
WebSocketUpgradeFilter filter = (WebSocketUpgradeFilter)context.getAttribute(WebSocketUpgradeFilter.class.getName());
|
||||
if (filter != null)
|
||||
{
|
||||
return filter;
|
||||
LOG.debug("Adding [{}] {} mapped to {} to {}", name, filter, pathSpec, context);
|
||||
}
|
||||
|
||||
// Dynamically add filter
|
||||
filter = new WebSocketUpgradeFilter();
|
||||
filter.setToAttribute(context, WebSocketUpgradeFilter.class.getName());
|
||||
|
||||
String name = "Jetty_Dynamic_WebSocketUpgradeFilter";
|
||||
String pathSpec = "/*";
|
||||
EnumSet<DispatcherType> dispatcherTypes = EnumSet.of(DispatcherType.REQUEST);
|
||||
boolean isMatchAfter = false;
|
||||
String urlPatterns[] = { pathSpec };
|
||||
|
||||
FilterRegistration.Dynamic dyn = context.addFilter(name,filter);
|
||||
dyn.setAsyncSupported(true);
|
||||
dyn.setInitParameter(CONTEXT_ATTRIBUTE_KEY,WebSocketUpgradeFilter.class.getName());
|
||||
dyn.addMappingForUrlPatterns(dispatcherTypes,isMatchAfter,urlPatterns);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
{
|
||||
LOG.debug("Adding [{}] {} mapped to {} to {}",name,filter,pathSpec,context);
|
||||
}
|
||||
|
||||
return filter;
|
||||
}
|
||||
|
||||
private final WebSocketServerFactory factory;
|
||||
private final PathMappings<WebSocketCreator> pathmap = new PathMappings<>();
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #configureContext(ServletContextHandler)} instead
|
||||
*/
|
||||
@Deprecated
|
||||
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");
|
||||
}
|
||||
|
||||
return configureContext((ServletContextHandler) handler);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
pathmap.put(spec,creator);
|
||||
mappedWebSocketCreator.addMapping(spec, creator);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,10 +163,9 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
public void destroy()
|
||||
{
|
||||
factory.cleanup();
|
||||
pathmap.reset();
|
||||
super.destroy();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
|
||||
{
|
||||
|
@ -180,10 +173,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;
|
||||
|
@ -195,7 +188,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();
|
||||
|
@ -203,37 +196,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)
|
||||
|
@ -251,71 +244,83 @@ 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
|
||||
{
|
||||
ServletContext ctx = config.getServletContext();
|
||||
factory.init(ctx);
|
||||
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);
|
||||
}
|
||||
factory.init(config.getServletContext());
|
||||
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)
|
||||
{
|
||||
|
@ -323,7 +328,7 @@ public class WebSocketUpgradeFilter extends ContainerLifeCycle implements Filter
|
|||
key = WebSocketUpgradeFilter.class.getName();
|
||||
}
|
||||
|
||||
setToAttribute(ctx, key);
|
||||
setToAttribute(config.getServletContext(), key);
|
||||
|
||||
factory.start();
|
||||
}
|
||||
|
@ -335,45 +340,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