actix / actix/actix-web

Actix HTTP2 error with long blocking requests and async task

Abierto
#2,728 7 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
Rust
Estrellas
24.8k
Forks
1.9k
Merge medio
23 h 10 min
PR fusionados (30 d)
26

Descripción

Hello, and thank you very much for this crate.

Here is the gist for a reproductible example: [https://gist.github.com/allencamal/cfe62dda9fc8132ba6707d5771cac7f1](https://gist.github.com/allencamal/cfe62dda9fc8132ba6707d5771cac7f1)

## Expected Behavior
It should be possible to curl the `failing` endpoint.

## Current Behavior
When `curl`ing the `failing` endpoint, `curl` returns the following error message:
`curl: (18) transfer closed with 7302400 bytes remaining to read`.

## Possible Solution
Actually, forcing HTTP1.1 via `curl` with the `--http1.1` option works, but I don't think this is a viable option. Disabling TLS forces HTTP1.1, and also works, but that is not something viable.
Also, using `spawn_blocking` for the blocking `sleep` fixes the issue, but that does not make the bug less curious.

## Steps to Reproduce (for bugs)
Gist: [https://gist.github.com/allencamal/cfe62dda9fc8132ba6707d5771cac7f1](https://gist.github.com/allencamal/cfe62dda9fc8132ba6707d5771cac7f1)
1. Create a new cargo project
2. Create a basic TLS server with `actix-web` and `rustls`
3. Create an endpoint that spawns a blocking task, sleeps for 15 seconds with `std::thread::sleep`, and return more than 13 MB of data.
4. Curl this endpoint. `curl --insecure https://localhost:8085/failing > a`

## Context
Here are the two endpoints:
```rust
#[actix_web::get("/working")]
async fn working(req: actix_web::HttpRequest) -> actix_web::Result {
log::info!("{:?}", req.version());
std::thread::sleep(std::time::Duration::from_secs(15));
Ok(actix_web::HttpResponse::Ok().body(DATA.to_vec()))
}

#[actix_web::get("/failing")]
async fn failing(req: actix_web::HttpRequest) -> actix_web::Result {
log::info!("{:?}", req.version());
actix_web::rt::task::spawn_blocking(|| println!("Request will fail")).await;
std::thread::sleep(std::time::Duration::from_secs(15));
Ok(actix_web::HttpResponse::Ok().body(DATA.to_vec()))
}
```

Here is the gist for a reproductible example: [https://gist.github.com/allencamal/cfe62dda9fc8132ba6707d5771cac7f1](https://gist.github.com/allencamal/cfe62dda9fc8132ba6707d5771cac7f1)
I'm creating a very basic HTTP web server, using TLS (so that http2 is available). I've got two endpoints, that basically just sleeps 15 secs (using `std`'s sleep, not `tokio`'s) and returns 20 MB of data. (for less data, e.g. 10 MB, the bug does not appears).

From my experience, sleeping for less than 15 seconds (e.g. 5 seconds) also fails, but anything less than 3 seconds works correctly.

The only difference between the two endpoints is that one calls:
`actix_web::rt::task::spawn_blocking(|| println!("Request will fail")).await;` before sleeping (blocking).

If I curl my two endpoints:

```bash
curl --insecure https://localhost:8085/working > a
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 19.0M 100 19.0M 0 0 1271k 0 0:00:15 0:00:15 --:--:-- 4709k
```

```bash
curl --insecure https://localhost:8085/failing > a
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
63 19.0M 63 12.1M 0 0 815k 0 0:00:23 0:00:15 0:00:08 3109k
curl: (18) transfer closed with 7302400 bytes remaining to read
```

The one that spawn a blocking task fails.

Note that on another project, I have a similar issue, but the error is
`curl (92) HTTP/2 stream 1 was not closed cleanly before end of the underlying stream.`

## Your Environment
`Cargo.toml` included at the beginning of the gist

- Rust Version (I.e, output of `rustc -V`): rustc 1.60.0 (7737e0b5c 2022-04-04)
- Actix Web Version: 4.0.1

Guía de contribución

Abrir la guía de contribución

Evaluación

Este issue todavía no se ha evaluado.

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.