Index: pkg/http_server/lib/src/virtual_directory.dart |
diff --git a/pkg/http_server/lib/src/virtual_directory.dart b/pkg/http_server/lib/src/virtual_directory.dart |
index 6dfec5d9124d7644a9e4479edcf331e0833a7754..d82fd992c6e30e6bc287f83b15920e3a49e3646e 100644 |
--- a/pkg/http_server/lib/src/virtual_directory.dart |
+++ b/pkg/http_server/lib/src/virtual_directory.dart |
@@ -209,54 +209,73 @@ class VirtualDirectory { |
response.headers.set(HttpHeaders.LAST_MODIFIED, lastModified); |
response.headers.set(HttpHeaders.ACCEPT_RANGES, "bytes"); |
- if (request.method == 'HEAD') { |
- response.close(); |
- return null; |
- } |
- |
return file.length().then((length) { |
- String range = request.headers.value("range"); |
+ String range = request.headers.value(HttpHeaders.RANGE); |
if (range != null) { |
// We only support one range, where the standard support several. |
Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range); |
// If the range header have the right format, handle it. |
- if (matches != null) { |
+ if (matches != null && |
+ (matches[1].isNotEmpty || matches[2].isNotEmpty)) { |
// Serve sub-range. |
- int start; |
- int end; |
+ int start; // First byte position - inclusive. |
+ int end; // Last byte position - inclusive. |
if (matches[1].isEmpty) { |
- start = matches[2].isEmpty ? |
- length : |
- length - int.parse(matches[2]); |
- end = length; |
+ start = length - int.parse(matches[2]); |
+ if (start < 0) start = 0; |
+ end = length - 1; |
} else { |
start = int.parse(matches[1]); |
- end = matches[2].isEmpty ? length : int.parse(matches[2]) + 1; |
+ end = matches[2].isEmpty ? length - 1: int.parse(matches[2]); |
} |
+ // If the range is syntactically invalid the Range header |
+ // MUST be ignored (RFC 2616 section 14.35.1). |
+ if (start <= end) { |
+ if (end >= length) { |
+ end = length - 1; |
+ } |
- // Override Content-Length with the actual bytes sent. |
- response.headers.set(HttpHeaders.CONTENT_LENGTH, end - start); |
+ if (start >= length) { |
+ response |
+ ..statusCode = HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE |
+ ..close(); |
+ return; |
+ } |
- // Set 'Partial Content' status code. |
- response.statusCode = HttpStatus.PARTIAL_CONTENT; |
- response.headers.set(HttpHeaders.CONTENT_RANGE, |
- "bytes $start-${end - 1}/$length"); |
+ // Override Content-Length with the actual bytes sent. |
+ response.headers.set(HttpHeaders.CONTENT_LENGTH, end - start + 1); |
- // Pipe the 'range' of the file. |
- file.openRead(start, end) |
- .pipe(new _VirtualDirectoryFileStream(response, file.path)) |
- .catchError((_) { |
- // TODO(kevmoo): log errors |
- }); |
- return; |
+ // Set 'Partial Content' status code. |
+ response |
+ ..statusCode = HttpStatus.PARTIAL_CONTENT |
+ ..headers.set(HttpHeaders.CONTENT_RANGE, |
+ 'bytes $start-$end/$length'); |
+ |
+ // Pipe the 'range' of the file. |
+ if (request.method == 'HEAD') { |
+ response.close(); |
+ } else { |
+ file.openRead(start, end + 1) |
+ .pipe(new _VirtualDirectoryFileStream(response, file.path)) |
+ .catchError((_) { |
+ // TODO(kevmoo): log errors |
+ }); |
+ } |
+ return; |
+ } |
} |
} |
- file.openRead() |
- .pipe(new _VirtualDirectoryFileStream(response, file.path)) |
- .catchError((_) { |
- // TODO(kevmoo): log errors |
- }); |
+ response.headers.set(HttpHeaders.CONTENT_LENGTH, length); |
+ if (request.method == 'HEAD') { |
+ response.close(); |
+ } else { |
+ file.openRead() |
+ .pipe(new _VirtualDirectoryFileStream(response, file.path)) |
+ .catchError((_) { |
+ // TODO(kevmoo): log errors |
+ }); |
+ } |
}); |
}).catchError((_) { |
response.statusCode = HttpStatus.NOT_FOUND; |