From 62279f8c35ca3c7e015fddc8e53339a00f5d8bb8 Mon Sep 17 00:00:00 2001 From: ruben Date: Sat, 29 Jun 2024 14:26:08 +0200 Subject: [PATCH] http3 make request future sync --- Cargo.toml | 4 +-- src/async_impl/h3_client/connect.rs | 10 ++++--- src/async_impl/h3_client/dns.rs | 43 ----------------------------- src/async_impl/h3_client/mod.rs | 3 +- src/async_impl/request.rs | 2 +- src/dns/resolve.rs | 6 ++-- tests/client.rs | 21 ++++++++------ 7 files changed, 26 insertions(+), 63 deletions(-) delete mode 100644 src/async_impl/h3_client/dns.rs diff --git a/Cargo.toml b/Cargo.toml index 7f81cb9ec..54a15879f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -160,8 +160,8 @@ tokio-socks = { version = "0.5.1", optional = true } hickory-resolver = { version = "0.24", optional = true, features = ["tokio-runtime"] } # HTTP/3 experimental support -h3 = { version = "0.0.5", optional = true } -h3-quinn = { version = "0.0.6", optional = true } +h3 = { git = "https://github.com/Ruben2424/h3.git", branch = "reqwest-sync", optional = true} +h3-quinn = {git = "https://github.com/Ruben2424/h3.git", branch = "reqwest-sync", optional = true} quinn = { version = "0.11.1", default-features = false, features = ["rustls", "runtime-tokio"], optional = true } slab = { version = "0.4.9", optional = true } # just to get minimal versions working with quinn futures-channel = { version = "0.3", optional = true } diff --git a/src/async_impl/h3_client/connect.rs b/src/async_impl/h3_client/connect.rs index 2cce1cf47..eed83f872 100644 --- a/src/async_impl/h3_client/connect.rs +++ b/src/async_impl/h3_client/connect.rs @@ -1,11 +1,9 @@ -use crate::async_impl::h3_client::dns::resolve; -use crate::dns::DynResolver; +use crate::dns::{resolve, DynResolver}; use crate::error::BoxError; use bytes::Bytes; use h3::client::SendRequest; use h3_quinn::{Connection, OpenStreams}; use http::Uri; -use hyper_util::client::legacy::connect::dns::Name; use quinn::crypto::rustls::QuicClientConfig; use quinn::{ClientConfig, Endpoint, TransportConfig}; use std::net::{IpAddr, SocketAddr}; @@ -58,7 +56,11 @@ impl H3Connector { // If the host is already an IP address, skip resolving. vec![SocketAddr::new(addr, port)] } else { - let addrs = resolve(&mut self.resolver, Name::from_str(host)?).await?; + let name = resolve::Name::from_str(host)?; + + + let addrs = self.resolver.resolver.resolve(name).await?; + let addrs = addrs.map(|mut addr| { addr.set_port(port); addr diff --git a/src/async_impl/h3_client/dns.rs b/src/async_impl/h3_client/dns.rs deleted file mode 100644 index bd59daaed..000000000 --- a/src/async_impl/h3_client/dns.rs +++ /dev/null @@ -1,43 +0,0 @@ -use core::task; -use hyper_util::client::legacy::connect::dns::Name; -use std::future::Future; -use std::net::SocketAddr; -use std::task::Poll; -use tower_service::Service; - -// Trait from hyper to implement DNS resolution for HTTP/3 client. -pub trait Resolve { - type Addrs: Iterator; - type Error: Into>; - type Future: Future>; - - fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll>; - fn resolve(&mut self, name: Name) -> Self::Future; -} - -impl Resolve for S -where - S: Service, - S::Response: Iterator, - S::Error: Into>, -{ - type Addrs = S::Response; - type Error = S::Error; - type Future = S::Future; - - fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> Poll> { - Service::poll_ready(self, cx) - } - - fn resolve(&mut self, name: Name) -> Self::Future { - Service::call(self, name) - } -} - -pub(super) async fn resolve(resolver: &mut R, name: Name) -> Result -where - R: Resolve, -{ - futures_util::future::poll_fn(|cx| resolver.poll_ready(cx)).await?; - resolver.resolve(name).await -} diff --git a/src/async_impl/h3_client/mod.rs b/src/async_impl/h3_client/mod.rs index aa1bc4422..4cea1df13 100644 --- a/src/async_impl/h3_client/mod.rs +++ b/src/async_impl/h3_client/mod.rs @@ -1,7 +1,6 @@ #![cfg(feature = "http3")] pub(crate) mod connect; -pub(crate) mod dns; mod pool; use crate::async_impl::body::ResponseBody; @@ -76,7 +75,7 @@ impl H3Client { } pub(crate) struct H3ResponseFuture { - inner: Pin, Error>> + Send>>, + inner: Pin, Error>> + Send + Sync>>, } impl Future for H3ResponseFuture { diff --git a/src/async_impl/request.rs b/src/async_impl/request.rs index aa900ca02..9a8c3ba93 100644 --- a/src/async_impl/request.rs +++ b/src/async_impl/request.rs @@ -504,7 +504,7 @@ impl RequestBuilder { /// # Ok(()) /// # } /// ``` - pub fn send(self) -> impl Future> { + pub fn send(self) -> impl Future> + Sync { match self.request { Ok(req) => self.client.execute_request(req), Err(err) => Pending::new_err(err), diff --git a/src/dns/resolve.rs b/src/dns/resolve.rs index 6ecd3e08a..6f714c976 100644 --- a/src/dns/resolve.rs +++ b/src/dns/resolve.rs @@ -12,10 +12,10 @@ use std::task::{Context, Poll}; use crate::error::BoxError; /// Alias for an `Iterator` trait object over `SocketAddr`. -pub type Addrs = Box + Send>; +pub type Addrs = Box + Send + Sync>; /// Alias for the `Future` type returned by a DNS resolver. -pub type Resolving = Pin> + Send>>; +pub type Resolving = Pin> + Send + Sync>>; /// Trait for customizing DNS resolution in reqwest. pub trait Resolve: Send + Sync { @@ -53,7 +53,7 @@ impl FromStr for Name { #[derive(Clone)] pub(crate) struct DynResolver { - resolver: Arc, + pub(crate) resolver: Arc, } impl DynResolver { diff --git a/tests/client.rs b/tests/client.rs index ce97456bf..f38564c4d 100644 --- a/tests/client.rs +++ b/tests/client.rs @@ -97,7 +97,7 @@ async fn http3_request_full() { }); let url = format!("https://{}/content-length", server.addr()); - let res = reqwest::Client::builder() + let res_fut = reqwest::Client::builder() .http3_prior_knowledge() .danger_accept_invalid_certs(true) .build() @@ -105,9 +105,12 @@ async fn http3_request_full() { .post(url) .version(http::Version::HTTP_3) .body("hello") - .send() - .await - .expect("request"); + .send(); + + fn assert_sync(_: &T) {} + assert_sync(&res_fut); + + let res = res_fut.await.expect("request"); assert_eq!(res.version(), http::Version::HTTP_3); assert_eq!(res.status(), reqwest::StatusCode::OK); @@ -121,14 +124,16 @@ async fn user_agent() { }); let url = format!("http://{}/ua", server.addr()); - let res = reqwest::Client::builder() + let res_fut = reqwest::Client::builder() .user_agent("reqwest-test-agent") .build() .expect("client builder") .get(&url) - .send() - .await - .expect("request"); + .send(); + + fn assert_sync(_: &T) {} + assert_sync(&res_fut); + let res = res_fut.await.expect("request"); assert_eq!(res.status(), reqwest::StatusCode::OK); }