Refactor HTTP Range support with ResourceRegion

Prior to this commit, the `ResourceHttpMessageConverter` would support
all HTTP Range requests and `MethodProcessors` would "wrap" controller
handler return values with a `HttpRangeResource` to support that use
case in Controllers.

This commit refactors that support in several ways:
* a new ResourceRegion class has been introduced
* a new, separate, ResourceRegionHttpMessageConverter handles the HTTP
range use cases when serving static resources with the
ResourceHttpRequestHandler
* the support of HTTP range requests on Controller handlers has been
removed until a better solution is found

Issue: SPR-14221, SPR-13834
This commit is contained in:
Brian Clozel
2016-05-02 15:39:07 +02:00
parent 7737c3c7e5
commit 5ac31fb39d
16 changed files with 627 additions and 468 deletions

View File

@@ -16,6 +16,10 @@
package org.springframework.web.servlet.mvc.method.annotation;
import static org.junit.Assert.*;
import static org.mockito.BDDMockito.*;
import static org.springframework.web.servlet.HandlerMapping.*;
import java.lang.reflect.Method;
import java.net.URI;
import java.nio.charset.Charset;
@@ -39,7 +43,6 @@ import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.HttpRangeResource;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
@@ -53,13 +56,6 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.ModelAndViewContainer;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.BDDMockito.*;
import static org.springframework.web.servlet.HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
/**
* Test fixture for {@link HttpEntityMethodProcessor} delegating to a mock
* {@link HttpMessageConverter}.
@@ -539,39 +535,6 @@ public class HttpEntityMethodProcessorMockTests {
assertEquals(200, servletResponse.getStatus());
}
@Test
public void handleReturnTypeResourceByteRange() throws Exception {
Resource resource = new ByteArrayResource("Content".getBytes(Charset.forName("UTF-8")));
ResponseEntity<Resource> returnValue = ResponseEntity.ok(resource);
servletRequest.addHeader("Range", "bytes=0-5");
given(resourceMessageConverter.canWrite(HttpRangeResource.class, null)).willReturn(true);
given(resourceMessageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.ALL));
given(resourceMessageConverter.canWrite(HttpRangeResource.class, MediaType.APPLICATION_OCTET_STREAM)).willReturn(true);
processor.handleReturnValue(returnValue, returnTypeResponseEntityResource, mavContainer, webRequest);
then(resourceMessageConverter).should(times(1)).write(any(ByteArrayResource.class),
eq(MediaType.APPLICATION_OCTET_STREAM), any(HttpOutputMessage.class));
assertEquals(206, servletResponse.getStatus());
}
@Test
public void handleReturnTypeResourceIllegalByteRange() throws Exception {
Resource resource = new ByteArrayResource("Content".getBytes(Charset.forName("UTF-8")));
ResponseEntity<Resource> returnValue = ResponseEntity.ok(resource);
servletRequest.addHeader("Range", "illegal");
given(resourceMessageConverter.canWrite(ByteArrayResource.class, null)).willReturn(true);
given(resourceMessageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.ALL));
processor.handleReturnValue(returnValue, returnTypeResponseEntityResource, mavContainer, webRequest);
then(resourceMessageConverter).should(never()).write(any(ByteArrayResource.class),
eq(MediaType.APPLICATION_OCTET_STREAM), any(HttpOutputMessage.class));
assertEquals(416, servletResponse.getStatus());
}
private void initStringMessageConversion(MediaType accepted) {
given(stringHttpMessageConverter.canWrite(String.class, null)).willReturn(true);
given(stringHttpMessageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.TEXT_PLAIN));

View File

@@ -36,7 +36,6 @@ import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.HttpRangeResource;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotReadableException;
@@ -313,37 +312,6 @@ public class RequestResponseBodyMethodProcessorMockTests {
assertEquals(200, servletResponse.getStatus());
}
@Test
public void handleReturnTypeResourceByteRange() throws Exception {
Resource returnValue = new ByteArrayResource("Content".getBytes(Charset.forName("UTF-8")));
servletRequest.addHeader("Range", "bytes=0-5");
given(resourceMessageConverter.canWrite(HttpRangeResource.class, null)).willReturn(true);
given(resourceMessageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.ALL));
given(resourceMessageConverter.canWrite(HttpRangeResource.class, MediaType.APPLICATION_OCTET_STREAM)).willReturn(true);
processor.handleReturnValue(returnValue, returnTypeResource, mavContainer, webRequest);
then(resourceMessageConverter).should(times(1)).write(any(ByteArrayResource.class),
eq(MediaType.APPLICATION_OCTET_STREAM), any(HttpOutputMessage.class));
assertEquals(206, servletResponse.getStatus());
}
@Test
public void handleReturnTypeResourceIllegalByteRange() throws Exception {
Resource returnValue = new ByteArrayResource("Content".getBytes(Charset.forName("UTF-8")));
servletRequest.addHeader("Range", "illegal");
given(resourceMessageConverter.canWrite(ByteArrayResource.class, null)).willReturn(true);
given(resourceMessageConverter.getSupportedMediaTypes()).willReturn(Collections.singletonList(MediaType.ALL));
processor.handleReturnValue(returnValue, returnTypeResource, mavContainer, webRequest);
then(resourceMessageConverter).should(never()).write(any(ByteArrayResource.class),
eq(MediaType.APPLICATION_OCTET_STREAM), any(HttpOutputMessage.class));
assertEquals(416, servletResponse.getStatus());
}
// SPR-9841
@Test