2021-05-30 13:24:03 +02:00
|
|
|
use crate::{
|
|
|
|
|
body::{Body, BoxBody},
|
|
|
|
|
handler::{Handler, HandlerSvc},
|
2021-06-01 21:15:48 +02:00
|
|
|
response::IntoResponse,
|
|
|
|
|
App, HandleError, IntoService, ResultExt,
|
2021-05-30 13:24:03 +02:00
|
|
|
};
|
|
|
|
|
use bytes::Bytes;
|
|
|
|
|
use futures_util::{future, ready};
|
|
|
|
|
use http::{Method, Request, Response, StatusCode};
|
2021-06-02 22:07:37 +02:00
|
|
|
use itertools::{EitherOrBoth, Itertools};
|
2021-05-30 13:24:03 +02:00
|
|
|
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,
|
2021-06-02 22:07:37 +02:00
|
|
|
str,
|
2021-05-30 13:24:03 +02:00
|
|
|
task::{Context, Poll},
|
|
|
|
|
};
|
2021-05-31 16:28:26 +02:00
|
|
|
use tower::{
|
|
|
|
|
buffer::{Buffer, BufferLayer},
|
|
|
|
|
util::BoxService,
|
2021-06-01 21:15:48 +02:00
|
|
|
BoxError, Layer, Service, ServiceBuilder,
|
2021-05-31 16:28:26 +02:00
|
|
|
};
|
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,
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-01 12:25:28 +02:00
|
|
|
macro_rules! define_route_at_methods {
|
|
|
|
|
(
|
|
|
|
|
RouteAt:
|
|
|
|
|
$name:ident,
|
|
|
|
|
$svc_method_name:ident,
|
|
|
|
|
$method:ident
|
|
|
|
|
) => {
|
|
|
|
|
pub fn $name<F, B, T>(self, handler_fn: F) -> RouteBuilder<Or<HandlerSvc<F, B, T>, R>>
|
|
|
|
|
where
|
|
|
|
|
F: Handler<B, T>,
|
|
|
|
|
{
|
|
|
|
|
self.add_route(handler_fn, Method::$method)
|
|
|
|
|
}
|
2021-05-30 13:24:03 +02:00
|
|
|
|
2021-06-01 12:25:28 +02:00
|
|
|
pub fn $svc_method_name<S, B>(self, service: S) -> RouteBuilder<Or<S, R>>
|
|
|
|
|
where
|
|
|
|
|
S: Service<Request<Body>, Response = Response<B>, Error = Infallible> + Clone,
|
|
|
|
|
{
|
2021-06-03 21:36:39 +02:00
|
|
|
self.add_route_service(service, Method::$method)
|
2021-06-01 12:25:28 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
(
|
|
|
|
|
RouteBuilder:
|
|
|
|
|
$name:ident,
|
|
|
|
|
$svc_method_name:ident,
|
|
|
|
|
$method:ident
|
|
|
|
|
) => {
|
|
|
|
|
pub fn $name<F, B, T>(self, handler_fn: F) -> RouteBuilder<Or<HandlerSvc<F, B, T>, R>>
|
|
|
|
|
where
|
|
|
|
|
F: Handler<B, T>,
|
|
|
|
|
{
|
|
|
|
|
self.app.at_bytes(self.route_spec).$name(handler_fn)
|
|
|
|
|
}
|
2021-05-30 13:24:03 +02:00
|
|
|
|
2021-06-01 12:25:28 +02:00
|
|
|
pub fn $svc_method_name<S, B>(self, service: S) -> RouteBuilder<Or<S, R>>
|
|
|
|
|
where
|
|
|
|
|
S: Service<Request<Body>, Response = Response<B>, Error = Infallible> + Clone,
|
|
|
|
|
{
|
|
|
|
|
self.app.at_bytes(self.route_spec).$svc_method_name(service)
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2021-05-30 13:24:03 +02:00
|
|
|
|
2021-06-01 12:25:28 +02:00
|
|
|
impl<R> RouteAt<R> {
|
|
|
|
|
define_route_at_methods!(RouteAt: get, get_service, GET);
|
|
|
|
|
define_route_at_methods!(RouteAt: post, post_service, POST);
|
|
|
|
|
define_route_at_methods!(RouteAt: put, put_service, PUT);
|
|
|
|
|
define_route_at_methods!(RouteAt: patch, patch_service, PATCH);
|
|
|
|
|
define_route_at_methods!(RouteAt: delete, delete_service, DELETE);
|
|
|
|
|
define_route_at_methods!(RouteAt: head, head_service, HEAD);
|
|
|
|
|
define_route_at_methods!(RouteAt: options, options_service, OPTIONS);
|
|
|
|
|
define_route_at_methods!(RouteAt: connect, connect_service, CONNECT);
|
|
|
|
|
define_route_at_methods!(RouteAt: trace, trace_service, TRACE);
|
2021-05-30 13:24:03 +02:00
|
|
|
|
|
|
|
|
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>,
|
|
|
|
|
{
|
2021-06-03 21:36:39 +02:00
|
|
|
self.add_route_service(HandlerSvc::new(handler), method)
|
2021-06-02 22:07:37 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-03 21:36:39 +02:00
|
|
|
fn add_route_service<S>(self, service: S, method: Method) -> RouteBuilder<Or<S, R>> {
|
2021-06-02 22:07:37 +02:00
|
|
|
let route_spec = self.route_spec.clone();
|
2021-06-03 21:36:39 +02:00
|
|
|
self.add_route_service_with_spec(service, RouteSpec::new(method, route_spec))
|
2021-05-30 13:24:03 +02:00
|
|
|
}
|
|
|
|
|
|
2021-06-02 22:07:37 +02:00
|
|
|
fn add_route_service_with_spec<S>(
|
|
|
|
|
self,
|
|
|
|
|
service: S,
|
|
|
|
|
route_spec: RouteSpec,
|
|
|
|
|
) -> 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-06-02 22:07:37 +02:00
|
|
|
route_spec,
|
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> {
|
2021-06-01 21:15:48 +02:00
|
|
|
fn new(app: App<R>, route_spec: impl Into<Bytes>) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
app,
|
|
|
|
|
route_spec: route_spec.into(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-30 13:24:03 +02:00
|
|
|
pub fn at(self, route_spec: &str) -> RouteAt<R> {
|
|
|
|
|
self.app.at(route_spec)
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-01 12:25:28 +02:00
|
|
|
define_route_at_methods!(RouteBuilder: get, get_service, GET);
|
|
|
|
|
define_route_at_methods!(RouteBuilder: post, post_service, POST);
|
|
|
|
|
define_route_at_methods!(RouteBuilder: put, put_service, PUT);
|
|
|
|
|
define_route_at_methods!(RouteBuilder: patch, patch_service, PATCH);
|
|
|
|
|
define_route_at_methods!(RouteBuilder: delete, delete_service, DELETE);
|
|
|
|
|
define_route_at_methods!(RouteBuilder: head, head_service, HEAD);
|
|
|
|
|
define_route_at_methods!(RouteBuilder: options, options_service, OPTIONS);
|
|
|
|
|
define_route_at_methods!(RouteBuilder: connect, connect_service, CONNECT);
|
|
|
|
|
define_route_at_methods!(RouteBuilder: trace, trace_service, TRACE);
|
2021-05-30 13:24:03 +02:00
|
|
|
|
|
|
|
|
pub fn into_service(self) -> IntoService<R> {
|
2021-06-01 21:15:48 +02:00
|
|
|
IntoService {
|
|
|
|
|
service_tree: self.app.service_tree,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn layer<L>(self, layer: L) -> RouteBuilder<L::Service>
|
|
|
|
|
where
|
|
|
|
|
L: Layer<R>,
|
|
|
|
|
{
|
|
|
|
|
let layered = layer.layer(self.app.service_tree);
|
|
|
|
|
let app = App::new(layered);
|
|
|
|
|
RouteBuilder::new(app, self.route_spec)
|
2021-05-30 13:24:03 +02:00
|
|
|
}
|
2021-05-31 16:28:26 +02:00
|
|
|
|
2021-06-01 21:15:48 +02:00
|
|
|
pub fn handle_error<F, B, Res>(self, f: F) -> RouteBuilder<HandleError<R, F, R::Error>>
|
|
|
|
|
where
|
|
|
|
|
R: Service<Request<Body>, Response = Response<B>>,
|
|
|
|
|
F: FnOnce(R::Error) -> Res,
|
|
|
|
|
Res: IntoResponse<Body>,
|
|
|
|
|
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
|
|
|
|
B::Error: Into<BoxError> + Send + Sync + 'static,
|
|
|
|
|
{
|
|
|
|
|
let svc = HandleError::new(self.app.service_tree, f);
|
|
|
|
|
let app = App::new(svc);
|
|
|
|
|
RouteBuilder::new(app, self.route_spec)
|
|
|
|
|
}
|
2021-06-01 00:34:09 +02:00
|
|
|
|
2021-05-31 16:28:26 +02:00
|
|
|
pub fn boxed<B>(self) -> RouteBuilder<BoxServiceTree<B>>
|
|
|
|
|
where
|
2021-06-01 00:34:09 +02:00
|
|
|
R: Service<Request<Body>, Response = Response<B>, Error = Infallible> + Send + 'static,
|
2021-05-31 16:28:26 +02:00
|
|
|
R::Future: Send,
|
2021-06-01 08:32:58 +02:00
|
|
|
B: From<String> + 'static,
|
2021-05-31 16:28:26 +02:00
|
|
|
{
|
|
|
|
|
let svc = ServiceBuilder::new()
|
|
|
|
|
.layer(BufferLayer::new(1024))
|
|
|
|
|
.layer(BoxService::layer())
|
|
|
|
|
.service(self.app.service_tree);
|
|
|
|
|
|
2021-06-01 21:15:48 +02:00
|
|
|
let app = App::new(BoxServiceTree {
|
|
|
|
|
inner: svc,
|
|
|
|
|
poll_ready_error: None,
|
|
|
|
|
});
|
|
|
|
|
RouteBuilder::new(app, self.route_spec)
|
2021-05-31 16:28:26 +02:00
|
|
|
}
|
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,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-02 22:07:37 +02:00
|
|
|
#[derive(Debug, Clone)]
|
2021-05-30 13:24:03 +02:00
|
|
|
struct RouteSpec {
|
2021-06-03 21:36:39 +02:00
|
|
|
method: Method,
|
2021-05-30 13:24:03 +02:00
|
|
|
spec: Bytes,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RouteSpec {
|
2021-06-03 21:36:39 +02:00
|
|
|
fn new(method: Method, spec: impl Into<Bytes>) -> Self {
|
2021-05-30 15:44:26 +02:00
|
|
|
Self {
|
2021-06-03 21:36:39 +02:00
|
|
|
method,
|
2021-05-30 15:44:26 +02:00
|
|
|
spec: spec.into(),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl RouteSpec {
|
|
|
|
|
fn matches<B>(&self, req: &Request<B>) -> Option<Vec<(String, String)>> {
|
2021-06-03 21:36:39 +02:00
|
|
|
// TODO(david): perform this matching outside
|
|
|
|
|
if req.method() != self.method {
|
|
|
|
|
return None;
|
2021-06-02 22:07:37 +02:00
|
|
|
}
|
2021-05-30 15:44:26 +02:00
|
|
|
|
|
|
|
|
let spec_parts = self.spec.split(|b| *b == b'/');
|
|
|
|
|
|
2021-06-02 22:07:37 +02:00
|
|
|
let path = req.uri().path().as_bytes();
|
|
|
|
|
let path_parts = path.split(|b| *b == b'/');
|
2021-05-30 15:44:26 +02:00
|
|
|
|
|
|
|
|
let mut params = Vec::new();
|
|
|
|
|
|
2021-06-02 22:07:37 +02:00
|
|
|
for pair in spec_parts.zip_longest(path_parts) {
|
|
|
|
|
match pair {
|
|
|
|
|
EitherOrBoth::Both(spec, path) => {
|
|
|
|
|
if let Some(key) = spec.strip_prefix(b":") {
|
|
|
|
|
let key = str::from_utf8(key).unwrap().to_string();
|
|
|
|
|
if let Ok(value) = std::str::from_utf8(path) {
|
|
|
|
|
params.push((key, value.to_string()));
|
|
|
|
|
} else {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
} else if spec != path {
|
|
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-06-03 21:36:39 +02:00
|
|
|
EitherOrBoth::Left(_) | EitherOrBoth::Right(_) => {
|
2021-06-02 22:07:37 +02:00
|
|
|
return None;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Some(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
|
2021-06-01 00:34:09 +02:00
|
|
|
H: Service<Request<Body>, Response = Response<HB>, Error = Infallible>,
|
2021-06-01 11:23:56 +02:00
|
|
|
HB: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
2021-05-30 13:24:03 +02:00
|
|
|
HB::Error: Into<BoxError>,
|
|
|
|
|
|
2021-06-01 00:34:09 +02:00
|
|
|
F: Service<Request<Body>, Response = Response<FB>, Error = Infallible>,
|
2021-06-01 11:23:56 +02:00
|
|
|
FB: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
2021-05-30 13:24:03 +02:00
|
|
|
FB::Error: Into<BoxError>,
|
|
|
|
|
{
|
2021-06-01 11:23:56 +02:00
|
|
|
type Response = Response<BoxBody>;
|
2021-06-01 00:34:09 +02:00
|
|
|
type Error = Infallible;
|
2021-05-30 13:24:03 +02:00
|
|
|
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 {
|
2021-06-01 00:34:09 +02:00
|
|
|
ready!(self.service.poll_ready(cx)).unwrap_infallible();
|
2021-05-30 13:24:03 +02:00
|
|
|
self.handler_ready = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if !self.fallback_ready {
|
2021-06-01 00:34:09 +02:00
|
|
|
ready!(self.fallback.poll_ready(cx)).unwrap_infallible();
|
2021-05-30 13:24:03 +02:00
|
|
|
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-06-03 21:36:39 +02:00
|
|
|
insert_url_params(&mut req, params);
|
2021-05-30 15:44:26 +02:00
|
|
|
|
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-06-03 21:36:39 +02:00
|
|
|
#[derive(Debug)]
|
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);
|
|
|
|
|
|
2021-06-01 00:34:09 +02:00
|
|
|
impl<F, B> Future for BoxResponseBody<F>
|
2021-05-30 13:24:03 +02:00
|
|
|
where
|
2021-06-01 00:34:09 +02:00
|
|
|
F: Future<Output = Result<Response<B>, Infallible>>,
|
2021-06-01 11:23:56 +02:00
|
|
|
B: http_body::Body<Data = Bytes> + Send + Sync + 'static,
|
2021-05-30 13:24:03 +02:00
|
|
|
B::Error: Into<BoxError>,
|
|
|
|
|
{
|
2021-06-01 11:23:56 +02:00
|
|
|
type Output = Result<Response<BoxBody>, Infallible>;
|
2021-05-30 13:24:03 +02:00
|
|
|
|
|
|
|
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
2021-06-01 00:34:09 +02:00
|
|
|
let response: Response<B> = ready!(self.project().0.poll(cx)).unwrap_infallible();
|
2021-05-30 13:24:03 +02:00
|
|
|
let response = response.map(|body| {
|
2021-06-01 00:34:09 +02:00
|
|
|
let body = body.map_err(Into::into);
|
2021-05-30 13:24:03 +02:00
|
|
|
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> {
|
2021-06-01 00:34:09 +02:00
|
|
|
inner: Buffer<BoxService<Request<Body>, Response<B>, Infallible>, Request<Body>>,
|
|
|
|
|
poll_ready_error: Option<BoxError>,
|
2021-05-31 16:28:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<B> Clone for BoxServiceTree<B> {
|
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
inner: self.inner.clone(),
|
2021-06-01 00:34:09 +02:00
|
|
|
poll_ready_error: None,
|
2021-05-31 16:28:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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
|
2021-06-01 00:34:09 +02:00
|
|
|
B: From<String> + 'static,
|
2021-05-31 16:28:26 +02:00
|
|
|
{
|
|
|
|
|
type Response = Response<B>;
|
2021-06-01 00:34:09 +02:00
|
|
|
type Error = Infallible;
|
2021-05-31 16:28:26 +02:00
|
|
|
type Future = BoxServiceTreeResponseFuture<B>;
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
2021-06-01 00:34:09 +02:00
|
|
|
// TODO(david): downcast this into one of the cases in `tower::buffer::error`
|
|
|
|
|
// and convert the error into a response. `ServiceError` should never be able to happen
|
|
|
|
|
// since all inner services use `Infallible` as the error type.
|
|
|
|
|
match ready!(self.inner.poll_ready(cx)) {
|
|
|
|
|
Ok(_) => Poll::Ready(Ok(())),
|
|
|
|
|
Err(err) => {
|
|
|
|
|
self.poll_ready_error = Some(err);
|
|
|
|
|
Poll::Ready(Ok(()))
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-05-31 16:28:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[inline]
|
|
|
|
|
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
2021-06-01 00:34:09 +02:00
|
|
|
if let Some(err) = self.poll_ready_error.take() {
|
|
|
|
|
return BoxServiceTreeResponseFuture {
|
|
|
|
|
kind: Kind::Response(Some(handle_buffer_error(err))),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2021-05-31 16:28:26 +02:00
|
|
|
BoxServiceTreeResponseFuture {
|
2021-06-01 00:34:09 +02:00
|
|
|
kind: Kind::Future(self.inner.call(req)),
|
2021-05-31 16:28:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[pin_project]
|
|
|
|
|
pub struct BoxServiceTreeResponseFuture<B> {
|
|
|
|
|
#[pin]
|
2021-06-01 00:34:09 +02:00
|
|
|
kind: Kind<B>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[pin_project(project = KindProj)]
|
|
|
|
|
enum Kind<B> {
|
|
|
|
|
Response(Option<Response<B>>),
|
|
|
|
|
Future(#[pin] InnerFuture<B>),
|
2021-05-31 16:28:26 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type InnerFuture<B> = tower::buffer::future::ResponseFuture<
|
2021-06-01 00:34:09 +02:00
|
|
|
Pin<Box<dyn Future<Output = Result<Response<B>, Infallible>> + Send + 'static>>,
|
2021-05-31 16:28:26 +02:00
|
|
|
>;
|
|
|
|
|
|
2021-06-01 00:34:09 +02:00
|
|
|
impl<B> Future for BoxServiceTreeResponseFuture<B>
|
|
|
|
|
where
|
|
|
|
|
B: From<String>,
|
|
|
|
|
{
|
|
|
|
|
type Output = Result<Response<B>, Infallible>;
|
2021-05-31 16:28:26 +02:00
|
|
|
|
|
|
|
|
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
2021-06-01 00:34:09 +02:00
|
|
|
match self.project().kind.project() {
|
|
|
|
|
KindProj::Response(res) => Poll::Ready(Ok(res.take().unwrap())),
|
|
|
|
|
KindProj::Future(future) => match ready!(future.poll(cx)) {
|
|
|
|
|
Ok(res) => Poll::Ready(Ok(res)),
|
|
|
|
|
Err(err) => Poll::Ready(Ok(handle_buffer_error(err))),
|
|
|
|
|
},
|
|
|
|
|
}
|
2021-05-31 16:28:26 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-01 00:34:09 +02:00
|
|
|
fn handle_buffer_error<B>(error: BoxError) -> Response<B>
|
|
|
|
|
where
|
|
|
|
|
B: From<String>,
|
|
|
|
|
{
|
|
|
|
|
use tower::buffer::error::{Closed, ServiceError};
|
|
|
|
|
|
|
|
|
|
let error = match error.downcast::<Closed>() {
|
|
|
|
|
Ok(closed) => {
|
|
|
|
|
return Response::builder()
|
|
|
|
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
|
|
|
.body(B::from(closed.to_string()))
|
|
|
|
|
.unwrap();
|
|
|
|
|
}
|
|
|
|
|
Err(e) => e,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let error = match error.downcast::<ServiceError>() {
|
|
|
|
|
Ok(service_error) => {
|
|
|
|
|
return Response::builder()
|
|
|
|
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
|
|
|
.body(B::from(format!("Service error: {}. This is a bug in tower-web. All inner services should be infallible. Please file an issue", service_error)))
|
|
|
|
|
.unwrap();
|
|
|
|
|
}
|
|
|
|
|
Err(e) => e,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Response::builder()
|
|
|
|
|
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
|
|
|
|
.body(B::from(format!(
|
|
|
|
|
"Uncountered an unknown error: {}. This should never happen. Please file an issue",
|
|
|
|
|
error
|
|
|
|
|
)))
|
|
|
|
|
.unwrap()
|
|
|
|
|
}
|
|
|
|
|
|
2021-06-03 21:36:39 +02:00
|
|
|
fn insert_url_params<B>(req: &mut Request<B>, params: Vec<(String, String)>) {
|
|
|
|
|
if let Some(current) = req.extensions_mut().get_mut::<Option<UrlParams>>() {
|
|
|
|
|
let mut current = current.take().unwrap();
|
|
|
|
|
current.0.extend(params);
|
|
|
|
|
req.extensions_mut().insert(Some(current));
|
|
|
|
|
} else {
|
|
|
|
|
req.extensions_mut().insert(Some(UrlParams(params)));
|
2021-06-02 22:07:37 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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)) {
|
2021-06-03 21:36:39 +02:00
|
|
|
let route = RouteSpec::new(route_spec.0.clone(), route_spec.1);
|
2021-05-30 15:44:26 +02:00
|
|
|
let req = Request::builder()
|
|
|
|
|
.method(req_spec.0.clone())
|
|
|
|
|
.uri(req_spec.1)
|
|
|
|
|
.body(())
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
|
route.matches(&req).is_some(),
|
2021-06-02 22:07:37 +02:00
|
|
|
"`{} {}` doesn't match `{:?} {}`",
|
2021-05-30 15:44:26 +02:00
|
|
|
req.method(),
|
|
|
|
|
req.uri().path(),
|
2021-06-03 21:36:39 +02:00
|
|
|
route.method,
|
2021-06-02 22:07:37 +02:00
|
|
|
str::from_utf8(&route.spec).unwrap(),
|
2021-05-30 15:44:26 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn refute_match(route_spec: (Method, &'static str), req_spec: (Method, &'static str)) {
|
2021-06-03 21:36:39 +02:00
|
|
|
let route = RouteSpec::new(route_spec.0.clone(), route_spec.1);
|
2021-05-30 15:44:26 +02:00
|
|
|
let req = Request::builder()
|
|
|
|
|
.method(req_spec.0.clone())
|
|
|
|
|
.uri(req_spec.1)
|
|
|
|
|
.body(())
|
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
|
|
assert!(
|
|
|
|
|
route.matches(&req).is_none(),
|
2021-06-02 22:07:37 +02:00
|
|
|
"`{} {}` shouldn't match `{:?} {}`",
|
2021-05-30 15:44:26 +02:00
|
|
|
req.method(),
|
|
|
|
|
req.uri().path(),
|
2021-06-03 21:36:39 +02:00
|
|
|
route.method,
|
2021-06-02 22:07:37 +02:00
|
|
|
str::from_utf8(&route.spec).unwrap(),
|
|
|
|
|
);
|
|
|
|
|
}
|
2021-05-30 15:44:26 +02:00
|
|
|
}
|