| 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 * A [VirtualDirectory] can serve files and directory-listing from a root path, | 8 * A [VirtualDirectory] can serve files and directory-listing from a root path, |
| 9 * to [HttpRequest]s. | 9 * to [HttpRequest]s. |
| 10 * | 10 * |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 71 return _serveErrorPage(HttpStatus.NOT_FOUND, request); | 71 return _serveErrorPage(HttpStatus.NOT_FOUND, request); |
| 72 } | 72 } |
| 73 | 73 |
| 74 _locateResource(new Path('.'), path.segments()) | 74 _locateResource(new Path('.'), path.segments()) |
| 75 .then((entity) { | 75 .then((entity) { |
| 76 if (entity == null) { | 76 if (entity == null) { |
| 77 _serveErrorPage(HttpStatus.NOT_FOUND, request); | 77 _serveErrorPage(HttpStatus.NOT_FOUND, request); |
| 78 return; | 78 return; |
| 79 } | 79 } |
| 80 if (entity is File) { | 80 if (entity is File) { |
| 81 entity.openRead().pipe(request.response).catchError((_) {}); | 81 _serveFile(entity, request); |
| 82 } else { | 82 } else { |
| 83 _serveErrorPage(HttpStatus.NOT_FOUND, request); | 83 _serveErrorPage(HttpStatus.NOT_FOUND, request); |
| 84 } | 84 } |
| 85 }); | 85 }); |
| 86 } | 86 } |
| 87 | 87 |
| 88 Future<FileSystemEntity> _locateResource(Path path, | 88 Future<FileSystemEntity> _locateResource(Path path, |
| 89 Iterable<String> segments) { | 89 Iterable<String> segments) { |
| 90 Path fullPath() => new Path(root).join(path); | 90 Path fullPath() => new Path(root).join(path); |
| 91 return FileSystemEntity.type(fullPath().toNativePath(), followLinks: false) | 91 return FileSystemEntity.type(fullPath().toNativePath(), followLinks: false) |
| (...skipping 28 matching lines...) Expand all Loading... |
| 120 segments.skip(1)); | 120 segments.skip(1)); |
| 121 }); | 121 }); |
| 122 } | 122 } |
| 123 break; | 123 break; |
| 124 } | 124 } |
| 125 // Return `null` on fall-through, to indicate NOT_FOUND. | 125 // Return `null` on fall-through, to indicate NOT_FOUND. |
| 126 return null; | 126 return null; |
| 127 }); | 127 }); |
| 128 } | 128 } |
| 129 | 129 |
| 130 void _serveFile(File file, HttpRequest request) { |
| 131 var response = request.response; |
| 132 // TODO(ajohnsen): Set up Zone support for these errors. |
| 133 file.lastModified().then((lastModified) { |
| 134 if (request.headers.ifModifiedSince != null && |
| 135 !lastModified.isAfter(request.headers.ifModifiedSince)) { |
| 136 response.statusCode = HttpStatus.NOT_MODIFIED; |
| 137 response.close(); |
| 138 return; |
| 139 } |
| 140 |
| 141 response.headers.set(HttpHeaders.LAST_MODIFIED, lastModified); |
| 142 response.headers.set(HttpHeaders.ACCEPT_RANGES, "bytes"); |
| 143 |
| 144 if (request.method == 'HEAD') { |
| 145 response.close(); |
| 146 return; |
| 147 } |
| 148 |
| 149 return file.length().then((length) { |
| 150 String range = request.headers.value("range"); |
| 151 if (range != null) { |
| 152 // We only support one range, where the standard support several. |
| 153 Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range); |
| 154 // If the range header have the right format, handle it. |
| 155 if (matches != null) { |
| 156 // Serve sub-range. |
| 157 int start; |
| 158 int end; |
| 159 if (matches[1].isEmpty) { |
| 160 start = matches[2].isEmpty ? |
| 161 length : |
| 162 length - int.parse(matches[2]); |
| 163 end = length; |
| 164 } else { |
| 165 start = int.parse(matches[1]); |
| 166 end = matches[2].isEmpty ? length : int.parse(matches[2]) + 1; |
| 167 } |
| 168 |
| 169 // Override Content-Length with the actual bytes sent. |
| 170 response.headers.set(HttpHeaders.CONTENT_LENGTH, end - start); |
| 171 |
| 172 // Set 'Partial Content' status code. |
| 173 response.statusCode = HttpStatus.PARTIAL_CONTENT; |
| 174 response.headers.set(HttpHeaders.CONTENT_RANGE, |
| 175 "bytes $start-${end - 1}/$length"); |
| 176 |
| 177 // Pipe the 'range' of the file. |
| 178 file.openRead(start, end).pipe(response).catchError((_) {}); |
| 179 return; |
| 180 } |
| 181 } |
| 182 |
| 183 file.openRead().pipe(response).catchError((_) {}); |
| 184 }); |
| 185 }).catchError((_) { |
| 186 response.close(); |
| 187 }); |
| 188 } |
| 189 |
| 130 void _serveErrorPage(int error, HttpRequest request) { | 190 void _serveErrorPage(int error, HttpRequest request) { |
| 131 request.response.statusCode = error; | 191 request.response.statusCode = error; |
| 132 request.response.close(); | 192 request.response.close(); |
| 133 } | 193 } |
| 134 } | 194 } |
| OLD | NEW |