From d1587938ece3c9a66069132d7f440ba5f5f30c51 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Thu, 11 Aug 2022 14:48:24 +0800 Subject: [PATCH 1/8] refactor: Remove lifetime from Context Signed-off-by: Xuanwo --- src/body/mod.rs | 2 +- src/client.rs | 18 +++++++++++------- src/cookies/interceptor.rs | 12 +++++------- src/default_headers.rs | 12 +++++------- src/handler.rs | 5 ++--- src/interceptor/context.rs | 24 ++++++++++++------------ src/interceptor/mod.rs | 20 ++++++++++---------- src/interceptor/obj.rs | 24 ++++++++++++------------ src/macros.rs | 2 +- src/redirect.rs | 8 ++++---- 10 files changed, 63 insertions(+), 64 deletions(-) diff --git a/src/body/mod.rs b/src/body/mod.rs index 35fe1374..cb49a933 100644 --- a/src/body/mod.rs +++ b/src/body/mod.rs @@ -188,7 +188,7 @@ impl AsyncBody { impl AsyncRead for AsyncBody { fn poll_read( mut self: Pin<&mut Self>, - cx: &mut Context<'_>, + cx: &mut Context, buf: &mut [u8], ) -> Poll> { match &mut self.0 { diff --git a/src/client.rs b/src/client.rs index 0514e787..bbeea3d2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -21,8 +21,7 @@ use futures_lite::{ }; use http::{ header::{HeaderMap, HeaderName, HeaderValue}, - Request, - Response, + Request, Response, }; use once_cell::sync::Lazy; use std::{ @@ -651,6 +650,11 @@ impl HttpClient { self.inner.cookie_jar.as_ref() } + /// Get the configured interceptors for this HTTP client. + pub(crate) fn interceptors(&self) -> &[InterceptorObj] { + &self.inner.interceptors + } + /// Send a GET request to the given URI. /// /// To customize the request further, see [`HttpClient::send`]. To execute @@ -1029,8 +1033,8 @@ impl HttpClient { } let ctx = interceptor::Context { - invoker: Arc::new(self), - interceptors: &self.inner.interceptors, + client: self.clone(), + interceptor_offset: 0, }; ctx.send(request).await @@ -1159,7 +1163,7 @@ impl HttpClient { } } -impl crate::interceptor::Invoke for &HttpClient { +impl crate::interceptor::Invoke for HttpClient { fn invoke( &self, mut request: Request, @@ -1260,7 +1264,7 @@ impl<'c> ResponseFuture<'c> { impl Future for ResponseFuture<'_> { type Output = Result, Error>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { self.0.as_mut().poll(cx) } } @@ -1281,7 +1285,7 @@ struct ResponseBody { impl AsyncRead for ResponseBody { fn poll_read( mut self: Pin<&mut Self>, - cx: &mut Context<'_>, + cx: &mut Context, buf: &mut [u8], ) -> Poll> { let inner = Pin::new(&mut self.inner); diff --git a/src/cookies/interceptor.rs b/src/cookies/interceptor.rs index 408c066e..f41f650c 100644 --- a/src/cookies/interceptor.rs +++ b/src/cookies/interceptor.rs @@ -19,20 +19,18 @@ pub(crate) struct CookieInterceptor { impl CookieInterceptor { pub(crate) fn new(cookie_jar: Option) -> Self { - Self { - cookie_jar, - } + Self { cookie_jar } } } impl Interceptor for CookieInterceptor { type Err = Error; - fn intercept<'a>( - &'a self, + fn intercept( + &self, mut request: Request, - ctx: Context<'a>, - ) -> InterceptorFuture<'a, Self::Err> { + ctx: Context, + ) -> InterceptorFuture<'_, Self::Err> { Box::pin(async move { // Determine the cookie jar to use for this request. If one is // attached to this specific request, use it, otherwise use the diff --git a/src/default_headers.rs b/src/default_headers.rs index f80de02a..db3725b3 100644 --- a/src/default_headers.rs +++ b/src/default_headers.rs @@ -13,20 +13,18 @@ pub(crate) struct DefaultHeadersInterceptor { impl From> for DefaultHeadersInterceptor { fn from(headers: HeaderMap) -> Self { - Self { - headers, - } + Self { headers } } } impl Interceptor for DefaultHeadersInterceptor { type Err = Error; - fn intercept<'a>( - &'a self, + fn intercept( + &self, mut request: Request, - ctx: Context<'a>, - ) -> InterceptorFuture<'a, Self::Err> { + ctx: Context, + ) -> InterceptorFuture<'_, Self::Err> { Box::pin(async move { // We are checking here if header already contains the key, simply // ignore it. In case the key wasn't present in parts.headers ensure diff --git a/src/handler.rs b/src/handler.rs index 27c34a85..5192a6cf 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -20,8 +20,7 @@ use std::{ ffi::CStr, fmt, future::Future, - io, - mem, + io, mem, net::SocketAddr, os::raw::{c_char, c_long}, pin::Pin, @@ -680,7 +679,7 @@ pub(crate) struct ResponseBodyReader { impl AsyncRead for ResponseBodyReader { fn poll_read( mut self: Pin<&mut Self>, - cx: &mut Context<'_>, + cx: &mut Context, buf: &mut [u8], ) -> Poll> { let inner = Pin::new(&mut self.inner); diff --git a/src/interceptor/context.rs b/src/interceptor/context.rs index 187e6cbb..d4a3e5a8 100644 --- a/src/interceptor/context.rs +++ b/src/interceptor/context.rs @@ -1,32 +1,32 @@ -use super::{Interceptor, InterceptorFuture, InterceptorObj}; -use crate::{body::AsyncBody, error::Error}; +use super::{Interceptor, InterceptorFuture}; +use crate::{body::AsyncBody, error::Error, HttpClient}; use http::{Request, Response}; -use std::{fmt, sync::Arc}; +use std::fmt; /// Execution context for an interceptor. -pub struct Context<'a> { - pub(crate) invoker: Arc, - pub(crate) interceptors: &'a [InterceptorObj], +pub struct Context { + pub(crate) client: HttpClient, + pub(crate) interceptor_offset: usize, } -impl<'a> Context<'a> { +impl Context { /// Send a request asynchronously, executing the next interceptor in the /// chain, if any. pub async fn send(&self, request: Request) -> Result, Error> { - if let Some(interceptor) = self.interceptors.first() { + if let Some(interceptor) = self.client.interceptors().get(self.interceptor_offset) { let inner_context = Self { - invoker: self.invoker.clone(), - interceptors: &self.interceptors[1..], + client: self.client.clone(), + interceptor_offset: self.interceptor_offset + 1, }; interceptor.intercept(request, inner_context).await } else { - self.invoker.invoke(request).await + self.client.invoke(request).await } } } -impl fmt::Debug for Context<'_> { +impl fmt::Debug for Context { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("Context").finish() } diff --git a/src/interceptor/mod.rs b/src/interceptor/mod.rs index 43b27b15..c6e08595 100644 --- a/src/interceptor/mod.rs +++ b/src/interceptor/mod.rs @@ -38,7 +38,7 @@ macro_rules! interceptor { ($request:ident, $ctx:ident, $body:expr) => {{ async fn interceptor( mut $request: $crate::http::Request<$crate::AsyncBody>, - $ctx: $crate::interceptor::Context<'_>, + $ctx: $crate::interceptor::Context, ) -> Result<$crate::http::Response<$crate::AsyncBody>, $crate::Error> { (move || async move { $body })().await.map_err(Into::into) } @@ -60,11 +60,11 @@ pub trait Interceptor: Send + Sync { /// /// The returned future is allowed to borrow the interceptor for the /// duration of its execution. - fn intercept<'a>( - &'a self, + fn intercept( + &self, request: Request, - ctx: Context<'a>, - ) -> InterceptorFuture<'a, Self::Err>; + ctx: Context, + ) -> InterceptorFuture<'_, Self::Err>; } /// The type of future returned by an interceptor. @@ -73,7 +73,7 @@ pub type InterceptorFuture<'a, E> = Pin(f: F) -> InterceptorFn where - F: for<'a> private::AsyncFn2, Context<'a>, Output = InterceptorResult> + F: for<'a> private::AsyncFn2, Context, Output = InterceptorResult> + Send + Sync + 'static, @@ -89,18 +89,18 @@ pub struct InterceptorFn(F); impl Interceptor for InterceptorFn where E: Error + Send + Sync + 'static, - F: for<'a> private::AsyncFn2, Context<'a>, Output = InterceptorResult> + F: for<'a> private::AsyncFn2, Context, Output = InterceptorResult> + Send + Sync + 'static, { type Err = E; - fn intercept<'a>( + fn intercept( &self, request: Request, - ctx: Context<'a>, - ) -> InterceptorFuture<'a, Self::Err> { + ctx: Context, + ) -> InterceptorFuture<'_, Self::Err> { Box::pin(self.0.call(request, ctx)) } } diff --git a/src/interceptor/obj.rs b/src/interceptor/obj.rs index d0f0c2f6..84be457c 100644 --- a/src/interceptor/obj.rs +++ b/src/interceptor/obj.rs @@ -17,11 +17,11 @@ where impl Interceptor for &InterceptorObj { type Err = Error; - fn intercept<'a>( - &'a self, + fn intercept( + &self, request: Request, - cx: Context<'a>, - ) -> InterceptorFuture<'a, Self::Err> { + cx: Context, + ) -> InterceptorFuture<'_, Self::Err> { self.0.dyn_intercept(request, cx) } } @@ -29,19 +29,19 @@ impl Interceptor for &InterceptorObj { /// Object-safe version of the interceptor used for type erasure. Implementation /// detail of [`InterceptorObj`]. trait DynInterceptor: Send + Sync { - fn dyn_intercept<'a>( - &'a self, + fn dyn_intercept( + &self, request: Request, - cx: Context<'a>, - ) -> InterceptorFuture<'a, Error>; + cx: Context, + ) -> InterceptorFuture<'_, Error>; } impl DynInterceptor for I { - fn dyn_intercept<'a>( - &'a self, + fn dyn_intercept( + &self, request: Request, - cx: Context<'a>, - ) -> InterceptorFuture<'a, Error> { + cx: Context, + ) -> InterceptorFuture<'_, Error> { Box::pin(async move { self.intercept(request, cx).await.map_err(Error::from_any) }) } } diff --git a/src/macros.rs b/src/macros.rs index 7d39c7b1..bcd629f8 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -31,7 +31,7 @@ macro_rules! decl_future { impl<$($($T: Unpin),*)*> ::std::future::Future for $ident<'_, $($($T),*)*> { type Output = $output; - fn poll(mut self: ::std::pin::Pin<&mut Self>, cx: &mut ::std::task::Context<'_>) -> ::std::task::Poll { + fn poll(mut self: ::std::pin::Pin<&mut Self>, cx: &mut ::std::task::Context) -> ::std::task::Poll { self.as_mut().inner.as_mut().poll(cx) } } diff --git a/src/redirect.rs b/src/redirect.rs index 21489e56..261f9994 100644 --- a/src/redirect.rs +++ b/src/redirect.rs @@ -26,11 +26,11 @@ pub(crate) struct RedirectInterceptor; impl Interceptor for RedirectInterceptor { type Err = Error; - fn intercept<'a>( - &'a self, + fn intercept( + &self, mut request: Request, - ctx: Context<'a>, - ) -> InterceptorFuture<'a, Self::Err> { + ctx: Context, + ) -> InterceptorFuture<'_, Self::Err> { Box::pin(async move { // Store the effective URI to include in the response. let mut effective_uri = request.uri().clone(); From c1b4064192d2bfb532f21e40f9d646581198415a Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Thu, 11 Aug 2022 19:11:40 +0800 Subject: [PATCH 2/8] Make ResponseFuture static Signed-off-by: Xuanwo --- src/client.rs | 50 ++++++++++++++++++++------------------ src/cookies/interceptor.rs | 10 +++++--- src/default_headers.rs | 12 ++++++--- src/handler.rs | 3 ++- src/interceptor/context.rs | 10 ++++---- src/interceptor/mod.rs | 15 +++--------- src/interceptor/obj.rs | 21 ++++------------ src/lib.rs | 12 ++++----- src/redirect.rs | 2 +- src/request.rs | 4 +-- 10 files changed, 67 insertions(+), 72 deletions(-) diff --git a/src/client.rs b/src/client.rs index bbeea3d2..bce23037 100644 --- a/src/client.rs +++ b/src/client.rs @@ -12,7 +12,7 @@ use crate::{ error::{Error, ErrorKind}, handler::{RequestHandler, ResponseBodyReader}, headers::HasHeaders, - interceptor::{self, Interceptor, InterceptorObj}, + interceptor::{self, Interceptor, InterceptorFuture, InterceptorObj}, parsing::header_to_curl_string, }; use futures_lite::{ @@ -21,7 +21,8 @@ use futures_lite::{ }; use http::{ header::{HeaderMap, HeaderName, HeaderValue}, - Request, Response, + Request, + Response, }; use once_cell::sync::Lazy; use std::{ @@ -686,7 +687,7 @@ impl HttpClient { /// /// To customize the request further, see [`HttpClient::send_async`]. To /// execute the request synchronously, see [`HttpClient::get`]. - pub fn get_async(&self, uri: U) -> ResponseFuture<'_> + pub fn get_async(&self, uri: U) -> ResponseFuture where http::Uri: TryFrom, >::Error: Into, @@ -728,7 +729,7 @@ impl HttpClient { /// /// To customize the request further, see [`HttpClient::send_async`]. To /// execute the request synchronously, see [`HttpClient::head`]. - pub fn head_async(&self, uri: U) -> ResponseFuture<'_> + pub fn head_async(&self, uri: U) -> ResponseFuture where http::Uri: TryFrom, >::Error: Into, @@ -774,7 +775,7 @@ impl HttpClient { /// /// To customize the request further, see [`HttpClient::send_async`]. To /// execute the request synchronously, see [`HttpClient::post`]. - pub fn post_async(&self, uri: U, body: B) -> ResponseFuture<'_> + pub fn post_async(&self, uri: U, body: B) -> ResponseFuture where http::Uri: TryFrom, >::Error: Into, @@ -822,7 +823,7 @@ impl HttpClient { /// /// To customize the request further, see [`HttpClient::send_async`]. To /// execute the request synchronously, see [`HttpClient::put`]. - pub fn put_async(&self, uri: U, body: B) -> ResponseFuture<'_> + pub fn put_async(&self, uri: U, body: B) -> ResponseFuture where http::Uri: TryFrom, >::Error: Into, @@ -854,7 +855,7 @@ impl HttpClient { /// /// To customize the request further, see [`HttpClient::send_async`]. To /// execute the request synchronously, see [`HttpClient::delete`]. - pub fn delete_async(&self, uri: U) -> ResponseFuture<'_> + pub fn delete_async(&self, uri: U) -> ResponseFuture where http::Uri: TryFrom, >::Error: Into, @@ -1001,7 +1002,7 @@ impl HttpClient { /// # Ok(()) } /// ``` #[inline] - pub fn send_async(&self, request: Request) -> ResponseFuture<'_> + pub fn send_async(&self, request: Request) -> ResponseFuture where B: Into, { @@ -1011,17 +1012,16 @@ impl HttpClient { uri = ?request.uri(), ); + let client = self.clone(); ResponseFuture::new( - self.send_async_inner(request.map(Into::into)) + client + .send_async_inner(request.map(Into::into)) .instrument(span), ) } /// Actually send the request. All the public methods go through here. - async fn send_async_inner( - &self, - mut request: Request, - ) -> Result, Error> { + fn send_async_inner(&self, mut request: Request) -> InterceptorFuture { // Populate request config, creating if necessary. if let Some(config) = request.extensions_mut().get_mut::() { // Merge request configuration with defaults. @@ -1037,7 +1037,7 @@ impl HttpClient { interceptor_offset: 0, }; - ctx.send(request).await + ctx.send(request) } fn create_easy_handle( @@ -1167,7 +1167,9 @@ impl crate::interceptor::Invoke for HttpClient { fn invoke( &self, mut request: Request, - ) -> crate::interceptor::InterceptorFuture<'_, Error> { + ) -> crate::interceptor::InterceptorFuture { + let client = self.clone(); + Box::pin(async move { let is_head_request = request.method() == http::Method::HEAD; @@ -1187,10 +1189,12 @@ impl crate::interceptor::Invoke for HttpClient { .unwrap_or(false); // Create and configure a curl easy handle to fulfil the request. - let (easy, future) = self.create_easy_handle(request).map_err(Error::from_any)?; + let (easy, future) = client + .create_easy_handle(request) + .map_err(Error::from_any)?; // Send the request to the agent to be executed. - self.inner.agent.submit_request(easy)?; + client.inner.agent.submit_request(easy)?; // Await for the response headers. let response = future.await?; @@ -1224,7 +1228,7 @@ impl crate::interceptor::Invoke for HttpClient { inner: reader, // Extend the lifetime of the agent by including a reference // to its handle in the response body. - _client: (*self).clone(), + _client: client, }; if let Some(len) = body_len { @@ -1246,12 +1250,12 @@ impl fmt::Debug for HttpClient { /// A future for a request being executed. #[must_use = "futures do nothing unless you `.await` or poll them"] -pub struct ResponseFuture<'c>(Pin::Output> + 'c + Send>>); +pub struct ResponseFuture(Pin::Output> + 'static + Send>>); -impl<'c> ResponseFuture<'c> { +impl ResponseFuture { fn new(future: F) -> Self where - F: Future::Output> + Send + 'c, + F: Future::Output> + Send + 'static, { ResponseFuture(Box::pin(future)) } @@ -1261,7 +1265,7 @@ impl<'c> ResponseFuture<'c> { } } -impl Future for ResponseFuture<'_> { +impl Future for ResponseFuture { type Output = Result, Error>; fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { @@ -1269,7 +1273,7 @@ impl Future for ResponseFuture<'_> { } } -impl<'c> fmt::Debug for ResponseFuture<'c> { +impl fmt::Debug for ResponseFuture { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("ResponseFuture").finish() } diff --git a/src/cookies/interceptor.rs b/src/cookies/interceptor.rs index f41f650c..945efc77 100644 --- a/src/cookies/interceptor.rs +++ b/src/cookies/interceptor.rs @@ -19,7 +19,9 @@ pub(crate) struct CookieInterceptor { impl CookieInterceptor { pub(crate) fn new(cookie_jar: Option) -> Self { - Self { cookie_jar } + Self { + cookie_jar, + } } } @@ -30,7 +32,9 @@ impl Interceptor for CookieInterceptor { &self, mut request: Request, ctx: Context, - ) -> InterceptorFuture<'_, Self::Err> { + ) -> InterceptorFuture { + let cookie_jar = self.cookie_jar.clone(); + Box::pin(async move { // Determine the cookie jar to use for this request. If one is // attached to this specific request, use it, otherwise use the @@ -39,7 +43,7 @@ impl Interceptor for CookieInterceptor { .extensions() .get::() .cloned() - .or_else(|| self.cookie_jar.clone()); + .or(cookie_jar); if let Some(jar) = jar.as_ref() { // Get the outgoing cookie header. diff --git a/src/default_headers.rs b/src/default_headers.rs index db3725b3..56f01f38 100644 --- a/src/default_headers.rs +++ b/src/default_headers.rs @@ -13,7 +13,9 @@ pub(crate) struct DefaultHeadersInterceptor { impl From> for DefaultHeadersInterceptor { fn from(headers: HeaderMap) -> Self { - Self { headers } + Self { + headers, + } } } @@ -24,14 +26,16 @@ impl Interceptor for DefaultHeadersInterceptor { &self, mut request: Request, ctx: Context, - ) -> InterceptorFuture<'_, Self::Err> { + ) -> InterceptorFuture { + let headers = self.headers.clone(); + Box::pin(async move { // We are checking here if header already contains the key, simply // ignore it. In case the key wasn't present in parts.headers ensure // that we have all the headers from default headers. - for name in self.headers.keys() { + for name in headers.keys() { if !request.headers().contains_key(name) { - for v in self.headers.get_all(name).iter() { + for v in headers.get_all(name).iter() { request.headers_mut().append(name, v.clone()); } } diff --git a/src/handler.rs b/src/handler.rs index 5192a6cf..f842d56c 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -20,7 +20,8 @@ use std::{ ffi::CStr, fmt, future::Future, - io, mem, + io, + mem, net::SocketAddr, os::raw::{c_char, c_long}, pin::Pin, diff --git a/src/interceptor/context.rs b/src/interceptor/context.rs index d4a3e5a8..68baca95 100644 --- a/src/interceptor/context.rs +++ b/src/interceptor/context.rs @@ -1,6 +1,6 @@ use super::{Interceptor, InterceptorFuture}; use crate::{body::AsyncBody, error::Error, HttpClient}; -use http::{Request, Response}; +use http::Request; use std::fmt; /// Execution context for an interceptor. @@ -12,16 +12,16 @@ pub struct Context { impl Context { /// Send a request asynchronously, executing the next interceptor in the /// chain, if any. - pub async fn send(&self, request: Request) -> Result, Error> { + pub fn send(&self, request: Request) -> InterceptorFuture { if let Some(interceptor) = self.client.interceptors().get(self.interceptor_offset) { let inner_context = Self { client: self.client.clone(), interceptor_offset: self.interceptor_offset + 1, }; - interceptor.intercept(request, inner_context).await + interceptor.intercept(request, inner_context) } else { - self.client.invoke(request).await + self.client.invoke(request) } } } @@ -33,5 +33,5 @@ impl fmt::Debug for Context { } pub(crate) trait Invoke { - fn invoke(&self, request: Request) -> InterceptorFuture<'_, Error>; + fn invoke(&self, request: Request) -> InterceptorFuture; } diff --git a/src/interceptor/mod.rs b/src/interceptor/mod.rs index c6e08595..2af90d67 100644 --- a/src/interceptor/mod.rs +++ b/src/interceptor/mod.rs @@ -60,15 +60,12 @@ pub trait Interceptor: Send + Sync { /// /// The returned future is allowed to borrow the interceptor for the /// duration of its execution. - fn intercept( - &self, - request: Request, - ctx: Context, - ) -> InterceptorFuture<'_, Self::Err>; + fn intercept(&self, request: Request, ctx: Context) -> InterceptorFuture; } /// The type of future returned by an interceptor. -pub type InterceptorFuture<'a, E> = Pin> + Send + 'a>>; +pub type InterceptorFuture = + Pin> + Send + 'static>>; /// Creates an interceptor from an arbitrary closure or function. pub fn from_fn(f: F) -> InterceptorFn @@ -96,11 +93,7 @@ where { type Err = E; - fn intercept( - &self, - request: Request, - ctx: Context, - ) -> InterceptorFuture<'_, Self::Err> { + fn intercept(&self, request: Request, ctx: Context) -> InterceptorFuture { Box::pin(self.0.call(request, ctx)) } } diff --git a/src/interceptor/obj.rs b/src/interceptor/obj.rs index 84be457c..98a7dcc0 100644 --- a/src/interceptor/obj.rs +++ b/src/interceptor/obj.rs @@ -17,11 +17,7 @@ where impl Interceptor for &InterceptorObj { type Err = Error; - fn intercept( - &self, - request: Request, - cx: Context, - ) -> InterceptorFuture<'_, Self::Err> { + fn intercept(&self, request: Request, cx: Context) -> InterceptorFuture { self.0.dyn_intercept(request, cx) } } @@ -29,19 +25,12 @@ impl Interceptor for &InterceptorObj { /// Object-safe version of the interceptor used for type erasure. Implementation /// detail of [`InterceptorObj`]. trait DynInterceptor: Send + Sync { - fn dyn_intercept( - &self, - request: Request, - cx: Context, - ) -> InterceptorFuture<'_, Error>; + fn dyn_intercept(&self, request: Request, cx: Context) -> InterceptorFuture; } impl DynInterceptor for I { - fn dyn_intercept( - &self, - request: Request, - cx: Context, - ) -> InterceptorFuture<'_, Error> { - Box::pin(async move { self.intercept(request, cx).await.map_err(Error::from_any) }) + fn dyn_intercept(&self, request: Request, cx: Context) -> InterceptorFuture { + let fut = self.intercept(request, cx); + Box::pin(async move { fut.await.map_err(Error::from_any) }) } } diff --git a/src/lib.rs b/src/lib.rs index e9813c90..03e9e838 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -328,7 +328,7 @@ where /// /// The request is executed using a shared [`HttpClient`] instance. See /// [`HttpClient::get_async`] for details. -pub fn get_async(uri: U) -> ResponseFuture<'static> +pub fn get_async(uri: U) -> ResponseFuture where http::Uri: TryFrom, >::Error: Into, @@ -352,7 +352,7 @@ where /// /// The request is executed using a shared [`HttpClient`] instance. See /// [`HttpClient::head_async`] for details. -pub fn head_async(uri: U) -> ResponseFuture<'static> +pub fn head_async(uri: U) -> ResponseFuture where http::Uri: TryFrom, >::Error: Into, @@ -397,7 +397,7 @@ where /// println!("{}", msg); /// # Ok(()) } /// ``` -pub fn post_async(uri: U, body: B) -> ResponseFuture<'static> +pub fn post_async(uri: U, body: B) -> ResponseFuture where http::Uri: TryFrom, >::Error: Into, @@ -424,7 +424,7 @@ where /// /// The request is executed using a shared [`HttpClient`] instance. See /// [`HttpClient::put_async`] for details. -pub fn put_async(uri: U, body: B) -> ResponseFuture<'static> +pub fn put_async(uri: U, body: B) -> ResponseFuture where http::Uri: TryFrom, >::Error: Into, @@ -449,7 +449,7 @@ where /// /// The request is executed using a shared [`HttpClient`] instance. See /// [`HttpClient::delete_async`] for details. -pub fn delete_async(uri: U) -> ResponseFuture<'static> +pub fn delete_async(uri: U) -> ResponseFuture where http::Uri: TryFrom, >::Error: Into, @@ -469,6 +469,6 @@ pub fn send>(request: Request) -> Result, Error> /// /// The request is executed using a shared [`HttpClient`] instance. See /// [`HttpClient::send_async`] for details. -pub fn send_async>(request: Request) -> ResponseFuture<'static> { +pub fn send_async>(request: Request) -> ResponseFuture { HttpClient::shared().send_async(request) } diff --git a/src/redirect.rs b/src/redirect.rs index 261f9994..c12f5548 100644 --- a/src/redirect.rs +++ b/src/redirect.rs @@ -30,7 +30,7 @@ impl Interceptor for RedirectInterceptor { &self, mut request: Request, ctx: Context, - ) -> InterceptorFuture<'_, Self::Err> { + ) -> InterceptorFuture { Box::pin(async move { // Store the effective URI to include in the response. let mut effective_uri = request.uri().clone(); diff --git a/src/request.rs b/src/request.rs index f0c464a0..8bff6aaa 100644 --- a/src/request.rs +++ b/src/request.rs @@ -44,7 +44,7 @@ pub trait RequestExt { /// /// This is a convenience method that is equivalent to /// [`send_async`](crate::send_async). - fn send_async(self) -> ResponseFuture<'static> + fn send_async(self) -> ResponseFuture where T: Into; } @@ -79,7 +79,7 @@ impl RequestExt for Request { crate::send(self) } - fn send_async(self) -> ResponseFuture<'static> + fn send_async(self) -> ResponseFuture where T: Into, { From b3db05af01928515de281ed749dd69f10d3c74f4 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Thu, 11 Aug 2022 19:24:09 +0800 Subject: [PATCH 3/8] Remove not needed clone Signed-off-by: Xuanwo --- src/client.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/client.rs b/src/client.rs index bce23037..c3333886 100644 --- a/src/client.rs +++ b/src/client.rs @@ -21,8 +21,7 @@ use futures_lite::{ }; use http::{ header::{HeaderMap, HeaderName, HeaderValue}, - Request, - Response, + Request, Response, }; use once_cell::sync::Lazy; use std::{ @@ -1012,10 +1011,8 @@ impl HttpClient { uri = ?request.uri(), ); - let client = self.clone(); ResponseFuture::new( - client - .send_async_inner(request.map(Into::into)) + self.send_async_inner(request.map(Into::into)) .instrument(span), ) } From 532237b48db554d824d3ed020782b87fa95feb54 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Thu, 11 Aug 2022 19:30:56 +0800 Subject: [PATCH 4/8] Code cleanup Signed-off-by: Xuanwo --- src/client.rs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/client.rs b/src/client.rs index c3333886..ea6abf1e 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1160,11 +1160,8 @@ impl HttpClient { } } -impl crate::interceptor::Invoke for HttpClient { - fn invoke( - &self, - mut request: Request, - ) -> crate::interceptor::InterceptorFuture { +impl interceptor::Invoke for HttpClient { + fn invoke(&self, mut request: Request) -> InterceptorFuture { let client = self.clone(); Box::pin(async move { From 241b34927216bcdc735b02a72bc93372b514c6c4 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 19 Aug 2022 12:03:38 +0800 Subject: [PATCH 5/8] Remove Invoke trait Signed-off-by: Xuanwo --- src/client.rs | 153 ++++++++++++++++++------------------- src/interceptor/context.rs | 4 - src/interceptor/mod.rs | 2 +- 3 files changed, 77 insertions(+), 82 deletions(-) diff --git a/src/client.rs b/src/client.rs index ea6abf1e..9d9461bb 100644 --- a/src/client.rs +++ b/src/client.rs @@ -21,7 +21,8 @@ use futures_lite::{ }; use http::{ header::{HeaderMap, HeaderName, HeaderValue}, - Request, Response, + Request, + Response, }; use once_cell::sync::Lazy; use std::{ @@ -1037,6 +1038,80 @@ impl HttpClient { ctx.send(request) } + pub(crate) fn invoke(&self, mut request: Request) -> InterceptorFuture { + let client = self.clone(); + + Box::pin(async move { + let is_head_request = request.method() == http::Method::HEAD; + + // Set default user agent if not specified. + request + .headers_mut() + .entry(http::header::USER_AGENT) + .or_insert(USER_AGENT.parse().unwrap()); + + // Check if automatic decompression is enabled; we'll need to know + // this later after the response is sent. + let is_automatic_decompression = request + .extensions() + .get::() + .unwrap() + .automatic_decompression + .unwrap_or(false); + + // Create and configure a curl easy handle to fulfil the request. + let (easy, future) = client + .create_easy_handle(request) + .map_err(Error::from_any)?; + + // Send the request to the agent to be executed. + client.inner.agent.submit_request(easy)?; + + // Await for the response headers. + let response = future.await?; + + // If a Content-Length header is present, include that information in + // the body as well. + let body_len = response.content_length().filter(|_| { + // If automatic decompression is enabled, and will likely be + // selected, then the value of Content-Length does not indicate + // the uncompressed body length and merely the compressed data + // length. If it looks like we are in this scenario then we + // ignore the Content-Length, since it can only cause confusion + // when included with the body. + if is_automatic_decompression { + if let Some(value) = response.headers().get(http::header::CONTENT_ENCODING) { + if value != "identity" { + return false; + } + } + } + + true + }); + + // Convert the reader into an opaque Body. + Ok(response.map(|reader| { + if is_head_request { + AsyncBody::empty() + } else { + let body = ResponseBody { + inner: reader, + // Extend the lifetime of the agent by including a reference + // to its handle in the response body. + _client: client, + }; + + if let Some(len) = body_len { + AsyncBody::from_reader_sized(body, len) + } else { + AsyncBody::from_reader(body) + } + } + })) + }) + } + fn create_easy_handle( &self, mut request: Request, @@ -1160,82 +1235,6 @@ impl HttpClient { } } -impl interceptor::Invoke for HttpClient { - fn invoke(&self, mut request: Request) -> InterceptorFuture { - let client = self.clone(); - - Box::pin(async move { - let is_head_request = request.method() == http::Method::HEAD; - - // Set default user agent if not specified. - request - .headers_mut() - .entry(http::header::USER_AGENT) - .or_insert(USER_AGENT.parse().unwrap()); - - // Check if automatic decompression is enabled; we'll need to know - // this later after the response is sent. - let is_automatic_decompression = request - .extensions() - .get::() - .unwrap() - .automatic_decompression - .unwrap_or(false); - - // Create and configure a curl easy handle to fulfil the request. - let (easy, future) = client - .create_easy_handle(request) - .map_err(Error::from_any)?; - - // Send the request to the agent to be executed. - client.inner.agent.submit_request(easy)?; - - // Await for the response headers. - let response = future.await?; - - // If a Content-Length header is present, include that information in - // the body as well. - let body_len = response.content_length().filter(|_| { - // If automatic decompression is enabled, and will likely be - // selected, then the value of Content-Length does not indicate - // the uncompressed body length and merely the compressed data - // length. If it looks like we are in this scenario then we - // ignore the Content-Length, since it can only cause confusion - // when included with the body. - if is_automatic_decompression { - if let Some(value) = response.headers().get(http::header::CONTENT_ENCODING) { - if value != "identity" { - return false; - } - } - } - - true - }); - - // Convert the reader into an opaque Body. - Ok(response.map(|reader| { - if is_head_request { - AsyncBody::empty() - } else { - let body = ResponseBody { - inner: reader, - // Extend the lifetime of the agent by including a reference - // to its handle in the response body. - _client: client, - }; - - if let Some(len) = body_len { - AsyncBody::from_reader_sized(body, len) - } else { - AsyncBody::from_reader(body) - } - } - })) - }) - } -} - impl fmt::Debug for HttpClient { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("HttpClient").finish() diff --git a/src/interceptor/context.rs b/src/interceptor/context.rs index 68baca95..26cf62f4 100644 --- a/src/interceptor/context.rs +++ b/src/interceptor/context.rs @@ -31,7 +31,3 @@ impl fmt::Debug for Context { f.debug_struct("Context").finish() } } - -pub(crate) trait Invoke { - fn invoke(&self, request: Request) -> InterceptorFuture; -} diff --git a/src/interceptor/mod.rs b/src/interceptor/mod.rs index 2af90d67..5f160036 100644 --- a/src/interceptor/mod.rs +++ b/src/interceptor/mod.rs @@ -25,7 +25,7 @@ mod context; mod obj; pub use self::context::Context; -pub(crate) use self::{context::Invoke, obj::InterceptorObj}; +pub(crate) use self::obj::InterceptorObj; type InterceptorResult = Result, E>; From 60965f4fc80ef025a722763672856717cfd48ffc Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 19 Aug 2022 13:40:53 +0800 Subject: [PATCH 6/8] Refactor default headers Signed-off-by: Xuanwo --- src/default_headers.rs | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/default_headers.rs b/src/default_headers.rs index 56f01f38..521913bf 100644 --- a/src/default_headers.rs +++ b/src/default_headers.rs @@ -27,21 +27,17 @@ impl Interceptor for DefaultHeadersInterceptor { mut request: Request, ctx: Context, ) -> InterceptorFuture { - let headers = self.headers.clone(); - - Box::pin(async move { - // We are checking here if header already contains the key, simply - // ignore it. In case the key wasn't present in parts.headers ensure - // that we have all the headers from default headers. - for name in headers.keys() { - if !request.headers().contains_key(name) { - for v in headers.get_all(name).iter() { - request.headers_mut().append(name, v.clone()); - } + // We are checking here if header already contains the key, simply + // ignore it. In case the key wasn't present in parts.headers ensure + // that we have all the headers from default headers. + for name in self.headers.keys() { + if !request.headers().contains_key(name) { + for v in self.headers.get_all(name).iter() { + request.headers_mut().append(name, v.clone()); } } + } - ctx.send(request).await - }) + ctx.send(request) } } From fdf392158821c6677e29f6835e973ed715932ed4 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 19 Aug 2022 13:46:54 +0800 Subject: [PATCH 7/8] Also refactor cookies Signed-off-by: Xuanwo --- src/cookies/interceptor.rs | 68 ++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 35 deletions(-) diff --git a/src/cookies/interceptor.rs b/src/cookies/interceptor.rs index 945efc77..18b3953f 100644 --- a/src/cookies/interceptor.rs +++ b/src/cookies/interceptor.rs @@ -33,46 +33,44 @@ impl Interceptor for CookieInterceptor { mut request: Request, ctx: Context, ) -> InterceptorFuture { - let cookie_jar = self.cookie_jar.clone(); - - Box::pin(async move { - // Determine the cookie jar to use for this request. If one is - // attached to this specific request, use it, otherwise use the - // default one. - let jar = request - .extensions() - .get::() - .cloned() - .or(cookie_jar); - - if let Some(jar) = jar.as_ref() { - // Get the outgoing cookie header. - let mut cookie_string = request - .headers_mut() - .remove(http::header::COOKIE) - .map(|value| value.as_bytes().to_vec()) - .unwrap_or_default(); - - // Append cookies in the jar to the cookie header value. - for cookie in jar.get_for_uri(request.uri()) { - if !cookie_string.is_empty() { - cookie_string.extend_from_slice(b"; "); - } - - cookie_string.extend_from_slice(cookie.name().as_bytes()); - cookie_string.push(b'='); - cookie_string.extend_from_slice(cookie.value().as_bytes()); + // Determine the cookie jar to use for this request. If one is + // attached to this specific request, use it, otherwise use the + // default one. + let jar = request + .extensions() + .get::() + .cloned() + .or_else(|| self.cookie_jar.clone()); + + if let Some(jar) = jar.as_ref() { + // Get the outgoing cookie header. + let mut cookie_string = request + .headers_mut() + .remove(http::header::COOKIE) + .map(|value| value.as_bytes().to_vec()) + .unwrap_or_default(); + + // Append cookies in the jar to the cookie header value. + for cookie in jar.get_for_uri(request.uri()) { + if !cookie_string.is_empty() { + cookie_string.extend_from_slice(b"; "); } - if !cookie_string.is_empty() { - if let Ok(header_value) = cookie_string.try_into() { - request - .headers_mut() - .insert(http::header::COOKIE, header_value); - } + cookie_string.extend_from_slice(cookie.name().as_bytes()); + cookie_string.push(b'='); + cookie_string.extend_from_slice(cookie.value().as_bytes()); + } + + if !cookie_string.is_empty() { + if let Ok(header_value) = cookie_string.try_into() { + request + .headers_mut() + .insert(http::header::COOKIE, header_value); } } + } + Box::pin(async move { let request_uri = request.uri().clone(); let mut response = ctx.send(request).await?; From 683bd9e6964f267a91758133a981bc201d25f6b7 Mon Sep 17 00:00:00 2001 From: Xuanwo Date: Fri, 19 Aug 2022 13:51:47 +0800 Subject: [PATCH 8/8] Enable elided_lifetimes_in_paths Signed-off-by: Xuanwo --- src/body/mod.rs | 2 +- src/client.rs | 4 ++-- src/handler.rs | 2 +- src/lib.rs | 1 + src/macros.rs | 2 +- 5 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/body/mod.rs b/src/body/mod.rs index cb49a933..35fe1374 100644 --- a/src/body/mod.rs +++ b/src/body/mod.rs @@ -188,7 +188,7 @@ impl AsyncBody { impl AsyncRead for AsyncBody { fn poll_read( mut self: Pin<&mut Self>, - cx: &mut Context, + cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { match &mut self.0 { diff --git a/src/client.rs b/src/client.rs index 9d9461bb..dd42243b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -1261,7 +1261,7 @@ impl ResponseFuture { impl Future for ResponseFuture { type Output = Result, Error>; - fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll { + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { self.0.as_mut().poll(cx) } } @@ -1282,7 +1282,7 @@ struct ResponseBody { impl AsyncRead for ResponseBody { fn poll_read( mut self: Pin<&mut Self>, - cx: &mut Context, + cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { let inner = Pin::new(&mut self.inner); diff --git a/src/handler.rs b/src/handler.rs index f842d56c..27c34a85 100644 --- a/src/handler.rs +++ b/src/handler.rs @@ -680,7 +680,7 @@ pub(crate) struct ResponseBodyReader { impl AsyncRead for ResponseBodyReader { fn poll_read( mut self: Pin<&mut Self>, - cx: &mut Context, + cx: &mut Context<'_>, buf: &mut [u8], ) -> Poll> { let inner = Pin::new(&mut self.inner); diff --git a/src/lib.rs b/src/lib.rs index 03e9e838..4fa60f9e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -236,6 +236,7 @@ missing_docs, unreachable_pub, unused, + elided_lifetimes_in_paths, clippy::all )] // These lints suggest to use features not available in our MSRV. diff --git a/src/macros.rs b/src/macros.rs index bcd629f8..7d39c7b1 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -31,7 +31,7 @@ macro_rules! decl_future { impl<$($($T: Unpin),*)*> ::std::future::Future for $ident<'_, $($($T),*)*> { type Output = $output; - fn poll(mut self: ::std::pin::Pin<&mut Self>, cx: &mut ::std::task::Context) -> ::std::task::Poll { + fn poll(mut self: ::std::pin::Pin<&mut Self>, cx: &mut ::std::task::Context<'_>) -> ::std::task::Poll { self.as_mut().inner.as_mut().poll(cx) } }