Chromium Code Reviews| 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 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 74 } | 74 } |
| 75 | 75 |
| 76 _locateResource(new Path('.'), path.segments()) | 76 _locateResource(new Path('.'), path.segments()) |
| 77 .then((entity) { | 77 .then((entity) { |
| 78 if (entity == null) { | 78 if (entity == null) { |
| 79 _serveErrorPage(HttpStatus.NOT_FOUND, request); | 79 _serveErrorPage(HttpStatus.NOT_FOUND, request); |
| 80 return; | 80 return; |
| 81 } | 81 } |
| 82 if (entity is File) { | 82 if (entity is File) { |
| 83 _serveFile(entity, request); | 83 _serveFile(entity, request); |
| 84 } else if (entity is Directory) { | |
| 85 _serveDirectory(entity, request); | |
| 84 } else { | 86 } else { |
| 85 _serveErrorPage(HttpStatus.NOT_FOUND, request); | 87 _serveErrorPage(HttpStatus.NOT_FOUND, request); |
| 86 } | 88 } |
| 87 }); | 89 }); |
| 88 } | 90 } |
| 89 | 91 |
| 90 void setErrorPageHandler(void callback(HttpRequest request)) { | 92 void setErrorPageHandler(void callback(HttpRequest request)) { |
| 91 _errorCallback = callback; | 93 _errorCallback = callback; |
| 92 } | 94 } |
| 93 | 95 |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 115 case FileSystemEntityType.LINK: | 117 case FileSystemEntityType.LINK: |
| 116 if (followLinks) { | 118 if (followLinks) { |
| 117 return new Link.fromPath(fullPath()).target() | 119 return new Link.fromPath(fullPath()).target() |
| 118 .then((target) { | 120 .then((target) { |
| 119 var targetPath = new Path(target).canonicalize(); | 121 var targetPath = new Path(target).canonicalize(); |
| 120 if (targetPath.isAbsolute) return null; | 122 if (targetPath.isAbsolute) return null; |
| 121 targetPath = path.directoryPath.join(targetPath) | 123 targetPath = path.directoryPath.join(targetPath) |
| 122 .canonicalize(); | 124 .canonicalize(); |
| 123 if (targetPath.segments().isEmpty || | 125 if (targetPath.segments().isEmpty || |
| 124 targetPath.segments().first == '..') return null; | 126 targetPath.segments().first == '..') return null; |
| 127 if (segments.isEmpty) { | |
| 128 return _locateResource(targetPath, []); | |
| 129 } | |
| 125 return _locateResource(targetPath.append(segments.first), | 130 return _locateResource(targetPath.append(segments.first), |
| 126 segments.skip(1)); | 131 segments.skip(1)); |
| 127 }); | 132 }); |
| 128 } | 133 } |
| 129 break; | 134 break; |
| 130 } | 135 } |
| 131 // Return `null` on fall-through, to indicate NOT_FOUND. | 136 // Return `null` on fall-through, to indicate NOT_FOUND. |
| 132 return null; | 137 return null; |
| 133 }); | 138 }); |
| 134 } | 139 } |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 190 | 195 |
| 191 file.openRead() | 196 file.openRead() |
| 192 .pipe(new _VirtualDirectoryFileStream(response, file.path)) | 197 .pipe(new _VirtualDirectoryFileStream(response, file.path)) |
| 193 .catchError((_) {}); | 198 .catchError((_) {}); |
| 194 }); | 199 }); |
| 195 }).catchError((_) { | 200 }).catchError((_) { |
| 196 response.close(); | 201 response.close(); |
| 197 }); | 202 }); |
| 198 } | 203 } |
| 199 | 204 |
| 205 void _serveDirectory(Directory dir, HttpRequest request) { | |
|
Søren Gjesse
2013/06/26 10:19:11
Maybe add a callback for the directory page as we
| |
| 206 var response = request.response; | |
| 207 dir.stat().then((stats) { | |
| 208 if (request.headers.ifModifiedSince != null && | |
| 209 !stats.modified.isAfter(request.headers.ifModifiedSince)) { | |
| 210 response.statusCode = HttpStatus.NOT_MODIFIED; | |
| 211 response.close(); | |
| 212 return; | |
| 213 } | |
| 214 | |
| 215 response.headers.set(HttpHeaders.LAST_MODIFIED, stats.modified); | |
| 216 var path = request.uri.path; | |
| 217 var header = | |
| 218 '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" | |
| 219 http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | |
| 220 <html xmlns="http://www.w3.org/1999/xhtml"> | |
| 221 <head> | |
| 222 <title>Index of $path</title> | |
| 223 </head> | |
| 224 <body> | |
| 225 <h1>Index of $path</h1> | |
| 226 <table> | |
| 227 <tr> | |
| 228 <td>Name</td> | |
| 229 <td>Last modified</td> | |
| 230 <td>Size</td> | |
| 231 </tr> | |
| 232 '''; | |
| 233 var server = response.headers.value(HttpHeaders.SERVER); | |
| 234 if (server == null) server = ""; | |
| 235 var footer = | |
| 236 '''</table> | |
| 237 $server | |
| 238 </body> | |
| 239 </html> | |
| 240 '''; | |
| 241 | |
| 242 response.write(header); | |
| 243 | |
| 244 void add(String name, DateTime modified, var size) { | |
| 245 if (size == null) size = "-"; | |
| 246 if (modified == null) modified = ""; | |
| 247 var p = new Path(path).append(name).canonicalize().toString(); | |
| 248 var entry = | |
| 249 ''' <tr> | |
| 250 <td><a href="$p">$name</a></td> | |
| 251 <td>$modified</td> | |
| 252 <td style="text-align: right">$size</td> | |
| 253 </tr>'''; | |
| 254 response.write(entry); | |
| 255 } | |
| 256 | |
| 257 if (path != '/') { | |
| 258 add('../', null, null); | |
| 259 } | |
| 260 | |
| 261 dir.list(followLinks: true).listen((entity) { | |
| 262 // TODO(ajohnsen): Consider async dir listing. | |
| 263 if (entity is File) { | |
| 264 add(new Path(entity.path).filename, | |
| 265 entity.statSync().modified, | |
| 266 entity.lengthSync()); | |
| 267 } else if (entity is Directory) { | |
| 268 add(new Path(entity.path).filename + '/', | |
| 269 entity.statSync().modified, | |
| 270 null); | |
| 271 } | |
| 272 }, onError: (e) { | |
| 273 }, onDone: () { | |
| 274 response.write(footer); | |
| 275 response.close(); | |
| 276 }); | |
| 277 }, onError: (e) => response.close()); | |
| 278 } | |
| 279 | |
| 200 void _serveErrorPage(int error, HttpRequest request) { | 280 void _serveErrorPage(int error, HttpRequest request) { |
| 201 var response = request.response; | 281 var response = request.response; |
| 202 response.statusCode = error; | 282 response.statusCode = error; |
| 203 if (_errorCallback != null) { | 283 if (_errorCallback != null) { |
| 204 _errorCallback(request); | 284 _errorCallback(request); |
| 205 return; | 285 return; |
| 206 } | 286 } |
| 207 // Default error page. | 287 // Default error page. |
| 208 var path = request.uri.path; | 288 var path = request.uri.path; |
| 209 var reason = response.reasonPhrase; | 289 var reason = response.reasonPhrase; |
| 210 response.write( | 290 |
| 211 '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n'); | |
| 212 response.writeln( | |
| 213 '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'); | |
| 214 response.writeln('<html xmlns="http://www.w3.org/1999/xhtml">'); | |
| 215 response.writeln('<head>'); | |
| 216 response.writeln('<title>$reason: $path</title>'); | |
| 217 response.writeln('</head>'); | |
| 218 response.writeln('<body>'); | |
| 219 response.writeln('<h1>Error $error at \'$path\': $reason</h1>'); | |
| 220 var server = response.headers.value(HttpHeaders.SERVER); | 291 var server = response.headers.value(HttpHeaders.SERVER); |
| 221 if (server != null) { | 292 if (server == null) server = ""; |
| 222 response.writeln(server); | 293 var page = |
| 223 } | 294 '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
| 224 response.writeln('</body>'); | 295 http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| 225 response.writeln('</html>'); | 296 <html xmlns="http://www.w3.org/1999/xhtml"> |
| 297 <head> | |
| 298 <title>$reason: $path</title> | |
| 299 </head> | |
| 300 <body> | |
| 301 <h1>Error $error at \'$path\': $reason</h1> | |
| 302 $server | |
| 303 </body> | |
| 304 </html>'''; | |
| 305 response.write(page); | |
| 226 response.close(); | 306 response.close(); |
| 227 } | 307 } |
| 228 } | 308 } |
| 229 | 309 |
| 230 class _VirtualDirectoryFileStream extends StreamConsumer<List<int>> { | 310 class _VirtualDirectoryFileStream extends StreamConsumer<List<int>> { |
| 231 final HttpResponse response; | 311 final HttpResponse response; |
| 232 final String path; | 312 final String path; |
| 233 var buffer = []; | 313 var buffer = []; |
| 234 | 314 |
| 235 _VirtualDirectoryFileStream(HttpResponse this.response, String this.path); | 315 _VirtualDirectoryFileStream(HttpResponse this.response, String this.path); |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 275 | 355 |
| 276 Future close() => new Future.value(); | 356 Future close() => new Future.value(); |
| 277 | 357 |
| 278 void setMimeType(var bytes) { | 358 void setMimeType(var bytes) { |
| 279 var mimeType = lookupMimeType(path, headerBytes: bytes); | 359 var mimeType = lookupMimeType(path, headerBytes: bytes); |
| 280 if (mimeType != null) { | 360 if (mimeType != null) { |
| 281 response.headers.contentType = ContentType.parse(mimeType); | 361 response.headers.contentType = ContentType.parse(mimeType); |
| 282 } | 362 } |
| 283 } | 363 } |
| 284 } | 364 } |
| OLD | NEW |