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