diff --git a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/PathParamSpec.java b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/PathParamSpec.java index 13158f99bba..d5366c0835a 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/PathParamSpec.java +++ b/jetty-websocket/javax-websocket-server-impl/src/main/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/PathParamSpec.java @@ -19,8 +19,11 @@ 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; @@ -95,9 +98,11 @@ public class PathParamSpec extends PathSpec List varNames = new ArrayList<>(); String segments[] = pathParamSpec.split("/"); + char segmentSignature[] = new char[segments.length]; this.pathDepth = segments.length - 1; - for (String segment : segments) + for (int i = 0; i < segments.length; i++) { + String segment = segments[i]; Matcher mat = VARIABLE_PATTERN.matcher(segment); if (mat.matches()) @@ -116,6 +121,7 @@ public class PathParamSpec extends PathSpec } else if (VALID_VARIABLE_NAME.matcher(variable).matches()) { + segmentSignature[i] = 'v'; // variable // valid variable name varNames.add(variable); // build regex @@ -171,6 +177,7 @@ public class PathParamSpec extends PathSpec else { // valid path segment + segmentSignature[i] = 'e'; // exact // build regex if (wantDelim) { @@ -189,25 +196,45 @@ public class PathParamSpec extends PathSpec int varcount = varNames.size(); this.variables = varNames.toArray(new String[varcount]); - if (varcount > 1) - { - this.group = PathSpecGroup.MIDDLE_GLOB; - } - if (varcount == 1) - { - if (isLastSegmentVariable) - { - this.group = PathSpecGroup.PREFIX_GLOB; - } - else - { - this.group = PathSpecGroup.MIDDLE_GLOB; - } - } - else + // 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; + } + } + + public Map getPathParams(String path) + { + Matcher matcher = getMatcher(path); + if (matcher.matches()) + { + if (group == PathSpecGroup.EXACT) + { + return Collections.emptyMap(); + } + Map 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() diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/PathParamSpecTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/PathParamSpecTest.java index db002c620d5..89f47694c19 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/PathParamSpecTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/PathParamSpecTest.java @@ -18,8 +18,11 @@ package org.eclipse.jetty.websocket.jsr356.server.pathmap; +import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import java.util.Map; + import org.junit.Test; /** @@ -27,6 +30,29 @@ import org.junit.Test; */ public class PathParamSpecTest { + private void assertDetectedVars(PathParamSpec 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(PathParamSpec spec, String path) + { + String msg = String.format("Spec(\"%s\").matches(\"%s\")",spec.getPathSpec(),path); + assertThat(msg,spec.matches(path),is(true)); + } + + private void assertNotMatches(PathParamSpec 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() { @@ -34,6 +60,7 @@ public class PathParamSpecTest 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.variableCount",0,spec.getVariableCount()); assertEquals("Spec.variable.length",0,spec.getVariables().length); @@ -46,6 +73,7 @@ public class PathParamSpecTest 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.variableCount",0,spec.getVariableCount()); assertEquals("Spec.variable.length",0,spec.getVariables().length); @@ -58,9 +86,16 @@ public class PathParamSpecTest 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.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 @@ -70,9 +105,62 @@ public class PathParamSpecTest 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.variableCount",1,spec.getVariableCount()); - assertEquals("Spec.variable.length",1,spec.getVariables().length); - assertEquals("Spec.variable[0]","foo",spec.getVariables()[0]); + assertDetectedVars(spec,"foo"); + + assertMatches(spec,"/a/b"); + assertNotMatches(spec,"/a/"); + assertNotMatches(spec,"/a"); + + Map 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 testTwoVarPrefixPathSpec() + { + PathParamSpec spec = new PathParamSpec("/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.group); + + assertDetectedVars(spec,"var1","var2"); + + assertMatches(spec,"/a/b/c"); + assertNotMatches(spec,"/a/bc"); + assertNotMatches(spec,"/a/b/"); + assertNotMatches(spec,"/a/b"); + + Map 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() + { + PathParamSpec spec = new PathParamSpec("/{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.group); + + assertDetectedVars(spec,"var1"); + + assertMatches(spec,"/a"); + assertNotMatches(spec,"/"); + assertNotMatches(spec,"/a/b"); + assertNotMatches(spec,"/a/b/c"); + + Map 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")); } } diff --git a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/ServletPathSpecTest.java b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/ServletPathSpecTest.java index a288e0ee4d7..3e708a9676f 100644 --- a/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/ServletPathSpecTest.java +++ b/jetty-websocket/javax-websocket-server-impl/src/test/java/org/eclipse/jetty/websocket/jsr356/server/pathmap/ServletPathSpecTest.java @@ -34,6 +34,7 @@ public class ServletPathSpecTest catch (IllegalArgumentException e) { // expected path + System.out.println(e); } }