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

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

Issue 124833003: pkg/http_server: return future for VirtualDirectory serveRequest (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: tweaks Created 6 years, 11 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/http_mock.dart » ('j') | pkg/http_server/test/http_mock.dart » ('J')
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 typedef dynamic _DirCallback(Directory dir, HttpRequest request);
8 typedef dynamic _ErrorCallback(HttpRequest request);
9
7 /** 10 /**
8 * A [VirtualDirectory] can serve files and directory-listing from a root path, 11 * A [VirtualDirectory] can serve files and directory-listing from a root path,
9 * to [HttpRequest]s. 12 * to [HttpRequest]s.
10 * 13 *
11 * The [VirtualDirectory] providing secure handling of request uris and 14 * The [VirtualDirectory] providing secure handling of request uris and
12 * file-system links, correct mime-types and custom error pages. 15 * file-system links, correct mime-types and custom error pages.
13 */ 16 */
14 class VirtualDirectory { 17 class VirtualDirectory {
15 final String root; 18 final String root;
16 19
(...skipping 10 matching lines...) Expand all
27 bool followLinks = true; 30 bool followLinks = true;
28 31
29 /** 32 /**
30 * Set or get if the [VirtualDirectory] should jail the root. When the root is 33 * 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. 34 * not jailed, links can be followed to outside the [root] directory.
32 */ 35 */
33 bool jailRoot = true; 36 bool jailRoot = true;
34 37
35 final RegExp _invalidPathRegExp = new RegExp("[\\\/\x00]"); 38 final RegExp _invalidPathRegExp = new RegExp("[\\\/\x00]");
36 39
37 Function _errorCallback; 40 _ErrorCallback _errorCallback;
38 Function _dirCallback; 41 _DirCallback _dirCallback;
39 42
40 /* 43 /*
41 * Create a new [VirtualDirectory] for serving static file content of 44 * Create a new [VirtualDirectory] for serving static file content of
42 * the path [root]. 45 * the path [root].
43 * 46 *
44 * The [root] is not required to exist. If the [root] doesn't exist at time of 47 * The [root] is not required to exist. If the [root] doesn't exist at time of
45 * a request, a 404 is generated. 48 * a request, a 404 is generated.
46 */ 49 */
47 VirtualDirectory(this.root); 50 VirtualDirectory(this.root);
48 51
49 /** 52 /**
50 * Serve a [Stream] of [HttpRequest]s, in this [VirtualDirectory]. 53 * Serve a [Stream] of [HttpRequest]s, in this [VirtualDirectory].
51 */ 54 */
52 void serve(Stream<HttpRequest> requests) { 55 StreamSubscription<HttpRequest> serve(Stream<HttpRequest> requests) =>
53 requests.listen(serveRequest); 56 requests.listen(serveRequest);
54 }
55 57
56 /** 58 /**
57 * Serve a single [HttpRequest], in this [VirtualDirectory]. 59 * Serve a single [HttpRequest], in this [VirtualDirectory].
58 */ 60 */
59 void serveRequest(HttpRequest request) { 61 Future serveRequest(HttpRequest request) {
60 _locateResource('.', request.uri.pathSegments.iterator..moveNext()) 62 return _locateResource('.', request.uri.pathSegments.iterator..moveNext())
61 .then((entity) { 63 .then((FileSystemEntity entity) {
62 if (entity == null) {
63 _serveErrorPage(HttpStatus.NOT_FOUND, request);
64 return;
65 }
66 if (entity is File) { 64 if (entity is File) {
67 serveFile(entity, request); 65 serveFile(entity, request);
68 } else if (entity is Directory) { 66 } else if (entity is Directory) {
69 _serveDirectory(entity, request); 67 _serveDirectory(entity, request);
70 } else { 68 } else {
71 _serveErrorPage(HttpStatus.NOT_FOUND, request); 69 _serveErrorPage(HttpStatus.NOT_FOUND, request);
72 } 70 }
71 return request.response.done;
73 }); 72 });
74 } 73 }
75 74
76 /** 75 /**
77 * Set the [callback] to override the default directory listing. The 76 * Set the [callback] to override the default directory listing. The
78 * [callback] will be called with the [Directory] to be listed and the 77 * [callback] will be called with the [Directory] to be listed and the
79 * [HttpRequest]. 78 * [HttpRequest].
80 */ 79 */
81 void set directoryHandler(void callback(Directory dir, HttpRequest request)) { 80 void set directoryHandler(void callback(Directory dir, HttpRequest request)) {
82 _dirCallback = callback; 81 _dirCallback = callback;
(...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 * This is usefull when e.g. overriding [directoryHandler] to redirect to 150 * This is usefull when e.g. overriding [directoryHandler] to redirect to
152 * some index file. 151 * some index file.
153 * 152 *
154 * In the request contains the [HttpStatus.IF_MODIFIED_SINCE] header, 153 * In the request contains the [HttpStatus.IF_MODIFIED_SINCE] header,
155 * [serveFile] will send a [HttpStatus.NOT_MODIFIED] response if the file 154 * [serveFile] will send a [HttpStatus.NOT_MODIFIED] response if the file
156 * was not changed. 155 * was not changed.
157 * 156 *
158 * Note that if it was unabled to read from [file], the [request]s response 157 * Note that if it was unabled to read from [file], the [request]s response
159 * is closed with error-code [HttpStatus.NOT_FOUND]. 158 * is closed with error-code [HttpStatus.NOT_FOUND].
160 */ 159 */
161 void serveFile(File file, HttpRequest request) { 160 void serveFile(File file, HttpRequest request) {
sethladd 2014/01/09 05:27:48 why not return the future from here?
Anders Johnsen 2014/01/09 06:12:23 response.done is the right way of doing it. Also,
162 var response = request.response; 161 var response = request.response;
163 // TODO(ajohnsen): Set up Zone support for these errors. 162 // TODO(ajohnsen): Set up Zone support for these errors.
164 file.lastModified().then((lastModified) { 163 file.lastModified().then((lastModified) {
165 if (request.headers.ifModifiedSince != null && 164 if (request.headers.ifModifiedSince != null &&
166 !lastModified.isAfter(request.headers.ifModifiedSince)) { 165 !lastModified.isAfter(request.headers.ifModifiedSince)) {
167 response.statusCode = HttpStatus.NOT_MODIFIED; 166 response.statusCode = HttpStatus.NOT_MODIFIED;
168 response.close(); 167 response.close();
169 return; 168 return null;
170 } 169 }
171 170
172 response.headers.set(HttpHeaders.LAST_MODIFIED, lastModified); 171 response.headers.set(HttpHeaders.LAST_MODIFIED, lastModified);
173 response.headers.set(HttpHeaders.ACCEPT_RANGES, "bytes"); 172 response.headers.set(HttpHeaders.ACCEPT_RANGES, "bytes");
174 173
175 if (request.method == 'HEAD') { 174 if (request.method == 'HEAD') {
176 response.close(); 175 response.close();
177 return; 176 return null;
178 } 177 }
179 178
180 return file.length().then((length) { 179 return file.length().then((length) {
181 String range = request.headers.value("range"); 180 String range = request.headers.value("range");
182 if (range != null) { 181 if (range != null) {
183 // We only support one range, where the standard support several. 182 // We only support one range, where the standard support several.
184 Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range); 183 Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range);
185 // If the range header have the right format, handle it. 184 // If the range header have the right format, handle it.
186 if (matches != null) { 185 if (matches != null) {
187 // Serve sub-range. 186 // Serve sub-range.
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after
276 <td>$modified</td> 275 <td>$modified</td>
277 <td style="text-align: right">$size</td> 276 <td style="text-align: right">$size</td>
278 </tr>'''; 277 </tr>''';
279 response.write(entry); 278 response.write(entry);
280 } 279 }
281 280
282 if (path != '/') { 281 if (path != '/') {
283 add('../', null, null); 282 add('../', null, null);
284 } 283 }
285 284
286 dir.list(followLinks: true).listen((entity) { 285 dir.list(followLinks: true).forEach((entity) {
Anders Johnsen 2014/01/09 06:12:23 This is not handling errors (they can happen if FS
kevmoo 2014/01/10 18:12:31 I think I've addressed this, with TODOs for loggin
287 // TODO(ajohnsen): Consider async dir listing.
288 if (entity is File) { 286 if (entity is File) {
289 var stat = entity.statSync(); 287 var stat = entity.statSync();
290 add(basename(entity.path), 288 add(basename(entity.path),
291 stat.modified.toString(), 289 stat.modified.toString(),
292 stat.size); 290 stat.size);
293 } else if (entity is Directory) { 291 } else if (entity is Directory) {
294 add(basename(entity.path) + '/', 292 add(basename(entity.path) + '/',
295 entity.statSync().modified.toString(), 293 entity.statSync().modified.toString(),
296 null); 294 null);
297 } 295 }
298 }, onError: (e) { 296 })
299 }, onDone: () { 297 .then((_) {
300 response.write(footer); 298 response.write(footer);
301 response.close(); 299 })
300 .whenComplete(() {
sethladd 2014/01/09 05:27:48 can this be a one-line function?
kevmoo 2014/01/10 05:02:00 Done.
301 return response.close();
Anders Johnsen 2014/01/09 06:12:23 Why return here? This value is not kept.
kevmoo 2014/01/10 05:02:00 Done.
302 }); 302 });
303 }, onError: (e) => response.close()); 303 });
304 } 304 }
305 305
306 void _serveErrorPage(int error, HttpRequest request) { 306 void _serveErrorPage(int error, HttpRequest request) {
307 var response = request.response; 307 var response = request.response;
308 response.statusCode = error; 308 response.statusCode = error;
309 if (_errorCallback != null) { 309 if (_errorCallback != null) {
310 _errorCallback(request); 310 _errorCallback(request);
311 return; 311 return;
312 } 312 }
313 // Default error page. 313 // Default error page.
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
381 381
382 Future close() => new Future.value(); 382 Future close() => new Future.value();
383 383
384 void setMimeType(var bytes) { 384 void setMimeType(var bytes) {
385 var mimeType = lookupMimeType(path, headerBytes: bytes); 385 var mimeType = lookupMimeType(path, headerBytes: bytes);
386 if (mimeType != null) { 386 if (mimeType != null) {
387 response.headers.contentType = ContentType.parse(mimeType); 387 response.headers.contentType = ContentType.parse(mimeType);
388 } 388 }
389 } 389 }
390 } 390 }
OLDNEW
« no previous file with comments | « no previous file | pkg/http_server/test/http_mock.dart » ('j') | pkg/http_server/test/http_mock.dart » ('J')

Powered by Google App Engine
This is Rietveld 408576698