http.rs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. use bytes::Bytes;
  5. use reqwest::{header::HeaderName, redirect::Policy, Method};
  6. use serde::{Deserialize, Serialize};
  7. use serde_json::Value;
  8. use serde_repr::{Deserialize_repr, Serialize_repr};
  9. use std::{collections::HashMap, path::PathBuf, time::Duration};
  10. /// Client builder.
  11. #[derive(Default, Deserialize)]
  12. #[serde(rename_all = "camelCase")]
  13. pub struct ClientBuilder {
  14. /// Max number of redirections to follow
  15. pub max_redirections: Option<usize>,
  16. /// Connect timeout in seconds for the request
  17. pub connect_timeout: Option<u64>,
  18. }
  19. impl ClientBuilder {
  20. /// Creates a new client builder with the default options.
  21. pub fn new() -> Self {
  22. Default::default()
  23. }
  24. /// Sets the maximum number of redirections.
  25. pub fn max_redirections(mut self, max_redirections: usize) -> Self {
  26. self.max_redirections = Some(max_redirections);
  27. self
  28. }
  29. /// Sets the connection timeout.
  30. pub fn connect_timeout(mut self, connect_timeout: u64) -> Self {
  31. self.connect_timeout = Some(connect_timeout);
  32. self
  33. }
  34. /// Builds the ClientOptions.
  35. pub fn build(self) -> crate::api::Result<Client> {
  36. let mut client_builder = reqwest::Client::builder();
  37. if let Some(max_redirections) = self.max_redirections {
  38. client_builder = client_builder.redirect(Policy::limited(max_redirections))
  39. }
  40. if let Some(connect_timeout) = self.connect_timeout {
  41. client_builder = client_builder.connect_timeout(Duration::from_secs(connect_timeout));
  42. }
  43. let client = client_builder.build()?;
  44. Ok(Client(client))
  45. }
  46. }
  47. /// The HTTP client.
  48. #[derive(Clone)]
  49. pub struct Client(reqwest::Client);
  50. impl Client {
  51. /// Executes an HTTP request
  52. ///
  53. /// The response will be transformed to String,
  54. /// If reading the response as binary, the byte array will be serialized using serde_json
  55. pub async fn send(&self, request: HttpRequestBuilder) -> crate::api::Result<Response> {
  56. let method = Method::from_bytes(request.method.to_uppercase().as_bytes())?;
  57. let mut request_builder = self.0.request(method, &request.url);
  58. if let Some(query) = request.query {
  59. request_builder = request_builder.query(&query);
  60. }
  61. if let Some(headers) = request.headers {
  62. for (header, header_value) in headers.iter() {
  63. request_builder =
  64. request_builder.header(HeaderName::from_bytes(header.as_bytes())?, header_value);
  65. }
  66. }
  67. if let Some(timeout) = request.timeout {
  68. request_builder = request_builder.timeout(Duration::from_secs(timeout));
  69. }
  70. let response = if let Some(body) = request.body {
  71. match body {
  72. Body::Bytes(data) => request_builder.body(Bytes::from(data)).send().await?,
  73. Body::Text(text) => request_builder.body(Bytes::from(text)).send().await?,
  74. Body::Json(json) => request_builder.json(&json).send().await?,
  75. Body::Form(form_body) => {
  76. let mut form = Vec::new();
  77. for (name, part) in form_body.0 {
  78. match part {
  79. FormPart::Bytes(bytes) => form.push((name, serde_json::to_string(&bytes)?)),
  80. FormPart::File(file_path) => form.push((name, serde_json::to_string(&file_path)?)),
  81. FormPart::Text(text) => form.push((name, text)),
  82. }
  83. }
  84. request_builder.form(&form).send().await?
  85. }
  86. }
  87. } else {
  88. request_builder.send().await?
  89. };
  90. let response = response.error_for_status()?;
  91. Ok(Response(
  92. request.response_type.unwrap_or(ResponseType::Json),
  93. response,
  94. ))
  95. }
  96. }
  97. #[derive(Serialize_repr, Deserialize_repr, Clone, Debug)]
  98. #[repr(u16)]
  99. #[non_exhaustive]
  100. /// The request's response type
  101. pub enum ResponseType {
  102. /// Read the response as JSON
  103. Json = 1,
  104. /// Read the response as text
  105. Text,
  106. /// Read the response as binary
  107. Binary,
  108. }
  109. /// FormBody data types.
  110. #[derive(Deserialize)]
  111. #[serde(untagged)]
  112. #[non_exhaustive]
  113. pub enum FormPart {
  114. /// A file path value.
  115. File(PathBuf),
  116. /// A string value.
  117. Text(String),
  118. /// A byte array value.
  119. Bytes(Vec<u8>),
  120. }
  121. /// Form body definition.
  122. #[derive(Deserialize)]
  123. pub struct FormBody(HashMap<String, FormPart>);
  124. impl FormBody {
  125. /// Creates a new form body.
  126. pub fn new(data: HashMap<String, FormPart>) -> Self {
  127. Self(data)
  128. }
  129. }
  130. /// A body for the request.
  131. #[derive(Deserialize)]
  132. #[serde(tag = "type", content = "payload")]
  133. #[non_exhaustive]
  134. pub enum Body {
  135. /// A multipart formdata body.
  136. Form(FormBody),
  137. /// A JSON body.
  138. Json(Value),
  139. /// A text string body.
  140. Text(String),
  141. /// A byte array body.
  142. Bytes(Vec<u8>),
  143. }
  144. /// The builder for a HTTP request.
  145. ///
  146. /// # Examples
  147. /// ```no_run
  148. /// use tauri::api::http::{ HttpRequestBuilder, ResponseType, ClientBuilder };
  149. /// async fn run() {
  150. /// let client = ClientBuilder::new()
  151. /// .max_redirections(3)
  152. /// .build()
  153. /// .unwrap();
  154. /// let mut request_builder = HttpRequestBuilder::new("GET", "http://example.com");
  155. /// let request = request_builder.response_type(ResponseType::Text);
  156. ///
  157. /// if let Ok(response) = client.send(request).await {
  158. /// println!("got response");
  159. /// } else {
  160. /// println!("Something Happened!");
  161. /// }
  162. /// }
  163. /// ```
  164. #[derive(Deserialize)]
  165. #[serde(rename_all = "camelCase")]
  166. pub struct HttpRequestBuilder {
  167. /// The request method (GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS, CONNECT or TRACE)
  168. pub method: String,
  169. /// The request URL
  170. pub url: String,
  171. /// The request query params
  172. pub query: Option<HashMap<String, String>>,
  173. /// The request headers
  174. pub headers: Option<HashMap<String, String>>,
  175. /// The request body
  176. pub body: Option<Body>,
  177. /// Timeout for the whole request
  178. pub timeout: Option<u64>,
  179. /// The response type (defaults to Json)
  180. pub response_type: Option<ResponseType>,
  181. }
  182. impl HttpRequestBuilder {
  183. /// Initializes a new instance of the HttpRequestrequest_builder.
  184. pub fn new(method: impl Into<String>, url: impl Into<String>) -> Self {
  185. Self {
  186. method: method.into(),
  187. url: url.into(),
  188. query: None,
  189. headers: None,
  190. body: None,
  191. timeout: None,
  192. response_type: None,
  193. }
  194. }
  195. /// Sets the request params.
  196. pub fn query(mut self, query: HashMap<String, String>) -> Self {
  197. self.query = Some(query);
  198. self
  199. }
  200. /// Sets the request headers.
  201. pub fn headers(mut self, headers: HashMap<String, String>) -> Self {
  202. self.headers = Some(headers);
  203. self
  204. }
  205. /// Sets the request body.
  206. pub fn body(mut self, body: Body) -> Self {
  207. self.body = Some(body);
  208. self
  209. }
  210. /// Sets the general request timeout.
  211. pub fn timeout(mut self, timeout: u64) -> Self {
  212. self.timeout = Some(timeout);
  213. self
  214. }
  215. /// Sets the type of the response. Interferes with the way we read the response.
  216. pub fn response_type(mut self, response_type: ResponseType) -> Self {
  217. self.response_type = Some(response_type);
  218. self
  219. }
  220. }
  221. /// The HTTP response.
  222. pub struct Response(ResponseType, reqwest::Response);
  223. impl Response {
  224. /// Reads the response and returns its info.
  225. pub async fn read(self) -> crate::api::Result<ResponseData> {
  226. let url = self.1.url().to_string();
  227. let mut headers = HashMap::new();
  228. for (name, value) in self.1.headers() {
  229. headers.insert(name.as_str().to_string(), value.to_str()?.to_string());
  230. }
  231. let status = self.1.status().as_u16();
  232. let data = match self.0 {
  233. ResponseType::Json => self.1.json().await?,
  234. ResponseType::Text => Value::String(self.1.text().await?),
  235. ResponseType::Binary => Value::String(serde_json::to_string(&self.1.bytes().await?)?),
  236. };
  237. Ok(ResponseData {
  238. url,
  239. status,
  240. headers,
  241. data,
  242. })
  243. }
  244. }
  245. /// The response type.
  246. #[derive(Serialize)]
  247. #[serde(rename_all = "camelCase")]
  248. pub struct ResponseData {
  249. url: String,
  250. status: u16,
  251. headers: HashMap<String, String>,
  252. data: Value,
  253. }