[go: up one dir, main page]

worker/
router.rs

1use std::{collections::HashMap, future::Future, rc::Rc};
2
3use futures_util::future::LocalBoxFuture;
4use matchit::{Match, Router as MatchItRouter};
5use worker_kv::KvStore;
6
7use crate::{
8    durable::ObjectNamespace,
9    env::{Env, Secret, Var},
10    http::Method,
11    request::Request,
12    response::Response,
13    Bucket, Fetcher, Result,
14};
15
16type HandlerFn<D> = fn(Request, RouteContext<D>) -> Result<Response>;
17type AsyncHandlerFn<'a, D> =
18    Rc<dyn 'a + Fn(Request, RouteContext<D>) -> LocalBoxFuture<'a, Result<Response>>>;
19
20/// Represents the URL parameters parsed from the path, e.g. a route with "/user/:id" pattern would
21/// contain a single "id" key.
22pub struct RouteParams(HashMap<String, String>);
23
24impl RouteParams {
25    fn get(&self, key: &str) -> Option<&String> {
26        self.0.get(key)
27    }
28}
29
30enum Handler<'a, D> {
31    Async(AsyncHandlerFn<'a, D>),
32    Sync(HandlerFn<D>),
33}
34
35impl<D> Clone for Handler<'_, D> {
36    fn clone(&self) -> Self {
37        match self {
38            Self::Async(rc) => Self::Async(rc.clone()),
39            Self::Sync(func) => Self::Sync(*func),
40        }
41    }
42}
43
44/// A path-based HTTP router supporting exact-match or wildcard placeholders and shared data.
45pub struct Router<'a, D> {
46    handlers: HashMap<Method, MatchItRouter<Handler<'a, D>>>,
47    or_else_any_method: MatchItRouter<Handler<'a, D>>,
48    data: D,
49}
50
51/// Container for a route's parsed parameters, data, and environment bindings from the Runtime (such
52/// as KV Stores, Durable Objects, Variables, and Secrets).
53pub struct RouteContext<D> {
54    pub data: D,
55    pub env: Env,
56    params: RouteParams,
57}
58
59impl<D> RouteContext<D> {
60    /// Get a reference to the generic associated data provided to the `Router`.
61    #[deprecated(since = "0.0.8", note = "please use the `data` field directly")]
62    pub fn data(&self) -> &D {
63        &self.data
64    }
65
66    /// Get the `Env` for this Worker. Typically users should opt for the `secret`, `var`, `kv` and
67    /// `durable_object` methods on the `RouteContext` instead.
68    #[deprecated(since = "0.0.8", note = "please use the `env` field directly")]
69    pub fn get_env(self) -> Env {
70        self.env
71    }
72
73    /// Get a Secret value associated with this Worker, should one exist.
74    pub fn secret(&self, binding: &str) -> Result<Secret> {
75        self.env.secret(binding)
76    }
77
78    /// Get an Environment Variable value associated with this Worker, should one exist.
79    pub fn var(&self, binding: &str) -> Result<Var> {
80        self.env.var(binding)
81    }
82
83    /// Get a KV Namespace associated with this Worker, should one exist.
84    pub fn kv(&self, binding: &str) -> Result<KvStore> {
85        KvStore::from_this(&self.env, binding).map_err(From::from)
86    }
87
88    /// Get a Durable Object Namespace associated with this Worker, should one exist.
89    pub fn durable_object(&self, binding: &str) -> Result<ObjectNamespace> {
90        self.env.durable_object(binding)
91    }
92
93    /// Get a URL parameter parsed by the router, by the name of its match or wildcard placeholder.
94    pub fn param(&self, key: &str) -> Option<&String> {
95        self.params.get(key)
96    }
97
98    /// Get a [Service Binding](https://developers.cloudflare.com/workers/runtime-apis/service-bindings/)
99    /// for Worker-to-Worker communication.
100    pub fn service(&self, binding: &str) -> Result<Fetcher> {
101        self.env.service(binding)
102    }
103
104    /// Get a R2 Bucket associated with this Worker, should one exist.
105    pub fn bucket(&self, binding: &str) -> Result<Bucket> {
106        self.env.bucket(binding)
107    }
108
109    /// Access a D1 Database by the binding name configured in your wrangler.toml file.
110    #[cfg(feature = "d1")]
111    pub fn d1(&self, binding: &str) -> Result<crate::D1Database> {
112        self.env.d1(binding)
113    }
114}
115
116impl Router<'_, ()> {
117    /// Construct a new `Router`. Or, call `Router::with_data(D)` to add arbitrary data that will be
118    /// available to your various routes.
119    pub fn new() -> Self {
120        Self::with_data(())
121    }
122}
123
124impl<'a, D: 'a> Router<'a, D> {
125    /// Construct a new `Router` with arbitrary data that will be available to your various routes.
126    pub fn with_data(data: D) -> Self {
127        Self {
128            handlers: HashMap::new(),
129            or_else_any_method: MatchItRouter::new(),
130            data,
131        }
132    }
133
134    /// Register an HTTP handler that will exclusively respond to HEAD requests.
135    pub fn head(mut self, pattern: &str, func: HandlerFn<D>) -> Self {
136        self.add_handler(pattern, Handler::Sync(func), vec![Method::Head]);
137        self
138    }
139
140    /// Register an HTTP handler that will exclusively respond to GET requests.
141    pub fn get(mut self, pattern: &str, func: HandlerFn<D>) -> Self {
142        self.add_handler(pattern, Handler::Sync(func), vec![Method::Get]);
143        self
144    }
145
146    /// Register an HTTP handler that will exclusively respond to POST requests.
147    pub fn post(mut self, pattern: &str, func: HandlerFn<D>) -> Self {
148        self.add_handler(pattern, Handler::Sync(func), vec![Method::Post]);
149        self
150    }
151
152    /// Register an HTTP handler that will exclusively respond to PUT requests.
153    pub fn put(mut self, pattern: &str, func: HandlerFn<D>) -> Self {
154        self.add_handler(pattern, Handler::Sync(func), vec![Method::Put]);
155        self
156    }
157
158    /// Register an HTTP handler that will exclusively respond to PATCH requests.
159    pub fn patch(mut self, pattern: &str, func: HandlerFn<D>) -> Self {
160        self.add_handler(pattern, Handler::Sync(func), vec![Method::Patch]);
161        self
162    }
163
164    /// Register an HTTP handler that will exclusively respond to DELETE requests.
165    pub fn delete(mut self, pattern: &str, func: HandlerFn<D>) -> Self {
166        self.add_handler(pattern, Handler::Sync(func), vec![Method::Delete]);
167        self
168    }
169
170    /// Register an HTTP handler that will exclusively respond to OPTIONS requests.
171    pub fn options(mut self, pattern: &str, func: HandlerFn<D>) -> Self {
172        self.add_handler(pattern, Handler::Sync(func), vec![Method::Options]);
173        self
174    }
175
176    /// Register an HTTP handler that will exclusively respond to REPORT requests.
177    pub fn report(mut self, pattern: &str, func: HandlerFn<D>) -> Self {
178        self.add_handler(pattern, Handler::Sync(func), vec![Method::Report]);
179        self
180    }
181
182    /// Register an HTTP handler that will respond to any requests.
183    pub fn on(mut self, pattern: &str, func: HandlerFn<D>) -> Self {
184        self.add_handler(pattern, Handler::Sync(func), Method::all());
185        self
186    }
187
188    /// Register an HTTP handler that will respond to all methods that are not handled explicitly by
189    /// other handlers.
190    pub fn or_else_any_method(mut self, pattern: &str, func: HandlerFn<D>) -> Self {
191        self.or_else_any_method
192            .insert(pattern, Handler::Sync(func))
193            .unwrap_or_else(|e| panic!("failed to register route for {} pattern: {}", pattern, e));
194        self
195    }
196
197    /// Register an HTTP handler that will exclusively respond to HEAD requests. Enables the use of
198    /// `async/await` syntax in the callback.
199    pub fn head_async<T>(
200        mut self,
201        pattern: &str,
202        func: impl Fn(Request, RouteContext<D>) -> T + 'a,
203    ) -> Self
204    where
205        T: Future<Output = Result<Response>> + 'a,
206    {
207        self.add_handler(
208            pattern,
209            Handler::Async(Rc::new(move |req, info| Box::pin(func(req, info)))),
210            vec![Method::Head],
211        );
212        self
213    }
214
215    /// Register an HTTP handler that will exclusively respond to GET requests. Enables the use of
216    /// `async/await` syntax in the callback.
217    pub fn get_async<T>(
218        mut self,
219        pattern: &str,
220        func: impl Fn(Request, RouteContext<D>) -> T + 'a,
221    ) -> Self
222    where
223        T: Future<Output = Result<Response>> + 'a,
224    {
225        self.add_handler(
226            pattern,
227            Handler::Async(Rc::new(move |req, info| Box::pin(func(req, info)))),
228            vec![Method::Get],
229        );
230        self
231    }
232
233    /// Register an HTTP handler that will exclusively respond to POST requests. Enables the use of
234    /// `async/await` syntax in the callback.
235    pub fn post_async<T>(
236        mut self,
237        pattern: &str,
238        func: impl Fn(Request, RouteContext<D>) -> T + 'a,
239    ) -> Self
240    where
241        T: Future<Output = Result<Response>> + 'a,
242    {
243        self.add_handler(
244            pattern,
245            Handler::Async(Rc::new(move |req, info| Box::pin(func(req, info)))),
246            vec![Method::Post],
247        );
248        self
249    }
250
251    /// Register an HTTP handler that will exclusively respond to PUT requests. Enables the use of
252    /// `async/await` syntax in the callback.
253    pub fn put_async<T>(
254        mut self,
255        pattern: &str,
256        func: impl Fn(Request, RouteContext<D>) -> T + 'a,
257    ) -> Self
258    where
259        T: Future<Output = Result<Response>> + 'a,
260    {
261        self.add_handler(
262            pattern,
263            Handler::Async(Rc::new(move |req, info| Box::pin(func(req, info)))),
264            vec![Method::Put],
265        );
266        self
267    }
268
269    /// Register an HTTP handler that will exclusively respond to PATCH requests. Enables the use of
270    /// `async/await` syntax in the callback.
271    pub fn patch_async<T>(
272        mut self,
273        pattern: &str,
274        func: impl Fn(Request, RouteContext<D>) -> T + 'a,
275    ) -> Self
276    where
277        T: Future<Output = Result<Response>> + 'a,
278    {
279        self.add_handler(
280            pattern,
281            Handler::Async(Rc::new(move |req, info| Box::pin(func(req, info)))),
282            vec![Method::Patch],
283        );
284        self
285    }
286
287    /// Register an HTTP handler that will exclusively respond to DELETE requests. Enables the use
288    /// of `async/await` syntax in the callback.
289    pub fn delete_async<T>(
290        mut self,
291        pattern: &str,
292        func: impl Fn(Request, RouteContext<D>) -> T + 'a,
293    ) -> Self
294    where
295        T: Future<Output = Result<Response>> + 'a,
296    {
297        self.add_handler(
298            pattern,
299            Handler::Async(Rc::new(move |req, info| Box::pin(func(req, info)))),
300            vec![Method::Delete],
301        );
302        self
303    }
304
305    /// Register an HTTP handler that will exclusively respond to OPTIONS requests. Enables the use
306    /// of `async/await` syntax in the callback.
307    pub fn options_async<T>(
308        mut self,
309        pattern: &str,
310        func: impl Fn(Request, RouteContext<D>) -> T + 'a,
311    ) -> Self
312    where
313        T: Future<Output = Result<Response>> + 'a,
314    {
315        self.add_handler(
316            pattern,
317            Handler::Async(Rc::new(move |req, info| Box::pin(func(req, info)))),
318            vec![Method::Options],
319        );
320        self
321    }
322
323    /// Register an HTTP handler that will respond to any requests. Enables the use of `async/await`
324    /// syntax in the callback.
325    pub fn on_async<T>(
326        mut self,
327        pattern: &str,
328        func: impl Fn(Request, RouteContext<D>) -> T + 'a,
329    ) -> Self
330    where
331        T: Future<Output = Result<Response>> + 'a,
332    {
333        self.add_handler(
334            pattern,
335            Handler::Async(Rc::new(move |req, route| Box::pin(func(req, route)))),
336            Method::all(),
337        );
338        self
339    }
340
341    /// Register an HTTP handler that will respond to all methods that are not handled explicitly by
342    /// other handlers. Enables the use of `async/await` syntax in the callback.
343    pub fn or_else_any_method_async<T>(
344        mut self,
345        pattern: &str,
346        func: impl Fn(Request, RouteContext<D>) -> T + 'a,
347    ) -> Self
348    where
349        T: Future<Output = Result<Response>> + 'a,
350    {
351        self.or_else_any_method
352            .insert(
353                pattern,
354                Handler::Async(Rc::new(move |req, route| Box::pin(func(req, route)))),
355            )
356            .unwrap_or_else(|e| panic!("failed to register route for {} pattern: {}", pattern, e));
357        self
358    }
359
360    fn add_handler(&mut self, pattern: &str, func: Handler<'a, D>, methods: Vec<Method>) {
361        for method in methods {
362            self.handlers
363                .entry(method.clone())
364                .or_default()
365                .insert(pattern, func.clone())
366                .unwrap_or_else(|e| {
367                    panic!(
368                        "failed to register {:?} route for {} pattern: {}",
369                        method, pattern, e
370                    )
371                });
372        }
373    }
374
375    /// Handle the request provided to the `Router` and return a `Future`.
376    pub async fn run(self, req: Request, env: Env) -> Result<Response> {
377        let (handlers, data, or_else_any_method_handler) = self.split();
378
379        if let Some(handlers) = handlers.get(&req.method()) {
380            if let Ok(Match { value, params }) = handlers.at(&req.path()) {
381                let route_info = RouteContext {
382                    data,
383                    env,
384                    params: params.into(),
385                };
386                return match value {
387                    Handler::Sync(func) => (func)(req, route_info),
388                    Handler::Async(func) => (func)(req, route_info).await,
389                };
390            }
391        }
392
393        for method in Method::all() {
394            if method == Method::Head || method == Method::Options || method == Method::Trace {
395                continue;
396            }
397            if let Some(handlers) = handlers.get(&method) {
398                if let Ok(Match { .. }) = handlers.at(&req.path()) {
399                    return Response::error("Method Not Allowed", 405);
400                }
401            }
402        }
403
404        if let Ok(Match { value, params }) = or_else_any_method_handler.at(&req.path()) {
405            let route_info = RouteContext {
406                data,
407                env,
408                params: params.into(),
409            };
410            return match value {
411                Handler::Sync(func) => (func)(req, route_info),
412                Handler::Async(func) => (func)(req, route_info).await,
413            };
414        }
415
416        Response::error("Not Found", 404)
417    }
418}
419
420type NodeWithHandlers<'a, D> = MatchItRouter<Handler<'a, D>>;
421
422impl<'a, D: 'a> Router<'a, D> {
423    fn split(
424        self,
425    ) -> (
426        HashMap<Method, NodeWithHandlers<'a, D>>,
427        D,
428        NodeWithHandlers<'a, D>,
429    ) {
430        (self.handlers, self.data, self.or_else_any_method)
431    }
432}
433
434impl From<matchit::Params<'_, '_>> for RouteParams {
435    fn from(p: matchit::Params) -> Self {
436        let mut route_params = RouteParams(HashMap::new());
437        for (ident, value) in p.iter() {
438            route_params.0.insert(ident.into(), value.into());
439        }
440
441        route_params
442    }
443}