Fix ResourceUrlEncodingFilter lifecycle

Prior to this commit, the `ResourceUrlEncodingFilter` would wrap the
response and keep a reference to the request. When
`HttpServletResponse.encodeURL` is later called during view rendering,
the filter looks at the request and extracts context mapping information
in order to resolve resource paths in views.

This approach is flawed, when the filter is used with JSPs - if the
request is forwarded to the container by the `InternalResourceView`,
the request information is overwritten by the container. When the view
is being rendered, the information available in the request is outdated
and does not allow to correctly compute that context mapping
information.

This commit ensures that that information is being extracted from the
request as soon as the `ResourceUrlProvider` is set as a request
attribute.

Issue: SPR-17421
This commit is contained in:
Brian Clozel
2018-10-24 21:18:57 +02:00
parent ffa032e78f
commit 50a4769162
3 changed files with 94 additions and 53 deletions

View File

@@ -69,10 +69,10 @@ public class ResourceUrlEncodingFilterTests {
@Test
public void encodeURL() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/");
request.setAttribute(ResourceUrlProviderExposingInterceptor.RESOURCE_URL_PROVIDER_ATTR, this.urlProvider);
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);
});
@@ -82,10 +82,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.urlProvider);
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);
});
@@ -95,10 +111,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.urlProvider);
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);
});
@@ -108,10 +124,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.urlProvider);
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);
});
@@ -121,10 +137,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.urlProvider);
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);
});
@@ -134,10 +150,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.urlProvider);
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);
});
@@ -148,10 +164,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.urlProvider);
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);
});

View File

@@ -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