Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(361)

Side by Side Diff: pkg/http_server/lib/src/virtual_directory.dart

Issue 721213002: Fix a number of issues with the Range header handling for serving files (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix test Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/http_server/test/http_mock.dart » ('j') | pkg/http_server/test/http_mock.dart » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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 }
OLDNEW
« no previous file with comments | « no previous file | pkg/http_server/test/http_mock.dart » ('j') | pkg/http_server/test/http_mock.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698