Introduce PathPatternParser for optimized path matching

This commit introduces a PathPatternParser which parses request pattern
strings into PathPattern objects which can then be used to fast
match incoming string paths. The parser and matching supports the syntax
as described in SPR-14544. The code is optimized around the common usages
of request patterns and is designed to create very little transient
garbage when matching.

Issue: SPR-14544
This commit is contained in:
Andy Clement
2016-10-11 15:14:08 -07:00
committed by Brian Clozel
parent 6f029392c7
commit f58ffad939
44 changed files with 3880 additions and 73 deletions

View File

@@ -31,11 +31,11 @@ import reactor.core.publisher.Mono;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.PathMatcher;
import org.springframework.web.reactive.function.BodyExtractor;
import org.springframework.web.server.WebSession;
import org.springframework.web.util.ParsingPathMatcher;
/**
* Implementations of {@link RequestPredicate} that implement various useful request matching operations, such as
@@ -46,7 +46,7 @@ import org.springframework.web.server.WebSession;
*/
public abstract class RequestPredicates {
private static final PathMatcher DEFAULT_PATH_MATCHER = new AntPathMatcher();
private static final PathMatcher DEFAULT_PATH_MATCHER = new ParsingPathMatcher();
/**
* Returns a {@code RequestPredicate} that always matches.

View File

@@ -22,7 +22,6 @@ import reactor.core.publisher.Mono;
import org.springframework.context.support.ApplicationObjectSupport;
import org.springframework.core.Ordered;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.Assert;
import org.springframework.util.PathMatcher;
import org.springframework.web.cors.CorsConfiguration;
@@ -35,6 +34,7 @@ import org.springframework.web.reactive.HandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebHandler;
import org.springframework.web.server.support.HttpRequestPathHelper;
import org.springframework.web.util.ParsingPathMatcher;
/**
* Abstract base class for {@link org.springframework.web.reactive.HandlerMapping}
@@ -53,7 +53,7 @@ public abstract class AbstractHandlerMapping extends ApplicationObjectSupport im
private HttpRequestPathHelper pathHelper = new HttpRequestPathHelper();
private PathMatcher pathMatcher = new AntPathMatcher();
private PathMatcher pathMatcher = new ParsingPathMatcher();
private final UrlBasedCorsConfigurationSource globalCorsConfigSource = new UrlBasedCorsConfigurationSource();

View File

@@ -33,11 +33,11 @@ import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.reactive.handler.SimpleUrlHandlerMapping;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.support.HttpRequestPathHelper;
import org.springframework.web.util.ParsingPathMatcher;
/**
* A central component to use to obtain the public URL path that clients should
@@ -56,7 +56,7 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
private HttpRequestPathHelper urlPathHelper = new HttpRequestPathHelper();
private PathMatcher pathMatcher = new AntPathMatcher();
private PathMatcher pathMatcher = new ParsingPathMatcher();
private final Map<String, ResourceWebHandler> handlerMap = new LinkedHashMap<>();

View File

@@ -27,11 +27,11 @@ import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.support.HttpRequestPathHelper;
import org.springframework.web.util.ParsingPathMatcher;
/**
* A logical disjunction (' || ') request condition that matches a request
@@ -90,7 +90,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition<Pat
this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns));
this.pathHelper = (pathHelper != null ? pathHelper : new HttpRequestPathHelper());
this.pathMatcher = (pathMatcher != null ? pathMatcher : new AntPathMatcher());
this.pathMatcher = (pathMatcher != null ? pathMatcher : new ParsingPathMatcher());
this.useSuffixPatternMatch = useSuffixPatternMatch;
this.useTrailingSlashMatch = useTrailingSlashMatch;
if (fileExtensions != null) {

View File

@@ -74,7 +74,7 @@ public class SimpleUrlHandlerMappingTests {
testUrl("welcome.html", null, handlerMapping, null);
testUrl("/pathmatchingAA.html", mainController, handlerMapping, "pathmatchingAA.html");
testUrl("/pathmatchingA.html", null, handlerMapping, null);
testUrl("/administrator/pathmatching.html", mainController, handlerMapping, "pathmatching.html");
testUrl("/administrator/pathmatching.html", mainController, handlerMapping, "/administrator/pathmatching.html");
testUrl("/administrator/test/pathmatching.html", mainController, handlerMapping, "test/pathmatching.html");
testUrl("/administratort/pathmatching.html", null, handlerMapping, null);
testUrl("/administrator/another/bla.xml", mainController, handlerMapping, "/administrator/another/bla.xml");

View File

@@ -98,9 +98,9 @@ public class PatternsRequestConditionTests {
@Test
public void matchSortPatterns() throws Exception {
PatternsRequestCondition condition = new PatternsRequestCondition("/**", "/foo/bar", "/foo/*");
PatternsRequestCondition condition = new PatternsRequestCondition("/*/*", "/foo/bar", "/foo/*");
PatternsRequestCondition match = condition.getMatchingCondition(createExchange("/foo/bar"));
PatternsRequestCondition expected = new PatternsRequestCondition("/foo/bar", "/foo/*", "/**");
PatternsRequestCondition expected = new PatternsRequestCondition("/foo/bar", "/foo/*", "/*/*");
assertEquals(expected, match);
}

View File

@@ -33,7 +33,6 @@ import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.mock.http.server.reactive.test.MockServerHttpRequest;
import org.springframework.mock.http.server.reactive.test.MockServerHttpResponse;
import org.springframework.stereotype.Controller;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.PathMatcher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.method.HandlerMethod;
@@ -41,6 +40,7 @@ import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.adapter.DefaultServerWebExchange;
import org.springframework.web.server.session.MockWebSessionManager;
import org.springframework.web.server.session.WebSessionManager;
import org.springframework.web.util.ParsingPathMatcher;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -158,7 +158,7 @@ public class HandlerMethodMappingTests {
private static class MyHandlerMethodMapping extends AbstractHandlerMethodMapping<String> {
private PathMatcher pathMatcher = new AntPathMatcher();
private PathMatcher pathMatcher = new ParsingPathMatcher();
@Override
protected boolean isHandler(Class<?> beanType) {

View File

@@ -1,23 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="mapping" class="org.springframework.web.reactive.handler.SimpleUrlHandlerMapping">
<property name="urlDecode" value="true" />
<property name="mappings">
<value>
welcome.html=mainController
/**/pathmatchingTest.html=mainController
/**/pathmatching??.html=mainController
/**/path??matching.html=mainController
/**/??path??matching.html=mainController
/**/*.jsp=mainController
/administrator/**/pathmatching.html=mainController
/administrator/**/testlast*=mainController
/*pathmatchingTest.html=mainController
/pathmatching??.html=mainController
/administrator/pathmatching.html=mainController
/administrator/*/pathmatching.html=mainController
/administrator/*/testlast*=mainController
/administrator/testing/longer/*=mainController
/??path??matching.html=mainController
/path??matching.html=mainController
/administrator/another/bla.xml=mainController
/administrator/testing/longer/**/**/**/**/**=mainController
/administrator/testing/longer2/**/**/bla/**=mainController
/*test*.jpeg=mainController
/*/test.jpeg=mainController
/outofpattern*yeah=mainController