OLD | NEW |
---|---|
1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 part of http_server; | 5 part of http_server; |
6 | 6 |
7 | 7 |
8 // Used for signal a directory redirecting, where a tailing slash is missing. | 8 // Used for signal a directory redirecting, where a tailing slash is missing. |
9 class _DirectoryRedirect { | 9 class _DirectoryRedirect { |
10 const _DirectoryRedirect(); | 10 const _DirectoryRedirect(); |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
203 !lastModified.isAfter(request.headers.ifModifiedSince)) { | 203 !lastModified.isAfter(request.headers.ifModifiedSince)) { |
204 response.statusCode = HttpStatus.NOT_MODIFIED; | 204 response.statusCode = HttpStatus.NOT_MODIFIED; |
205 response.close(); | 205 response.close(); |
206 return null; | 206 return null; |
207 } | 207 } |
208 | 208 |
209 response.headers.set(HttpHeaders.LAST_MODIFIED, lastModified); | 209 response.headers.set(HttpHeaders.LAST_MODIFIED, lastModified); |
210 response.headers.set(HttpHeaders.ACCEPT_RANGES, "bytes"); | 210 response.headers.set(HttpHeaders.ACCEPT_RANGES, "bytes"); |
211 | 211 |
212 if (request.method == 'HEAD') { | 212 if (request.method == 'HEAD') { |
213 response.close(); | 213 response.close(); |
kustermann
2014/11/14 13:25:26
Maybe set the content-length header in case of HEA
Søren Gjesse
2014/11/14 15:27:29
Yes, done. Also added it for serving the whole fil
| |
214 return null; | 214 return; |
215 } | 215 } |
216 | 216 |
217 return file.length().then((length) { | 217 return file.length().then((length) { |
218 String range = request.headers.value("range"); | 218 String range = request.headers.value(HttpHeaders.RANGE); |
219 if (range != null) { | 219 if (range != null) { |
Anders Johnsen
2014/11/13 16:12:31
This range handling is getting quite complicated.
Søren Gjesse
2014/11/14 15:27:29
I will postpone this to a separate CL, as I will a
| |
220 // We only support one range, where the standard support several. | 220 // We only support one range, where the standard support several. |
221 Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range); | 221 Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range); |
222 // If the range header have the right format, handle it. | 222 // If the range header have the right format, handle it. |
223 if (matches != null) { | 223 if (matches != null && |
224 (matches[1].isNotEmpty || matches[2].isNotEmpty)) { | |
224 // Serve sub-range. | 225 // Serve sub-range. |
225 int start; | 226 int start; // First byte position - inclusive. |
226 int end; | 227 int end; // Last byte position - inclusive. |
227 if (matches[1].isEmpty) { | 228 if (matches[1].isEmpty) { |
228 start = matches[2].isEmpty ? | 229 start = length - int.parse(matches[2]); |
229 length : | 230 if (start < 0) start = 0; |
230 length - int.parse(matches[2]); | 231 end = length - 1; |
231 end = length; | |
232 } else { | 232 } else { |
233 start = int.parse(matches[1]); | 233 start = int.parse(matches[1]); |
234 end = matches[2].isEmpty ? length : int.parse(matches[2]) + 1; | 234 end = matches[2].isEmpty ? length - 1: int.parse(matches[2]); |
235 } | 235 } |
236 // If the range is syntactically invalid the Range header | |
237 // MUST be ignored (RFC 2616 section 14.35.1). | |
238 if (start <= end) { | |
239 if (end > length) { | |
kustermann
2014/11/14 13:25:26
Since end is inclusive, shouldn't this be "end >=
Søren Gjesse
2014/11/14 15:27:29
Yes. Good catch. Added additional test.
| |
240 end = length - 1; | |
241 } | |
236 | 242 |
237 // Override Content-Length with the actual bytes sent. | 243 if (start >= length) { |
238 response.headers.set(HttpHeaders.CONTENT_LENGTH, end - start); | 244 response.statusCode = |
245 HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE; | |
246 response.close(); | |
247 return; | |
kustermann
2014/11/14 13:25:27
Maybe use cascades:
response
..statusCode =
Søren Gjesse
2014/11/14 15:27:29
Done.
| |
248 } | |
239 | 249 |
240 // Set 'Partial Content' status code. | 250 // Override Content-Length with the actual bytes sent. |
241 response.statusCode = HttpStatus.PARTIAL_CONTENT; | 251 response.headers.set(HttpHeaders.CONTENT_LENGTH, end - start + 1); |
242 response.headers.set(HttpHeaders.CONTENT_RANGE, | |
243 "bytes $start-${end - 1}/$length"); | |
244 | 252 |
245 // Pipe the 'range' of the file. | 253 // Set 'Partial Content' status code. |
246 file.openRead(start, end) | 254 response.statusCode = HttpStatus.PARTIAL_CONTENT; |
247 .pipe(new _VirtualDirectoryFileStream(response, file.path)) | 255 response.headers.set(HttpHeaders.CONTENT_RANGE, |
248 .catchError((_) { | 256 "bytes $start-$end/$length"); |
kustermann
2014/11/14 13:25:27
Cascades?
Søren Gjesse
2014/11/14 15:27:29
Done.
| |
249 // TODO(kevmoo): log errors | 257 |
250 }); | 258 // Pipe the 'range' of the file. |
251 return; | 259 file.openRead(start, end + 1) |
260 .pipe(new _VirtualDirectoryFileStream(response, file.path)) | |
261 .catchError((_) { | |
262 // TODO(kevmoo): log errors | |
263 }); | |
264 return; | |
265 } | |
252 } | 266 } |
253 } | 267 } |
254 | 268 |
255 file.openRead() | 269 file.openRead() |
256 .pipe(new _VirtualDirectoryFileStream(response, file.path)) | 270 .pipe(new _VirtualDirectoryFileStream(response, file.path)) |
257 .catchError((_) { | 271 .catchError((_) { |
258 // TODO(kevmoo): log errors | 272 // TODO(kevmoo): log errors |
259 }); | 273 }); |
260 }); | 274 }); |
261 }).catchError((_) { | 275 }).catchError((_) { |
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
444 | 458 |
445 Future close() => new Future.value(); | 459 Future close() => new Future.value(); |
446 | 460 |
447 void setMimeType(List<int> bytes) { | 461 void setMimeType(List<int> bytes) { |
448 var mimeType = lookupMimeType(path, headerBytes: bytes); | 462 var mimeType = lookupMimeType(path, headerBytes: bytes); |
449 if (mimeType != null) { | 463 if (mimeType != null) { |
450 response.headers.contentType = ContentType.parse(mimeType); | 464 response.headers.contentType = ContentType.parse(mimeType); |
451 } | 465 } |
452 } | 466 } |
453 } | 467 } |
OLD | NEW |