fix: drain multipart body before returning early upload errors
When the rate limit, ban, or event-lock check fired before the multipart body was consumed, the HTTP keep-alive connection was left in a bad state. The next request from the Vite proxy then received a connection reset, causing cascading "Netzwerkfehler" / empty-body 500 responses. Fix: call drain_multipart() before every early return in the upload handler so the connection remains clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -24,6 +24,7 @@ pub async fn upload(
|
|||||||
.rate_limiter
|
.rate_limiter
|
||||||
.check(format!("upload:{}", auth.user_id), upload_rate, Duration::from_secs(3600))
|
.check(format!("upload:{}", auth.user_id), upload_rate, Duration::from_secs(3600))
|
||||||
{
|
{
|
||||||
|
drain_multipart(multipart).await;
|
||||||
return Err(AppError::TooManyRequests(
|
return Err(AppError::TooManyRequests(
|
||||||
"Du hast dein Upload-Limit für diese Stunde erreicht.".into(),
|
"Du hast dein Upload-Limit für diese Stunde erreicht.".into(),
|
||||||
));
|
));
|
||||||
@@ -34,6 +35,7 @@ pub async fn upload(
|
|||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Benutzer nicht gefunden.".into()))?;
|
.ok_or_else(|| AppError::NotFound("Benutzer nicht gefunden.".into()))?;
|
||||||
if user.is_banned {
|
if user.is_banned {
|
||||||
|
drain_multipart(multipart).await;
|
||||||
return Err(AppError::Forbidden("Du bist gesperrt.".into()));
|
return Err(AppError::Forbidden("Du bist gesperrt.".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,6 +44,7 @@ pub async fn upload(
|
|||||||
.await?
|
.await?
|
||||||
.ok_or_else(|| AppError::NotFound("Event nicht gefunden.".into()))?;
|
.ok_or_else(|| AppError::NotFound("Event nicht gefunden.".into()))?;
|
||||||
if event.uploads_locked_at.is_some() {
|
if event.uploads_locked_at.is_some() {
|
||||||
|
drain_multipart(multipart).await;
|
||||||
return Err(AppError::Forbidden("Uploads sind gesperrt.".into()));
|
return Err(AppError::Forbidden("Uploads sind gesperrt.".into()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -239,6 +242,15 @@ pub async fn delete_upload(
|
|||||||
Ok(StatusCode::NO_CONTENT)
|
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 {
|
async fn get_config_i64(pool: &sqlx::PgPool, key: &str, default: i64) -> i64 {
|
||||||
let row: Option<(String,)> =
|
let row: Option<(String,)> =
|
||||||
sqlx::query_as("SELECT value FROM config WHERE key = $1")
|
sqlx::query_as("SELECT value FROM config WHERE key = $1")
|
||||||
|
|||||||
Reference in New Issue
Block a user