Remove async-std and surf from client

Switch to isahc library. It's not as fancy, but it works and has a smaller footprint.
This commit is contained in:
Nathan Sobo 2022-04-26 21:19:15 -06:00
parent 1293b21b2d
commit 78afbb3599
14 changed files with 161 additions and 474 deletions

View file

@ -594,14 +594,13 @@ mod tests {
use super::*;
use crate::test::{FakeHttpClient, FakeServer};
use gpui::TestAppContext;
use surf::http::Response;
#[gpui::test]
async fn test_channel_messages(cx: &mut TestAppContext) {
cx.foreground().forbid_parking();
let user_id = 5;
let http_client = FakeHttpClient::new(|_| async move { Ok(Response::new(404)) });
let http_client = FakeHttpClient::with_404_response();
let mut client = Client::new(http_client.clone());
let server = FakeServer::for_client(user_id, &mut client, &cx).await;

View file

@ -34,8 +34,8 @@ use std::{
},
time::{Duration, Instant},
};
use surf::{http::Method, Url};
use thiserror::Error;
use url::Url;
use util::{ResultExt, TryFutureExt};
pub use channel::*;
@ -89,9 +89,11 @@ pub enum EstablishConnectionError {
#[error("{0}")]
Other(#[from] anyhow::Error),
#[error("{0}")]
Http(#[from] http::Error),
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Http(#[from] async_tungstenite::tungstenite::http::Error),
Websocket(#[from] async_tungstenite::tungstenite::http::Error),
}
impl From<WebsocketError> for EstablishConnectionError {
@ -779,30 +781,27 @@ impl Client {
let http = self.http.clone();
cx.background().spawn(async move {
let mut rpc_url = format!("{}/rpc", *ZED_SERVER_URL);
let rpc_request = surf::Request::new(
Method::Get,
surf::Url::parse(&rpc_url).context("invalid ZED_SERVER_URL")?,
);
let rpc_response = http.send(rpc_request).await?;
let rpc_response = http.get(&rpc_url, Default::default()).await?;
if rpc_response.status().is_redirection() {
rpc_url = rpc_response
.header("Location")
.headers()
.get("Location")
.ok_or_else(|| anyhow!("missing location header in /rpc response"))?
.as_str()
.to_str()
.map_err(|error| EstablishConnectionError::other(error))?
.to_string();
}
// Until we switch the zed.dev domain to point to the new Next.js app, there
// will be no redirect required, and the app will connect directly to
// wss://zed.dev/rpc.
else if rpc_response.status() != surf::StatusCode::UpgradeRequired {
else if rpc_response.status() != StatusCode::UPGRADE_REQUIRED {
Err(anyhow!(
"unexpected /rpc response status {}",
rpc_response.status()
))?
}
let mut rpc_url = surf::Url::parse(&rpc_url).context("invalid rpc url")?;
let mut rpc_url = Url::parse(&rpc_url).context("invalid rpc url")?;
let rpc_host = rpc_url
.host_str()
.zip(rpc_url.port_or_known_default())

View file

@ -1,26 +1,45 @@
pub use anyhow::{anyhow, Result};
use futures::future::BoxFuture;
use std::sync::Arc;
pub use surf::{
http::{Method, Response as ServerResponse},
Request, Response, Url,
use isahc::{
config::{Configurable, RedirectPolicy},
AsyncBody,
};
use std::sync::Arc;
pub use anyhow::{anyhow, Result};
pub use isahc::{
http::{Method, Uri},
Error,
};
pub use url::Url;
pub type Request = isahc::Request<AsyncBody>;
pub type Response = isahc::Response<AsyncBody>;
pub trait HttpClient: Send + Sync {
fn send<'a>(&'a self, req: Request) -> BoxFuture<'a, Result<Response>>;
fn send<'a>(&'a self, req: Request) -> BoxFuture<'a, Result<Response, Error>>;
fn get<'a>(&'a self, uri: &str, body: AsyncBody) -> BoxFuture<'a, Result<Response, Error>> {
self.send(
isahc::Request::builder()
.method(Method::GET)
.uri(uri)
.body(body)
.unwrap(),
)
}
}
pub fn client() -> Arc<dyn HttpClient> {
Arc::new(surf::client())
Arc::new(
isahc::HttpClient::builder()
.redirect_policy(RedirectPolicy::Follow)
.build()
.unwrap(),
)
}
impl HttpClient for surf::Client {
fn send<'a>(&'a self, req: Request) -> BoxFuture<'a, Result<Response>> {
Box::pin(async move {
Ok(self
.send(req)
.await
.map_err(|e| anyhow!("http request failed: {}", e))?)
})
impl HttpClient for isahc::HttpClient {
fn send<'a>(&'a self, req: Request) -> BoxFuture<'a, Result<Response, Error>> {
Box::pin(async move { self.send_async(req).await })
}
}

View file

@ -1,5 +1,5 @@
use crate::{
http::{HttpClient, Request, Response, ServerResponse},
http::{self, HttpClient, Request, Response},
Client, Connection, Credentials, EstablishConnectionError, UserStore,
};
use anyhow::{anyhow, Result};
@ -176,14 +176,18 @@ impl FakeServer {
}
pub struct FakeHttpClient {
handler:
Box<dyn 'static + Send + Sync + Fn(Request) -> BoxFuture<'static, Result<ServerResponse>>>,
handler: Box<
dyn 'static
+ Send
+ Sync
+ Fn(Request) -> BoxFuture<'static, Result<Response, http::Error>>,
>,
}
impl FakeHttpClient {
pub fn new<Fut, F>(handler: F) -> Arc<dyn HttpClient>
where
Fut: 'static + Send + Future<Output = Result<ServerResponse>>,
Fut: 'static + Send + Future<Output = Result<Response, http::Error>>,
F: 'static + Send + Sync + Fn(Request) -> Fut,
{
Arc::new(Self {
@ -192,7 +196,12 @@ impl FakeHttpClient {
}
pub fn with_404_response() -> Arc<dyn HttpClient> {
Self::new(|_| async move { Ok(ServerResponse::new(404)) })
Self::new(|_| async move {
Ok(isahc::Response::builder()
.status(404)
.body(Default::default())
.unwrap())
})
}
}
@ -203,7 +212,7 @@ impl fmt::Debug for FakeHttpClient {
}
impl HttpClient for FakeHttpClient {
fn send<'a>(&'a self, req: Request) -> BoxFuture<'a, Result<Response>> {
fn send<'a>(&'a self, req: Request) -> BoxFuture<'a, Result<Response, crate::http::Error>> {
let future = (self.handler)(req);
Box::pin(async move { future.await.map(Into::into) })
}

View file

@ -1,9 +1,6 @@
use super::{
http::{HttpClient, Method, Request, Url},
proto, Client, Status, TypedEnvelope,
};
use anyhow::{anyhow, Context, Result};
use futures::future;
use super::{http::HttpClient, proto, Client, Status, TypedEnvelope};
use anyhow::{anyhow, Result};
use futures::{future, AsyncReadExt};
use gpui::{AsyncAppContext, Entity, ImageData, ModelContext, ModelHandle, Task};
use postage::{prelude::Stream, sink::Sink, watch};
use std::{
@ -255,22 +252,22 @@ impl Contact {
}
async fn fetch_avatar(http: &dyn HttpClient, url: &str) -> Result<Arc<ImageData>> {
let url = Url::parse(url).with_context(|| format!("failed to parse avatar url {:?}", url))?;
let mut request = Request::new(Method::Get, url);
request.middleware(surf::middleware::Redirect::default());
let mut response = http
.send(request)
.get(url, Default::default())
.await
.map_err(|e| anyhow!("failed to send user avatar request: {}", e))?;
if !response.status().is_success() {
return Err(anyhow!("avatar request failed {:?}", response.status()));
}
let bytes = response
.body_bytes()
let mut body = Vec::new();
response
.body_mut()
.read_to_end(&mut body)
.await
.map_err(|e| anyhow!("failed to read user avatar response body: {}", e))?;
let format = image::guess_format(&bytes)?;
let image = image::load_from_memory_with_format(&bytes, format)?.into_bgra8();
let format = image::guess_format(&body)?;
let image = image::load_from_memory_with_format(&body, format)?.into_bgra8();
Ok(ImageData::new(image))
}