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::{Result as IoResult, Write};
use std::sync::{Arc, Mutex};
use std::time;
use qstring::QString;
use url::Url;
@@ -38,6 +39,7 @@ pub(crate) struct Unit {
pub timeout_connect: u64,
pub timeout_read: u64,
pub timeout_write: u64,
pub deadline: Option<time::Instant>,
pub method: String,
pub proxy: Option<Proxy>,
#[cfg(feature = "tls")]
@@ -89,6 +91,14 @@ impl Unit {
.cloned()
.collect();
let deadline = match req.timeout {
None => None,
Some(timeout) => {
let now = time::Instant::now();
Some(now.checked_add(timeout).unwrap())
}
};
Unit {
agent: Arc::clone(&req.agent),
url: url.clone(),
@@ -98,6 +108,7 @@ impl Unit {
timeout_connect: req.timeout_connect,
timeout_read: req.timeout_read,
timeout_write: req.timeout_write,
deadline,
method: req.method.clone(),
proxy: req.proxy.clone(),
#[cfg(feature = "tls")]
@@ -154,6 +165,7 @@ pub(crate) fn connect(
let body_bytes_sent = body::send_body(body, unit.is_chunked, &mut stream)?;
// start reading the response to process cookies and redirects.
let mut stream = stream::DeadlineStream::new(stream, unit.deadline);
let mut resp = Response::from_read(&mut stream);
if let Some(err) = resp.synthetic_error() {
@@ -208,8 +220,8 @@ pub(crate) fn connect(
}
// since it is not a redirect, or we're not following redirects,
// give away the incoming stream to the response object
crate::response::set_stream(&mut resp, unit.url.to_string(), Some(unit), stream);
// give away the incoming stream to the response object.
crate::response::set_stream(&mut resp, unit.url.to_string(), Some(unit), stream.into());
// release the response
Ok(resp)