actix-web icon indicating copy to clipboard operation
actix-web copied to clipboard

Next request timeouts after error in File upload

Open Dummerle opened this issue 1 year ago • 3 comments

Current Behavior

When uploading an image or some other kind of file, but a custom extractor returns an error, the next request runs into a timeout.

Expected Behavior

Get no timeout

Possible Solution

No idea

Steps to Reproduce (for bugs)

I have this small example

use std::future::{Ready, ready};
use actix_web::{App, FromRequest, get, HttpRequest, HttpResponse, HttpServer, post, Responder};
use actix_web::error::{ErrorBadRequest};
use actix_web::web::Payload;

struct SomeFailingExtractor;
impl FromRequest for SomeFailingExtractor {
    type Error = actix_web::Error;
    type Future = Ready<Result<Self, Self::Error>>;

    fn from_request(_: &HttpRequest, _: &mut actix_web::dev::Payload) -> Self::Future {
        return ready(Err(ErrorBadRequest("Bad request")));
    }
}

#[post("/image")]
async fn upload_image(
    _: SomeFailingExtractor,
    _payload: Payload
) -> impl Responder {
    return HttpResponse::Ok().body("Uploaded");
}

#[get("/other")]
async fn other_endpoint() -> impl Responder {
    println!("Other endpoint called");
    return HttpResponse::Ok().body("Hello there");
}

#[actix_web::main]
async fn main() {
    HttpServer::new(move || {
        App::new()
            .service(upload_image)
            .service(other_endpoint)
    })
        .bind(("0.0.0.0", 8080)).expect("Error")
        .run()
        .await.expect("Error")
}

The following python script runs into the timeout. It works, if you don't use a session. The second request on /other works. The bug also occurs on a simple website

import requests

session = requests.session()
file = open("/path/to/image.png", "rb").read()

resp1 = session.post(
    "http://localhorst:8080/image",
    data=file,
    headers={
        # also happens with multipart/form-data,  image/png
        "content-type": "application/octet-stream",
    }
)

print(resp1.text)

try:
    resp2 = session.get("http://localhorst:8080/other", timeout=30)
    print(resp2.text)
except requests.exceptions.ReadTimeout:
    print("Timeout")
    resp2 = session.get("http://localhorst:8080/other", timeout=3)
    print(resp2.text) 

Context

I wanted to add an endpoint, where you can upload an image.

Your Environment

  • Rust Version (I.e, output of rustc -V): 1.74, but also doesn't work with 1.69
  • Actix Web Version: 4.4.0
  • OS: Arch Linux

Dummerle avatar Nov 19 '23 14:11 Dummerle

A temporary workaround is to disable keep_alive.

Dummerle avatar Nov 20 '23 18:11 Dummerle

Just bumped the same issue. One more note. If I try to upload 2.4MB image file everything FromRequest works well. If i try to upload small size image (in my case less than 100kb) it will stack on next request

bublikOff avatar Apr 04 '24 22:04 bublikOff

related to https://github.com/actix/actix-web/issues/3220 pr: https://github.com/actix/actix-web/pull/3221

jonatansalemes avatar Apr 17 '24 01:04 jonatansalemes