Skip to content

Commit f813a63

Browse files
committed
PathContainer parses URL paths only
Collapse non-URL vs URL based path parsing into one essentially supporting URL paths only.
1 parent af83d23 commit f813a63

File tree

12 files changed

+70
-129
lines changed

12 files changed

+70
-129
lines changed

spring-web/src/main/java/org/springframework/http/server/DefaultPathContainer.java

Lines changed: 35 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import java.util.ArrayList;
2222
import java.util.Collections;
2323
import java.util.List;
24-
import java.util.function.Function;
2524
import java.util.stream.Collectors;
2625

2726
import org.springframework.lang.Nullable;
@@ -90,16 +89,11 @@ public String toString() {
9089
}
9190

9291

93-
static PathContainer createFromPath(String path, String separator) {
94-
return parsePathInternal(path, separator, DefaultPathSegment::new);
95-
}
96-
97-
private static PathContainer parsePathInternal(String path, String separator,
98-
Function<String, PathSegment> segmentParser) {
99-
92+
static PathContainer createFromUrlPath(String path) {
10093
if (path.equals("")) {
10194
return EMPTY_PATH;
10295
}
96+
String separator = "/";
10397
Separator separatorElement = separator.equals(SEPARATOR.value()) ? SEPARATOR : () -> separator;
10498
List<Element> elements = new ArrayList<>();
10599
int begin;
@@ -114,7 +108,7 @@ private static PathContainer parsePathInternal(String path, String separator,
114108
int end = path.indexOf(separator, begin);
115109
String segment = (end != -1 ? path.substring(begin, end) : path.substring(begin));
116110
if (!segment.equals("")) {
117-
elements.add(segmentParser.apply(segment));
111+
elements.add(parsePathSegment(segment));
118112
}
119113
if (end == -1) {
120114
break;
@@ -125,21 +119,19 @@ private static PathContainer parsePathInternal(String path, String separator,
125119
return new DefaultPathContainer(path, elements);
126120
}
127121

128-
static PathContainer createFromUrlPath(String path) {
129-
return parsePathInternal(path, "/", segment -> {
130-
Charset charset = StandardCharsets.UTF_8;
131-
int index = segment.indexOf(';');
132-
if (index == -1) {
133-
String valueToMatch = StringUtils.uriDecode(segment, charset);
134-
return new DefaultUrlPathSegment(segment, valueToMatch, EMPTY_MAP);
135-
}
136-
else {
137-
String valueToMatch = StringUtils.uriDecode(segment.substring(0, index), charset);
138-
String pathParameterContent = segment.substring(index);
139-
MultiValueMap<String, String> parameters = parsePathParams(pathParameterContent, charset);
140-
return new DefaultUrlPathSegment(segment, valueToMatch, parameters);
141-
}
142-
});
122+
private static PathSegment parsePathSegment(String segment) {
123+
Charset charset = StandardCharsets.UTF_8;
124+
int index = segment.indexOf(';');
125+
if (index == -1) {
126+
String valueToMatch = StringUtils.uriDecode(segment, charset);
127+
return new DefaultPathSegment(segment, valueToMatch, EMPTY_MAP);
128+
}
129+
else {
130+
String valueToMatch = StringUtils.uriDecode(segment.substring(0, index), charset);
131+
String pathParameterContent = segment.substring(index);
132+
MultiValueMap<String, String> parameters = parsePathParams(pathParameterContent, charset);
133+
return new DefaultPathSegment(segment, valueToMatch, parameters);
134+
}
143135
}
144136

145137
private static MultiValueMap<String, String> parsePathParams(String input, Charset charset) {
@@ -204,10 +196,20 @@ private static class DefaultPathSegment implements PathSegment {
204196

205197
private final char[] valueAsChars;
206198

199+
private final String valueToMatch;
200+
201+
private final char[] valueToMatchAsChars;
202+
203+
private final MultiValueMap<String, String> parameters;
207204

208-
DefaultPathSegment(String value) {
205+
206+
DefaultPathSegment(String value, String valueToMatch, MultiValueMap<String, String> params) {
207+
Assert.isTrue(!value.contains("/"), () -> "Invalid path segment value: " + value);
209208
this.value = value;
210209
this.valueAsChars = value.toCharArray();
210+
this.valueToMatch = valueToMatch;
211+
this.valueToMatchAsChars = valueToMatch.toCharArray();
212+
this.parameters = CollectionUtils.unmodifiableMultiValueMap(params);
211213
}
212214

213215

@@ -218,14 +220,20 @@ public String value() {
218220

219221
@Override
220222
public String valueToMatch() {
221-
return this.value;
223+
return this.valueToMatch;
222224
}
223225

224226
@Override
225227
public char[] valueToMatchAsChars() {
226-
return this.valueAsChars;
228+
return this.valueToMatchAsChars;
227229
}
228230

231+
@Override
232+
public MultiValueMap<String, String> parameters() {
233+
return this.parameters;
234+
}
235+
236+
229237
@Override
230238
public boolean equals(@Nullable Object other) {
231239
if (this == other) {
@@ -246,40 +254,5 @@ public String toString() {
246254
return "[value='" + this.value + "']"; }
247255
}
248256

249-
250-
private static class DefaultUrlPathSegment extends DefaultPathSegment implements UrlPathSegment {
251-
252-
private final String valueToMatch;
253-
254-
private final char[] valueToMatchAsChars;
255-
256-
private final MultiValueMap<String, String> parameters;
257-
258-
259-
DefaultUrlPathSegment(String value, String valueToMatch, MultiValueMap<String, String> params) {
260-
super(value);
261-
Assert.isTrue(!value.contains("/"), () -> "Invalid path segment value: " + value);
262-
this.valueToMatch = valueToMatch;
263-
this.valueToMatchAsChars = valueToMatch.toCharArray();
264-
this.parameters = CollectionUtils.unmodifiableMultiValueMap(params);
265-
}
266-
267-
268-
@Override
269-
public String valueToMatch() {
270-
return this.valueToMatch;
271-
}
272-
273-
@Override
274-
public char[] valueToMatchAsChars() {
275-
return this.valueToMatchAsChars;
276-
}
277-
278-
@Override
279-
public MultiValueMap<String, String> parameters() {
280-
return this.parameters;
281-
}
282-
}
283-
284257
}
285258

spring-web/src/main/java/org/springframework/http/server/DefaultRequestPath.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020
import java.util.List;
2121

2222
import org.springframework.lang.Nullable;
23-
import org.springframework.util.Assert;
2423
import org.springframework.util.StringUtils;
2524

2625
/**
@@ -39,7 +38,7 @@ class DefaultRequestPath implements RequestPath {
3938

4039

4140
DefaultRequestPath(URI uri, @Nullable String contextPath) {
42-
this.fullPath = PathContainer.parseUrlPath(uri.getRawPath());
41+
this.fullPath = PathContainer.parsePath(uri.getRawPath());
4342
this.contextPath = initContextPath(this.fullPath, contextPath);
4443
this.pathWithinApplication = extractPathWithinApplication(this.fullPath, this.contextPath);
4544
}
@@ -52,7 +51,7 @@ private DefaultRequestPath(RequestPath requestPath, String contextPath) {
5251

5352
private static PathContainer initContextPath(PathContainer path, @Nullable String contextPath) {
5453
if (!StringUtils.hasText(contextPath) || "/".equals(contextPath)) {
55-
return PathContainer.parseUrlPath("");
54+
return PathContainer.parsePath("");
5655
}
5756

5857
validateContextPath(path.value(), contextPath);

spring-web/src/main/java/org/springframework/http/server/PathContainer.java

Lines changed: 8 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,14 @@
2121
import org.springframework.util.MultiValueMap;
2222

2323
/**
24-
* Structured representation of a path whose elements are parsed into a sequence
25-
* of {@link Separator Separator} and {@link PathSegment PathSegment} elements.
24+
* Structured representation of a URI path whose elements have been pre-parsed
25+
* into a sequence of {@link Separator Separator} and {@link PathSegment
26+
* PathSegment} elements.
2627
*
27-
* <p>An instance of this class can be created via {@link #parsePath(String)} or
28-
* {@link #parseUrlPath(String)}.
29-
*
30-
* <p>For a URL path each {@link UrlPathSegment UrlPathSegment} exposes its
31-
* structure decoded safely without the risk of encoded reserved characters
32-
* altering the path or segment structure and without path parameters for
33-
* path matching purposes.
28+
* <p>An instance of this class can be created via {@link #parsePath(String)}.
29+
* Each {@link PathSegment PathSegment} exposes its structure decoded
30+
* safely without the risk of encoded reserved characters altering the path or
31+
* segment structure and without path parameters for path matching purposes.
3432
*
3533
* @author Rossen Stoyanchev
3634
* @since 5.0
@@ -71,31 +69,10 @@ default PathContainer subPath(int startIndex, int endIndex) {
7169
/**
7270
* Parse the path value into a sequence of {@link Separator Separator} and
7371
* {@link PathSegment PathSegment} elements.
74-
* @param path the path value to parse
75-
* @return the parsed path
76-
*/
77-
static PathContainer parsePath(String path) {
78-
return parsePath(path, "/");
79-
}
80-
81-
/**
82-
* Parse the path value into a sequence of {@link Separator Separator} and
83-
* {@link PathSegment PathSegment} elements.
84-
* @param path the path value to parse
85-
* @param separator the value to treat as separator
86-
* @return the parsed path
87-
*/
88-
static PathContainer parsePath(String path, String separator) {
89-
return DefaultPathContainer.createFromPath(path, separator);
90-
}
91-
92-
/**
93-
* Parse the path value into a sequence of {@link Separator Separator} and
94-
* {@link UrlPathSegment UrlPathSegment} elements.
9572
* @param path the encoded, raw URL path value to parse
9673
* @return the parsed path
9774
*/
98-
static PathContainer parseUrlPath(String path) {
75+
static PathContainer parsePath(String path) {
9976
return DefaultPathContainer.createFromUrlPath(path);
10077
}
10178

@@ -135,14 +112,6 @@ interface PathSegment extends Element {
135112
* The same as {@link #valueToMatch()} but as a {@code char[]}.
136113
*/
137114
char[] valueToMatchAsChars();
138-
}
139-
140-
141-
/**
142-
* Specialization of {@link PathSegment} for a URL path.
143-
* The {@link #valueToMatch()} is decoded and without path parameters.
144-
*/
145-
interface UrlPathSegment extends PathSegment {
146115

147116
/**
148117
* Path parameters parsed from the path segment.

spring-web/src/main/java/org/springframework/web/util/pattern/CaptureTheRestPathElement.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import java.util.List;
2020

2121
import org.springframework.http.server.PathContainer.Element;
22-
import org.springframework.http.server.PathContainer.UrlPathSegment;
22+
import org.springframework.http.server.PathContainer.PathSegment;
2323
import org.springframework.util.LinkedMultiValueMap;
2424
import org.springframework.util.MultiValueMap;
2525
import org.springframework.web.util.pattern.PathPattern.MatchingContext;
@@ -65,8 +65,8 @@ public boolean matches(int pathIndex, MatchingContext matchingContext) {
6565
MultiValueMap<String,String> parametersCollector = null;
6666
for (int i = pathIndex; i < matchingContext.pathLength; i++) {
6767
Element element = matchingContext.pathElements.get(i);
68-
if (element instanceof UrlPathSegment) {
69-
MultiValueMap<String, String> parameters = ((UrlPathSegment) element).parameters();
68+
if (element instanceof PathSegment) {
69+
MultiValueMap<String, String> parameters = ((PathSegment) element).parameters();
7070
if (!parameters.isEmpty()) {
7171
if (parametersCollector == null) {
7272
parametersCollector = new LinkedMultiValueMap<>();
@@ -85,8 +85,8 @@ private String pathToString(int fromSegment, List<Element> pathElements) {
8585
StringBuilder buf = new StringBuilder();
8686
for (int i = fromSegment, max = pathElements.size(); i < max; i++) {
8787
Element element = pathElements.get(i);
88-
if (element instanceof UrlPathSegment) {
89-
buf.append(((UrlPathSegment)element).valueToMatch());
88+
if (element instanceof PathSegment) {
89+
buf.append(((PathSegment)element).valueToMatch());
9090
}
9191
else {
9292
buf.append(element.value());

spring-web/src/main/java/org/springframework/web/util/pattern/CaptureVariablePathElement.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import java.util.regex.Matcher;
2020
import java.util.regex.Pattern;
2121

22-
import org.springframework.http.server.PathContainer.UrlPathSegment;
22+
import org.springframework.http.server.PathContainer.PathSegment;
2323
import org.springframework.lang.Nullable;
2424

2525
/**
@@ -120,7 +120,7 @@ else if (this.next != null) {
120120
}
121121

122122
if (match && matchingContext.extractingVariables) {
123-
matchingContext.set(this.variableName, candidateCapture, ((UrlPathSegment)matchingContext.pathElements.get(pathIndex-1)).parameters());
123+
matchingContext.set(this.variableName, candidateCapture, ((PathSegment)matchingContext.pathElements.get(pathIndex-1)).parameters());
124124
}
125125
return match;
126126
}

spring-web/src/main/java/org/springframework/web/util/pattern/ParsingPathMatcher.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,26 +60,26 @@ public boolean isPattern(String path) {
6060
@Override
6161
public boolean match(String pattern, String path) {
6262
PathPattern pathPattern = getPathPattern(pattern);
63-
return pathPattern.matches(PathContainer.parseUrlPath(path));
63+
return pathPattern.matches(PathContainer.parsePath(path));
6464
}
6565

6666
@Override
6767
public boolean matchStart(String pattern, String path) {
6868
PathPattern pathPattern = getPathPattern(pattern);
69-
return pathPattern.matchStart(PathContainer.parseUrlPath(path));
69+
return pathPattern.matchStart(PathContainer.parsePath(path));
7070
}
7171

7272
@Override
7373
public String extractPathWithinPattern(String pattern, String path) {
7474
PathPattern pathPattern = getPathPattern(pattern);
75-
PathContainer pathContainer = PathContainer.parseUrlPath(path);
75+
PathContainer pathContainer = PathContainer.parsePath(path);
7676
return pathPattern.extractPathWithinPattern(pathContainer).value();
7777
}
7878

7979
@Override
8080
public Map<String, String> extractUriTemplateVariables(String pattern, String path) {
8181
PathPattern pathPattern = getPathPattern(pattern);
82-
PathContainer pathContainer = PathContainer.parseUrlPath(path);
82+
PathContainer pathContainer = PathContainer.parsePath(path);
8383
PathMatchResult results = pathPattern.matchAndExtract(pathContainer);
8484
// Collapse PathMatchResults to simple value results
8585
// TODO: (path parameters are lost in this translation)

spring-web/src/main/java/org/springframework/web/util/pattern/PathPattern.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ else if (!hasLength(pathContainer)) {
261261
public PathContainer extractPathWithinPattern(PathContainer path) {
262262
// TODO: implement extractPathWithinPattern for PathContainer
263263
String result = extractPathWithinPattern(path.value());
264-
return PathContainer.parseUrlPath(result);
264+
return PathContainer.parsePath(result);
265265
}
266266

267267
private String extractPathWithinPattern(String path) {
@@ -403,7 +403,7 @@ else if (!StringUtils.hasLength(pattern2string.patternString)) {
403403
// /usr + /user => /usr/user
404404
// /{foo} + /bar => /{foo}/bar
405405
if (!this.patternString.equals(pattern2string.patternString) && this.capturedVariableCount == 0 &&
406-
matches(PathContainer.parseUrlPath(pattern2string.patternString))) {
406+
matches(PathContainer.parsePath(pattern2string.patternString))) {
407407
return pattern2string;
408408
}
409409

spring-web/src/main/java/org/springframework/web/util/pattern/RegexPathElement.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
import java.util.regex.Matcher;
2222
import java.util.regex.Pattern;
2323

24-
import org.springframework.http.server.PathContainer.UrlPathSegment;
24+
import org.springframework.http.server.PathContainer.PathSegment;
2525
import org.springframework.web.util.pattern.PathPattern.MatchingContext;
2626

2727
/**
@@ -173,7 +173,7 @@ public boolean matches(int pathIndex, MatchingContext matchingContext) {
173173
String value = matcher.group(i);
174174
matchingContext.set(name, value,
175175
(i == this.variableNames.size())?
176-
((UrlPathSegment)matchingContext.pathElements.get(pathIndex)).parameters():
176+
((PathSegment)matchingContext.pathElements.get(pathIndex)).parameters():
177177
NO_PARAMETERS);
178178
}
179179
}

0 commit comments

Comments
 (0)