Skip to content

Commit 6ad8d6e

Browse files
committed
Optimize path matching in UrlHandlerFilter
See gh-32830
1 parent 80d1d50 commit 6ad8d6e

File tree

1 file changed

+24
-21
lines changed

1 file changed

+24
-21
lines changed

spring-web/src/main/java/org/springframework/web/filter/UrlHandlerFilter.java

Lines changed: 24 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818

1919
import java.io.IOException;
2020
import java.util.Arrays;
21-
import java.util.LinkedHashMap;
2221
import java.util.List;
2322
import java.util.Map;
2423
import java.util.function.Consumer;
@@ -37,17 +36,18 @@
3736
import org.springframework.http.HttpStatus;
3837
import org.springframework.http.server.RequestPath;
3938
import org.springframework.lang.Nullable;
39+
import org.springframework.util.LinkedMultiValueMap;
40+
import org.springframework.util.MultiValueMap;
4041
import org.springframework.util.StringUtils;
4142
import org.springframework.web.util.ServletRequestPathUtils;
4243
import org.springframework.web.util.pattern.PathPattern;
4344
import org.springframework.web.util.pattern.PathPatternParser;
4445

4546
/**
46-
* {@code Filter} that can be configured to trim trailing slashes, and either
47-
* send a redirect or wrap the request and continue processing.
47+
* {@link jakarta.servlet.Filter} that modifies the URL, and then redirects or
48+
* wraps the request to apply the change.
4849
*
49-
* <p>Use the static {@link #trailingSlashHandler(String...)} method to begin to
50-
* configure and build an instance. For example:
50+
* <p>To create an instance, you can use the following:
5151
*
5252
* <pre>
5353
* UrlHandlerFilter filter = UrlHandlerFilter
@@ -56,8 +56,8 @@
5656
* .build();
5757
* </pre>
5858
*
59-
* <p>Note that this {@code Filter} should be ordered after
60-
* {@link ForwardedHeaderFilter} and before the Spring Security filter chain.
59+
* <p>This {@code Filter} should be ordered after {@link ForwardedHeaderFilter}
60+
* and before any security filters.
6161
*
6262
* @author Rossen Stoyanchev
6363
* @since 6.2
@@ -67,11 +67,11 @@ public final class UrlHandlerFilter extends OncePerRequestFilter {
6767
private static final Log logger = LogFactory.getLog(UrlHandlerFilter.class);
6868

6969

70-
private final Map<PathPattern, UrlHandler> handlers;
70+
private final MultiValueMap<UrlHandler, PathPattern> handlers;
7171

7272

73-
private UrlHandlerFilter(Map<PathPattern, UrlHandler> handlers) {
74-
this.handlers = new LinkedHashMap<>(handlers);
73+
private UrlHandlerFilter(MultiValueMap<UrlHandler, PathPattern> handlers) {
74+
this.handlers = new LinkedMultiValueMap<>(handlers);
7575
}
7676

7777

@@ -95,11 +95,15 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
9595
if (path == null) {
9696
path = ServletRequestPathUtils.parseAndCache(request);
9797
}
98-
for (Map.Entry<PathPattern, UrlHandler> entry : this.handlers.entrySet()) {
99-
UrlHandler handler = entry.getValue();
100-
if (entry.getKey().matches(path) && handler.canHandle(request)) {
101-
handler.handle(request, response, chain);
102-
return;
98+
for (Map.Entry<UrlHandler, List<PathPattern>> entry : this.handlers.entrySet()) {
99+
if (!entry.getKey().canHandle(request)) {
100+
continue;
101+
}
102+
for (PathPattern pattern : entry.getValue()) {
103+
if (pattern.matches(path)) {
104+
entry.getKey().handle(request, response, chain);
105+
return;
106+
}
103107
}
104108
}
105109
}
@@ -114,8 +118,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
114118

115119

116120
/**
117-
* Create a builder for a {@link UrlHandlerFilter} by adding a handler for
118-
* URL's with a trailing slash.
121+
* Create a builder by adding a handler for URL's with a trailing slash.
119122
* @param pathPatterns path patterns to map the handler to, e.g.
120123
* <code>"/path/&#42;"</code>, <code>"/path/&#42;&#42;"</code>,
121124
* <code>"/path/foo/"</code>.
@@ -153,8 +156,8 @@ public interface Builder {
153156
interface TrailingSlashSpec {
154157

155158
/**
156-
* Intercept requests with a trailing slash. The callback is invoked
157-
* just before the configured trailing slash handler.
159+
* Configure a request consumer to be called just before the handler
160+
* is invoked when a URL with a trailing slash is matched.
158161
*/
159162
TrailingSlashSpec intercept(Consumer<HttpServletRequest> consumer);
160163

@@ -185,15 +188,15 @@ private static final class DefaultBuilder implements Builder {
185188

186189
private final PathPatternParser patternParser = new PathPatternParser();
187190

188-
private final Map<PathPattern, UrlHandler> handlers = new LinkedHashMap<>();
191+
private final MultiValueMap<UrlHandler, PathPattern> handlers = new LinkedMultiValueMap<>();
189192

190193
@Override
191194
public TrailingSlashSpec trailingSlashHandler(String... patterns) {
192195
return new DefaultTrailingSlashSpec(patterns);
193196
}
194197

195198
private DefaultBuilder addHandler(List<PathPattern> pathPatterns, UrlHandler handler) {
196-
pathPatterns.forEach(pattern -> this.handlers.put(pattern, handler));
199+
pathPatterns.forEach(pattern -> this.handlers.add(handler, pattern));
197200
return this;
198201
}
199202

0 commit comments

Comments
 (0)