| 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 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 44 * var router = new Router(server); | 44 * var router = new Router(server); |
| 45 * router.filter(matchesAny(['/foo', '/bar']), authFilter); | 45 * router.filter(matchesAny(['/foo', '/bar']), authFilter); |
| 46 * router.serve('/foo').listen(fooHandler); | 46 * router.serve('/foo').listen(fooHandler); |
| 47 * router.serve('/bar').listen(barHandler); | 47 * router.serve('/bar').listen(barHandler); |
| 48 * router.defaultStream.listen(send404); | 48 * router.defaultStream.listen(send404); |
| 49 * }); | 49 * }); |
| 50 */ | 50 */ |
| 51 class Router { | 51 class Router { |
| 52 final Stream<HttpRequest> _incoming; | 52 final Stream<HttpRequest> _incoming; |
| 53 | 53 |
| 54 final Map<Pattern, StreamController> _controllers = | 54 final List<_Route> _routes = <_Route>[]; |
| 55 new LinkedHashMap<Pattern, StreamController>(); | 55 |
| 56 | |
| 57 final Map<Pattern, Filter> _filters = new LinkedHashMap<Pattern, Filter>(); | 56 final Map<Pattern, Filter> _filters = new LinkedHashMap<Pattern, Filter>(); |
| 58 | 57 |
| 59 final StreamController<HttpRequest> _defaultController = | 58 final StreamController<HttpRequest> _defaultController = |
| 60 new StreamController<HttpRequest>(); | 59 new StreamController<HttpRequest>(); |
| 61 | 60 |
| 62 /** | 61 /** |
| 63 * Create a new Router that listens to the [incoming] stream, usually an | 62 * Create a new Router that listens to the [incoming] stream, usually an |
| 64 * instance of [HttpServer]. | 63 * instance of [HttpServer]. |
| 65 */ | 64 */ |
| 66 Router(Stream<HttpRequest> incoming) : _incoming = incoming { | 65 Router(Stream<HttpRequest> incoming) : _incoming = incoming { |
| 67 _incoming.listen(_handleRequest); | 66 _incoming.listen(_handleRequest); |
| 68 } | 67 } |
| 69 | 68 |
| 70 /** | 69 /** |
| 71 * Request whose URI matches [url] are sent to the stream created by this | 70 * Request whose URI matches [url] and [method] (if provided) are sent to the |
| 72 * method, and not sent to any other router streams. | 71 * stream created by this method, and not sent to any other router streams. |
| 73 */ | 72 */ |
| 74 Stream<HttpRequest> serve(Pattern url) { | 73 Stream<HttpRequest> serve(Pattern url, {String method}) { |
| 75 var controller = new StreamController<HttpRequest>(); | 74 var controller = new StreamController<HttpRequest>(); |
| 76 _controllers[url] = controller; | 75 _routes.add(new _Route(controller, url, method:method)); |
| 77 return controller.stream; | 76 return controller.stream; |
| 78 } | 77 } |
| 79 | 78 |
| 80 /** | 79 /** |
| 81 * A [Filter] returns a [Future<bool>] that tells the router whether to apply | 80 * A [Filter] returns a [Future<bool>] that tells the router whether to apply |
| 82 * the remaining filters and send requests to the streams created by [serve]. | 81 * the remaining filters and send requests to the streams created by [serve]. |
| 83 * | 82 * |
| 84 * If the filter returns true, the request is passed to the next filter, and | 83 * If the filter returns true, the request is passed to the next filter, and |
| 85 * then to the first matching server stream. If the filter returns false, it's | 84 * then to the first matching server stream. If the filter returns false, it's |
| 86 * assumed that the filter is handling the request and it's not forwarded. | 85 * assumed that the filter is handling the request and it's not forwarded. |
| 87 */ | 86 */ |
| 88 void filter(Pattern url, Filter filter) { | 87 void filter(Pattern url, Filter filter) { |
| 89 _filters[url] = filter; | 88 _filters[url] = filter; |
| 90 } | 89 } |
| 91 | 90 |
| 92 Stream<HttpRequest> get defaultStream => _defaultController.stream; | 91 Stream<HttpRequest> get defaultStream => _defaultController.stream; |
| 93 | 92 |
| 94 void _handleRequest(HttpRequest req) { | 93 void _handleRequest(HttpRequest req) { |
| 95 bool cont = true; | 94 bool cont = true; |
| 96 doWhile(_filters.keys, (Pattern pattern) { | 95 doWhile(_filters.keys, (Pattern pattern) { |
| 97 if (matchesFull(pattern, req.uri.path)) { | 96 if (matchesFull(pattern, req.uri.path)) { |
| 98 return _filters[pattern](req).then((c) { | 97 return _filters[pattern](req).then((c) { |
| 99 cont = c; | 98 cont = c; |
| 100 return c; | 99 return c; |
| 101 }); | 100 }); |
| 102 } | 101 } |
| 103 return new Future.value(true); | 102 return new Future.value(true); |
| 104 }).then((_) { | 103 }).then((_) { |
| 105 if (cont) { | 104 if (cont) { |
| 106 bool handled = false; | 105 bool handled = false; |
| 107 for (Pattern pattern in _controllers.keys) { | 106 var matches = _routes.where((r) => r.matches(req)); |
| 108 if (matchesFull(pattern, req.uri.path)) { | 107 if (!matches.isEmpty) { |
| 109 _controllers[pattern].add(req); | 108 matches.first.controller.add(req); |
| 110 handled = true; | 109 } else { |
| 111 break; | |
| 112 } | |
| 113 } | |
| 114 if (!handled) { | |
| 115 if (_defaultController.hasListener) { | 110 if (_defaultController.hasListener) { |
| 116 _defaultController.add(req); | 111 _defaultController.add(req); |
| 117 } else { | 112 } else { |
| 118 send404(req); | 113 send404(req); |
| 119 } | 114 } |
| 120 } | 115 } |
| 121 } | 116 } |
| 122 }); | 117 }); |
| 123 } | 118 } |
| 124 } | 119 } |
| 125 | 120 |
| 126 void send404(HttpRequest req) { | 121 void send404(HttpRequest req) { |
| 127 req.response.statusCode = HttpStatus.NOT_FOUND; | 122 req.response.statusCode = HttpStatus.NOT_FOUND; |
| 128 req.response.write("Not Found"); | 123 req.response.write("Not Found"); |
| 129 req.response.close(); | 124 req.response.close(); |
| 130 } | 125 } |
| 126 |
| 127 class _Route { |
| 128 final Pattern url; |
| 129 final String method; |
| 130 final StreamController controller; |
| 131 _Route(this.controller, this.url, {this.method}); |
| 132 |
| 133 bool matches(HttpRequest request) => matchesFull(url, request.uri.path) && |
| 134 (method == null || request.method.toUpperCase() == method); |
| 135 } |
| OLD | NEW |