Add HttpRange tests, set Accept-Range header, polish
Issue: SPR-10805
This commit is contained in:
@@ -246,7 +246,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
return;
|
||||
}
|
||||
|
||||
if (request.getHeader("Range") == null) {
|
||||
if (request.getHeader(HttpHeaders.RANGE) == null) {
|
||||
setHeaders(response, resource, mediaType);
|
||||
writeContent(response, resource);
|
||||
}
|
||||
@@ -408,6 +408,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
if (resource instanceof EncodedResource) {
|
||||
response.setHeader(CONTENT_ENCODING, ((EncodedResource) resource).getContentEncoding());
|
||||
}
|
||||
|
||||
response.setHeader(HttpHeaders.ACCEPT_RANGES, "bytes");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -433,26 +435,25 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
}
|
||||
|
||||
/**
|
||||
* Write partial content out to the given servlet response,
|
||||
* streaming parts of the resource's content, as indicated by the request's
|
||||
* {@code Range} header.
|
||||
* Write parts of the resource as indicated by the request {@code Range} header.
|
||||
* @param request current servlet request
|
||||
* @param response current servlet response
|
||||
* @param resource the identified resource (never {@code null})
|
||||
* @param contentType the content type
|
||||
* @throws IOException in case of errors while writing the content
|
||||
*/
|
||||
protected void writePartialContent(HttpServletRequest request,
|
||||
HttpServletResponse response, Resource resource, MediaType contentType) throws IOException {
|
||||
long resourceLength = resource.contentLength();
|
||||
protected void writePartialContent(HttpServletRequest request, HttpServletResponse response,
|
||||
Resource resource, MediaType contentType) throws IOException {
|
||||
|
||||
long length = resource.contentLength();
|
||||
|
||||
List<HttpRange> ranges;
|
||||
try {
|
||||
HttpHeaders requestHeaders =
|
||||
new ServletServerHttpRequest(request).getHeaders();
|
||||
ranges = requestHeaders.getRange();
|
||||
} catch (IllegalArgumentException ex) {
|
||||
response.addHeader("Content-Range", "bytes */" + resourceLength);
|
||||
HttpHeaders headers = new ServletServerHttpRequest(request).getHeaders();
|
||||
ranges = headers.getRange();
|
||||
}
|
||||
catch (IllegalArgumentException ex) {
|
||||
response.addHeader("Content-Range", "bytes */" + length);
|
||||
response.sendError(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
|
||||
return;
|
||||
}
|
||||
@@ -462,20 +463,17 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
if (ranges.size() == 1) {
|
||||
HttpRange range = ranges.get(0);
|
||||
|
||||
long rangeStart = range.getRangeStart(resourceLength);
|
||||
long rangeEnd = range.getRangeEnd(resourceLength);
|
||||
long rangeLength = rangeEnd - rangeStart + 1;
|
||||
long start = range.getRangeStart(length);
|
||||
long end = range.getRangeEnd(length);
|
||||
long rangeLength = end - start + 1;
|
||||
|
||||
setHeaders(response, resource, contentType);
|
||||
response.addHeader("Content-Range", "bytes "
|
||||
+ rangeStart + "-"
|
||||
+ rangeEnd + "/"
|
||||
+ resourceLength);
|
||||
response.addHeader("Content-Range", "bytes " + start + "-" + end + "/" + length);
|
||||
response.setContentLength((int) rangeLength);
|
||||
|
||||
InputStream in = resource.getInputStream();
|
||||
try {
|
||||
copyRange(in, response.getOutputStream(), rangeStart, rangeEnd);
|
||||
copyRange(in, response.getOutputStream(), start, end);
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
@@ -493,8 +491,8 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
ServletOutputStream out = response.getOutputStream();
|
||||
|
||||
for (HttpRange range : ranges) {
|
||||
long rangeStart = range.getRangeStart(resourceLength);
|
||||
long rangeEnd = range.getRangeEnd(resourceLength);
|
||||
long start = range.getRangeStart(length);
|
||||
long end = range.getRangeEnd(length);
|
||||
|
||||
InputStream in = resource.getInputStream();
|
||||
|
||||
@@ -504,27 +502,23 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
if (contentType != null) {
|
||||
out.println("Content-Type: " + contentType);
|
||||
}
|
||||
out.println("Content-Range: bytes " + rangeStart + "-" +
|
||||
rangeEnd + "/" + resourceLength);
|
||||
out.println("Content-Range: bytes " + start + "-" + end + "/" + length);
|
||||
out.println();
|
||||
|
||||
// Printing content
|
||||
copyRange(in, out, rangeStart, rangeEnd);
|
||||
|
||||
copyRange(in, out, start, end);
|
||||
}
|
||||
out.println();
|
||||
out.print("--" + boundaryString + "--");
|
||||
}
|
||||
}
|
||||
|
||||
private void copyRange(InputStream in, OutputStream out, long start, long end)
|
||||
throws IOException {
|
||||
private void copyRange(InputStream in, OutputStream out, long start, long end) throws IOException {
|
||||
|
||||
long skipped = in.skip(start);
|
||||
|
||||
if (skipped < start) {
|
||||
throw new IOException("Could only skip " + skipped + " bytes out of " +
|
||||
start + " required");
|
||||
throw new IOException("Skipped only " + skipped + " bytes out of " + start + " required.");
|
||||
}
|
||||
|
||||
long bytesToCopy = end - start + 1;
|
||||
@@ -546,6 +540,7 @@ public class ResourceHttpRequestHandler extends WebContentGenerator implements H
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ResourceHttpRequestHandler [locations=" +
|
||||
|
||||
@@ -286,8 +286,7 @@ public class ResourceHttpRequestHandlerTests {
|
||||
assertEquals(206, this.response.getStatus());
|
||||
assertEquals("text/plain", this.response.getContentType());
|
||||
assertEquals(2, this.response.getContentLength());
|
||||
assertEquals("bytes 0-1/10",
|
||||
this.response.getHeader("Content-Range"));
|
||||
assertEquals("bytes 0-1/10", this.response.getHeader("Content-Range"));
|
||||
assertEquals("So", this.response.getContentAsString());
|
||||
}
|
||||
|
||||
@@ -300,8 +299,7 @@ public class ResourceHttpRequestHandlerTests {
|
||||
assertEquals(206, this.response.getStatus());
|
||||
assertEquals("text/plain", this.response.getContentType());
|
||||
assertEquals(1, this.response.getContentLength());
|
||||
assertEquals("bytes 9-9/10",
|
||||
this.response.getHeader("Content-Range"));
|
||||
assertEquals("bytes 9-9/10", this.response.getHeader("Content-Range"));
|
||||
assertEquals(".", this.response.getContentAsString());
|
||||
}
|
||||
|
||||
@@ -314,8 +312,7 @@ public class ResourceHttpRequestHandlerTests {
|
||||
assertEquals(206, this.response.getStatus());
|
||||
assertEquals("text/plain", this.response.getContentType());
|
||||
assertEquals(1, this.response.getContentLength());
|
||||
assertEquals("bytes 9-9/10",
|
||||
this.response.getHeader("Content-Range"));
|
||||
assertEquals("bytes 9-9/10", this.response.getHeader("Content-Range"));
|
||||
assertEquals(".", this.response.getContentAsString());
|
||||
}
|
||||
|
||||
@@ -328,8 +325,7 @@ public class ResourceHttpRequestHandlerTests {
|
||||
assertEquals(206, this.response.getStatus());
|
||||
assertEquals("text/plain", this.response.getContentType());
|
||||
assertEquals(1, this.response.getContentLength());
|
||||
assertEquals("bytes 9-9/10",
|
||||
this.response.getHeader("Content-Range"));
|
||||
assertEquals("bytes 9-9/10", this.response.getHeader("Content-Range"));
|
||||
assertEquals(".", this.response.getContentAsString());
|
||||
}
|
||||
|
||||
@@ -342,21 +338,18 @@ public class ResourceHttpRequestHandlerTests {
|
||||
assertEquals(206, this.response.getStatus());
|
||||
assertEquals("text/plain", this.response.getContentType());
|
||||
assertEquals(10, this.response.getContentLength());
|
||||
assertEquals("bytes 0-9/10",
|
||||
this.response.getHeader("Content-Range"));
|
||||
assertEquals("bytes 0-9/10", this.response.getHeader("Content-Range"));
|
||||
assertEquals("Some text.", this.response.getContentAsString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void partialContentInvalidRangeHeader() throws Exception {
|
||||
this.request.addHeader("Range", "bytes= foo bar");
|
||||
this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE,
|
||||
"foo.txt");
|
||||
this.request.setAttribute(HandlerMapping.PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE, "foo.txt");
|
||||
this.handler.handleRequest(this.request, this.response);
|
||||
|
||||
assertEquals(416, this.response.getStatus());
|
||||
assertEquals("bytes */10",
|
||||
this.response.getHeader("Content-Range"));
|
||||
assertEquals("bytes */10", this.response.getHeader("Content-Range"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -366,13 +359,12 @@ public class ResourceHttpRequestHandlerTests {
|
||||
this.handler.handleRequest(this.request, this.response);
|
||||
|
||||
assertEquals(206, this.response.getStatus());
|
||||
assertTrue(this.response.getContentType()
|
||||
.startsWith("multipart/byteranges; boundary="));
|
||||
assertTrue(this.response.getContentType().startsWith("multipart/byteranges; boundary="));
|
||||
|
||||
String boundary = "--" + this.response.getContentType().substring(31);
|
||||
|
||||
String[] ranges = StringUtils.tokenizeToStringArray(this.response.getContentAsString(),
|
||||
"\r\n", false, true);
|
||||
String content = this.response.getContentAsString();
|
||||
String[] ranges = StringUtils.tokenizeToStringArray(content, "\r\n", false, true);
|
||||
|
||||
assertEquals(boundary, ranges[0]);
|
||||
assertEquals("Content-Type: text/plain", ranges[1]);
|
||||
@@ -391,8 +383,6 @@ public class ResourceHttpRequestHandlerTests {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private long headerAsLong(String responseHeaderName) {
|
||||
return Long.valueOf(this.response.getHeader(responseHeaderName));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user