warp/filter/
service.rs

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