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

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: version 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/pubspec.yaml » ('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 // Used for signal a directory redirecting, where a tailing slash is missing. 8 // Used for signal a directory redirecting, where a tailing slash is missing.
9 class _DirectoryRedirect { 9 class _DirectoryRedirect {
10 const _DirectoryRedirect(); 10 const _DirectoryRedirect();
11 } 11 }
12 12
13 typedef dynamic _DirCallback(Directory dir, HttpRequest request);
14 typedef dynamic _ErrorCallback(HttpRequest request);
15
13 /** 16 /**
14 * A [VirtualDirectory] can serve files and directory-listing from a root path, 17 * A [VirtualDirectory] can serve files and directory-listing from a root path,
15 * to [HttpRequest]s. 18 * to [HttpRequest]s.
16 * 19 *
17 * The [VirtualDirectory] providing secure handling of request uris and 20 * The [VirtualDirectory] providing secure handling of request uris and
18 * file-system links, correct mime-types and custom error pages. 21 * file-system links, correct mime-types and custom error pages.
19 */ 22 */
20 class VirtualDirectory { 23 class VirtualDirectory {
21 final String root; 24 final String root;
22 25
(...skipping 10 matching lines...) Expand all
33 bool followLinks = true; 36 bool followLinks = true;
34 37
35 /** 38 /**
36 * Set or get if the [VirtualDirectory] should jail the root. When the root is 39 * Set or get if the [VirtualDirectory] should jail the root. When the root is
37 * not jailed, links can be followed to outside the [root] directory. 40 * not jailed, links can be followed to outside the [root] directory.
38 */ 41 */
39 bool jailRoot = true; 42 bool jailRoot = true;
40 43
41 final RegExp _invalidPathRegExp = new RegExp("[\\\/\x00]"); 44 final RegExp _invalidPathRegExp = new RegExp("[\\\/\x00]");
42 45
43 Function _errorCallback; 46 _ErrorCallback _errorCallback;
44 Function _dirCallback; 47 _DirCallback _dirCallback;
45 48
46 /* 49 /*
47 * Create a new [VirtualDirectory] for serving static file content of 50 * Create a new [VirtualDirectory] for serving static file content of
48 * the path [root]. 51 * the path [root].
49 * 52 *
50 * The [root] is not required to exist. If the [root] doesn't exist at time of 53 * The [root] is not required to exist. If the [root] doesn't exist at time of
51 * a request, a 404 is generated. 54 * a request, a 404 is generated.
52 */ 55 */
53 VirtualDirectory(this.root); 56 VirtualDirectory(this.root);
54 57
55 /** 58 /**
56 * Serve a [Stream] of [HttpRequest]s, in this [VirtualDirectory]. 59 * Serve a [Stream] of [HttpRequest]s, in this [VirtualDirectory].
57 */ 60 */
58 void serve(Stream<HttpRequest> requests) { 61 StreamSubscription<HttpRequest> serve(Stream<HttpRequest> requests) =>
59 requests.listen(serveRequest); 62 requests.listen(serveRequest);
60 }
61 63
62 /** 64 /**
63 * Serve a single [HttpRequest], in this [VirtualDirectory]. 65 * Serve a single [HttpRequest], in this [VirtualDirectory].
64 */ 66 */
65 void serveRequest(HttpRequest request) { 67 Future serveRequest(HttpRequest request) {
66 _locateResource('.', request.uri.pathSegments.iterator..moveNext()) 68 return _locateResource('.', request.uri.pathSegments.iterator..moveNext())
67 .then((entity) { 69 .then((entity) {
68 if (entity == null) {
69 _serveErrorPage(HttpStatus.NOT_FOUND, request);
70 return;
71 }
72 if (entity is File) { 70 if (entity is File) {
73 serveFile(entity, request); 71 serveFile(entity, request);
74 } else if (entity is Directory) { 72 } else if (entity is Directory) {
75 if (allowDirectoryListing) { 73 if (allowDirectoryListing) {
76 _serveDirectory(entity, request); 74 _serveDirectory(entity, request);
77 } else { 75 } else {
78 _serveErrorPage(HttpStatus.NOT_FOUND, request); 76 _serveErrorPage(HttpStatus.NOT_FOUND, request);
79 } 77 }
80 } else if (entity is _DirectoryRedirect) { 78 } else if (entity is _DirectoryRedirect) {
81 // TODO(ajohnsen): Use HttpRequest.requestedUri once 1.2 is out. 79 // TODO(ajohnsen): Use HttpRequest.requestedUri once 1.2 is out.
82 request.response.redirect(Uri.parse('${request.uri}/'), 80 request.response.redirect(Uri.parse('${request.uri}/'),
83 status: HttpStatus.MOVED_PERMANENTLY); 81 status: HttpStatus.MOVED_PERMANENTLY);
84 } else { 82 } else {
83 assert(entity == null);
85 _serveErrorPage(HttpStatus.NOT_FOUND, request); 84 _serveErrorPage(HttpStatus.NOT_FOUND, request);
86 } 85 }
86 return request.response.done;
87 }); 87 });
88 } 88 }
89 89
90 /** 90 /**
91 * Set the [callback] to override the default directory listing. The 91 * Set the [callback] to override the default directory listing. The
92 * [callback] will be called with the [Directory] to be listed and the 92 * [callback] will be called with the [Directory] to be listed and the
93 * [HttpRequest]. 93 * [HttpRequest].
94 */ 94 */
95 void set directoryHandler(void callback(Directory dir, HttpRequest request)) { 95 void set directoryHandler(void callback(Directory dir, HttpRequest request)) {
96 _dirCallback = callback; 96 _dirCallback = callback;
(...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 * is closed with error-code [HttpStatus.NOT_FOUND]. 172 * is closed with error-code [HttpStatus.NOT_FOUND].
173 */ 173 */
174 void serveFile(File file, HttpRequest request) { 174 void serveFile(File file, HttpRequest request) {
175 var response = request.response; 175 var response = request.response;
176 // TODO(ajohnsen): Set up Zone support for these errors. 176 // TODO(ajohnsen): Set up Zone support for these errors.
177 file.lastModified().then((lastModified) { 177 file.lastModified().then((lastModified) {
178 if (request.headers.ifModifiedSince != null && 178 if (request.headers.ifModifiedSince != null &&
179 !lastModified.isAfter(request.headers.ifModifiedSince)) { 179 !lastModified.isAfter(request.headers.ifModifiedSince)) {
180 response.statusCode = HttpStatus.NOT_MODIFIED; 180 response.statusCode = HttpStatus.NOT_MODIFIED;
181 response.close(); 181 response.close();
182 return; 182 return null;
183 } 183 }
184 184
185 response.headers.set(HttpHeaders.LAST_MODIFIED, lastModified); 185 response.headers.set(HttpHeaders.LAST_MODIFIED, lastModified);
186 response.headers.set(HttpHeaders.ACCEPT_RANGES, "bytes"); 186 response.headers.set(HttpHeaders.ACCEPT_RANGES, "bytes");
187 187
188 if (request.method == 'HEAD') { 188 if (request.method == 'HEAD') {
189 response.close(); 189 response.close();
190 return; 190 return null;
191 } 191 }
192 192
193 return file.length().then((length) { 193 return file.length().then((length) {
194 String range = request.headers.value("range"); 194 String range = request.headers.value("range");
195 if (range != null) { 195 if (range != null) {
196 // We only support one range, where the standard support several. 196 // We only support one range, where the standard support several.
197 Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range); 197 Match matches = new RegExp(r"^bytes=(\d*)\-(\d*)$").firstMatch(range);
198 // If the range header have the right format, handle it. 198 // If the range header have the right format, handle it.
199 if (matches != null) { 199 if (matches != null) {
200 // Serve sub-range. 200 // Serve sub-range.
(...skipping 13 matching lines...) Expand all
214 response.headers.set(HttpHeaders.CONTENT_LENGTH, end - start); 214 response.headers.set(HttpHeaders.CONTENT_LENGTH, end - start);
215 215
216 // Set 'Partial Content' status code. 216 // Set 'Partial Content' status code.
217 response.statusCode = HttpStatus.PARTIAL_CONTENT; 217 response.statusCode = HttpStatus.PARTIAL_CONTENT;
218 response.headers.set(HttpHeaders.CONTENT_RANGE, 218 response.headers.set(HttpHeaders.CONTENT_RANGE,
219 "bytes $start-${end - 1}/$length"); 219 "bytes $start-${end - 1}/$length");
220 220
221 // Pipe the 'range' of the file. 221 // Pipe the 'range' of the file.
222 file.openRead(start, end) 222 file.openRead(start, end)
223 .pipe(new _VirtualDirectoryFileStream(response, file.path)) 223 .pipe(new _VirtualDirectoryFileStream(response, file.path))
224 .catchError((_) {}); 224 .catchError((_) {
225 // TODO(kevmoo): log errors
226 });
225 return; 227 return;
226 } 228 }
227 } 229 }
228 230
229 file.openRead() 231 file.openRead()
230 .pipe(new _VirtualDirectoryFileStream(response, file.path)) 232 .pipe(new _VirtualDirectoryFileStream(response, file.path))
231 .catchError((_) {}); 233 .catchError((_) {
234 // TODO(kevmoo): log errors
235 });
232 }); 236 });
233 }).catchError((_) { 237 }).catchError((_) {
234 response.statusCode = HttpStatus.NOT_FOUND; 238 response.statusCode = HttpStatus.NOT_FOUND;
235 response.close(); 239 response.close();
236 }); 240 });
237 } 241 }
238 242
239 void _serveDirectory(Directory dir, HttpRequest request) { 243 void _serveDirectory(Directory dir, HttpRequest request) {
240 if (_dirCallback != null) { 244 if (_dirCallback != null) {
241 _dirCallback(dir, request); 245 _dirCallback(dir, request);
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after
290 <td style="text-align: right">$size</td> 294 <td style="text-align: right">$size</td>
291 </tr>'''; 295 </tr>''';
292 response.write(entry); 296 response.write(entry);
293 } 297 }
294 298
295 if (path != '/') { 299 if (path != '/') {
296 add('../', null, null); 300 add('../', null, null);
297 } 301 }
298 302
299 dir.list(followLinks: true).listen((entity) { 303 dir.list(followLinks: true).listen((entity) {
300 // TODO(ajohnsen): Consider async dir listing.
301 if (entity is File) { 304 if (entity is File) {
302 var stat = entity.statSync(); 305 var stat = entity.statSync();
303 add(basename(entity.path), 306 add(basename(entity.path),
304 stat.modified.toString(), 307 stat.modified.toString(),
305 stat.size); 308 stat.size);
306 } else if (entity is Directory) { 309 } else if (entity is Directory) {
307 add(basename(entity.path) + '/', 310 add(basename(entity.path) + '/',
308 entity.statSync().modified.toString(), 311 entity.statSync().modified.toString(),
309 null); 312 null);
310 } 313 }
311 }, onError: (e) { 314 }, onError: (e) {
315 // TODO(kevmoo): log error
312 }, onDone: () { 316 }, onDone: () {
313 response.write(footer); 317 response.write(footer);
314 response.close(); 318 response.close();
315 }); 319 });
316 }, onError: (e) => response.close()); 320 }, onError: (e) {
321 // TODO(kevmoo): log error
322 response.close();
323 });
317 } 324 }
318 325
319 void _serveErrorPage(int error, HttpRequest request) { 326 void _serveErrorPage(int error, HttpRequest request) {
320 var response = request.response; 327 var response = request.response;
321 response.statusCode = error; 328 response.statusCode = error;
322 if (_errorCallback != null) { 329 if (_errorCallback != null) {
323 _errorCallback(request); 330 _errorCallback(request);
324 return; 331 return;
325 } 332 }
326 // Default error page. 333 // Default error page.
(...skipping 15 matching lines...) Expand all
342 </body> 349 </body>
343 </html>'''; 350 </html>''';
344 response.write(page); 351 response.write(page);
345 response.close(); 352 response.close();
346 } 353 }
347 } 354 }
348 355
349 class _VirtualDirectoryFileStream extends StreamConsumer<List<int>> { 356 class _VirtualDirectoryFileStream extends StreamConsumer<List<int>> {
350 final HttpResponse response; 357 final HttpResponse response;
351 final String path; 358 final String path;
352 var buffer = []; 359 List<int> buffer = [];
353 360
354 _VirtualDirectoryFileStream(HttpResponse this.response, String this.path); 361 _VirtualDirectoryFileStream(HttpResponse this.response, String this.path);
355 362
356 Future addStream(Stream<List<int>> stream) { 363 Future addStream(Stream<List<int>> stream) {
357 stream.listen( 364 stream.listen(
358 (data) { 365 (data) {
359 if (buffer == null) { 366 if (buffer == null) {
360 response.add(data); 367 response.add(data);
361 return; 368 return;
362 } 369 }
(...skipping 24 matching lines...) Expand all
387 } 394 }
388 } 395 }
389 response.close(); 396 response.close();
390 }, 397 },
391 onError: response.addError); 398 onError: response.addError);
392 return response.done; 399 return response.done;
393 } 400 }
394 401
395 Future close() => new Future.value(); 402 Future close() => new Future.value();
396 403
397 void setMimeType(var bytes) { 404 void setMimeType(List<int> bytes) {
398 var mimeType = lookupMimeType(path, headerBytes: bytes); 405 var mimeType = lookupMimeType(path, headerBytes: bytes);
399 if (mimeType != null) { 406 if (mimeType != null) {
400 response.headers.contentType = ContentType.parse(mimeType); 407 response.headers.contentType = ContentType.parse(mimeType);
401 } 408 }
402 } 409 }
403 } 410 }
OLDNEW
« no previous file with comments | « no previous file | pkg/http_server/pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698