diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java index f00b39e946029a6d1de6dea0f7b8cea8a389d17d..e15b1f2db7fd7de7dd51b610ed1b378e9f3936b2 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2017 the original author or authors. + * Copyright 2002-2018 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,6 +22,7 @@ import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; @@ -53,73 +54,74 @@ public class ResourceUrlEncodingFilter extends GenericFilterBean { public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException { if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { - throw new ServletException("ResourceUrlEncodingFilter just supports HTTP requests"); + throw new ServletException("ResourceUrlEncodingFilter only supports HTTP requests"); } - HttpServletRequest httpRequest = (HttpServletRequest) request; - HttpServletResponse httpResponse = (HttpServletResponse) response; - filterChain.doFilter(httpRequest, new ResourceUrlEncodingResponseWrapper(httpRequest, httpResponse)); + ResourceUrlEncodingRequestWrapper wrappedRequest = + new ResourceUrlEncodingRequestWrapper((HttpServletRequest) request); + ResourceUrlEncodingResponseWrapper wrappedResponse = + new ResourceUrlEncodingResponseWrapper(wrappedRequest, (HttpServletResponse) response); + filterChain.doFilter(wrappedRequest, wrappedResponse); } + private static class ResourceUrlEncodingRequestWrapper extends HttpServletRequestWrapper { - private static class ResourceUrlEncodingResponseWrapper extends HttpServletResponseWrapper { - - private final HttpServletRequest request; + @Nullable + private ResourceUrlProvider resourceUrlProvider; - /* Cache the index and prefix of the path within the DispatcherServlet mapping */ @Nullable private Integer indexLookupPath; private String prefixLookupPath = ""; - public ResourceUrlEncodingResponseWrapper(HttpServletRequest request, HttpServletResponse wrapped) { - super(wrapped); - this.request = request; + ResourceUrlEncodingRequestWrapper(HttpServletRequest request) { + super(request); } @Override - public String encodeURL(String url) { - ResourceUrlProvider resourceUrlProvider = getResourceUrlProvider(); - if (resourceUrlProvider == null) { - logger.debug("Request attribute exposing ResourceUrlProvider not found"); - return super.encodeURL(url); - } - - int index = initLookupPath(resourceUrlProvider); - if (url.startsWith(this.prefixLookupPath)) { - int suffixIndex = getQueryParamsIndex(url); - String suffix = url.substring(suffixIndex); - String lookupPath = url.substring(index, suffixIndex); - lookupPath = resourceUrlProvider.getForLookupPath(lookupPath); - if (lookupPath != null) { - return super.encodeURL(this.prefixLookupPath + lookupPath + suffix); + public void setAttribute(String name, Object o) { + super.setAttribute(name, o); + if (ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR.equals(name)) { + if(o instanceof ResourceUrlProvider) { + initLookupPath((ResourceUrlProvider) o); } } - return super.encodeURL(url); - } - - @Nullable - private ResourceUrlProvider getResourceUrlProvider() { - return (ResourceUrlProvider) this.request.getAttribute( - ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR); } - private int initLookupPath(ResourceUrlProvider urlProvider) { + private void initLookupPath(ResourceUrlProvider urlProvider) { + this.resourceUrlProvider = urlProvider; if (this.indexLookupPath == null) { - UrlPathHelper pathHelper = urlProvider.getUrlPathHelper(); - String requestUri = pathHelper.getRequestUri(this.request); - String lookupPath = pathHelper.getLookupPathForRequest(this.request); + UrlPathHelper pathHelper = this.resourceUrlProvider.getUrlPathHelper(); + String requestUri = pathHelper.getRequestUri(this); + String lookupPath = pathHelper.getLookupPathForRequest(this); this.indexLookupPath = requestUri.lastIndexOf(lookupPath); this.prefixLookupPath = requestUri.substring(0, this.indexLookupPath); if ("/".equals(lookupPath) && !"/".equals(requestUri)) { - String contextPath = pathHelper.getContextPath(this.request); + String contextPath = pathHelper.getContextPath(this); if (requestUri.equals(contextPath)) { this.indexLookupPath = requestUri.length(); this.prefixLookupPath = requestUri; } } } - return this.indexLookupPath; + } + + @Nullable + public String resolveUrlPath(String url) { + if (this.resourceUrlProvider == null) { + logger.debug("Request attribute exposing ResourceUrlProvider not found"); + return null; + } + if (url.startsWith(this.prefixLookupPath)) { + int suffixIndex = getQueryParamsIndex(url); + String suffix = url.substring(suffixIndex); + String lookupPath = url.substring(this.indexLookupPath, suffixIndex); + lookupPath = this.resourceUrlProvider.getForLookupPath(lookupPath); + if (lookupPath != null) { + return this.prefixLookupPath + lookupPath + suffix; + } + } + return null; } private int getQueryParamsIndex(String url) { @@ -128,4 +130,24 @@ public class ResourceUrlEncodingFilter extends GenericFilterBean { } } -} + + private static class ResourceUrlEncodingResponseWrapper extends HttpServletResponseWrapper { + + private final ResourceUrlEncodingRequestWrapper request; + + ResourceUrlEncodingResponseWrapper(ResourceUrlEncodingRequestWrapper request, HttpServletResponse wrapped) { + super(wrapped); + this.request = request; + } + + @Override + public String encodeURL(String url) { + String urlPath = this.request.resolveUrlPath(url); + if (urlPath != null) { + return super.encodeURL(urlPath); + } + return super.encodeURL(url); + } + } + +} \ No newline at end of file diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java index d7716c7d7cfff7c56c291683bf8ea51e90006b8a..7722f763198b23cb72ffdcda86a15482bbf05af1 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlEncodingFilterTests.java @@ -38,7 +38,7 @@ public class ResourceUrlEncodingFilterTests { private ResourceUrlEncodingFilter filter; - private ResourceUrlProvider resourceUrlProvider; + private ResourceUrlProvider urlProvider; @Before public void createFilter() throws Exception { @@ -49,16 +49,16 @@ public class ResourceUrlEncodingFilterTests { List resolvers = Arrays.asList(versionResolver, pathResolver); this.filter = new ResourceUrlEncodingFilter(); - this.resourceUrlProvider = createResourceUrlProvider(resolvers); + this.urlProvider = createResourceUrlProvider(resolvers); } @Test public void encodeURL() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest("GET", "/"); - request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider); MockHttpServletResponse response = new MockHttpServletResponse(); this.filter.doFilter(request, response, (req, res) -> { + req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider); String result = ((HttpServletResponse) res).encodeURL("/resources/bar.css"); assertEquals("/resources/bar-11e16cf79faee7ac698c805cf28248d2.css", result); }); @@ -68,10 +68,26 @@ public class ResourceUrlEncodingFilterTests { public void encodeURLWithContext() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context/foo"); request.setContextPath("/context"); - request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider); MockHttpServletResponse response = new MockHttpServletResponse(); this.filter.doFilter(request, response, (req, res) -> { + req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider); + String result = ((HttpServletResponse) res).encodeURL("/context/resources/bar.css"); + assertEquals("/context/resources/bar-11e16cf79faee7ac698c805cf28248d2.css", result); + }); + } + + + @Test + public void encodeUrlWithContextAndForwardedRequest() throws Exception { + MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context/foo"); + request.setContextPath("/context"); + MockHttpServletResponse response = new MockHttpServletResponse(); + + this.filter.doFilter(request, response, (req, res) -> { + req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider); + request.setRequestURI("/forwarded"); + request.setContextPath("/"); String result = ((HttpServletResponse) res).encodeURL("/context/resources/bar.css"); assertEquals("/context/resources/bar-11e16cf79faee7ac698c805cf28248d2.css", result); }); @@ -82,10 +98,10 @@ public class ResourceUrlEncodingFilterTests { public void encodeContextPathUrlWithoutSuffix() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context"); request.setContextPath("/context"); - request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider); MockHttpServletResponse response = new MockHttpServletResponse(); this.filter.doFilter(request, response, (req, res) -> { + req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider); String result = ((HttpServletResponse) res).encodeURL("/context/resources/bar.css"); assertEquals("/context/resources/bar-11e16cf79faee7ac698c805cf28248d2.css", result); }); @@ -95,10 +111,10 @@ public class ResourceUrlEncodingFilterTests { public void encodeContextPathUrlWithSuffix() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context/"); request.setContextPath("/context"); - request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider); MockHttpServletResponse response = new MockHttpServletResponse(); this.filter.doFilter(request, response, (req, res) -> { + req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider); String result = ((HttpServletResponse) res).encodeURL("/context/resources/bar.css"); assertEquals("/context/resources/bar-11e16cf79faee7ac698c805cf28248d2.css", result); }); @@ -109,10 +125,10 @@ public class ResourceUrlEncodingFilterTests { public void encodeEmptyURLWithContext() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context/foo"); request.setContextPath("/context"); - request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider); MockHttpServletResponse response = new MockHttpServletResponse(); this.filter.doFilter(request, response, (req, res) -> { + req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider); String result = ((HttpServletResponse) res).encodeURL("?foo=1"); assertEquals("?foo=1", result); }); @@ -123,10 +139,10 @@ public class ResourceUrlEncodingFilterTests { public void encodeURLWithRequestParams() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo"); request.setContextPath("/"); - request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider); MockHttpServletResponse response = new MockHttpServletResponse(); this.filter.doFilter(request, response, (req, res) -> { + req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider); String result = ((HttpServletResponse) res).encodeURL("/resources/bar.css?foo=bar&url=http://example.org"); assertEquals("/resources/bar-11e16cf79faee7ac698c805cf28248d2.css?foo=bar&url=http://example.org", result); }); @@ -138,10 +154,10 @@ public class ResourceUrlEncodingFilterTests { MockHttpServletRequest request = new MockHttpServletRequest("GET", "/context-path/index"); request.setContextPath("/context-path"); request.setServletPath(""); - request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.resourceUrlProvider); MockHttpServletResponse response = new MockHttpServletResponse(); this.filter.doFilter(request, response, (req, res) -> { + req.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider); String result = ((HttpServletResponse) res).encodeURL("index?key=value"); assertEquals("index?key=value", result); }); diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderJavaConfigTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderJavaConfigTests.java index 9ad2f6aa4d01dd8aada3f1945e6981b4c2d23394..9036d1d156eca2d9edf739fedb62235d2cbef86c 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderJavaConfigTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceUrlProviderJavaConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2018 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. @@ -55,8 +55,6 @@ public class ResourceUrlProviderJavaConfigTests { @Before @SuppressWarnings("resource") public void setup() throws Exception { - this.filterChain = new MockFilterChain(this.servlet, new ResourceUrlEncodingFilter()); - AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); context.setServletContext(new MockServletContext()); context.register(WebConfig.class); @@ -66,8 +64,13 @@ public class ResourceUrlProviderJavaConfigTests { this.request.setContextPath("/myapp"); this.response = new MockHttpServletResponse(); - Object urlProvider = context.getBean(ResourceUrlProvider.class); - this.request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, urlProvider); + this.filterChain = new MockFilterChain(this.servlet, + new ResourceUrlEncodingFilter(), + (request, response, chain) -> { + Object urlProvider = context.getBean(ResourceUrlProvider.class); + request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, urlProvider); + chain.doFilter(request, response); + }); } @Test