Repository: seanmonstar/reqwest (language: rust)

Issue:
Requests with timeouts only respect them for the first read
In trying to work around #1366, I found that if you set a request with a high timeout, it's only respected for the first read on the response.  Here's a simple program that creates a client with a 1 second timeout, and a request with a really long timeout. You need a server that will stream back data and wait more than one second between. I used:

`while true; do { echo -e 'HTTP/1.1 200 OK\r\n'; echo "H"; sleep 1000; } | nc -lp 8888; done`

```rust
//! `cargo run --example blocking_timeout --features=blocking`
#![deny(warnings)]

use std::time::Duration;
use std::io::Read;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    env_logger::init();

    let client = reqwest::blocking::Client::builder()
        .connect_timeout(Some(Duration::new(1,0)))
        .timeout(Some(Duration::new(1,0)))
        .build()
        .unwrap();

    println!("GET http://localhost:8888 (should not timeout)");

    let req = client.get("http://localhost:8888").timeout(Duration::new(u32::MAX.into(), 0));
    //let resp = client.execute(req);
    let mut resp = req.send().unwrap();

    loop {
        let mut buf = vec![0];
        println!("will try to read");
        resp.read(&mut buf).unwrap();
        println!("read: {:?}", buf);
    }
}
```

Here's the trace logs from reqwest:
```
RUST_LOG=reqwest=trace cargo run --example blocking_timeout --features blocking
    Finished dev [unoptimized + debuginfo] target(s) in 0.06s
     Running `target/debug/examples/blocking_timeout`
Please run `nc -lp8888` before running this
[2021-11-04T23:27:10Z TRACE reqwest::blocking::wait] (ThreadId(1)) park without timeout
[2021-11-04T23:27:10Z TRACE reqwest::blocking::client] (ThreadId(2)) start runtime::block_on
GET http://localhost:8888 (should not timeout)
[2021-11-04T23:27:10Z TRACE reqwest::blocking::wait] wait at most 4294967295s
[2021-11-04T23:27:10Z TRACE reqwest::blocking::wait] (ThreadId(1)) park timeout 4294967294.9999958s
[2021-11-04T23:27:10Z DEBUG reqwest::connect] starting new connection: http://localhost:8888/
[2021-11-04T23:27:10Z DEBUG reqwest::async_impl::client] response '200 OK' for http://localhost:8888/
will try to read
[2021-11-04T23:27:10Z TRACE reqwest::blocking::wait] wait at most 1s
read: [72]
will try to read
[2021-11-04T23:27:10Z TRACE reqwest::blocking::wait] wait at most 1s
read: [10]
will try to read
[2021-11-04T23:27:10Z TRACE reqwest::blocking::wait] wait at most 1s
[2021-11-04T23:27:10Z TRACE reqwest::blocking::wait] (ThreadId(1)) park timeout 999.992196ms
[2021-11-04T23:27:11Z TRACE reqwest::blocking::wait] wait timeout exceeded
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: Custom { kind: Other, error: reqwest::Error { kind: Decode, source: TimedOut } }', examples/blocking_timeout.rs:27:29
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
[2021-11-04T23:27:11Z TRACE reqwest::blocking::client] closing runtime thread (ThreadId(2))
[2021-11-04T23:27:11Z TRACE reqwest::blocking::client] signaled close for runtime thread (ThreadId(2))
[2021-11-04T23:27:11Z TRACE reqwest::blocking::client] (ThreadId(2)) Receiver is shutdown
[2021-11-04T23:27:11Z TRACE reqwest::blocking::client] (ThreadId(2)) end runtime::block_on
[2021-11-04T23:27:11Z TRACE reqwest::blocking::client] (ThreadId(2)) finished
[2021-11-04T23:27:11Z TRACE reqwest::blocking::client] closed runtime thread (ThreadId(2))
```

You can see that the first wait is long, as specified, but then it switches back to just waiting for 1 second, which is the client timeout.


Discussion / hints:

Fix patch:
diff --git a/src/blocking/client.rs b/src/blocking/client.rs
index d3548e12b..8da4251b3 100644
--- a/src/blocking/client.rs
+++ b/src/blocking/client.rs
@@ -1039,7 +1039,7 @@ impl ClientHandle {
             Ok(Err(err)) => Err(err.with_url(url)),
             Ok(Ok(res)) => Ok(Response::new(
                 res,
-                self.timeout.0,
+                timeout,
                 KeepCoreThreadAlive(Some(self.inner.clone())),
             )),
             Err(wait::Waited::TimedOut(e)) => Err(crate::error::request(e).with_url(url)),


Test patch:
diff --git a/tests/timeouts.rs b/tests/timeouts.rs
index d2c7289c7..eca56a1b8 100644
--- a/tests/timeouts.rs
+++ b/tests/timeouts.rs
@@ -174,6 +174,43 @@ fn timeout_blocking_request() {
     assert_eq!(err.url().map(|u| u.as_str()), Some(url.as_str()));
 }
 
+#[cfg(feature = "blocking")]
+#[test]
+fn blocking_request_timeout_body() {
+    let _ = env_logger::try_init();
+
+    let client = reqwest::blocking::Client::builder()
+        // this should be overridden
+        .connect_timeout(Duration::from_millis(200))
+        // this should be overridden
+        .timeout(Duration::from_millis(200))
+        .build()
+        .unwrap();
+
+    let server = server::http(move |_req| {
+        async {
+            // immediate response, but delayed body
+            let body = hyper::Body::wrap_stream(futures_util::stream::once(async {
+                tokio::time::sleep(Duration::from_secs(1)).await;
+                Ok::<_, std::convert::Infallible>("Hello")
+            }));
+
+            http::Response::new(body)
+        }
+    });
+
+    let url = format!("http://{}/closes", server.addr());
+    let res = client
+        .get(&url)
+        // longer than client timeout
+        .timeout(Duration::from_secs(5))
+        .send()
+        .expect("get response");
+
+    let text = res.text().unwrap();
+    assert_eq!(text, "Hello");
+}
+
 #[cfg(feature = "blocking")]
 #[test]
 fn write_timeout_large_body() {