Skip to content

copy_with_timeout() may exceed its timeout #46

@Shnatsel

Description

@Shnatsel

copy_with_timeout() as implemented in v0.7.2 may exceed its deadline:

http_req/src/request.rs

Lines 50 to 75 in eba0471

///Copies data from `reader` to `writer` until the `deadline` is reached.
///Returns how many bytes has been read.
pub fn copy_with_timeout<R, W>(reader: &mut R, writer: &mut W, deadline: Instant) -> io::Result<u64>
where
R: Read + ?Sized,
W: Write + ?Sized,
{
let mut buf = [0; BUF_SIZE];
let mut copied = 0;
let mut counter = Counter::new(TEST_FREQ);
loop {
let len = match reader.read(&mut buf) {
Ok(0) => return Ok(copied),
Ok(len) => len,
Err(ref e) if e.kind() == ErrorKind::Interrupted => continue,
Err(e) => return Err(e),
};
writer.write_all(&buf[..len])?;
copied += len as u64;
if counter.next().unwrap() && Instant::now() >= deadline {
return Ok(copied);
}
}
}

In here the timeout is only checked after the read operation is issued. However, a call to read() may block according to the documentation. If used on a reader that reads from a network socket, IIRC it will block waiting for data from the remote server until the underlying TCP timeout expires (5 minutes by default).

I believe this is a fundamental limitation of the approach; I am not aware of any ways to fix it without spawning an auxiliary thread or using an async runtime.

If a read timeout is desired, it should be configured on the socket explicitly instead.

If a timeout for the entire request is desired, it can be implemented either by spawning an auxiliary thread that interrupts the main thread (as done in attohttpc), or by setting the read timeout to the minimum of read timeout and the remaining time until the deadline for the entire request (as done in minreq).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions