Compare commits

...

1 Commits

Author SHA1 Message Date
Mikayla
b2b62d34f1 use ureq 3.x 2024-10-08 17:00:23 -07:00
9 changed files with 214 additions and 127 deletions

105
Cargo.lock generated
View File

@@ -3009,7 +3009,7 @@ dependencies = [
"core-foundation-sys",
"coreaudio-rs",
"dasp_sample",
"jni",
"jni 0.21.1",
"js-sys",
"libc",
"mach2",
@@ -5410,6 +5410,19 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "hoot"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "936723a03900105673cf6efb8821399ebdc287a494949c375f2ce50db83bf6c9"
dependencies = [
"http 1.1.0",
"httparse",
"log",
"tinyvec",
"url",
]
[[package]]
name = "hound"
version = "3.5.1"
@@ -6103,6 +6116,20 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
[[package]]
name = "jni"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c6df18c2e3db7e453d3c6ac5b3e9d5182664d28788126d39b91f2d1e22b017ec"
dependencies = [
"cesu8",
"combine",
"jni-sys",
"log",
"thiserror",
"walkdir",
]
[[package]]
name = "jni"
version = "0.21.1"
@@ -7512,7 +7539,7 @@ version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8b61bebd49e5d43f5f8cc7ee2891c16e0f41ec7954d36bcb6c14c5e0de867fb"
dependencies = [
"jni",
"jni 0.21.1",
"ndk",
"ndk-context",
"num-derive",
@@ -9704,6 +9731,7 @@ version = "0.23.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dabaac7466917e566adb06783a81ca48944c6898a1b08b9374106dd671f4c8"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
@@ -9724,6 +9752,19 @@ dependencies = [
"security-framework",
]
[[package]]
name = "rustls-native-certs"
version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5bfb394eeed242e909609f56089eecfe5fda225042e8b171791b9c95f5931e5"
dependencies = [
"openssl-probe",
"rustls-pemfile 2.1.3",
"rustls-pki-types",
"schannel",
"security-framework",
]
[[package]]
name = "rustls-native-certs"
version = "0.8.0"
@@ -9762,6 +9803,33 @@ version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc0a2ce646f8655401bb81e7927b812614bd5d91dbc968696be50603510fcaf0"
[[package]]
name = "rustls-platform-verifier"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afbb878bdfdf63a336a5e63561b1835e7a8c91524f51621db870169eac84b490"
dependencies = [
"core-foundation 0.9.4",
"core-foundation-sys",
"jni 0.19.0",
"log",
"once_cell",
"rustls 0.23.13",
"rustls-native-certs 0.7.3",
"rustls-platform-verifier-android",
"rustls-webpki 0.102.8",
"security-framework",
"security-framework-sys",
"webpki-roots 0.26.6",
"winapi",
]
[[package]]
name = "rustls-platform-verifier-android"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
[[package]]
name = "rustls-webpki"
version = "0.101.7"
@@ -10054,6 +10122,7 @@ dependencies = [
"core-foundation 0.9.4",
"core-foundation-sys",
"libc",
"num-bigint",
"security-framework-sys",
]
@@ -12695,18 +12764,27 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.9.1"
version = "3.0.0-rc1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8cdd25c339e200129fe4de81451814e5228c9b771d57378817d6117cc2b3f97"
checksum = "443349052924188e919be78d5870f6e827435579085947d1ae9ba32bb997e16f"
dependencies = [
"base64 0.21.7",
"base64 0.22.1",
"cc",
"flate2",
"hoot",
"http 1.1.0",
"log",
"once_cell",
"rustls 0.21.12",
"rustls-webpki 0.101.7",
"url",
"webpki-roots 0.25.4",
"rustls 0.23.13",
"rustls-pemfile 2.1.3",
"rustls-pki-types",
"rustls-platform-verifier",
"serde",
"serde_json",
"smallvec",
"thiserror",
"utf-8",
"webpki-roots 0.26.6",
]
[[package]]
@@ -13610,6 +13688,15 @@ version = "0.25.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
[[package]]
name = "webpki-roots"
version = "0.26.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "841c67bff177718f1d4dfefde8d8f0e78f9b6589319ba88312f567fc5841a958"
dependencies = [
"rustls-pki-types",
]
[[package]]
name = "weezl"
version = "0.1.8"

View File

@@ -1,4 +1,4 @@
use std::{borrow::Cow, io::Read, pin::Pin, task::Poll};
use std::{any::type_name, borrow::Cow, fmt::Debug, io::Read, pin::Pin, task::Poll};
use futures::{AsyncRead, AsyncReadExt};
@@ -34,6 +34,21 @@ impl AsyncBody {
}
}
impl Debug for AsyncBody {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct(type_name::<Self>())
.field(
"Kind",
&match self.0 {
Inner::Empty => "Empty",
Inner::SyncReader(_) => "SyncReader",
Inner::AsyncReader(_) => "AsyncReader",
},
)
.finish()
}
}
impl Default for AsyncBody {
fn default() -> Self {
Self(Inner::Empty)

View File

@@ -94,7 +94,6 @@ pub trait HttpClient: 'static + Send + Sync {
RedirectPolicy::NoFollow
})
.body(body);
match request {
Ok(request) => Box::pin(async move { self.send(request).await.map_err(Into::into) }),
Err(e) => Box::pin(async move { Err(e.into()) }),

View File

@@ -16,8 +16,8 @@ path = "src/reqwest_client.rs"
doctest = true
[[example]]
name = "client"
path = "examples/client.rs"
name = "reqwest_example"
path = "examples/reqwest_example.rs"
[dependencies]
anyhow.workspace = true

View File

@@ -16,8 +16,8 @@ path = "src/ureq_client.rs"
doctest = true
[[example]]
name = "client"
path = "examples/client.rs"
name = "ureq_example"
path = "examples/ureq_example.rs"
[dependencies]
anyhow.workspace = true
@@ -27,5 +27,9 @@ http_client.workspace = true
parking_lot.workspace = true
serde.workspace = true
smol.workspace = true
ureq = "=2.9.1"
ureq = "3.0.0-rc1"
util.workspace = true
[dev-dependencies]
gpui = {workspace = true, features = ["test-support"]}

View File

@@ -1,24 +0,0 @@
use futures::AsyncReadExt;
use http_client::{AsyncBody, HttpClient};
use ureq_client::UreqClient;
fn main() {
gpui::App::headless().run(|cx| {
println!("{:?}", std::thread::current().id());
cx.spawn(|cx| async move {
let resp = UreqClient::new(
None,
"Conrad's bot".to_string(),
cx.background_executor().clone(),
)
.get("http://zed.dev", AsyncBody::empty(), true)
.await
.unwrap();
let mut body = String::new();
resp.into_body().read_to_string(&mut body).await.unwrap();
println!("{}", body);
})
.detach();
})
}

View File

@@ -0,0 +1,40 @@
use http_client::{AsyncBody, HttpClient};
use ureq_client::UreqClient;
fn main() {
gpui::App::headless().run(|cx| {
println!("{:?}", std::thread::current().id());
cx.spawn(|cx| async move {
let client = UreqClient::new(
None,
"Conrad's bot".to_string(),
cx.background_executor().clone(),
);
let resp = client
.get("http://zed.dev", AsyncBody::empty(), false)
.await
.unwrap();
let mut body = String::new();
futures::AsyncReadExt::read_to_string(&mut resp.into_body(), &mut body)
.await
.unwrap();
println!("{}", body);
// Test sync read
let resp = client
.get("http://zed.dev", AsyncBody::empty(), false)
.await
.unwrap();
let mut body = String::new();
std::io::Read::read_to_string(&mut resp.into_body(), &mut body).unwrap();
println!("{}", body);
cx.update(|cx| {
cx.quit();
})
})
.detach();
})
}

View File

@@ -1,7 +1,4 @@
use std::any::type_name;
use std::collections::HashMap;
use std::io::Read;
use std::sync::Arc;
use std::time::Duration;
use std::{pin::Pin, task::Poll};
@@ -9,20 +6,14 @@ use anyhow::Error;
use futures::channel::mpsc;
use futures::future::BoxFuture;
use futures::{AsyncRead, SinkExt, StreamExt};
use http_client::{http, AsyncBody, HttpClient, RedirectPolicy, Uri};
use http_client::http::Response;
use http_client::{http, AsyncBody, HttpClient, RedirectPolicy, Request, Uri};
use smol::future::FutureExt;
use ureq::{Agent, Proxy, SendBody, Timeouts};
use util::ResultExt;
pub struct UreqClient {
// Note in ureq 2.x the options are stored on the Agent.
// In ureq 3.x we'll be able to set these on the request.
// In practice it's probably "fine" to have many clients, the number of distinct options
// is low; and most requests to the same connection will have the same options so the
// connection pool will work.
clients: Arc<parking_lot::Mutex<HashMap<(Duration, RedirectPolicy), ureq::Agent>>>,
proxy_url: Option<Uri>,
proxy: Option<ureq::Proxy>,
user_agent: String,
agent: ureq::Agent,
background_executor: gpui::BackgroundExecutor,
}
@@ -32,47 +23,28 @@ impl UreqClient {
user_agent: String,
background_executor: gpui::BackgroundExecutor,
) -> Self {
let agent: ureq::Agent = ureq::Config {
http_status_as_error: false,
proxy: proxy_url.and_then(|url| Proxy::new(&url.to_string()).log_err()),
timeouts: Timeouts {
connect: Some(Duration::from_secs(5)),
..Default::default()
},
user_agent: Some(user_agent),
..Default::default()
}
.into();
Self {
clients: Arc::default(),
proxy_url: proxy_url.clone(),
proxy: proxy_url.and_then(|url| ureq::Proxy::new(url.to_string()).log_err()),
user_agent,
agent,
background_executor,
}
}
fn agent_for(&self, redirect_policy: RedirectPolicy, timeout: Duration) -> ureq::Agent {
let mut clients = self.clients.lock();
// in case our assumption of distinct options is wrong, we'll sporadically clean it out.
if clients.len() > 50 {
clients.clear()
}
clients
.entry((timeout, redirect_policy.clone()))
.or_insert_with(|| {
let mut builder = ureq::AgentBuilder::new()
.timeout_connect(Duration::from_secs(5))
.timeout_read(timeout)
.timeout_write(timeout)
.user_agent(&self.user_agent)
.tls_config(http_client::TLS_CONFIG.clone())
.redirects(match redirect_policy {
RedirectPolicy::NoFollow => 0,
RedirectPolicy::FollowLimit(limit) => limit,
RedirectPolicy::FollowAll => 100,
});
if let Some(proxy) = &self.proxy {
builder = builder.proxy(proxy.clone());
}
builder.build()
})
.clone()
}
}
impl HttpClient for UreqClient {
fn proxy(&self) -> Option<&Uri> {
self.proxy_url.as_ref()
self.agent.config().proxy.as_ref().map(|proxy| proxy.uri())
}
fn type_name(&self) -> &'static str {
@@ -83,51 +55,43 @@ impl HttpClient for UreqClient {
&self,
request: http::Request<AsyncBody>,
) -> BoxFuture<'static, Result<http::Response<AsyncBody>, Error>> {
let agent = self.agent_for(
request
.extensions()
.get::<RedirectPolicy>()
.cloned()
.unwrap_or_default(),
request
.extensions()
.get::<http_client::ReadTimeout>()
.cloned()
.unwrap_or_default()
.0,
);
let mut req = agent.request(&request.method().as_ref(), &request.uri().to_string());
for (name, value) in request.headers().into_iter() {
req = req.set(name.as_str(), value.to_str().unwrap());
}
let body = request.into_body();
let executor = self.background_executor.clone();
let redirect_policy = request
.extensions()
.get::<RedirectPolicy>()
.cloned()
.unwrap_or_default();
let timeout = request
.extensions()
.get::<http_client::ReadTimeout>()
.cloned()
.unwrap_or_default()
.0;
let agent = self.agent.clone();
let executor = self.background_executor.clone();
self.background_executor
.spawn(async move {
let response = match req.send(body) {
Ok(response) => response,
Err(e) => match e {
ureq::Error::Status(_, response) => response,
ureq::Error::Transport(transport) => {
anyhow::bail!(transport)
}
},
let (parts, body) = request.into_parts();
let body = match body.0 {
http_client::Inner::Empty => SendBody::none(),
_ => SendBody::from_owned_reader(body),
};
let mut request = Request::from_parts(parts, body);
let mut builder = http::Response::builder()
.status(response.status())
.version(http::Version::HTTP_11);
for name in response.headers_names() {
if let Some(value) = response.header(&name) {
builder = builder.header(name, value);
}
}
let config = agent.configure_request(&mut request);
config.max_redirects = match redirect_policy {
RedirectPolicy::NoFollow => 0,
RedirectPolicy::FollowLimit(limit) => limit,
RedirectPolicy::FollowAll => 100,
};
config.timeouts.recv_body = Some(timeout);
let body = AsyncBody::from_reader(UreqResponseReader::new(executor, response));
let http_response = builder.body(body)?;
let response = agent.run(request)?;
Ok(http_response)
let (parts, response_body) = response.into_parts();
let response_body =
AsyncBody::from_reader(UreqResponseReader::new(executor, response_body));
Ok(Response::from_parts(parts, response_body))
})
.boxed()
}
@@ -141,13 +105,14 @@ struct UreqResponseReader {
}
impl UreqResponseReader {
fn new(background_executor: gpui::BackgroundExecutor, response: ureq::Response) -> Self {
fn new(background_executor: gpui::BackgroundExecutor, response: ureq::Body) -> Self {
let (mut sender, receiver) = mpsc::channel(1);
let mut reader = response.into_reader();
let task = background_executor.spawn(async move {
let mut buffer = vec![0; 8192];
loop {
let n = match reader.read(&mut buffer) {
let n = match std::io::Read::read(&mut reader, &mut buffer) {
Ok(0) => break,
Ok(n) => n,
Err(e) => {
@@ -195,6 +160,7 @@ impl AsyncRead for UreqResponseReader {
self.buffer.clear();
self.idx = 0;
}
Poll::Ready(Ok(n))
}
}