Add overall timeout for requests. (#67)

This deprecates timeout_read() and timeout_write() in favor of
timeout(). The new timeout method on Request takes a Duration instead
of a number of milliseconds, and is measured against overall request
time, not per-read time.

Once a request is started, the timeout is turned into a deadline
specific to that call. The deadline is used in conjunction with the
new DeadlineStream class, which sets a timeout on each read according
to the remaining time for the request. Once the request is done,
the DeadlineStream is unwrapped via .into::<Stream>() to become
an undecorated Stream again for return to the pool. Timeouts on the
stream are unset at this point.

Still to be done:

Add a setting on Agent for default timeout.
Change header-writing code to apply overall deadline rather than
per-write timeout.
Fixes #28.
This commit is contained in:
Jacob Hoffman-Andrews
2020-06-21 00:47:35 -07:00
committed by GitHub
parent d6b712f56f
commit 57be414d97
7 changed files with 279 additions and 42 deletions

View File

@@ -1,5 +1,6 @@
use std::io::Read;
use std::sync::{Arc, Mutex};
use std::time;
use lazy_static::lazy_static;
use qstring::QString;
@@ -46,6 +47,7 @@ pub struct Request {
pub(crate) timeout_connect: u64,
pub(crate) timeout_read: u64,
pub(crate) timeout_write: u64,
pub(crate) timeout: Option<time::Duration>,
pub(crate) redirects: u32,
pub(crate) proxy: Option<crate::proxy::Proxy>,
#[cfg(feature = "tls")]
@@ -336,6 +338,8 @@ impl Request {
}
/// Timeout for the socket connection to be successful.
/// If both this and .timeout() are both set, .timeout_connect()
/// takes precedence.
///
/// The default is `0`, which means a request can block forever.
///
@@ -351,6 +355,8 @@ impl Request {
}
/// Timeout for the individual reads of the socket.
/// If both this and .timeout() are both set, .timeout()
/// takes precedence.
///
/// The default is `0`, which means it can block forever.
///
@@ -360,12 +366,15 @@ impl Request {
/// .call();
/// println!("{:?}", r);
/// ```
#[deprecated(note = "Please use the timeout() function instead")]
pub fn timeout_read(&mut self, millis: u64) -> &mut Request {
self.timeout_read = millis;
self
}
/// Timeout for the individual writes to the socket.
/// If both this and .timeout() are both set, .timeout()
/// takes precedence.
///
/// The default is `0`, which means it can block forever.
///
@@ -375,11 +384,32 @@ impl Request {
/// .call();
/// println!("{:?}", r);
/// ```
#[deprecated(note = "Please use the timeout() function instead")]
pub fn timeout_write(&mut self, millis: u64) -> &mut Request {
self.timeout_write = millis;
self
}
/// Timeout for the overall request, including DNS resolution, connection
/// time, redirects, and reading the response body. Slow DNS resolution
/// may cause a request to exceed the timeout, because the DNS request
/// cannot be interrupted with the available APIs.
///
/// This takes precedence over .timeout_read() and .timeout_write(), but
/// not .timeout_connect().
///
/// ```
/// // wait max 1 second for whole request to complete.
/// let r = ureq::get("/my_page")
/// .timeout(std::time::Duration::from_secs(1))
/// .call();
/// println!("{:?}", r);
/// ```
pub fn timeout(&mut self, timeout: time::Duration) -> &mut Request {
self.timeout = Some(timeout);
self
}
/// Basic auth.
///
/// These are the same