Separate out URL path parsing in PathContainer

PathContainer now has separate parsePath and parseUrlPath methods each
creating PathSegment and UrlPathSegment respectively.
This commit is contained in:
Rossen Stoyanchev 2017-07-11 10:06:15 +02:00
parent 1d201a55db
commit 0e370e0703
18 changed files with 149 additions and 103 deletions

View File

@ -20,6 +20,7 @@ import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -88,7 +89,11 @@ class DefaultPathContainer implements PathContainer {
} }
static PathContainer parsePath(String path, Charset charset) { static PathContainer createFromPath(String path) {
return parsePathInternal(path, DefaultPathSegment::new);
}
private static PathContainer parsePathInternal(String path, Function<String, PathSegment> segmentParser) {
if (path.equals("")) { if (path.equals("")) {
return EMPTY_PATH; return EMPTY_PATH;
} }
@ -105,7 +110,7 @@ class DefaultPathContainer implements PathContainer {
int end = path.indexOf('/', begin); int end = path.indexOf('/', begin);
String segment = (end != -1 ? path.substring(begin, end) : path.substring(begin)); String segment = (end != -1 ? path.substring(begin, end) : path.substring(begin));
if (!segment.equals("")) { if (!segment.equals("")) {
elements.add(parsePathSegment(segment, charset)); elements.add(segmentParser.apply(segment));
} }
if (end == -1) { if (end == -1) {
break; break;
@ -116,27 +121,31 @@ class DefaultPathContainer implements PathContainer {
return new DefaultPathContainer(path, elements); return new DefaultPathContainer(path, elements);
} }
private static PathContainer.Segment parsePathSegment(String input, Charset charset) { static PathContainer createFromUrlPath(String path, Charset charset) {
return parsePathInternal(path, segment -> parseUrlPathSegment(segment, charset));
}
private static PathContainer.UrlPathSegment parseUrlPathSegment(String input, Charset charset) {
int index = input.indexOf(';'); int index = input.indexOf(';');
if (index == -1) { if (index == -1) {
String valueToMatch = StringUtils.uriDecode(input, charset); String valueToMatch = StringUtils.uriDecode(input, charset);
return new DefaultPathSegment(input, valueToMatch, EMPTY_MAP); return new DefaultUrlPathSegment(input, valueToMatch, EMPTY_MAP);
} }
else { else {
String valueToMatch = StringUtils.uriDecode(input.substring(0, index), charset); String valueToMatch = StringUtils.uriDecode(input.substring(0, index), charset);
String pathParameterContent = input.substring(index); String pathParameterContent = input.substring(index);
MultiValueMap<String, String> parameters = parseParams(pathParameterContent, charset); MultiValueMap<String, String> parameters = parsePathParams(pathParameterContent, charset);
return new DefaultPathSegment(input, valueToMatch, parameters); return new DefaultUrlPathSegment(input, valueToMatch, parameters);
} }
} }
private static MultiValueMap<String, String> parseParams(String input, Charset charset) { private static MultiValueMap<String, String> parsePathParams(String input, Charset charset) {
MultiValueMap<String, String> result = new LinkedMultiValueMap<>(); MultiValueMap<String, String> result = new LinkedMultiValueMap<>();
int begin = 1; int begin = 1;
while (begin < input.length()) { while (begin < input.length()) {
int end = input.indexOf(';', begin); int end = input.indexOf(';', begin);
String param = (end != -1 ? input.substring(begin, end) : input.substring(begin)); String param = (end != -1 ? input.substring(begin, end) : input.substring(begin));
parseParamValues(param, charset, result); parsePathParamValues(param, charset, result);
if (end == -1) { if (end == -1) {
break; break;
} }
@ -145,7 +154,7 @@ class DefaultPathContainer implements PathContainer {
return result; return result;
} }
private static void parseParamValues(String input, Charset charset, MultiValueMap<String, String> output) { private static void parsePathParamValues(String input, Charset charset, MultiValueMap<String, String> output) {
if (StringUtils.hasText(input)) { if (StringUtils.hasText(input)) {
int index = input.indexOf("="); int index = input.indexOf("=");
if (index != -1) { if (index != -1) {
@ -186,24 +195,19 @@ class DefaultPathContainer implements PathContainer {
} }
private static class DefaultPathSegment implements PathContainer.Segment { private static class DefaultPathSegment implements PathSegment {
private final String value; private final String value;
private final String valueToMatch; private final char[] valueAsChars;
private final char[] valueToMatchAsChars;
private final MultiValueMap<String, String> parameters; DefaultPathSegment(String value) {
DefaultPathSegment(String value, String valueToMatch, MultiValueMap<String, String> params) {
Assert.isTrue(!value.contains("/"), () -> "Invalid path segment value: " + value);
this.value = value; this.value = value;
this.valueToMatch = valueToMatch; this.valueAsChars = value.toCharArray();
this.valueToMatchAsChars = valueToMatch.toCharArray();
this.parameters = CollectionUtils.unmodifiableMultiValueMap(params);
} }
@Override @Override
public String value() { public String value() {
return this.value; return this.value;
@ -211,17 +215,12 @@ class DefaultPathContainer implements PathContainer {
@Override @Override
public String valueToMatch() { public String valueToMatch() {
return this.valueToMatch; return this.value;
} }
@Override @Override
public char[] valueToMatchAsChars() { public char[] valueToMatchAsChars() {
return this.valueToMatchAsChars; return this.valueAsChars;
}
@Override
public MultiValueMap<String, String> parameters() {
return this.parameters;
} }
@Override @Override
@ -241,7 +240,42 @@ class DefaultPathContainer implements PathContainer {
} }
public String toString() { public String toString() {
return "[value='" + this.value + "', parameters=" + this.parameters + "']"; } return "[value='" + this.value + "']"; }
}
private static class DefaultUrlPathSegment extends DefaultPathSegment implements UrlPathSegment {
private final String valueToMatch;
private final char[] valueToMatchAsChars;
private final MultiValueMap<String, String> parameters;
DefaultUrlPathSegment(String value, String valueToMatch, MultiValueMap<String, String> params) {
super(value);
Assert.isTrue(!value.contains("/"), () -> "Invalid path segment value: " + value);
this.valueToMatch = valueToMatch;
this.valueToMatchAsChars = valueToMatch.toCharArray();
this.parameters = CollectionUtils.unmodifiableMultiValueMap(params);
}
@Override
public String valueToMatch() {
return this.valueToMatch;
}
@Override
public char[] valueToMatchAsChars() {
return this.valueToMatchAsChars;
}
@Override
public MultiValueMap<String, String> parameters() {
return this.parameters;
}
} }
} }

View File

@ -17,7 +17,6 @@
package org.springframework.http.server.reactive; package org.springframework.http.server.reactive;
import java.net.URI; import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
@ -40,7 +39,7 @@ class DefaultRequestPath implements RequestPath {
DefaultRequestPath(URI uri, @Nullable String contextPath) { DefaultRequestPath(URI uri, @Nullable String contextPath) {
this.fullPath = PathContainer.parse(uri.getRawPath(), StandardCharsets.UTF_8); this.fullPath = PathContainer.parseUrlPath(uri.getRawPath());
this.contextPath = initContextPath(this.fullPath, contextPath); this.contextPath = initContextPath(this.fullPath, contextPath);
this.pathWithinApplication = extractPathWithinApplication(this.fullPath, this.contextPath); this.pathWithinApplication = extractPathWithinApplication(this.fullPath, this.contextPath);
} }
@ -53,7 +52,7 @@ class DefaultRequestPath implements RequestPath {
private static PathContainer initContextPath(PathContainer path, @Nullable String contextPath) { private static PathContainer initContextPath(PathContainer path, @Nullable String contextPath) {
if (!StringUtils.hasText(contextPath) || "/".equals(contextPath)) { if (!StringUtils.hasText(contextPath) || "/".equals(contextPath)) {
return PathContainer.parse("", StandardCharsets.UTF_8); return PathContainer.parseUrlPath("");
} }
Assert.isTrue(contextPath.startsWith("/") && !contextPath.endsWith("/") && Assert.isTrue(contextPath.startsWith("/") && !contextPath.endsWith("/") &&

View File

@ -16,34 +16,36 @@
package org.springframework.http.server.reactive; package org.springframework.http.server.reactive;
import java.nio.charset.Charset; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.List;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
/** /**
* Structured representation of a path whose {@link Element Elements} are * Structured representation of a path whose elements are parsed into a sequence
* accessible as a sequence of either {@link Separator Separator} and/or * of {@link Separator Separator} and {@link PathSegment PathSegment} elements.
* {@link Segment Segment} (element) types.
* *
* <p>Each {@code Segment} exposes its own structure decoded safely without the * <p>An instance of this class can be created via {@link #parsePath(String)} or
* risk of encoded reserved characters altering the path or segment structure. * {@link #parseUrlPath(String)}. For an HTTP request the path can be
* accessed via {@link ServerHttpRequest#getPath()}.
* *
* <p>An instance of this class can also be created via * <p>For a URL path each {@link UrlPathSegment UrlPathSegment} exposes its
* {@link #parse(String, Charset)}. The path for an HTTP request is parsed once * structure decoded safely without the risk of encoded reserved characters
* and subsequently accessible via {@link ServerHttpRequest#getPath()}. * altering the path or segment structure and without path parameters for
* path matching purposes.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 5.0
*/ */
public interface PathContainer { public interface PathContainer {
/** /**
* The original, raw (encoded) path value including path parameters. * The original path that was parsed.
*/ */
String value(); String value();
/** /**
* The list of path elements, either {@link Separator} or {@link Segment}. * The list of path elements, either {@link Separator} or {@link PathSegment}.
*/ */
List<Element> elements(); List<Element> elements();
@ -69,13 +71,23 @@ public interface PathContainer {
/** /**
* Parse the given path value into a {@link PathContainer}. * Parse the path value into a sequence of {@link Separator Separator} and
* @param path the encoded, raw path value to parse * {@link PathSegment PathSegment} elements.
* @param encoding the charset to use for decoded path segment values * @param path the path value to parse
* @return the parsed path * @return the parsed path
*/ */
static PathContainer parse(String path, Charset encoding) { static PathContainer parsePath(String path) {
return DefaultPathContainer.parsePath(path, encoding); return DefaultPathContainer.createFromPath(path);
}
/**
* Parse the path value into a sequence of {@link Separator Separator} and
* {@link UrlPathSegment UrlPathSegment} elements.
* @param path the encoded, raw URL path value to parse
* @return the parsed path
*/
static PathContainer parseUrlPath(String path) {
return DefaultPathContainer.createFromUrlPath(path, StandardCharsets.UTF_8);
} }
@ -101,19 +113,27 @@ public interface PathContainer {
/** /**
* Path segment element. * Path segment element.
*/ */
interface Segment extends Element { interface PathSegment extends Element {
/** /**
* Return the path segment value to use for pattern matching purposes. * Return the path segment value to use for pattern matching purposes.
* This may differ from {@link #value()} such as being decoded, without * By default this is the same as {@link #value()} but may also differ
* path parameters, etc. * in sub-interfaces (e.g. decoded, sanitized, etc.).
*/ */
String valueToMatch(); String valueToMatch();
/** /**
* Variant of {@link #valueToMatch()} as a {@code char[]}. * The same as {@link #valueToMatch()} but as a {@code char[]}.
*/ */
char[] valueToMatchAsChars(); char[] valueToMatchAsChars();
}
/**
* Specialization of {@link PathSegment} for a URL path.
* The {@link #valueToMatch()} is decoded and without path parameters.
*/
interface UrlPathSegment extends PathSegment {
/** /**
* Path parameters parsed from the path segment. * Path parameters parsed from the path segment.

View File

@ -19,7 +19,7 @@ package org.springframework.web.util.pattern;
import java.util.List; import java.util.List;
import org.springframework.http.server.reactive.PathContainer.Element; import org.springframework.http.server.reactive.PathContainer.Element;
import org.springframework.http.server.reactive.PathContainer.Segment; import org.springframework.http.server.reactive.PathContainer.UrlPathSegment;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import org.springframework.web.util.pattern.PathPattern.MatchingContext; import org.springframework.web.util.pattern.PathPattern.MatchingContext;
@ -65,8 +65,8 @@ class CaptureTheRestPathElement extends PathElement {
MultiValueMap<String,String> parametersCollector = null; MultiValueMap<String,String> parametersCollector = null;
for (int i = pathIndex; i < matchingContext.pathLength; i++) { for (int i = pathIndex; i < matchingContext.pathLength; i++) {
Element element = matchingContext.pathElements.get(i); Element element = matchingContext.pathElements.get(i);
if (element instanceof Segment) { if (element instanceof UrlPathSegment) {
MultiValueMap<String, String> parameters = ((Segment) element).parameters(); MultiValueMap<String, String> parameters = ((UrlPathSegment) element).parameters();
if (!parameters.isEmpty()) { if (!parameters.isEmpty()) {
if (parametersCollector == null) { if (parametersCollector == null) {
parametersCollector = new LinkedMultiValueMap<>(); parametersCollector = new LinkedMultiValueMap<>();
@ -85,8 +85,8 @@ class CaptureTheRestPathElement extends PathElement {
StringBuilder buf = new StringBuilder(); StringBuilder buf = new StringBuilder();
for (int i = fromSegment, max = pathElements.size(); i < max; i++) { for (int i = fromSegment, max = pathElements.size(); i < max; i++) {
Element element = pathElements.get(i); Element element = pathElements.get(i);
if (element instanceof Segment) { if (element instanceof UrlPathSegment) {
buf.append(((Segment)element).valueToMatch()); buf.append(((UrlPathSegment)element).valueToMatch());
} }
else { else {
buf.append(element.value()); buf.append(element.value());

View File

@ -19,7 +19,7 @@ package org.springframework.web.util.pattern;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.springframework.http.server.reactive.PathContainer.Segment; import org.springframework.http.server.reactive.PathContainer.UrlPathSegment;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
/** /**
@ -120,7 +120,7 @@ class CaptureVariablePathElement extends PathElement {
} }
if (match && matchingContext.extractingVariables) { if (match && matchingContext.extractingVariables) {
matchingContext.set(this.variableName, candidateCapture, ((Segment)matchingContext.pathElements.get(pathIndex-1)).parameters()); matchingContext.set(this.variableName, candidateCapture, ((UrlPathSegment)matchingContext.pathElements.get(pathIndex-1)).parameters());
} }
return match; return match;
} }

View File

@ -16,8 +16,9 @@
package org.springframework.web.util.pattern; package org.springframework.web.util.pattern;
import org.springframework.http.server.reactive.PathContainer;
import org.springframework.http.server.reactive.PathContainer.Element; import org.springframework.http.server.reactive.PathContainer.Element;
import org.springframework.http.server.reactive.PathContainer.Segment; import org.springframework.http.server.reactive.PathContainer.PathSegment;
import org.springframework.web.util.pattern.PathPattern.MatchingContext; import org.springframework.web.util.pattern.PathPattern.MatchingContext;
/** /**
@ -59,16 +60,16 @@ class LiteralPathElement extends PathElement {
return false; return false;
} }
Element element = matchingContext.pathElements.get(pathIndex); Element element = matchingContext.pathElements.get(pathIndex);
if (!(element instanceof Segment)) { if (!(element instanceof PathContainer.PathSegment)) {
return false; return false;
} }
String value = ((Segment)element).valueToMatch(); String value = ((PathSegment)element).valueToMatch();
if (value.length() != len) { if (value.length() != len) {
// Not enough data to match this path element // Not enough data to match this path element
return false; return false;
} }
char[] data = ((Segment)element).valueToMatchAsChars(); char[] data = ((PathContainer.PathSegment)element).valueToMatchAsChars();
if (this.caseSensitive) { if (this.caseSensitive) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (data[i] != this.text[i]) { if (data[i] != this.text[i]) {

View File

@ -16,7 +16,6 @@
package org.springframework.web.util.pattern; package org.springframework.web.util.pattern;
import java.nio.charset.StandardCharsets;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Map; import java.util.Map;
@ -61,26 +60,26 @@ public class ParsingPathMatcher implements PathMatcher {
@Override @Override
public boolean match(String pattern, String path) { public boolean match(String pattern, String path) {
PathPattern pathPattern = getPathPattern(pattern); PathPattern pathPattern = getPathPattern(pattern);
return pathPattern.matches(PathContainer.parse(path, StandardCharsets.UTF_8)); return pathPattern.matches(PathContainer.parseUrlPath(path));
} }
@Override @Override
public boolean matchStart(String pattern, String path) { public boolean matchStart(String pattern, String path) {
PathPattern pathPattern = getPathPattern(pattern); PathPattern pathPattern = getPathPattern(pattern);
return pathPattern.matchStart(PathContainer.parse(path, StandardCharsets.UTF_8)); return pathPattern.matchStart(PathContainer.parseUrlPath(path));
} }
@Override @Override
public String extractPathWithinPattern(String pattern, String path) { public String extractPathWithinPattern(String pattern, String path) {
PathPattern pathPattern = getPathPattern(pattern); PathPattern pathPattern = getPathPattern(pattern);
PathContainer pathContainer = PathContainer.parse(path, StandardCharsets.UTF_8); PathContainer pathContainer = PathContainer.parseUrlPath(path);
return pathPattern.extractPathWithinPattern(pathContainer).value(); return pathPattern.extractPathWithinPattern(pathContainer).value();
} }
@Override @Override
public Map<String, String> extractUriTemplateVariables(String pattern, String path) { public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
PathPattern pathPattern = getPathPattern(pattern); PathPattern pathPattern = getPathPattern(pattern);
PathContainer pathContainer = PathContainer.parse(path, StandardCharsets.UTF_8); PathContainer pathContainer = PathContainer.parseUrlPath(path);
PathMatchResult results = pathPattern.matchAndExtract(pathContainer); PathMatchResult results = pathPattern.matchAndExtract(pathContainer);
// Collapse PathMatchResults to simple value results // Collapse PathMatchResults to simple value results
// TODO: (path parameters are lost in this translation) // TODO: (path parameters are lost in this translation)

View File

@ -16,7 +16,6 @@
package org.springframework.web.util.pattern; package org.springframework.web.util.pattern;
import java.nio.charset.StandardCharsets;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
@ -24,7 +23,6 @@ import java.util.Map;
import org.springframework.http.server.reactive.PathContainer; import org.springframework.http.server.reactive.PathContainer;
import org.springframework.http.server.reactive.PathContainer.Element; import org.springframework.http.server.reactive.PathContainer.Element;
import org.springframework.http.server.reactive.PathContainer.Segment;
import org.springframework.http.server.reactive.PathContainer.Separator; import org.springframework.http.server.reactive.PathContainer.Separator;
import org.springframework.lang.Nullable; import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils; import org.springframework.util.CollectionUtils;
@ -71,7 +69,7 @@ import org.springframework.util.StringUtils;
*/ */
public class PathPattern implements Comparable<PathPattern> { public class PathPattern implements Comparable<PathPattern> {
private final static PathContainer EMPTY_PATH = PathContainer.parse("", StandardCharsets.UTF_8); private final static PathContainer EMPTY_PATH = PathContainer.parsePath("");
/** The parser used to construct this pattern */ /** The parser used to construct this pattern */
private final PathPatternParser parser; private final PathPatternParser parser;
@ -263,7 +261,7 @@ public class PathPattern implements Comparable<PathPattern> {
public PathContainer extractPathWithinPattern(PathContainer path) { public PathContainer extractPathWithinPattern(PathContainer path) {
// TODO: implement extractPathWithinPattern for PathContainer // TODO: implement extractPathWithinPattern for PathContainer
String result = extractPathWithinPattern(path.value()); String result = extractPathWithinPattern(path.value());
return PathContainer.parse(result, StandardCharsets.UTF_8); return PathContainer.parseUrlPath(result);
} }
private String extractPathWithinPattern(String path) { private String extractPathWithinPattern(String path) {
@ -405,7 +403,7 @@ public class PathPattern implements Comparable<PathPattern> {
// /usr + /user => /usr/user // /usr + /user => /usr/user
// /{foo} + /bar => /{foo}/bar // /{foo} + /bar => /{foo}/bar
if (!this.patternString.equals(pattern2string.patternString) && this.capturedVariableCount == 0 && if (!this.patternString.equals(pattern2string.patternString) && this.capturedVariableCount == 0 &&
matches(PathContainer.parse(pattern2string.patternString, StandardCharsets.UTF_8))) { matches(PathContainer.parseUrlPath(pattern2string.patternString))) {
return pattern2string; return pattern2string;
} }
@ -682,8 +680,8 @@ public class PathPattern implements Comparable<PathPattern> {
*/ */
String pathElementValue(int pathIndex) { String pathElementValue(int pathIndex) {
Element element = (pathIndex < pathLength) ? pathElements.get(pathIndex) : null; Element element = (pathIndex < pathLength) ? pathElements.get(pathIndex) : null;
if (element instanceof Segment) { if (element instanceof PathContainer.PathSegment) {
return ((Segment)element).valueToMatch(); return ((PathContainer.PathSegment)element).valueToMatch();
} }
return ""; return "";
} }

View File

@ -21,7 +21,7 @@ import java.util.List;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.springframework.http.server.reactive.PathContainer.Segment; import org.springframework.http.server.reactive.PathContainer.UrlPathSegment;
import org.springframework.web.util.pattern.PathPattern.MatchingContext; import org.springframework.web.util.pattern.PathPattern.MatchingContext;
/** /**
@ -173,7 +173,7 @@ class RegexPathElement extends PathElement {
String value = matcher.group(i); String value = matcher.group(i);
matchingContext.set(name, value, matchingContext.set(name, value,
(i == this.variableNames.size())? (i == this.variableNames.size())?
((Segment)matchingContext.pathElements.get(pathIndex)).parameters(): ((UrlPathSegment)matchingContext.pathElements.get(pathIndex)).parameters():
NO_PARAMETERS); NO_PARAMETERS);
} }
} }

View File

@ -17,7 +17,7 @@
package org.springframework.web.util.pattern; package org.springframework.web.util.pattern;
import org.springframework.http.server.reactive.PathContainer.Element; import org.springframework.http.server.reactive.PathContainer.Element;
import org.springframework.http.server.reactive.PathContainer.Segment; import org.springframework.http.server.reactive.PathContainer.PathSegment;
import org.springframework.web.util.pattern.PathPattern.MatchingContext; import org.springframework.web.util.pattern.PathPattern.MatchingContext;
/** /**
@ -65,16 +65,16 @@ class SingleCharWildcardedPathElement extends PathElement {
} }
Element element = matchingContext.pathElements.get(pathIndex); Element element = matchingContext.pathElements.get(pathIndex);
if (!(element instanceof Segment)) { if (!(element instanceof PathSegment)) {
return false; return false;
} }
String value = ((Segment)element).valueToMatch(); String value = ((PathSegment)element).valueToMatch();
if (value.length() != len) { if (value.length() != len) {
// Not enough data to match this path element // Not enough data to match this path element
return false; return false;
} }
char[] data = ((Segment)element).valueToMatchAsChars(); char[] data = ((PathSegment)element).valueToMatchAsChars();
if (this.caseSensitive) { if (this.caseSensitive) {
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
char ch = this.text[i]; char ch = this.text[i];

View File

@ -16,8 +16,8 @@
package org.springframework.web.util.pattern; package org.springframework.web.util.pattern;
import org.springframework.http.server.reactive.PathContainer;
import org.springframework.http.server.reactive.PathContainer.Element; import org.springframework.http.server.reactive.PathContainer.Element;
import org.springframework.http.server.reactive.PathContainer.Segment;
import org.springframework.web.util.pattern.PathPattern.MatchingContext; import org.springframework.web.util.pattern.PathPattern.MatchingContext;
/** /**
@ -46,11 +46,11 @@ class WildcardPathElement extends PathElement {
// Assert if it exists it is a segment // Assert if it exists it is a segment
if (pathIndex < matchingContext.pathLength) { if (pathIndex < matchingContext.pathLength) {
Element element = matchingContext.pathElements.get(pathIndex); Element element = matchingContext.pathElements.get(pathIndex);
if (!(element instanceof Segment)) { if (!(element instanceof PathContainer.PathSegment)) {
// Should not match a separator // Should not match a separator
return false; return false;
} }
segmentData = ((Segment)element).valueToMatch(); segmentData = ((PathContainer.PathSegment)element).valueToMatch();
pathIndex++; pathIndex++;
} }

View File

@ -22,10 +22,10 @@ import java.util.stream.Collectors;
import org.junit.Test; import org.junit.Test;
import org.springframework.http.server.reactive.PathContainer.UrlPathSegment;
import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap; import org.springframework.util.MultiValueMap;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame; import static org.junit.Assert.assertSame;
@ -76,7 +76,7 @@ public class DefaultPathContainerTests {
private void testPathSegment(String rawValue, String valueToMatch, MultiValueMap<String, String> params) { private void testPathSegment(String rawValue, String valueToMatch, MultiValueMap<String, String> params) {
PathContainer container = DefaultPathContainer.parsePath(rawValue, UTF_8); PathContainer container = PathContainer.parseUrlPath(rawValue);
if ("".equals(rawValue)) { if ("".equals(rawValue)) {
assertEquals(0, container.elements().size()); assertEquals(0, container.elements().size());
@ -84,7 +84,7 @@ public class DefaultPathContainerTests {
} }
assertEquals(1, container.elements().size()); assertEquals(1, container.elements().size());
PathContainer.Segment segment = (PathContainer.Segment) container.elements().get(0); UrlPathSegment segment = (UrlPathSegment) container.elements().get(0);
assertEquals("value: '" + rawValue + "'", rawValue, segment.value()); assertEquals("value: '" + rawValue + "'", rawValue, segment.value());
assertEquals("valueToMatch: '" + rawValue + "'", valueToMatch, segment.valueToMatch()); assertEquals("valueToMatch: '" + rawValue + "'", valueToMatch, segment.valueToMatch());
@ -114,7 +114,7 @@ public class DefaultPathContainerTests {
private void testPath(String input, String value, List<String> expectedElements) { private void testPath(String input, String value, List<String> expectedElements) {
PathContainer path = PathContainer.parse(input, UTF_8); PathContainer path = PathContainer.parseUrlPath(input);
assertEquals("value: '" + input + "'", value, path.value()); assertEquals("value: '" + input + "'", value, path.value());
assertEquals("elements: " + input, expectedElements, path.elements().stream() assertEquals("elements: " + input, expectedElements, path.elements().stream()
@ -124,17 +124,17 @@ public class DefaultPathContainerTests {
@Test @Test
public void subPath() throws Exception { public void subPath() throws Exception {
// basic // basic
PathContainer path = PathContainer.parse("/a/b/c", UTF_8); PathContainer path = PathContainer.parseUrlPath("/a/b/c");
assertSame(path, path.subPath(0)); assertSame(path, path.subPath(0));
assertEquals("/b/c", path.subPath(2).value()); assertEquals("/b/c", path.subPath(2).value());
assertEquals("/c", path.subPath(4).value()); assertEquals("/c", path.subPath(4).value());
// root path // root path
path = PathContainer.parse("/", UTF_8); path = PathContainer.parseUrlPath("/");
assertEquals("/", path.subPath(0).value()); assertEquals("/", path.subPath(0).value());
// trailing slash // trailing slash
path = PathContainer.parse("/a/b/", UTF_8); path = PathContainer.parseUrlPath("/a/b/");
assertEquals("/b/", path.subPath(2).value()); assertEquals("/b/", path.subPath(2).value());
} }

View File

@ -16,7 +16,6 @@
package org.springframework.web.util.pattern; package org.springframework.web.util.pattern;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -587,7 +586,7 @@ public class PathPatternMatcherTests {
PathPatternParser ppp = new PathPatternParser(); PathPatternParser ppp = new PathPatternParser();
ppp.setMatchOptionalTrailingSlash(false); ppp.setMatchOptionalTrailingSlash(false);
PathPattern pp = ppp.parse("test"); PathPattern pp = ppp.parse("test");
assertFalse(pp.matchStart(PathContainer.parse("test/",StandardCharsets.UTF_8))); assertFalse(pp.matchStart(PathContainer.parsePath("test/")));
checkStartNoMatch("test/*/","test//"); checkStartNoMatch("test/*/","test//");
checkStartMatches("test/*","test/abc"); checkStartMatches("test/*","test/abc");
@ -1317,7 +1316,7 @@ public class PathPatternMatcherTests {
if (path == null) { if (path == null) {
return null; return null;
} }
return PathContainer.parse(path, StandardCharsets.UTF_8); return PathContainer.parseUrlPath(path);
} }
private void checkMatches(String uriTemplate, String path) { private void checkMatches(String uriTemplate, String path) {

View File

@ -19,9 +19,7 @@ package org.springframework.web.reactive.function.server;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
import java.net.URI; import java.net.URI;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Principal; import java.security.Principal;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map; import java.util.Map;
@ -80,7 +78,7 @@ public interface ServerRequest {
* Return the request path as {@code PathContainer}. * Return the request path as {@code PathContainer}.
*/ */
default PathContainer pathContainer() { default PathContainer pathContainer() {
return PathContainer.parse(path(), StandardCharsets.UTF_8); return PathContainer.parseUrlPath(path());
} }
/** /**

View File

@ -16,7 +16,6 @@
package org.springframework.web.reactive.resource; package org.springframework.web.reactive.resource;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
@ -174,7 +173,7 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
int queryIndex = getQueryIndex(requestUrl); int queryIndex = getQueryIndex(requestUrl);
String lookupPath = requestUrl.substring(0, queryIndex); String lookupPath = requestUrl.substring(0, queryIndex);
String query = requestUrl.substring(queryIndex); String query = requestUrl.substring(queryIndex);
PathContainer parsedLookupPath = PathContainer.parse(lookupPath, StandardCharsets.UTF_8); PathContainer parsedLookupPath = PathContainer.parseUrlPath(lookupPath);
return getForLookupPath(parsedLookupPath).map(resolvedPath -> return getForLookupPath(parsedLookupPath).map(resolvedPath ->
request.getPath().contextPath().value() + resolvedPath + query); request.getPath().contextPath().value() + resolvedPath + query);
} }

View File

@ -83,7 +83,7 @@ public class ResourceHandlerRegistryTests {
public void mapPathToLocation() throws Exception { public void mapPathToLocation() throws Exception {
MockServerWebExchange exchange = MockServerHttpRequest.get("").toExchange(); MockServerWebExchange exchange = MockServerHttpRequest.get("").toExchange();
exchange.getAttributes().put(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, exchange.getAttributes().put(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE,
PathContainer.parse("/testStylesheet.css", StandardCharsets.UTF_8)); PathContainer.parsePath("/testStylesheet.css"));
ResourceWebHandler handler = getHandler("/resources/**"); ResourceWebHandler handler = getHandler("/resources/**");
handler.handle(exchange).block(Duration.ofSeconds(5)); handler.handle(exchange).block(Duration.ofSeconds(5));

View File

@ -16,7 +16,6 @@
package org.springframework.web.reactive.resource; package org.springframework.web.reactive.resource;
import java.nio.charset.StandardCharsets;
import java.time.Duration; import java.time.Duration;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -75,7 +74,7 @@ public class ResourceUrlProviderTests {
@Test @Test
public void getStaticResourceUrl() { public void getStaticResourceUrl() {
PathContainer path = PathContainer.parse("/resources/foo.css", StandardCharsets.UTF_8); PathContainer path = PathContainer.parsePath("/resources/foo.css");
String url = this.urlProvider.getForLookupPath(path).block(Duration.ofSeconds(5)); String url = this.urlProvider.getForLookupPath(path).block(Duration.ofSeconds(5));
assertEquals("/resources/foo.css", url); assertEquals("/resources/foo.css", url);
} }
@ -105,7 +104,7 @@ public class ResourceUrlProviderTests {
resolvers.add(new PathResourceResolver()); resolvers.add(new PathResourceResolver());
this.handler.setResourceResolvers(resolvers); this.handler.setResourceResolvers(resolvers);
PathContainer path = PathContainer.parse("/resources/foo.css", StandardCharsets.UTF_8); PathContainer path = PathContainer.parsePath("/resources/foo.css");
String url = this.urlProvider.getForLookupPath(path).block(Duration.ofSeconds(5)); String url = this.urlProvider.getForLookupPath(path).block(Duration.ofSeconds(5));
assertEquals("/resources/foo-e36d2e05253c6c7085a91522ce43a0b4.css", url); assertEquals("/resources/foo-e36d2e05253c6c7085a91522ce43a0b4.css", url);
} }
@ -127,7 +126,7 @@ public class ResourceUrlProviderTests {
this.handlerMap.put("/resources/*.css", otherHandler); this.handlerMap.put("/resources/*.css", otherHandler);
this.urlProvider.registerHandlers(this.handlerMap); this.urlProvider.registerHandlers(this.handlerMap);
PathContainer path = PathContainer.parse("/resources/foo.css", StandardCharsets.UTF_8); PathContainer path = PathContainer.parsePath("/resources/foo.css");
String url = this.urlProvider.getForLookupPath(path).block(Duration.ofSeconds(5)); String url = this.urlProvider.getForLookupPath(path).block(Duration.ofSeconds(5));
assertEquals("/resources/foo-e36d2e05253c6c7085a91522ce43a0b4.css", url); assertEquals("/resources/foo-e36d2e05253c6c7085a91522ce43a0b4.css", url);
} }

View File

@ -553,7 +553,7 @@ public class ResourceWebHandlerTests {
private void setPathWithinHandlerMapping(ServerWebExchange exchange, String path) { private void setPathWithinHandlerMapping(ServerWebExchange exchange, String path) {
exchange.getAttributes().put(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, exchange.getAttributes().put(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE,
PathContainer.parse(path, StandardCharsets.UTF_8)); PathContainer.parsePath(path));
} }
private long resourceLastModified(String resourceName) throws IOException { private long resourceLastModified(String resourceName) throws IOException {