This is an automated email from the ASF dual-hosted git repository.

mneumann pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/arrow-rs-object-store.git


The following commit(s) were added to refs/heads/main by this push:
     new 5f36e0e  Expose `read_timeout` from `reqwest` (#681)
5f36e0e is described below

commit 5f36e0e6744dbdd199b66b69827ff1a6d43bfd76
Author: Mak Nazečić-Andrlon <[email protected]>
AuthorDate: Tue Apr 7 19:54:22 2026 +1000

    Expose `read_timeout` from `reqwest` (#681)
---
 src/client/mod.rs | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 59 insertions(+)

diff --git a/src/client/mod.rs b/src/client/mod.rs
index d559d3c..5b9f1f4 100644
--- a/src/client/mod.rs
+++ b/src/client/mod.rs
@@ -185,6 +185,15 @@ pub enum ClientConfigKey {
     /// Supported keys:
     /// - `randomize_addresses`
     RandomizeAddresses,
+    /// Read timeout
+    ///
+    /// The timeout applies to each read operation, and resets after a
+    /// successful read. This is useful for detecting stalled connections
+    /// when the size of the response is not known beforehand.
+    ///
+    /// Supported keys:
+    /// - `read_timeout`
+    ReadTimeout,
     /// Request timeout
     ///
     /// The timeout is applied from when the request starts connecting until 
the
@@ -219,6 +228,7 @@ impl AsRef<str> for ClientConfigKey {
             Self::ProxyCaCertificate => "proxy_ca_certificate",
             Self::ProxyExcludes => "proxy_excludes",
             Self::RandomizeAddresses => "randomize_addresses",
+            Self::ReadTimeout => "read_timeout",
             Self::Timeout => "timeout",
             Self::UserAgent => "user_agent",
         }
@@ -246,6 +256,7 @@ impl FromStr for ClientConfigKey {
             "proxy_ca_certificate" => Ok(Self::ProxyCaCertificate),
             "proxy_excludes" => Ok(Self::ProxyExcludes),
             "randomize_addresses" => Ok(Self::RandomizeAddresses),
+            "read_timeout" => Ok(Self::ReadTimeout),
             "timeout" => Ok(Self::Timeout),
             "user_agent" => Ok(Self::UserAgent),
             _ => Err(super::Error::UnknownConfigurationKey {
@@ -323,6 +334,7 @@ pub struct ClientOptions {
     allow_insecure: ConfigValue<bool>,
     timeout: Option<ConfigValue<Duration>>,
     connect_timeout: Option<ConfigValue<Duration>>,
+    read_timeout: Option<ConfigValue<Duration>>,
     pool_idle_timeout: Option<ConfigValue<Duration>>,
     pool_max_idle_per_host: Option<ConfigValue<usize>>,
     http2_keep_alive_interval: Option<ConfigValue<Duration>>,
@@ -357,6 +369,7 @@ impl Default for ClientOptions {
             allow_insecure: Default::default(),
             timeout: Some(Duration::from_secs(30).into()),
             connect_timeout: Some(Duration::from_secs(5).into()),
+            read_timeout: None,
             pool_idle_timeout: None,
             pool_max_idle_per_host: None,
             http2_keep_alive_interval: None,
@@ -387,6 +400,9 @@ impl ClientOptions {
             ClientConfigKey::ConnectTimeout => {
                 self.connect_timeout = 
Some(ConfigValue::Deferred(value.into()))
             }
+            ClientConfigKey::ReadTimeout => {
+                self.read_timeout = Some(ConfigValue::Deferred(value.into()))
+            }
             ClientConfigKey::DefaultContentType => self.default_content_type = 
Some(value.into()),
             ClientConfigKey::Http1Only => self.http1_only.parse(value),
             ClientConfigKey::Http2Only => self.http2_only.parse(value),
@@ -428,6 +444,7 @@ impl ClientOptions {
             ClientConfigKey::AllowHttp => Some(self.allow_http.to_string()),
             ClientConfigKey::AllowInvalidCertificates => 
Some(self.allow_insecure.to_string()),
             ClientConfigKey::ConnectTimeout => 
self.connect_timeout.as_ref().map(fmt_duration),
+            ClientConfigKey::ReadTimeout => 
self.read_timeout.as_ref().map(fmt_duration),
             ClientConfigKey::DefaultContentType => 
self.default_content_type.clone(),
             ClientConfigKey::Http1Only => Some(self.http1_only.to_string()),
             ClientConfigKey::Http2KeepAliveInterval => {
@@ -652,6 +669,36 @@ impl ClientOptions {
         self
     }
 
+    /// Set a read timeout
+    ///
+    /// The timeout applies to each read operation, and resets after a
+    /// successful read. This is useful for detecting stalled connections
+    /// when the size of the response is not known beforehand.
+    ///
+    /// Timeout errors are retried, subject to the [`RetryConfig`]
+    ///
+    /// Default is disabled (no read timeout)
+    ///
+    /// # See Also
+    /// * [`Self::with_read_timeout_disabled`] to disable the read timeout
+    /// * [`Self::with_timeout`] to set a timeout for the overall request
+    /// * [`Self::with_connect_timeout`] to set a timeout for the connect phase
+    ///
+    /// [`RetryConfig`]: crate::RetryConfig
+    pub fn with_read_timeout(mut self, timeout: Duration) -> Self {
+        self.read_timeout = Some(ConfigValue::Parsed(timeout));
+        self
+    }
+
+    /// Disables the read timeout
+    ///
+    /// # See Also
+    /// * [`Self::with_read_timeout`]
+    pub fn with_read_timeout_disabled(mut self) -> Self {
+        self.read_timeout = None;
+        self
+    }
+
     /// Set the pool max idle timeout
     ///
     /// This is the length of time an idle connection will be kept alive
@@ -787,6 +834,10 @@ impl ClientOptions {
             builder = builder.connect_timeout(timeout.get()?)
         }
 
+        if let Some(timeout) = &self.read_timeout {
+            builder = builder.read_timeout(timeout.get()?)
+        }
+
         if let Some(timeout) = &self.pool_idle_timeout {
             builder = builder.pool_idle_timeout(timeout.get()?)
         }
@@ -1019,6 +1070,7 @@ mod tests {
         let pool_idle_timeout = "93 seconds".to_string();
         let pool_max_idle_per_host = "94".to_string();
         let proxy_url = "https://fake_proxy_url".to_string();
+        let read_timeout = "45 seconds".to_string();
         let timeout = "95 seconds".to_string();
         let user_agent = "object_store:fake_user_agent".to_string();
 
@@ -1045,6 +1097,7 @@ mod tests {
             ("pool_idle_timeout", pool_idle_timeout.clone()),
             ("pool_max_idle_per_host", pool_max_idle_per_host.clone()),
             ("proxy_url", proxy_url.clone()),
+            ("read_timeout", read_timeout.clone()),
             ("timeout", timeout.clone()),
             ("user_agent", user_agent.clone()),
         ]);
@@ -1134,6 +1187,12 @@ mod tests {
                 .unwrap(),
             proxy_url
         );
+        assert_eq!(
+            builder
+                .get_config_value(&ClientConfigKey::ReadTimeout)
+                .unwrap(),
+            read_timeout
+        );
         assert_eq!(
             builder.get_config_value(&ClientConfigKey::Timeout).unwrap(),
             timeout

Reply via email to