Index: pkg/http_server/lib/src/virtual_directory.dart |
diff --git a/pkg/http_server/lib/src/virtual_directory.dart b/pkg/http_server/lib/src/virtual_directory.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a09613527008cdebc12d9ea51a3f1192fe9c0cbd |
--- /dev/null |
+++ b/pkg/http_server/lib/src/virtual_directory.dart |
@@ -0,0 +1,122 @@ |
+// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+part of http_server; |
+ |
+/** |
+ * A [VirtualDirectory] can serve files and directory-listing from a root path, |
+ * to [HttpRequest]s. |
+ * |
+ * The [VirtualDirectory] providing secure handling of request uris and |
+ * file-system links, correct mime-types and custom error pages. |
+ */ |
+abstract class VirtualDirectory { |
+ final String root; |
+ |
+ /** |
+ * Set or get if the [VirtualDirectory] should list the content of |
+ * directories. |
+ */ |
+ bool allowDirectoryListing = false; |
+ |
+ /** |
+ * Set or get if the [VirtualDirectory] should follow links, that point |
+ * to other resources within the [root] directory. |
+ */ |
+ bool followLinks = true; |
+ |
+ /* |
+ * Create a new [VirtualDirectory] for serving static file content of |
+ * the path [root]. |
+ * |
+ * The [root] is not required to exist. If the [root] doesn't exist at time of |
+ * a request, a 404 is generated. |
+ */ |
+ factory VirtualDirectory(String root) => new _VirtualDirectory(root); |
+ |
+ /** |
+ * Serve a [Stream] of [HttpRequest]s, in this [VirtualDirectory]. |
+ */ |
+ void serve(Stream<HttpRequest> requests); |
+ |
+ /** |
+ * Serve a single [HttpRequest], in this [VirtualDirectory]. |
+ */ |
+ void serveRequest(HttpRequest request); |
+ |
+ /** |
+ * Set the [callback] to override the error page handler. When [callback] is |
+ * invoked, the `statusCode` property of the response is set. |
+ */ |
+ void setErrorPageHandler(void callback(HttpResponse response)); |
+} |
+ |
+class _VirtualDirectory implements VirtualDirectory { |
+ final String root; |
+ |
+ bool _allowDirectoryListing = false; |
+ bool _followLinks = true; |
+ |
+ _VirtualDirectory(this.root); |
+ |
+ void serve(Stream<HttpRequest> requests) { |
+ requests.listen(serveRequest); |
+ } |
+ |
+ void serveRequest(HttpRequest request) { |
+ var path = new Path(request.uri.path).canonicalize(); |
+ |
+ if (!path.isAbsolute) { |
+ return _serveErrorPage(HttpStatus.NOT_FOUND, request); |
+ } |
+ |
+ _locateResource(new Path(root), path.segments()) |
+ .then((entity) { |
+ if (entity == null) { |
+ _serveErrorPage(HttpStatus.NOT_FOUND, request); |
+ return; |
+ } |
+ if (entity is File) { |
+ entity.openRead().pipe(request.response).catchError((_) {}); |
+ } else { |
+ _serveErrorPage(HttpStatus.NOT_FOUND, request); |
+ } |
+ }); |
+ } |
+ |
+ Future<FileSystemEntity> _locateResource(Path path, |
+ Iterable<String> segments) { |
+ return FileSystemEntity.type(path.toString(), followLinks: false) |
+ .then((type) { |
+ switch (type) { |
+ case FileSystemEntityType.FILE: |
+ if (segments.isEmpty) return new File.fromPath(path); |
+ break; |
+ |
+ case FileSystemEntityType.DIRECTORY: |
+ if (segments.isEmpty) { |
+ if (_allowDirectoryListing) return new Directory.fromPath(path); |
+ } else { |
+ return _locateResource(path.append(segments.first), |
+ segments.skip(1)); |
+ } |
+ break; |
+ |
+ case FileSystemEntityType.LINK: |
+ if (followLinks) { |
+ // TODO |
+ } |
+ break; |
+ |
+ } |
+ // Return `null` on fall-through, to indicate NOT_FOUND. |
+ return null; |
+ }); |
+ } |
+ |
+ void _serveErrorPage(int error, HttpRequest request) { |
+ request.response.statusCode = error; |
+ request.response.close(); |
+ } |
+} |