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,12 +1,13 @@
use std::io::{Cursor, Error as IoError, ErrorKind, Read, Result as IoResult};
use std::str::FromStr;
use std::time::Instant;
use chunked_transfer::Decoder as ChunkDecoder;
use crate::error::Error;
use crate::header::Header;
use crate::pool::PoolReturnRead;
use crate::stream::Stream;
use crate::stream::{DeadlineStream, Stream};
use crate::unit::Unit;
#[cfg(feature = "json")]
@@ -46,6 +47,7 @@ pub struct Response {
headers: Vec<Header>,
unit: Option<Unit>,
stream: Option<Stream>,
deadline: Option<Instant>,
}
/// index into status_line where we split: HTTP/1.1 200 OK
@@ -273,7 +275,6 @@ impl Response {
/// ```
pub fn into_reader(self) -> impl Read {
//
let is_http10 = self.http_version().eq_ignore_ascii_case("HTTP/1.0");
let is_close = self
.header("connection")
@@ -306,6 +307,8 @@ impl Response {
let stream = self.stream.expect("No reader in response?!");
let unit = self.unit;
let deadline = unit.as_ref().and_then(|u| u.deadline);
let stream = DeadlineStream::new(stream, deadline);
match (use_chunked, limit_bytes) {
(true, _) => {
@@ -472,6 +475,7 @@ impl Response {
headers,
unit: None,
stream: None,
deadline: None,
})
}
@@ -551,6 +555,9 @@ impl Into<Response> for Error {
/// *Internal API*
pub(crate) fn set_stream(resp: &mut Response, url: String, unit: Option<Unit>, stream: Stream) {
resp.url = Some(url);
if let Some(unit) = &unit {
resp.deadline = unit.deadline;
}
resp.unit = unit;
resp.stream = Some(stream);
}
@@ -586,7 +593,7 @@ struct LimitedRead<R> {
position: usize,
}
impl<R> LimitedRead<R> {
impl<R: Read> LimitedRead<R> {
fn new(reader: R, limit: usize) -> Self {
LimitedRead {
reader,
@@ -617,7 +624,7 @@ impl<R: Read> Read for LimitedRead<R> {
}
}
impl<R> From<LimitedRead<R>> for Stream
impl<R: Read> From<LimitedRead<R>> for Stream
where
Stream: From<R>,
{