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

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

Issue 17833003: Add directory-listing to VirtualDirectory. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 7 years, 5 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 *
(...skipping 28 matching lines...) Expand all
39 * Serve a [Stream] of [HttpRequest]s, in this [VirtualDirectory]. 39 * Serve a [Stream] of [HttpRequest]s, in this [VirtualDirectory].
40 */ 40 */
41 void serve(Stream<HttpRequest> requests); 41 void serve(Stream<HttpRequest> requests);
42 42
43 /** 43 /**
44 * Serve a single [HttpRequest], in this [VirtualDirectory]. 44 * Serve a single [HttpRequest], in this [VirtualDirectory].
45 */ 45 */
46 void serveRequest(HttpRequest request); 46 void serveRequest(HttpRequest request);
47 47
48 /** 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 /**
49 * Set the [callback] to override the error page handler. When [callback] is 56 * Set the [callback] to override the error page handler. When [callback] is
50 * invoked, the `statusCode` property of the response is set. 57 * invoked, the `statusCode` property of the response is set.
51 */ 58 */
52 void setErrorPageHandler(void callback(HttpRequest request)); 59 void setErrorPageHandler(void callback(HttpRequest request));
53 } 60 }
54 61
55 class _VirtualDirectory implements VirtualDirectory { 62 class _VirtualDirectory implements VirtualDirectory {
56 final String root; 63 final String root;
57 64
58 bool allowDirectoryListing = false; 65 bool allowDirectoryListing = false;
59 bool followLinks = true; 66 bool followLinks = true;
60 67
61 Function _errorCallback; 68 Function _errorCallback;
69 Function _dirCallback;
62 70
63 _VirtualDirectory(this.root); 71 _VirtualDirectory(this.root);
64 72
65 void serve(Stream<HttpRequest> requests) { 73 void serve(Stream<HttpRequest> requests) {
66 requests.listen(serveRequest); 74 requests.listen(serveRequest);
67 } 75 }
68 76
69 void serveRequest(HttpRequest request) { 77 void serveRequest(HttpRequest request) {
70 var path = new Path(request.uri.path).canonicalize(); 78 var path = new Path(request.uri.path).canonicalize();
71 79
72 if (!path.isAbsolute) { 80 if (!path.isAbsolute) {
73 return _serveErrorPage(HttpStatus.NOT_FOUND, request); 81 return _serveErrorPage(HttpStatus.NOT_FOUND, request);
74 } 82 }
75 83
76 _locateResource(new Path('.'), path.segments()) 84 _locateResource(new Path('.'), path.segments())
77 .then((entity) { 85 .then((entity) {
78 if (entity == null) { 86 if (entity == null) {
79 _serveErrorPage(HttpStatus.NOT_FOUND, request); 87 _serveErrorPage(HttpStatus.NOT_FOUND, request);
80 return; 88 return;
81 } 89 }
82 if (entity is File) { 90 if (entity is File) {
83 _serveFile(entity, request); 91 _serveFile(entity, request);
92 } else if (entity is Directory) {
93 _serveDirectory(entity, request);
84 } else { 94 } else {
85 _serveErrorPage(HttpStatus.NOT_FOUND, request); 95 _serveErrorPage(HttpStatus.NOT_FOUND, request);
86 } 96 }
87 }); 97 });
88 } 98 }
89 99
100 void setDirectoryHandler(void callback(Directory dir, HttpRequest request)) {
101 _dirCallback = callback;
102 }
103
90 void setErrorPageHandler(void callback(HttpRequest request)) { 104 void setErrorPageHandler(void callback(HttpRequest request)) {
91 _errorCallback = callback; 105 _errorCallback = callback;
92 } 106 }
93 107
94 Future<FileSystemEntity> _locateResource(Path path, 108 Future<FileSystemEntity> _locateResource(Path path,
95 Iterable<String> segments) { 109 Iterable<String> segments) {
96 Path fullPath() => new Path(root).join(path); 110 Path fullPath() => new Path(root).join(path);
97 return FileSystemEntity.type(fullPath().toNativePath(), followLinks: false) 111 return FileSystemEntity.type(fullPath().toNativePath(), followLinks: false)
98 .then((type) { 112 .then((type) {
99 switch (type) { 113 switch (type) {
(...skipping 11 matching lines...) Expand all
111 segments.skip(1)); 125 segments.skip(1));
112 } 126 }
113 break; 127 break;
114 128
115 case FileSystemEntityType.LINK: 129 case FileSystemEntityType.LINK:
116 if (followLinks) { 130 if (followLinks) {
117 return new Link.fromPath(fullPath()).target() 131 return new Link.fromPath(fullPath()).target()
118 .then((target) { 132 .then((target) {
119 var targetPath = new Path(target).canonicalize(); 133 var targetPath = new Path(target).canonicalize();
120 if (targetPath.isAbsolute) return null; 134 if (targetPath.isAbsolute) return null;
121 targetPath = path.directoryPath.join(targetPath) 135 targetPath =
122 .canonicalize(); 136 path.directoryPath.join(targetPath).canonicalize();
123 if (targetPath.segments().isEmpty || 137 if (targetPath.segments().isEmpty ||
124 targetPath.segments().first == '..') return null; 138 targetPath.segments().first == '..') return null;
139 if (segments.isEmpty) {
140 return _locateResource(targetPath, []);
141 }
125 return _locateResource(targetPath.append(segments.first), 142 return _locateResource(targetPath.append(segments.first),
126 segments.skip(1)); 143 segments.skip(1));
127 }); 144 });
128 } 145 }
129 break; 146 break;
130 } 147 }
131 // Return `null` on fall-through, to indicate NOT_FOUND. 148 // Return `null` on fall-through, to indicate NOT_FOUND.
132 return null; 149 return null;
133 }); 150 });
134 } 151 }
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
190 207
191 file.openRead() 208 file.openRead()
192 .pipe(new _VirtualDirectoryFileStream(response, file.path)) 209 .pipe(new _VirtualDirectoryFileStream(response, file.path))
193 .catchError((_) {}); 210 .catchError((_) {});
194 }); 211 });
195 }).catchError((_) { 212 }).catchError((_) {
196 response.close(); 213 response.close();
197 }); 214 });
198 } 215 }
199 216
217 void _serveDirectory(Directory dir, HttpRequest request) {
218 if (_dirCallback != null) {
219 _dirCallback(dir, request);
220 return;
221 }
222 var response = request.response;
223 dir.stat().then((stats) {
224 if (request.headers.ifModifiedSince != null &&
225 !stats.modified.isAfter(request.headers.ifModifiedSince)) {
226 response.statusCode = HttpStatus.NOT_MODIFIED;
227 response.close();
228 return;
229 }
230
231 response.headers.set(HttpHeaders.LAST_MODIFIED, stats.modified);
232 var path = request.uri.path;
233 var header =
234 '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
235 http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
236 <html xmlns="http://www.w3.org/1999/xhtml">
237 <head>
238 <title>Index of $path</title>
239 </head>
240 <body>
241 <h1>Index of $path</h1>
242 <table>
243 <tr>
244 <td>Name</td>
245 <td>Last modified</td>
246 <td>Size</td>
247 </tr>
248 ''';
249 var server = response.headers.value(HttpHeaders.SERVER);
250 if (server == null) server = "";
251 var footer =
252 '''</table>
253 $server
254 </body>
255 </html>
256 ''';
257
258 response.write(header);
259
260 void add(String name, String modified, var size) {
261 if (size == null) size = "-";
262 if (modified == null) modified = "";
263 var p = new Path(path).append(name).canonicalize().toString();
264 var entry =
265 ''' <tr>
266 <td><a href="$p">$name</a></td>
267 <td>$modified</td>
268 <td style="text-align: right">$size</td>
269 </tr>''';
270 response.write(entry);
271 }
272
273 if (path != '/') {
274 add('../', null, null);
275 }
276
277 dir.list(followLinks: true).listen((entity) {
278 // TODO(ajohnsen): Consider async dir listing.
279 if (entity is File) {
280 var stat = entity.statSync();
281 add(new Path(entity.path).filename,
282 stat.modified.toString(),
283 stat.size);
284 } else if (entity is Directory) {
285 add(new Path(entity.path).filename + '/',
286 entity.statSync().modified.toString(),
287 null);
288 }
289 }, onError: (e) {
290 }, onDone: () {
291 response.write(footer);
292 response.close();
293 });
294 }, onError: (e) => response.close());
295 }
296
200 void _serveErrorPage(int error, HttpRequest request) { 297 void _serveErrorPage(int error, HttpRequest request) {
201 var response = request.response; 298 var response = request.response;
202 response.statusCode = error; 299 response.statusCode = error;
203 if (_errorCallback != null) { 300 if (_errorCallback != null) {
204 _errorCallback(request); 301 _errorCallback(request);
205 return; 302 return;
206 } 303 }
207 // Default error page. 304 // Default error page.
208 var path = request.uri.path; 305 var path = request.uri.path;
209 var reason = response.reasonPhrase; 306 var reason = response.reasonPhrase;
210 response.write( 307
211 '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"\n');
212 response.writeln(
213 '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">');
214 response.writeln('<html xmlns="http://www.w3.org/1999/xhtml">');
215 response.writeln('<head>');
216 response.writeln('<title>$reason: $path</title>');
217 response.writeln('</head>');
218 response.writeln('<body>');
219 response.writeln('<h1>Error $error at \'$path\': $reason</h1>');
220 var server = response.headers.value(HttpHeaders.SERVER); 308 var server = response.headers.value(HttpHeaders.SERVER);
221 if (server != null) { 309 if (server == null) server = "";
222 response.writeln(server); 310 var page =
223 } 311 '''<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
224 response.writeln('</body>'); 312 http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
225 response.writeln('</html>'); 313 <html xmlns="http://www.w3.org/1999/xhtml">
314 <head>
315 <title>$reason: $path</title>
316 </head>
317 <body>
318 <h1>Error $error at \'$path\': $reason</h1>
319 $server
320 </body>
321 </html>''';
322 response.write(page);
226 response.close(); 323 response.close();
227 } 324 }
228 } 325 }
229 326
230 class _VirtualDirectoryFileStream extends StreamConsumer<List<int>> { 327 class _VirtualDirectoryFileStream extends StreamConsumer<List<int>> {
231 final HttpResponse response; 328 final HttpResponse response;
232 final String path; 329 final String path;
233 var buffer = []; 330 var buffer = [];
234 331
235 _VirtualDirectoryFileStream(HttpResponse this.response, String this.path); 332 _VirtualDirectoryFileStream(HttpResponse this.response, String this.path);
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
275 372
276 Future close() => new Future.value(); 373 Future close() => new Future.value();
277 374
278 void setMimeType(var bytes) { 375 void setMimeType(var bytes) {
279 var mimeType = lookupMimeType(path, headerBytes: bytes); 376 var mimeType = lookupMimeType(path, headerBytes: bytes);
280 if (mimeType != null) { 377 if (mimeType != null) {
281 response.headers.contentType = ContentType.parse(mimeType); 378 response.headers.contentType = ContentType.parse(mimeType);
282 } 379 }
283 } 380 }
284 } 381 }
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