From bdcb189e50ec3280231d8b5431d3e13ea018c608 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 | 6 +- .../setup/PatternMappingFilterProxy.java | 6 +- .../web/filter/ForwardedHeaderFilter.java | 15 +---- .../web/util/UrlPathHelper.java | 57 ++++++++++++++++++- .../condition/PatternsRequestCondition.java | 4 +- ...stractMessageConverterMethodProcessor.java | 15 ++--- ...vletCookieValueMethodArgumentResolver.java | 4 +- .../servlet/resource/ResourceUrlProvider.java | 4 +- .../support/AbstractFlashMapManager.java | 4 +- .../support/ServletUriComponentsBuilder.java | 4 +- 10 files changed, 77 insertions(+), 42 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 1e68094c06..a5642018fe 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 @@ -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. @@ -83,8 +83,6 @@ public class MockHttpServletRequestBuilder private static final Charset UTF8_CHARSET = Charset.forName("UTF-8"); - private static final UrlPathHelper urlPathHelper = new UrlPathHelper(); - private final String method; @@ -697,7 +695,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 ded58c13b0..b5c5bef899 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 4bf1557694..7cc5ed7fc4 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 @@ -75,20 +75,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. @@ -146,7 +137,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)); @@ -219,7 +210,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); @@ -233,7 +224,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 6573f544c7..9f256fead2 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 @@ -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. @@ -22,11 +22,13 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Properties; + import javax.servlet.http.HttpServletRequest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.springframework.util.Assert; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; @@ -69,6 +71,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 +84,7 @@ public class UrlPathHelper { *

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

Default is "true". */ public void setRemoveSemicolonContent(boolean removeSemicolonContent) { + checkReadOnly(); this.removeSemicolonContent = removeSemicolonContent; } @@ -125,6 +132,7 @@ public class UrlPathHelper { * Whether configured to remove ";" (semicolon) content from the request URI. */ public boolean shouldRemoveSemicolonContent() { + checkReadOnly(); return this.removeSemicolonContent; } @@ -142,6 +150,7 @@ public class UrlPathHelper { * @see WebUtils#DEFAULT_CHARACTER_ENCODING */ public void setDefaultEncoding(String defaultEncoding) { + checkReadOnly(); this.defaultEncoding = defaultEncoding; } @@ -152,6 +161,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 @@ -604,4 +624,39 @@ public class UrlPathHelper { return !websphereComplianceFlag; } + + /** + * 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 53ee0dc93b..f96dd5fb5f 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. @@ -103,7 +103,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 f090be5fb9..1f2d95e002 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 @@ -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. @@ -75,13 +75,6 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe private static final UrlPathHelper DECODING_URL_PATH_HELPER = new UrlPathHelper(); - private static final UrlPathHelper RAW_URL_PATH_HELPER = new UrlPathHelper(); - - static { - RAW_URL_PATH_HELPER.setRemoveSemicolonContent(false); - RAW_URL_PATH_HELPER.setUrlDecode(false); - } - private final ContentNegotiationManager contentNegotiationManager; @@ -364,7 +357,7 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe } HttpServletRequest servletRequest = request.getServletRequest(); - String requestUri = RAW_URL_PATH_HELPER.getOriginatingRequestUri(servletRequest); + String requestUri = UrlPathHelper.rawPathInstance.getOriginatingRequestUri(servletRequest); int index = requestUri.lastIndexOf('/') + 1; String filename = requestUri.substring(index); @@ -376,10 +369,10 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe filename = filename.substring(0, index); } - filename = DECODING_URL_PATH_HELPER.decodeRequestString(servletRequest, filename); + filename = UrlPathHelper.defaultInstance.decodeRequestString(servletRequest, filename); String ext = StringUtils.getFilenameExtension(filename); - pathParams = DECODING_URL_PATH_HELPER.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 a6687deeca..a3831bced8 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-2016 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. @@ -35,7 +35,7 @@ import org.springframework.web.util.WebUtils; */ public class ServletCookieValueMethodArgumentResolver extends AbstractCookieValueMethodArgumentResolver { - private UrlPathHelper urlPathHelper = new UrlPathHelper(); + private UrlPathHelper urlPathHelper = UrlPathHelper.defaultInstance; public ServletCookieValueMethodArgumentResolver(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 e68586dc75..0ac6f35223 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-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. @@ -52,7 +52,7 @@ public class ResourceUrlProvider implements ApplicationListener