From e5dbe12e850d2b7d56a02e099fc687fd06e9c6f8 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Fri, 6 May 2016 11:50:47 +0200 Subject: [PATCH] ResourceHttpRequestHandler sets "Accept-Ranges" header only once Issue: SPR-14221 --- .../resource/ResourceHttpRequestHandler.java | 44 +++++++++++++------ .../ResourceHttpRequestHandlerTests.java | 33 ++++++++++++-- 2 files changed, 60 insertions(+), 17 deletions(-) diff --git a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java index f8101dcdefb..e495ae32034 100644 --- a/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java +++ b/spring-webmvc/src/main/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandler.java @@ -20,7 +20,6 @@ import java.io.IOException; import java.net.URLDecoder; import java.util.ArrayList; import java.util.List; - import javax.servlet.ServletException; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; @@ -120,7 +119,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator /** - * Set a {@code List} of {@code Resource} paths to use as sources + * Set the {@code List} of {@code Resource} paths to use as sources * for serving static resources. */ public void setLocations(List locations) { @@ -129,6 +128,10 @@ public class ResourceHttpRequestHandler extends WebContentGenerator this.locations.addAll(locations); } + /** + * Return the {@code List} of {@code Resource} paths to use as sources + * for serving static resources. + */ public List getLocations() { return this.locations; } @@ -173,12 +176,16 @@ public class ResourceHttpRequestHandler extends WebContentGenerator /** * Configure the {@link ResourceHttpMessageConverter} to use. *

By default a {@link ResourceHttpMessageConverter} will be configured. - * @since 4.3.0 + * @since 4.3 */ public void setResourceHttpMessageConverter(ResourceHttpMessageConverter resourceHttpMessageConverter) { this.resourceHttpMessageConverter = resourceHttpMessageConverter; } + /** + * Return the list of configured resource converters. + * @since 4.3 + */ public ResourceHttpMessageConverter getResourceHttpMessageConverter() { return this.resourceHttpMessageConverter; } @@ -186,15 +193,18 @@ public class ResourceHttpRequestHandler extends WebContentGenerator /** * Configure the {@link ResourceRegionHttpMessageConverter} to use. *

By default a {@link ResourceRegionHttpMessageConverter} will be configured. - * @since 4.3.0 + * @since 4.3 */ - public ResourceRegionHttpMessageConverter getResourceRegionHttpMessageConverter() { - return resourceRegionHttpMessageConverter; + public void setResourceRegionHttpMessageConverter(ResourceRegionHttpMessageConverter resourceRegionHttpMessageConverter) { + this.resourceRegionHttpMessageConverter = resourceRegionHttpMessageConverter; } - public void setResourceRegionHttpMessageConverter( - ResourceRegionHttpMessageConverter resourceRegionHttpMessageConverter) { - this.resourceRegionHttpMessageConverter = resourceRegionHttpMessageConverter; + /** + * Return the list of configured resource region converters. + * @since 4.3 + */ + public ResourceRegionHttpMessageConverter getResourceRegionHttpMessageConverter() { + return this.resourceRegionHttpMessageConverter; } /** @@ -215,6 +225,10 @@ public class ResourceHttpRequestHandler extends WebContentGenerator this.contentNegotiationManager = contentNegotiationManager; } + /** + * Return the specified content negotiation manager. + * @since 4.3 + */ public ContentNegotiationManager getContentNegotiationManager() { return this.contentNegotiationManager; } @@ -227,6 +241,9 @@ public class ResourceHttpRequestHandler extends WebContentGenerator this.corsConfiguration = corsConfiguration; } + /** + * Return the specified CORS configuration. + */ @Override public CorsConfiguration getCorsConfiguration(HttpServletRequest request) { return this.corsConfiguration; @@ -348,13 +365,12 @@ public class ResourceHttpRequestHandler extends WebContentGenerator } ServletServerHttpResponse outputMessage = new ServletServerHttpResponse(response); - outputMessage.getHeaders().add(HttpHeaders.ACCEPT_RANGES, "bytes"); - if (request.getHeader(HttpHeaders.RANGE) == null) { setHeaders(response, resource, mediaType); this.resourceHttpMessageConverter.write(resource, mediaType, outputMessage); } else { + response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes"); ServletServerHttpRequest inputMessage = new ServletServerHttpRequest(request); try { List httpRanges = inputMessage.getHeaders().getRange(); @@ -364,12 +380,12 @@ public class ResourceHttpRequestHandler extends WebContentGenerator this.resourceRegionHttpMessageConverter.write(resourceRegion, mediaType, outputMessage); } else { - this.resourceRegionHttpMessageConverter - .write(HttpRange.toResourceRegions(httpRanges, resource), mediaType, outputMessage); + this.resourceRegionHttpMessageConverter.write( + HttpRange.toResourceRegions(httpRanges, resource), mediaType, outputMessage); } } catch (IllegalArgumentException ex) { - response.addHeader("Content-Range", "bytes */" + resource.contentLength()); + response.setHeader("Content-Range", "bytes */" + resource.contentLength()); response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); } } diff --git a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java index feaf0983222..52feb87d3f2 100644 --- a/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java +++ b/spring-webmvc/src/test/java/org/springframework/web/servlet/resource/ResourceHttpRequestHandlerTests.java @@ -16,8 +16,6 @@ package org.springframework.web.servlet.resource; -import static org.junit.Assert.*; - import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; @@ -26,7 +24,6 @@ import java.util.Collections; import java.util.List; import java.util.Locale; import java.util.TimeZone; - import javax.servlet.http.HttpServletResponse; import org.hamcrest.Matchers; @@ -48,6 +45,8 @@ import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.ContentNegotiationManagerFactoryBean; import org.springframework.web.servlet.HandlerMapping; +import static org.junit.Assert.*; + /** * Unit tests for ResourceHttpRequestHandler. * @@ -97,6 +96,8 @@ public class ResourceHttpRequestHandlerTests { assertEquals("max-age=3600", this.response.getHeader("Cache-Control")); assertTrue(this.response.containsHeader("Last-Modified")); assertEquals(this.response.getHeader("Last-Modified"), resourceLastModifiedDate("test/foo.css")); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); assertEquals("h1 { color:red; }", this.response.getContentAsString()); } @@ -112,6 +113,8 @@ public class ResourceHttpRequestHandlerTests { assertEquals("max-age=3600", this.response.getHeader("Cache-Control")); assertTrue(this.response.containsHeader("Last-Modified")); assertEquals(this.response.getHeader("Last-Modified"), resourceLastModifiedDate("test/foo.css")); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); assertEquals(0, this.response.getContentAsByteArray().length); } @@ -134,6 +137,8 @@ public class ResourceHttpRequestHandlerTests { assertEquals("no-store", this.response.getHeader("Cache-Control")); assertTrue(this.response.containsHeader("Last-Modified")); assertEquals(this.response.getHeader("Last-Modified"), resourceLastModifiedDate("test/foo.css")); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); } @Test @@ -147,6 +152,8 @@ public class ResourceHttpRequestHandlerTests { this.handler.handleRequest(this.request, this.response); assertEquals("\"versionString\"", this.response.getHeader("ETag")); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); } @Test @@ -163,6 +170,8 @@ public class ResourceHttpRequestHandlerTests { assertTrue(dateHeaderAsLong("Expires") >= System.currentTimeMillis() - 1000 + (3600 * 1000)); assertTrue(this.response.containsHeader("Last-Modified")); assertEquals(this.response.getHeader("Last-Modified"), resourceLastModifiedDate("test/foo.css")); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); } @Test @@ -181,6 +190,8 @@ public class ResourceHttpRequestHandlerTests { assertTrue(dateHeaderAsLong("Expires") <= System.currentTimeMillis()); assertTrue(this.response.containsHeader("Last-Modified")); assertEquals(dateHeaderAsLong("Last-Modified") / 1000, resourceLastModified("test/foo.css") / 1000); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); } @Test @@ -192,6 +203,8 @@ public class ResourceHttpRequestHandlerTests { assertEquals("max-age=3600", this.response.getHeader("Cache-Control")); assertTrue(this.response.containsHeader("Last-Modified")); assertEquals(this.response.getHeader("Last-Modified"), resourceLastModifiedDate("test/foo.html")); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); } @Test @@ -204,6 +217,8 @@ public class ResourceHttpRequestHandlerTests { assertEquals("max-age=3600", this.response.getHeader("Cache-Control")); assertTrue(this.response.containsHeader("Last-Modified")); assertEquals(this.response.getHeader("Last-Modified"), resourceLastModifiedDate("testalternatepath/baz.css")); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); assertEquals("h1 { color:red; }", this.response.getContentAsString()); } @@ -455,6 +470,8 @@ public class ResourceHttpRequestHandlerTests { assertEquals(2, this.response.getContentLength()); assertEquals("bytes 0-1/10", this.response.getHeader("Content-Range")); assertEquals("So", this.response.getContentAsString()); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); } @Test @@ -468,6 +485,8 @@ public class ResourceHttpRequestHandlerTests { assertEquals(1, this.response.getContentLength()); assertEquals("bytes 9-9/10", this.response.getHeader("Content-Range")); assertEquals(".", this.response.getContentAsString()); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); } @Test @@ -481,6 +500,8 @@ public class ResourceHttpRequestHandlerTests { assertEquals(1, this.response.getContentLength()); assertEquals("bytes 9-9/10", this.response.getHeader("Content-Range")); assertEquals(".", this.response.getContentAsString()); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); } @Test @@ -494,6 +515,8 @@ public class ResourceHttpRequestHandlerTests { assertEquals(1, this.response.getContentLength()); assertEquals("bytes 9-9/10", this.response.getHeader("Content-Range")); assertEquals(".", this.response.getContentAsString()); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); } @Test @@ -507,6 +530,8 @@ public class ResourceHttpRequestHandlerTests { assertEquals(10, this.response.getContentLength()); assertEquals("bytes 0-9/10", this.response.getHeader("Content-Range")); assertEquals("Some text.", this.response.getContentAsString()); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); } @Test @@ -517,6 +542,8 @@ public class ResourceHttpRequestHandlerTests { assertEquals(416, this.response.getStatus()); assertEquals("bytes */10", this.response.getHeader("Content-Range")); + assertEquals("bytes", this.response.getHeader("Accept-Ranges")); + assertEquals(1, this.response.getHeaders("Accept-Ranges").size()); } @Test