Compare commits
1 Commits
commit-vie
...
fix-http-t
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2b62d34f1 |
105
Cargo.lock
generated
105
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()) }),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"]}
|
||||
|
||||
@@ -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();
|
||||
})
|
||||
}
|
||||
40
crates/ureq_client/examples/ureq_example.rs
Normal file
40
crates/ureq_client/examples/ureq_example.rs
Normal 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();
|
||||
})
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user