Chromium Code Reviews| OLD | NEW |
|---|---|
| 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 /** | 5 /** |
| 6 * This library provides a simple API for routing HttpRequests based on thier | 6 * This library provides a simple API for routing HttpRequests based on thier |
| 7 * URL. | 7 * URL. |
| 8 */ | 8 */ |
| 9 library route.server; | 9 library route.server; |
| 10 | 10 |
| (...skipping 14 matching lines...) Expand all Loading... | |
| 25 * [serve] creates a new [Stream] of requests whose paths match against the | 25 * [serve] creates a new [Stream] of requests whose paths match against the |
| 26 * given pattern. Matching requests are not sent to any other streams created by | 26 * given pattern. Matching requests are not sent to any other streams created by |
| 27 * a server() call. | 27 * a server() call. |
| 28 * | 28 * |
| 29 * [filter] registers a [Filter] function to run against matching requests. On | 29 * [filter] registers a [Filter] function to run against matching requests. On |
| 30 * each request the filters that match are run in order, waiting for each to | 30 * each request the filters that match are run in order, waiting for each to |
| 31 * complete since filters return a Future. If any filter completes false, the | 31 * complete since filters return a Future. If any filter completes false, the |
| 32 * subsequent filters and request handlers are not run. This way a filter can | 32 * subsequent filters and request handlers are not run. This way a filter can |
| 33 * prevent further processing, like needed for authentication. | 33 * prevent further processing, like needed for authentication. |
| 34 * | 34 * |
| 35 * Requests not matched by a call to [serve] are sent to the [defaultStream]. | |
| 36 * If there's no subscriber to the defaultStream then a 404 is sent to the | |
| 37 * response. | |
| 38 * | |
| 35 * Example: | 39 * Example: |
| 36 * import 'package:route/server.dart'; | 40 * import 'package:route/server.dart'; |
| 37 * import 'package:route/pattern.dart'; | 41 * import 'package:route/pattern.dart'; |
| 38 * | 42 * |
| 39 * HttpServer.bind().then((server) { | 43 * HttpServer.bind().then((server) { |
| 40 * var router = new Router(server); | 44 * var router = new Router(server); |
| 41 * router.filter(matchesAny(['/foo', '/bar']), authFilter); | 45 * router.filter(matchesAny(['/foo', '/bar']), authFilter); |
| 42 * router.serve('/foo').listen(fooHandler); | 46 * router.serve('/foo').listen(fooHandler); |
| 43 * router.serve('/bar').listen(barHandler); | 47 * router.serve('/bar').listen(barHandler); |
| 48 * router.defaultStream.listen(send404); | |
| 44 * }); | 49 * }); |
| 45 */ | 50 */ |
| 46 class Router { | 51 class Router { |
| 47 final Stream<HttpRequest> incoming; | 52 final Stream<HttpRequest> _incoming; |
| 48 final Map<Pattern, StreamController> _controllers = new LinkedHashMap(); | 53 |
| 49 final Map<Pattern, Filter> _filters = new LinkedHashMap(); | 54 final Map<Pattern, StreamController> _controllers = |
| 50 | 55 new LinkedHashMap<Pattern, StreamController>(); |
| 51 Router(this.incoming) { | 56 |
| 52 incoming.listen(_handleRequest); | 57 final Map<Pattern, Filter> _filters = new LinkedHashMap<Pattern, Filter>(); |
| 58 | |
| 59 final StreamController<HttpRequest> _defaultController = | |
| 60 new StreamController<HttpRequest>(); | |
| 61 | |
| 62 /** | |
| 63 * Create a new Router that listens to the [incoming] stream, usually an | |
| 64 * instance of [HttpServer]. | |
| 65 */ | |
| 66 Router(Stream<HttpRequest> incoming) : _incoming = incoming { | |
| 67 _incoming.listen(_handleRequest); | |
| 53 } | 68 } |
| 54 | 69 |
| 55 /** | 70 /** |
| 56 * Request whose URI matches [url] are sent the the stream created by this\ | 71 * Request whose URI matches [url] are sent the the stream created by this |
|
butlermatt
2013/03/07 13:21:52
double 'the'
| |
| 57 * method, and not sent to any other serve streams. | 72 * method, and not sent to any other serve streams. |
| 58 */ | 73 */ |
| 59 Stream<HttpRequest> serve(Pattern url) { | 74 Stream<HttpRequest> serve(Pattern url) { |
| 60 var controller = new StreamController<HttpRequest>(); | 75 var controller = new StreamController<HttpRequest>(); |
| 61 _controllers[url] = controller; | 76 _controllers[url] = controller; |
| 62 return controller.stream; | 77 return controller.stream; |
| 63 } | 78 } |
| 64 | 79 |
| 65 /** | 80 /** |
| 66 * A [Filter] returns a [Future<bool>] that tells the router whether to apply | 81 * A [Filter] returns a [Future<bool>] that tells the router whether to apply |
| 67 * the remaining filters and send requests to the streams created by [serve]. | 82 * the remaining filters and send requests to the streams created by [serve]. |
| 68 * | 83 * |
| 69 * If the filter returns true, the request is passed to the next filter, and | 84 * If the filter returns true, the request is passed to the next filter, and |
| 70 * then to the first matching server stream. If the filter returns false, it's | 85 * then to the first matching server stream. If the filter returns false, it's |
| 71 * assumed that the filter is handling the request and it's not forwarded. | 86 * assumed that the filter is handling the request and it's not forwarded. |
| 72 */ | 87 */ |
| 73 void filter(Pattern url, Filter filter) { | 88 void filter(Pattern url, Filter filter) { |
| 74 _filters[url] = filter; | 89 _filters[url] = filter; |
| 75 } | 90 } |
| 76 | 91 |
| 92 Stream<HttpRequest> get defaultStream => _defaultController.stream; | |
| 93 | |
| 77 void _handleRequest(HttpRequest req) { | 94 void _handleRequest(HttpRequest req) { |
| 78 bool cont = true; | 95 bool cont = true; |
| 79 doWhile(_filters.keys, (Pattern pattern) { | 96 doWhile(_filters.keys, (Pattern pattern) { |
| 80 if (matchesFull(pattern, req.uri.path)) { | 97 if (matchesFull(pattern, req.uri.path)) { |
| 81 return _filters[pattern](req).then((c) { | 98 return _filters[pattern](req).then((c) { |
| 82 cont = c; | 99 cont = c; |
| 83 return c; | 100 return c; |
| 84 }); | 101 }); |
| 85 } | 102 } |
| 86 return new Future.immediate(true); | 103 return new Future.immediate(true); |
| 87 }).then((_) { | 104 }).then((_) { |
| 88 if (cont) { | 105 if (cont) { |
| 89 bool handled = false; | 106 bool handled = false; |
| 90 for (Pattern pattern in _controllers.keys) { | 107 for (Pattern pattern in _controllers.keys) { |
| 91 if (matchesFull(pattern, req.uri.path)) { | 108 if (matchesFull(pattern, req.uri.path)) { |
| 92 _controllers[pattern].add(req); | 109 _controllers[pattern].add(req); |
| 93 handled = true; | 110 handled = true; |
| 94 break; | 111 break; |
| 95 } | 112 } |
| 96 } | 113 } |
| 97 if (!handled) { | 114 if (!handled) { |
| 98 send404(req); | 115 if (_defaultController.hasSubscribers) { |
| 116 _defaultController.add(req); | |
| 117 } else { | |
| 118 send404(req); | |
| 119 } | |
| 99 } | 120 } |
| 100 } | 121 } |
| 101 }); | 122 }); |
| 102 } | 123 } |
| 103 } | 124 } |
| 104 | 125 |
| 105 void send404(HttpRequest req) { | 126 void send404(HttpRequest req) { |
| 106 req.response.statusCode = HttpStatus.NOT_FOUND; | 127 req.response.statusCode = HttpStatus.NOT_FOUND; |
| 107 req.response.addString("Not Found"); | 128 req.response.addString("Not Found"); |
| 108 req.response.close(); | 129 req.response.close(); |
| 109 } | 130 } |
| OLD | NEW |