Apply deadline across redirects. (#313)
Previously, each redirect could take timeout time, so a series of slow redirects could run for longer than expected, or indefinitely.
This commit is contained in:
committed by
GitHub
parent
d627ef9704
commit
b246f0a9d2
@@ -1,5 +1,5 @@
|
|||||||
use std::fmt;
|
|
||||||
use std::io::Read;
|
use std::io::Read;
|
||||||
|
use std::{fmt, time};
|
||||||
|
|
||||||
use url::{form_urlencoded, Url};
|
use url::{form_urlencoded, Url};
|
||||||
|
|
||||||
@@ -116,8 +116,23 @@ impl Request {
|
|||||||
for (name, value) in self.query_params.clone() {
|
for (name, value) in self.query_params.clone() {
|
||||||
url.query_pairs_mut().append_pair(&name, &value);
|
url.query_pairs_mut().append_pair(&name, &value);
|
||||||
}
|
}
|
||||||
|
let deadline = match self.agent.config.timeout {
|
||||||
|
None => None,
|
||||||
|
Some(timeout) => {
|
||||||
|
let now = time::Instant::now();
|
||||||
|
Some(now.checked_add(timeout).unwrap())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let reader = payload.into_read();
|
let reader = payload.into_read();
|
||||||
let unit = Unit::new(&self.agent, &self.method, &url, &self.headers, &reader);
|
let unit = Unit::new(
|
||||||
|
&self.agent,
|
||||||
|
&self.method,
|
||||||
|
&url,
|
||||||
|
&self.headers,
|
||||||
|
&reader,
|
||||||
|
deadline,
|
||||||
|
);
|
||||||
let response = unit::connect(unit, true, reader).map_err(|e| e.url(url.clone()))?;
|
let response = unit::connect(unit, true, reader).map_err(|e| e.url(url.clone()))?;
|
||||||
|
|
||||||
if response.status() >= 400 {
|
if response.status() >= 400 {
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
use std::{
|
use std::{
|
||||||
io::{self, Write},
|
io::{self, Write},
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
|
thread,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
use testserver::{self, TestServer};
|
use testserver::{self, TestServer};
|
||||||
|
|
||||||
@@ -158,6 +160,31 @@ fn redirect_308() {
|
|||||||
assert_eq!(resp.get_url(), "test://host/valid_response");
|
assert_eq!(resp.get_url(), "test://host/valid_response");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn redirects_hit_timeout() {
|
||||||
|
// A chain of 2 redirects and an OK, each of which takes 50ms; we set a
|
||||||
|
// timeout of 100ms and should error.
|
||||||
|
test::set_handler("/redirect_sleep1", |_| {
|
||||||
|
thread::sleep(Duration::from_millis(50));
|
||||||
|
test::make_response(302, "Go here", vec!["Location: /redirect_sleep2"], vec![])
|
||||||
|
});
|
||||||
|
test::set_handler("/redirect_sleep2", |_| {
|
||||||
|
thread::sleep(Duration::from_millis(50));
|
||||||
|
test::make_response(302, "Go here", vec!["Location: /ok"], vec![])
|
||||||
|
});
|
||||||
|
test::set_handler("/ok", |_| {
|
||||||
|
thread::sleep(Duration::from_millis(50));
|
||||||
|
test::make_response(200, "Go here", vec![], vec![])
|
||||||
|
});
|
||||||
|
let req = crate::builder().timeout(Duration::from_millis(100)).build();
|
||||||
|
let result = req.get("test://host/redirect_sleep1").call();
|
||||||
|
assert!(
|
||||||
|
matches!(result, Err(Error::Transport(_))),
|
||||||
|
"expected Transport error, got {:?}",
|
||||||
|
result
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn too_many_redirects() {
|
fn too_many_redirects() {
|
||||||
for i in 0..10_000 {
|
for i in 0..10_000 {
|
||||||
|
|||||||
18
src/unit.rs
18
src/unit.rs
@@ -38,6 +38,7 @@ impl Unit {
|
|||||||
url: &Url,
|
url: &Url,
|
||||||
headers: &Vec<Header>,
|
headers: &Vec<Header>,
|
||||||
body: &SizedReader,
|
body: &SizedReader,
|
||||||
|
deadline: Option<time::Instant>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -99,14 +100,6 @@ impl Unit {
|
|||||||
.cloned()
|
.cloned()
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let deadline = match agent.config.timeout {
|
|
||||||
None => None,
|
|
||||||
Some(timeout) => {
|
|
||||||
let now = time::Instant::now();
|
|
||||||
Some(now.checked_add(timeout).unwrap())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Unit {
|
Unit {
|
||||||
agent: agent.clone(),
|
agent: agent.clone(),
|
||||||
method: method.to_string(),
|
method: method.to_string(),
|
||||||
@@ -213,7 +206,14 @@ pub(crate) fn connect(
|
|||||||
history.push(unit.url.to_string());
|
history.push(unit.url.to_string());
|
||||||
body = Payload::Empty.into_read();
|
body = Payload::Empty.into_read();
|
||||||
// recreate the unit to get a new hostname and cookies for the new host.
|
// recreate the unit to get a new hostname and cookies for the new host.
|
||||||
unit = Unit::new(&unit.agent, &new_method, &new_url, &unit.headers, &body);
|
unit = Unit::new(
|
||||||
|
&unit.agent,
|
||||||
|
&new_method,
|
||||||
|
&new_url,
|
||||||
|
&unit.headers,
|
||||||
|
&body,
|
||||||
|
unit.deadline,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
resp.history = history;
|
resp.history = history;
|
||||||
Ok(resp)
|
Ok(resp)
|
||||||
|
|||||||
Reference in New Issue
Block a user