diff --git a/backend/src/handlers/upload.rs b/backend/src/handlers/upload.rs index eb319f6..0441a95 100644 --- a/backend/src/handlers/upload.rs +++ b/backend/src/handlers/upload.rs @@ -24,6 +24,7 @@ pub async fn upload( .rate_limiter .check(format!("upload:{}", auth.user_id), upload_rate, Duration::from_secs(3600)) { + drain_multipart(multipart).await; return Err(AppError::TooManyRequests( "Du hast dein Upload-Limit für diese Stunde erreicht.".into(), )); @@ -34,6 +35,7 @@ pub async fn upload( .await? .ok_or_else(|| AppError::NotFound("Benutzer nicht gefunden.".into()))?; if user.is_banned { + drain_multipart(multipart).await; return Err(AppError::Forbidden("Du bist gesperrt.".into())); } @@ -42,6 +44,7 @@ pub async fn upload( .await? .ok_or_else(|| AppError::NotFound("Event nicht gefunden.".into()))?; if event.uploads_locked_at.is_some() { + drain_multipart(multipart).await; return Err(AppError::Forbidden("Uploads sind gesperrt.".into())); } @@ -239,6 +242,15 @@ pub async fn delete_upload( Ok(StatusCode::NO_CONTENT) } +/// Drain a multipart body so the HTTP connection stays clean when returning an early error. +/// Without draining, the client may still be sending the body after we've sent our response, +/// which can corrupt the keep-alive connection for subsequent requests. +async fn drain_multipart(mut mp: Multipart) { + while let Ok(Some(mut field)) = mp.next_field().await { + while field.chunk().await.ok().flatten().is_some() {} + } +} + async fn get_config_i64(pool: &sqlx::PgPool, key: &str, default: i64) -> i64 { let row: Option<(String,)> = sqlx::query_as("SELECT value FROM config WHERE key = $1")