Переглянути джерело

refactor(core): Remove attohttpc in favor of reqwest (#7143)

Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.studio>
Fabian-Lars 2 роки тому
батько
коміт
db7c5fbf2e

+ 5 - 0
.changes/remove-attohttpc-bundler.md

@@ -0,0 +1,5 @@
+---
+'tauri-bundler': patch
+---
+
+Remove `attohttpc` in favor of `ureq`.

+ 5 - 0
.changes/remove-attohttpc-core.md

@@ -0,0 +1,5 @@
+---
+'tauri': patch
+---
+
+Remove `attohttpc` in favor of `reqwest`.

+ 6 - 7
core/tauri/Cargo.toml

@@ -37,7 +37,7 @@ targets = [
 ]
 
 [package.metadata.cargo-udeps.ignore]
-normal = [ "attohttpc", "reqwest" ]
+normal = [ "reqwest" ]
 
 [dependencies]
 serde_json = { version = "1.0", features = [ "raw_value" ] }
@@ -69,7 +69,6 @@ base64 = { version = "0.21", optional = true }
 clap = { version = "3", optional = true }
 reqwest = { version = "0.11", features = [ "json", "stream" ], optional = true }
 bytes = { version = "1", features = [ "serde" ], optional = true }
-attohttpc = { version = "0.22", features = [ "compress", "json", "form" ], optional = true }
 open = { version = "3.2", optional = true }
 shared_child = { version = "1.0", optional = true }
 os_pipe = { version = "1.0", optional = true }
@@ -141,14 +140,14 @@ updater = [
   "dialog-ask",
   "fs-extract-api"
 ]
-http-api = [ "attohttpc" ]
-http-multipart = [ "attohttpc/multipart-form", "reqwest/multipart" ]
+http-api = [ "reqwest", "bytes" ]
+http-multipart = [ "reqwest/multipart" ]
 os-api = [ "sys-locale" ]
 shell-open-api = [ "open", "regex", "tauri-macros/shell-scope" ]
 fs-extract-api = [ "zip" ]
-reqwest-client = [ "reqwest", "bytes" ]
-reqwest-native-tls-vendored = [ "reqwest-client", "reqwest/native-tls-vendored" ]
-native-tls-vendored = [ "attohttpc/tls-vendored" ]
+reqwest-client = [ "http-api" ]
+reqwest-native-tls-vendored = [ "native-tls-vendored" ]
+native-tls-vendored = [ "reqwest/native-tls-vendored" ]
 process-command-api = [ "shared_child", "os_pipe" ]
 global-shortcut = [
   "tauri-runtime/global-shortcut",

+ 1 - 8
core/tauri/src/api/error.rs

@@ -22,20 +22,13 @@ pub enum Error {
   #[error("user cancelled the dialog")]
   DialogCancelled,
   /// The network error.
-  #[cfg(all(feature = "http-api", not(feature = "reqwest-client")))]
-  #[error("Network Error: {0}")]
-  Network(#[from] attohttpc::Error),
-  /// The network error.
-  #[cfg(feature = "reqwest-client")]
-  #[cfg_attr(doc_cfg, doc(cfg(feature = "reqwest-client")))]
+  #[cfg(feature = "http-api")]
   #[error("Network Error: {0}")]
   Network(#[from] reqwest::Error),
   /// HTTP method error.
   #[error(transparent)]
   HttpMethod(#[from] http::method::InvalidMethod),
   /// Invalid HTTP header value.
-  #[cfg(feature = "reqwest-client")]
-  #[cfg_attr(doc_cfg, doc(cfg(feature = "reqwest-client")))]
   #[error(transparent)]
   HttpHeaderValue(#[from] http::header::InvalidHeaderValue),
   /// Invalid HTTP header value.

+ 0 - 175
core/tauri/src/api/http.rs

@@ -13,12 +13,8 @@ use url::Url;
 
 use std::{collections::HashMap, path::PathBuf, time::Duration};
 
-#[cfg(feature = "reqwest-client")]
 pub use reqwest::header;
 
-#[cfg(not(feature = "reqwest-client"))]
-pub use attohttpc::header;
-
 use header::{HeaderName, HeaderValue};
 
 #[derive(Deserialize)]
@@ -73,13 +69,6 @@ impl ClientBuilder {
   }
 
   /// Builds the Client.
-  #[cfg(not(feature = "reqwest-client"))]
-  pub fn build(self) -> crate::api::Result<Client> {
-    Ok(Client(self))
-  }
-
-  /// Builds the Client.
-  #[cfg(feature = "reqwest-client")]
   pub fn build(self) -> crate::api::Result<Client> {
     let mut client_builder = reqwest::Client::builder();
 
@@ -101,146 +90,9 @@ impl ClientBuilder {
 }
 
 /// The HTTP client based on [`reqwest`].
-#[cfg(feature = "reqwest-client")]
 #[derive(Debug, Clone)]
 pub struct Client(reqwest::Client);
 
-/// The HTTP client.
-#[cfg(not(feature = "reqwest-client"))]
-#[derive(Debug, Clone)]
-pub struct Client(ClientBuilder);
-
-#[cfg(not(feature = "reqwest-client"))]
-impl Client {
-  /// Executes an HTTP request.
-  ///
-  /// # Examples
-  ///
-  /// ```rust,no_run
-  /// use tauri::api::http::{ClientBuilder, HttpRequestBuilder, ResponseType};
-  /// async fn run_request() {
-  ///   let client = ClientBuilder::new().build().unwrap();
-  ///   let response = client.send(
-  ///     HttpRequestBuilder::new("GET", "https://www.rust-lang.org")
-  ///       .unwrap()
-  ///       .response_type(ResponseType::Binary)
-  ///   ).await;
-  ///   if let Ok(response) = response {
-  ///     let bytes = response.bytes();
-  ///   }
-  /// }
-  /// ```
-  pub async fn send(&self, request: HttpRequestBuilder) -> crate::api::Result<Response> {
-    let method = Method::from_bytes(request.method.to_uppercase().as_bytes())?;
-
-    let mut request_builder = attohttpc::RequestBuilder::try_new(method, &request.url)?;
-
-    if let Some(query) = request.query {
-      request_builder = request_builder.params(&query);
-    }
-
-    if let Some(headers) = &request.headers {
-      for (name, value) in headers.0.iter() {
-        request_builder = request_builder.header(name, value);
-      }
-    }
-
-    if let Some(max_redirections) = self.0.max_redirections {
-      if max_redirections == 0 {
-        request_builder = request_builder.follow_redirects(false);
-      } else {
-        request_builder = request_builder.max_redirections(max_redirections as u32);
-      }
-    }
-
-    if let Some(timeout) = request.timeout {
-      request_builder = request_builder.timeout(timeout);
-    }
-
-    let response = if let Some(body) = request.body {
-      match body {
-        Body::Bytes(data) => request_builder.body(attohttpc::body::Bytes(data)).send()?,
-        Body::Text(text) => request_builder.body(attohttpc::body::Bytes(text)).send()?,
-        Body::Json(json) => request_builder.json(&json)?.send()?,
-        Body::Form(form_body) => {
-          #[allow(unused_variables)]
-          fn send_form(
-            request_builder: attohttpc::RequestBuilder,
-            headers: &Option<HeaderMap>,
-            form_body: FormBody,
-          ) -> crate::api::Result<attohttpc::Response> {
-            #[cfg(feature = "http-multipart")]
-            if matches!(
-              headers
-                .as_ref()
-                .and_then(|h| h.0.get("content-type"))
-                .map(|v| v.as_bytes()),
-              Some(b"multipart/form-data")
-            ) {
-              let mut multipart = attohttpc::MultipartBuilder::new();
-              let mut byte_cache: HashMap<String, Vec<u8>> = Default::default();
-
-              for (name, part) in &form_body.0 {
-                if let FormPart::File { file, .. } = part {
-                  byte_cache.insert(name.to_string(), file.clone().try_into()?);
-                }
-              }
-              for (name, part) in &form_body.0 {
-                multipart = match part {
-                  FormPart::File {
-                    file,
-                    mime,
-                    file_name,
-                  } => {
-                    // safe to unwrap: always set by previous loop
-                    let mut file =
-                      attohttpc::MultipartFile::new(name, byte_cache.get(name).unwrap());
-                    if let Some(mime) = mime {
-                      file = file.with_type(mime)?;
-                    }
-                    if let Some(file_name) = file_name {
-                      file = file.with_filename(file_name);
-                    }
-                    multipart.with_file(file)
-                  }
-                  FormPart::Text(value) => multipart.with_text(name, value),
-                };
-              }
-              return request_builder
-                .body(multipart.build()?)
-                .send()
-                .map_err(Into::into);
-            }
-
-            let mut form = Vec::new();
-            for (name, part) in form_body.0 {
-              match part {
-                FormPart::File { file, .. } => {
-                  let bytes: Vec<u8> = file.try_into()?;
-                  form.push((name, serde_json::to_string(&bytes)?))
-                }
-                FormPart::Text(value) => form.push((name, value)),
-              }
-            }
-            request_builder.form(&form)?.send().map_err(Into::into)
-          }
-
-          send_form(request_builder, &request.headers, form_body)?
-        }
-      }
-    } else {
-      request_builder.send()?
-    };
-
-    Ok(Response(
-      request.response_type.unwrap_or(ResponseType::Json),
-      response,
-      request.url,
-    ))
-  }
-}
-
-#[cfg(feature = "reqwest-client")]
 impl Client {
   /// Executes an HTTP request
   ///
@@ -557,13 +409,8 @@ impl HttpRequestBuilder {
 }
 
 /// The HTTP response.
-#[cfg(feature = "reqwest-client")]
 #[derive(Debug)]
 pub struct Response(ResponseType, reqwest::Response);
-/// The HTTP response.
-#[cfg(not(feature = "reqwest-client"))]
-#[derive(Debug)]
-pub struct Response(ResponseType, attohttpc::Response, Url);
 
 impl Response {
   /// Get the [`StatusCode`] of this Response.
@@ -579,20 +426,10 @@ impl Response {
   /// Reads the response as raw bytes.
   pub async fn bytes(self) -> crate::api::Result<RawResponse> {
     let status = self.status().as_u16();
-    #[cfg(feature = "reqwest-client")]
     let data = self.1.bytes().await?.to_vec();
-    #[cfg(not(feature = "reqwest-client"))]
-    let data = self.1.bytes()?;
     Ok(RawResponse { status, data })
   }
 
-  #[cfg(not(feature = "reqwest-client"))]
-  #[allow(dead_code)]
-  pub(crate) fn reader(self) -> attohttpc::ResponseReader {
-    let (_, _, reader) = self.1.split();
-    reader
-  }
-
   // Convert the response into a Stream of [`bytes::Bytes`] from the body.
   //
   // # Examples
@@ -612,7 +449,6 @@ impl Response {
   // # Ok(())
   // # }
   // ```
-  #[cfg(feature = "reqwest-client")]
   #[allow(dead_code)]
   pub(crate) fn bytes_stream(
     self,
@@ -625,10 +461,7 @@ impl Response {
   ///
   /// Note that the body is serialized to a [`Value`].
   pub async fn read(self) -> crate::api::Result<ResponseData> {
-    #[cfg(feature = "reqwest-client")]
     let url = self.1.url().clone();
-    #[cfg(not(feature = "reqwest-client"))]
-    let url = self.2;
 
     let mut headers = HashMap::new();
     let mut raw_headers = HashMap::new();
@@ -650,20 +483,12 @@ impl Response {
     }
     let status = self.1.status().as_u16();
 
-    #[cfg(feature = "reqwest-client")]
     let data = match self.0 {
       ResponseType::Json => self.1.json().await?,
       ResponseType::Text => Value::String(self.1.text().await?),
       ResponseType::Binary => serde_json::to_value(&self.1.bytes().await?)?,
     };
 
-    #[cfg(not(feature = "reqwest-client"))]
-    let data = match self.0 {
-      ResponseType::Json => self.1.json()?,
-      ResponseType::Text => Value::String(self.1.text()?),
-      ResponseType::Binary => serde_json::to_value(self.1.bytes()?)?,
-    };
-
     Ok(ResponseData {
       url,
       status,

+ 3 - 3
core/tauri/src/lib.rs

@@ -22,9 +22,9 @@
 //! - **shell-open-api**: Enables the [`api::shell`] module.
 //! - **http-api**: Enables the [`api::http`] module.
 //! - **http-multipart**: Adds support to `multipart/form-data` requests.
-//! - **reqwest-client**: Uses `reqwest` as HTTP client on the `http` APIs. Improves performance, but increases the bundle size.
-//! - **native-tls-vendored**: Compile and statically link to a vendored copy of OpenSSL (applies to the default HTTP client).
-//! - **reqwest-native-tls-vendored**: Compile and statically link to a vendored copy of OpenSSL (applies to the `reqwest` HTTP client).
+//! - **reqwest-client**: Alias for the `http-api` feature flag.
+//! - **native-tls-vendored**: Compile and statically link to a vendored copy of OpenSSL.
+//! - **reqwest-native-tls-vendored**: Alias for the `native-tls-vendored` feature flag.
 //! - **os-api**: Enables the [`api::os`] module.
 //! - **process-command-api**: Enables the [`api::process::Command`] APIs.
 //! - **global-shortcut**: Enables the global shortcut APIs.

+ 0 - 20
core/tauri/src/updater/core.rs

@@ -559,7 +559,6 @@ impl<R: Runtime> Update<R> {
       .and_then(|value| value.parse().ok());
 
     let mut buffer = Vec::new();
-    #[cfg(feature = "reqwest-client")]
     {
       use futures_util::StreamExt;
       let mut stream = response.bytes_stream();
@@ -570,25 +569,6 @@ impl<R: Runtime> Update<R> {
         buffer.extend(bytes);
       }
     }
-    #[cfg(not(feature = "reqwest-client"))]
-    {
-      let mut reader = response.reader();
-      let mut buf = [0; 16384];
-      loop {
-        match reader.read(&mut buf) {
-          Ok(b) => {
-            if b == 0 {
-              break;
-            } else {
-              let bytes = buf[0..b].to_vec();
-              on_chunk(bytes.len(), content_length);
-              buffer.extend(bytes);
-            }
-          }
-          Err(e) => return Err(e.into()),
-        }
-      }
-    }
 
     on_download_finish();
 

+ 0 - 1
examples/api/src-tauri/Cargo.toml

@@ -29,7 +29,6 @@ features = [
   "isolation",
   "macos-private-api",
   "windows7-compat",
-  "reqwest-client",
   "system-tray",
   "updater"
 ]

+ 6 - 5
tooling/bundler/Cargo.toml

@@ -32,7 +32,8 @@ tempfile = "3.5.0"
 log = { version = "0.4.18", features = [ "kv_unstable" ] }
 dirs-next = "2.0"
 os_pipe = "1"
-attohttpc = { version = "0.25", default-features = false }
+ureq = { version = "2.5", default-features = false }
+native-tls = { version = "0.2", optional = true }
 hex = "0.4"
 semver = "1"
 sha1 = "0.10"
@@ -64,7 +65,7 @@ name = "tauri_bundler"
 path = "src/lib.rs"
 
 [features]
-default = [ "native-tls" ]
-native-tls = [ "attohttpc/tls-native" ]
-native-tls-vendored = [ "attohttpc/tls-native-vendored" ]
-rustls = [ "attohttpc/rustls" ]
+default = ["rustls"]
+native-tls = [ "ureq/native-tls" ]
+native-tls-vendored = [ "native-tls", "native-tls/vendored" ]
+rustls = [ "ureq/tls" ]

+ 4 - 2
tooling/bundler/src/bundle/windows/util.rs

@@ -27,8 +27,10 @@ pub const WIX_UPDATER_OUTPUT_FOLDER_NAME: &str = "msi-updater";
 
 pub fn download(url: &str) -> crate::Result<Vec<u8>> {
   info!(action = "Downloading"; "{}", url);
-  let response = attohttpc::get(url).send()?;
-  response.bytes().map_err(Into::into)
+  let response = ureq::get(url).call().map_err(Box::new)?;
+  let mut bytes = Vec::new();
+  response.into_reader().read_to_end(&mut bytes)?;
+  Ok(bytes)
 }
 
 pub enum HashAlgorithm {

+ 1 - 1
tooling/bundler/src/error.rs

@@ -49,7 +49,7 @@ pub enum Error {
   RegexError(#[from] regex::Error),
   /// Failed to perform HTTP request.
   #[error("`{0}`")]
-  HttpError(#[from] attohttpc::Error),
+  HttpError(#[from] Box<ureq::Error>),
   /// Invalid glob pattern.
   #[cfg(windows)]
   #[error("{0}")]

+ 4 - 49
tooling/cli/Cargo.lock

@@ -161,21 +161,6 @@ dependencies = [
  "syn 2.0.16",
 ]
 
-[[package]]
-name = "attohttpc"
-version = "0.25.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e57d6e7a84f33ff3316e97af3180fe7f86597a6a60161c0be70c0e45f382620"
-dependencies = [
- "http",
- "log",
- "native-tls",
- "rustls 0.21.1",
- "url",
- "webpki",
- "webpki-roots 0.23.0",
-]
-
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -2781,28 +2766,6 @@ dependencies = [
  "webpki",
 ]
 
-[[package]]
-name = "rustls"
-version = "0.21.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c911ba11bc8433e811ce56fde130ccf32f5127cab0e0194e9c68c5a5b671791e"
-dependencies = [
- "log",
- "ring",
- "rustls-webpki",
- "sct",
-]
-
-[[package]]
-name = "rustls-webpki"
-version = "0.100.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d6207cd5ed3d8dca7816f8f3725513a34609c0c765bf652b8c3cb4cfd87db46b"
-dependencies = [
- "ring",
- "untrusted",
-]
-
 [[package]]
 name = "ryu"
 version = "1.0.13"
@@ -3359,7 +3322,6 @@ version = "1.2.1"
 dependencies = [
  "anyhow",
  "ar",
- "attohttpc",
  "bitness",
  "dirs-next",
  "dunce",
@@ -3371,6 +3333,7 @@ dependencies = [
  "libflate",
  "log",
  "md5",
+ "native-tls",
  "os_pipe",
  "plist",
  "regex",
@@ -3386,6 +3349,7 @@ dependencies = [
  "tempfile",
  "thiserror",
  "time",
+ "ureq",
  "uuid",
  "walkdir",
  "winreg 0.50.0",
@@ -3873,10 +3837,10 @@ dependencies = [
  "log",
  "native-tls",
  "once_cell",
- "rustls 0.20.8",
+ "rustls",
  "url",
  "webpki",
- "webpki-roots 0.22.6",
+ "webpki-roots",
 ]
 
 [[package]]
@@ -4088,15 +4052,6 @@ dependencies = [
  "webpki",
 ]
 
-[[package]]
-name = "webpki-roots"
-version = "0.23.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa54963694b65584e170cf5dc46aeb4dcaa5584e652ff5f3952e56d66aff0125"
-dependencies = [
- "rustls-webpki",
-]
-
 [[package]]
 name = "weezl"
 version = "0.1.7"

+ 1 - 2
tooling/cli/Cargo.toml

@@ -96,6 +96,5 @@ lto = true
 [features]
 default = [ "rustls" ]
 native-tls = [ "tauri-bundler/native-tls", "ureq/native-tls" ]
-# ureq doesn't have a vendored mode but atto's feature should hopefully take care of it.
-native-tls-vendored = [ "tauri-bundler/native-tls-vendored" ]
+native-tls-vendored = [ "native-tls", "tauri-bundler/native-tls-vendored" ]
 rustls = [ "tauri-bundler/rustls", "ureq/tls" ]