Issue #4776 - UriTemplatePathSpec sorting on simplified path spec.
+ Moved testcase to jetty-http (better place for it) + Introduced simplified path spec to aid in sorting + Made .getMatches(String) return sorted list by best match. Signed-off-by: Joakim Erdfelt <joakim.erdfelt@gmail.com> Signed-off-by: Ludovic Orban <lorban@bitronix.be>
This commit is contained in:
parent
00d68e2144
commit
79e76544ff
|
@ -0,0 +1,35 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.http.pathmap;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Sort {@link MappedResource}s by their {@link MappedResource#getPathSpec()} logical declarations.
|
||||
*/
|
||||
public class LogicalDeclarationComparator implements Comparator<MappedResource>
|
||||
{
|
||||
public static final LogicalDeclarationComparator INSTANCE = new LogicalDeclarationComparator();
|
||||
|
||||
@Override
|
||||
public int compare(MappedResource o1, MappedResource o2)
|
||||
{
|
||||
return o1.getPathSpec().compareTo(o2.getPathSpec());
|
||||
}
|
||||
}
|
|
@ -116,6 +116,7 @@ public class PathMappings<E> implements Iterable<MappedResource<E>>, Dumpable
|
|||
break;
|
||||
}
|
||||
}
|
||||
ret.sort(LogicalDeclarationComparator.INSTANCE);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,6 +68,10 @@ public class UriTemplatePathSpec implements PathSpec
|
|||
private final int _specLength;
|
||||
private final Pattern _pattern;
|
||||
private final String[] _variables;
|
||||
/**
|
||||
* The logical (simplified) declaration
|
||||
*/
|
||||
private final String _logicalDeclaration;
|
||||
|
||||
public UriTemplatePathSpec(String rawSpec)
|
||||
{
|
||||
|
@ -81,6 +85,7 @@ public class UriTemplatePathSpec implements PathSpec
|
|||
_specLength = 1;
|
||||
_pattern = Pattern.compile("^/$");
|
||||
_variables = new String[0];
|
||||
_logicalDeclaration = "/";
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -104,6 +109,7 @@ public class UriTemplatePathSpec implements PathSpec
|
|||
// split up into path segments (ignoring the first slash that will always be empty)
|
||||
String[] segments = rawSpec.substring(1).split("/");
|
||||
char[] segmentSignature = new char[segments.length];
|
||||
StringBuilder logicalSignature = new StringBuilder();
|
||||
int pathDepth = segments.length;
|
||||
for (int i = 0; i < segments.length; i++)
|
||||
{
|
||||
|
@ -123,6 +129,7 @@ public class UriTemplatePathSpec implements PathSpec
|
|||
assertIsValidVariableLiteral(variable, declaration);
|
||||
|
||||
segmentSignature[i] = 'v'; // variable
|
||||
logicalSignature.append("/*");
|
||||
// valid variable name
|
||||
varNames.add(variable);
|
||||
// build regex
|
||||
|
@ -147,6 +154,7 @@ public class UriTemplatePathSpec implements PathSpec
|
|||
{
|
||||
// valid path segment
|
||||
segmentSignature[i] = 'e'; // exact
|
||||
logicalSignature.append('/').append(segment);
|
||||
// build regex
|
||||
regex.append('/');
|
||||
// escape regex special characters
|
||||
|
@ -162,7 +170,10 @@ public class UriTemplatePathSpec implements PathSpec
|
|||
|
||||
// Handle trailing slash (which is not picked up during split)
|
||||
if (rawSpec.charAt(rawSpec.length() - 1) == '/')
|
||||
{
|
||||
regex.append('/');
|
||||
logicalSignature.append('/');
|
||||
}
|
||||
|
||||
regex.append('$');
|
||||
|
||||
|
@ -190,6 +201,7 @@ public class UriTemplatePathSpec implements PathSpec
|
|||
_specLength = declaration.length();
|
||||
_pattern = pattern;
|
||||
_variables = variables;
|
||||
_logicalDeclaration = logicalSignature.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -265,6 +277,20 @@ public class UriTemplatePathSpec implements PathSpec
|
|||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(PathSpec other)
|
||||
{
|
||||
if (other instanceof UriTemplatePathSpec)
|
||||
{
|
||||
UriTemplatePathSpec otherUriPathSpec = (UriTemplatePathSpec)other;
|
||||
return otherUriPathSpec._logicalDeclaration.compareTo(this._logicalDeclaration);
|
||||
}
|
||||
else
|
||||
{
|
||||
return PathSpec.super.compareTo(other);
|
||||
}
|
||||
}
|
||||
|
||||
public Map<String, String> getPathParams(String path)
|
||||
{
|
||||
Matcher matcher = getMatcher(path);
|
||||
|
|
|
@ -1,22 +1,44 @@
|
|||
package org.eclipse.jetty.websocket.servlet;
|
||||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2020 Mort Bay Consulting Pty Ltd and others.
|
||||
// ------------------------------------------------------------------------
|
||||
// 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.http.pathmap;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jetty.http.pathmap.MappedResource;
|
||||
import org.eclipse.jetty.http.pathmap.PathMappings;
|
||||
import org.eclipse.jetty.http.pathmap.UriTemplatePathSpec;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.not;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
public class WebSocketUriMappingTest
|
||||
{
|
||||
private PathMappings<String> mapping = new PathMappings<>();
|
||||
|
||||
private String getMatch(String pathSpecString)
|
||||
private String getBestMatch(String uriPath)
|
||||
{
|
||||
MappedResource<String> resource = mapping.getMatch(pathSpecString);
|
||||
return resource == null ? null : resource.getResource();
|
||||
List<MappedResource<String>> resources = mapping.getMatches(uriPath);
|
||||
assertThat("Matches on " + uriPath, resources, is(not(nullValue())));
|
||||
if (resources.isEmpty())
|
||||
return null;
|
||||
return resources.get(0).getResource();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -24,8 +46,8 @@ public class WebSocketUriMappingTest
|
|||
{
|
||||
mapping.put("/a/b", "endpointA");
|
||||
|
||||
assertThat(getMatch("/a/b"), is("endpointA"));
|
||||
assertNull(getMatch("/a/c"));
|
||||
assertThat(getBestMatch("/a/b"), is("endpointA"));
|
||||
assertNull(getBestMatch("/a/c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -33,10 +55,10 @@ public class WebSocketUriMappingTest
|
|||
{
|
||||
mapping.put(new UriTemplatePathSpec("/a/{var}"), "endpointA");
|
||||
|
||||
assertThat(getMatch("/a/b"), is("endpointA"));
|
||||
assertThat(getMatch("/a/apple"), is("endpointA"));
|
||||
assertNull(getMatch("/a"));
|
||||
assertNull(getMatch("/a/b/c"));
|
||||
assertThat(getBestMatch("/a/b"), is("endpointA"));
|
||||
assertThat(getBestMatch("/a/apple"), is("endpointA"));
|
||||
assertNull(getBestMatch("/a"));
|
||||
assertNull(getBestMatch("/a/b/c"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -46,9 +68,9 @@ public class WebSocketUriMappingTest
|
|||
mapping.put(new UriTemplatePathSpec("/a/b/c"), "endpointB");
|
||||
mapping.put(new UriTemplatePathSpec("/a/{var1}/{var2}"), "endpointC");
|
||||
|
||||
assertThat(getMatch("/a/b/c"), is("endpointB"));
|
||||
assertThat(getMatch("/a/d/c"), is("endpointA"));
|
||||
assertThat(getMatch("/a/x/y"), is("endpointC"));
|
||||
assertThat(getBestMatch("/a/b/c"), is("endpointB"));
|
||||
assertThat(getBestMatch("/a/d/c"), is("endpointA"));
|
||||
assertThat(getBestMatch("/a/x/y"), is("endpointC"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -57,7 +79,7 @@ public class WebSocketUriMappingTest
|
|||
mapping.put(new UriTemplatePathSpec("/{var1}/d"), "endpointA");
|
||||
mapping.put(new UriTemplatePathSpec("/b/{var2}"), "endpointB");
|
||||
|
||||
assertThat(getMatch("/b/d"), is("endpointB"));
|
||||
assertThat(getBestMatch("/b/d"), is("endpointB"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -66,7 +88,9 @@ public class WebSocketUriMappingTest
|
|||
mapping.put(new UriTemplatePathSpec("/{a}/b"), "suffix");
|
||||
mapping.put(new UriTemplatePathSpec("/{a}/{b}"), "prefix");
|
||||
|
||||
assertThat(getMatch("/a/b"), is("suffix"));
|
||||
List<MappedResource<String>> matches = mapping.getMatches("/a/b");
|
||||
|
||||
assertThat(getBestMatch("/a/b"), is("suffix"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -75,7 +99,7 @@ public class WebSocketUriMappingTest
|
|||
mapping.put(new UriTemplatePathSpec("/a/{b}/c"), "middle");
|
||||
mapping.put(new UriTemplatePathSpec("/a/b/{c}"), "suffix");
|
||||
|
||||
assertThat(getMatch("/a/b/c"), is("suffix"));
|
||||
assertThat(getBestMatch("/a/b/c"), is("suffix"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -84,7 +108,7 @@ public class WebSocketUriMappingTest
|
|||
mapping.put(new UriTemplatePathSpec("/{a}/b/{c}"), "middle");
|
||||
mapping.put(new UriTemplatePathSpec("/{a}/b/c"), "suffix");
|
||||
|
||||
assertThat(getMatch("/a/b/c"), is("suffix"));
|
||||
assertThat(getBestMatch("/a/b/c"), is("suffix"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -93,7 +117,7 @@ public class WebSocketUriMappingTest
|
|||
mapping.put(new UriTemplatePathSpec("/a/{b}/{c}/d"), "middle");
|
||||
mapping.put(new UriTemplatePathSpec("/a/b/c/{d}"), "prefix");
|
||||
|
||||
assertThat(getMatch("/a/b/c/d"), is("prefix"));
|
||||
assertThat(getBestMatch("/a/b/c/d"), is("prefix"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -103,7 +127,7 @@ public class WebSocketUriMappingTest
|
|||
mapping.put(new UriTemplatePathSpec("/a/{b}/{c}/d"), "middle1");
|
||||
mapping.put(new UriTemplatePathSpec("/a/{b}/c/d"), "middle2");
|
||||
|
||||
assertThat(getMatch("/a/b/c/d"), is("middle2"));
|
||||
assertThat(getBestMatch("/a/b/c/d"), is("middle2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -112,7 +136,7 @@ public class WebSocketUriMappingTest
|
|||
mapping.put(new UriTemplatePathSpec("/{a}/{bz}/c/{d}"), "middle1");
|
||||
mapping.put(new UriTemplatePathSpec("/{a}/{ba}/{c}/d"), "middle2");
|
||||
|
||||
assertThat(getMatch("/a/b/c/d"), is("middle1"));
|
||||
assertThat(getBestMatch("/a/b/c/d"), is("middle1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -121,7 +145,7 @@ public class WebSocketUriMappingTest
|
|||
mapping.put(new UriTemplatePathSpec("/{a}/{ba}/c/{d}"), "middle1");
|
||||
mapping.put(new UriTemplatePathSpec("/{a}/{bz}/{c}/d"), "middle2");
|
||||
|
||||
assertThat(getMatch("/a/b/c/d"), is("middle1"));
|
||||
assertThat(getBestMatch("/a/b/c/d"), is("middle1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -131,7 +155,7 @@ public class WebSocketUriMappingTest
|
|||
mapping.put(new UriTemplatePathSpec("/a/{b}/{c}"), "prefix1");
|
||||
mapping.put(new UriTemplatePathSpec("/a/b/{c}"), "prefix2");
|
||||
|
||||
assertThat(getMatch("/a/b/c"), is("prefix2"));
|
||||
assertThat(getBestMatch("/a/b/c"), is("prefix2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -141,6 +165,6 @@ public class WebSocketUriMappingTest
|
|||
mapping.put(new UriTemplatePathSpec("/{a}/{b}/c"), "suffix1");
|
||||
mapping.put(new UriTemplatePathSpec("/{a}/b/c"), "suffix2");
|
||||
|
||||
assertThat(getMatch("/a/b/c"), is("suffix2"));
|
||||
assertThat(getBestMatch("/a/b/c"), is("suffix2"));
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue