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 |