Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(113)

Side by Side Diff: pkg/http_server/lib/src/virtual_directory.dart

Issue 31633003: Make setDirectoryHandler and setErrorPageHandler setters, and add a jailRoot property. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/http_server/test/virtual_directory_test.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 *
11 * The [VirtualDirectory] providing secure handling of request uris and 11 * The [VirtualDirectory] providing secure handling of request uris and
12 * file-system links, correct mime-types and custom error pages. 12 * file-system links, correct mime-types and custom error pages.
13 */ 13 */
14 abstract class VirtualDirectory { 14 class VirtualDirectory {
15 final String root; 15 final String root;
16 16
17 /** 17 /**
18 * Set or get if the [VirtualDirectory] should list the content of 18 * Set or get if the [VirtualDirectory] should list the content of
19 * directories. 19 * directories.
20 */ 20 */
21 bool allowDirectoryListing = false; 21 bool allowDirectoryListing = false;
22 22
23 /** 23 /**
24 * Set or get if the [VirtualDirectory] should follow links, that point 24 * Set or get if the [VirtualDirectory] should follow links, that point
25 * to other resources within the [root] directory. 25 * to other resources within the [root] directory.
26 */ 26 */
27 bool followLinks = true; 27 bool followLinks = true;
28 28
29 /**
30 * Set or get if the [VirtualDirectory] should jail the root. When the root is
31 * not jailed, links can be followed to outside the [root] directory.
32 */
33 bool jailRoot = true;
Søren Gjesse 2013/10/21 10:44:31 I don't have better names ("confineToRoot" or "onl
Anders Johnsen 2013/10/21 12:20:27 Done.
34
35 final RegExp _invalidPathRegExp = new RegExp("[\\\/\x00]");
36
37 Function _errorCallback;
38 Function _dirCallback;
39
29 /* 40 /*
30 * Create a new [VirtualDirectory] for serving static file content of 41 * Create a new [VirtualDirectory] for serving static file content of
31 * the path [root]. 42 * the path [root].
32 * 43 *
33 * The [root] is not required to exist. If the [root] doesn't exist at time of 44 * The [root] is not required to exist. If the [root] doesn't exist at time of
34 * a request, a 404 is generated. 45 * a request, a 404 is generated.
35 */ 46 */
36 factory VirtualDirectory(String root) => new _VirtualDirectory(root); 47 VirtualDirectory(this.root);
37 48
38 /** 49 /**
39 * Serve a [Stream] of [HttpRequest]s, in this [VirtualDirectory]. 50 * Serve a [Stream] of [HttpRequest]s, in this [VirtualDirectory].
40 */ 51 */
41 void serve(Stream<HttpRequest> requests); 52 void serve(Stream<HttpRequest> requests) {
53 requests.listen(serveRequest);
54 }
42 55
43 /** 56 /**
44 * Serve a single [HttpRequest], in this [VirtualDirectory]. 57 * Serve a single [HttpRequest], in this [VirtualDirectory].
45 */ 58 */
46 void serveRequest(HttpRequest request);
47
48 /**
49 * Set the [callback] to override the default directory listing. The
50 * [callback] will be called with the [Directory] to be listed and the
51 * [HttpRequest].
52 */
53 void setDirectoryHandler(void callback(Directory dir, HttpRequest request));
54
55 /**
56 * Set the [callback] to override the error page handler. When [callback] is
57 * invoked, the `statusCode` property of the response is set.
58 */
59 void setErrorPageHandler(void callback(HttpRequest request));
60 }
61
62 class _VirtualDirectory implements VirtualDirectory {
63 final String root;
64
65 bool allowDirectoryListing = false;
66 bool followLinks = true;
67
68 final RegExp _invalidPathRegExp = new RegExp("[\\\/\x00]");
69
70 Function _errorCallback;
71 Function _dirCallback;
72
73 _VirtualDirectory(this.root);
74
75 void serve(Stream<HttpRequest> requests) {
76 requests.listen(serveRequest);
77 }
78
79 void serveRequest(HttpRequest request) { 59 void serveRequest(HttpRequest request) {
80 _locateResource('.', request.uri.pathSegments.iterator..moveNext()) 60 _locateResource('.', request.uri.pathSegments.iterator..moveNext())
81 .then((entity) { 61 .then((entity) {
82 if (entity == null) { 62 if (entity == null) {
83 _serveErrorPage(HttpStatus.NOT_FOUND, request); 63 _serveErrorPage(HttpStatus.NOT_FOUND, request);
84 return; 64 return;
85 } 65 }
86 if (entity is File) { 66 if (entity is File) {
87 _serveFile(entity, request); 67 _serveFile(entity, request);
88 } else if (entity is Directory) { 68 } else if (entity is Directory) {
89 _serveDirectory(entity, request); 69 _serveDirectory(entity, request);
90 } else { 70 } else {
91 _serveErrorPage(HttpStatus.NOT_FOUND, request); 71 _serveErrorPage(HttpStatus.NOT_FOUND, request);
92 } 72 }
93 }); 73 });
94 } 74 }
95 75
96 void setDirectoryHandler(void callback(Directory dir, HttpRequest request)) { 76 /**
77 * Set the [callback] to override the default directory listing. The
78 * [callback] will be called with the [Directory] to be listed and the
79 * [HttpRequest].
80 */
81 void set directoryHandler(void callback(Directory dir, HttpRequest request)) {
97 _dirCallback = callback; 82 _dirCallback = callback;
98 } 83 }
99 84
100 void setErrorPageHandler(void callback(HttpRequest request)) { 85 /**
86 * Set the [callback] to override the error page handler. When [callback] is
87 * invoked, the `statusCode` property of the response is set.
88 */
89 void set errorPageHandler(void callback(HttpRequest request)) {
101 _errorCallback = callback; 90 _errorCallback = callback;
102 } 91 }
103 92
104 Future<FileSystemEntity> _locateResource(String path, 93 Future<FileSystemEntity> _locateResource(String path,
105 Iterator<String> segments) { 94 Iterator<String> segments) {
95 // Don't allow navigating up paths.
96 if (segments.current == "..") return new Future.value(null);
106 path = normalize(path); 97 path = normalize(path);
107 if (split(path).first == "..") return new Future.value(null); 98 // If we jail to root, the relative path can never go up.
99 if (jailRoot && split(path).first == "..") return new Future.value(null);
108 String fullPath() => join(root, path); 100 String fullPath() => join(root, path);
109 return FileSystemEntity.type(fullPath(), followLinks: false) 101 return FileSystemEntity.type(fullPath(), followLinks: false)
110 .then((type) { 102 .then((type) {
111 switch (type) { 103 switch (type) {
112 case FileSystemEntityType.FILE: 104 case FileSystemEntityType.FILE:
113 if (segments.current == null) { 105 if (segments.current == null) {
114 return new File(fullPath()); 106 return new File(fullPath());
115 } 107 }
116 break; 108 break;
117 109
118 case FileSystemEntityType.DIRECTORY: 110 case FileSystemEntityType.DIRECTORY:
119 if (segments.current == null) { 111 if (segments.current == null) {
120 if (allowDirectoryListing) { 112 if (allowDirectoryListing) {
121 return new Directory(fullPath()); 113 return new Directory(fullPath());
122 } 114 }
123 } else { 115 } else {
124 if (_invalidPathRegExp.hasMatch(segments.current)) break; 116 if (_invalidPathRegExp.hasMatch(segments.current)) break;
125 return _locateResource(join(path, segments.current), 117 return _locateResource(join(path, segments.current),
126 segments..moveNext()); 118 segments..moveNext());
127 } 119 }
128 break; 120 break;
129 121
130 case FileSystemEntityType.LINK: 122 case FileSystemEntityType.LINK:
131 if (followLinks) { 123 if (followLinks) {
132 return new Link(fullPath()).target() 124 return new Link(fullPath()).target()
133 .then((target) { 125 .then((target) {
126 print("Link target: $target");
Søren Gjesse 2013/10/21 10:44:31 Debug print.
Anders Johnsen 2013/10/21 12:20:27 Done.
134 String targetPath = normalize(target); 127 String targetPath = normalize(target);
135 if (isAbsolute(targetPath)) return null; 128 if (isAbsolute(targetPath)) {
136 targetPath = join(dirname(path), targetPath); 129 // If we jail to root, the path can never be absolute.
137 return _locateResource(targetPath, segments); 130 if (jailRoot) return null;
131 print(targetPath);
Søren Gjesse 2013/10/21 10:44:31 Ditto.
Anders Johnsen 2013/10/21 12:20:27 Done.
132 return _locateResource(targetPath, segments);
133 } else {
134 targetPath = join(dirname(path), targetPath);
135 return _locateResource(targetPath, segments);
136 }
138 }); 137 });
139 } 138 }
140 break; 139 break;
141 } 140 }
142 // Return `null` on fall-through, to indicate NOT_FOUND. 141 // Return `null` on fall-through, to indicate NOT_FOUND.
143 return null; 142 return null;
144 }); 143 });
145 } 144 }
146 145
147 void _serveFile(File file, HttpRequest request) { 146 void _serveFile(File file, HttpRequest request) {
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
366 365
367 Future close() => new Future.value(); 366 Future close() => new Future.value();
368 367
369 void setMimeType(var bytes) { 368 void setMimeType(var bytes) {
370 var mimeType = lookupMimeType(path, headerBytes: bytes); 369 var mimeType = lookupMimeType(path, headerBytes: bytes);
371 if (mimeType != null) { 370 if (mimeType != null) {
372 response.headers.contentType = ContentType.parse(mimeType); 371 response.headers.contentType = ContentType.parse(mimeType);
373 } 372 }
374 } 373 }
375 } 374 }
OLDNEW
« no previous file with comments | « no previous file | pkg/http_server/test/virtual_directory_test.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698