Don't reuse conns with bytes pending from server (#372)

This makes us less likely to try and reuse a closed connection, which
produces problems in particular for requests that can't be retried.

Fixes #361
Fixes #124
This commit is contained in:
Jacob Hoffman-Andrews
2021-04-18 10:46:20 -07:00
committed by GitHub
parent 5e4b37a393
commit 50d9ccff8c
2 changed files with 56 additions and 3 deletions

View File

@@ -4,6 +4,7 @@ use crate::error::Error;
use crate::testserver::{read_request, TestServer};
use std::io::{self, Read, Write};
use std::net::TcpStream;
use std::thread;
use std::time::Duration;
use super::super::*;
@@ -17,6 +18,18 @@ fn idle_timeout_handler(mut stream: TcpStream) -> io::Result<()> {
Ok(())
}
// Handler that answers with a simple HTTP response, and times
// out idle connections after 2 seconds, sending an HTTP 408 response
fn idle_timeout_handler_408(mut stream: TcpStream) -> io::Result<()> {
read_request(&stream);
stream.write_all(b"HTTP/1.1 200 OK\r\nContent-Length: 8\r\n\r\nresponse")?;
let twosec = Duration::from_secs(2);
stream.set_read_timeout(Some(twosec))?;
thread::sleep(twosec);
stream.write_all(b"HTTP/1.1 408 Request Timeout\r\nContent-Length: 7\r\n\r\ntimeout")?;
Ok(())
}
#[test]
fn connection_reuse() {
let testserver = TestServer::new(idle_timeout_handler);
@@ -44,6 +57,33 @@ fn connection_reuse() {
assert_eq!(resp.status(), 200);
}
#[test]
fn connection_reuse_with_408() {
let testserver = TestServer::new(idle_timeout_handler_408);
let url = format!("http://localhost:{}", testserver.port);
let agent = Agent::new();
let resp = agent.get(&url).call().unwrap();
// use up the connection so it gets returned to the pool
assert_eq!(resp.status(), 200);
resp.into_string().unwrap();
assert!(agent.state.pool.len() > 0);
// wait for the server to close the connection.
std::thread::sleep(Duration::from_secs(3));
// try and make a new request on the pool. this fails
// when we discover that the TLS connection is dead
// first when attempting to read from it.
// Note: This test assumes the second .call() actually
// pulls from the pool. If for some reason the timed-out
// connection wasn't in the pool, we won't be testing what
// we thought we were testing.
let resp = agent.post(&url).send_string("hello".into()).unwrap();
assert_eq!(resp.status(), 200);
}
#[test]
fn custom_resolver() {
use std::io::Read;