| 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 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 58 */ | 58 */ |
| 59 void setErrorPageHandler(void callback(HttpRequest request)); | 59 void setErrorPageHandler(void callback(HttpRequest request)); |
| 60 } | 60 } |
| 61 | 61 |
| 62 class _VirtualDirectory implements VirtualDirectory { | 62 class _VirtualDirectory implements VirtualDirectory { |
| 63 final String root; | 63 final String root; |
| 64 | 64 |
| 65 bool allowDirectoryListing = false; | 65 bool allowDirectoryListing = false; |
| 66 bool followLinks = true; | 66 bool followLinks = true; |
| 67 | 67 |
| 68 final RegExp _invalidPathRegExp = new RegExp("[\\\/\x00]"); |
| 69 |
| 68 Function _errorCallback; | 70 Function _errorCallback; |
| 69 Function _dirCallback; | 71 Function _dirCallback; |
| 70 | 72 |
| 71 _VirtualDirectory(this.root); | 73 _VirtualDirectory(this.root); |
| 72 | 74 |
| 73 void serve(Stream<HttpRequest> requests) { | 75 void serve(Stream<HttpRequest> requests) { |
| 74 requests.listen(serveRequest); | 76 requests.listen(serveRequest); |
| 75 } | 77 } |
| 76 | 78 |
| 77 void serveRequest(HttpRequest request) { | 79 void serveRequest(HttpRequest request) { |
| 78 var path = new Path(request.uri.path).canonicalize(); | 80 _locateResource(new Path('.'), request.uri.pathSegments.iterator..moveNext()
) |
| 79 | |
| 80 if (!path.isAbsolute) { | |
| 81 return _serveErrorPage(HttpStatus.NOT_FOUND, request); | |
| 82 } | |
| 83 | |
| 84 _locateResource(new Path('.'), path.segments()) | |
| 85 .then((entity) { | 81 .then((entity) { |
| 86 if (entity == null) { | 82 if (entity == null) { |
| 87 _serveErrorPage(HttpStatus.NOT_FOUND, request); | 83 _serveErrorPage(HttpStatus.NOT_FOUND, request); |
| 88 return; | 84 return; |
| 89 } | 85 } |
| 90 if (entity is File) { | 86 if (entity is File) { |
| 91 _serveFile(entity, request); | 87 _serveFile(entity, request); |
| 92 } else if (entity is Directory) { | 88 } else if (entity is Directory) { |
| 93 _serveDirectory(entity, request); | 89 _serveDirectory(entity, request); |
| 94 } else { | 90 } else { |
| 95 _serveErrorPage(HttpStatus.NOT_FOUND, request); | 91 _serveErrorPage(HttpStatus.NOT_FOUND, request); |
| 96 } | 92 } |
| 97 }); | 93 }); |
| 98 } | 94 } |
| 99 | 95 |
| 100 void setDirectoryHandler(void callback(Directory dir, HttpRequest request)) { | 96 void setDirectoryHandler(void callback(Directory dir, HttpRequest request)) { |
| 101 _dirCallback = callback; | 97 _dirCallback = callback; |
| 102 } | 98 } |
| 103 | 99 |
| 104 void setErrorPageHandler(void callback(HttpRequest request)) { | 100 void setErrorPageHandler(void callback(HttpRequest request)) { |
| 105 _errorCallback = callback; | 101 _errorCallback = callback; |
| 106 } | 102 } |
| 107 | 103 |
| 108 Future<FileSystemEntity> _locateResource(Path path, | 104 Future<FileSystemEntity> _locateResource(Path path, |
| 109 Iterable<String> segments) { | 105 Iterator<String> segments) { |
| 106 path = path.canonicalize(); |
| 107 if (path.segments().first == "..") return new Future.value(null); |
| 110 Path fullPath() => new Path(root).join(path); | 108 Path fullPath() => new Path(root).join(path); |
| 111 return FileSystemEntity.type(fullPath().toNativePath(), followLinks: false) | 109 return FileSystemEntity.type(fullPath().toNativePath(), followLinks: false) |
| 112 .then((type) { | 110 .then((type) { |
| 113 switch (type) { | 111 switch (type) { |
| 114 case FileSystemEntityType.FILE: | 112 case FileSystemEntityType.FILE: |
| 115 if (segments.isEmpty) return new File.fromPath(fullPath()); | 113 if (segments.current == null) { |
| 114 return new File.fromPath(fullPath()); |
| 115 } |
| 116 break; | 116 break; |
| 117 | 117 |
| 118 case FileSystemEntityType.DIRECTORY: | 118 case FileSystemEntityType.DIRECTORY: |
| 119 if (segments.isEmpty) { | 119 if (segments.current == null) { |
| 120 if (allowDirectoryListing) { | 120 if (allowDirectoryListing) { |
| 121 return new Directory.fromPath(fullPath()); | 121 return new Directory.fromPath(fullPath()); |
| 122 } | 122 } |
| 123 } else { | 123 } else { |
| 124 return _locateResource(path.append(segments.first), | 124 if (_invalidPathRegExp.hasMatch(segments.current)) break; |
| 125 segments.skip(1)); | 125 return _locateResource(path.append(segments.current), |
| 126 segments..moveNext()); |
| 126 } | 127 } |
| 127 break; | 128 break; |
| 128 | 129 |
| 129 case FileSystemEntityType.LINK: | 130 case FileSystemEntityType.LINK: |
| 130 if (followLinks) { | 131 if (followLinks) { |
| 131 return new Link.fromPath(fullPath()).target() | 132 return new Link.fromPath(fullPath()).target() |
| 132 .then((target) { | 133 .then((target) { |
| 133 var targetPath = new Path(target).canonicalize(); | 134 var targetPath = new Path(target).canonicalize(); |
| 134 if (targetPath.isAbsolute) return null; | 135 if (targetPath.isAbsolute) return null; |
| 135 targetPath = | 136 targetPath = path.directoryPath.join(targetPath); |
| 136 path.directoryPath.join(targetPath).canonicalize(); | 137 return _locateResource(targetPath, segments); |
| 137 if (targetPath.segments().isEmpty || | |
| 138 targetPath.segments().first == '..') return null; | |
| 139 if (segments.isEmpty) { | |
| 140 return _locateResource(targetPath, []); | |
| 141 } | |
| 142 return _locateResource(targetPath.append(segments.first), | |
| 143 segments.skip(1)); | |
| 144 }); | 138 }); |
| 145 } | 139 } |
| 146 break; | 140 break; |
| 147 } | 141 } |
| 148 // Return `null` on fall-through, to indicate NOT_FOUND. | 142 // Return `null` on fall-through, to indicate NOT_FOUND. |
| 149 return null; | 143 return null; |
| 150 }); | 144 }); |
| 151 } | 145 } |
| 152 | 146 |
| 153 void _serveFile(File file, HttpRequest request) { | 147 void _serveFile(File file, HttpRequest request) { |
| (...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 372 | 366 |
| 373 Future close() => new Future.value(); | 367 Future close() => new Future.value(); |
| 374 | 368 |
| 375 void setMimeType(var bytes) { | 369 void setMimeType(var bytes) { |
| 376 var mimeType = lookupMimeType(path, headerBytes: bytes); | 370 var mimeType = lookupMimeType(path, headerBytes: bytes); |
| 377 if (mimeType != null) { | 371 if (mimeType != null) { |
| 378 response.headers.contentType = ContentType.parse(mimeType); | 372 response.headers.contentType = ContentType.parse(mimeType); |
| 379 } | 373 } |
| 380 } | 374 } |
| 381 } | 375 } |
| OLD | NEW |