From 26141b0d56f8220b293a0fc845a9818c849c9827 Mon Sep 17 00:00:00 2001 From: Rossen Stoyanchev Date: Thu, 3 Sep 2020 20:21:00 +0100 Subject: [PATCH] Shared read-only instances of UrlPathHelper UrlPathHelper is often created and used without customizations or with the same customizations. This commit introduces re-usable, instances. Effectively a backport of commit 23233c. Closes gh-25690 --- .../MockHttpServletRequestBuilder.java | 5 +- .../setup/PatternMappingFilterProxy.java | 6 +-- .../web/filter/ForwardedHeaderFilter.java | 15 ++---- .../web/util/UrlPathHelper.java | 54 +++++++++++++++++++ .../condition/PatternsRequestCondition.java | 4 +- ...stractMessageConverterMethodProcessor.java | 16 ++---- ...vletCookieValueMethodArgumentResolver.java | 4 +- .../servlet/resource/ResourceUrlProvider.java | 4 +- .../support/AbstractFlashMapManager.java | 4 +- .../support/ServletUriComponentsBuilder.java | 4 +- 10 files changed, 73 insertions(+), 43 deletions(-) diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java index 3b1f8b8d80f..00f2de06c8a 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/request/MockHttpServletRequestBuilder.java @@ -82,9 +82,6 @@ import org.springframework.web.util.UrlPathHelper; public class MockHttpServletRequestBuilder implements ConfigurableSmartRequestBuilder, Mergeable { - private static final UrlPathHelper urlPathHelper = new UrlPathHelper(); - - private final String method; private final URI url; @@ -696,7 +693,7 @@ public class MockHttpServletRequestBuilder } String extraPath = requestUri.substring(this.contextPath.length() + this.servletPath.length()); this.pathInfo = (StringUtils.hasText(extraPath) ? - urlPathHelper.decodeRequestString(request, extraPath) : null); + UrlPathHelper.defaultInstance.decodeRequestString(request, extraPath) : null); } request.setPathInfo(this.pathInfo); } diff --git a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/PatternMappingFilterProxy.java b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/PatternMappingFilterProxy.java index 9d6b4aa0449..03488cd010f 100644 --- a/spring-test/src/main/java/org/springframework/test/web/servlet/setup/PatternMappingFilterProxy.java +++ b/spring-test/src/main/java/org/springframework/test/web/servlet/setup/PatternMappingFilterProxy.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,8 +44,6 @@ final class PatternMappingFilterProxy implements Filter { private static final String PATH_MAPPING_PATTERN = "/*"; - private static final UrlPathHelper urlPathHelper = new UrlPathHelper(); - private final Filter delegate; /** Patterns that require an exact match, e.g. "/test" */ @@ -95,7 +93,7 @@ final class PatternMappingFilterProxy implements Filter { throws IOException, ServletException { HttpServletRequest httpRequest = (HttpServletRequest) request; - String requestPath = urlPathHelper.getPathWithinApplication(httpRequest); + String requestPath = UrlPathHelper.defaultInstance.getPathWithinApplication(httpRequest); if (matches(requestPath)) { this.delegate.doFilter(request, response, filterChain); diff --git a/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java b/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java index 15459bb74e9..8a8e6cd9b4d 100644 --- a/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java +++ b/spring-web/src/main/java/org/springframework/web/filter/ForwardedHeaderFilter.java @@ -76,20 +76,11 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { } - private final UrlPathHelper pathHelper; - private boolean removeOnly; private boolean relativeRedirects; - public ForwardedHeaderFilter() { - this.pathHelper = new UrlPathHelper(); - this.pathHelper.setUrlDecode(false); - this.pathHelper.setRemoveSemicolonContent(false); - } - - /** * Enables mode in which any "Forwarded" or "X-Forwarded-*" headers are * removed only and the information in them ignored. @@ -145,7 +136,7 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { filterChain.doFilter(theRequest, response); } else { - HttpServletRequest theRequest = new ForwardedHeaderExtractingRequest(request, this.pathHelper); + HttpServletRequest theRequest = new ForwardedHeaderExtractingRequest(request); HttpServletResponse theResponse = (this.relativeRedirects ? RelativeRedirectResponseWrapper.wrapIfNecessary(response, HttpStatus.SEE_OTHER) : new ForwardedHeaderExtractingResponse(response, theRequest)); @@ -221,7 +212,7 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { private final String requestUrl; - public ForwardedHeaderExtractingRequest(HttpServletRequest request, UrlPathHelper pathHelper) { + public ForwardedHeaderExtractingRequest(HttpServletRequest request) { super(request); HttpRequest httpRequest = new ServletServerHttpRequest(request); @@ -235,7 +226,7 @@ public class ForwardedHeaderFilter extends OncePerRequestFilter { String prefix = getForwardedPrefix(request); this.contextPath = (prefix != null ? prefix : request.getContextPath()); - this.requestUri = this.contextPath + pathHelper.getPathWithinApplication(request); + this.requestUri = this.contextPath + UrlPathHelper.rawPathInstance.getPathWithinApplication(request); this.requestUrl = this.scheme + "://" + this.host + (port == -1 ? "" : ":" + port) + this.requestUri; } diff --git a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java index dcf373e6684..03fef3a32fc 100644 --- a/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java +++ b/spring-web/src/main/java/org/springframework/web/util/UrlPathHelper.java @@ -27,6 +27,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; @@ -69,6 +70,8 @@ public class UrlPathHelper { private String defaultEncoding = WebUtils.DEFAULT_CHARACTER_ENCODING; + private boolean readOnly = false; + /** * Whether URL lookups should always use the full path within the current @@ -80,6 +83,7 @@ public class UrlPathHelper { *

By default this is set to "false". */ public void setAlwaysUseFullPath(boolean alwaysUseFullPath) { + checkReadOnly(); this.alwaysUseFullPath = alwaysUseFullPath; } @@ -102,6 +106,7 @@ public class UrlPathHelper { * @see java.net.URLDecoder#decode(String, String) */ public void setUrlDecode(boolean urlDecode) { + checkReadOnly(); this.urlDecode = urlDecode; } @@ -118,6 +123,7 @@ public class UrlPathHelper { *

Default is "true". */ public void setRemoveSemicolonContent(boolean removeSemicolonContent) { + checkReadOnly(); this.removeSemicolonContent = removeSemicolonContent; } @@ -125,6 +131,7 @@ public class UrlPathHelper { * Whether configured to remove ";" (semicolon) content from the request URI. */ public boolean shouldRemoveSemicolonContent() { + checkReadOnly(); return this.removeSemicolonContent; } @@ -142,6 +149,7 @@ public class UrlPathHelper { * @see WebUtils#DEFAULT_CHARACTER_ENCODING */ public void setDefaultEncoding(String defaultEncoding) { + checkReadOnly(); this.defaultEncoding = defaultEncoding; } @@ -152,6 +160,17 @@ public class UrlPathHelper { return this.defaultEncoding; } + /** + * Switch to read-only mode where further configuration changes are not allowed. + */ + private void setReadOnly() { + this.readOnly = true; + } + + private void checkReadOnly() { + Assert.isTrue(!this.readOnly, "This instance cannot be modified"); + } + /** * Return the mapping lookup path for the given request, within the current @@ -605,4 +624,39 @@ public class UrlPathHelper { return !flagToUse; } + + /** + * Shared, read-only instance with defaults. The following apply: + *

+ */ + public static final UrlPathHelper defaultInstance = new UrlPathHelper(); + + static { + defaultInstance.setReadOnly(); + } + + + /** + * Shared, read-only instance for the full, encoded path. The following apply: + * + */ + public static final UrlPathHelper rawPathInstance = new UrlPathHelper(); + + static { + rawPathInstance.setAlwaysUseFullPath(true); + rawPathInstance.setUrlDecode(false); + rawPathInstance.setRemoveSemicolonContent(false); + rawPathInstance.setReadOnly(); + } + } diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java index 0a28eba7dca..1cdf2cacc8a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/condition/PatternsRequestCondition.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -105,7 +105,7 @@ public final class PatternsRequestCondition extends AbstractRequestCondition fileExtensions) { this.patterns = Collections.unmodifiableSet(prependLeadingSlash(patterns)); - this.pathHelper = (urlPathHelper != null ? urlPathHelper : new UrlPathHelper()); + this.pathHelper = (urlPathHelper != null ? urlPathHelper : UrlPathHelper.defaultInstance); this.pathMatcher = (pathMatcher != null ? pathMatcher : new AntPathMatcher()); this.useSuffixPatternMatch = useSuffixPatternMatch; this.useTrailingSlashMatch = useTrailingSlashMatch; diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java index ec1c3dd00cd..1b4380b336b 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java @@ -84,16 +84,6 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe new ParameterizedTypeReference>() { }.getType(); - private static final UrlPathHelper decodingUrlPathHelper = new UrlPathHelper(); - - private static final UrlPathHelper rawUrlPathHelper = new UrlPathHelper(); - - static { - rawUrlPathHelper.setRemoveSemicolonContent(false); - rawUrlPathHelper.setUrlDecode(false); - } - - private final ContentNegotiationManager contentNegotiationManager; private final PathExtensionContentNegotiationStrategy pathStrategy; @@ -406,7 +396,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe } HttpServletRequest servletRequest = request.getServletRequest(); - String requestUri = rawUrlPathHelper.getOriginatingRequestUri(servletRequest); + String requestUri = UrlPathHelper.rawPathInstance.getOriginatingRequestUri(servletRequest); int index = requestUri.lastIndexOf('/') + 1; String filename = requestUri.substring(index); @@ -418,10 +408,10 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe filename = filename.substring(0, index); } - filename = decodingUrlPathHelper.decodeRequestString(servletRequest, filename); + filename = UrlPathHelper.defaultInstance.decodeRequestString(servletRequest, filename); String ext = StringUtils.getFilenameExtension(filename); - pathParams = decodingUrlPathHelper.decodeRequestString(servletRequest, pathParams); + pathParams = UrlPathHelper.defaultInstance.decodeRequestString(servletRequest, pathParams); String extInPathParams = StringUtils.getFilenameExtension(pathParams); if (!safeExtension(servletRequest, ext) || !safeExtension(servletRequest, extInPathParams)) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletCookieValueMethodArgumentResolver.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletCookieValueMethodArgumentResolver.java index d1928fb9f65..97c66a581a3 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletCookieValueMethodArgumentResolver.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/ServletCookieValueMethodArgumentResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,7 @@ import org.springframework.web.util.WebUtils; */ public class ServletCookieValueMethodArgumentResolver extends AbstractCookieValueMethodArgumentResolver { - private UrlPathHelper urlPathHelper = new UrlPathHelper(); + private UrlPathHelper urlPathHelper = UrlPathHelper.defaultInstance; public ServletCookieValueMethodArgumentResolver(@Nullable ConfigurableBeanFactory beanFactory) { diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java index 8f5bc5b020d..a81a3678b3a 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlProvider.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,7 +52,7 @@ public class ResourceUrlProvider implements ApplicationListener