-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Weird behavior when returning response without consuming multipart body [REPRO] #2850
Comments
If it helps, I can post the code I wrote to work around the issue when I ran into it in #2445. It's a trivial function, feel free to use it as public domain / with no restrictions. In my case I only cared about handling file uploads, I wasn't worried about enforcing size limits on other kinds of requests. So I ended up not using the 'built-in' axum middleware for enforcing size limits at all, and wrote the following upload handler:
The basic idea is to go ahead and 'consume' all of the chunks / fields, even though we don't actually do anything with them once the error condition has been triggered. Then at the very end, send an error response if appropriate. As discussed in #2445 this appeases (some?) browsers, and gives the right behavior. Other clients like cURL don't mind receiving a response before they are done sending their request, and so either approach works. |
@CmdrMoozy thank you, that's a good workaround in "userspace". impl<B> Body for Limited<B>
where
B: Body,
B::Error: Into<Box<dyn Error + Send + Sync>>,
{
type Data = B::Data;
type Error = Box<dyn Error + Send + Sync>;
fn poll_frame(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Result<Frame<Self::Data>, Self::Error>>> {
let mut this = self.project();
let res = match this.inner.as_mut().poll_frame(cx) {
Poll::Pending => return Poll::Pending,
Poll::Ready(None) => None,
Poll::Ready(Some(Ok(frame))) => {
if let Some(data) = frame.data_ref() {
if data.remaining() > *this.remaining {
*this.remaining = 0;
// >>>>>>>>> CHANGES START HERE <<<<<<<<<
// NOTE: returning Pending directly here does not work, since we need to error on Ready(None)
// println!("PATCH: polling remaining frames!");
match this.inner.as_mut().poll_frame(cx) {
Poll::Pending => {
print!("pending ");
return Poll::Pending
},
Poll::Ready(None) => {
println!("ready none ");
return Poll::Ready(Some(Err(LengthLimitError.into())))
}
// continue polling until None reached
// NOTE: this codepath was never triggered with my test file
Poll::Ready(Some(Ok(frame))) => {
print!("frame ");
return Poll::Pending;
},
// NOTE: this codepath was never triggered with my test file
Poll::Ready(Some(Err(err))) => {
let err = err.into();
println!("some err: {err}");
return Poll::Ready(Some(Err(err)))
},
}
} else {
*this.remaining -= data.remaining();
Some(Ok(frame))
}
} else {
Some(Ok(frame))
}
}
Poll::Ready(Some(Err(err))) => Some(Err(err.into())),
};
Poll::Ready(res)
} It just polls again the moment it finds the body to be larger, then repeats the cycle. It ends up a bunch of times in |
A bit more explanation and instructions on how to see what I mean directly can be found in this reproduction:
https://github.com/fs-99/axum-multipart-bug-repro
The problem I'm facing appears when verifying size limits on very large file uploads with axum.
The body/request size limit holds, but if the body is not buffered completely beforehand, the frontend does not receive a "413 Payload Too Large" error, just a "network error".
This might be because closing a stream does not sit well with browsers, in which case this is a browser issue.
Postman does not seem to have this problem, it rightfully receives a 413 from the server.
As Discussed in #2445
Originally posted by momozahara December 25, 2023
(there was another one)
As Discussed in #1650
The text was updated successfully, but these errors were encountered: