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 |