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 Map<RequestPattern, StreamController> _controllers = |
justinfagnani
2013/04/19 19:20:51
Make _controllers a List. See comment below.
adambender
2013/04/19 19:43:11
Done.
| |
55 new LinkedHashMap<Pattern, StreamController>(); | 55 new LinkedHashMap<RequestPattern, StreamController>(); |
56 | 56 |
57 final Map<Pattern, Filter> _filters = new LinkedHashMap<Pattern, Filter>(); | 57 final Map<Pattern, Filter> _filters = new LinkedHashMap<Pattern, Filter>(); |
58 | 58 |
59 final StreamController<HttpRequest> _defaultController = | 59 final StreamController<HttpRequest> _defaultController = |
60 new StreamController<HttpRequest>(); | 60 new StreamController<HttpRequest>(); |
61 | 61 |
62 /** | 62 /** |
63 * Create a new Router that listens to the [incoming] stream, usually an | 63 * Create a new Router that listens to the [incoming] stream, usually an |
64 * instance of [HttpServer]. | 64 * instance of [HttpServer]. |
65 */ | 65 */ |
66 Router(Stream<HttpRequest> incoming) : _incoming = incoming { | 66 Router(Stream<HttpRequest> incoming) : _incoming = incoming { |
67 _incoming.listen(_handleRequest); | 67 _incoming.listen(_handleRequest); |
68 } | 68 } |
69 | 69 |
70 /** | 70 /** |
71 * Request whose URI matches [url] are sent to the stream created by this | 71 * Request whose URI matches [url] are sent to the stream created by this |
72 * method, and not sent to any other router streams. | 72 * method, and not sent to any other router streams. |
justinfagnani
2013/04/19 19:20:51
Add docs for [method]
adambender
2013/04/19 19:43:11
Done.
| |
73 */ | 73 */ |
74 Stream<HttpRequest> serve(Pattern url) { | 74 Stream<HttpRequest> serve(Pattern url, {String method}) { |
75 var controller = new StreamController<HttpRequest>(); | 75 var controller = new StreamController<HttpRequest>(); |
76 _controllers[url] = controller; | 76 _controllers[new RequestPattern(url, method:method)] = controller; |
77 return controller.stream; | 77 return controller.stream; |
78 } | 78 } |
79 | 79 |
80 /** | 80 /** |
81 * 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 |
82 * 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]. |
83 * | 83 * |
84 * 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 |
85 * 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 |
86 * 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. |
87 */ | 87 */ |
88 void filter(Pattern url, Filter filter) { | 88 void filter(Pattern url, Filter filter) { |
89 _filters[url] = filter; | 89 _filters[url] = filter; |
90 } | 90 } |
91 | 91 |
92 Stream<HttpRequest> get defaultStream => _defaultController.stream; | 92 Stream<HttpRequest> get defaultStream => _defaultController.stream; |
93 | 93 |
94 void _handleRequest(HttpRequest req) { | 94 void _handleRequest(HttpRequest req) { |
95 bool cont = true; | 95 bool cont = true; |
96 doWhile(_filters.keys, (Pattern pattern) { | 96 doWhile(_filters.keys, (Pattern pattern) { |
97 if (matchesFull(pattern, req.uri.path)) { | 97 if (matchesFull(pattern, req.uri.path)) { |
98 return _filters[pattern](req).then((c) { | 98 return _filters[pattern](req).then((c) { |
99 cont = c; | 99 cont = c; |
100 return c; | 100 return c; |
101 }); | 101 }); |
102 } | 102 } |
103 return new Future.value(true); | 103 return new Future.value(true); |
104 }).then((_) { | 104 }).then((_) { |
105 if (cont) { | 105 if (cont) { |
106 bool handled = false; | 106 bool handled = false; |
107 for (Pattern pattern in _controllers.keys) { | 107 for (RequestPattern reqPattern in _controllers.keys) { |
justinfagnani
2013/04/19 19:20:51
if we want to be even more Darty, we can use Itera
adambender
2013/04/19 19:43:11
Done.
| |
108 if (matchesFull(pattern, req.uri.path)) { | 108 if (reqPattern.matches(req)) { |
109 _controllers[pattern].add(req); | 109 _controllers[reqPattern].add(req); |
110 handled = true; | 110 handled = true; |
111 break; | 111 break; |
112 } | 112 } |
113 } | 113 } |
114 if (!handled) { | 114 if (!handled) { |
115 if (_defaultController.hasListener) { | 115 if (_defaultController.hasListener) { |
116 _defaultController.add(req); | 116 _defaultController.add(req); |
117 } else { | 117 } else { |
118 send404(req); | 118 send404(req); |
119 } | 119 } |
120 } | 120 } |
121 } | 121 } |
122 }); | 122 }); |
123 } | 123 } |
124 } | 124 } |
125 | 125 |
126 void send404(HttpRequest req) { | 126 void send404(HttpRequest req) { |
127 req.response.statusCode = HttpStatus.NOT_FOUND; | 127 req.response.statusCode = HttpStatus.NOT_FOUND; |
128 req.response.write("Not Found"); | 128 req.response.write("Not Found"); |
129 req.response.close(); | 129 req.response.close(); |
130 } | 130 } |
131 | |
132 class RequestPattern{ | |
justinfagnani
2013/04/19 19:20:51
On the client-side I have a similar class called R
adambender
2013/04/19 19:43:11
Done.
| |
133 final Pattern url; | |
134 final String method; | |
135 RequestPattern(this.url, {this.method}); | |
136 | |
137 bool matches(HttpRequest request){ | |
justinfagnani
2013/04/19 19:20:51
space before {
justinfagnani
2013/04/19 19:20:51
you can use the "fat arrow syntax" here
adambender
2013/04/19 19:43:11
Done.
adambender
2013/04/19 19:43:11
Done.
| |
138 return matchesFull(url, request.uri.path) && (method == null || request.met hod.toUpperCase() == method); | |
justinfagnani
2013/04/19 19:20:51
we're using an 80 col limit
adambender
2013/04/19 19:43:11
Done.
| |
139 } | |
140 } | |
OLD | NEW |