瀏覽代碼

refactor(core): remove attohttpc client, closes #6415 (#6468)

* refactor(core): remove attohttpc client, closes #6415

* lint [skip ci]
Lucas Fernandes Nogueira 2 年之前
父節點
當前提交
dddaa943e7

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

@@ -0,0 +1,5 @@
+---
+"tauri": major
+---
+
+Removed the attohttpc client. The `reqwest-*` Cargo features were also removed.

+ 2 - 7
core/tauri-runtime/src/window.rs

@@ -99,9 +99,10 @@ fn get_menu_ids(map: &mut HashMap<MenuHash, MenuId>, menu: &Menu) {
 
 /// Describes the appearance of the mouse cursor.
 #[non_exhaustive]
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum CursorIcon {
   /// The platform-dependent default cursor.
+  #[default]
   Default,
   /// A simple crosshair.
   Crosshair,
@@ -203,12 +204,6 @@ impl<'de> Deserialize<'de> for CursorIcon {
   }
 }
 
-impl Default for CursorIcon {
-  fn default() -> Self {
-    CursorIcon::Default
-  }
-}
-
 #[cfg(target_os = "android")]
 pub struct CreationContext<'a> {
   pub env: jni::JNIEnv<'a>,

+ 5 - 12
core/tauri/Cargo.toml

@@ -37,9 +37,6 @@ targets = [
   "x86_64-apple-darwin"
 ]
 
-[package.metadata.cargo-udeps.ignore]
-normal = [ "attohttpc", "reqwest" ]
-
 [dependencies]
 serde_json = { version = "1.0", features = [ "raw_value" ] }
 serde = { version = "1.0", features = [ "derive" ] }
@@ -68,9 +65,8 @@ dirs-next = "2.0"
 percent-encoding = "2.2"
 base64 = { version = "0.21", optional = true }
 clap = { version = "3", optional = true }
-reqwest = { version = "0.11", default-features = false, features = [ "json", "stream" ], optional = true }
-bytes = { version = "1", features = [ "serde" ], optional = true }
-attohttpc = { version = "0.24", default-features = false, features = [ "compress", "json", "form" ] }
+reqwest = { version = "0.11", default-features = false, features = [ "json", "stream" ] }
+bytes = { version = "1", features = [ "serde" ] }
 open = { version = "3.0", optional = true }
 shared_child = { version = "1.0", optional = true }
 os_pipe = { version = "1.0", optional = true }
@@ -157,14 +153,11 @@ updater = [
   "fs-extract-api"
 ]
 http-api = [ ]
-http-multipart = [ "attohttpc/multipart-form", "reqwest/multipart" ]
+http-multipart = [ "reqwest/multipart" ]
 shell-open-api = [ "open", "regex", "tauri-macros/shell-scope" ]
 fs-extract-api = [ "zip" ]
-reqwest-client = [ "reqwest", "bytes" ]
-reqwest-default-tls = [ "reqwest-client", "reqwest/default-tls" ]
-default-tls = [ "attohttpc/tls-native" ]
-reqwest-native-tls-vendored = [ "reqwest-client", "reqwest/native-tls-vendored" ]
-native-tls-vendored = [ "attohttpc/tls-vendored" ]
+default-tls = [ "reqwest/default-tls" ]
+native-tls-vendored = [ "reqwest/native-tls-vendored" ]
 process-command-api = [ "shared_child", "os_pipe" ]
 global-shortcut = [
   "tauri-runtime/global-shortcut",

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

@@ -22,20 +22,12 @@ 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")))]
   #[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,

+ 2 - 5
core/tauri/src/lib.rs

@@ -22,11 +22,8 @@
 //! - **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.
-//! - **default-tls**: Provides TLS support to connect over HTTPS (applies to the default HTTP client).
-//! - **reqwest-default-tls**: Provides TLS support to connect over HTTPS (applies to the `reqwest` HTTP client).
-//! - **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).
+//! - **default-tls**: Provides TLS support to connect over HTTPS.
+//! - **native-tls-vendored**: Compile and statically link to a vendored copy of OpenSSL.
 //! - **process-command-api**: Enables the [`api::process::Command`] APIs.
 //! - **global-shortcut**: Enables the global shortcut APIs.
 //! - **clipboard**: Enables the clipboard APIs.

+ 15 - 8
core/tauri/src/manager.rs

@@ -911,7 +911,7 @@ impl<R: Runtime> WindowManager<R> {
     struct CachedResponse {
       status: http::StatusCode,
       headers: http::HeaderMap,
-      body: Cow<'static, [u8]>,
+      body: bytes::Bytes,
     }
 
     #[cfg(all(dev, mobile))]
@@ -938,16 +938,22 @@ impl<R: Runtime> WindowManager<R> {
 
       #[cfg(all(dev, mobile))]
       let mut response = {
-        use attohttpc::StatusCode;
+        use reqwest::StatusCode;
         let decoded_path = percent_encoding::percent_decode(path.as_bytes())
           .decode_utf8_lossy()
           .to_string();
         let url = format!("{url}{decoded_path}");
-        let mut proxy_builder = attohttpc::get(&url).danger_accept_invalid_certs(true);
+        #[allow(unused_mut)]
+        let mut client_builder = reqwest::ClientBuilder::new();
+        #[cfg(feature = "default-tls")]
+        {
+          client_builder = client_builder.danger_accept_invalid_certs(true);
+        }
+        let mut proxy_builder = client_builder.build().unwrap().get(&url);
         for (name, value) in request.headers() {
           proxy_builder = proxy_builder.header(name, value);
         }
-        match proxy_builder.send() {
+        match crate::async_runtime::block_on(proxy_builder.send()) {
           Ok(r) => {
             let mut response_cache_ = response_cache.lock().unwrap();
             let mut response = None;
@@ -957,12 +963,13 @@ impl<R: Runtime> WindowManager<R> {
             let response = if let Some(r) = response {
               r
             } else {
-              let (status, headers, reader) = r.split();
-              let body = reader.bytes()?;
+              let status = r.status();
+              let headers = r.headers().clone();
+              let body = crate::async_runtime::block_on(r.bytes())?;
               let response = CachedResponse {
                 status,
                 headers,
-                body: body.into(),
+                body,
               };
               response_cache_.insert(url.clone(), response);
               response_cache_.get(&url).unwrap()
@@ -972,7 +979,7 @@ impl<R: Runtime> WindowManager<R> {
             }
             builder
               .status(response.status)
-              .body(response.body.clone())?
+              .body(response.body.to_vec())?
           }
           Err(e) => {
             debug_eprintln!("Failed to request {}: {}", url.as_str(), e);

+ 8 - 29
core/tauri/src/updater/core.rs

@@ -10,6 +10,7 @@ use crate::{
   AppHandle, Manager, Runtime,
 };
 use base64::Engine;
+use futures_util::StreamExt;
 use http::{
   header::{HeaderName, HeaderValue},
   HeaderMap, StatusCode,
@@ -561,35 +562,13 @@ 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();
-      while let Some(chunk) = stream.next().await {
-        let chunk = chunk?;
-        let bytes = chunk.as_ref().to_vec();
-        on_chunk(bytes.len(), content_length);
-        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()),
-        }
-      }
+
+    let mut stream = response.bytes_stream();
+    while let Some(chunk) = stream.next().await {
+      let chunk = chunk?;
+      let bytes = chunk.as_ref().to_vec();
+      on_chunk(bytes.len(), content_length);
+      buffer.extend(bytes);
     }
 
     on_download_finish();

+ 0 - 31
examples/api/src-tauri/Cargo.lock

@@ -136,23 +136,6 @@ dependencies = [
  "system-deps",
 ]
 
-[[package]]
-name = "attohttpc"
-version = "0.24.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8d9a9bf8b79a749ee0b911b91b671cc2b6c670bdbc7e3dfd537576ddc94bb2a2"
-dependencies = [
- "flate2",
- "http",
- "log",
- "mime",
- "multipart",
- "serde",
- "serde_json",
- "serde_urlencoded",
- "url",
-]
-
 [[package]]
 name = "atty"
 version = "0.2.14"
@@ -1753,19 +1736,6 @@ dependencies = [
  "windows-sys 0.42.0",
 ]
 
-[[package]]
-name = "multipart"
-version = "0.18.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00dec633863867f29cb39df64a397cdf4a6354708ddd7759f70c7fb51c5f9182"
-dependencies = [
- "log",
- "mime",
- "mime_guess",
- "rand 0.8.5",
- "tempfile",
-]
-
 [[package]]
 name = "ndk"
 version = "0.6.0"
@@ -3004,7 +2974,6 @@ name = "tauri"
 version = "2.0.0-alpha.3"
 dependencies = [
  "anyhow",
- "attohttpc",
  "base64 0.21.0",
  "bytes",
  "clap",

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

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

+ 4 - 12
tooling/cli/src/interface/rust.rs

@@ -353,18 +353,10 @@ fn shared_options(
     let all_features = app_settings
       .manifest
       .all_enabled_features(if let Some(f) = features { f } else { &[] });
-    if all_features.contains(&"tauri/default-tls".into())
-      || all_features.contains(&"tauri/reqwest-default-tls".into())
-    {
-      if all_features.contains(&"tauri/reqwest-client".into()) {
-        features
-          .get_or_insert(Vec::new())
-          .push("tauri/reqwest-native-tls-vendored".into());
-      } else {
-        features
-          .get_or_insert(Vec::new())
-          .push("tauri/native-tls-vendored".into());
-      }
+    if all_features.contains(&"tauri/default-tls".into()) {
+      features
+        .get_or_insert(Vec::new())
+        .push("tauri/native-tls-vendored".into());
     }
   }
 }