| 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 // TODO(ajohnsen): Use HttpRequest.requestedUri once 1.2 is out. |
| 82 request.response.redirect(Uri.parse('${request.uri}/'), |
| 83 status: HttpStatus.MOVED_PERMANENTLY); |
| 70 } else { | 84 } else { |
| 71 _serveErrorPage(HttpStatus.NOT_FOUND, request); | 85 _serveErrorPage(HttpStatus.NOT_FOUND, request); |
| 72 } | 86 } |
| 73 }); | 87 }); |
| 74 } | 88 } |
| 75 | 89 |
| 76 /** | 90 /** |
| 77 * Set the [callback] to override the default directory listing. The | 91 * Set the [callback] to override the default directory listing. The |
| 78 * [callback] will be called with the [Directory] to be listed and the | 92 * [callback] will be called with the [Directory] to be listed and the |
| 79 * [HttpRequest]. | 93 * [HttpRequest]. |
| 80 */ | 94 */ |
| 81 void set directoryHandler(void callback(Directory dir, HttpRequest request)) { | 95 void set directoryHandler(void callback(Directory dir, HttpRequest request)) { |
| 82 _dirCallback = callback; | 96 _dirCallback = callback; |
| 83 } | 97 } |
| 84 | 98 |
| 85 /** | 99 /** |
| 86 * Set the [callback] to override the error page handler. When [callback] is | 100 * Set the [callback] to override the error page handler. When [callback] is |
| 87 * invoked, the `statusCode` property of the response is set. | 101 * invoked, the `statusCode` property of the response is set. |
| 88 */ | 102 */ |
| 89 void set errorPageHandler(void callback(HttpRequest request)) { | 103 void set errorPageHandler(void callback(HttpRequest request)) { |
| 90 _errorCallback = callback; | 104 _errorCallback = callback; |
| 91 } | 105 } |
| 92 | 106 |
| 93 Future<FileSystemEntity> _locateResource(String path, | 107 Future _locateResource(String path, Iterator<String> segments) { |
| 94 Iterator<String> segments) { | |
| 95 // Don't allow navigating up paths. | 108 // Don't allow navigating up paths. |
| 96 if (segments.current == "..") return new Future.value(null); | 109 if (segments.current == "..") return new Future.value(null); |
| 97 path = normalize(path); | 110 path = normalize(path); |
| 98 // If we jail to root, the relative path can never go up. | 111 // If we jail to root, the relative path can never go up. |
| 99 if (jailRoot && split(path).first == "..") return new Future.value(null); | 112 if (jailRoot && split(path).first == "..") return new Future.value(null); |
| 100 String fullPath({bool endSlash: false}) { | 113 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) | 114 return FileSystemEntity.type(fullPath(), followLinks: false) |
| 106 .then((type) { | 115 .then((type) { |
| 107 switch (type) { | 116 switch (type) { |
| 108 case FileSystemEntityType.FILE: | 117 case FileSystemEntityType.FILE: |
| 109 if (segments.current == null) { | 118 if (segments.current == null) { |
| 110 return new File(fullPath()); | 119 return new File(fullPath()); |
| 111 } | 120 } |
| 112 break; | 121 break; |
| 113 | 122 |
| 114 case FileSystemEntityType.DIRECTORY: | 123 case FileSystemEntityType.DIRECTORY: |
| 115 if (segments.current == null) { | 124 String dirFullPath() => '${fullPath()}$separator'; |
| 116 if (allowDirectoryListing) { | 125 var current = segments.current; |
| 117 return new Directory(fullPath(endSlash: true)); | 126 if (current == null) { |
| 118 } | 127 if (path == '.') return new Directory(dirFullPath()); |
| 128 return const _DirectoryRedirect(); |
| 129 } |
| 130 bool hasNext = segments.moveNext(); |
| 131 if (!hasNext && current == "") { |
| 132 return new Directory(dirFullPath()); |
| 119 } else { | 133 } else { |
| 120 if (_invalidPathRegExp.hasMatch(segments.current)) break; | 134 if (_invalidPathRegExp.hasMatch(current)) break; |
| 121 return _locateResource(join(path, segments.current), | 135 return _locateResource(join(path, current), segments); |
| 122 segments..moveNext()); | |
| 123 } | 136 } |
| 124 break; | 137 break; |
| 125 | 138 |
| 126 case FileSystemEntityType.LINK: | 139 case FileSystemEntityType.LINK: |
| 127 if (followLinks) { | 140 if (followLinks) { |
| 128 return new Link(fullPath()).target() | 141 return new Link(fullPath()).target() |
| 129 .then((target) { | 142 .then((target) { |
| 130 String targetPath = normalize(target); | 143 String targetPath = normalize(target); |
| 131 if (isAbsolute(targetPath)) { | 144 if (isAbsolute(targetPath)) { |
| 132 // If we jail to root, the path can never be absolute. | 145 // 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 | 394 |
| 382 Future close() => new Future.value(); | 395 Future close() => new Future.value(); |
| 383 | 396 |
| 384 void setMimeType(var bytes) { | 397 void setMimeType(var bytes) { |
| 385 var mimeType = lookupMimeType(path, headerBytes: bytes); | 398 var mimeType = lookupMimeType(path, headerBytes: bytes); |
| 386 if (mimeType != null) { | 399 if (mimeType != null) { |
| 387 response.headers.contentType = ContentType.parse(mimeType); | 400 response.headers.contentType = ContentType.parse(mimeType); |
| 388 } | 401 } |
| 389 } | 402 } |
| 390 } | 403 } |
| OLD | NEW |