This feature was broken in #67, which reset timeouts on the stream before passing it to set_stream. As part of this change, refactor the internal storage of timeouts on the Request object to use Option<Duration>. Remove the deadline field on Response. It wasn't used. The deadline field on unit was used instead. Add a unittest.
137 lines
4.7 KiB
Rust
137 lines
4.7 KiB
Rust
use crate::test::testserver::*;
|
|
use std::io::{self, Write};
|
|
use std::net::TcpStream;
|
|
use std::thread;
|
|
use std::time::Duration;
|
|
|
|
use super::super::*;
|
|
|
|
// Send an HTTP response on the TcpStream at a rate of two bytes every 10
|
|
// milliseconds, for a total of 600 bytes.
|
|
fn dribble_body_respond(mut stream: TcpStream, contents: &[u8]) -> io::Result<()> {
|
|
read_headers(&stream);
|
|
let headers = format!(
|
|
"HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n",
|
|
contents.len() * 2
|
|
);
|
|
stream.write_all(headers.as_bytes())?;
|
|
for i in 0..contents.len() {
|
|
stream.write_all(&contents[i..i + 1])?;
|
|
stream.write_all(&[b'\n'; 1])?;
|
|
stream.flush()?;
|
|
thread::sleep(Duration::from_millis(100));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn get_and_expect_timeout(url: String) {
|
|
let agent = Agent::default().build();
|
|
let timeout = Duration::from_millis(500);
|
|
let resp = agent.get(&url).timeout(timeout).call();
|
|
|
|
match resp.into_string() {
|
|
Err(io_error) => match io_error.kind() {
|
|
io::ErrorKind::TimedOut => Ok(()),
|
|
_ => Err(format!("{:?}", io_error)),
|
|
},
|
|
Ok(_) => Err("successful response".to_string()),
|
|
}
|
|
.expect("expected timeout but got something else");
|
|
}
|
|
|
|
#[test]
|
|
fn overall_timeout_during_body() {
|
|
// Start a test server on an available port, that dribbles out a response at 1 write per 10ms.
|
|
let server = TestServer::new(|stream| dribble_body_respond(stream, &[b'a'; 300]));
|
|
let url = format!("http://localhost:{}/", server.port);
|
|
get_and_expect_timeout(url);
|
|
}
|
|
|
|
#[test]
|
|
fn read_timeout_during_body() {
|
|
let server = TestServer::new(|stream| dribble_body_respond(stream, &[b'a'; 300]));
|
|
let url = format!("http://localhost:{}/", server.port);
|
|
let agent = Agent::default().build();
|
|
let resp = agent.get(&url).timeout_read(5).call();
|
|
match resp.into_string() {
|
|
Err(io_error) => match io_error.kind() {
|
|
io::ErrorKind::TimedOut => Ok(()),
|
|
_ => Err(format!("{:?}", io_error)),
|
|
},
|
|
Ok(_) => Err("successful response".to_string()),
|
|
}
|
|
.expect("expected timeout but got something else");
|
|
}
|
|
|
|
// Send HTTP headers on the TcpStream at a rate of one header every 100
|
|
// milliseconds, for a total of 30 headers.
|
|
fn dribble_headers_respond(mut stream: TcpStream) -> io::Result<()> {
|
|
for _ in 0..30 {
|
|
stream.write_all(b"a: b\n")?;
|
|
stream.flush()?;
|
|
thread::sleep(Duration::from_millis(100));
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
#[test]
|
|
fn read_timeout_during_headers() {
|
|
let server = TestServer::new(dribble_headers_respond);
|
|
let url = format!("http://localhost:{}/", server.port);
|
|
let resp = crate::get(&url).timeout_read(10).call();
|
|
assert!(!resp.ok());
|
|
assert_eq!(
|
|
resp.into_string().unwrap(),
|
|
"Network Error: timed out reading response\n"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn overall_timeout_during_headers() {
|
|
// Start a test server on an available port, that dribbles out a response at 1 write per 10ms.
|
|
let server = TestServer::new(dribble_headers_respond);
|
|
let url = format!("http://localhost:{}/", server.port);
|
|
let agent = Agent::default().build();
|
|
let timeout = Duration::from_millis(500);
|
|
let resp = agent.get(&url).timeout(timeout).call();
|
|
assert!(
|
|
matches!(resp.synthetic_error(), Some(Error::Io(_))),
|
|
"expected timeout error, got {:?}",
|
|
resp.synthetic_error()
|
|
);
|
|
assert_eq!(
|
|
resp.synthetic_error().as_ref().unwrap().body_text(),
|
|
"Network Error: timed out reading response"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
#[cfg(feature = "json")]
|
|
fn overall_timeout_reading_json() {
|
|
// Start a test server on an available port, that dribbles out a response at 1 write per 10ms.
|
|
let server = TestServer::new(|stream| {
|
|
dribble_body_respond(
|
|
stream,
|
|
b"[1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
|
|
1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]",
|
|
)
|
|
});
|
|
let url = format!("http://localhost:{}/", server.port);
|
|
|
|
let agent = Agent::default().build();
|
|
let timeout = Duration::from_millis(500);
|
|
let resp = agent.get(&url).timeout(timeout).call();
|
|
|
|
match resp.into_json() {
|
|
Ok(_) => Err("successful response".to_string()),
|
|
Err(e) => match e.kind() {
|
|
io::ErrorKind::TimedOut => Ok(()),
|
|
_ => Err(format!("Unexpected io::ErrorKind: {:?}", e)),
|
|
},
|
|
}
|
|
.expect("expected timeout but got something else");
|
|
}
|