cc_utils/
requests.rs

1//! Implementation of utilities for working with MessagePack with requests in `salvo` and `reqwest`.
2
3use crate::prelude::*;
4
5#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
6use serde::Deserialize;
7
8#[cfg(feature = "reqwest")]
9use serde::Serialize;
10
11#[cfg(feature = "salvo")]
12#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
13use salvo::Request;
14
15#[cfg(feature = "reqwest")]
16use reqwest::RequestBuilder;
17
18#[cfg(feature = "salvo")]
19#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
20#[salvo::async_trait]
21pub trait MsgPackParser {
22  async fn parse_msgpack<'de, T: Deserialize<'de>>(&'de mut self) -> MResult<T>;
23  async fn parse_msgpack_with_max_size<'de, T: Deserialize<'de>>(&'de mut self, max_size: usize) -> MResult<T>;
24}
25
26#[cfg(feature = "salvo")]
27#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
28#[salvo::async_trait]
29impl MsgPackParser for Request {
30  /// Parse MessagePack body as type `T` from request with default max size limit.
31  #[inline]
32  async fn parse_msgpack<'de, T: Deserialize<'de>>(&'de mut self) -> MResult<T> {
33    self.parse_msgpack_with_max_size(salvo::http::request::secure_max_size()).await
34  }
35  
36  /// Parse MessagePack body as type `T` from request with max size limit.
37  #[inline]
38  async fn parse_msgpack_with_max_size<'de, T: Deserialize<'de>>(&'de mut self, max_size: usize) -> MResult<T> {
39    let ctype = self.content_type();
40    if let Some(ctype) = ctype {
41      if ctype.subtype() == salvo::http::mime::MSGPACK {
42        let payload = self.payload_with_max_size(max_size).await?;
43        let payload = if payload.is_empty() {
44          "null".as_bytes()
45        } else {
46          payload.as_ref()
47        };
48        log::debug!("{:?}", payload);
49        return rmp_serde::from_slice::<T>(payload).consider(Some(StatusCode::BAD_REQUEST), None, true)
50      }
51    }
52    Err(
53      ErrorResponse {
54        status_code: Some(StatusCode::BAD_REQUEST),
55        error_text: "Bad content type, must be `application/msgpack`.".into(),
56        original_text: None,
57        public_error: true
58      }
59    )
60  }
61}
62
63#[cfg(feature = "reqwest")]
64#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
65pub trait MsgPackBuilder {
66  fn msgpack<T: Serialize + ?Sized>(self, msgpack: &T) -> MResult<RequestBuilder>;
67}
68
69#[cfg(feature = "reqwest")]
70#[cfg(not(any(target_arch = "wasm32", target_arch = "wasm64")))]
71impl MsgPackBuilder for RequestBuilder {
72  fn msgpack<T: Serialize + ?Sized>(self, msgpack: &T) -> MResult<RequestBuilder> {
73    let (cli, mut req) = self.build_split();
74    let mut error = None;
75    if let Ok(req) = req.as_mut() {
76      match rmp_serde::to_vec(msgpack) {
77        Ok(body) => {
78          if !req.headers().contains_key(reqwest::header::CONTENT_TYPE) {
79            req.headers_mut().insert(reqwest::header::CONTENT_TYPE, reqwest::header::HeaderValue::from_static("application/msgpack"));
80          }
81          *req.body_mut() = Some(body.into());
82        },
83        Err(err) => { error = Some(err); },
84      }
85    }
86    if let Some(err) = error {
87      Err(err.to_string().into())
88    } else {
89      Ok(RequestBuilder::from_parts(cli, req?))
90    }
91  }
92}
93
94#[cfg(feature = "reqwest")]
95#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
96pub trait MsgPackBuilder {
97  fn msgpack<T: Serialize + ?Sized>(self, msgpack: &T) -> CResult<reqwest::Request>;
98}
99
100#[cfg(feature = "reqwest")]
101#[cfg(any(target_arch = "wasm32", target_arch = "wasm64"))]
102impl MsgPackBuilder for RequestBuilder {
103  fn msgpack<T: Serialize + ?Sized>(self, msgpack: &T) -> CResult<reqwest::Request> {
104    let mut req = self.build();
105    let mut error = None;
106    if let Ok(req) = req.as_mut() {
107      match rmp_serde::to_vec(msgpack) {
108        Ok(body) => {
109          if !req.headers().contains_key(reqwest::header::CONTENT_TYPE) {
110            req.headers_mut().insert(reqwest::header::CONTENT_TYPE, reqwest::header::HeaderValue::from_static("application/msgpack"));
111          }
112          *req.body_mut() = Some(body.into());
113        },
114        Err(err) => { error = Some(err); },
115      }
116    }
117    if let Some(err) = error {
118      Err(err.to_string().into())
119    } else {
120      Ok(req?)
121    }
122  }
123}