Remove reqwest dependency from gpui (#44424)

Julia Ryan created

This was pulling in tokio which is pretty unfortunate. The solution is
to do the `reqwest::Form` to `http::Reqwest` conversion in the
reliability crate instead of our http client wrapper.

Release Notes:

- N/A

Change summary

Cargo.lock                                  |  1 
crates/http_client/Cargo.toml               |  1 
crates/http_client/src/async_body.rs        | 11 --------
crates/http_client/src/http_client.rs       | 29 ----------------------
crates/reqwest_client/src/reqwest_client.rs | 20 ---------------
crates/zed/src/reliability.rs               | 15 +++++++----
6 files changed, 10 insertions(+), 67 deletions(-)

Detailed changes

Cargo.lock 🔗

@@ -7756,7 +7756,6 @@ dependencies = [
  "tempfile",
  "url",
  "util",
- "zed-reqwest",
 ]
 
 [[package]]

crates/http_client/Cargo.toml 🔗

@@ -28,7 +28,6 @@ http-body.workspace = true
 http.workspace = true
 log.workspace = true
 parking_lot.workspace = true
-reqwest.workspace = true
 serde.workspace = true
 serde_json.workspace = true
 serde_urlencoded.workspace = true

crates/http_client/src/async_body.rs 🔗

@@ -88,17 +88,6 @@ impl From<&'static str> for AsyncBody {
     }
 }
 
-impl TryFrom<reqwest::Body> for AsyncBody {
-    type Error = anyhow::Error;
-
-    fn try_from(value: reqwest::Body) -> Result<Self, Self::Error> {
-        value
-            .as_bytes()
-            .ok_or_else(|| anyhow::anyhow!("Underlying data is a stream"))
-            .map(|bytes| Self::from_bytes(Bytes::copy_from_slice(bytes)))
-    }
-}
-
 impl<T: Into<Self>> From<Option<T>> for AsyncBody {
     fn from(body: Option<T>) -> Self {
         match body {

crates/http_client/src/http_client.rs 🔗

@@ -8,10 +8,7 @@ use derive_more::Deref;
 use http::HeaderValue;
 pub use http::{self, Method, Request, Response, StatusCode, Uri, request::Builder};
 
-use futures::{
-    FutureExt as _,
-    future::{self, BoxFuture},
-};
+use futures::future::BoxFuture;
 use parking_lot::Mutex;
 use serde::Serialize;
 use std::sync::Arc;
@@ -110,14 +107,6 @@ pub trait HttpClient: 'static + Send + Sync {
     fn as_fake(&self) -> &FakeHttpClient {
         panic!("called as_fake on {}", type_name::<Self>())
     }
-
-    fn send_multipart_form<'a>(
-        &'a self,
-        _url: &str,
-        _request: reqwest::multipart::Form,
-    ) -> BoxFuture<'a, anyhow::Result<Response<AsyncBody>>> {
-        future::ready(Err(anyhow!("not implemented"))).boxed()
-    }
 }
 
 /// An [`HttpClient`] that may have a proxy.
@@ -165,14 +154,6 @@ impl HttpClient for HttpClientWithProxy {
     fn as_fake(&self) -> &FakeHttpClient {
         self.client.as_fake()
     }
-
-    fn send_multipart_form<'a>(
-        &'a self,
-        url: &str,
-        form: reqwest::multipart::Form,
-    ) -> BoxFuture<'a, anyhow::Result<Response<AsyncBody>>> {
-        self.client.send_multipart_form(url, form)
-    }
 }
 
 /// An [`HttpClient`] that has a base URL.
@@ -306,14 +287,6 @@ impl HttpClient for HttpClientWithUrl {
     fn as_fake(&self) -> &FakeHttpClient {
         self.client.as_fake()
     }
-
-    fn send_multipart_form<'a>(
-        &'a self,
-        url: &str,
-        request: reqwest::multipart::Form,
-    ) -> BoxFuture<'a, anyhow::Result<Response<AsyncBody>>> {
-        self.client.send_multipart_form(url, request)
-    }
 }
 
 pub fn read_proxy_from_env() -> Option<Url> {

crates/reqwest_client/src/reqwest_client.rs 🔗

@@ -270,26 +270,6 @@ impl http_client::HttpClient for ReqwestClient {
         }
         .boxed()
     }
-
-    fn send_multipart_form<'a>(
-        &'a self,
-        url: &str,
-        form: reqwest::multipart::Form,
-    ) -> futures::future::BoxFuture<'a, anyhow::Result<http_client::Response<http_client::AsyncBody>>>
-    {
-        let response = self.client.post(url).multipart(form).send();
-        self.handle
-            .spawn(async move {
-                let response = response.await?;
-                let mut builder = http::response::Builder::new().status(response.status());
-                for (k, v) in response.headers() {
-                    builder = builder.header(k, v)
-                }
-                Ok(builder.body(response.bytes().await?.into())?)
-            })
-            .map(|e| e?)
-            .boxed()
-    }
 }
 
 #[cfg(test)]

crates/zed/src/reliability.rs 🔗

@@ -1,8 +1,8 @@
 use anyhow::{Context as _, Result};
 use client::{Client, telemetry::MINIDUMP_ENDPOINT};
-use futures::AsyncReadExt;
+use futures::{AsyncReadExt, TryStreamExt};
 use gpui::{App, AppContext as _, SerializedThreadTaskTimings};
-use http_client::{self, HttpClient};
+use http_client::{self, AsyncBody, HttpClient, Request};
 use log::info;
 use project::Project;
 use proto::{CrashReport, GetCrashFilesResponse};
@@ -296,11 +296,14 @@ async fn upload_minidump(
 
     // TODO: feature-flag-context, and more of device-context like screen resolution, available ram, device model, etc
 
+    let stream = form
+        .into_stream()
+        .map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))
+        .into_async_read();
+    let body = AsyncBody::from_reader(stream);
+    let req = Request::builder().uri(endpoint).body(body)?;
     let mut response_text = String::new();
-    let mut response = client
-        .http_client()
-        .send_multipart_form(endpoint, form)
-        .await?;
+    let mut response = client.http_client().send(req).await?;
     response
         .body_mut()
         .read_to_string(&mut response_text)