Skip to content

middleware::Compress seems to be buffering content indefinitely #3410

Open
@lovasoa

Description

@lovasoa

Initially reported sqlpage/SQLPage#435

When making streaming responses and activating middleware::Compress, the compression seems to buffer content indefinitely, without ever flushing the streaming data when it's small enough.

Here is an example demonstrating the problem

use actix_web::{
    get, middleware,
    web::{self, Bytes},
    App, HttpResponse, HttpServer,
};
use futures::stream::{self, StreamExt};
use std::time::Duration;
use tokio::time::sleep;

#[get("/")]
async fn index() -> HttpResponse {
    let stream = stream::once(async {
        let initial_content = r#"
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Streaming HTML Example</title>
</head>
<body>
    <div>This content appears immediately</div>
"#;
        Ok::<_, std::io::Error>(Bytes::from(initial_content))
    })
    .chain(stream::once(async {
        sleep(Duration::from_secs(5)).await;
        let delayed_content = r#"
    <div>This content appears after 5 seconds</div>
</body>
</html>
"#;
        Ok::<_, std::io::Error>(Bytes::from(delayed_content))
    }));

    HttpResponse::Ok()
        .content_type("text/html; charset=utf-8")
        .streaming(stream)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(
                web::scope("/compressed")
                    .wrap(middleware::Compress::default())
                    .service(index),
            )
            .service(index)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

Expected Behavior

  • When visiting I should see This content appears immediately immediateley, and then after 5 seconds This content appears after 5 seconds
  • When visiting http://localhost:8080/compressed/ the behavior should be the same, or as close as possible.

Current Behavior

  • When visiting http://localhost:8080/compressed/ the page stays blank for five seconds, then displays This content appears immediately and This content appears after 5 seconds at the same time.

Possible Solution

The compression middleware should flush the writer object after a few milliseconds without new input data, to avoid indefinitely retaining data in memory on the server when it could actually already be rendered on the client.

It's possible to retain data in memory for a few milliseconds to ensure good compression ratios, but actix shouldn't keep streaming data in memory for multiple seconds.

Context

We are trying to integrate easy native loading spinners without javascript in SQLPage

Your Environment

  • Actix Web Version: 4.8.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-webproject: actix-webC-bugCategory: bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions