Handle Resources beyond int length through Servlet 3.1's setContentLengthLong
Issue: SPR-14135
This commit is contained in:
parent
463c8f149d
commit
37bd51cf6d
94
build.gradle
94
build.gradle
|
|
@ -744,52 +744,6 @@ project("spring-web") {
|
|||
}
|
||||
}
|
||||
|
||||
project("spring-websocket") {
|
||||
description = "Spring WebSocket"
|
||||
|
||||
dependencies {
|
||||
compile(project(":spring-core"))
|
||||
compile(project(":spring-context"))
|
||||
compile(project(":spring-web"))
|
||||
optional(project(":spring-messaging"))
|
||||
optional(project(":spring-webmvc"))
|
||||
optional("javax.servlet:javax.servlet-api:3.1.0")
|
||||
optional("javax.websocket:javax.websocket-api:1.0")
|
||||
optional("org.apache.tomcat:tomcat-websocket:${tomcatVersion}") {
|
||||
exclude group: "org.apache.tomcat", module: "tomcat-websocket-api"
|
||||
exclude group: "org.apache.tomcat", module: "tomcat-servlet-api"
|
||||
}
|
||||
optional("org.glassfish.tyrus:tyrus-spi:${tyrusVersion}")
|
||||
optional("org.glassfish.tyrus:tyrus-core:${tyrusVersion}")
|
||||
optional("org.glassfish.tyrus:tyrus-server:${tyrusVersion}")
|
||||
optional("org.glassfish.tyrus:tyrus-container-servlet:${tyrusVersion}")
|
||||
optional("org.eclipse.jetty:jetty-webapp:${jettyVersion}") {
|
||||
exclude group: "javax.servlet", module: "javax.servlet"
|
||||
}
|
||||
optional("org.eclipse.jetty.websocket:websocket-server:${jettyVersion}") {
|
||||
exclude group: "javax.servlet", module: "javax.servlet"
|
||||
}
|
||||
optional("org.eclipse.jetty.websocket:websocket-client:${jettyVersion}")
|
||||
optional("org.eclipse.jetty:jetty-client:${jettyVersion}")
|
||||
optional("io.undertow:undertow-core:${undertowVersion}")
|
||||
optional("io.undertow:undertow-servlet:${undertowVersion}") {
|
||||
exclude group: "org.jboss.spec.javax.servlet", module: "jboss-servlet-api_3.1_spec"
|
||||
exclude group: "org.jboss.spec.javax.annotation", module: "jboss-annotations-api_1.2_spec"
|
||||
}
|
||||
optional("io.undertow:undertow-websockets-jsr:${undertowVersion}") {
|
||||
exclude group: "org.jboss.spec.javax.websocket", module: "jboss-websocket-api_1.1_spec"
|
||||
}
|
||||
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
|
||||
testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
|
||||
testCompile("org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatVersion}")
|
||||
testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}")
|
||||
testCompile("io.projectreactor:reactor-net:${reactorVersion}")
|
||||
testCompile("io.netty:netty-all:${nettyVersion}")
|
||||
testCompile("log4j:log4j:1.2.17")
|
||||
testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}")
|
||||
}
|
||||
}
|
||||
|
||||
project("spring-orm") {
|
||||
description = "Spring Object/Relational Mapping"
|
||||
|
||||
|
|
@ -874,7 +828,7 @@ project("spring-webmvc") {
|
|||
compile(files(project(":spring-core").objenesisRepackJar))
|
||||
compile(project(":spring-expression"))
|
||||
compile(project(":spring-web"))
|
||||
provided("javax.servlet:javax.servlet-api:3.0.1")
|
||||
provided("javax.servlet:javax.servlet-api:3.1.0")
|
||||
optional(project(":spring-context-support")) // for Velocity support
|
||||
optional(project(":spring-oxm")) // for MarshallingView
|
||||
optional("javax.servlet.jsp:javax.servlet.jsp-api:2.2.1")
|
||||
|
|
@ -993,6 +947,52 @@ project("spring-webmvc-portlet") {
|
|||
}
|
||||
}
|
||||
|
||||
project("spring-websocket") {
|
||||
description = "Spring WebSocket"
|
||||
|
||||
dependencies {
|
||||
compile(project(":spring-core"))
|
||||
compile(project(":spring-context"))
|
||||
compile(project(":spring-web"))
|
||||
optional(project(":spring-messaging"))
|
||||
optional(project(":spring-webmvc"))
|
||||
optional("javax.servlet:javax.servlet-api:3.1.0")
|
||||
optional("javax.websocket:javax.websocket-api:1.0")
|
||||
optional("org.apache.tomcat:tomcat-websocket:${tomcatVersion}") {
|
||||
exclude group: "org.apache.tomcat", module: "tomcat-websocket-api"
|
||||
exclude group: "org.apache.tomcat", module: "tomcat-servlet-api"
|
||||
}
|
||||
optional("org.glassfish.tyrus:tyrus-spi:${tyrusVersion}")
|
||||
optional("org.glassfish.tyrus:tyrus-core:${tyrusVersion}")
|
||||
optional("org.glassfish.tyrus:tyrus-server:${tyrusVersion}")
|
||||
optional("org.glassfish.tyrus:tyrus-container-servlet:${tyrusVersion}")
|
||||
optional("org.eclipse.jetty:jetty-webapp:${jettyVersion}") {
|
||||
exclude group: "javax.servlet", module: "javax.servlet"
|
||||
}
|
||||
optional("org.eclipse.jetty.websocket:websocket-server:${jettyVersion}") {
|
||||
exclude group: "javax.servlet", module: "javax.servlet"
|
||||
}
|
||||
optional("org.eclipse.jetty.websocket:websocket-client:${jettyVersion}")
|
||||
optional("org.eclipse.jetty:jetty-client:${jettyVersion}")
|
||||
optional("io.undertow:undertow-core:${undertowVersion}")
|
||||
optional("io.undertow:undertow-servlet:${undertowVersion}") {
|
||||
exclude group: "org.jboss.spec.javax.servlet", module: "jboss-servlet-api_3.1_spec"
|
||||
exclude group: "org.jboss.spec.javax.annotation", module: "jboss-annotations-api_1.2_spec"
|
||||
}
|
||||
optional("io.undertow:undertow-websockets-jsr:${undertowVersion}") {
|
||||
exclude group: "org.jboss.spec.javax.websocket", module: "jboss-websocket-api_1.1_spec"
|
||||
}
|
||||
optional("com.fasterxml.jackson.core:jackson-databind:${jackson2Version}")
|
||||
testCompile("org.apache.tomcat.embed:tomcat-embed-core:${tomcatVersion}")
|
||||
testCompile("org.apache.tomcat.embed:tomcat-embed-websocket:${tomcatVersion}")
|
||||
testCompile("org.apache.tomcat.embed:tomcat-embed-logging-juli:${tomcatVersion}")
|
||||
testCompile("io.projectreactor:reactor-net:${reactorVersion}")
|
||||
testCompile("io.netty:netty-all:${nettyVersion}")
|
||||
testCompile("log4j:log4j:1.2.17")
|
||||
testCompile("org.slf4j:slf4j-jcl:${slf4jVersion}")
|
||||
}
|
||||
}
|
||||
|
||||
project("spring-test") {
|
||||
description = "Spring TestContext Framework"
|
||||
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.activation.MimetypesFileTypeMap;
|
||||
|
||||
|
|
@ -108,9 +107,6 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
|||
return null;
|
||||
}
|
||||
long contentLength = resource.contentLength();
|
||||
if (contentLength > Integer.MAX_VALUE) {
|
||||
throw new IOException("Resource content too long (beyond Integer.MAX_VALUE): " + resource);
|
||||
}
|
||||
return (contentLength < 0 ? null : contentLength);
|
||||
}
|
||||
|
||||
|
|
@ -158,9 +154,7 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
|||
* @throws IOException in case of errors while writing the content
|
||||
*/
|
||||
protected void writePartialContent(HttpRangeResource resource, HttpOutputMessage outputMessage) throws IOException {
|
||||
|
||||
Assert.notNull(resource, "Resource should not be null");
|
||||
|
||||
List<HttpRange> ranges = resource.getHttpRanges();
|
||||
HttpHeaders responseHeaders = outputMessage.getHeaders();
|
||||
MediaType contentType = responseHeaders.getContentType();
|
||||
|
|
@ -168,14 +162,11 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
|||
|
||||
if (ranges.size() == 1) {
|
||||
HttpRange range = ranges.get(0);
|
||||
|
||||
long start = range.getRangeStart(length);
|
||||
long end = range.getRangeEnd(length);
|
||||
long rangeLength = end - start + 1;
|
||||
|
||||
responseHeaders.add("Content-Range", "bytes " + start + "-" + end + "/" + length);
|
||||
responseHeaders.setContentLength((int) rangeLength);
|
||||
|
||||
responseHeaders.setContentLength(rangeLength);
|
||||
InputStream in = resource.getInputStream();
|
||||
try {
|
||||
copyRange(in, outputMessage.getBody(), start, end);
|
||||
|
|
@ -192,15 +183,11 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
|||
else {
|
||||
String boundaryString = MimeTypeUtils.generateMultipartBoundaryString();
|
||||
responseHeaders.set(HttpHeaders.CONTENT_TYPE, "multipart/byteranges; boundary=" + boundaryString);
|
||||
|
||||
OutputStream out = outputMessage.getBody();
|
||||
|
||||
for (HttpRange range : ranges) {
|
||||
long start = range.getRangeStart(length);
|
||||
long end = range.getRangeEnd(length);
|
||||
|
||||
InputStream in = resource.getInputStream();
|
||||
|
||||
// Writing MIME header.
|
||||
println(out);
|
||||
print(out, "--" + boundaryString);
|
||||
|
|
@ -212,7 +199,6 @@ public class ResourceHttpMessageConverter extends AbstractHttpMessageConverter<R
|
|||
print(out, "Content-Range: bytes " + start + "-" + end + "/" + length);
|
||||
println(out);
|
||||
println(out);
|
||||
|
||||
// Printing content
|
||||
copyRange(in, out, start, end);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ 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;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
|
|
@ -38,6 +39,7 @@ import org.springframework.http.converter.ResourceHttpMessageConverter;
|
|||
import org.springframework.http.server.ServletServerHttpRequest;
|
||||
import org.springframework.http.server.ServletServerHttpResponse;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ResourceUtils;
|
||||
|
|
@ -88,8 +90,13 @@ import org.springframework.web.servlet.support.WebContentGenerator;
|
|||
public class ResourceHttpRequestHandler extends WebContentGenerator
|
||||
implements HttpRequestHandler, InitializingBean, CorsConfigurationSource {
|
||||
|
||||
// Servlet 3.1 setContentLengthLong(long) available?
|
||||
private static final boolean contentLengthLongAvailable =
|
||||
ClassUtils.hasMethod(ServletResponse.class, "setContentLengthLong", long.class);
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ResourceHttpRequestHandler.class);
|
||||
|
||||
|
||||
private final List<Resource> locations = new ArrayList<Resource>(4);
|
||||
|
||||
private final List<ResourceResolver> resourceResolvers = new ArrayList<ResourceResolver>(4);
|
||||
|
|
@ -515,9 +522,17 @@ public class ResourceHttpRequestHandler extends WebContentGenerator
|
|||
protected void setHeaders(HttpServletResponse response, Resource resource, MediaType mediaType) throws IOException {
|
||||
long length = resource.contentLength();
|
||||
if (length > Integer.MAX_VALUE) {
|
||||
throw new IOException("Resource content too long (beyond Integer.MAX_VALUE): " + resource);
|
||||
if (contentLengthLongAvailable) {
|
||||
response.setContentLengthLong(length);
|
||||
}
|
||||
else {
|
||||
response.setHeader(HttpHeaders.CONTENT_LENGTH, Long.toString(length));
|
||||
}
|
||||
}
|
||||
response.setContentLength((int) length);
|
||||
else {
|
||||
response.setContentLength((int) length);
|
||||
}
|
||||
|
||||
if (mediaType != null) {
|
||||
response.setContentType(mediaType.toString());
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue