1use std::any::Any;
14use std::fmt;
15use std::sync::Arc;
16
17use bytes::Bytes;
18use http::{Request, Response};
19
20use super::middleware::{Exchange, HttpMiddleware};
21use super::{HttpClientExt, LazyBody, MultipartForm, Result, StreamingResponse};
22use crate::wasm_compat::{WasmBoxedFuture, WasmCompatSend, WasmCompatSync};
23
24pub(crate) trait ErasedHttpClient: WasmCompatSend + WasmCompatSync {
28 fn send_bytes(
29 &self,
30 req: Request<Bytes>,
31 ) -> WasmBoxedFuture<'static, Result<Response<LazyBody<Bytes>>>>;
32
33 fn send_multipart_bytes(
34 &self,
35 req: Request<MultipartForm>,
36 ) -> WasmBoxedFuture<'static, Result<Response<LazyBody<Bytes>>>>;
37
38 fn send_streaming_bytes(
41 &self,
42 req: Request<Bytes>,
43 ) -> WasmBoxedFuture<'_, Result<StreamingResponse>>;
44}
45
46impl<H> ErasedHttpClient for H
47where
48 H: HttpClientExt + 'static,
49{
50 fn send_bytes(
51 &self,
52 req: Request<Bytes>,
53 ) -> WasmBoxedFuture<'static, Result<Response<LazyBody<Bytes>>>> {
54 Box::pin(self.send::<Bytes, Bytes>(req))
55 }
56
57 fn send_multipart_bytes(
58 &self,
59 req: Request<MultipartForm>,
60 ) -> WasmBoxedFuture<'static, Result<Response<LazyBody<Bytes>>>> {
61 Box::pin(self.send_multipart::<Bytes>(req))
62 }
63
64 fn send_streaming_bytes(
65 &self,
66 req: Request<Bytes>,
67 ) -> WasmBoxedFuture<'_, Result<StreamingResponse>> {
68 Box::pin(self.send_streaming::<Bytes>(req))
69 }
70}
71
72#[derive(Clone)]
84pub struct DynHttpClient {
85 inner: Arc<dyn ErasedHttpClient>,
86 middleware: Vec<Arc<dyn HttpMiddleware>>,
89}
90
91impl DynHttpClient {
92 pub fn new<H>(http: H) -> Self
95 where
96 H: HttpClientExt + 'static,
97 {
98 if let Some(already) = (&http as &dyn Any).downcast_ref::<DynHttpClient>() {
99 return already.clone();
100 }
101 Self {
102 inner: Arc::new(http),
103 middleware: Vec::new(),
104 }
105 }
106
107 pub fn with_middleware<M>(mut self, middleware: M) -> Self
113 where
114 M: HttpMiddleware + 'static,
115 {
116 self.middleware.push(Arc::new(middleware));
117 self
118 }
119
120 pub fn ptr_eq(&self, other: &Self) -> bool {
125 Arc::ptr_eq(&self.inner, &other.inner)
126 }
127
128 async fn apply_request_middleware(
131 &self,
132 parts: &mut http::request::Parts,
133 body: Bytes,
134 ) -> Result<Bytes> {
135 self.apply_header_middleware(parts).await?;
136 let exchange = Exchange {
137 method: &parts.method,
138 uri: &parts.uri,
139 extensions: &parts.extensions,
140 };
141 let mut body = body;
142 for mw in &self.middleware {
143 body = mw
144 .before_request_body(exchange, &parts.headers, body)
145 .await?;
146 }
147 Ok(body)
148 }
149
150 async fn apply_header_middleware(&self, parts: &mut http::request::Parts) -> Result<()> {
153 for mw in &self.middleware {
154 let exchange = Exchange {
155 method: &parts.method,
156 uri: &parts.uri,
157 extensions: &parts.extensions,
158 };
159 mw.before_request_headers(exchange, &mut parts.headers)
160 .await?;
161 }
162 Ok(())
163 }
164
165 async fn observe<T, B, F>(
171 &self,
172 parts: http::request::Parts,
173 body: T,
174 send: impl FnOnce(Request<T>) -> F,
175 ) -> Result<Response<B>>
176 where
177 F: Future<Output = Result<Response<B>>>,
178 {
179 let (method, uri, extensions) = (
180 parts.method.clone(),
181 parts.uri.clone(),
182 parts.extensions.clone(),
183 );
184 let exchange = Exchange {
185 method: &method,
186 uri: &uri,
187 extensions: &extensions,
188 };
189 let error = match send(Request::from_parts(parts, body)).await {
190 Ok(response) => {
191 for mw in &self.middleware {
192 mw.after_response(exchange, response.status(), response.headers())
193 .await?;
194 }
195 return Ok(response);
196 }
197 Err(error) => error,
198 };
199 if let (Some(status), Some(headers)) =
200 (error.non_success_status(), error.non_success_headers())
201 {
202 for mw in &self.middleware {
203 if mw.after_response(exchange, status, headers).await.is_err() {
204 break;
205 }
206 }
207 }
208 Err(error)
209 }
210}
211
212impl fmt::Debug for DynHttpClient {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 f.write_str("DynHttpClient")
215 }
216}
217
218impl HttpClientExt for DynHttpClient {
219 fn send<T, U>(
220 &self,
221 req: Request<T>,
222 ) -> impl Future<Output = Result<Response<LazyBody<U>>>> + WasmCompatSend + 'static
223 where
224 T: Into<Bytes>,
225 T: WasmCompatSend,
226 U: From<Bytes>,
227 U: WasmCompatSend + 'static,
228 {
229 let this = self.clone();
230 let (mut parts, body) = req.map(Into::into).into_parts();
231 async move {
232 let body = this.apply_request_middleware(&mut parts, body).await?;
233 let response = this
234 .observe(parts, body, |request| this.inner.send_bytes(request))
235 .await?;
236 Ok(convert_body::<U>(response))
237 }
238 }
239
240 fn send_multipart<U>(
241 &self,
242 req: Request<MultipartForm>,
243 ) -> impl Future<Output = Result<Response<LazyBody<U>>>> + WasmCompatSend + 'static
244 where
245 U: From<Bytes>,
246 U: WasmCompatSend + 'static,
247 {
248 let this = self.clone();
249 let (mut parts, body) = req.into_parts();
250 async move {
251 this.apply_header_middleware(&mut parts).await?;
252 let response = this
253 .observe(parts, body, |request| {
254 this.inner.send_multipart_bytes(request)
255 })
256 .await?;
257 Ok(convert_body::<U>(response))
258 }
259 }
260
261 fn send_streaming<T>(
262 &self,
263 req: Request<T>,
264 ) -> impl Future<Output = Result<StreamingResponse>> + WasmCompatSend
265 where
266 T: Into<Bytes> + WasmCompatSend,
267 {
268 let (mut parts, body) = req.map(Into::into).into_parts();
269 async move {
270 let body = self.apply_request_middleware(&mut parts, body).await?;
271 self.observe(parts, body, |request| {
272 self.inner.send_streaming_bytes(request)
273 })
274 .await
275 }
276 }
277}
278
279fn convert_body<U>(response: Response<LazyBody<Bytes>>) -> Response<LazyBody<U>>
280where
281 U: From<Bytes> + WasmCompatSend + 'static,
282{
283 response.map(|body| -> LazyBody<U> { Box::pin(async move { body.await.map(U::from) }) })
284}
285
286#[cfg(test)]
287mod tests;