Merge PathContainer refactoring changes

This commit is contained in:
Rossen Stoyanchev
2017-07-11 10:58:45 +02:00
24 changed files with 236 additions and 205 deletions

View File

@@ -19,7 +19,6 @@ package org.springframework.mock.web.reactive.function.server;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.Arrays;
import java.util.Collections;
@@ -92,7 +91,7 @@ public class MockServerRequest implements ServerRequest {
this.method = method;
this.uri = uri;
this.pathContainer = RequestPath.create(uri, contextPath, StandardCharsets.UTF_8);
this.pathContainer = RequestPath.parse(uri, contextPath);
this.headers = headers;
this.cookies = cookies;
this.body = body;

View File

@@ -61,7 +61,7 @@ public abstract class AbstractServerHttpRequest implements ServerHttpRequest {
*/
public AbstractServerHttpRequest(URI uri, String contextPath, HttpHeaders headers) {
this.uri = uri;
this.path = new DefaultRequestPath(uri, contextPath, StandardCharsets.UTF_8);
this.path = RequestPath.parse(uri, contextPath);
this.headers = HttpHeaders.readOnlyHttpHeaders(headers);
}

View File

@@ -17,9 +17,11 @@
package org.springframework.http.server.reactive;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.lang.Nullable;
@@ -88,54 +90,65 @@ class DefaultPathContainer implements PathContainer {
}
static PathContainer parsePath(String path, Charset charset) {
static PathContainer createFromPath(String path, String separator) {
return parsePathInternal(path, separator, DefaultPathSegment::new);
}
private static PathContainer parsePathInternal(String path, String separator,
Function<String, PathSegment> segmentParser) {
if (path.equals("")) {
return EMPTY_PATH;
}
Separator separatorElement = separator.equals(SEPARATOR.value()) ? SEPARATOR : () -> separator;
List<Element> elements = new ArrayList<>();
int begin;
if (path.length() > 0 && path.charAt(0) == '/') {
begin = 1;
elements.add(SEPARATOR);
if (path.length() > 0 && path.startsWith(separator)) {
begin = separator.length();
elements.add(separatorElement);
}
else {
begin = 0;
}
while (begin < path.length()) {
int end = path.indexOf('/', begin);
int end = path.indexOf(separator, begin);
String segment = (end != -1 ? path.substring(begin, end) : path.substring(begin));
if (!segment.equals("")) {
elements.add(parsePathSegment(segment, charset));
elements.add(segmentParser.apply(segment));
}
if (end == -1) {
break;
}
elements.add(SEPARATOR);
begin = end + 1;
elements.add(separatorElement);
begin = end + separator.length();
}
return new DefaultPathContainer(path, elements);
}
private static PathContainer.Segment parsePathSegment(String input, Charset charset) {
int index = input.indexOf(';');
if (index == -1) {
String inputDecoded = StringUtils.uriDecode(input, charset);
return new DefaultPathSegment(input, inputDecoded, "", EMPTY_MAP);
}
String value = input.substring(0, index);
String valueDecoded = StringUtils.uriDecode(value, charset);
String semicolonContent = input.substring(index);
MultiValueMap<String, String> parameters = parseParams(semicolonContent, charset);
return new DefaultPathSegment(value, valueDecoded, semicolonContent, parameters);
static PathContainer createFromUrlPath(String path) {
return parsePathInternal(path, "/", segment -> {
Charset charset = StandardCharsets.UTF_8;
int index = segment.indexOf(';');
if (index == -1) {
String valueToMatch = StringUtils.uriDecode(segment, charset);
return new DefaultUrlPathSegment(segment, valueToMatch, EMPTY_MAP);
}
else {
String valueToMatch = StringUtils.uriDecode(segment.substring(0, index), charset);
String pathParameterContent = segment.substring(index);
MultiValueMap<String, String> parameters = parsePathParams(pathParameterContent, charset);
return new DefaultUrlPathSegment(segment, 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<>();
int begin = 1;
while (begin < input.length()) {
int end = input.indexOf(';', begin);
String param = (end != -1 ? input.substring(begin, end) : input.substring(begin));
parseParamValues(param, charset, result);
parsePathParamValues(param, charset, result);
if (end == -1) {
break;
}
@@ -144,7 +157,7 @@ class DefaultPathContainer implements PathContainer {
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)) {
int index = input.indexOf("=");
if (index != -1) {
@@ -185,52 +198,32 @@ class DefaultPathContainer implements PathContainer {
}
private static class DefaultPathSegment implements PathContainer.Segment {
private static class DefaultPathSegment implements PathSegment {
private final String value;
private final String valueDecoded;
private final char[] valueAsChars;
private final char[] valueDecodedChars;
private final String semicolonContent;
private final MultiValueMap<String, String> parameters;
DefaultPathSegment(String value, String valueDecoded, String semicolonContent,
MultiValueMap<String, String> params) {
Assert.isTrue(!value.contains("/"), () -> "Invalid path segment value: " + value);
DefaultPathSegment(String value) {
this.value = value;
this.valueDecoded = valueDecoded;
this.valueDecodedChars = valueDecoded.toCharArray();
this.semicolonContent = semicolonContent;
this.parameters = CollectionUtils.unmodifiableMultiValueMap(params);
this.valueAsChars = value.toCharArray();
}
@Override
public String value() {
return this.value;
}
@Override
public String valueDecoded() {
return this.valueDecoded;
public String valueToMatch() {
return this.value;
}
@Override
public char[] valueDecodedChars() {
return this.valueDecodedChars;
}
@Override
public String semicolonContent() {
return this.semicolonContent;
}
@Override
public MultiValueMap<String, String> parameters() {
return this.parameters;
public char[] valueToMatchAsChars() {
return this.valueAsChars;
}
@Override
@@ -241,25 +234,50 @@ class DefaultPathContainer implements PathContainer {
if (other == null || getClass() != other.getClass()) {
return false;
}
DefaultPathSegment segment = (DefaultPathSegment) other;
return (this.value.equals(segment.value) &&
this.semicolonContent.equals(segment.semicolonContent) &&
this.parameters.equals(segment.parameters));
return this.value.equals(((DefaultPathSegment) other).value);
}
@Override
public int hashCode() {
int result = this.value.hashCode();
result = 31 * result + this.semicolonContent.hashCode();
result = 31 * result + this.parameters.hashCode();
return result;
return this.value.hashCode();
}
public String toString() {
return "[value='" + this.value + "\', " +
"semicolonContent='" + this.semicolonContent + "\', " +
"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,8 +17,6 @@
package org.springframework.http.server.reactive;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.springframework.lang.Nullable;
@@ -40,8 +38,8 @@ class DefaultRequestPath implements RequestPath {
private final PathContainer pathWithinApplication;
DefaultRequestPath(URI uri, @Nullable String contextPath, Charset charset) {
this.fullPath = PathContainer.parse(uri.getRawPath(), charset);
DefaultRequestPath(URI uri, @Nullable String contextPath) {
this.fullPath = PathContainer.parseUrlPath(uri.getRawPath());
this.contextPath = initContextPath(this.fullPath, contextPath);
this.pathWithinApplication = extractPathWithinApplication(this.fullPath, this.contextPath);
}
@@ -54,7 +52,7 @@ class DefaultRequestPath implements RequestPath {
private static PathContainer initContextPath(PathContainer path, @Nullable String contextPath) {
if (!StringUtils.hasText(contextPath) || "/".equals(contextPath)) {
return PathContainer.parse("", StandardCharsets.UTF_8);
return PathContainer.parseUrlPath("");
}
Assert.isTrue(contextPath.startsWith("/") && !contextPath.endsWith("/") &&
@@ -66,11 +64,8 @@ class DefaultRequestPath implements RequestPath {
for (int i=0; i < path.elements().size(); i++) {
PathContainer.Element element = path.elements().get(i);
counter += element.value().length();
if (element instanceof PathContainer.Segment) {
counter += ((Segment) element).semicolonContent().length();
}
if (length == counter) {
return DefaultPathContainer.subPath(path, 0, i + 1);
return path.subPath(0, i + 1);
}
}
@@ -80,13 +75,12 @@ class DefaultRequestPath implements RequestPath {
}
private static PathContainer extractPathWithinApplication(PathContainer fullPath, PathContainer contextPath) {
return PathContainer.subPath(fullPath, contextPath.elements().size());
return fullPath.subPath(contextPath.elements().size());
}
// PathContainer methods..
@Override
public String value() {
return this.fullPath.value();

View File

@@ -18,7 +18,6 @@ package org.springframework.http.server.reactive;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
@@ -107,14 +106,14 @@ class DefaultServerHttpRequestBuilder implements ServerHttpRequest.Builder {
@Nullable
private RequestPath getRequestPathToUse(@Nullable URI uriToUse) {
if (uriToUse == null) {
if (this.contextPath == null) {
return null;
}
if (uriToUse == null && this.contextPath == null) {
return null;
}
else if (uriToUse == null) {
return new DefaultRequestPath(this.delegate.getPath(), this.contextPath);
}
else {
return new DefaultRequestPath(uriToUse, this.contextPath, StandardCharsets.UTF_8);
return RequestPath.parse(uriToUse, this.contextPath);
}
}

View File

@@ -16,66 +16,94 @@
package org.springframework.http.server.reactive;
import java.nio.charset.Charset;
import java.util.List;
import org.springframework.util.MultiValueMap;
/**
* Structured path representation.
* Structured representation of a path whose elements are parsed into a sequence
* of {@link Separator Separator} and {@link PathSegment PathSegment} elements.
*
* <p>Typically consumed via {@link ServerHttpRequest#getPath()} but can also
* be created by parsing a path value via {@link #parse(String, Charset)}.
* <p>An instance of this class can be created via {@link #parsePath(String)} or
* {@link #parseUrlPath(String)}. For an HTTP request the path can be
* accessed via {@link ServerHttpRequest#getPath()}.
*
* <p>For a URL path each {@link UrlPathSegment UrlPathSegment} exposes its
* structure decoded safely without the risk of encoded reserved characters
* altering the path or segment structure and without path parameters for
* path matching purposes.
*
* @author Rossen Stoyanchev
* @since 5.0
*/
public interface PathContainer {
/**
* The original, raw (encoded) path value including path parameters.
* The original path that was parsed.
*/
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();
/**
* Parse the given path value into a {@link PathContainer}.
* @param path the encoded, raw path value to parse
* @param encoding the charset to use for decoded path segment values
* @return the parsed path
*/
static PathContainer parse(String path, Charset encoding) {
return DefaultPathContainer.parsePath(path, encoding);
}
/**
* Extract a sub-path from the given offset into the path elements list.
* @param path the path to extract from
* Extract a sub-path from the given offset into the elements list.
* @param index the start element index (inclusive)
* @return the sub-path
*/
static PathContainer subPath(PathContainer path, int index) {
return subPath(path, index, path.elements().size());
default PathContainer subPath(int index) {
return subPath(index, elements().size());
}
/**
* Extract a sub-path from the given start offset (inclusive) into the path
* Extract a sub-path from the given start offset (inclusive) into the
* element list and to the end offset (exclusive).
* @param path the path to extract from
* @param startIndex the start element index (inclusive)
* @param endIndex the end element index (exclusive)
* @return the sub-path
*/
static PathContainer subPath(PathContainer path, int startIndex, int endIndex) {
return DefaultPathContainer.subPath(path, startIndex, endIndex);
default PathContainer subPath(int startIndex, int endIndex) {
return DefaultPathContainer.subPath(this, startIndex, endIndex);
}
/**
* Parse the path value into a sequence of {@link Separator Separator} and
* {@link PathSegment PathSegment} elements.
* @param path the path value to parse
* @return the parsed path
*/
static PathContainer parsePath(String path) {
return parsePath(path, "/");
}
/**
* Parse the path value into a sequence of {@link Separator Separator} and
* {@link PathSegment PathSegment} elements.
* @param path the path value to parse
* @param separator the value to treat as separator
* @return the parsed path
*/
static PathContainer parsePath(String path, String separator) {
return DefaultPathContainer.createFromPath(path, separator);
}
/**
* 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);
}
/**
* Common representation of a path element, e.g. separator or segment.
*/
interface Element {
/**
@@ -86,33 +114,36 @@ public interface PathContainer {
/**
* A path separator element.
* Path separator element.
*/
interface Separator extends Element {
}
/**
* A path segment element.
* Path segment element.
*/
interface Segment extends Element {
interface PathSegment extends Element {
/**
* Return the path segment {@link #value()} decoded.
* Return the path segment value to use for pattern matching purposes.
* By default this is the same as {@link #value()} but may also differ
* in sub-interfaces (e.g. decoded, sanitized, etc.).
*/
String valueDecoded();
String valueToMatch();
/**
* Variant of {@link #valueDecoded()} as a {@code char[]}.
* The same as {@link #valueToMatch()} but as a {@code char[]}.
*/
char[] valueDecodedChars();
char[] valueToMatchAsChars();
}
/**
* Return the portion of the path segment after and including the first
* ";" (semicolon) representing path parameters. The actual parsed
* parameters if any can be obtained via {@link #parameters()}.
*/
String semicolonContent();
/**
* 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.

View File

@@ -16,7 +16,8 @@
package org.springframework.http.server.reactive;
import java.net.URI;
import java.nio.charset.Charset;
import org.springframework.lang.Nullable;
/**
* Represents the complete path for a request.
@@ -41,11 +42,12 @@ public interface RequestPath extends PathContainer {
*/
PathContainer pathWithinApplication();
/**
* Create a new {@code RequestPath} with the given parameters.
*/
static RequestPath create(URI uri, String contextPath, Charset charset) {
return new DefaultRequestPath(uri, contextPath, charset);
static RequestPath parse(URI uri, @Nullable String contextPath) {
return new DefaultRequestPath(uri, contextPath);
}
}

View File

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

View File

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

View File

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

View File

@@ -16,7 +16,6 @@
package org.springframework.web.util.pattern;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
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.Element;
import org.springframework.http.server.reactive.PathContainer.Segment;
import org.springframework.http.server.reactive.PathContainer.Separator;
import org.springframework.lang.Nullable;
import org.springframework.util.CollectionUtils;
@@ -71,7 +69,7 @@ import org.springframework.util.StringUtils;
*/
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 */
private final PathPatternParser parser;
@@ -205,7 +203,7 @@ public class PathPattern implements Comparable<PathPattern> {
info = new PathRemainingMatchInfo(EMPTY_PATH, matchingContext.getPathMatchResult());
}
else {
info = new PathRemainingMatchInfo(PathContainer.subPath(pathContainer, matchingContext.remainingPathIndex),
info = new PathRemainingMatchInfo(pathContainer.subPath(matchingContext.remainingPathIndex),
matchingContext.getPathMatchResult());
}
return info;
@@ -261,12 +259,11 @@ public class PathPattern implements Comparable<PathPattern> {
* @return the subset of the path that is matched by pattern or "" if none of it is matched by pattern elements
*/
public PathContainer extractPathWithinPattern(PathContainer path) {
// TODO: implement extractPathWithinPattern for PathContainer
String result = extractPathWithinPattern(path.value());
return PathContainer.parse(result, StandardCharsets.UTF_8);
return PathContainer.parseUrlPath(result);
}
// TODO: implement extractPathWithinPattern natively for PathContainer
private String extractPathWithinPattern(String path) {
// assert this.matches(path)
PathElement elem = head;
@@ -406,7 +403,7 @@ public class PathPattern implements Comparable<PathPattern> {
// /usr + /user => /usr/user
// /{foo} + /bar => /{foo}/bar
if (!this.patternString.equals(pattern2string.patternString) && this.capturedVariableCount == 0 &&
matches(PathContainer.parse(pattern2string.patternString, StandardCharsets.UTF_8))) {
matches(PathContainer.parseUrlPath(pattern2string.patternString))) {
return pattern2string;
}
@@ -683,8 +680,8 @@ public class PathPattern implements Comparable<PathPattern> {
*/
String pathElementValue(int pathIndex) {
Element element = (pathIndex < pathLength) ? pathElements.get(pathIndex) : null;
if (element instanceof Segment) {
return ((Segment)element).valueDecoded();
if (element instanceof PathContainer.PathSegment) {
return ((PathContainer.PathSegment)element).valueToMatch();
}
return "";
}

View File

@@ -21,7 +21,7 @@ import java.util.List;
import java.util.regex.Matcher;
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;
/**
@@ -173,7 +173,7 @@ class RegexPathElement extends PathElement {
String value = matcher.group(i);
matchingContext.set(name, value,
(i == this.variableNames.size())?
((Segment)matchingContext.pathElements.get(pathIndex)).parameters():
((UrlPathSegment)matchingContext.pathElements.get(pathIndex)).parameters():
NO_PARAMETERS);
}
}

View File

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

View File

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

View File

@@ -22,10 +22,10 @@ import java.util.stream.Collectors;
import org.junit.Test;
import org.springframework.http.server.reactive.PathContainer.UrlPathSegment;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
@@ -38,14 +38,14 @@ public class DefaultPathContainerTests {
@Test
public void pathSegment() throws Exception {
// basic
testPathSegment("cars", "", "cars", "cars", new LinkedMultiValueMap<>());
testPathSegment("cars", "cars", new LinkedMultiValueMap<>());
// empty
testPathSegment("", "", "", "", new LinkedMultiValueMap<>());
testPathSegment("", "", new LinkedMultiValueMap<>());
// spaces
testPathSegment("%20%20", "", "%20%20", " ", new LinkedMultiValueMap<>());
testPathSegment("%20a%20", "", "%20a%20", " a ", new LinkedMultiValueMap<>());
testPathSegment("%20%20", " ", new LinkedMultiValueMap<>());
testPathSegment("%20a%20", " a ", new LinkedMultiValueMap<>());
}
@Test
@@ -56,40 +56,38 @@ public class DefaultPathContainerTests {
params.add("colors", "blue");
params.add("colors", "green");
params.add("year", "2012");
testPathSegment("cars", ";colors=red,blue,green;year=2012", "cars", "cars", params);
testPathSegment("cars;colors=red,blue,green;year=2012", "cars", params);
// trailing semicolon
params = new LinkedMultiValueMap<>();
params.add("p", "1");
testPathSegment("path", ";p=1;", "path", "path", params);
testPathSegment("path;p=1;", "path", params);
// params with spaces
params = new LinkedMultiValueMap<>();
params.add("param name", "param value");
testPathSegment("path", ";param%20name=param%20value;%20", "path", "path", params);
testPathSegment("path;param%20name=param%20value;%20", "path", params);
// empty params
params = new LinkedMultiValueMap<>();
params.add("p", "1");
testPathSegment("path", ";;;%20;%20;p=1;%20", "path", "path", params);
testPathSegment("path;;;%20;%20;p=1;%20", "path", params);
}
private void testPathSegment(String rawValue, String semicolonContent,
String value, String valueDecoded, MultiValueMap<String, String> params) {
private void testPathSegment(String rawValue, String valueToMatch, MultiValueMap<String, String> params) {
PathContainer container = DefaultPathContainer.parsePath(rawValue + semicolonContent, UTF_8);
PathContainer container = PathContainer.parseUrlPath(rawValue);
if ("".equals(value)) {
if ("".equals(rawValue)) {
assertEquals(0, container.elements().size());
return;
}
assertEquals(1, container.elements().size());
PathContainer.Segment segment = (PathContainer.Segment) container.elements().get(0);
UrlPathSegment segment = (UrlPathSegment) container.elements().get(0);
assertEquals("value: '" + rawValue + "'", value, segment.value());
assertEquals("valueDecoded: '" + rawValue + "'", valueDecoded, segment.valueDecoded());
assertEquals("semicolonContent: '" + rawValue + "'", semicolonContent, segment.semicolonContent());
assertEquals("value: '" + rawValue + "'", rawValue, segment.value());
assertEquals("valueToMatch: '" + rawValue + "'", valueToMatch, segment.valueToMatch());
assertEquals("params: '" + rawValue + "'", params, segment.parameters());
}
@@ -116,7 +114,7 @@ public class DefaultPathContainerTests {
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("elements: " + input, expectedElements, path.elements().stream()
@@ -126,18 +124,18 @@ public class DefaultPathContainerTests {
@Test
public void subPath() throws Exception {
// basic
PathContainer path = PathContainer.parse("/a/b/c", UTF_8);
assertSame(path, PathContainer.subPath(path, 0));
assertEquals("/b/c", PathContainer.subPath(path, 2).value());
assertEquals("/c", PathContainer.subPath(path, 4).value());
PathContainer path = PathContainer.parseUrlPath("/a/b/c");
assertSame(path, path.subPath(0));
assertEquals("/b/c", path.subPath(2).value());
assertEquals("/c", path.subPath(4).value());
// root path
path = PathContainer.parse("/", UTF_8);
assertEquals("/", PathContainer.subPath(path, 0).value());
path = PathContainer.parseUrlPath("/");
assertEquals("/", path.subPath(0).value());
// trailing slash
path = PathContainer.parse("/a/b/", UTF_8);
assertEquals("/b/", PathContainer.subPath(path, 2).value());
path = PathContainer.parseUrlPath("/a/b/");
assertEquals("/b/", path.subPath(2).value());
}
}

View File

@@ -19,7 +19,6 @@ import java.net.URI;
import org.junit.Test;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.junit.Assert.assertEquals;
/**
@@ -54,7 +53,7 @@ public class DefaultRequestPathTests {
private void testRequestPath(String fullPath, String contextPath, String pathWithinApplication) {
URI uri = URI.create("http://localhost:8080" + fullPath);
RequestPath requestPath = new DefaultRequestPath(uri, contextPath, UTF_8);
RequestPath requestPath = RequestPath.parse(uri, contextPath);
assertEquals(contextPath.equals("/") ? "" : contextPath, requestPath.contextPath().value());
assertEquals(pathWithinApplication, requestPath.pathWithinApplication().value());

View File

@@ -16,7 +16,6 @@
package org.springframework.web.util.pattern;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -587,7 +586,7 @@ public class PathPatternMatcherTests {
PathPatternParser ppp = new PathPatternParser();
ppp.setMatchOptionalTrailingSlash(false);
PathPattern pp = ppp.parse("test");
assertFalse(pp.matchStart(PathContainer.parse("test/",StandardCharsets.UTF_8)));
assertFalse(pp.matchStart(PathContainer.parsePath("test/")));
checkStartNoMatch("test/*/","test//");
checkStartMatches("test/*","test/abc");
@@ -1317,7 +1316,7 @@ public class PathPatternMatcherTests {
if (path == null) {
return null;
}
return PathContainer.parse(path, StandardCharsets.UTF_8);
return PathContainer.parseUrlPath(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.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -80,7 +78,7 @@ public interface ServerRequest {
* Return the request path as {@code 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;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -174,7 +173,7 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
int queryIndex = getQueryIndex(requestUrl);
String lookupPath = requestUrl.substring(0, queryIndex);
String query = requestUrl.substring(queryIndex);
PathContainer parsedLookupPath = PathContainer.parse(lookupPath, StandardCharsets.UTF_8);
PathContainer parsedLookupPath = PathContainer.parseUrlPath(lookupPath);
return getForLookupPath(parsedLookupPath).map(resolvedPath ->
request.getPath().contextPath().value() + resolvedPath + query);
}
@@ -215,7 +214,7 @@ public class ResourceUrlProvider implements ApplicationListener<ContextRefreshed
.map(entry -> {
PathContainer path = entry.getKey().extractPathWithinPattern(lookupPath);
int endIndex = lookupPath.elements().size() - path.elements().size();
PathContainer mapping = PathContainer.subPath(lookupPath, 0, endIndex);
PathContainer mapping = lookupPath.subPath(0, endIndex);
if (logger.isTraceEnabled()) {
logger.trace("Invoking ResourceResolverChain for URL pattern " +
"\"" + entry.getKey() + "\"");

View File

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

View File

@@ -19,7 +19,6 @@ package org.springframework.web.reactive.function.server;
import java.net.InetSocketAddress;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Principal;
import java.util.Arrays;
import java.util.Collections;
@@ -91,7 +90,7 @@ public class MockServerRequest implements ServerRequest {
this.method = method;
this.uri = uri;
this.pathContainer = RequestPath.create(uri, contextPath, StandardCharsets.UTF_8);
this.pathContainer = RequestPath.parse(uri, contextPath);
this.headers = headers;
this.cookies = cookies;
this.body = body;

View File

@@ -16,7 +16,6 @@
package org.springframework.web.reactive.resource;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.util.ArrayList;
import java.util.HashMap;
@@ -75,7 +74,7 @@ public class ResourceUrlProviderTests {
@Test
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));
assertEquals("/resources/foo.css", url);
}
@@ -105,7 +104,7 @@ public class ResourceUrlProviderTests {
resolvers.add(new PathResourceResolver());
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));
assertEquals("/resources/foo-e36d2e05253c6c7085a91522ce43a0b4.css", url);
}
@@ -127,7 +126,7 @@ public class ResourceUrlProviderTests {
this.handlerMap.put("/resources/*.css", otherHandler);
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));
assertEquals("/resources/foo-e36d2e05253c6c7085a91522ce43a0b4.css", url);
}

View File

@@ -553,7 +553,7 @@ public class ResourceWebHandlerTests {
private void setPathWithinHandlerMapping(ServerWebExchange exchange, String path) {
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 {