Skip to content

Commit 67e175b

Browse files
committed
Routing modes
Signed-off-by: itowlson <[email protected]>
1 parent b1aa715 commit 67e175b

File tree

1 file changed

+72
-2
lines changed

1 file changed

+72
-2
lines changed

src/http/router.rs

Lines changed: 72 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,38 @@ pub type Params = Captures<'static, 'static>;
154154
/// # fn handle_single_segment(req: Request, params: Params) -> anyhow::Result<Response> { todo!() }
155155
/// # fn handle_exact(req: Request, params: Params) -> anyhow::Result<Response> { todo!() }
156156
/// ```
157+
///
158+
/// Route based on the trailing segment of a Spin wildcard route, instead of on the full path
159+
///
160+
/// ```no_run
161+
/// // spin.toml
162+
/// //
163+
/// // [[trigger.http]]
164+
/// // route = "/shop/..."
165+
///
166+
/// // component
167+
/// # use spin_sdk::http::{IntoResponse, Params, Request, Response, Router};
168+
/// fn handle_route(req: Request) -> Response {
169+
/// let mut router = Router::suffix();
170+
/// router.any("/users/*", handle_users);
171+
/// router.any("/products/*", handle_products);
172+
/// router.handle(req)
173+
/// }
174+
///
175+
/// // '/shop/users/1' is routed to `handle_users`
176+
/// // '/shop/products/1' is routed to `handle_products`
177+
/// # fn handle_users(req: Request, params: Params) -> anyhow::Result<Response> { todo!() }
178+
/// # fn handle_products(req: Request, params: Params) -> anyhow::Result<Response> { todo!() }
179+
/// ```
157180
pub struct Router {
158181
methods_map: HashMap<Method, MethodRouter<Box<dyn Handler>>>,
159182
any_methods: MethodRouter<Box<dyn Handler>>,
183+
route_on: RouteOn,
184+
}
185+
186+
enum RouteOn {
187+
FullPath,
188+
Suffix,
160189
}
161190

162191
impl Default for Router {
@@ -203,7 +232,16 @@ impl Router {
203232
Err(e) => return e.into_response(),
204233
};
205234
let method = request.method.clone();
206-
let path = &request.path();
235+
let path = match self.route_on {
236+
RouteOn::FullPath => request.path(),
237+
RouteOn::Suffix => match trailing_suffix(&request) {
238+
Some(path) => path,
239+
None => {
240+
eprintln!("Internal error: Router configured with suffix routing but trigger route has no trailing wildcard");
241+
return responses::internal_server_error();
242+
}
243+
},
244+
};
207245
let RouteMatch { params, handler } = self.find(path, method);
208246
handler.handle(request, params).await
209247
}
@@ -516,11 +554,22 @@ impl Router {
516554
self.add_async(path, Method::Options, handler)
517555
}
518556

519-
/// Construct a new Router.
557+
/// Construct a new Router that matches on the full path.
520558
pub fn new() -> Self {
521559
Router {
522560
methods_map: HashMap::default(),
523561
any_methods: MethodRouter::new(),
562+
route_on: RouteOn::FullPath,
563+
}
564+
}
565+
566+
/// Construct a new Router that matches on the trailing wildcard
567+
/// component of the route.
568+
pub fn suffix() -> Self {
569+
Router {
570+
methods_map: HashMap::default(),
571+
any_methods: MethodRouter::new(),
572+
route_on: RouteOn::Suffix,
524573
}
525574
}
526575
}
@@ -533,6 +582,11 @@ async fn method_not_allowed(_req: Request, _params: Params) -> Response {
533582
responses::method_not_allowed()
534583
}
535584

585+
fn trailing_suffix(req: &Request) -> Option<&str> {
586+
req.header("spin-path-info")
587+
.and_then(|path_info| path_info.as_str())
588+
}
589+
536590
/// A macro to help with constructing a Router from a stream of tokens.
537591
#[macro_export]
538592
macro_rules! http_router {
@@ -579,6 +633,12 @@ mod tests {
579633
Request::new(method, path)
580634
}
581635

636+
fn make_wildcard_request(method: Method, path: &str, trailing: &str) -> Request {
637+
let mut req = Request::new(method, path);
638+
req.set_header("spin-path-info", trailing);
639+
req
640+
}
641+
582642
fn echo_param(_req: Request, params: Params) -> Response {
583643
match params.get("x") {
584644
Some(path) => Response::new(200, path),
@@ -666,6 +726,16 @@ mod tests {
666726
assert_eq!(res.body, "foo".to_owned().into_bytes());
667727
}
668728

729+
#[test]
730+
fn test_spin_trailing_wildcard() {
731+
let mut router = Router::suffix();
732+
router.get("/:x/*", echo_param);
733+
734+
let req = make_wildcard_request(Method::Get, "/base/baser/foo/bar", "/foo/bar");
735+
let res = router.handle(req);
736+
assert_eq!(res.body, "foo".to_owned().into_bytes());
737+
}
738+
669739
#[test]
670740
fn test_router_display() {
671741
let mut router = Router::default();

0 commit comments

Comments
 (0)