reqwest icon indicating copy to clipboard operation
reqwest copied to clipboard

Response pauses indefinitely after 360MB

Open mstange opened this issue 2 years ago • 5 comments

I'm hitting a problem when downloading this file with reqwest and rusttls: https://chromium-browser-symsrv.commondatastorage.googleapis.com/chrome.dll.pdb/93B17FC546DE07D14C4C44205044422E1/chrome.dll.pdb

The download stops after around 360MB, and the response.chunk() future never completes.

I've put a full reproduction here: https://github.com/mstange/reqwest-with-rusttls-stops-after-360mb

The bug only occurs when using the reqwest feature rusttls-tls. I think this means that it only happens if the request is made using HTTP/2.

The bug also only occurs when downloading the gzip-compressed version of the file.

Steps to reproduce:

  1. Clone the following repo: https://github.com/mstange/reqwest-with-rusttls-stops-after-360mb
  2. Inside the local clone, run cargo run --release
  3. Wait for a 640MB download to complete.

Expected results:

The program should complete and print Done! Downloaded 639588034 bytes in total.

Actual results:

The program stops downloading after around 360MB. The last printed line is usually something like Downloaded 361462551 bytes. And then the response.chunk() future just never completes.

Workarounds

Any of the following changes make the download complete successfully:

  • Change https:// into http:// in the URL.
  • Remove the Accept-Encoding: gzip header.
  • Edit Cargo.toml to use "default-tls" instead of "rustls-tls" (causes HTTP/1.1 to be used)
  • Call .http1_only() on the builder.

The file can be downloaded successfully with curl -o chrome.dll.pdb --compressed "https://chromium-browser-symsrv.commondatastorage.googleapis.com/chrome.dll.pdb/93B17FC546DE07D14C4C44205044422E1/chrome.dll.pdb", which also uses HTTP/2. It decompresses to a 3GB file.

System configuration

I'm hitting this bug on macOS 13.1, with Rust 1.67.0.

Full code

src/main.rs:

#[tokio::main(flavor = "current_thread")]
async fn main() {
    eprintln!("Downloading chrome.dll.pdb...");
    let url = "https://chromium-browser-symsrv.commondatastorage.googleapis.com/chrome.dll.pdb/93B17FC546DE07D14C4C44205044422E1/chrome.dll.pdb";

    let client_builder = reqwest::Client::builder();
    let client = client_builder
        .no_gzip()
        .no_brotli()
        .no_deflate()
        .build()
        .unwrap();
    let builder = client.get(url);
    let builder = builder.header("Accept-Encoding", "gzip");
    let mut response = builder.send().await.unwrap();

    let mut downloaded_len = 0;
    let mut last_reported_len = 0;
    while let Ok(Some(chunk)) = response.chunk().await {
        downloaded_len += chunk.len();
        if downloaded_len >= last_reported_len + 1_000_000 {
            eprintln!("Downloaded {downloaded_len} bytes.");
            last_reported_len = downloaded_len;
        }
    }

    eprintln!("Done! Downloaded {downloaded_len} bytes in total.");
}

Cargo.toml:

[package]
name = "reqwest-with-rusttls-stops-after-360mb"
version = "0.1.0"
edition = "2021"

[dependencies]
reqwest = { version = "0.11.14", default-features = false, features = [
    "stream",
    "rustls-tls",
    # "default-tls",

] }
tokio = { version = "1.25.0", default-features = false, features = [
    "macros",
    "rt"
] }

mstange avatar Feb 24 '23 18:02 mstange

The bug only occurs when using the reqwest feature rusttls-tls. I think this means that it only happens if the request is made using HTTP/2.

I was trying to check whether rusttls or HTTP/2 is the cause, by setting the version to HTTP/1.1. However, with this change, the bug still occurs and I think it's still using HTTP/2 (based on the presence of h2:: functions in the profile):

diff --git a/src/main.rs b/src/main.rs
index f8c93fc..5fe252d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -11,7 +11,9 @@ async fn main() {
         .build()
         .unwrap();
     let builder = client.get(url);
-    let builder = builder.header("Accept-Encoding", "gzip");
+    let builder = builder
+        .header("Accept-Encoding", "gzip")
+        .version(reqwest::Version::HTTP_11);
     let mut response = builder.send().await.unwrap();
 
     let mut downloaded_len = 0;

mstange avatar Feb 26 '23 18:02 mstange

Possibly could be a flow control bug in h2... We just published a new version today with a couple fixes, they did touch some flow control code (though none was to fix a hang)...

seanmonstar avatar Feb 27 '23 19:02 seanmonstar

I can still reproduce this with reqwest 0.11.17

jrmuizel avatar May 02 '23 15:05 jrmuizel

Switching reqwest to use default-tls and native-tls-alpn still reproduces the bug for me. Which suggests that it is indeed an HTTP2 issue and not related to the TLS library.

jrmuizel avatar May 03 '23 18:05 jrmuizel

This issue still occurs on the hyper-v1 branch with h2 0.4.2.

mstange avatar Mar 06 '24 04:03 mstange