27
27
import org .springframework .util .StringUtils ;
28
28
29
29
/**
30
- * Represents an HTTP (byte) range, as used in the {@code Range} header.
30
+ * Represents an HTTP (byte) range for use with the HTTP {@code " Range" } header.
31
31
*
32
32
* @author Arjen Poutsma
33
33
* @see <a href="http://tools.ietf.org/html/rfc7233">HTTP/1.1: Range Requests</a>
@@ -41,57 +41,51 @@ public abstract class HttpRange {
41
41
42
42
43
43
/**
44
- * Creates a {@code HttpRange} that ranges from the given position to the end of the
45
- * representation.
44
+ * Return the start of the range given the total length of a representation.
45
+ * @param length the length of the representation
46
+ * @return the start of this range for the representation
47
+ */
48
+ public abstract long getRangeStart (long length );
49
+
50
+ /**
51
+ * Return the end of the range (inclusive) given the total length of a representation.
52
+ * @param length the length of the representation
53
+ * @return the end of the range for the representation
54
+ */
55
+ public abstract long getRangeEnd (long length );
56
+
57
+
58
+ /**
59
+ * Create an {@code HttpRange} from the given position to the end.
46
60
* @param firstBytePos the first byte position
47
- * @return a byte range that ranges from {@code firstBytePos } till the end
61
+ * @return a byte range that ranges from {@code firstPos } till the end
48
62
* @see <a href="http://tools.ietf.org/html/rfc7233#section-2.1">Byte Ranges</a>
49
63
*/
50
64
public static HttpRange createByteRange (long firstBytePos ) {
51
65
return new ByteRange (firstBytePos , null );
52
66
}
53
67
54
68
/**
55
- * Creates a {@code HttpRange} that ranges from the given fist position to the given
56
- * last position.
69
+ * Create a {@code HttpRange} from the given fist to last position.
57
70
* @param firstBytePos the first byte position
58
71
* @param lastBytePos the last byte position
59
- * @return a byte range that ranges from {@code firstBytePos } till {@code lastBytePos }
72
+ * @return a byte range that ranges from {@code firstPos } till {@code lastPos }
60
73
* @see <a href="http://tools.ietf.org/html/rfc7233#section-2.1">Byte Ranges</a>
61
74
*/
62
75
public static HttpRange createByteRange (long firstBytePos , long lastBytePos ) {
63
- Assert .isTrue (firstBytePos <= lastBytePos ,
64
- "\" firstBytePost\" should be " + "less then or equal to \" lastBytePos\" " );
65
76
return new ByteRange (firstBytePos , lastBytePos );
66
77
}
67
78
68
79
/**
69
- * Creates a {@code HttpRange} that ranges over the last given number of bytes.
70
- * @param suffixLength the number of bytes
80
+ * Create an {@code HttpRange} that ranges over the last given number of bytes.
81
+ * @param suffixLength the number of bytes for the range
71
82
* @return a byte range that ranges over the last {@code suffixLength} number of bytes
72
83
* @see <a href="http://tools.ietf.org/html/rfc7233#section-2.1">Byte Ranges</a>
73
84
*/
74
85
public static HttpRange createSuffixRange (long suffixLength ) {
75
86
return new SuffixByteRange (suffixLength );
76
87
}
77
88
78
-
79
- /**
80
- * Return the start of this range, given the total length of the representation.
81
- * @param length the length of the representation.
82
- * @return the start of this range
83
- */
84
- public abstract long getRangeStart (long length );
85
-
86
- /**
87
- * Return the end of this range (inclusive), given the total length of the
88
- * representation.
89
- * @param length the length of the representation.
90
- * @return the end of this range
91
- */
92
- public abstract long getRangeEnd (long length );
93
-
94
-
95
89
/**
96
90
* Parse the given, comma-separated string into a list of {@code HttpRange} objects.
97
91
* <p>This method can be used to parse an {@code Range} header.
@@ -104,8 +98,7 @@ public static List<HttpRange> parseRanges(String ranges) {
104
98
return Collections .emptyList ();
105
99
}
106
100
if (!ranges .startsWith (BYTE_RANGE_PREFIX )) {
107
- throw new IllegalArgumentException ("Range \" " + ranges + "\" does not " +
108
- "start with \" " + BYTE_RANGE_PREFIX + "\" " );
101
+ throw new IllegalArgumentException ("Range '" + ranges + "' does not start with 'bytes='" );
109
102
}
110
103
ranges = ranges .substring (BYTE_RANGE_PREFIX .length ());
111
104
@@ -118,36 +111,25 @@ public static List<HttpRange> parseRanges(String ranges) {
118
111
}
119
112
120
113
private static HttpRange parseRange (String range ) {
121
- if (range == null ) {
122
- return null ;
123
- }
114
+ Assert .notNull (range );
124
115
int dashIdx = range .indexOf ('-' );
125
- if (dashIdx < 0 ) {
126
- throw new IllegalArgumentException ("Range '\" " + range + "\" does not" +
127
- "contain \" -\" " );
128
- }
129
- else if (dashIdx > 0 ) {
130
- // standard byte range, i.e. "bytes=0-500"
116
+ if (dashIdx > 0 ) {
131
117
long firstPos = Long .parseLong (range .substring (0 , dashIdx ));
132
- ByteRange byteRange ;
133
118
if (dashIdx < range .length () - 1 ) {
134
- long lastPos =
135
- Long .parseLong (range .substring (dashIdx + 1 , range .length ()));
136
- byteRange = new ByteRange (firstPos , lastPos );
119
+ Long lastPos = Long .parseLong (range .substring (dashIdx + 1 , range .length ()));
120
+ return new ByteRange (firstPos , lastPos );
137
121
}
138
122
else {
139
- byteRange = new ByteRange (firstPos , null );
123
+ return new ByteRange (firstPos , null );
140
124
}
141
- if (!byteRange .validate ()) {
142
- throw new IllegalArgumentException ("Invalid Range \" " + range + "\" " );
143
- }
144
- return byteRange ;
145
125
}
146
- else { // dashIdx == 0
147
- // suffix byte range, i.e. "bytes=-500"
126
+ else if (dashIdx == 0 ) {
148
127
long suffixLength = Long .parseLong (range .substring (1 ));
149
128
return new SuffixByteRange (suffixLength );
150
129
}
130
+ else {
131
+ throw new IllegalArgumentException ("Range '" + range + "' does not contain \" -\" " );
132
+ }
151
133
}
152
134
153
135
/**
@@ -157,6 +139,7 @@ else if (dashIdx > 0) {
157
139
* @return the string representation
158
140
*/
159
141
public static String toString (Collection <HttpRange > ranges ) {
142
+ Assert .notNull (ranges );
160
143
StringBuilder builder = new StringBuilder (BYTE_RANGE_PREFIX );
161
144
for (Iterator <HttpRange > iterator = ranges .iterator (); iterator .hasNext (); ) {
162
145
HttpRange range = iterator .next ();
@@ -177,6 +160,7 @@ public String toString() {
177
160
178
161
abstract void appendTo (StringBuilder builder );
179
162
163
+
180
164
/**
181
165
* Represents an HTTP/1.1 byte range, with a first and optional last position.
182
166
* @see <a href="http://tools.ietf.org/html/rfc7233#section-2.1">Byte Ranges</a>
@@ -189,11 +173,23 @@ private static class ByteRange extends HttpRange {
189
173
190
174
private final Long lastPos ;
191
175
176
+
192
177
private ByteRange (long firstPos , Long lastPos ) {
178
+ assertPositions (firstPos , lastPos );
193
179
this .firstPos = firstPos ;
194
180
this .lastPos = lastPos ;
195
181
}
196
182
183
+ private void assertPositions (long firstBytePos , Long lastBytePos ) {
184
+ if (firstBytePos < 0 ) {
185
+ throw new IllegalArgumentException ("Invalid firstPos=" + firstBytePos );
186
+ }
187
+ if (lastBytePos != null && lastBytePos < firstBytePos ) {
188
+ throw new IllegalArgumentException ("firstPost= " + firstBytePos +
189
+ " should be less then or equal to lastBytePosition=" + lastBytePos );
190
+ }
191
+ }
192
+
197
193
@ Override
198
194
public long getRangeStart (long length ) {
199
195
return this .firstPos ;
@@ -206,7 +202,6 @@ public long getRangeEnd(long length) {
206
202
}
207
203
else {
208
204
return length - 1 ;
209
-
210
205
}
211
206
}
212
207
@@ -219,18 +214,6 @@ void appendTo(StringBuilder builder) {
219
214
}
220
215
}
221
216
222
- boolean validate () {
223
- if (this .firstPos < 0 ) {
224
- return false ;
225
- }
226
- if (this .lastPos == null ) {
227
- return true ;
228
- }
229
- else {
230
- return this .firstPos <= this .lastPos ;
231
- }
232
- }
233
-
234
217
@ Override
235
218
public boolean equals (Object o ) {
236
219
if (this == o ) {
@@ -239,11 +222,8 @@ public boolean equals(Object o) {
239
222
if (!(o instanceof ByteRange )) {
240
223
return false ;
241
224
}
242
-
243
225
ByteRange other = (ByteRange ) o ;
244
-
245
- return this .firstPos == other .firstPos &&
246
- ObjectUtils .nullSafeEquals (this .lastPos , other .lastPos );
226
+ return this .firstPos == other .firstPos && ObjectUtils .nullSafeEquals (this .lastPos , other .lastPos );
247
227
}
248
228
249
229
@ Override
@@ -252,7 +232,6 @@ public int hashCode() {
252
232
hashCode = 31 * hashCode + ObjectUtils .nullSafeHashCode (this .lastPos );
253
233
return hashCode ;
254
234
}
255
-
256
235
}
257
236
258
237
/**
@@ -264,15 +243,14 @@ private static class SuffixByteRange extends HttpRange {
264
243
265
244
private final long suffixLength ;
266
245
246
+
267
247
private SuffixByteRange (long suffixLength ) {
248
+ if (suffixLength < 0 ) {
249
+ throw new IllegalArgumentException ("Invalid suffixLength=" + suffixLength );
250
+ }
268
251
this .suffixLength = suffixLength ;
269
252
}
270
253
271
- @ Override
272
- void appendTo (StringBuilder builder ) {
273
- builder .append ('-' );
274
- builder .append (this .suffixLength );
275
- }
276
254
277
255
@ Override
278
256
public long getRangeStart (long length ) {
@@ -289,6 +267,12 @@ public long getRangeEnd(long length) {
289
267
return length - 1 ;
290
268
}
291
269
270
+ @ Override
271
+ void appendTo (StringBuilder builder ) {
272
+ builder .append ('-' );
273
+ builder .append (this .suffixLength );
274
+ }
275
+
292
276
@ Override
293
277
public boolean equals (Object o ) {
294
278
if (this == o ) {
0 commit comments