484440 - Swap WebSocket PathMappings for new jetty-http PathMappings
+ Deprecated jetty-server ServletPathSpec and RegexPathSpec + Moved all other code to using new jetty-http versions
This commit is contained in:
parent
7c5bec1b48
commit
3bec195d08
|
@ -71,8 +71,10 @@ import org.eclipse.jetty.util.URIUtil;
|
|||
* This class is not synchronized. If concurrent modifications are
|
||||
* possible then it should be synchronized at a higher level.
|
||||
*
|
||||
* @param <O> the Map.Entry value type
|
||||
* @param <O> the Map.Entry value type
|
||||
* @deprecated replaced with {@link org.eclipse.jetty.http.pathmap.PathMappings} (this class will be removed in Jetty 10)
|
||||
*/
|
||||
@Deprecated
|
||||
public class PathMap<O> extends HashMap<String,O>
|
||||
{
|
||||
/* ------------------------------------------------------------ */
|
||||
|
|
|
@ -29,6 +29,8 @@ import javax.websocket.Extension.Parameter;
|
|||
import javax.websocket.server.ServerEndpointConfig;
|
||||
import javax.websocket.server.ServerEndpointConfig.Configurator;
|
||||
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
|
||||
import org.eclipse.jetty.util.StringUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
|
@ -37,8 +39,6 @@ import org.eclipse.jetty.websocket.api.extensions.ExtensionFactory;
|
|||
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
|
||||
import org.eclipse.jetty.websocket.jsr356.JsrExtension;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.pathmap.WebSocketPathSpec;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
@ -148,10 +148,10 @@ public class JsrCreator implements WebSocketCreator
|
|||
// Do not decorate here (let the Connection and Session start first)
|
||||
// This will allow CDI to see Session for injection into Endpoint classes.
|
||||
PathSpec pathSpec = hsreq.getRequestPathSpec();
|
||||
if (pathSpec instanceof WebSocketPathSpec)
|
||||
if (pathSpec instanceof UriTemplatePathSpec)
|
||||
{
|
||||
// We have a PathParam path spec
|
||||
WebSocketPathSpec wspathSpec = (WebSocketPathSpec)pathSpec;
|
||||
UriTemplatePathSpec wspathSpec = (UriTemplatePathSpec)pathSpec;
|
||||
String requestPath = req.getRequestPath();
|
||||
// Wrap the config with the path spec information
|
||||
config = new PathParamServerEndpointConfig(containerScope,config,wspathSpec,requestPath);
|
||||
|
|
|
@ -25,7 +25,7 @@ import java.util.Map;
|
|||
|
||||
import javax.websocket.server.HandshakeRequest;
|
||||
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest;
|
||||
|
||||
public class JsrHandshakeRequest implements HandshakeRequest
|
||||
|
|
|
@ -23,8 +23,8 @@ import java.util.Map;
|
|||
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
|
||||
import org.eclipse.jetty.websocket.common.scopes.WebSocketContainerScope;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.pathmap.WebSocketPathSpec;
|
||||
|
||||
/**
|
||||
* Wrapper for a {@link ServerEndpointConfig} where there PathParm information from the incoming request.
|
||||
|
@ -33,7 +33,7 @@ public class PathParamServerEndpointConfig extends BasicServerEndpointConfig imp
|
|||
{
|
||||
private final Map<String, String> pathParamMap;
|
||||
|
||||
public PathParamServerEndpointConfig(WebSocketContainerScope containerScope, ServerEndpointConfig config, WebSocketPathSpec pathSpec, String requestPath)
|
||||
public PathParamServerEndpointConfig(WebSocketContainerScope containerScope, ServerEndpointConfig config, UriTemplatePathSpec pathSpec, String requestPath)
|
||||
{
|
||||
super(containerScope, config);
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ import javax.websocket.Endpoint;
|
|||
import javax.websocket.server.ServerEndpoint;
|
||||
import javax.websocket.server.ServerEndpointConfig;
|
||||
|
||||
import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.common.events.EventDriverFactory;
|
||||
|
@ -35,7 +36,6 @@ import org.eclipse.jetty.websocket.jsr356.JsrSessionFactory;
|
|||
import org.eclipse.jetty.websocket.jsr356.annotations.AnnotatedEndpointScanner;
|
||||
import org.eclipse.jetty.websocket.jsr356.endpoints.EndpointInstance;
|
||||
import org.eclipse.jetty.websocket.jsr356.metadata.EndpointMetadata;
|
||||
import org.eclipse.jetty.websocket.jsr356.server.pathmap.WebSocketPathSpec;
|
||||
import org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.server.WebSocketServerFactory;
|
||||
|
||||
|
@ -99,7 +99,7 @@ public class ServerContainer extends ClientContainer implements javax.websocket.
|
|||
private void addEndpoint(ServerEndpointMetadata metadata) throws DeploymentException
|
||||
{
|
||||
JsrCreator creator = new JsrCreator(this,metadata,webSocketServerFactory.getExtensionFactory());
|
||||
mappedCreator.addMapping(new WebSocketPathSpec(metadata.getPath()),creator);
|
||||
mappedCreator.addMapping(new UriTemplatePathSpec(metadata.getPath()),creator);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,347 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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.pathmap;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
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.util.TypeUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathSpecGroup;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.RegexPathSpec;
|
||||
|
||||
/**
|
||||
* PathSpec for WebSocket @{@link ServerEndpoint} declarations with support for URI templates and @{@link PathParam} annotations
|
||||
*
|
||||
* @see javax.websocket spec (JSR-356) Section 3.1.1 URI Mapping
|
||||
* @see <a href="https://tools.ietf.org/html/rfc6570">URI Templates (Level 1)</a>
|
||||
*/
|
||||
public class WebSocketPathSpec extends RegexPathSpec
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(WebSocketPathSpec.class);
|
||||
|
||||
private static final Pattern VARIABLE_PATTERN = Pattern.compile("\\{(.*)\\}");
|
||||
/** Reserved Symbols in URI Template variable */
|
||||
private static final String VARIABLE_RESERVED = ":/?#[]@" + // gen-delims
|
||||
"!$&'()*+,;="; // sub-delims
|
||||
/** Allowed Symboles in a URI Template variable */
|
||||
private static final String VARIABLE_SYMBOLS="-._";
|
||||
private static final Set<String> FORBIDDEN_SEGMENTS;
|
||||
|
||||
static
|
||||
{
|
||||
FORBIDDEN_SEGMENTS = new HashSet<>();
|
||||
FORBIDDEN_SEGMENTS.add("/./");
|
||||
FORBIDDEN_SEGMENTS.add("/../");
|
||||
FORBIDDEN_SEGMENTS.add("//");
|
||||
}
|
||||
|
||||
private String variables[];
|
||||
|
||||
public WebSocketPathSpec(String pathParamSpec)
|
||||
{
|
||||
super();
|
||||
Objects.requireNonNull(pathParamSpec,"Path Param Spec cannot be null");
|
||||
|
||||
if ("".equals(pathParamSpec) || "/".equals(pathParamSpec))
|
||||
{
|
||||
super.pathSpec = "/";
|
||||
super.pattern = Pattern.compile("^/$");
|
||||
super.pathDepth = 1;
|
||||
this.specLength = 1;
|
||||
this.variables = new String[0];
|
||||
this.group = PathSpecGroup.EXACT;
|
||||
return;
|
||||
}
|
||||
|
||||
if (pathParamSpec.charAt(0) != '/')
|
||||
{
|
||||
// path specs must start with '/'
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Syntax Error: path spec \"");
|
||||
err.append(pathParamSpec);
|
||||
err.append("\" must start with '/'");
|
||||
throw new IllegalArgumentException(err.toString());
|
||||
}
|
||||
|
||||
for (String forbidden : FORBIDDEN_SEGMENTS)
|
||||
{
|
||||
if (pathParamSpec.contains(forbidden))
|
||||
{
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Syntax Error: segment ");
|
||||
err.append(forbidden);
|
||||
err.append(" is forbidden in path spec: ");
|
||||
err.append(pathParamSpec);
|
||||
throw new IllegalArgumentException(err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
this.pathSpec = pathParamSpec;
|
||||
|
||||
StringBuilder regex = new StringBuilder();
|
||||
regex.append('^');
|
||||
|
||||
List<String> varNames = new ArrayList<>();
|
||||
// split up into path segments (ignoring the first slash that will always be empty)
|
||||
String segments[] = pathParamSpec.substring(1).split("/");
|
||||
char segmentSignature[] = new char[segments.length];
|
||||
this.pathDepth = segments.length;
|
||||
for (int i = 0; i < segments.length; i++)
|
||||
{
|
||||
String segment = segments[i];
|
||||
Matcher mat = VARIABLE_PATTERN.matcher(segment);
|
||||
|
||||
if (mat.matches())
|
||||
{
|
||||
// entire path segment is a variable.
|
||||
String variable = mat.group(1);
|
||||
if (varNames.contains(variable))
|
||||
{
|
||||
// duplicate variable names
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Syntax Error: variable ");
|
||||
err.append(variable);
|
||||
err.append(" is duplicated in path spec: ");
|
||||
err.append(pathParamSpec);
|
||||
throw new IllegalArgumentException(err.toString());
|
||||
}
|
||||
|
||||
assertIsValidVariableLiteral(variable);
|
||||
|
||||
segmentSignature[i] = 'v'; // variable
|
||||
// valid variable name
|
||||
varNames.add(variable);
|
||||
// build regex
|
||||
regex.append("/([^/]+)");
|
||||
}
|
||||
else if (mat.find(0))
|
||||
{
|
||||
// variable exists as partial segment
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Syntax Error: variable ");
|
||||
err.append(mat.group());
|
||||
err.append(" must exist as entire path segment: ");
|
||||
err.append(pathParamSpec);
|
||||
throw new IllegalArgumentException(err.toString());
|
||||
}
|
||||
else if ((segment.indexOf('{') >= 0) || (segment.indexOf('}') >= 0))
|
||||
{
|
||||
// variable is split with a path separator
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Syntax Error: invalid path segment /");
|
||||
err.append(segment);
|
||||
err.append("/ variable declaration incomplete: ");
|
||||
err.append(pathParamSpec);
|
||||
throw new IllegalArgumentException(err.toString());
|
||||
}
|
||||
else if (segment.indexOf('*') >= 0)
|
||||
{
|
||||
// glob segment
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Syntax Error: path segment /");
|
||||
err.append(segment);
|
||||
err.append("/ contains a wildcard symbol (not supported by javax.websocket): ");
|
||||
err.append(pathParamSpec);
|
||||
throw new IllegalArgumentException(err.toString());
|
||||
}
|
||||
else
|
||||
{
|
||||
// valid path segment
|
||||
segmentSignature[i] = 'e'; // exact
|
||||
// build regex
|
||||
regex.append('/');
|
||||
// escape regex special characters
|
||||
for (char c : segment.toCharArray())
|
||||
{
|
||||
if ((c == '.') || (c == '[') || (c == ']') || (c == '\\'))
|
||||
{
|
||||
regex.append('\\');
|
||||
}
|
||||
regex.append(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle trailing slash (which is not picked up during split)
|
||||
if(pathParamSpec.charAt(pathParamSpec.length()-1) == '/')
|
||||
{
|
||||
regex.append('/');
|
||||
}
|
||||
|
||||
regex.append('$');
|
||||
|
||||
this.pattern = Pattern.compile(regex.toString());
|
||||
|
||||
int varcount = varNames.size();
|
||||
this.variables = varNames.toArray(new String[varcount]);
|
||||
|
||||
// Convert signature to group
|
||||
String sig = String.valueOf(segmentSignature);
|
||||
|
||||
if (Pattern.matches("^e*$",sig))
|
||||
{
|
||||
this.group = PathSpecGroup.EXACT;
|
||||
}
|
||||
else if (Pattern.matches("^e*v+",sig))
|
||||
{
|
||||
this.group = PathSpecGroup.PREFIX_GLOB;
|
||||
}
|
||||
else if (Pattern.matches("^v+e+",sig))
|
||||
{
|
||||
this.group = PathSpecGroup.SUFFIX_GLOB;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.group = PathSpecGroup.MIDDLE_GLOB;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate variable literal name, per RFC6570, Section 2.1 Literals
|
||||
* @param variable
|
||||
* @param pathParamSpec
|
||||
*/
|
||||
private void assertIsValidVariableLiteral(String variable)
|
||||
{
|
||||
int len = variable.length();
|
||||
|
||||
int i = 0;
|
||||
int codepoint;
|
||||
boolean valid = (len > 0); // must not be zero length
|
||||
|
||||
while (valid && i < len)
|
||||
{
|
||||
codepoint = variable.codePointAt(i);
|
||||
i += Character.charCount(codepoint);
|
||||
|
||||
// basic letters, digits, or symbols
|
||||
if (isValidBasicLiteralCodepoint(codepoint))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// The ucschar and iprivate pieces
|
||||
if (Character.isSupplementaryCodePoint(codepoint))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// pct-encoded
|
||||
if (codepoint == '%')
|
||||
{
|
||||
if (i + 2 > len)
|
||||
{
|
||||
// invalid percent encoding, missing extra 2 chars
|
||||
valid = false;
|
||||
continue;
|
||||
}
|
||||
codepoint = TypeUtil.convertHexDigit(variable.codePointAt(i++)) << 4;
|
||||
codepoint |= TypeUtil.convertHexDigit(variable.codePointAt(i++));
|
||||
|
||||
// validate basic literal
|
||||
if (isValidBasicLiteralCodepoint(codepoint))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
valid = false;
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
// invalid variable name
|
||||
StringBuilder err = new StringBuilder();
|
||||
err.append("Syntax Error: variable {");
|
||||
err.append(variable);
|
||||
err.append("} an invalid variable name: ");
|
||||
err.append(pathSpec);
|
||||
throw new IllegalArgumentException(err.toString());
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isValidBasicLiteralCodepoint(int codepoint)
|
||||
{
|
||||
// basic letters or digits
|
||||
if((codepoint >= 'a' && codepoint <= 'z') ||
|
||||
(codepoint >= 'A' && codepoint <= 'Z') ||
|
||||
(codepoint >= '0' && codepoint <= '9'))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// basic allowed symbols
|
||||
if(VARIABLE_SYMBOLS.indexOf(codepoint) >= 0)
|
||||
{
|
||||
return true; // valid simple value
|
||||
}
|
||||
|
||||
// basic reserved symbols
|
||||
if(VARIABLE_RESERVED.indexOf(codepoint) >= 0)
|
||||
{
|
||||
LOG.warn("Detected URI Template reserved symbol [{}] in path spec \"{}\"",(char)codepoint,pathSpec);
|
||||
return false; // valid simple value
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map<String, String> getPathParams(String path)
|
||||
{
|
||||
Matcher matcher = getMatcher(path);
|
||||
if (matcher.matches())
|
||||
{
|
||||
if (group == PathSpecGroup.EXACT)
|
||||
{
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, String> ret = new HashMap<>();
|
||||
int groupCount = matcher.groupCount();
|
||||
for (int i = 1; i <= groupCount; i++)
|
||||
{
|
||||
ret.put(this.variables[i - 1],matcher.group(i));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public int getVariableCount()
|
||||
{
|
||||
return variables.length;
|
||||
}
|
||||
|
||||
public String[] getVariables()
|
||||
{
|
||||
return this.variables;
|
||||
}
|
||||
}
|
|
@ -18,9 +18,9 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.jsr356.server;
|
||||
|
||||
import org.eclipse.jetty.http.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.websocket.server.MappedWebSocketCreator;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
||||
public class DummyCreator implements MappedWebSocketCreator
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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.pathmap;
|
||||
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
|
||||
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;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void dumpMappings(PathMappings<String> p)
|
||||
{
|
||||
for (MappedResource<String> res : p)
|
||||
{
|
||||
System.out.printf(" %s%n",res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 WebSocketPathSpec("/animal/{type}/{name}/chat"),"animalChat");
|
||||
p.put(new WebSocketPathSpec("/animal/{type}/{name}/cam"),"animalCam");
|
||||
p.put(new WebSocketPathSpec("/entrance/cam"),"entranceCam");
|
||||
|
||||
// dumpMappings(p);
|
||||
|
||||
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 WebSocket API (JSR-356)
|
||||
* <p>
|
||||
* <ul>
|
||||
* <li>Exact match</li>
|
||||
* <li>Longest prefix match</li>
|
||||
* <li>Longest suffix match</li>
|
||||
* </ul>
|
||||
*/
|
||||
@Test
|
||||
public void testWebsocketMatchOrder()
|
||||
{
|
||||
PathMappings<String> p = new PathMappings<>();
|
||||
|
||||
p.put(new WebSocketPathSpec("/a/{var}/c"),"endpointA");
|
||||
p.put(new WebSocketPathSpec("/a/b/c"),"endpointB");
|
||||
p.put(new WebSocketPathSpec("/a/{var1}/{var2}"),"endpointC");
|
||||
p.put(new WebSocketPathSpec("/{var1}/d"),"endpointD");
|
||||
p.put(new WebSocketPathSpec("/b/{var2}"),"endpointE");
|
||||
|
||||
// dumpMappings(p);
|
||||
|
||||
assertMatch(p,"/a/b/c","endpointB");
|
||||
assertMatch(p,"/a/d/c","endpointA");
|
||||
assertMatch(p,"/a/x/y","endpointC");
|
||||
|
||||
assertMatch(p,"/b/d","endpointE");
|
||||
}
|
||||
}
|
|
@ -1,87 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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.pathmap;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.junit.runners.Parameterized.Parameters;
|
||||
|
||||
/**
|
||||
* Tests for bad path specs on ServerEndpoint Path Param / URI Template
|
||||
*/
|
||||
@RunWith(Parameterized.class)
|
||||
public class WebSocketPathSpecBadSpecsTest
|
||||
{
|
||||
private static void bad(List<String[]> data, String str)
|
||||
{
|
||||
data.add(new String[]
|
||||
{ str });
|
||||
}
|
||||
|
||||
@Parameters
|
||||
public static Collection<String[]> data()
|
||||
{
|
||||
List<String[]> data = new ArrayList<>();
|
||||
bad(data,"/a/b{var}"); // bad syntax - variable does not encompass whole path segment
|
||||
bad(data,"a/{var}"); // bad syntax - no start slash
|
||||
bad(data,"/a/{var/b}"); // path segment separator in variable name
|
||||
bad(data,"/{var}/*"); // bad syntax - no globs allowed
|
||||
bad(data,"/{var}.do"); // bad syntax - variable does not encompass whole path segment
|
||||
bad(data,"/a/{var*}"); // use of glob character not allowed in variable name
|
||||
bad(data,"/a/{}"); // bad syntax - no variable name
|
||||
// MIGHT BE ALLOWED bad(data,"/a/{---}"); // no alpha in variable name
|
||||
bad(data,"{var}"); // bad syntax - no start slash
|
||||
bad(data,"/a/{my special variable}"); // bad syntax - space in variable name
|
||||
bad(data,"/a/{var}/{var}"); // variable name duplicate
|
||||
// MIGHT BE ALLOWED bad(data,"/a/{var}/{Var}/{vAR}"); // variable name duplicated (diff case)
|
||||
bad(data,"/a/../../../{var}"); // path navigation not allowed
|
||||
bad(data,"/a/./{var}"); // path navigation not allowed
|
||||
bad(data,"/a//{var}"); // bad syntax - double path slash (no path segment)
|
||||
return data;
|
||||
}
|
||||
|
||||
private String pathSpec;
|
||||
|
||||
public WebSocketPathSpecBadSpecsTest(String pathSpec)
|
||||
{
|
||||
this.pathSpec = pathSpec;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadPathSpec()
|
||||
{
|
||||
try
|
||||
{
|
||||
new WebSocketPathSpec(this.pathSpec);
|
||||
fail("Expected IllegalArgumentException for a bad PathParam pathspec on: " + pathSpec);
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
// expected path
|
||||
System.out.println(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,286 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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.pathmap;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.websocket.server.pathmap.PathSpecGroup;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* Tests for ServerEndpoint Path Param / URI Template Path Specs
|
||||
*/
|
||||
public class WebSocketPathSpecTest
|
||||
{
|
||||
private void assertDetectedVars(WebSocketPathSpec spec, String... expectedVars)
|
||||
{
|
||||
String prefix = String.format("Spec(\"%s\")",spec.getPathSpec());
|
||||
assertEquals(prefix + ".variableCount",expectedVars.length,spec.getVariableCount());
|
||||
assertEquals(prefix + ".variable.length",expectedVars.length,spec.getVariables().length);
|
||||
for (int i = 0; i < expectedVars.length; i++)
|
||||
{
|
||||
assertEquals(String.format("%s.variable[%d]",prefix,i),expectedVars[i],spec.getVariables()[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMatches(PathSpec spec, String path)
|
||||
{
|
||||
String msg = String.format("Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
|
||||
assertThat(msg,spec.matches(path),is(true));
|
||||
}
|
||||
|
||||
private void assertNotMatches(PathSpec spec, String path)
|
||||
{
|
||||
String msg = String.format("!Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
|
||||
assertThat(msg,spec.matches(path),is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultPathSpec()
|
||||
{
|
||||
WebSocketPathSpec spec = new WebSocketPathSpec("/");
|
||||
assertEquals("Spec.pathSpec","/",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",1,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.EXACT,spec.getGroup());
|
||||
|
||||
assertEquals("Spec.variableCount",0,spec.getVariableCount());
|
||||
assertEquals("Spec.variable.length",0,spec.getVariables().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExactOnePathSpec()
|
||||
{
|
||||
WebSocketPathSpec spec = new WebSocketPathSpec("/a");
|
||||
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.getGroup());
|
||||
|
||||
assertMatches(spec,"/a");
|
||||
assertMatches(spec,"/a?type=other");
|
||||
assertNotMatches(spec,"/a/b");
|
||||
assertNotMatches(spec,"/a/");
|
||||
|
||||
assertEquals("Spec.variableCount",0,spec.getVariableCount());
|
||||
assertEquals("Spec.variable.length",0,spec.getVariables().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExactPathSpec_TestWebapp()
|
||||
{
|
||||
WebSocketPathSpec spec = new WebSocketPathSpec("/javax.websocket/");
|
||||
assertEquals("Spec.pathSpec","/javax.websocket/",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/javax\\.websocket/$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",1,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.EXACT,spec.getGroup());
|
||||
|
||||
assertMatches(spec,"/javax.websocket/");
|
||||
assertNotMatches(spec,"/javax.websocket");
|
||||
|
||||
assertEquals("Spec.variableCount",0,spec.getVariableCount());
|
||||
assertEquals("Spec.variable.length",0,spec.getVariables().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExactTwoPathSpec()
|
||||
{
|
||||
WebSocketPathSpec spec = new WebSocketPathSpec("/a/b");
|
||||
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.getGroup());
|
||||
|
||||
assertEquals("Spec.variableCount",0,spec.getVariableCount());
|
||||
assertEquals("Spec.variable.length",0,spec.getVariables().length);
|
||||
|
||||
assertMatches(spec,"/a/b");
|
||||
|
||||
assertNotMatches(spec,"/a/b/");
|
||||
assertNotMatches(spec,"/a/");
|
||||
assertNotMatches(spec,"/a/bb");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMiddleVarPathSpec()
|
||||
{
|
||||
WebSocketPathSpec spec = new WebSocketPathSpec("/a/{var}/c");
|
||||
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.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var");
|
||||
|
||||
assertMatches(spec,"/a/b/c");
|
||||
assertMatches(spec,"/a/zz/c");
|
||||
assertMatches(spec,"/a/hello+world/c");
|
||||
assertNotMatches(spec,"/a/bc");
|
||||
assertNotMatches(spec,"/a/b/");
|
||||
assertNotMatches(spec,"/a/b");
|
||||
|
||||
Map<String, String> mapped = spec.getPathParams("/a/b/c");
|
||||
assertThat("Spec.pathParams",mapped,notNullValue());
|
||||
assertThat("Spec.pathParams.size",mapped.size(),is(1));
|
||||
assertEquals("Spec.pathParams[var]","b",mapped.get("var"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneVarPathSpec()
|
||||
{
|
||||
WebSocketPathSpec spec = new WebSocketPathSpec("/a/{foo}");
|
||||
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.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"foo");
|
||||
|
||||
assertMatches(spec,"/a/b");
|
||||
assertNotMatches(spec,"/a/");
|
||||
assertNotMatches(spec,"/a");
|
||||
|
||||
Map<String, String> mapped = spec.getPathParams("/a/b");
|
||||
assertThat("Spec.pathParams",mapped,notNullValue());
|
||||
assertThat("Spec.pathParams.size",mapped.size(),is(1));
|
||||
assertEquals("Spec.pathParams[foo]","b",mapped.get("foo"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOneVarSuffixPathSpec()
|
||||
{
|
||||
WebSocketPathSpec spec = new WebSocketPathSpec("/{var}/b/c");
|
||||
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.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var");
|
||||
|
||||
assertMatches(spec,"/a/b/c");
|
||||
assertMatches(spec,"/az/b/c");
|
||||
assertMatches(spec,"/hello+world/b/c");
|
||||
assertNotMatches(spec,"/a/bc");
|
||||
assertNotMatches(spec,"/a/b/");
|
||||
assertNotMatches(spec,"/a/b");
|
||||
|
||||
Map<String, String> mapped = spec.getPathParams("/a/b/c");
|
||||
assertThat("Spec.pathParams",mapped,notNullValue());
|
||||
assertThat("Spec.pathParams.size",mapped.size(),is(1));
|
||||
assertEquals("Spec.pathParams[var]","a",mapped.get("var"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoVarComplexInnerPathSpec()
|
||||
{
|
||||
WebSocketPathSpec spec = new WebSocketPathSpec("/a/{var1}/c/{var2}/e");
|
||||
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.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var1","var2");
|
||||
|
||||
assertMatches(spec,"/a/b/c/d/e");
|
||||
assertNotMatches(spec,"/a/bc/d/e");
|
||||
assertNotMatches(spec,"/a/b/d/e");
|
||||
assertNotMatches(spec,"/a/b//d/e");
|
||||
|
||||
Map<String, String> mapped = spec.getPathParams("/a/b/c/d/e");
|
||||
assertThat("Spec.pathParams",mapped,notNullValue());
|
||||
assertThat("Spec.pathParams.size",mapped.size(),is(2));
|
||||
assertEquals("Spec.pathParams[var1]","b",mapped.get("var1"));
|
||||
assertEquals("Spec.pathParams[var2]","d",mapped.get("var2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoVarComplexOuterPathSpec()
|
||||
{
|
||||
WebSocketPathSpec spec = new WebSocketPathSpec("/{var1}/b/{var2}/{var3}");
|
||||
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.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var1","var2","var3");
|
||||
|
||||
assertMatches(spec,"/a/b/c/d");
|
||||
assertNotMatches(spec,"/a/bc/d/e");
|
||||
assertNotMatches(spec,"/a/c/d/e");
|
||||
assertNotMatches(spec,"/a//d/e");
|
||||
|
||||
Map<String, String> mapped = spec.getPathParams("/a/b/c/d");
|
||||
assertThat("Spec.pathParams",mapped,notNullValue());
|
||||
assertThat("Spec.pathParams.size",mapped.size(),is(3));
|
||||
assertEquals("Spec.pathParams[var1]","a",mapped.get("var1"));
|
||||
assertEquals("Spec.pathParams[var2]","c",mapped.get("var2"));
|
||||
assertEquals("Spec.pathParams[var3]","d",mapped.get("var3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoVarPrefixPathSpec()
|
||||
{
|
||||
WebSocketPathSpec spec = new WebSocketPathSpec("/a/{var1}/{var2}");
|
||||
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.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var1","var2");
|
||||
|
||||
assertMatches(spec,"/a/b/c");
|
||||
assertNotMatches(spec,"/a/bc");
|
||||
assertNotMatches(spec,"/a/b/");
|
||||
assertNotMatches(spec,"/a/b");
|
||||
|
||||
Map<String, String> mapped = spec.getPathParams("/a/b/c");
|
||||
assertThat("Spec.pathParams",mapped,notNullValue());
|
||||
assertThat("Spec.pathParams.size",mapped.size(),is(2));
|
||||
assertEquals("Spec.pathParams[var1]","b",mapped.get("var1"));
|
||||
assertEquals("Spec.pathParams[var2]","c",mapped.get("var2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVarOnlyPathSpec()
|
||||
{
|
||||
WebSocketPathSpec spec = new WebSocketPathSpec("/{var1}");
|
||||
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.getGroup());
|
||||
|
||||
assertDetectedVars(spec,"var1");
|
||||
|
||||
assertMatches(spec,"/a");
|
||||
assertNotMatches(spec,"/");
|
||||
assertNotMatches(spec,"/a/b");
|
||||
assertNotMatches(spec,"/a/b/c");
|
||||
|
||||
Map<String, String> mapped = spec.getPathParams("/a");
|
||||
assertThat("Spec.pathParams",mapped,notNullValue());
|
||||
assertThat("Spec.pathParams.size",mapped.size(),is(1));
|
||||
assertEquals("Spec.pathParams[var1]","a",mapped.get("var1"));
|
||||
}
|
||||
}
|
|
@ -18,8 +18,8 @@
|
|||
|
||||
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.http.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.websocket.servlet.WebSocketCreator;
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,6 +33,9 @@ import javax.servlet.ServletResponse;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.pathmap.MappedResource;
|
||||
import org.eclipse.jetty.http.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
import org.eclipse.jetty.servlet.FilterHolder;
|
||||
|
@ -44,9 +47,6 @@ 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.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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,13 +24,13 @@ import javax.servlet.ServletException;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.pathmap.MappedResource;
|
||||
import org.eclipse.jetty.http.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.http.pathmap.PathSpec;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.MappedByteBufferPool;
|
||||
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
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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 java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
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.
|
||||
* <p>
|
||||
* Sorted into search order upon entry into the Set
|
||||
*
|
||||
* @param <E> the type of mapping endpoint
|
||||
*/
|
||||
@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;
|
||||
private final E resource;
|
||||
|
||||
public MappedResource(PathSpec pathSpec, E resource)
|
||||
{
|
||||
this.pathSpec = pathSpec;
|
||||
this.resource = resource;
|
||||
}
|
||||
|
||||
/**
|
||||
* Comparison is based solely on the pathSpec
|
||||
*/
|
||||
@Override
|
||||
public int compareTo(MappedResource<E> other)
|
||||
{
|
||||
return this.pathSpec.compareTo(other.pathSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
MappedResource<?> other = (MappedResource<?>)obj;
|
||||
if (pathSpec == null)
|
||||
{
|
||||
if (other.pathSpec != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!pathSpec.equals(other.pathSpec))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ManagedAttribute(value = "path spec", readonly = true)
|
||||
public PathSpec getPathSpec()
|
||||
{
|
||||
return pathSpec;
|
||||
}
|
||||
|
||||
@ManagedAttribute(value = "resource", readonly = true)
|
||||
public E getResource()
|
||||
{
|
||||
return resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = (prime * result) + ((pathSpec == null)?0:pathSpec.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("MappedResource[pathSpec=%s,resource=%s]",pathSpec,resource);
|
||||
}
|
||||
}
|
||||
|
||||
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 void reset()
|
||||
{
|
||||
mappings.clear();
|
||||
}
|
||||
|
||||
public MappedResource<E> getMatch(String path)
|
||||
{
|
||||
int len = mappings.size();
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
MappedResource<E> mr = mappings.get(i);
|
||||
if (mr.getPathSpec().matches(path))
|
||||
{
|
||||
return mr;
|
||||
}
|
||||
}
|
||||
return defaultResource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<MappedResource<E>> iterator()
|
||||
{
|
||||
return mappings.iterator();
|
||||
}
|
||||
|
||||
public void put(PathSpec pathSpec, E resource)
|
||||
{
|
||||
MappedResource<E> entry = new MappedResource<>(pathSpec,resource);
|
||||
if (pathSpec.group == PathSpecGroup.DEFAULT)
|
||||
{
|
||||
defaultResource = entry;
|
||||
}
|
||||
// TODO: warning on replacement of existing mapping?
|
||||
mappings.add(entry);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("Added {} to {}",entry,this);
|
||||
Collections.sort(mappings);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("%s[size=%d]",this.getClass().getSimpleName(),mappings.size());
|
||||
}
|
||||
}
|
|
@ -1,167 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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;
|
||||
|
||||
/**
|
||||
* The base PathSpec, what all other path specs are based on
|
||||
*/
|
||||
public abstract class PathSpec implements Comparable<PathSpec>
|
||||
{
|
||||
protected String pathSpec;
|
||||
protected PathSpecGroup group;
|
||||
protected int pathDepth;
|
||||
protected int specLength;
|
||||
|
||||
@Override
|
||||
public int compareTo(PathSpec other)
|
||||
{
|
||||
// Grouping (increasing)
|
||||
int diff = this.group.ordinal() - other.group.ordinal();
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Spec Length (decreasing)
|
||||
diff = other.specLength - this.specLength;
|
||||
if (diff != 0)
|
||||
{
|
||||
return diff;
|
||||
}
|
||||
|
||||
// Path Spec Name (alphabetical)
|
||||
return this.pathSpec.compareTo(other.pathSpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (getClass() != obj.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
PathSpec other = (PathSpec)obj;
|
||||
if (pathSpec == null)
|
||||
{
|
||||
if (other.pathSpec != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!pathSpec.equals(other.pathSpec))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public PathSpecGroup getGroup()
|
||||
{
|
||||
return group;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of path elements that this path spec declares.
|
||||
* <p>
|
||||
* This is used to determine longest match logic.
|
||||
*
|
||||
* @return the depth of the path segments that this spec declares
|
||||
*/
|
||||
public int getPathDepth()
|
||||
{
|
||||
return pathDepth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the portion of the path that is after the path spec.
|
||||
*
|
||||
* @param path
|
||||
* the path to match against
|
||||
* @return the path info portion of the string
|
||||
*/
|
||||
public abstract String getPathInfo(String path);
|
||||
|
||||
/**
|
||||
* Return the portion of the path that matches a path spec.
|
||||
*
|
||||
* @param path
|
||||
* the path to match against
|
||||
* @return the match, or null if no match at all
|
||||
*/
|
||||
public abstract String getPathMatch(String path);
|
||||
|
||||
/**
|
||||
* The as-provided path spec.
|
||||
*
|
||||
* @return the as-provided path spec
|
||||
*/
|
||||
public String getPathSpec()
|
||||
{
|
||||
return pathSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the relative path.
|
||||
*
|
||||
* @param base
|
||||
* the base the path is relative to
|
||||
* @param path
|
||||
* the additional path
|
||||
* @return the base plus path with pathSpec portion removed
|
||||
*/
|
||||
public abstract String getRelativePath(String base, String path);
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = (prime * result) + ((pathSpec == null)?0:pathSpec.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to see if the provided path matches this path spec
|
||||
*
|
||||
* @param path
|
||||
* the path to test
|
||||
* @return true if the path matches this path spec, false otherwise
|
||||
*/
|
||||
public abstract boolean matches(String path);
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
StringBuilder str = new StringBuilder();
|
||||
str.append(this.getClass().getSimpleName()).append("[\"");
|
||||
str.append(pathSpec);
|
||||
str.append("\",pathDepth=").append(pathDepth);
|
||||
str.append(",group=").append(group);
|
||||
str.append("]");
|
||||
return str.toString();
|
||||
}
|
||||
}
|
|
@ -1,82 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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;
|
||||
|
||||
/**
|
||||
* Types of path spec groups.
|
||||
* <p>
|
||||
* This is used to facilitate proper pathspec search order.
|
||||
* <p>
|
||||
* Search Order: {@link PathSpecGroup#ordinal()} [increasin], {@link PathSpec#specLength} [decreasing], {@link PathSpec#pathSpec} [natural sort order]
|
||||
*/
|
||||
public enum PathSpecGroup
|
||||
{
|
||||
// NOTE: Order of enums determines order of Groups.
|
||||
|
||||
/**
|
||||
* For exactly defined path specs, no glob.
|
||||
*/
|
||||
EXACT,
|
||||
/**
|
||||
* For path specs that have a hardcoded prefix and suffix with wildcard glob in the middle.
|
||||
*
|
||||
* <pre>
|
||||
* "^/downloads/[^/]*.zip$" - regex spec
|
||||
* "/a/{var}/c" - websocket spec
|
||||
* </pre>
|
||||
*
|
||||
* Note: there is no known servlet spec variant of this kind of path spec
|
||||
*/
|
||||
MIDDLE_GLOB,
|
||||
/**
|
||||
* For path specs that have a hardcoded prefix and a trailing wildcard glob.
|
||||
* <p>
|
||||
*
|
||||
* <pre>
|
||||
* "/downloads/*" - servlet spec
|
||||
* "/api/*" - servlet spec
|
||||
* "^/rest/.*$" - regex spec
|
||||
* "/bookings/{guest-id}" - websocket spec
|
||||
* "/rewards/{vip-level}" - websocket spec
|
||||
* </pre>
|
||||
*/
|
||||
PREFIX_GLOB,
|
||||
/**
|
||||
* For path specs that have a wildcard glob with a hardcoded suffix
|
||||
*
|
||||
* <pre>
|
||||
* "*.do" - servlet spec
|
||||
* "*.css" - servlet spec
|
||||
* "^.*\.zip$" - regex spec
|
||||
* </pre>
|
||||
*
|
||||
* Note: there is no known websocket spec variant of this kind of path spec
|
||||
*/
|
||||
SUFFIX_GLOB,
|
||||
/**
|
||||
* The default spec for accessing the Root and/or Default behavior.
|
||||
*
|
||||
* <pre>
|
||||
* "/" - servlet spec (Default Servlet)
|
||||
* "/" - websocket spec (Root Context)
|
||||
* "^/$" - regex spec (Root Context)
|
||||
* </pre>
|
||||
*/
|
||||
DEFAULT;
|
||||
}
|
|
@ -18,159 +18,14 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.server.pathmap;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class RegexPathSpec extends PathSpec
|
||||
/**
|
||||
* @deprecated moved to jetty-http {@link org.eclipse.jetty.http.pathmap.RegexPathSpec} (this facade will be removed in Jetty 9.4)
|
||||
*/
|
||||
@Deprecated
|
||||
public class RegexPathSpec extends org.eclipse.jetty.http.pathmap.RegexPathSpec
|
||||
{
|
||||
protected Pattern pattern;
|
||||
|
||||
protected RegexPathSpec()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public RegexPathSpec(String regex)
|
||||
{
|
||||
super.pathSpec = regex;
|
||||
boolean inGrouping = false;
|
||||
this.pathDepth = 0;
|
||||
this.specLength = pathSpec.length();
|
||||
// build up a simple signature we can use to identify the grouping
|
||||
StringBuilder signature = new StringBuilder();
|
||||
for (char c : pathSpec.toCharArray())
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '[':
|
||||
inGrouping = true;
|
||||
break;
|
||||
case ']':
|
||||
inGrouping = false;
|
||||
signature.append('g'); // glob
|
||||
break;
|
||||
case '*':
|
||||
signature.append('g'); // glob
|
||||
break;
|
||||
case '/':
|
||||
if (!inGrouping)
|
||||
{
|
||||
this.pathDepth++;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (!inGrouping)
|
||||
{
|
||||
if (Character.isLetterOrDigit(c))
|
||||
{
|
||||
signature.append('l'); // literal (exact)
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.pattern = Pattern.compile(pathSpec);
|
||||
|
||||
// Figure out the grouping based on the signature
|
||||
String sig = signature.toString();
|
||||
|
||||
if (Pattern.matches("^l*$",sig))
|
||||
{
|
||||
this.group = PathSpecGroup.EXACT;
|
||||
}
|
||||
else if (Pattern.matches("^l*g+",sig))
|
||||
{
|
||||
this.group = PathSpecGroup.PREFIX_GLOB;
|
||||
}
|
||||
else if (Pattern.matches("^g+l+$",sig))
|
||||
{
|
||||
this.group = PathSpecGroup.SUFFIX_GLOB;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.group = PathSpecGroup.MIDDLE_GLOB;
|
||||
}
|
||||
}
|
||||
|
||||
public Matcher getMatcher(String path)
|
||||
{
|
||||
return this.pattern.matcher(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathInfo(String path)
|
||||
{
|
||||
// Path Info only valid for PREFIX_GLOB types
|
||||
if (group == PathSpecGroup.PREFIX_GLOB)
|
||||
{
|
||||
Matcher matcher = getMatcher(path);
|
||||
if (matcher.matches())
|
||||
{
|
||||
if (matcher.groupCount() >= 1)
|
||||
{
|
||||
String pathInfo = matcher.group(1);
|
||||
if ("".equals(pathInfo))
|
||||
{
|
||||
return "/";
|
||||
}
|
||||
else
|
||||
{
|
||||
return pathInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathMatch(String path)
|
||||
{
|
||||
Matcher matcher = getMatcher(path);
|
||||
if (matcher.matches())
|
||||
{
|
||||
if (matcher.groupCount() >= 1)
|
||||
{
|
||||
int idx = matcher.start(1);
|
||||
if (idx > 0)
|
||||
{
|
||||
if (path.charAt(idx - 1) == '/')
|
||||
{
|
||||
idx--;
|
||||
}
|
||||
return path.substring(0,idx);
|
||||
}
|
||||
}
|
||||
return path;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Pattern getPattern()
|
||||
{
|
||||
return this.pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRelativePath(String base, String path)
|
||||
{
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(final String path)
|
||||
{
|
||||
int idx = path.indexOf('?');
|
||||
if (idx >= 0)
|
||||
{
|
||||
// match only non-query part
|
||||
return getMatcher(path.substring(0,idx)).matches();
|
||||
}
|
||||
else
|
||||
{
|
||||
// match entire path
|
||||
return getMatcher(path).matches();
|
||||
}
|
||||
super(regex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,274 +18,14 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.server.pathmap;
|
||||
|
||||
import org.eclipse.jetty.util.URIUtil;
|
||||
|
||||
public class ServletPathSpec extends PathSpec
|
||||
/**
|
||||
* @deprecated moved to jetty-http {@link org.eclipse.jetty.http.pathmap.ServletPathSpec} (this facade will be removed in Jetty 9.4)
|
||||
*/
|
||||
@Deprecated
|
||||
public class ServletPathSpec extends org.eclipse.jetty.http.pathmap.ServletPathSpec
|
||||
{
|
||||
public static final String PATH_SPEC_SEPARATORS = ":,";
|
||||
|
||||
/**
|
||||
* Get multi-path spec splits.
|
||||
*
|
||||
* @param servletPathSpec
|
||||
* the path spec that might contain multiple declared path specs
|
||||
* @return the individual path specs found.
|
||||
*/
|
||||
public static ServletPathSpec[] getMultiPathSpecs(String servletPathSpec)
|
||||
{
|
||||
String pathSpecs[] = servletPathSpec.split(PATH_SPEC_SEPARATORS);
|
||||
int len = pathSpecs.length;
|
||||
ServletPathSpec sps[] = new ServletPathSpec[len];
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
sps[i] = new ServletPathSpec(pathSpecs[i]);
|
||||
}
|
||||
return sps;
|
||||
}
|
||||
|
||||
public ServletPathSpec(String servletPathSpec)
|
||||
{
|
||||
super();
|
||||
assertValidServletPathSpec(servletPathSpec);
|
||||
|
||||
// The Path Spec for Default Servlet
|
||||
if ((servletPathSpec == null) || (servletPathSpec.length() == 0) || "/".equals(servletPathSpec))
|
||||
{
|
||||
super.pathSpec = "/";
|
||||
super.pathDepth = -1; // force this to be last in sort order
|
||||
this.specLength = 1;
|
||||
this.group = PathSpecGroup.DEFAULT;
|
||||
return;
|
||||
}
|
||||
|
||||
this.specLength = servletPathSpec.length();
|
||||
super.pathDepth = 0;
|
||||
char lastChar = servletPathSpec.charAt(specLength - 1);
|
||||
// prefix based
|
||||
if ((servletPathSpec.charAt(0) == '/') && (specLength > 1) && (lastChar == '*'))
|
||||
{
|
||||
this.group = PathSpecGroup.PREFIX_GLOB;
|
||||
}
|
||||
// suffix based
|
||||
else if (servletPathSpec.charAt(0) == '*')
|
||||
{
|
||||
this.group = PathSpecGroup.SUFFIX_GLOB;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.group = PathSpecGroup.EXACT;
|
||||
}
|
||||
|
||||
for (int i = 0; i < specLength; i++)
|
||||
{
|
||||
int cp = servletPathSpec.codePointAt(i);
|
||||
if (cp < 128)
|
||||
{
|
||||
char c = (char)cp;
|
||||
switch (c)
|
||||
{
|
||||
case '/':
|
||||
super.pathDepth++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.pathSpec = servletPathSpec;
|
||||
}
|
||||
|
||||
private void assertValidServletPathSpec(String servletPathSpec)
|
||||
{
|
||||
if ((servletPathSpec == null) || servletPathSpec.equals(""))
|
||||
{
|
||||
return; // empty path spec
|
||||
}
|
||||
|
||||
// Ensure we don't have path spec separators here in our single path spec.
|
||||
for (char c : PATH_SPEC_SEPARATORS.toCharArray())
|
||||
{
|
||||
if (servletPathSpec.indexOf(c) >= 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Servlet Spec 12.2 violation: encountered Path Spec Separator [" + PATH_SPEC_SEPARATORS
|
||||
+ "] within specified path spec. did you forget to split this path spec up?");
|
||||
}
|
||||
}
|
||||
|
||||
int len = servletPathSpec.length();
|
||||
// path spec must either start with '/' or '*.'
|
||||
if (servletPathSpec.charAt(0) == '/')
|
||||
{
|
||||
// Prefix Based
|
||||
if (len == 1)
|
||||
{
|
||||
return; // simple '/' path spec
|
||||
}
|
||||
int idx = servletPathSpec.indexOf('*');
|
||||
if (idx < 0)
|
||||
{
|
||||
return; // no hit on glob '*'
|
||||
}
|
||||
// only allowed to have '*' at the end of the path spec
|
||||
if (idx != (len - 1))
|
||||
{
|
||||
throw new IllegalArgumentException("Servlet Spec 12.2 violation: glob '*' can only exist at end of prefix based matches");
|
||||
}
|
||||
}
|
||||
else if (servletPathSpec.startsWith("*."))
|
||||
{
|
||||
// Suffix Based
|
||||
int idx = servletPathSpec.indexOf('/');
|
||||
// cannot have path separator
|
||||
if (idx >= 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have path separators");
|
||||
}
|
||||
|
||||
idx = servletPathSpec.indexOf('*',2);
|
||||
// only allowed to have 1 glob '*', at the start of the path spec
|
||||
if (idx >= 1)
|
||||
{
|
||||
throw new IllegalArgumentException("Servlet Spec 12.2 violation: suffix based path spec cannot have multiple glob '*'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalArgumentException("Servlet Spec 12.2 violation: path spec must start with \"/\" or \"*.\"");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathInfo(String path)
|
||||
{
|
||||
// Path Info only valid for PREFIX_GLOB types
|
||||
if (group == PathSpecGroup.PREFIX_GLOB)
|
||||
{
|
||||
if (path.length() == (specLength - 2))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return path.substring(specLength - 2);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPathMatch(String path)
|
||||
{
|
||||
switch (group)
|
||||
{
|
||||
case EXACT:
|
||||
if (pathSpec.equals(path))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case PREFIX_GLOB:
|
||||
if (isWildcardMatch(path))
|
||||
{
|
||||
return path.substring(0,specLength - 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case SUFFIX_GLOB:
|
||||
if (path.regionMatches(path.length() - (specLength - 1),pathSpec,1,specLength - 1))
|
||||
{
|
||||
return path;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case DEFAULT:
|
||||
return path;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRelativePath(String base, String path)
|
||||
{
|
||||
String info = getPathInfo(path);
|
||||
if (info == null)
|
||||
{
|
||||
info = path;
|
||||
}
|
||||
|
||||
if (info.startsWith("./"))
|
||||
{
|
||||
info = info.substring(2);
|
||||
}
|
||||
if (base.endsWith(URIUtil.SLASH))
|
||||
{
|
||||
if (info.startsWith(URIUtil.SLASH))
|
||||
{
|
||||
path = base + info.substring(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
path = base + info;
|
||||
}
|
||||
}
|
||||
else if (info.startsWith(URIUtil.SLASH))
|
||||
{
|
||||
path = base + info;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = base + URIUtil.SLASH + info;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
private boolean isExactMatch(String path)
|
||||
{
|
||||
if (group == PathSpecGroup.EXACT)
|
||||
{
|
||||
if (pathSpec.equals(path))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return (path.charAt(path.length() - 1) == '/') && (path.equals(pathSpec + '/'));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isWildcardMatch(String path)
|
||||
{
|
||||
// For a spec of "/foo/*" match "/foo" , "/foo/..." but not "/foobar"
|
||||
int cpl = specLength - 2;
|
||||
if ((group == PathSpecGroup.PREFIX_GLOB) && (path.regionMatches(0,pathSpec,0,cpl)))
|
||||
{
|
||||
if ((path.length() == cpl) || ('/' == path.charAt(cpl)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String path)
|
||||
{
|
||||
switch (group)
|
||||
{
|
||||
case EXACT:
|
||||
return isExactMatch(path);
|
||||
case PREFIX_GLOB:
|
||||
return isWildcardMatch(path);
|
||||
case SUFFIX_GLOB:
|
||||
return path.regionMatches((path.length() - specLength) + 1,pathSpec,1,specLength - 1);
|
||||
case DEFAULT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
super(servletPathSpec);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,226 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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 java.util.concurrent.CyclicBarrier;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.http.PathMap;
|
||||
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.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@RunWith(AdvancedRunner.class)
|
||||
public class PathMappingsBenchmarkTest
|
||||
{
|
||||
public static abstract class AbstractPathMapThread extends Thread
|
||||
{
|
||||
private int iterations;
|
||||
private CyclicBarrier barrier;
|
||||
@SuppressWarnings("unused")
|
||||
private long success;
|
||||
@SuppressWarnings("unused")
|
||||
private long error;
|
||||
|
||||
public AbstractPathMapThread(int iterations, CyclicBarrier barrier)
|
||||
{
|
||||
this.iterations = iterations;
|
||||
this.barrier = barrier;
|
||||
}
|
||||
|
||||
public abstract String getMatchedResource(String path);
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
int llen = LOOKUPS.length;
|
||||
String path;
|
||||
String expectedResource;
|
||||
String matchedResource;
|
||||
await(barrier);
|
||||
for (int iter = 0; iter < iterations; iter++)
|
||||
{
|
||||
for (int li = 0; li < llen; li++)
|
||||
{
|
||||
path = LOOKUPS[li][0];
|
||||
expectedResource = LOOKUPS[li][1];
|
||||
matchedResource = getMatchedResource(path);
|
||||
if (matchedResource.equals(expectedResource))
|
||||
{
|
||||
success++;
|
||||
}
|
||||
else
|
||||
{
|
||||
error++;
|
||||
}
|
||||
}
|
||||
}
|
||||
await(barrier);
|
||||
}
|
||||
}
|
||||
|
||||
public static class PathMapMatchThread extends AbstractPathMapThread
|
||||
{
|
||||
private PathMap<String> pathmap;
|
||||
|
||||
public PathMapMatchThread(PathMap<String> pathmap, int iters, CyclicBarrier barrier)
|
||||
{
|
||||
super(iters,barrier);
|
||||
this.pathmap = pathmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMatchedResource(String path)
|
||||
{
|
||||
return pathmap.getMatch(path).getValue();
|
||||
}
|
||||
}
|
||||
|
||||
public static class PathMatchThread extends AbstractPathMapThread
|
||||
{
|
||||
private PathMappings<String> pathmap;
|
||||
|
||||
public PathMatchThread(PathMappings<String> pathmap, int iters, CyclicBarrier barrier)
|
||||
{
|
||||
super(iters,barrier);
|
||||
this.pathmap = pathmap;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMatchedResource(String path)
|
||||
{
|
||||
return pathmap.getMatch(path).getResource();
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger LOG = Log.getLogger(PathMappingsBenchmarkTest.class);
|
||||
private static final String[][] LOOKUPS;
|
||||
private int runs = 20;
|
||||
private int threads = 200;
|
||||
private int iters = 10000;
|
||||
|
||||
static
|
||||
{
|
||||
LOOKUPS = new String[][]
|
||||
{
|
||||
// @formatter:off
|
||||
{ "/abs/path", "path" },
|
||||
{ "/abs/path/longer","longpath" },
|
||||
{ "/abs/path/foo","default" },
|
||||
{ "/main.css","default" },
|
||||
{ "/downloads/script.gz","gzipped" },
|
||||
{ "/downloads/distribution.tar.gz","tarball" },
|
||||
{ "/downloads/readme.txt","default" },
|
||||
{ "/downloads/logs.tgz","default" },
|
||||
{ "/animal/horse/mustang","animals" },
|
||||
{ "/animal/bird/eagle/bald","birds" },
|
||||
{ "/animal/fish/shark/hammerhead","fishes" },
|
||||
{ "/animal/insect/ladybug","animals" },
|
||||
// @formatter:on
|
||||
};
|
||||
}
|
||||
|
||||
private static void await(CyclicBarrier barrier)
|
||||
{
|
||||
try
|
||||
{
|
||||
barrier.await();
|
||||
}
|
||||
catch (Exception x)
|
||||
{
|
||||
throw new RuntimeException(x);
|
||||
}
|
||||
}
|
||||
|
||||
@Stress("High CPU")
|
||||
@Test
|
||||
public void testServletPathMap()
|
||||
{
|
||||
// Setup (old) PathMap
|
||||
|
||||
PathMap<String> p = new PathMap<>();
|
||||
|
||||
p.put("/abs/path","path");
|
||||
p.put("/abs/path/longer","longpath");
|
||||
p.put("/animal/bird/*","birds");
|
||||
p.put("/animal/fish/*","fishes");
|
||||
p.put("/animal/*","animals");
|
||||
p.put("*.tar.gz","tarball");
|
||||
p.put("*.gz","gzipped");
|
||||
p.put("/","default");
|
||||
|
||||
final CyclicBarrier barrier = new CyclicBarrier(threads + 1);
|
||||
|
||||
for (int r = 0; r < runs; r++)
|
||||
{
|
||||
for (int t = 0; t < threads; t++)
|
||||
{
|
||||
PathMapMatchThread thread = new PathMapMatchThread(p,iters,barrier);
|
||||
thread.start();
|
||||
}
|
||||
await(barrier);
|
||||
long begin = System.nanoTime();
|
||||
await(barrier);
|
||||
long end = System.nanoTime();
|
||||
long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
|
||||
int totalMatches = threads * iters * LOOKUPS.length;
|
||||
LOG.info("jetty-http/PathMap (Servlet only) threads:{}/iters:{}/total-matches:{} => {} ms",threads,iters,totalMatches,elapsed);
|
||||
}
|
||||
}
|
||||
|
||||
@Stress("High CPU")
|
||||
@Test
|
||||
public void testServletPathMappings()
|
||||
{
|
||||
// Setup (new) PathMappings
|
||||
|
||||
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");
|
||||
|
||||
final CyclicBarrier barrier = new CyclicBarrier(threads + 1);
|
||||
|
||||
for (int r = 0; r < runs; r++)
|
||||
{
|
||||
for (int t = 0; t < threads; t++)
|
||||
{
|
||||
PathMatchThread thread = new PathMatchThread(p,iters,barrier);
|
||||
thread.start();
|
||||
}
|
||||
await(barrier);
|
||||
long begin = System.nanoTime();
|
||||
await(barrier);
|
||||
long end = System.nanoTime();
|
||||
long elapsed = TimeUnit.NANOSECONDS.toMillis(end - begin);
|
||||
int totalMatches = threads * iters * LOOKUPS.length;
|
||||
LOG.info("jetty-websocket/PathMappings (Servlet only) threads:{}/iters:{}/total-matches:{} => {} ms",threads,iters,totalMatches,elapsed);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,119 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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.notNullValue;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
public void dumpMappings(PathMappings<String> p)
|
||||
{
|
||||
for (MappedResource<String> res : p)
|
||||
{
|
||||
System.out.printf(" %s%n",res);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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");
|
||||
|
||||
// dumpMappings(p);
|
||||
|
||||
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");
|
||||
|
||||
// dumpMappings(p);
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
|
@ -1,135 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class RegexPathSpecTest
|
||||
{
|
||||
public static void assertMatches(PathSpec spec, String path)
|
||||
{
|
||||
String msg = String.format("Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
|
||||
assertThat(msg,spec.matches(path),is(true));
|
||||
}
|
||||
|
||||
public static void assertNotMatches(PathSpec spec, String path)
|
||||
{
|
||||
String msg = String.format("!Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
|
||||
assertThat(msg,spec.matches(path),is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExactSpec()
|
||||
{
|
||||
RegexPathSpec spec = new RegexPathSpec("^/a$");
|
||||
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);
|
||||
|
||||
assertMatches(spec,"/a");
|
||||
|
||||
assertNotMatches(spec,"/aa");
|
||||
assertNotMatches(spec,"/a/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMiddleSpec()
|
||||
{
|
||||
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 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()
|
||||
{
|
||||
RegexPathSpec spec = new RegexPathSpec("^/a/(.*)$");
|
||||
assertEquals("Spec.pathSpec","^/a/(.*)$",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^/a/(.*)$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",2,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.PREFIX_GLOB,spec.group);
|
||||
|
||||
assertMatches(spec,"/a/");
|
||||
assertMatches(spec,"/a/b");
|
||||
assertMatches(spec,"/a/b/c/d/e");
|
||||
|
||||
assertNotMatches(spec,"/a");
|
||||
assertNotMatches(spec,"/aa");
|
||||
assertNotMatches(spec,"/aa/bb");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuffixSpec()
|
||||
{
|
||||
RegexPathSpec spec = new RegexPathSpec("^(.*).do$");
|
||||
assertEquals("Spec.pathSpec","^(.*).do$",spec.getPathSpec());
|
||||
assertEquals("Spec.pattern","^(.*).do$",spec.getPattern().pattern());
|
||||
assertEquals("Spec.pathDepth",0,spec.getPathDepth());
|
||||
assertEquals("Spec.group",PathSpecGroup.SUFFIX_GLOB,spec.group);
|
||||
|
||||
assertMatches(spec,"/a.do");
|
||||
assertMatches(spec,"/a/b/c.do");
|
||||
assertMatches(spec,"/abcde.do");
|
||||
assertMatches(spec,"/abc/efg.do");
|
||||
|
||||
assertNotMatches(spec,"/a");
|
||||
assertNotMatches(spec,"/aa");
|
||||
assertNotMatches(spec,"/aa/bb");
|
||||
assertNotMatches(spec,"/aa/bb.do/more");
|
||||
}
|
||||
}
|
|
@ -1,188 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2015 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.is;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class ServletPathSpecTest
|
||||
{
|
||||
private void assertBadServletPathSpec(String pathSpec)
|
||||
{
|
||||
try
|
||||
{
|
||||
new ServletPathSpec(pathSpec);
|
||||
fail("Expected IllegalArgumentException for a bad servlet pathspec on: " + pathSpec);
|
||||
}
|
||||
catch (IllegalArgumentException e)
|
||||
{
|
||||
// expected path
|
||||
System.out.println(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void assertMatches(ServletPathSpec spec, String path)
|
||||
{
|
||||
String msg = String.format("Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
|
||||
assertThat(msg,spec.matches(path),is(true));
|
||||
}
|
||||
|
||||
private void assertNotMatches(ServletPathSpec spec, String path)
|
||||
{
|
||||
String msg = String.format("!Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path);
|
||||
assertThat(msg,spec.matches(path),is(false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadServletPathSpecA()
|
||||
{
|
||||
assertBadServletPathSpec("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadServletPathSpecB()
|
||||
{
|
||||
assertBadServletPathSpec("/foo/*.do");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadServletPathSpecC()
|
||||
{
|
||||
assertBadServletPathSpec("foo/*.do");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadServletPathSpecD()
|
||||
{
|
||||
assertBadServletPathSpec("foo/*.*do");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBadServletPathSpecE()
|
||||
{
|
||||
assertBadServletPathSpec("*do");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultPathSpec()
|
||||
{
|
||||
ServletPathSpec spec = new ServletPathSpec("/");
|
||||
assertEquals("Spec.pathSpec","/",spec.getPathSpec());
|
||||
assertEquals("Spec.pathDepth",-1,spec.getPathDepth());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyPathSpec()
|
||||
{
|
||||
ServletPathSpec spec = new ServletPathSpec("");
|
||||
assertEquals("Spec.pathSpec","/",spec.getPathSpec());
|
||||
assertEquals("Spec.pathDepth",-1,spec.getPathDepth());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExactPathSpec()
|
||||
{
|
||||
ServletPathSpec spec = new ServletPathSpec("/abs/path");
|
||||
assertEquals("Spec.pathSpec","/abs/path",spec.getPathSpec());
|
||||
assertEquals("Spec.pathDepth",2,spec.getPathDepth());
|
||||
|
||||
assertMatches(spec,"/abs/path");
|
||||
assertMatches(spec,"/abs/path/");
|
||||
|
||||
assertNotMatches(spec,"/abs/path/more");
|
||||
assertNotMatches(spec,"/foo");
|
||||
assertNotMatches(spec,"/foo/abs/path");
|
||||
assertNotMatches(spec,"/foo/abs/path/");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPathInfo()
|
||||
{
|
||||
assertEquals("pathInfo exact",null,new ServletPathSpec("/Foo/bar").getPathInfo("/Foo/bar"));
|
||||
assertEquals("pathInfo prefix","/bar",new ServletPathSpec("/Foo/*").getPathInfo("/Foo/bar"));
|
||||
assertEquals("pathInfo prefix","/*",new ServletPathSpec("/Foo/*").getPathInfo("/Foo/*"));
|
||||
assertEquals("pathInfo prefix","/",new ServletPathSpec("/Foo/*").getPathInfo("/Foo/"));
|
||||
assertEquals("pathInfo prefix",null,new ServletPathSpec("/Foo/*").getPathInfo("/Foo"));
|
||||
assertEquals("pathInfo suffix",null,new ServletPathSpec("*.ext").getPathInfo("/Foo/bar.ext"));
|
||||
assertEquals("pathInfo default",null,new ServletPathSpec("/").getPathInfo("/Foo/bar.ext"));
|
||||
|
||||
assertEquals("pathInfo default","/xxx/zzz",new ServletPathSpec("/*").getPathInfo("/xxx/zzz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNullPathSpec()
|
||||
{
|
||||
ServletPathSpec spec = new ServletPathSpec(null);
|
||||
assertEquals("Spec.pathSpec","/",spec.getPathSpec());
|
||||
assertEquals("Spec.pathDepth",-1,spec.getPathDepth());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPathMatch()
|
||||
{
|
||||
assertEquals("pathMatch exact","/Foo/bar",new ServletPathSpec("/Foo/bar").getPathMatch("/Foo/bar"));
|
||||
assertEquals("pathMatch prefix","/Foo",new ServletPathSpec("/Foo/*").getPathMatch("/Foo/bar"));
|
||||
assertEquals("pathMatch prefix","/Foo",new ServletPathSpec("/Foo/*").getPathMatch("/Foo/"));
|
||||
assertEquals("pathMatch prefix","/Foo",new ServletPathSpec("/Foo/*").getPathMatch("/Foo"));
|
||||
assertEquals("pathMatch suffix","/Foo/bar.ext",new ServletPathSpec("*.ext").getPathMatch("/Foo/bar.ext"));
|
||||
assertEquals("pathMatch default","/Foo/bar.ext",new ServletPathSpec("/").getPathMatch("/Foo/bar.ext"));
|
||||
|
||||
assertEquals("pathMatch default","",new ServletPathSpec("/*").getPathMatch("/xxx/zzz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrefixPathSpec()
|
||||
{
|
||||
ServletPathSpec spec = new ServletPathSpec("/downloads/*");
|
||||
assertEquals("Spec.pathSpec","/downloads/*",spec.getPathSpec());
|
||||
assertEquals("Spec.pathDepth",2,spec.getPathDepth());
|
||||
|
||||
assertMatches(spec,"/downloads/logo.jpg");
|
||||
assertMatches(spec,"/downloads/distribution.tar.gz");
|
||||
assertMatches(spec,"/downloads/distribution.tgz");
|
||||
assertMatches(spec,"/downloads/distribution.zip");
|
||||
|
||||
assertMatches(spec,"/downloads");
|
||||
|
||||
assertEquals("Spec.pathInfo","/",spec.getPathInfo("/downloads/"));
|
||||
assertEquals("Spec.pathInfo","/distribution.zip",spec.getPathInfo("/downloads/distribution.zip"));
|
||||
assertEquals("Spec.pathInfo","/dist/9.0/distribution.tar.gz",spec.getPathInfo("/downloads/dist/9.0/distribution.tar.gz"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuffixPathSpec()
|
||||
{
|
||||
ServletPathSpec spec = new ServletPathSpec("*.gz");
|
||||
assertEquals("Spec.pathSpec","*.gz",spec.getPathSpec());
|
||||
assertEquals("Spec.pathDepth",0,spec.getPathDepth());
|
||||
|
||||
assertMatches(spec,"/downloads/distribution.tar.gz");
|
||||
assertMatches(spec,"/downloads/jetty.log.gz");
|
||||
|
||||
assertNotMatches(spec,"/downloads/distribution.zip");
|
||||
assertNotMatches(spec,"/downloads/distribution.tgz");
|
||||
assertNotMatches(spec,"/abs/path");
|
||||
|
||||
assertEquals("Spec.pathInfo",null,spec.getPathInfo("/downloads/distribution.tar.gz"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue