Files
axum/src/routing.rs
T

488 lines
13 KiB
Rust
Raw Normal View History

2021-05-30 13:24:03 +02:00
use crate::{
body::{Body, BoxBody},
error::Error,
handler::{Handler, HandlerSvc},
App, IntoService,
};
use bytes::Bytes;
use futures_util::{future, ready};
use http::{Method, Request, Response, StatusCode};
use pin_project::pin_project;
use std::{
convert::Infallible,
2021-05-31 16:28:26 +02:00
fmt,
2021-05-30 13:24:03 +02:00
future::Future,
pin::Pin,
task::{Context, Poll},
};
2021-05-31 16:28:26 +02:00
use tower::{
buffer::{Buffer, BufferLayer},
util::BoxService,
BoxError, Service, ServiceBuilder,
};
2021-05-30 13:24:03 +02:00
#[derive(Clone, Copy)]
2021-05-31 16:28:26 +02:00
pub struct AlwaysNotFound(pub(crate) ());
2021-05-30 13:24:03 +02:00
2021-05-31 16:28:26 +02:00
impl<R> Service<R> for AlwaysNotFound {
2021-05-30 13:24:03 +02:00
type Response = Response<Body>;
type Error = Infallible;
type Future = future::Ready<Result<Self::Response, Self::Error>>;
fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
fn call(&mut self, _req: R) -> Self::Future {
let mut res = Response::new(Body::empty());
*res.status_mut() = StatusCode::NOT_FOUND;
future::ok(res)
}
}
#[derive(Debug, Clone)]
pub struct RouteAt<R> {
pub(crate) app: App<R>,
pub(crate) route_spec: Bytes,
}
impl<R> RouteAt<R> {
2021-05-31 16:28:26 +02:00
pub fn get<F, B, T>(self, handler_fn: F) -> RouteBuilder<Or<HandlerSvc<F, B, T>, R>>
2021-05-30 13:24:03 +02:00
where
F: Handler<B, T>,
{
self.add_route(handler_fn, Method::GET)
}
2021-05-31 16:28:26 +02:00
pub fn get_service<S, B>(self, service: S) -> RouteBuilder<Or<S, R>>
2021-05-30 13:24:03 +02:00
where
S: Service<Request<Body>, Response = Response<B>> + Clone,
S::Error: Into<BoxError>,
{
self.add_route_service(service, Method::GET)
}
2021-05-31 16:28:26 +02:00
pub fn post<F, B, T>(self, handler_fn: F) -> RouteBuilder<Or<HandlerSvc<F, B, T>, R>>
2021-05-30 13:24:03 +02:00
where
F: Handler<B, T>,
{
self.add_route(handler_fn, Method::POST)
}
2021-05-31 16:28:26 +02:00
pub fn post_service<S, B>(self, service: S) -> RouteBuilder<Or<S, R>>
2021-05-30 13:24:03 +02:00
where
S: Service<Request<Body>, Response = Response<B>> + Clone,
S::Error: Into<BoxError>,
{
self.add_route_service(service, Method::POST)
}
fn add_route<H, B, T>(
self,
handler: H,
method: Method,
2021-05-31 16:28:26 +02:00
) -> RouteBuilder<Or<HandlerSvc<H, B, T>, R>>
2021-05-30 13:24:03 +02:00
where
H: Handler<B, T>,
{
self.add_route_service(HandlerSvc::new(handler), method)
}
2021-05-31 16:28:26 +02:00
fn add_route_service<S>(self, service: S, method: Method) -> RouteBuilder<Or<S, R>> {
2021-05-30 15:44:26 +02:00
assert!(
self.route_spec.starts_with(b"/"),
"route spec must start with a slash (`/`)"
);
2021-05-30 13:24:03 +02:00
let new_app = App {
2021-05-31 16:28:26 +02:00
service_tree: Or {
2021-05-30 13:24:03 +02:00
service,
2021-05-30 15:44:26 +02:00
route_spec: RouteSpec::new(method, self.route_spec.clone()),
2021-05-31 16:28:26 +02:00
fallback: self.app.service_tree,
2021-05-30 13:24:03 +02:00
handler_ready: false,
fallback_ready: false,
},
};
RouteBuilder {
app: new_app,
route_spec: self.route_spec,
}
}
}
pub struct RouteBuilder<R> {
app: App<R>,
route_spec: Bytes,
}
impl<R> Clone for RouteBuilder<R>
where
R: Clone,
{
fn clone(&self) -> Self {
Self {
app: self.app.clone(),
route_spec: self.route_spec.clone(),
}
}
}
impl<R> RouteBuilder<R> {
pub fn at(self, route_spec: &str) -> RouteAt<R> {
self.app.at(route_spec)
}
2021-05-31 16:28:26 +02:00
pub fn get<F, B, T>(self, handler_fn: F) -> RouteBuilder<Or<HandlerSvc<F, B, T>, R>>
2021-05-30 13:24:03 +02:00
where
F: Handler<B, T>,
{
self.app.at_bytes(self.route_spec).get(handler_fn)
}
2021-05-31 16:28:26 +02:00
pub fn get_service<S, B>(self, service: S) -> RouteBuilder<Or<S, R>>
2021-05-30 13:24:03 +02:00
where
S: Service<Request<Body>, Response = Response<B>> + Clone,
S::Error: Into<BoxError>,
{
self.app.at_bytes(self.route_spec).get_service(service)
}
2021-05-31 16:28:26 +02:00
pub fn post<F, B, T>(self, handler_fn: F) -> RouteBuilder<Or<HandlerSvc<F, B, T>, R>>
2021-05-30 13:24:03 +02:00
where
F: Handler<B, T>,
{
self.app.at_bytes(self.route_spec).post(handler_fn)
}
2021-05-31 16:28:26 +02:00
pub fn post_service<S, B>(self, service: S) -> RouteBuilder<Or<S, R>>
2021-05-30 13:24:03 +02:00
where
S: Service<Request<Body>, Response = Response<B>> + Clone,
S::Error: Into<BoxError>,
{
self.app.at_bytes(self.route_spec).post_service(service)
}
pub fn into_service(self) -> IntoService<R> {
IntoService {
app: self.app,
poll_ready_error: None,
}
}
2021-05-31 16:28:26 +02:00
pub fn boxed<B>(self) -> RouteBuilder<BoxServiceTree<B>>
where
R: Service<Request<Body>, Response = Response<B>, Error = Error> + Send + 'static,
R::Future: Send,
B: Default + 'static,
{
let svc = ServiceBuilder::new()
.layer(BufferLayer::new(1024))
.layer(BoxService::layer())
.service(self.app.service_tree);
let app = App {
service_tree: BoxServiceTree { inner: svc },
};
RouteBuilder {
app,
route_spec: self.route_spec,
}
}
2021-05-30 13:24:03 +02:00
}
2021-05-31 16:28:26 +02:00
pub struct Or<H, F> {
2021-05-30 13:24:03 +02:00
service: H,
route_spec: RouteSpec,
fallback: F,
handler_ready: bool,
fallback_ready: bool,
}
2021-05-31 16:28:26 +02:00
impl<H, F> Clone for Or<H, F>
2021-05-30 13:24:03 +02:00
where
H: Clone,
F: Clone,
{
fn clone(&self) -> Self {
Self {
service: self.service.clone(),
fallback: self.fallback.clone(),
route_spec: self.route_spec.clone(),
// important to reset readiness when cloning
handler_ready: false,
fallback_ready: false,
}
}
}
#[derive(Clone)]
struct RouteSpec {
method: Method,
spec: Bytes,
}
impl RouteSpec {
2021-05-30 15:44:26 +02:00
fn new(method: Method, spec: impl Into<Bytes>) -> Self {
Self {
method,
spec: spec.into(),
}
}
}
impl RouteSpec {
fn matches<B>(&self, req: &Request<B>) -> Option<Vec<(String, String)>> {
if req.method() != self.method {
return None;
}
let path = req.uri().path().as_bytes();
let path_parts = path.split(|b| *b == b'/');
let spec_parts = self.spec.split(|b| *b == b'/');
if spec_parts.clone().count() != path_parts.clone().count() {
return None;
}
let mut params = Vec::new();
spec_parts
.zip(path_parts)
.all(|(spec, path)| {
if let Some(key) = spec.strip_prefix(b":") {
let key = std::str::from_utf8(key).unwrap().to_string();
if let Ok(value) = std::str::from_utf8(path) {
params.push((key, value.to_string()));
true
} else {
false
}
} else {
spec == path
}
})
.then(|| params)
2021-05-30 13:24:03 +02:00
}
}
2021-05-31 16:28:26 +02:00
impl<H, F, HB, FB> Service<Request<Body>> for Or<H, F>
2021-05-30 13:24:03 +02:00
where
H: Service<Request<Body>, Response = Response<HB>>,
H::Error: Into<Error>,
HB: http_body::Body + Send + Sync + 'static,
HB::Error: Into<BoxError>,
F: Service<Request<Body>, Response = Response<FB>>,
F::Error: Into<Error>,
FB: http_body::Body<Data = HB::Data> + Send + Sync + 'static,
FB::Error: Into<BoxError>,
{
type Response = Response<BoxBody<HB::Data, Error>>;
type Error = Error;
type Future = future::Either<BoxResponseBody<H::Future>, BoxResponseBody<F::Future>>;
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
loop {
if !self.handler_ready {
ready!(self.service.poll_ready(cx)).map_err(Into::into)?;
self.handler_ready = true;
}
if !self.fallback_ready {
ready!(self.fallback.poll_ready(cx)).map_err(Into::into)?;
self.fallback_ready = true;
}
if self.handler_ready && self.fallback_ready {
return Poll::Ready(Ok(()));
}
}
}
2021-05-30 15:44:26 +02:00
fn call(&mut self, mut req: Request<Body>) -> Self::Future {
if let Some(params) = self.route_spec.matches(&req) {
2021-05-30 13:24:03 +02:00
assert!(
self.handler_ready,
"handler not ready. Did you forget to call `poll_ready`?"
);
self.handler_ready = false;
2021-05-30 15:44:26 +02:00
req.extensions_mut().insert(Some(UrlParams(params)));
2021-05-30 13:24:03 +02:00
future::Either::Left(BoxResponseBody(self.service.call(req)))
} else {
assert!(
self.fallback_ready,
"fallback not ready. Did you forget to call `poll_ready`?"
);
self.fallback_ready = false;
// TODO(david): this leads to each route creating one box body, probably not great
future::Either::Right(BoxResponseBody(self.fallback.call(req)))
}
}
}
2021-05-30 15:44:26 +02:00
pub(crate) struct UrlParams(pub(crate) Vec<(String, String)>);
2021-05-30 13:24:03 +02:00
#[pin_project]
pub struct BoxResponseBody<F>(#[pin] F);
impl<F, B, E> Future for BoxResponseBody<F>
where
F: Future<Output = Result<Response<B>, E>>,
E: Into<Error>,
B: http_body::Body + Send + Sync + 'static,
B::Error: Into<BoxError>,
{
type Output = Result<Response<BoxBody<B::Data, Error>>, Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let response: Response<B> = ready!(self.project().0.poll(cx)).map_err(Into::into)?;
let response = response.map(|body| {
// TODO(david): attempt to downcast this into `Error`
let body = body.map_err(|err| Error::ResponseBody(err.into()));
BoxBody::new(body)
});
Poll::Ready(Ok(response))
}
}
2021-05-30 15:44:26 +02:00
2021-05-31 16:28:26 +02:00
pub struct BoxServiceTree<B> {
inner: Buffer<BoxService<Request<Body>, Response<B>, Error>, Request<Body>>,
}
impl<B> Clone for BoxServiceTree<B> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<B> fmt::Debug for BoxServiceTree<B> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("BoxServiceTree").finish()
}
}
impl<B> Service<Request<Body>> for BoxServiceTree<B>
where
B: 'static,
{
type Response = Response<B>;
type Error = Error;
type Future = BoxServiceTreeResponseFuture<B>;
#[inline]
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
self.inner.poll_ready(cx).map_err(Error::from_service_error)
}
#[inline]
fn call(&mut self, req: Request<Body>) -> Self::Future {
BoxServiceTreeResponseFuture {
inner: self.inner.call(req),
}
}
}
#[pin_project]
pub struct BoxServiceTreeResponseFuture<B> {
#[pin]
inner: InnerFuture<B>,
}
type InnerFuture<B> = tower::buffer::future::ResponseFuture<
Pin<Box<dyn Future<Output = Result<Response<B>, Error>> + Send + 'static>>,
>;
impl<B> Future for BoxServiceTreeResponseFuture<B> {
type Output = Result<Response<B>, Error>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
self.project()
.inner
.poll(cx)
.map_err(Error::from_service_error)
}
}
2021-05-30 15:44:26 +02:00
#[cfg(test)]
mod tests {
#[allow(unused_imports)]
use super::*;
#[test]
fn test_routing() {
assert_match((Method::GET, "/"), (Method::GET, "/"));
refute_match((Method::GET, "/"), (Method::POST, "/"));
refute_match((Method::POST, "/"), (Method::GET, "/"));
assert_match((Method::GET, "/foo"), (Method::GET, "/foo"));
assert_match((Method::GET, "/foo/"), (Method::GET, "/foo/"));
refute_match((Method::GET, "/foo"), (Method::GET, "/foo/"));
refute_match((Method::GET, "/foo/"), (Method::GET, "/foo"));
assert_match((Method::GET, "/foo/bar"), (Method::GET, "/foo/bar"));
refute_match((Method::GET, "/foo/bar/"), (Method::GET, "/foo/bar"));
refute_match((Method::GET, "/foo/bar"), (Method::GET, "/foo/bar/"));
assert_match((Method::GET, "/:value"), (Method::GET, "/foo"));
assert_match((Method::GET, "/users/:id"), (Method::GET, "/users/1"));
assert_match(
(Method::GET, "/users/:id/action"),
(Method::GET, "/users/42/action"),
);
refute_match(
(Method::GET, "/users/:id/action"),
(Method::GET, "/users/42"),
);
refute_match(
(Method::GET, "/users/:id"),
(Method::GET, "/users/42/action"),
);
}
fn assert_match(route_spec: (Method, &'static str), req_spec: (Method, &'static str)) {
let route = RouteSpec::new(route_spec.0.clone(), route_spec.1);
let req = Request::builder()
.method(req_spec.0.clone())
.uri(req_spec.1)
.body(())
.unwrap();
assert!(
route.matches(&req).is_some(),
"`{} {}` doesn't match `{} {}`",
req.method(),
req.uri().path(),
route.method,
std::str::from_utf8(&route.spec).unwrap(),
);
}
fn refute_match(route_spec: (Method, &'static str), req_spec: (Method, &'static str)) {
let route = RouteSpec::new(route_spec.0.clone(), route_spec.1);
let req = Request::builder()
.method(req_spec.0.clone())
.uri(req_spec.1)
.body(())
.unwrap();
assert!(
route.matches(&req).is_none(),
"`{} {}` shouldn't match `{} {}`",
req.method(),
req.uri().path(),
route.method,
std::str::from_utf8(&route.spec).unwrap(),
);
}
}