1
1
/*
2
- * Copyright 2002-2017 the original author or authors.
2
+ * Copyright 2002-2018 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
44
44
*/
45
45
public abstract class HttpRange {
46
46
47
+ /** Maximum ranges per request. */
48
+ private static final int MAX_RANGES = 100 ;
49
+
47
50
private static final String BYTE_RANGE_PREFIX = "bytes=" ;
48
51
49
52
@@ -59,16 +62,22 @@ public ResourceRegion toResourceRegion(Resource resource) {
59
62
// Note: custom InputStreamResource subclasses could provide a pre-calculated content length!
60
63
Assert .isTrue (resource .getClass () != InputStreamResource .class ,
61
64
"Cannot convert an InputStreamResource to a ResourceRegion" );
65
+ long contentLength = getLengthFor (resource );
66
+ long start = getRangeStart (contentLength );
67
+ long end = getRangeEnd (contentLength );
68
+ return new ResourceRegion (resource , start , end - start + 1 );
69
+ }
70
+
71
+ private static long getLengthFor (Resource resource ) {
72
+ long contentLength ;
62
73
try {
63
- long contentLength = resource .contentLength ();
74
+ contentLength = resource .contentLength ();
64
75
Assert .isTrue (contentLength > 0 , "Resource content length should be > 0" );
65
- long start = getRangeStart (contentLength );
66
- long end = getRangeEnd (contentLength );
67
- return new ResourceRegion (resource , start , end - start + 1 );
68
76
}
69
77
catch (IOException ex ) {
70
- throw new IllegalArgumentException ("Failed to convert Resource to ResourceRegion " , ex );
78
+ throw new IllegalArgumentException ("Failed to obtain Resource content length " , ex );
71
79
}
80
+ return contentLength ;
72
81
}
73
82
74
83
/**
@@ -122,7 +131,8 @@ public static HttpRange createSuffixRange(long suffixLength) {
122
131
* <p>This method can be used to parse an {@code Range} header.
123
132
* @param ranges the string to parse
124
133
* @return the list of ranges
125
- * @throws IllegalArgumentException if the string cannot be parsed
134
+ * @throws IllegalArgumentException if the string cannot be parsed, or if
135
+ * the number of ranges is greater than 100.
126
136
*/
127
137
public static List <HttpRange > parseRanges (@ Nullable String ranges ) {
128
138
if (!StringUtils .hasLength (ranges )) {
@@ -134,6 +144,7 @@ public static List<HttpRange> parseRanges(@Nullable String ranges) {
134
144
ranges = ranges .substring (BYTE_RANGE_PREFIX .length ());
135
145
136
146
String [] tokens = StringUtils .tokenizeToStringArray (ranges , "," );
147
+ Assert .isTrue (tokens .length <= MAX_RANGES , () -> "Too many ranges " + tokens .length );
137
148
List <HttpRange > result = new ArrayList <>(tokens .length );
138
149
for (String token : tokens ) {
139
150
result .add (parseRange (token ));
@@ -169,6 +180,8 @@ else if (dashIdx == 0) {
169
180
* @param ranges the list of ranges
170
181
* @param resource the resource to select the regions from
171
182
* @return the list of regions for the given resource
183
+ * @throws IllegalArgumentException if the sum of all ranges exceeds the
184
+ * resource length.
172
185
* @since 4.3
173
186
*/
174
187
public static List <ResourceRegion > toResourceRegions (List <HttpRange > ranges , Resource resource ) {
@@ -179,6 +192,13 @@ public static List<ResourceRegion> toResourceRegions(List<HttpRange> ranges, Res
179
192
for (HttpRange range : ranges ) {
180
193
regions .add (range .toResourceRegion (resource ));
181
194
}
195
+ if (ranges .size () > 1 ) {
196
+ long length = getLengthFor (resource );
197
+ long total = regions .stream ().map (ResourceRegion ::getCount ).reduce (0L , (count , sum ) -> sum + count );
198
+ Assert .isTrue (total < length ,
199
+ () -> "The sum of all ranges (" + total + ") " +
200
+ "should be less than the resource length (" + length + ")" );
201
+ }
182
202
return regions ;
183
203
}
184
204
0 commit comments