warp/filter/
service.rs

1use std::convert::Infallible;
2use std::future::Future;
3use std::pin::Pin;
4use std::task::{Context, Poll};
5
6use futures_util::future::TryFuture;
7use pin_project::pin_project;
8use tower_service::Service;
9
10use crate::reject::IsReject;
11use crate::reply::{Reply, Response};
12use crate::route::{self, Route};
13use crate::{Filter, Request};
14
15/// Convert a `Filter` into a `Service`.
16///
17/// Filters are normally what APIs are built on in warp. However, it can be
18/// useful to convert a `Filter` into a [`Service`][Service], such as if
19/// further customizing a `hyper::Service`, or if wanting to make use of
20/// the greater [Tower][tower] set of middleware.
21///
22/// # Example
23///
24/// Running a `warp::Filter` on a regular `hyper::Server`:
25///
26/// ```
27/// # async fn run() -> Result<(), Box<dyn std::error::Error>> {
28/// use std::convert::Infallible;
29/// use warp::Filter;
30///
31/// // Our Filter...
32/// let route = warp::any().map(|| "Hello From Warp!");
33///
34/// // Convert it into a `Service`...
35/// let svc = warp::service(route);
36/// # drop(svc);
37/// # Ok(())
38/// # }
39/// ```
40///
41/// [Service]: https://docs.rs/tower_service/latest/tower_service/trait.Service.html
42/// [tower]: https://docs.rs/tower
43pub fn service<F>(filter: F) -> FilteredService<F>
44where
45    F: Filter,
46    <F::Future as TryFuture>::Ok: Reply,
47    <F::Future as TryFuture>::Error: IsReject,
48{
49    FilteredService { filter }
50}
51
52#[derive(Copy, Clone, Debug)]
53pub struct FilteredService<F> {
54    filter: F,
55}
56
57impl<F> FilteredService<F>
58where
59    F: Filter,
60    <F::Future as TryFuture>::Ok: Reply,
61    <F::Future as TryFuture>::Error: IsReject,
62{
63    #[inline]
64    pub(crate) fn call_route(&self, req: Request) -> FilteredFuture<F::Future> {
65        debug_assert!(!route::is_set(), "nested route::set calls");
66
67        let route = Route::new(req);
68        let fut = route::set(&route, || self.filter.filter(super::Internal));
69        FilteredFuture { future: fut, route }
70    }
71}
72
73impl<F, B> Service<http::Request<B>> for FilteredService<F>
74where
75    F: Filter,
76    <F::Future as TryFuture>::Ok: Reply,
77    <F::Future as TryFuture>::Error: IsReject,
78    B: http_body::Body + Send + Sync + 'static,
79    B::Error: Into<Box<dyn std::error::Error + Send + Sync>>,
80{
81    type Response = Response;
82    type Error = Infallible;
83    type Future = FilteredFuture<F::Future>;
84
85    fn poll_ready(&mut self, _: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
86        Poll::Ready(Ok(()))
87    }
88
89    #[inline]
90    fn call(&mut self, req: http::Request<B>) -> Self::Future {
91        let req = req.map(crate::bodyt::Body::wrap);
92        self.call_route(req)
93    }
94}
95
96#[pin_project]
97#[derive(Debug)]
98pub struct FilteredFuture<F> {
99    #[pin]
100    future: F,
101    route: ::std::cell::RefCell<Route>,
102}
103
104impl<F> Future for FilteredFuture<F>
105where
106    F: TryFuture,
107    F::Ok: Reply,
108    F::Error: IsReject,
109{
110    type Output = Result<Response, Infallible>;
111
112    #[inline]
113    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
114        debug_assert!(!route::is_set(), "nested route::set calls");
115
116        let pin = self.project();
117        let fut = pin.future;
118        match route::set(pin.route, || fut.try_poll(cx)) {
119            Poll::Ready(Ok(ok)) => Poll::Ready(Ok(ok.into_response())),
120            Poll::Pending => Poll::Pending,
121            Poll::Ready(Err(err)) => {
122                tracing::debug!("rejected: {:?}", err);
123                Poll::Ready(Ok(err.into_response()))
124            }
125        }
126    }
127}