From 20dd247923639c6e3d28897ad447cd7f69f2e837 Mon Sep 17 00:00:00 2001 From: Yusuke Tanaka Date: Sun, 18 Aug 2024 23:22:24 +0900 Subject: [PATCH] feat!(client): expose whether a connection is reused from the pool This introduces a new type `ErrorConnectInfo` that contains information about connection pool as well as transport-related data. This new type can now be obtained from the `Error::connect_info()` method, which means that this is a breaking change as the return type from the method has changed. That said, all information that was available on the previous return type is available on the new type through various getter methods. --- src/client/legacy/client.rs | 52 ++++++++++++++++++++++++++------ src/client/legacy/connect/mod.rs | 3 ++ 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/src/client/legacy/client.rs b/src/client/legacy/client.rs index 8562584..501f28b 100644 --- a/src/client/legacy/client.rs +++ b/src/client/legacy/client.rs @@ -57,7 +57,7 @@ pub struct Error { kind: ErrorKind, source: Option>, #[cfg(any(feature = "http1", feature = "http2"))] - connect_info: Option, + connect_info: Option, } #[derive(Debug)] @@ -71,6 +71,34 @@ enum ErrorKind { SendRequest, } +/// Extra information about a failed connection. +pub struct ErroredConnectInfo { + conn_info: Connected, + is_reused: bool, +} + +impl ErroredConnectInfo { + /// Determines if the connected transport is to an HTTP proxy. + pub fn is_proxied(&self) -> bool { + self.conn_info.is_proxied() + } + + /// Copies the extra connection information into an `Extensions` map. + pub fn get_extras(&self, extensions: &mut http::Extensions) { + self.conn_info.get_extras(extensions); + } + + /// Determines if the connected transport negotiated HTTP/2 as its next protocol. + pub fn is_negotiated_h2(&self) -> bool { + self.conn_info.is_negotiated_h2() + } + + /// Determines if the connection is a reused one from the connection pool. + pub fn is_reused(&self) -> bool { + self.is_reused + } +} + macro_rules! e { ($kind:ident) => { Error { @@ -282,7 +310,7 @@ where if req.version() == Version::HTTP_2 { warn!("Connection is HTTP/1, but request requires HTTP/2"); return Err(TrySendError::Nope( - e!(UserUnsupportedVersion).with_connect_info(pooled.conn_info.clone()), + e!(UserUnsupportedVersion).with_connect_info(&pooled), )); } @@ -317,14 +345,12 @@ where Err(mut err) => { return if let Some(req) = err.take_message() { Err(TrySendError::Retryable { - error: e!(Canceled, err.into_error()) - .with_connect_info(pooled.conn_info.clone()), + error: e!(Canceled, err.into_error()).with_connect_info(&pooled), req, }) } else { Err(TrySendError::Nope( - e!(SendRequest, err.into_error()) - .with_connect_info(pooled.conn_info.clone()), + e!(SendRequest, err.into_error()).with_connect_info(&pooled), )) } } @@ -464,7 +490,7 @@ where Ok(checked_out) } // Connect won, checkout can just be dropped. - Either::Right((Ok(connected), _checkout)) => Ok(connected), + Either::Right((Ok(connected), _checkout)) => Ok(connected) // Either checkout or connect could get canceled: // // 1. Connect is canceled if this is HTTP/2 and there is @@ -1619,14 +1645,20 @@ impl Error { /// Returns the info of the client connection on which this error occurred. #[cfg(any(feature = "http1", feature = "http2"))] - pub fn connect_info(&self) -> Option<&Connected> { + pub fn connect_info(&self) -> Option<&ErroredConnectInfo> { self.connect_info.as_ref() } #[cfg(any(feature = "http1", feature = "http2"))] - fn with_connect_info(self, connect_info: Connected) -> Self { + fn with_connect_info(self, pooled: &pool::Pooled, PoolKey>) -> Self + where + B: Send + 'static, + { Self { - connect_info: Some(connect_info), + connect_info: Some(ErroredConnectInfo { + conn_info: pooled.conn_info.clone(), + is_reused: pooled.is_reused(), + }), ..self } } diff --git a/src/client/legacy/connect/mod.rs b/src/client/legacy/connect/mod.rs index e3369b5..9435334 100644 --- a/src/client/legacy/connect/mod.rs +++ b/src/client/legacy/connect/mod.rs @@ -99,6 +99,7 @@ pub trait Connection { pub struct Connected { pub(super) alpn: Alpn, pub(super) is_proxied: bool, + pub(super) is_reused: bool, pub(super) extra: Option, pub(super) poisoned: PoisonPill, } @@ -149,6 +150,7 @@ impl Connected { Connected { alpn: Alpn::None, is_proxied: false, + is_reused: false, extra: None, poisoned: PoisonPill::healthy(), } @@ -226,6 +228,7 @@ impl Connected { Connected { alpn: self.alpn, is_proxied: self.is_proxied, + is_reused: self.is_reused, extra: self.extra.clone(), poisoned: self.poisoned.clone(), }