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 | |
| 8 // Used for signal a directory redirecting, where a tailing slash is missing. | |
| 9 class _DirectoryRedirect { | |
| 10 const _DirectoryRedirect(); | |
| 11 } | |
| 12 | |
| 7 /** | 13 /** |
| 8 * A [VirtualDirectory] can serve files and directory-listing from a root path, | 14 * A [VirtualDirectory] can serve files and directory-listing from a root path, |
| 9 * to [HttpRequest]s. | 15 * to [HttpRequest]s. |
| 10 * | 16 * |
| 11 * The [VirtualDirectory] providing secure handling of request uris and | 17 * The [VirtualDirectory] providing secure handling of request uris and |
| 12 * file-system links, correct mime-types and custom error pages. | 18 * file-system links, correct mime-types and custom error pages. |
| 13 */ | 19 */ |
| 14 class VirtualDirectory { | 20 class VirtualDirectory { |
| 15 final String root; | 21 final String root; |
| 16 | 22 |
| (...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 59 void serveRequest(HttpRequest request) { | 65 void serveRequest(HttpRequest request) { |
| 60 _locateResource('.', request.uri.pathSegments.iterator..moveNext()) | 66 _locateResource('.', request.uri.pathSegments.iterator..moveNext()) |
| 61 .then((entity) { | 67 .then((entity) { |
| 62 if (entity == null) { | 68 if (entity == null) { |
| 63 _serveErrorPage(HttpStatus.NOT_FOUND, request); | 69 _serveErrorPage(HttpStatus.NOT_FOUND, request); |
| 64 return; | 70 return; |
| 65 } | 71 } |
| 66 if (entity is File) { | 72 if (entity is File) { |
| 67 serveFile(entity, request); | 73 serveFile(entity, request); |
| 68 } else if (entity is Directory) { | 74 } else if (entity is Directory) { |
| 69 _serveDirectory(entity, request); | 75 if (allowDirectoryListing) { |
| 76 _serveDirectory(entity, request); | |
| 77 } else { | |
| 78 _serveErrorPage(HttpStatus.NOT_FOUND, request); | |
| 79 } | |
| 80 } else if (entity is _DirectoryRedirect) { | |
| 81 request.response.redirect(Uri.parse('${request.uri}/'), | |
|
Søren Gjesse
2014/01/06 13:18:08
According to RFC 2616 section 14.30 the redirect l
Anders Johnsen
2014/01/09 09:32:25
Done.
| |
| 82 status: HttpStatus.MOVED_PERMANENTLY); | |
| 70 } else { | 83 } else { |
| 71 _serveErrorPage(HttpStatus.NOT_FOUND, request); | 84 _serveErrorPage(HttpStatus.NOT_FOUND, request); |
| 72 } | 85 } |
| 73 }); | 86 }); |
| 74 } | 87 } |
| 75 | 88 |
| 76 /** | 89 /** |
| 77 * Set the [callback] to override the default directory listing. The | 90 * Set the [callback] to override the default directory listing. The |
| 78 * [callback] will be called with the [Directory] to be listed and the | 91 * [callback] will be called with the [Directory] to be listed and the |
| 79 * [HttpRequest]. | 92 * [HttpRequest]. |
| 80 */ | 93 */ |
| 81 void set directoryHandler(void callback(Directory dir, HttpRequest request)) { | 94 void set directoryHandler(void callback(Directory dir, HttpRequest request)) { |
| 82 _dirCallback = callback; | 95 _dirCallback = callback; |
| 83 } | 96 } |
| 84 | 97 |
| 85 /** | 98 /** |
| 86 * Set the [callback] to override the error page handler. When [callback] is | 99 * Set the [callback] to override the error page handler. When [callback] is |
| 87 * invoked, the `statusCode` property of the response is set. | 100 * invoked, the `statusCode` property of the response is set. |
| 88 */ | 101 */ |
| 89 void set errorPageHandler(void callback(HttpRequest request)) { | 102 void set errorPageHandler(void callback(HttpRequest request)) { |
| 90 _errorCallback = callback; | 103 _errorCallback = callback; |
| 91 } | 104 } |
| 92 | 105 |
| 93 Future<FileSystemEntity> _locateResource(String path, | 106 Future _locateResource(String path, Iterator<String> segments) { |
| 94 Iterator<String> segments) { | |
| 95 // Don't allow navigating up paths. | 107 // Don't allow navigating up paths. |
| 96 if (segments.current == "..") return new Future.value(null); | 108 if (segments.current == "..") return new Future.value(null); |
| 97 path = normalize(path); | 109 path = normalize(path); |
| 98 // If we jail to root, the relative path can never go up. | 110 // If we jail to root, the relative path can never go up. |
| 99 if (jailRoot && split(path).first == "..") return new Future.value(null); | 111 if (jailRoot && split(path).first == "..") return new Future.value(null); |
| 100 String fullPath({bool endSlash: false}) { | 112 String fullPath() => join(root, path); |
| 101 var p = join(root, path); | |
| 102 if (endSlash && path != ".") p = "$p$separator"; | |
| 103 return p; | |
| 104 } | |
| 105 return FileSystemEntity.type(fullPath(), followLinks: false) | 113 return FileSystemEntity.type(fullPath(), followLinks: false) |
| 106 .then((type) { | 114 .then((type) { |
| 107 switch (type) { | 115 switch (type) { |
| 108 case FileSystemEntityType.FILE: | 116 case FileSystemEntityType.FILE: |
| 109 if (segments.current == null) { | 117 if (segments.current == null) { |
| 110 return new File(fullPath()); | 118 return new File(fullPath()); |
| 111 } | 119 } |
| 112 break; | 120 break; |
| 113 | 121 |
| 114 case FileSystemEntityType.DIRECTORY: | 122 case FileSystemEntityType.DIRECTORY: |
| 115 if (segments.current == null) { | 123 String dirFullPath() => '${fullPath()}$separator'; |
| 116 if (allowDirectoryListing) { | 124 var current = segments.current; |
| 117 return new Directory(fullPath(endSlash: true)); | 125 if (current == null) { |
| 118 } | 126 if (path == '.') return new Directory(dirFullPath()); |
| 127 return const _DirectoryRedirect(); | |
| 128 } | |
| 129 bool hasNext = segments.moveNext(); | |
| 130 if (!hasNext && current == "") { | |
| 131 return new Directory(dirFullPath()); | |
| 119 } else { | 132 } else { |
| 120 if (_invalidPathRegExp.hasMatch(segments.current)) break; | 133 if (_invalidPathRegExp.hasMatch(current)) break; |
| 121 return _locateResource(join(path, segments.current), | 134 return _locateResource(join(path, current), segments); |
| 122 segments..moveNext()); | |
| 123 } | 135 } |
| 124 break; | 136 break; |
| 125 | 137 |
| 126 case FileSystemEntityType.LINK: | 138 case FileSystemEntityType.LINK: |
| 127 if (followLinks) { | 139 if (followLinks) { |
| 128 return new Link(fullPath()).target() | 140 return new Link(fullPath()).target() |
| 129 .then((target) { | 141 .then((target) { |
| 130 String targetPath = normalize(target); | 142 String targetPath = normalize(target); |
| 131 if (isAbsolute(targetPath)) { | 143 if (isAbsolute(targetPath)) { |
| 132 // If we jail to root, the path can never be absolute. | 144 // If we jail to root, the path can never be absolute. |
| (...skipping 248 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 381 | 393 |
| 382 Future close() => new Future.value(); | 394 Future close() => new Future.value(); |
| 383 | 395 |
| 384 void setMimeType(var bytes) { | 396 void setMimeType(var bytes) { |
| 385 var mimeType = lookupMimeType(path, headerBytes: bytes); | 397 var mimeType = lookupMimeType(path, headerBytes: bytes); |
| 386 if (mimeType != null) { | 398 if (mimeType != null) { |
| 387 response.headers.contentType = ContentType.parse(mimeType); | 399 response.headers.contentType = ContentType.parse(mimeType); |
| 388 } | 400 } |
| 389 } | 401 } |
| 390 } | 402 } |
| OLD | NEW |