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 |