summaryrefslogtreecommitdiff
path: root/spring-web
diff options
context:
space:
mode:
Diffstat (limited to 'spring-web')
-rw-r--r--spring-web/src/main/java/org/springframework/http/HttpRange.java37
-rw-r--r--spring-web/src/main/java/org/springframework/http/client/BufferingClientHttpRequestFactory.java4
-rw-r--r--spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java9
-rw-r--r--spring-web/src/test/java/org/springframework/http/HttpRangeTests.java49
-rw-r--r--spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java27
5 files changed, 102 insertions, 24 deletions
diff --git a/spring-web/src/main/java/org/springframework/http/HttpRange.java b/spring-web/src/main/java/org/springframework/http/HttpRange.java
index c11a57a0..ea3fef01 100644
--- a/spring-web/src/main/java/org/springframework/http/HttpRange.java
+++ b/spring-web/src/main/java/org/springframework/http/HttpRange.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2016 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.
@@ -43,6 +43,9 @@ import org.springframework.util.StringUtils;
*/
public abstract class HttpRange {
+ /** Maximum ranges per request. */
+ private static final int MAX_RANGES = 100;
+
private static final String BYTE_RANGE_PREFIX = "bytes=";
@@ -58,16 +61,23 @@ public abstract class HttpRange {
// Note: custom InputStreamResource subclasses could provide a pre-calculated content length!
Assert.isTrue(resource.getClass() != InputStreamResource.class,
"Cannot convert an InputStreamResource to a ResourceRegion");
+ long contentLength = getLengthFor(resource);
+ Assert.isTrue(contentLength > 0, "Resource content length should be > 0");
+ long start = getRangeStart(contentLength);
+ long end = getRangeEnd(contentLength);
+ return new ResourceRegion(resource, start, end - start + 1);
+ }
+
+ private static long getLengthFor(Resource resource) {
+ long contentLength;
try {
- long contentLength = resource.contentLength();
+ contentLength = resource.contentLength();
Assert.isTrue(contentLength > 0, "Resource content length should be > 0");
- long start = getRangeStart(contentLength);
- long end = getRangeEnd(contentLength);
- return new ResourceRegion(resource, start, end - start + 1);
}
catch (IOException ex) {
- throw new IllegalArgumentException("Failed to convert Resource to ResourceRegion", ex);
+ throw new IllegalArgumentException("Failed to obtain Resource content length", ex);
}
+ return contentLength;
}
/**
@@ -121,7 +131,8 @@ public abstract class HttpRange {
* <p>This method can be used to parse an {@code Range} header.
* @param ranges the string to parse
* @return the list of ranges
- * @throws IllegalArgumentException if the string cannot be parsed
+ * @throws IllegalArgumentException if the string cannot be parsed, or if
+ * the number of ranges is greater than 100.
*/
public static List<HttpRange> parseRanges(String ranges) {
if (!StringUtils.hasLength(ranges)) {
@@ -133,6 +144,7 @@ public abstract class HttpRange {
ranges = ranges.substring(BYTE_RANGE_PREFIX.length());
String[] tokens = StringUtils.tokenizeToStringArray(ranges, ",");
+ Assert.isTrue(tokens.length <= MAX_RANGES, "Too many ranges " + tokens.length);
List<HttpRange> result = new ArrayList<HttpRange>(tokens.length);
for (String token : tokens) {
result.add(parseRange(token));
@@ -169,6 +181,8 @@ public abstract class HttpRange {
* @param resource the resource to select the regions from
* @return the list of regions for the given resource
* @since 4.3
+ * @throws IllegalArgumentException if the sum of all ranges exceeds the
+ * resource length.
*/
public static List<ResourceRegion> toResourceRegions(List<HttpRange> ranges, Resource resource) {
if (CollectionUtils.isEmpty(ranges)) {
@@ -178,6 +192,15 @@ public abstract class HttpRange {
for (HttpRange range : ranges) {
regions.add(range.toResourceRegion(resource));
}
+ if (ranges.size() > 1) {
+ long length = getLengthFor(resource);
+ long total = 0;
+ for (ResourceRegion region : regions) {
+ total += region.getCount();
+ }
+ Assert.isTrue(total < length, "The sum of all ranges (" + total + ") " +
+ "should be less than the resource length (" + length + ")");
+ }
return regions;
}
diff --git a/spring-web/src/main/java/org/springframework/http/client/BufferingClientHttpRequestFactory.java b/spring-web/src/main/java/org/springframework/http/client/BufferingClientHttpRequestFactory.java
index 3f3b4185..ec251fdf 100644
--- a/spring-web/src/main/java/org/springframework/http/client/BufferingClientHttpRequestFactory.java
+++ b/spring-web/src/main/java/org/springframework/http/client/BufferingClientHttpRequestFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2015 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.
@@ -26,7 +26,7 @@ import org.springframework.http.HttpMethod;
* all outgoing and incoming streams in memory.
*
* <p>Using this wrapper allows for multiple reads of the
- * @linkplain ClientHttpResponse#getBody() response body}.
+ * {@linkplain ClientHttpResponse#getBody() response body}.
*
* @author Arjen Poutsma
* @since 3.1
diff --git a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java
index 877f6094..fc04869c 100644
--- a/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java
+++ b/spring-web/src/main/java/org/springframework/web/context/request/ServletWebRequest.java
@@ -1,11 +1,11 @@
/*
- * Copyright 2002-2017 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.
* You may obtain a copy of the License at
*
- * http://www.apache.org/licenses/LICENSE-2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -63,13 +63,13 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
private static final List<String> SAFE_METHODS = Arrays.asList("GET", "HEAD");
/**
- * Pattern matching ETag multiple field values in headers such as "If-Match", "If-None-Match"
+ * Pattern matching ETag multiple field values in headers such as "If-Match", "If-None-Match".
* @see <a href="https://tools.ietf.org/html/rfc7232#section-2.3">Section 2.3 of RFC 7232</a>
*/
private static final Pattern ETAG_HEADER_VALUE_PATTERN = Pattern.compile("\\*|\\s*((W\\/)?(\"[^\"]*\"))\\s*,?");
/**
- * Date formats as specified in the HTTP RFC
+ * Date formats as specified in the HTTP RFC.
* @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.1.1">Section 7.1.1.1 of RFC 7231</a>
*/
private static final String[] DATE_FORMATS = new String[] {
@@ -233,7 +233,6 @@ public class ServletWebRequest extends ServletRequestAttributes implements Nativ
}
// Update response
-
boolean isHttpGetOrHead = SAFE_METHODS.contains(getRequest().getMethod());
if (this.notModified) {
response.setStatus(isHttpGetOrHead ?
diff --git a/spring-web/src/test/java/org/springframework/http/HttpRangeTests.java b/spring-web/src/test/java/org/springframework/http/HttpRangeTests.java
index fe672951..8445cc66 100644
--- a/spring-web/src/test/java/org/springframework/http/HttpRangeTests.java
+++ b/spring-web/src/test/java/org/springframework/http/HttpRangeTests.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 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.
@@ -18,6 +18,7 @@ package org.springframework.http;
import java.io.IOException;
import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -102,6 +103,31 @@ public class HttpRangeTests {
}
@Test
+ public void parseRangesValidations() {
+
+ // 1. At limit..
+ StringBuilder sb = new StringBuilder("bytes=0-0");
+ for (int i=0; i < 99; i++) {
+ sb.append(",").append(i).append("-").append(i + 1);
+ }
+ List<HttpRange> ranges = HttpRange.parseRanges(sb.toString());
+ assertEquals(100, ranges.size());
+
+ // 2. Above limit..
+ sb = new StringBuilder("bytes=0-0");
+ for (int i=0; i < 100; i++) {
+ sb.append(",").append(i).append("-").append(i + 1);
+ }
+ try {
+ HttpRange.parseRanges(sb.toString());
+ fail();
+ }
+ catch (IllegalArgumentException ex) {
+ // Expected
+ }
+ }
+
+ @Test
public void rangeToString() {
List<HttpRange> ranges = new ArrayList<>();
ranges.add(HttpRange.createByteRange(0, 499));
@@ -145,4 +171,25 @@ public class HttpRangeTests {
range.toResourceRegion(resource);
}
+ @Test
+ public void toResourceRegionsValidations() {
+ byte[] bytes = "12345".getBytes(StandardCharsets.UTF_8);
+ ByteArrayResource resource = new ByteArrayResource(bytes);
+
+ // 1. Below full length
+ List<HttpRange> ranges = HttpRange.parseRanges("bytes=0-1,2-3");
+ List<ResourceRegion> regions = HttpRange.toResourceRegions(ranges, resource);
+ assertEquals(2, regions.size());
+
+ // 2. At full length
+ ranges = HttpRange.parseRanges("bytes=0-1,2-4");
+ try {
+ HttpRange.toResourceRegions(ranges, resource);
+ fail();
+ }
+ catch (IllegalArgumentException ex) {
+ // Expected..
+ }
+ }
+
}
diff --git a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java
index c1b99345..733a2588 100644
--- a/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java
+++ b/spring-web/src/test/java/org/springframework/mock/web/test/MockHttpServletRequest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2002-2017 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.
@@ -101,7 +101,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
new BufferedReader(new StringReader(""));
/**
- * Date formats as specified in the HTTP RFC
+ * Date formats as specified in the HTTP RFC.
* @see <a href="https://tools.ietf.org/html/rfc7231#section-7.1.1.1">Section 7.1.1.1 of RFC 7231</a>
*/
private static final String[] DATE_FORMATS = new String[] {
@@ -510,7 +510,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
}
/**
- * Adds all provided parameters <strong>without</strong> replacing any
+ * Add all provided parameters <strong>without</strong> replacing any
* existing values. To replace existing values, use
* {@link #setParameters(java.util.Map)}.
*/
@@ -540,7 +540,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
}
/**
- * Removes all existing parameters.
+ * Remove all existing parameters.
*/
public void removeAllParameters() {
this.parameters.clear();
@@ -702,8 +702,8 @@ public class MockHttpServletRequest implements HttpServletRequest {
/**
* Set the list of preferred locales, in descending order, effectively replacing
* any existing locales.
- * @see #addPreferredLocale
* @since 3.2
+ * @see #addPreferredLocale
*/
public void setPreferredLocales(List<Locale> locales) {
Assert.notEmpty(locales, "Locale list must not be empty");
@@ -890,9 +890,9 @@ public class MockHttpServletRequest implements HttpServletRequest {
}
/**
- * Add a header entry for the given name.
- * <p>While this method can take any {@code Object} as a parameter, it
- * is recommended to use the following types:
+ * Add an HTTP header entry for the given name.
+ * <p>While this method can take any {@code Object} as a parameter,
+ * it is recommended to use the following types:
* <ul>
* <li>String or any Object to be converted using {@code toString()}; see {@link #getHeader}.</li>
* <li>String, Number, or Date for date headers; see {@link #getDateHeader}.</li>
@@ -932,6 +932,15 @@ public class MockHttpServletRequest implements HttpServletRequest {
}
/**
+ * Remove already registered entries for the specified HTTP header, if any.
+ * @since 4.3.20
+ */
+ public void removeHeader(String name) {
+ Assert.notNull(name, "Header name must not be null");
+ this.headers.remove(name);
+ }
+
+ /**
* Return the long timestamp for the date header with the given {@code name}.
* <p>If the internal value representation is a String, this method will try
* to parse it as a date using the supported date formats:
@@ -1164,7 +1173,7 @@ public class MockHttpServletRequest implements HttpServletRequest {
public String changeSessionId() {
Assert.isTrue(this.session != null, "The request does not have a session");
if (this.session instanceof MockHttpSession) {
- return ((MockHttpSession) session).changeSessionId();
+ return ((MockHttpSession) this.session).changeSessionId();
}
return this.session.getId();
}