volo_http/client/
request_builder.rs

1//! Request builder for building a request and sending to server
2//!
3//! See [`RequestBuilder`] for more details.
4
5use std::{borrow::Cow, error::Error};
6
7use faststr::FastStr;
8use http::{
9    header::{HeaderMap, HeaderName, HeaderValue},
10    method::Method,
11    uri::{PathAndQuery, Scheme, Uri},
12    version::Version,
13};
14use motore::layer::Layer;
15use volo::{
16    client::{Apply, OneShotService, WithOptService},
17    net::Address,
18};
19
20use super::{CallOpt, insert_header, target::Target};
21use crate::{
22    body::Body,
23    context::ClientContext,
24    error::{
25        BoxError, ClientError,
26        client::{Result, builder_error},
27    },
28    request::Request,
29    response::Response,
30    utils::consts,
31};
32
33/// The builder for building a request.
34pub struct RequestBuilder<S, B = Body> {
35    inner: S,
36    target: Target,
37    version: Option<Version>,
38    request: Request<B>,
39    status: Result<()>,
40}
41
42impl<S> RequestBuilder<S> {
43    pub(super) fn new(inner: S) -> Self {
44        Self {
45            inner,
46            target: Default::default(),
47            version: None,
48            request: Request::default(),
49            status: Ok(()),
50        }
51    }
52
53    /// Set the request body.
54    pub fn data<D>(mut self, data: D) -> Self
55    where
56        D: TryInto<Body>,
57        D::Error: Error + Send + Sync + 'static,
58    {
59        if self.status.is_err() {
60            return self;
61        }
62
63        let body = match data.try_into() {
64            Ok(body) => body,
65            Err(err) => {
66                self.status = Err(builder_error(err));
67                return self;
68            }
69        };
70
71        let (parts, _) = self.request.into_parts();
72        self.request = Request::from_parts(parts, body);
73
74        self
75    }
76
77    /// Set the request body as json from object with [`Serialize`](serde::Serialize).
78    #[cfg(feature = "json")]
79    pub fn json<T>(mut self, json: &T) -> Self
80    where
81        T: serde::Serialize,
82    {
83        if self.status.is_err() {
84            return self;
85        }
86
87        let json = match crate::utils::json::serialize(json) {
88            Ok(json) => json,
89            Err(err) => {
90                self.status = Err(builder_error(err));
91                return self;
92            }
93        };
94
95        let (mut parts, _) = self.request.into_parts();
96        parts.headers.insert(
97            http::header::CONTENT_TYPE,
98            crate::utils::consts::APPLICATION_JSON,
99        );
100        self.request = Request::from_parts(parts, Body::from(json));
101
102        self
103    }
104
105    /// Set the request body as form from object with [`Serialize`](serde::Serialize).
106    #[cfg(feature = "form")]
107    pub fn form<T>(mut self, form: &T) -> Self
108    where
109        T: serde::Serialize,
110    {
111        if self.status.is_err() {
112            return self;
113        }
114
115        let form = match serde_urlencoded::to_string(form) {
116            Ok(form) => form,
117            Err(err) => {
118                self.status = Err(builder_error(err));
119                return self;
120            }
121        };
122
123        let (mut parts, _) = self.request.into_parts();
124        parts.headers.insert(
125            http::header::CONTENT_TYPE,
126            crate::utils::consts::APPLICATION_WWW_FORM_URLENCODED,
127        );
128        self.request = Request::from_parts(parts, Body::from(form));
129
130        self
131    }
132}
133
134impl<S, B> RequestBuilder<S, B> {
135    /// Set method for the request.
136    pub fn method(mut self, method: Method) -> Self {
137        *self.request.method_mut() = method;
138        self
139    }
140
141    /// Get a reference to method in the request.
142    pub fn method_ref(&self) -> &Method {
143        self.request.method()
144    }
145
146    /// Set uri for building request.
147    ///
148    /// The uri will be split into two parts scheme+host and path+query. The scheme and host can be
149    /// empty and it will be resolved as the target address. The path and query must exist and they
150    /// are used to build the request uri.
151    ///
152    /// Note that only path and query will be set to the request uri. For setting the full uri, use
153    /// `full_uri` instead.
154    pub fn uri<U>(mut self, uri: U) -> Self
155    where
156        U: TryInto<Uri>,
157        U::Error: Into<BoxError>,
158    {
159        if self.status.is_err() {
160            return self;
161        }
162        let uri = match uri.try_into() {
163            Ok(uri) => uri,
164            Err(err) => {
165                self.status = Err(builder_error(err));
166                return self;
167            }
168        };
169        if uri.host().is_some() {
170            match Target::from_uri(&uri) {
171                Ok(target) => self.target = target,
172                Err(err) => {
173                    self.status = Err(err);
174                    return self;
175                }
176            }
177        }
178        let rela_uri = uri
179            .path_and_query()
180            .map(PathAndQuery::to_owned)
181            .unwrap_or_else(|| PathAndQuery::from_static("/"))
182            .into();
183        *self.request.uri_mut() = rela_uri;
184
185        self
186    }
187
188    /// Set query for the uri in request from object with [`Serialize`](serde::Serialize).
189    #[cfg(feature = "query")]
190    pub fn set_query<T>(mut self, query: &T) -> Self
191    where
192        T: serde::Serialize,
193    {
194        if self.status.is_err() {
195            return self;
196        }
197        let query_str = match serde_urlencoded::to_string(query) {
198            Ok(query) => query,
199            Err(err) => {
200                self.status = Err(builder_error(err));
201                return self;
202            }
203        };
204
205        // We should keep path only without query
206        let path_str = self.request.uri().path();
207        let mut path = String::with_capacity(path_str.len() + 1 + query_str.len());
208        path.push_str(path_str);
209        path.push('?');
210        path.push_str(&query_str);
211        let Ok(uri) = Uri::from_maybe_shared(path) else {
212            // path part is from a valid uri, and the result of urlencoded must be valid.
213            unreachable!();
214        };
215
216        *self.request.uri_mut() = uri;
217
218        self
219    }
220
221    /// Get a reference to uri in the request.
222    pub fn uri_ref(&self) -> &Uri {
223        self.request.uri()
224    }
225
226    /// Set version of the HTTP request.
227    ///
228    /// If it is not set, the request will use HTTP/2 if it is enabled and supported by default.
229    pub fn version(mut self, version: Version) -> Self {
230        self.version = Some(version);
231        self
232    }
233
234    /// Get a reference to version in the request.
235    pub fn version_ref(&self) -> Option<Version> {
236        self.version
237    }
238
239    /// Insert a header into the request header map.
240    pub fn header<K, V>(mut self, key: K, value: V) -> Self
241    where
242        K: TryInto<HeaderName>,
243        K::Error: Error + Send + Sync + 'static,
244        V: TryInto<HeaderValue>,
245        V::Error: Error + Send + Sync + 'static,
246    {
247        if self.status.is_err() {
248            return self;
249        }
250
251        if let Err(err) = insert_header(self.request.headers_mut(), key, value) {
252            self.status = Err(err);
253        }
254
255        self
256    }
257
258    /// Get a reference to headers in the request.
259    pub fn headers(&self) -> &HeaderMap {
260        self.request.headers()
261    }
262
263    /// Get a mutable reference to headers in the request.
264    pub fn headers_mut(&mut self) -> &mut HeaderMap {
265        self.request.headers_mut()
266    }
267
268    /// Set target address for the request.
269    pub fn address<A>(mut self, address: A) -> Self
270    where
271        A: Into<Address>,
272    {
273        self.target = Target::from(address.into());
274        self
275    }
276
277    /// Set target host for the request.
278    ///
279    /// It uses http with port 80 by default.
280    ///
281    /// For setting scheme and port, use [`Self::with_scheme`] and [`Self::with_port`] after
282    /// specifying host.
283    pub fn host<H>(mut self, host: H) -> Self
284    where
285        H: Into<Cow<'static, str>>,
286    {
287        // SAFETY: using HTTP is safe
288        self.target = unsafe {
289            Target::new_host_unchecked(
290                Scheme::HTTP,
291                FastStr::from(host.into()),
292                consts::HTTP_DEFAULT_PORT,
293            )
294        };
295        self
296    }
297
298    /// Set scheme for target of the request.
299    pub fn with_scheme(mut self, scheme: Scheme) -> Self {
300        if self.status.is_err() {
301            return self;
302        }
303        if let Err(err) = self.target.set_scheme(scheme) {
304            self.status = Err(err);
305        }
306        self
307    }
308
309    /// Set port for target address of this request.
310    pub fn with_port(mut self, port: u16) -> Self {
311        if self.status.is_err() {
312            return self;
313        }
314        if let Err(err) = self.target.set_port(port) {
315            self.status = Err(err);
316        }
317        self
318    }
319
320    /// Get a reference to [`Target`].
321    pub fn target_ref(&self) -> &Target {
322        &self.target
323    }
324
325    /// Get a mutable reference to [`Target`].
326    pub fn target_mut(&mut self) -> &mut Target {
327        &mut self.target
328    }
329
330    /// Set a request body.
331    pub fn body<B2>(self, body: B2) -> RequestBuilder<S, B2> {
332        let (parts, _) = self.request.into_parts();
333        let request = Request::from_parts(parts, body);
334
335        RequestBuilder {
336            inner: self.inner,
337            target: self.target,
338            version: self.version,
339            request,
340            status: self.status,
341        }
342    }
343
344    /// Get a reference to body in the request.
345    pub fn body_ref(&self) -> &B {
346        self.request.body()
347    }
348
349    /// Add a new [`Layer`] to the front of request builder.
350    ///
351    /// Note that the [`Layer`] generated `Service` should be a [`OneShotService`].
352    pub fn layer<L>(self, layer: L) -> RequestBuilder<L::Service, B>
353    where
354        L: Layer<S>,
355    {
356        RequestBuilder {
357            inner: layer.layer(self.inner),
358            target: self.target,
359            version: self.version,
360            request: self.request,
361            status: self.status,
362        }
363    }
364
365    /// Apply a [`CallOpt`] to the request.
366    pub fn with_callopt(self, callopt: CallOpt) -> RequestBuilder<WithOptService<S, CallOpt>, B> {
367        self.layer(WithOptLayer::new(callopt))
368    }
369
370    fn set_version(&mut self) {
371        let ver = match self.version {
372            Some(ver) => ver,
373            None => {
374                // Use HTTP/1.1 by default
375                if cfg!(feature = "http1") {
376                    Version::HTTP_11
377                } else {
378                    Version::HTTP_2
379                }
380            }
381        };
382        *self.request.version_mut() = ver;
383    }
384
385    /// Send the request and get the response.
386    pub async fn send<RespBody>(mut self) -> Result<Response<RespBody>>
387    where
388        S: OneShotService<
389                ClientContext,
390                Request<B>,
391                Response = Response<RespBody>,
392                Error = ClientError,
393            > + Send
394            + Sync
395            + 'static,
396        B: Send + 'static,
397    {
398        self.set_version();
399        self.status?;
400
401        let mut cx = ClientContext::new();
402        self.target.apply(&mut cx)?;
403        self.inner.call(&mut cx, self.request).await
404    }
405}
406
407struct WithOptLayer {
408    opt: CallOpt,
409}
410
411impl WithOptLayer {
412    const fn new(opt: CallOpt) -> Self {
413        Self { opt }
414    }
415}
416
417impl<S> Layer<S> for WithOptLayer {
418    type Service = WithOptService<S, CallOpt>;
419
420    fn layer(self, inner: S) -> Self::Service {
421        WithOptService::new(inner, self.opt)
422    }
423}