| 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 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 202 if (request.headers.ifModifiedSince != null && | 202 if (request.headers.ifModifiedSince != null && |
| 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') { | |
| 213 response.close(); | |
| 214 return null; | |
| 215 } | |
| 216 | |
| 217 return file.length().then((length) { | 212 return file.length().then((length) { |
| 218 String range = request.headers.value("range"); | 213 String range = request.headers.value(HttpHeaders.RANGE); |
| 219 if (range != null) { | 214 if (range != null) { |
| 220 // We only support one range, where the standard support several. | 215 // We only support one range, where the standard support several. |
| 221 Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range); | 216 Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range); |
| 222 // If the range header have the right format, handle it. | 217 // If the range header have the right format, handle it. |
| 223 if (matches != null) { | 218 if (matches != null && |
| 219 (matches[1].isNotEmpty || matches[2].isNotEmpty)) { |
| 224 // Serve sub-range. | 220 // Serve sub-range. |
| 225 int start; | 221 int start; // First byte position - inclusive. |
| 226 int end; | 222 int end; // Last byte position - inclusive. |
| 227 if (matches[1].isEmpty) { | 223 if (matches[1].isEmpty) { |
| 228 start = matches[2].isEmpty ? | 224 start = length - int.parse(matches[2]); |
| 229 length : | 225 if (start < 0) start = 0; |
| 230 length - int.parse(matches[2]); | 226 end = length - 1; |
| 231 end = length; | |
| 232 } else { | 227 } else { |
| 233 start = int.parse(matches[1]); | 228 start = int.parse(matches[1]); |
| 234 end = matches[2].isEmpty ? length : int.parse(matches[2]) + 1; | 229 end = matches[2].isEmpty ? length - 1: int.parse(matches[2]); |
| 235 } | 230 } |
| 231 // If the range is syntactically invalid the Range header |
| 232 // MUST be ignored (RFC 2616 section 14.35.1). |
| 233 if (start <= end) { |
| 234 if (end >= length) { |
| 235 end = length - 1; |
| 236 } |
| 236 | 237 |
| 237 // Override Content-Length with the actual bytes sent. | 238 if (start >= length) { |
| 238 response.headers.set(HttpHeaders.CONTENT_LENGTH, end - start); | 239 response |
| 240 ..statusCode = HttpStatus.REQUESTED_RANGE_NOT_SATISFIABLE |
| 241 ..close(); |
| 242 return; |
| 243 } |
| 239 | 244 |
| 240 // Set 'Partial Content' status code. | 245 // Override Content-Length with the actual bytes sent. |
| 241 response.statusCode = HttpStatus.PARTIAL_CONTENT; | 246 response.headers.set(HttpHeaders.CONTENT_LENGTH, end - start + 1); |
| 242 response.headers.set(HttpHeaders.CONTENT_RANGE, | |
| 243 "bytes $start-${end - 1}/$length"); | |
| 244 | 247 |
| 245 // Pipe the 'range' of the file. | 248 // Set 'Partial Content' status code. |
| 246 file.openRead(start, end) | 249 response |
| 247 .pipe(new _VirtualDirectoryFileStream(response, file.path)) | 250 ..statusCode = HttpStatus.PARTIAL_CONTENT |
| 248 .catchError((_) { | 251 ..headers.set(HttpHeaders.CONTENT_RANGE, |
| 249 // TODO(kevmoo): log errors | 252 'bytes $start-$end/$length'); |
| 250 }); | 253 |
| 251 return; | 254 // Pipe the 'range' of the file. |
| 255 if (request.method == 'HEAD') { |
| 256 response.close(); |
| 257 } else { |
| 258 file.openRead(start, end + 1) |
| 259 .pipe(new _VirtualDirectoryFileStream(response, file.path)) |
| 260 .catchError((_) { |
| 261 // TODO(kevmoo): log errors |
| 262 }); |
| 263 } |
| 264 return; |
| 265 } |
| 252 } | 266 } |
| 253 } | 267 } |
| 254 | 268 |
| 255 file.openRead() | 269 response.headers.set(HttpHeaders.CONTENT_LENGTH, length); |
| 256 .pipe(new _VirtualDirectoryFileStream(response, file.path)) | 270 if (request.method == 'HEAD') { |
| 257 .catchError((_) { | 271 response.close(); |
| 258 // TODO(kevmoo): log errors | 272 } else { |
| 259 }); | 273 file.openRead() |
| 274 .pipe(new _VirtualDirectoryFileStream(response, file.path)) |
| 275 .catchError((_) { |
| 276 // TODO(kevmoo): log errors |
| 277 }); |
| 278 } |
| 260 }); | 279 }); |
| 261 }).catchError((_) { | 280 }).catchError((_) { |
| 262 response.statusCode = HttpStatus.NOT_FOUND; | 281 response.statusCode = HttpStatus.NOT_FOUND; |
| 263 response.close(); | 282 response.close(); |
| 264 }); | 283 }); |
| 265 } | 284 } |
| 266 | 285 |
| 267 void _serveDirectory(Directory dir, HttpRequest request) { | 286 void _serveDirectory(Directory dir, HttpRequest request) { |
| 268 if (_dirCallback != null) { | 287 if (_dirCallback != null) { |
| 269 _dirCallback(dir, request); | 288 _dirCallback(dir, request); |
| (...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 444 | 463 |
| 445 Future close() => new Future.value(); | 464 Future close() => new Future.value(); |
| 446 | 465 |
| 447 void setMimeType(List<int> bytes) { | 466 void setMimeType(List<int> bytes) { |
| 448 var mimeType = lookupMimeType(path, headerBytes: bytes); | 467 var mimeType = lookupMimeType(path, headerBytes: bytes); |
| 449 if (mimeType != null) { | 468 if (mimeType != null) { |
| 450 response.headers.contentType = ContentType.parse(mimeType); | 469 response.headers.contentType = ContentType.parse(mimeType); |
| 451 } | 470 } |
| 452 } | 471 } |
| 453 } | 472 } |
| OLD | NEW |