JSR-356 working out server endpoint creation
This commit is contained in:
parent
346034b44f
commit
a4644dc780
|
@ -18,6 +18,7 @@
|
|||
|
||||
package org.eclipse.jetty.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -32,6 +33,9 @@ import javax.servlet.ServletContext;
|
|||
import javax.servlet.ServletException;
|
||||
|
||||
import org.eclipse.jetty.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.component.AbstractLifeCycle;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
||||
|
@ -193,6 +197,16 @@ public class FilterHolder extends Holder<Filter>
|
|||
{
|
||||
return getName();
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
super.dump(out, indent);
|
||||
if(_filter instanceof Dumpable) {
|
||||
((Dumpable) _filter).dump(out, indent);
|
||||
}
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------ */
|
||||
public FilterRegistration.Dynamic getRegistration()
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
<dependency>
|
||||
<groupId>org.eclipse.jetty.drafts</groupId>
|
||||
<artifactId>javax.websocket-client-api</artifactId>
|
||||
<version>1.0.0.PFD-SNAPSHOT</version>
|
||||
<version>1.0.0.DRAFT-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty</groupId>
|
||||
|
|
|
@ -56,13 +56,7 @@ public class ClientContainer implements ContainerService
|
|||
|
||||
private Session connect(Object websocket, ClientEndpointConfig config, URI path) throws IOException
|
||||
{
|
||||
ClientEndpointConfig cec = config;
|
||||
if (cec == null)
|
||||
{
|
||||
cec = ClientEndpointConfig.Builder.create().build();
|
||||
}
|
||||
|
||||
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(websocket,cec);
|
||||
ConfiguredEndpoint endpoint = new ConfiguredEndpoint(websocket,config);
|
||||
ClientUpgradeRequest req = new ClientUpgradeRequest();
|
||||
if (config != null)
|
||||
{
|
||||
|
|
|
@ -30,6 +30,8 @@ import javax.websocket.WebSocketContainer;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Assert;
|
||||
import org.junit.BeforeClass;
|
||||
|
@ -37,6 +39,7 @@ import org.junit.Test;
|
|||
|
||||
public class EndpointEchoTest
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(EndpointEchoTest.class);
|
||||
private static Server server;
|
||||
private static EchoHandler handler;
|
||||
private static URI serverUri;
|
||||
|
@ -87,7 +90,9 @@ public class EndpointEchoTest
|
|||
EndpointEchoClient echoer = new EndpointEchoClient();
|
||||
Assert.assertThat(echoer,instanceOf(javax.websocket.Endpoint.class));
|
||||
Session session = container.connectToServer(echoer,serverUri);
|
||||
LOG.debug("Client Connected: {}",session);
|
||||
session.getBasicRemote().sendText("Echo");
|
||||
LOG.debug("Client Message Sent");
|
||||
echoer.textCapture.messageQueue.awaitMessages(1,1000,TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,10 +26,15 @@
|
|||
<artifactId>javax-websocket-client-impl</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.websocket</groupId>
|
||||
<artifactId>websocket-server</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.drafts</groupId>
|
||||
<artifactId>javax.websocket-api</artifactId>
|
||||
<version>1.0.0.PFD-SNAPSHOT</version>
|
||||
<version>1.0.0.DRAFT-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.eclipse.jetty.toolchain</groupId>
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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 java.util.List;
|
||||
|
||||
import javax.websocket.Extension;
|
||||
import javax.websocket.HandshakeResponse;
|
||||
import javax.websocket.server.HandshakeRequest;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
import javax.websocket.server.ServerEndpointConfig.Configurator;
|
||||
|
||||
public class JettyServerEndpointConfigurator extends Configurator
|
||||
{
|
||||
@Override
|
||||
public boolean checkOrigin(String originHeaderValue)
|
||||
{
|
||||
return super.checkOrigin(originHeaderValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException
|
||||
{
|
||||
return super.getEndpointInstance(endpointClass);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Extension> getNegotiatedExtensions(List<Extension> installed, List<Extension> requested)
|
||||
{
|
||||
return super.getNegotiatedExtensions(installed,requested);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNegotiatedSubprotocol(List<String> supported, List<String> requested)
|
||||
{
|
||||
return super.getNegotiatedSubprotocol(supported,requested);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response)
|
||||
{
|
||||
super.modifyHandshake(sec,request,response);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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 java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.UpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.api.UpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
||||
public class JsrCreator implements WebSocketCreator
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(JsrCreator.class);
|
||||
private final ServerEndpointConfig config;
|
||||
|
||||
public JsrCreator(ServerEndpointConfig config)
|
||||
{
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object createWebSocket(UpgradeRequest req, UpgradeResponse resp)
|
||||
{
|
||||
JsrHandshakeRequest hsreq = new JsrHandshakeRequest(req);
|
||||
JsrHandshakeResponse hsresp = new JsrHandshakeResponse(resp);
|
||||
|
||||
ServerEndpointConfig.Configurator configurator = config.getConfigurator();
|
||||
|
||||
// modify handshake
|
||||
configurator.modifyHandshake(config,hsreq,hsresp);
|
||||
|
||||
// check origin
|
||||
if (!configurator.checkOrigin(req.getOrigin()))
|
||||
{
|
||||
try
|
||||
{
|
||||
resp.sendForbidden("Origin mismatch");
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOG.debug("Unable to send error response",e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// deal with sub protocols
|
||||
List<String> supported = config.getSubprotocols();
|
||||
List<String> requested = req.getSubProtocols();
|
||||
String subprotocol = configurator.getNegotiatedSubprotocol(supported,requested);
|
||||
if (subprotocol != null)
|
||||
{
|
||||
resp.setAcceptedSubProtocol(subprotocol);
|
||||
}
|
||||
|
||||
// create endpoint class
|
||||
try
|
||||
{
|
||||
return config.getEndpointClass().newInstance();
|
||||
}
|
||||
catch (InstantiationException | IllegalAccessException e)
|
||||
{
|
||||
LOG.debug("Unable to create websocket: " + config.getEndpointClass().getName(),e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[config=%s]",this.getClass().getName(),config);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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 java.net.URI;
|
||||
import java.security.Principal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.websocket.server.HandshakeRequest;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.UpgradeRequest;
|
||||
|
||||
public class JsrHandshakeRequest implements HandshakeRequest
|
||||
{
|
||||
private final UpgradeRequest request;
|
||||
|
||||
public JsrHandshakeRequest(UpgradeRequest req)
|
||||
{
|
||||
this.request = req;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
return request.getHeaders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getHttpSession()
|
||||
{
|
||||
return request.getSession();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getParameterMap()
|
||||
{
|
||||
return request.getParameterMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getQueryString()
|
||||
{
|
||||
return request.getQueryString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getRequestURI()
|
||||
{
|
||||
return request.getRequestURI();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getUserPrincipal()
|
||||
{
|
||||
// TODO: need to return User Principal
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isUserInRole(String role)
|
||||
{
|
||||
// TODO: need to return isUserInRole result
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -18,8 +18,25 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.jsr356.server;
|
||||
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class WebSocketHandler extends HandlerWrapper
|
||||
import javax.websocket.HandshakeResponse;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.UpgradeResponse;
|
||||
|
||||
public class JsrHandshakeResponse implements HandshakeResponse
|
||||
{
|
||||
private final UpgradeResponse response;
|
||||
|
||||
public JsrHandshakeResponse(UpgradeResponse resp)
|
||||
{
|
||||
this.response = resp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, List<String>> getHeaders()
|
||||
{
|
||||
return response.getHeaders();
|
||||
}
|
||||
}
|
|
@ -106,4 +106,9 @@ public class JsrServerMetadata extends JsrMetadata<ServerEndpoint>
|
|||
// Copy constructor
|
||||
return new JettyServerEndpointConfig(config);
|
||||
}
|
||||
|
||||
public String getPath()
|
||||
{
|
||||
return config.getPath();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,33 +18,78 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.jsr356.server;
|
||||
|
||||
import java.util.EnumSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import javax.servlet.DispatcherType;
|
||||
import javax.websocket.ContainerProvider;
|
||||
import javax.websocket.DeploymentException;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.eclipse.jetty.websocket.jsr356.ClientContainer;
|
||||
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.pathmap.WebSocketPathSpec;
|
||||
import org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter;
|
||||
|
||||
public class ServerContainer extends ClientContainer implements javax.websocket.server.ServerContainer
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ServerContainer.class);
|
||||
|
||||
public static ServerContainer getInstance()
|
||||
{
|
||||
return (ServerContainer)ContainerProvider.getWebSocketContainer();
|
||||
}
|
||||
private ConcurrentHashMap<Class<?>, JsrServerMetadata> endpointServerMetadataCache = new ConcurrentHashMap<>();
|
||||
private MappedWebSocketCreator mappedCreator;
|
||||
|
||||
public ServerContainer()
|
||||
{
|
||||
super();
|
||||
WebAppContext webapp = WebAppContext.getCurrentWebAppContext();
|
||||
if (webapp != null)
|
||||
{
|
||||
WebSocketUpgradeFilter filter = new WebSocketUpgradeFilter();
|
||||
FilterHolder fholder = new FilterHolder(filter);
|
||||
fholder.setName("Jetty_WebSocketUpgradeFilter");
|
||||
fholder.setDisplayName("WebSocket Upgrade Filter");
|
||||
String pathSpec = "/*";
|
||||
webapp.addFilter(fholder,pathSpec,EnumSet.of(DispatcherType.REQUEST));
|
||||
LOG.debug("Adding {} mapped to {} to {}",filter,pathSpec,webapp);
|
||||
mappedCreator = filter;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG.debug("No active WebAppContext detected");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEndpoint(Class<?> endpointClass) throws DeploymentException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
JsrServerMetadata metadata = getServerEndpointMetadata(endpointClass);
|
||||
addEndpoint(metadata);
|
||||
}
|
||||
|
||||
public void addEndpoint(JsrServerMetadata metadata)
|
||||
public void addEndpoint(JsrServerMetadata metadata) throws DeploymentException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
addEndpoint(metadata.getEndpointConfigCopy());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addEndpoint(ServerEndpointConfig serverConfig) throws DeploymentException
|
||||
public void addEndpoint(ServerEndpointConfig config) throws DeploymentException
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
JsrCreator creator = new JsrCreator(config);
|
||||
mappedCreator.addMapping(new WebSocketPathSpec(config.getPath()),creator);
|
||||
}
|
||||
|
||||
public void addMappedCreator(MappedWebSocketCreator mappedCreator)
|
||||
{
|
||||
this.mappedCreator = mappedCreator;
|
||||
}
|
||||
|
||||
public JsrServerMetadata getServerEndpointMetadata(Class<?> endpointClass) throws DeploymentException
|
||||
|
|
|
@ -29,10 +29,14 @@ import java.util.Set;
|
|||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.websocket.server.PathParam;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathSpecGroup;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.RegexPathSpec;
|
||||
|
||||
/**
|
||||
* PathSpec for WebSocket @{@link ServerEndpoint} declarations.
|
||||
* PathSpec for WebSocket @{@link ServerEndpoint} declarations with support for URI templates and @{@link PathParam} annotations
|
||||
*/
|
||||
public class WebSocketPathSpec extends RegexPathSpec
|
||||
{
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.jetty.websocket.jsr356.server.JettyServerEndpointConfigurator
|
|
@ -35,11 +35,14 @@ import org.eclipse.jetty.webapp.WebInfConfiguration;
|
|||
import org.eclipse.jetty.webapp.WebXmlConfiguration;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.BasicEchoSocket;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoSocket;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Example of an annotated echo server discovered via annotation scanning.
|
||||
*/
|
||||
public class BasicAnnotatedTest
|
||||
{
|
||||
@Rule
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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 java.net.URI;
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.server.ServerContainer;
|
||||
|
||||
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.jsr356.server.samples.echo.BasicEchoEndpoint;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoEndpointConfigContextListener;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Example of an {@link Endpoint} extended echo server added programmatically via the
|
||||
* {@link ServerContainer#addEndpoint(javax.websocket.server.ServerEndpointConfig)}
|
||||
*/
|
||||
public class BasicEndpointTest
|
||||
{
|
||||
@Rule
|
||||
public TestingDir testdir = new TestingDir();
|
||||
|
||||
@Test
|
||||
public void testEcho() throws Exception
|
||||
{
|
||||
WSServer wsb = new WSServer(testdir,"app");
|
||||
wsb.copyWebInf("basic-echo-endpoint-config-web.xml");
|
||||
// the endpoint (extends javax.websocket.Endpoint)
|
||||
wsb.copyClass(BasicEchoEndpoint.class);
|
||||
// the configuration (adds the endpoint)
|
||||
wsb.copyClass(BasicEchoEndpointConfigContextListener.class);
|
||||
|
||||
try
|
||||
{
|
||||
wsb.start();
|
||||
URI uri = wsb.getServerBaseURI();
|
||||
|
||||
WebAppContext webapp = wsb.createWebAppContext();
|
||||
// default webapp configuration used (no annotation scanning)
|
||||
wsb.deployWebapp(webapp);
|
||||
wsb.dump();
|
||||
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
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 World");
|
||||
Queue<String> msgs = clientEcho.awaitMessages(1);
|
||||
Assert.assertEquals("Expected message","Hello World",msgs.poll());
|
||||
}
|
||||
finally
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
wsb.stop();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Test the JettyServerEndpointConfigurator impl.
|
||||
*/
|
||||
public class JettyServerEndpointConfiguratorTest
|
||||
{
|
||||
@Test
|
||||
public void testServiceLoader()
|
||||
{
|
||||
System.out.printf("Service Name: %s%n",ServerEndpointConfig.Configurator.class.getName());
|
||||
|
||||
ServiceLoader<ServerEndpointConfig.Configurator> loader = ServiceLoader.load(javax.websocket.server.ServerEndpointConfig.Configurator.class);
|
||||
assertThat("loader",loader,notNullValue());
|
||||
Iterator<ServerEndpointConfig.Configurator> iter = loader.iterator();
|
||||
assertThat("loader.iterator",iter,notNullValue());
|
||||
assertThat("loader.iterator.hasNext",iter.hasNext(),is(true));
|
||||
|
||||
ServerEndpointConfig.Configurator configr = iter.next();
|
||||
assertThat("Configurator",configr,notNullValue());
|
||||
assertThat("COnfigurator type",configr,instanceOf(JettyServerEndpointConfigurator.class));
|
||||
}
|
||||
}
|
|
@ -34,6 +34,8 @@ import org.eclipse.jetty.toolchain.test.IO;
|
|||
import org.eclipse.jetty.toolchain.test.MavenTestingUtils;
|
||||
import org.eclipse.jetty.toolchain.test.OS;
|
||||
import org.eclipse.jetty.toolchain.test.TestingDir;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.webapp.WebAppContext;
|
||||
import org.junit.Assert;
|
||||
|
||||
|
@ -44,6 +46,7 @@ import org.junit.Assert;
|
|||
*/
|
||||
public class WSServer
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(WSServer.class);
|
||||
@SuppressWarnings("unused")
|
||||
private final TestingDir testdir;
|
||||
private final File contextDir;
|
||||
|
@ -62,18 +65,34 @@ public class WSServer
|
|||
FS.ensureEmpty(contextDir);
|
||||
}
|
||||
|
||||
public void copyEndpoint(Class<?> endpointClass) throws Exception
|
||||
public void copyClass(Class<?> clazz) throws Exception
|
||||
{
|
||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||
String endpointPath = endpointClass.getName().replace('.','/') + ".class";
|
||||
String endpointPath = clazz.getName().replace('.','/') + ".class";
|
||||
URL classUrl = cl.getResource(endpointPath);
|
||||
Assert.assertThat("Class URL for: " + endpointClass,classUrl,notNullValue());
|
||||
Assert.assertThat("Class URL for: " + clazz,classUrl,notNullValue());
|
||||
File destFile = new File(classesDir,OS.separators(endpointPath));
|
||||
FS.ensureDirExists(destFile.getParentFile());
|
||||
File srcFile = new File(classUrl.toURI());
|
||||
IO.copy(srcFile,destFile);
|
||||
}
|
||||
|
||||
public void copyEndpoint(Class<?> endpointClass) throws Exception
|
||||
{
|
||||
copyClass(endpointClass);
|
||||
}
|
||||
|
||||
public void copyWebInf(String testResourceName) throws IOException
|
||||
{
|
||||
webinf = new File(contextDir,"WEB-INF");
|
||||
FS.ensureDirExists(webinf);
|
||||
classesDir = new File(webinf,"classes");
|
||||
FS.ensureDirExists(classesDir);
|
||||
File webxml = new File(webinf,"web.xml");
|
||||
File testWebXml = MavenTestingUtils.getTestResourceFile(testResourceName);
|
||||
IO.copy(testWebXml,webxml);
|
||||
}
|
||||
|
||||
public WebAppContext createWebAppContext()
|
||||
{
|
||||
WebAppContext context = new WebAppContext();
|
||||
|
@ -84,13 +103,7 @@ public class WSServer
|
|||
|
||||
public void createWebInf() throws IOException
|
||||
{
|
||||
webinf = new File(contextDir,"WEB-INF");
|
||||
FS.ensureDirExists(webinf);
|
||||
classesDir = new File(webinf,"classes");
|
||||
FS.ensureDirExists(classesDir);
|
||||
File webxml = new File(webinf,"web.xml");
|
||||
File emptyWebXml = MavenTestingUtils.getTestResourceFile("empty-web.xml");
|
||||
IO.copy(emptyWebXml,webxml);
|
||||
copyWebInf("empty-web.xml");
|
||||
}
|
||||
|
||||
public void deployWebapp(WebAppContext webapp) throws Exception
|
||||
|
@ -134,6 +147,7 @@ public class WSServer
|
|||
}
|
||||
int port = connector.getLocalPort();
|
||||
serverUri = new URI(String.format("ws://%s:%d/",host,port));
|
||||
LOG.debug("Server started on {}",serverUri);
|
||||
}
|
||||
|
||||
public void stop()
|
||||
|
|
|
@ -20,7 +20,9 @@ package org.eclipse.jetty.websocket.jsr356.server.pathmap;
|
|||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import org.eclipse.jetty.websocket.jsr356.server.pathmap.PathMappings.MappedResource;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathMappings.MappedResource;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.ServletPathSpec;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -72,49 +74,6 @@ public class PathMappingsTest
|
|||
assertMatch(p,"/entrance/cam","entranceCam");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the match order rules imposed by the Servlet API.
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>Exact match</li>
|
||||
* <li>Longest prefix match</li>
|
||||
* <li>Longest suffix match</li>
|
||||
* <li>default</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Test
|
||||
public void testServletMatchOrder()
|
||||
{
|
||||
PathMappings<String> p = new PathMappings<>();
|
||||
|
||||
p.put(new ServletPathSpec("/abs/path"),"path");
|
||||
p.put(new ServletPathSpec("/abs/path/longer"),"longpath");
|
||||
p.put(new ServletPathSpec("/animal/bird/*"),"birds");
|
||||
p.put(new ServletPathSpec("/animal/fish/*"),"fishes");
|
||||
p.put(new ServletPathSpec("/animal/*"),"animals");
|
||||
p.put(new ServletPathSpec("*.tar.gz"),"tarball");
|
||||
p.put(new ServletPathSpec("*.gz"),"gzipped");
|
||||
p.put(new ServletPathSpec("/"),"default");
|
||||
|
||||
for (MappedResource<String> res : p)
|
||||
{
|
||||
System.out.printf(" %s%n",res);
|
||||
}
|
||||
|
||||
assertMatch(p,"/abs/path","path");
|
||||
assertMatch(p,"/abs/path/longer","longpath");
|
||||
assertMatch(p,"/abs/path/foo","default");
|
||||
assertMatch(p,"/main.css","default");
|
||||
assertMatch(p,"/downloads/script.gz","gzipped");
|
||||
assertMatch(p,"/downloads/distribution.tar.gz","tarball");
|
||||
assertMatch(p,"/downloads/readme.txt","default");
|
||||
assertMatch(p,"/downloads/logs.tgz","default");
|
||||
assertMatch(p,"/animal/horse/mustang","animals");
|
||||
assertMatch(p,"/animal/bird/eagle/bald","birds");
|
||||
assertMatch(p,"/animal/fish/shark/hammerhead","fishes");
|
||||
assertMatch(p,"/animal/insect/ladybug","animals");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the match order rules imposed by the WebSocket API (JSR-356)
|
||||
* <p>
|
||||
|
|
|
@ -23,6 +23,7 @@ import static org.junit.Assert.*;
|
|||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathSpecGroup;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
|
@ -60,7 +61,7 @@ public class WebSocketPathSpecTest
|
|||
assertEquals("Spec.pathSpec","/",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",1,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.EXACT,spec.group);
|
||||
assertEquals("Spec.group",PathSpecGroup.EXACT,spec.getGroup());
|
||||
|
||||
assertEquals("Spec.variableCount",0,spec.getVariableCount());
|
||||
assertEquals("Spec.variable.length",0,spec.getVariables().length);
|
||||
|
@ -73,7 +74,7 @@ public class WebSocketPathSpecTest
|
|||
assertEquals("Spec.pathSpec","/a",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/a$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",1,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.EXACT,spec.group);
|
||||
assertEquals("Spec.group",PathSpecGroup.EXACT,spec.getGroup());
|
||||
|
||||
assertEquals("Spec.variableCount",0,spec.getVariableCount());
|
||||
assertEquals("Spec.variable.length",0,spec.getVariables().length);
|
||||
|
@ -86,7 +87,7 @@ public class WebSocketPathSpecTest
|
|||
assertEquals("Spec.pathSpec","/a/b",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/a/b$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",2,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.EXACT,spec.group);
|
||||
assertEquals("Spec.group",PathSpecGroup.EXACT,spec.getGroup());
|
||||
|
||||
assertEquals("Spec.variableCount",0,spec.getVariableCount());
|
||||
assertEquals("Spec.variable.length",0,spec.getVariables().length);
|
||||
|
@ -105,7 +106,7 @@ public class WebSocketPathSpecTest
|
|||
assertEquals("Spec.pathSpec","/a/{var}/c",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/a/([^/]+)/c$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",3,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.group);
|
||||
assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var");
|
||||
|
||||
|
@ -129,7 +130,7 @@ public class WebSocketPathSpecTest
|
|||
assertEquals("Spec.pathSpec","/a/{foo}",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/a/([^/]+)$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",2,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.PREFIX_GLOB,spec.group);
|
||||
assertEquals("Spec.group",PathSpecGroup.PREFIX_GLOB,spec.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"foo");
|
||||
|
||||
|
@ -150,7 +151,7 @@ public class WebSocketPathSpecTest
|
|||
assertEquals("Spec.pathSpec","/{var}/b/c",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/([^/]+)/b/c$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",3,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.SUFFIX_GLOB,spec.group);
|
||||
assertEquals("Spec.group",PathSpecGroup.SUFFIX_GLOB,spec.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var");
|
||||
|
||||
|
@ -174,7 +175,7 @@ public class WebSocketPathSpecTest
|
|||
assertEquals("Spec.pathSpec","/a/{var1}/c/{var2}/e",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/a/([^/]+)/c/([^/]+)/e$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",5,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.group);
|
||||
assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var1","var2");
|
||||
|
||||
|
@ -197,7 +198,7 @@ public class WebSocketPathSpecTest
|
|||
assertEquals("Spec.pathSpec","/{var1}/b/{var2}/{var3}",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/([^/]+)/b/([^/]+)/([^/]+)$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",4,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.group);
|
||||
assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var1","var2","var3");
|
||||
|
||||
|
@ -221,7 +222,7 @@ public class WebSocketPathSpecTest
|
|||
assertEquals("Spec.pathSpec","/a/{var1}/{var2}",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/a/([^/]+)/([^/]+)$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",3,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.PREFIX_GLOB,spec.group);
|
||||
assertEquals("Spec.group",PathSpecGroup.PREFIX_GLOB,spec.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var1","var2");
|
||||
|
||||
|
@ -244,7 +245,7 @@ public class WebSocketPathSpecTest
|
|||
assertEquals("Spec.pathSpec","/{var1}",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/([^/]+)$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",1,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.PREFIX_GLOB,spec.group);
|
||||
assertEquals("Spec.group",PathSpecGroup.PREFIX_GLOB,spec.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var1");
|
||||
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.samples.echo;
|
||||
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.Session;
|
||||
|
||||
/**
|
||||
* Example of websocket endpoint based on extending {@link Endpoint}
|
||||
*/
|
||||
public class BasicEchoEndpoint extends Endpoint implements MessageHandler.Whole<String>
|
||||
{
|
||||
private Session session;
|
||||
|
||||
@Override
|
||||
public void onMessage(String msg)
|
||||
{
|
||||
// reply with echo
|
||||
session.getAsyncRemote().sendText(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config)
|
||||
{
|
||||
this.session = session;
|
||||
this.session.addMessageHandler(this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.samples.echo;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.websocket.ContainerProvider;
|
||||
import javax.websocket.DeploymentException;
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.server.ServerContainer;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
/**
|
||||
* Example of adding a server WebSocket (extending {@link Endpoint}) programmatically via config
|
||||
*/
|
||||
public class BasicEchoEndpointConfigContextListener implements ServletContextListener
|
||||
{
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
ServerContainer container = (ServerContainer)ContainerProvider.getWebSocketContainer();
|
||||
// Build up a configuration with a specific path
|
||||
String path = "/echo";
|
||||
ServerEndpointConfig.Builder builder = ServerEndpointConfig.Builder.create(BasicEchoEndpoint.class,path);
|
||||
try
|
||||
{
|
||||
container.addEndpoint(builder.build());
|
||||
}
|
||||
catch (DeploymentException e)
|
||||
{
|
||||
throw new RuntimeException("Unable to add endpoint via config file",e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.samples.echo;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.websocket.ContainerProvider;
|
||||
import javax.websocket.DeploymentException;
|
||||
import javax.websocket.Endpoint;
|
||||
import javax.websocket.server.ServerContainer;
|
||||
|
||||
/**
|
||||
* Example of adding a server WebSocket (extending {@link Endpoint}) programmatically directly.
|
||||
* <p>
|
||||
* NOTE: this shouldn't work as the endpoint has no path associated with it.
|
||||
*/
|
||||
public class BasicEchoEndpointContextListener implements ServletContextListener
|
||||
{
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
ServerContainer container = (ServerContainer)ContainerProvider.getWebSocketContainer();
|
||||
try
|
||||
{
|
||||
// Should fail as there is no path associated with this endpoint
|
||||
container.addEndpoint(BasicEchoEndpoint.class);
|
||||
}
|
||||
catch (DeploymentException e)
|
||||
{
|
||||
throw new RuntimeException("Unable to add endpoint directly",e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,12 +16,15 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.server.samples;
|
||||
package org.eclipse.jetty.websocket.jsr356.server.samples.echo;
|
||||
|
||||
import javax.websocket.OnMessage;
|
||||
import javax.websocket.Session;
|
||||
import javax.websocket.server.ServerEndpoint;
|
||||
|
||||
/**
|
||||
* Annotated echo socket
|
||||
*/
|
||||
@ServerEndpoint("/echo")
|
||||
public class BasicEchoSocket
|
||||
{
|
|
@ -0,0 +1,56 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.samples.echo;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.websocket.ContainerProvider;
|
||||
import javax.websocket.DeploymentException;
|
||||
import javax.websocket.server.ServerContainer;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
/**
|
||||
* Example of adding a server socket (annotated) programmatically via config
|
||||
*/
|
||||
public class BasicEchoSocketConfigContextListener implements ServletContextListener
|
||||
{
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
ServerContainer container = (ServerContainer)ContainerProvider.getWebSocketContainer();
|
||||
// Build up a configuration with a specific path
|
||||
// Intentionally using alternate path in config (which differs from @ServerEndpoint declaration)
|
||||
String path = "/echo-alt";
|
||||
ServerEndpointConfig.Builder builder = ServerEndpointConfig.Builder.create(BasicEchoSocket.class,path);
|
||||
try
|
||||
{
|
||||
container.addEndpoint(builder.build());
|
||||
}
|
||||
catch (DeploymentException e)
|
||||
{
|
||||
throw new RuntimeException("Unable to add endpoint via config file",e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.samples.echo;
|
||||
|
||||
import javax.servlet.ServletContextEvent;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.websocket.ContainerProvider;
|
||||
import javax.websocket.DeploymentException;
|
||||
import javax.websocket.server.ServerContainer;
|
||||
|
||||
/**
|
||||
* Example of adding a server socket (annotated) programmatically directly with no config
|
||||
*/
|
||||
public class BasicEchoSocketContextListener implements ServletContextListener
|
||||
{
|
||||
@Override
|
||||
public void contextDestroyed(ServletContextEvent sce)
|
||||
{
|
||||
/* do nothing */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void contextInitialized(ServletContextEvent sce)
|
||||
{
|
||||
ServerContainer container = (ServerContainer)ContainerProvider.getWebSocketContainer();
|
||||
try
|
||||
{
|
||||
container.addEndpoint(BasicEchoSocket.class);
|
||||
}
|
||||
catch (DeploymentException e)
|
||||
{
|
||||
throw new RuntimeException("Unable to add endpoint directly",e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="http://java.sun.com/xml/ns/javaee"
|
||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
|
||||
metadata-complete="false"
|
||||
version="3.0">
|
||||
|
||||
<listener>
|
||||
<listener-class>org.eclipse.jetty.websocket.jsr356.server.samples.echo.BasicEchoEndpointConfigContextListener</listener-class>
|
||||
</listener>
|
||||
</web-app>
|
|
@ -0,0 +1,7 @@
|
|||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
org.eclipse.jetty.LEVEL=WARN
|
||||
|
||||
org.eclipse.jetty.websocket.LEVEL=DEBUG
|
||||
# org.eclipse.jetty.websocket.LEVEL=WARN
|
||||
# org.eclipse.jetty.websocket.common.io.LEVEL=DEBUG
|
||||
|
|
@ -137,7 +137,7 @@ public class WebSocketClient extends ContainerLifeCycle
|
|||
}
|
||||
|
||||
// Validate websocket URI
|
||||
LOG.debug("connect websocket:{} to:{}",websocket,toUri);
|
||||
LOG.debug("connect websocket {} to {}",websocket,toUri);
|
||||
|
||||
// Grab Connection Manager
|
||||
ConnectionManager manager = getConnectionManager();
|
||||
|
@ -163,6 +163,8 @@ public class WebSocketClient extends ContainerLifeCycle
|
|||
// Create the appropriate (physical vs virtual) connection task
|
||||
ConnectPromise promise = manager.connect(this,driver,request);
|
||||
|
||||
LOG.debug("Connect Promise: {}",promise);
|
||||
|
||||
// Execute the connection on the executor thread
|
||||
executor.execute(promise);
|
||||
|
||||
|
|
|
@ -489,7 +489,7 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
|
|||
{
|
||||
super.onOpen();
|
||||
this.ioState.setState(ConnectionState.OPEN);
|
||||
LOG.debug("fillInterested");
|
||||
LOG.debug("{} fillInterested",policy.getBehavior());
|
||||
fillInterested();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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;
|
||||
|
||||
/**
|
||||
* Common interface for MappedWebSocketCreator
|
||||
*/
|
||||
public interface MappedWebSocketCreator
|
||||
{
|
||||
public void addMapping(PathSpec spec, WebSocketCreator creator);
|
||||
|
||||
public PathMappings<WebSocketCreator> getMappings();
|
||||
}
|
|
@ -138,12 +138,16 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
|
|||
|
||||
@Override
|
||||
public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
return acceptWebSocket(getCreator(),request,response);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptWebSocket(WebSocketCreator creator, HttpServletRequest request, HttpServletResponse response) throws IOException
|
||||
{
|
||||
ServletWebSocketRequest sockreq = new ServletWebSocketRequest(request);
|
||||
ServletWebSocketResponse sockresp = new ServletWebSocketResponse(response);
|
||||
|
||||
WebSocketCreator creator = getCreator();
|
||||
|
||||
UpgradeContext context = getActiveUpgradeContext();
|
||||
if (context == null)
|
||||
{
|
||||
|
@ -272,22 +276,41 @@ public class WebSocketServerFactory extends ContainerLifeCycle implements WebSoc
|
|||
@Override
|
||||
public boolean isUpgradeRequest(HttpServletRequest request, HttpServletResponse response)
|
||||
{
|
||||
if (!"GET".equalsIgnoreCase(request.getMethod()))
|
||||
{
|
||||
// not a "GET" request (not a websocket upgrade)
|
||||
return false;
|
||||
}
|
||||
|
||||
String connection = request.getHeader("connection");
|
||||
if (connection == null)
|
||||
{
|
||||
// no "Connection: upgrade" header present.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!"upgrade".equalsIgnoreCase(connection))
|
||||
{
|
||||
LOG.debug("Not a 'Connection: Upgrade' (was [Connection: " + connection + "])");
|
||||
return false;
|
||||
}
|
||||
|
||||
String upgrade = request.getHeader("Upgrade");
|
||||
if (upgrade == null)
|
||||
{
|
||||
// Quietly fail
|
||||
// no "Upgrade: websocket" header present.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!"websocket".equalsIgnoreCase(upgrade))
|
||||
{
|
||||
LOG.warn("Not a 'Upgrade: WebSocket' (was [Upgrade: " + upgrade + "])");
|
||||
LOG.debug("Not a 'Upgrade: WebSocket' (was [Upgrade: " + upgrade + "])");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!"HTTP/1.1".equals(request.getProtocol()))
|
||||
{
|
||||
LOG.warn("Not a 'HTTP/1.1' request (was [" + request.getProtocol() + "])");
|
||||
LOG.debug("Not a 'HTTP/1.1' request (was [" + request.getProtocol() + "])");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,188 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.io.IOException;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketBehavior;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketPolicy;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathMappings.MappedResource;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory;
|
||||
|
||||
/**
|
||||
* Inline Servlet Filter to capture WebSocket upgrade requests and perform path mappings to {@link WebSocketCreator} objects.
|
||||
*/
|
||||
@ManagedObject("WebSocket Upgrade Filter")
|
||||
public class WebSocketUpgradeFilter implements Filter, MappedWebSocketCreator, Dumpable
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(WebSocketUpgradeFilter.class);
|
||||
private PathMappings<WebSocketCreator> pathmap = new PathMappings<>();
|
||||
private WebSocketServletFactory factory;
|
||||
|
||||
@Override
|
||||
public void addMapping(PathSpec spec, WebSocketCreator creator)
|
||||
{
|
||||
pathmap.put(spec,creator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
factory.cleanup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
|
||||
{
|
||||
if (factory == null)
|
||||
{
|
||||
// no factory, cannot operate
|
||||
LOG.debug("WebSocketUpgradeFilter is not operational - no WebSocketServletFactory configured");
|
||||
chain.doFilter(request,response);
|
||||
return;
|
||||
}
|
||||
|
||||
if ((request instanceof HttpServletRequest) && (request instanceof HttpServletResponse))
|
||||
{
|
||||
HttpServletRequest httpreq = (HttpServletRequest)request;
|
||||
HttpServletResponse httpresp = (HttpServletResponse)response;
|
||||
String target = httpreq.getPathInfo();
|
||||
|
||||
if (factory.isUpgradeRequest(httpreq,httpresp))
|
||||
{
|
||||
MappedResource<WebSocketCreator> resource = pathmap.getMatch(target);
|
||||
if (resource == null)
|
||||
{
|
||||
// no match.
|
||||
httpresp.sendError(HttpServletResponse.SC_NOT_FOUND,"No websocket endpoint matching path: " + target);
|
||||
return;
|
||||
}
|
||||
|
||||
WebSocketCreator creator = resource.getResource();
|
||||
|
||||
// Store PathSpec resource mapping as request attribute
|
||||
httpreq.setAttribute(PathSpec.class.getName(),resource);
|
||||
|
||||
// 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)
|
||||
if (response.isCommitted())
|
||||
{
|
||||
// not much we can do at this point.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// not an Upgrade request
|
||||
chain.doFilter(request,response);
|
||||
return;
|
||||
}
|
||||
|
||||
@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 + " ");
|
||||
}
|
||||
|
||||
@ManagedAttribute(value = "mappings", readonly = true)
|
||||
@Override
|
||||
public PathMappings<WebSocketCreator> getMappings()
|
||||
{
|
||||
return pathmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig config) throws ServletException
|
||||
{
|
||||
try
|
||||
{
|
||||
WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER);
|
||||
|
||||
String max = config.getInitParameter("maxIdleTime");
|
||||
if (max != null)
|
||||
{
|
||||
policy.setIdleTimeout(Long.parseLong(max));
|
||||
}
|
||||
|
||||
max = config.getInitParameter("maxTextMessageSize");
|
||||
if (max != null)
|
||||
{
|
||||
policy.setMaxTextMessageSize(Integer.parseInt(max));
|
||||
}
|
||||
|
||||
max = config.getInitParameter("maxBinaryMessageSize");
|
||||
if (max != null)
|
||||
{
|
||||
policy.setMaxBinaryMessageSize(Integer.parseInt(max));
|
||||
}
|
||||
|
||||
max = config.getInitParameter("inputBufferSize");
|
||||
if (max != null)
|
||||
{
|
||||
policy.setInputBufferSize(Integer.parseInt(max));
|
||||
}
|
||||
|
||||
factory = WebSocketServletFactory.Loader.create(policy);
|
||||
factory.init();
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
throw new ServletException(x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[factory=%s,pathmap=%s]",this.getClass().getSimpleName(),factory,pathmap);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.io.IOException;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.handler.HandlerWrapper;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathMappings.MappedResource;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
||||
public class WebSocketUpgradeHandlerWrapper extends HandlerWrapper implements MappedWebSocketCreator
|
||||
{
|
||||
private PathMappings<WebSocketCreator> pathmap = new PathMappings<>();
|
||||
private final WebSocketServerFactory factory;
|
||||
|
||||
public WebSocketUpgradeHandlerWrapper()
|
||||
{
|
||||
factory = new WebSocketServerFactory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addMapping(PathSpec spec, WebSocketCreator creator)
|
||||
{
|
||||
pathmap.put(spec,creator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PathMappings<WebSocketCreator> getMappings()
|
||||
{
|
||||
return pathmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
|
||||
{
|
||||
if (factory.isUpgradeRequest(request,response))
|
||||
{
|
||||
MappedResource<WebSocketCreator> resource = pathmap.getMatch(target);
|
||||
if (resource == null)
|
||||
{
|
||||
// no match.
|
||||
response.sendError(HttpServletResponse.SC_NOT_FOUND,"No websocket endpoint matching path: " + target);
|
||||
return;
|
||||
}
|
||||
|
||||
WebSocketCreator creator = resource.getResource();
|
||||
|
||||
// Store PathSpec resource mapping as request attribute
|
||||
request.setAttribute(PathSpec.class.getName(),resource);
|
||||
|
||||
// We have an upgrade request
|
||||
if (factory.acceptWebSocket(creator,request,response))
|
||||
{
|
||||
// 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)
|
||||
if (response.isCommitted())
|
||||
{
|
||||
// not much we can do at this point.
|
||||
return;
|
||||
}
|
||||
}
|
||||
super.handle(target,baseRequest,request,response);
|
||||
}
|
||||
}
|
|
@ -16,14 +16,21 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.server.pathmap;
|
||||
package org.eclipse.jetty.websocket.server.pathmap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.websocket.jsr356.server.pathmap.PathMappings.MappedResource;
|
||||
import org.eclipse.jetty.util.annotation.ManagedAttribute;
|
||||
import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||
import org.eclipse.jetty.util.component.ContainerLifeCycle;
|
||||
import org.eclipse.jetty.util.component.Dumpable;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathMappings.MappedResource;
|
||||
|
||||
/**
|
||||
* Path Mappings of PathSpec to Resource.
|
||||
|
@ -32,8 +39,10 @@ import org.eclipse.jetty.websocket.jsr356.server.pathmap.PathMappings.MappedReso
|
|||
*
|
||||
* @param <E>
|
||||
*/
|
||||
public class PathMappings<E> implements Iterable<MappedResource<E>>
|
||||
@ManagedObject("Path Mappings")
|
||||
public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
||||
{
|
||||
@ManagedObject("Mapped Resource")
|
||||
public static class MappedResource<E> implements Comparable<MappedResource<E>>
|
||||
{
|
||||
private final PathSpec pathSpec;
|
||||
|
@ -84,11 +93,13 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>
|
|||
return true;
|
||||
}
|
||||
|
||||
@ManagedAttribute(value = "path spec", readonly = true)
|
||||
public PathSpec getPathSpec()
|
||||
{
|
||||
return pathSpec;
|
||||
}
|
||||
|
||||
@ManagedAttribute(value = "resource", readonly = true)
|
||||
public E getResource()
|
||||
{
|
||||
return resource;
|
||||
|
@ -110,9 +121,28 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>
|
|||
}
|
||||
}
|
||||
|
||||
private static final Logger LOG = Log.getLogger(PathMappings.class);
|
||||
private List<MappedResource<E>> mappings = new ArrayList<MappedResource<E>>();
|
||||
private MappedResource<E> defaultResource = null;
|
||||
|
||||
@Override
|
||||
public String dump()
|
||||
{
|
||||
return ContainerLifeCycle.dump(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dump(Appendable out, String indent) throws IOException
|
||||
{
|
||||
ContainerLifeCycle.dump(out,indent,mappings);
|
||||
}
|
||||
|
||||
@ManagedAttribute(value = "mappings", readonly = true)
|
||||
public List<MappedResource<E>> getMappings()
|
||||
{
|
||||
return mappings;
|
||||
}
|
||||
|
||||
public MappedResource<E> getMatch(String path)
|
||||
{
|
||||
int len = mappings.size();
|
||||
|
@ -142,6 +172,13 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>
|
|||
}
|
||||
// TODO: warning on replacement of existing mapping?
|
||||
mappings.add(entry);
|
||||
LOG.debug("Added {} to {}",entry,this);
|
||||
Collections.sort(mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[size=%d]",this.getClass().getSimpleName(),mappings.size());
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.server.pathmap;
|
||||
package org.eclipse.jetty.websocket.server.pathmap;
|
||||
|
||||
/**
|
||||
* The base PathSpec, what all other path specs are based on
|
||||
|
@ -79,6 +79,11 @@ public abstract class PathSpec implements Comparable<PathSpec>
|
|||
return true;
|
||||
}
|
||||
|
||||
public PathSpecGroup getGroup()
|
||||
{
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of path elements that this path spec declares.
|
||||
* <p>
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.server.pathmap;
|
||||
package org.eclipse.jetty.websocket.server.pathmap;
|
||||
|
||||
/**
|
||||
* Types of path spec groups.
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.server.pathmap;
|
||||
package org.eclipse.jetty.websocket.server.pathmap;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.server.pathmap;
|
||||
package org.eclipse.jetty.websocket.server.pathmap;
|
||||
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.server.pathmap;
|
||||
package org.eclipse.jetty.websocket.server.pathmap;
|
||||
|
||||
import java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -26,6 +26,8 @@ import org.eclipse.jetty.toolchain.test.AdvancedRunner;
|
|||
import org.eclipse.jetty.toolchain.test.annotation.Stress;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.ServletPathSpec;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
|
@ -0,0 +1,117 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.pathmap;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathMappings.MappedResource;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class PathMappingsTest
|
||||
{
|
||||
private void assertMatch(PathMappings<String> pathmap, String path, String expectedValue)
|
||||
{
|
||||
String msg = String.format(".getMatch(\"%s\")",path);
|
||||
MappedResource<String> match = pathmap.getMatch(path);
|
||||
Assert.assertThat(msg,match,notNullValue());
|
||||
String actualMatch = match.getResource();
|
||||
Assert.assertEquals(msg,expectedValue,actualMatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the match order rules with a mixed Servlet and WebSocket path specs
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>Exact match</li>
|
||||
* <li>Longest prefix match</li>
|
||||
* <li>Longest suffix match</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Test
|
||||
public void testMixedMatchOrder()
|
||||
{
|
||||
PathMappings<String> p = new PathMappings<>();
|
||||
|
||||
p.put(new ServletPathSpec("/"),"default");
|
||||
p.put(new ServletPathSpec("/animal/bird/*"),"birds");
|
||||
p.put(new ServletPathSpec("/animal/fish/*"),"fishes");
|
||||
p.put(new ServletPathSpec("/animal/*"),"animals");
|
||||
p.put(new RegexPathSpec("^/animal/.*/chat$"),"animalChat");
|
||||
p.put(new RegexPathSpec("^/animal/.*/cam$"),"animalCam");
|
||||
p.put(new RegexPathSpec("^/entrance/cam$"),"entranceCam");
|
||||
|
||||
for (MappedResource<String> res : p)
|
||||
{
|
||||
System.out.printf(" %s%n",res);
|
||||
}
|
||||
|
||||
assertMatch(p,"/animal/bird/eagle","birds");
|
||||
assertMatch(p,"/animal/fish/bass/sea","fishes");
|
||||
assertMatch(p,"/animal/peccary/javalina/evolution","animals");
|
||||
assertMatch(p,"/","default");
|
||||
assertMatch(p,"/animal/bird/eagle/chat","animalChat");
|
||||
assertMatch(p,"/animal/bird/penguin/chat","animalChat");
|
||||
assertMatch(p,"/animal/fish/trout/cam","animalCam");
|
||||
assertMatch(p,"/entrance/cam","entranceCam");
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the match order rules imposed by the Servlet API.
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>Exact match</li>
|
||||
* <li>Longest prefix match</li>
|
||||
* <li>Longest suffix match</li>
|
||||
* <li>default</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Test
|
||||
public void testServletMatchOrder()
|
||||
{
|
||||
PathMappings<String> p = new PathMappings<>();
|
||||
|
||||
p.put(new ServletPathSpec("/abs/path"),"path");
|
||||
p.put(new ServletPathSpec("/abs/path/longer"),"longpath");
|
||||
p.put(new ServletPathSpec("/animal/bird/*"),"birds");
|
||||
p.put(new ServletPathSpec("/animal/fish/*"),"fishes");
|
||||
p.put(new ServletPathSpec("/animal/*"),"animals");
|
||||
p.put(new ServletPathSpec("*.tar.gz"),"tarball");
|
||||
p.put(new ServletPathSpec("*.gz"),"gzipped");
|
||||
p.put(new ServletPathSpec("/"),"default");
|
||||
|
||||
for (MappedResource<String> res : p)
|
||||
{
|
||||
System.out.printf(" %s%n",res);
|
||||
}
|
||||
|
||||
assertMatch(p,"/abs/path","path");
|
||||
assertMatch(p,"/abs/path/longer","longpath");
|
||||
assertMatch(p,"/abs/path/foo","default");
|
||||
assertMatch(p,"/main.css","default");
|
||||
assertMatch(p,"/downloads/script.gz","gzipped");
|
||||
assertMatch(p,"/downloads/distribution.tar.gz","tarball");
|
||||
assertMatch(p,"/downloads/readme.txt","default");
|
||||
assertMatch(p,"/downloads/logs.tgz","default");
|
||||
assertMatch(p,"/animal/horse/mustang","animals");
|
||||
assertMatch(p,"/animal/bird/eagle/bald","birds");
|
||||
assertMatch(p,"/animal/fish/shark/hammerhead","fishes");
|
||||
assertMatch(p,"/animal/insect/ladybug","animals");
|
||||
}
|
||||
}
|
|
@ -16,7 +16,7 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.server.pathmap;
|
||||
package org.eclipse.jetty.websocket.server.pathmap;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
@ -73,6 +73,27 @@ public class RegexPathSpecTest
|
|||
assertNotMatches(spec,"/rest/list");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMiddleSpecNoGrouping()
|
||||
{
|
||||
RegexPathSpec spec = new RegexPathSpec("^/rest/[^/]+/list$");
|
||||
assertEquals("Spec.pathSpec","^/rest/[^/]+/list$",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/rest/[^/]+/list$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",3,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.MIDDLE_GLOB,spec.group);
|
||||
|
||||
assertMatches(spec,"/rest/api/list");
|
||||
assertMatches(spec,"/rest/1.0/list");
|
||||
assertMatches(spec,"/rest/2.0/list");
|
||||
assertMatches(spec,"/rest/accounts/list");
|
||||
|
||||
assertNotMatches(spec,"/a");
|
||||
assertNotMatches(spec,"/aa");
|
||||
assertNotMatches(spec,"/aa/bb");
|
||||
assertNotMatches(spec,"/rest/admin/delete");
|
||||
assertNotMatches(spec,"/rest/list");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrefixSpec()
|
||||
{
|
|
@ -16,11 +16,12 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket.jsr356.server.pathmap;
|
||||
package org.eclipse.jetty.websocket.server.pathmap;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import org.eclipse.jetty.websocket.server.pathmap.ServletPathSpec;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ServletPathSpecTest
|
|
@ -19,8 +19,6 @@
|
|||
package org.eclipse.jetty.websocket.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
|
@ -128,24 +126,7 @@ public abstract class WebSocketServlet extends HttpServlet
|
|||
policy.setInputBufferSize(Integer.parseInt(max));
|
||||
}
|
||||
|
||||
WebSocketServletFactory baseFactory;
|
||||
Iterator<WebSocketServletFactory> factories = ServiceLoader.load(WebSocketServletFactory.class).iterator();
|
||||
|
||||
if (factories.hasNext())
|
||||
{
|
||||
baseFactory = factories.next();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load the default class if ServiceLoader mechanism isn't valid in this environment. (such as OSGi)
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<WebSocketServletFactory> wssf = (Class<WebSocketServletFactory>)loader
|
||||
.loadClass("org.eclipse.jetty.websocket.server.WebSocketServerFactory");
|
||||
baseFactory = wssf.newInstance();
|
||||
}
|
||||
|
||||
factory = baseFactory.createFactory(policy);
|
||||
factory = WebSocketServletFactory.Loader.create(policy);
|
||||
|
||||
configure(factory);
|
||||
|
||||
|
|
|
@ -19,6 +19,8 @@
|
|||
package org.eclipse.jetty.websocket.servlet;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -32,8 +34,47 @@ import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
|
|||
*/
|
||||
public interface WebSocketServletFactory
|
||||
{
|
||||
public static class Loader
|
||||
{
|
||||
private static WebSocketServletFactory INSTANCE;
|
||||
|
||||
public static WebSocketServletFactory create(WebSocketPolicy policy) throws ClassNotFoundException, InstantiationException, IllegalAccessException
|
||||
{
|
||||
return load(policy).createFactory(policy);
|
||||
}
|
||||
|
||||
public static WebSocketServletFactory load(WebSocketPolicy policy) throws ClassNotFoundException, InstantiationException, IllegalAccessException
|
||||
{
|
||||
if (INSTANCE != null)
|
||||
{
|
||||
return INSTANCE;
|
||||
}
|
||||
WebSocketServletFactory baseFactory;
|
||||
Iterator<WebSocketServletFactory> factories = ServiceLoader.load(WebSocketServletFactory.class).iterator();
|
||||
|
||||
if (factories.hasNext())
|
||||
{
|
||||
baseFactory = factories.next();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Load the default class if ServiceLoader mechanism isn't valid in this environment. (such as OSGi)
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<WebSocketServletFactory> wssf = (Class<WebSocketServletFactory>)loader
|
||||
.loadClass("org.eclipse.jetty.websocket.server.WebSocketServerFactory");
|
||||
baseFactory = wssf.newInstance();
|
||||
}
|
||||
|
||||
INSTANCE = baseFactory;
|
||||
return INSTANCE;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean acceptWebSocket(HttpServletRequest request, HttpServletResponse response) throws IOException;
|
||||
|
||||
public boolean acceptWebSocket(WebSocketCreator creator, HttpServletRequest request, HttpServletResponse response) throws IOException;
|
||||
|
||||
public void cleanup();
|
||||
|
||||
public WebSocketServletFactory createFactory(WebSocketPolicy policy);
|
||||
|
|
Loading…
Reference in New Issue