Skip to main content

xitca_web/service/
tower_http_compat.rs

1use core::{
2    cell::RefCell,
3    convert::Infallible,
4    pin::Pin,
5    task::{Context, Poll},
6};
7
8use std::borrow::Cow;
9
10use pin_project_lite::pin_project;
11use xitca_http::util::service::router::{PathGen, RouteGen, RouterMapErr};
12use xitca_unsafe_collection::fake::{FakeSend, FakeSync};
13
14use crate::{
15    body::{Body, Frame, ResponseBody, SizeHint},
16    bytes::{Buf, Bytes, BytesMut},
17    context::WebContext,
18    http::{Request, RequestExt, Response, WebResponse},
19    service::{Service, ready::ReadyService},
20};
21
22/// A middleware type that bridge `xitca-service` and `tower-service`.
23/// Any `tower-http` type that impl [tower_service::Service] trait can be passed to it and used as xitca-web's service type.
24pub struct TowerHttpCompat<S>(S);
25
26impl<S> TowerHttpCompat<S> {
27    pub const fn new(service: S) -> Self
28    where
29        S: Clone,
30    {
31        Self(service)
32    }
33}
34
35impl<S> Service for TowerHttpCompat<S>
36where
37    S: Clone,
38{
39    type Response = TowerCompatService<S>;
40    type Error = Infallible;
41
42    async fn call(&self, _: ()) -> Result<Self::Response, Self::Error> {
43        let service = self.0.clone();
44        Ok(TowerCompatService(RefCell::new(service)))
45    }
46}
47
48impl<S> PathGen for TowerHttpCompat<S> {}
49
50impl<S> RouteGen for TowerHttpCompat<S> {
51    type Route<R> = RouterMapErr<R>;
52
53    fn route_gen<R>(route: R) -> Self::Route<R> {
54        RouterMapErr(route)
55    }
56}
57
58pub struct TowerCompatService<S>(RefCell<S>);
59
60impl<S> TowerCompatService<S> {
61    pub const fn new(service: S) -> Self {
62        Self(RefCell::new(service))
63    }
64}
65
66impl<'r, C, ReqB, S, ResB> Service<WebContext<'r, C, ReqB>> for TowerCompatService<S>
67where
68    S: tower_service::Service<Request<CompatReqBody<RequestExt<ReqB>, C>>, Response = Response<ResB>>,
69    ResB: http_body::Body,
70    C: Clone + 'static,
71    ReqB: Default,
72{
73    type Response = WebResponse<CompatBody<ResB>>;
74    type Error = S::Error;
75
76    async fn call(&self, mut ctx: WebContext<'r, C, ReqB>) -> Result<Self::Response, Self::Error> {
77        let (parts, ext) = ctx.take_request().into_parts();
78        let ctx = ctx.state().clone();
79        let req = Request::from_parts(parts, CompatReqBody::new(ext, ctx));
80        let fut = tower_service::Service::call(&mut *self.0.borrow_mut(), req);
81        fut.await.map(|res| res.map(CompatBody::new))
82    }
83}
84
85impl<S> ReadyService for TowerCompatService<S> {
86    type Ready = ();
87
88    #[inline]
89    async fn ready(&self) -> Self::Ready {}
90}
91
92pub struct CompatReqBody<B, C> {
93    body: FakeSend<CompatBody<B>>,
94    ctx: FakeSend<FakeSync<C>>,
95}
96
97impl<B, C> CompatReqBody<B, C> {
98    #[inline]
99    pub fn new(body: B, ctx: C) -> Self {
100        Self {
101            body: FakeSend::new(CompatBody::new(body)),
102            ctx: FakeSend::new(FakeSync::new(ctx)),
103        }
104    }
105
106    /// destruct compat body into owned value of body and state context
107    ///
108    /// # Panics
109    /// - When called from a thread not where B is originally constructed.
110    #[inline]
111    pub fn into_parts(self) -> (B, C) {
112        (self.body.into_inner().into_inner(), self.ctx.into_inner().into_inner())
113    }
114}
115
116impl<B, C> http_body::Body for CompatReqBody<B, C>
117where
118    B: Body + Unpin,
119    C: Unpin,
120    B::Data: Buf,
121{
122    type Data = B::Data;
123    type Error = B::Error;
124
125    #[inline]
126    fn poll_frame(
127        self: Pin<&mut Self>,
128        cx: &mut Context<'_>,
129    ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
130        http_body::Body::poll_frame(Pin::new(&mut *self.get_mut().body), cx)
131    }
132
133    #[inline]
134    fn size_hint(&self) -> http_body::SizeHint {
135        http_body::Body::size_hint(&*self.body)
136    }
137}
138
139pin_project! {
140    #[derive(Default)]
141    pub struct CompatBody<B> {
142        #[pin]
143        body: B
144    }
145}
146
147impl<B> CompatBody<B> {
148    pub const fn new(body: B) -> Self {
149        Self { body }
150    }
151
152    pub fn into_inner(self) -> B {
153        self.body
154    }
155}
156
157// useful shortcuts conversion for B type.
158macro_rules! impl_from {
159    ($ty: ty) => {
160        impl<B> From<$ty> for CompatBody<B>
161        where
162            B: From<$ty>,
163        {
164            fn from(body: $ty) -> Self {
165                Self::new(B::from(body))
166            }
167        }
168    };
169}
170
171impl_from!(Bytes);
172impl_from!(BytesMut);
173impl_from!(&'static [u8]);
174impl_from!(&'static str);
175impl_from!(Box<[u8]>);
176impl_from!(Vec<u8>);
177impl_from!(String);
178impl_from!(Box<str>);
179impl_from!(Cow<'static, str>);
180impl_from!(ResponseBody);
181
182impl<B> http_body::Body for CompatBody<B>
183where
184    B: Body,
185    B::Data: Buf,
186{
187    type Data = B::Data;
188    type Error = B::Error;
189
190    #[inline]
191    fn poll_frame(
192        self: Pin<&mut Self>,
193        cx: &mut Context<'_>,
194    ) -> Poll<Option<Result<http_body::Frame<Self::Data>, Self::Error>>> {
195        Body::poll_frame(self.project().body, cx).map_ok(|frame| match frame {
196            Frame::Data(data) => http_body::Frame::data(data),
197            Frame::Trailers(trailers) => http_body::Frame::trailers(trailers),
198        })
199    }
200
201    #[inline]
202    fn is_end_stream(&self) -> bool {
203        Body::is_end_stream(&self.body)
204    }
205
206    #[inline]
207    fn size_hint(&self) -> http_body::SizeHint {
208        match Body::size_hint(&self.body) {
209            SizeHint::Exact(size) => http_body::SizeHint::with_exact(size),
210            _ => http_body::SizeHint::default(),
211        }
212    }
213}
214
215impl<B> Body for CompatBody<B>
216where
217    B: http_body::Body,
218{
219    type Data = B::Data;
220    type Error = B::Error;
221
222    #[inline]
223    fn poll_frame(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
224        http_body::Body::poll_frame(self.project().body, cx).map_ok(|frame| match frame.into_data() {
225            Ok(data) => Frame::Data(data),
226            Err(frame) => Frame::Trailers(frame.into_trailers().ok().unwrap()),
227        })
228    }
229
230    #[inline]
231    fn is_end_stream(&self) -> bool {
232        http_body::Body::is_end_stream(&self.body)
233    }
234
235    #[inline]
236    fn size_hint(&self) -> SizeHint {
237        match http_body::Body::size_hint(&self.body).exact() {
238            Some(size) => SizeHint::Exact(size),
239            None => SizeHint::Unknown,
240        }
241    }
242}