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
Failure to flush Payload when it is dropped before handler consumes all bytes #2764
Comments
Looking closer, I'm seeing actix-web/actix-http/src/h1/dispatcher.rs Lines 703 to 709 in 6a5b370
which talks about polling the request payload. But nothing in this loop actually fills the read buffer. So, if there are unread bytes in the request according to the I'm not sure of the best way to fill the read buffer in this drain loop, but that is looking like the root cause. |
Ok, I have something that seems to solve the problem, but it's not quite usable as a PR yet -- In the bit of code above, I add a check for empty loop {
if this.read_buf.is_empty() {
log::trace!("read buffer is empty; reading available bytes");
self.read_available(cx)?; // fails borrow checker
}
// ...
} I ended up copying the entire contents of Anyone know how I can call this function from inside |
In cases where a handler exits early without reading a supplied
Payload
, there is some new code in actix-web (see PR #2624) that consumes the rest of the bytes in the request.It seems not to work with large request bodies (approaching 512k).
Expected Behavior
If a
Payload
is dropped before all bytes are consumed, the rest of the client request should still be consumed.Current Behavior
If a
Payload
is dropped before all bytes are consumed, the rest of the request is consumed if the request is small, but larger requests stop reading and drop the connection with a message"handler did not read whole payload and dispatcher could not drain read buf; return 500 and close connection"
.Possible Solution
Haven't quite found the root cause yet, but it appears that the
read_buf
in theDispatcher
is empty. WhenPayloadDecoder::decode()
is called withread_buf
assrc
, it reports zero bytes available, butself.kind
contains a nonzeroKind::Length
of remaining bytes. If they were both correctly zero, it would returnPayloadItem::Eof
, and the flush would terminate correctly. If they were both correctly nonzero, it would successfully returnPayloadItem::Chunk(buf)
, which would continue the flush loop until exhausted.I'm not sure which one is wrong here, but it only seems to happen if there are a certain number of reads required to flush the request.
Steps to Reproduce (for bugs)
Minimal server:
Client-side - I was able to duplicate this by sending a 511k file (succeeded), and a 512k file (failed), but managed to narrow down to exact number of bytes by using
curl
's-C
to skip initial bytes in the file. Byte ranges may vary across OSs, not sure.Context
I'd like to be able to return from a handler without having to worry about whether I've finished reading the
Payload
completely. For example, streaming a file to disk, then having a write fail, it's much easier to handle with a simple?
and a handler returning aResult
, than having to wrap the whole thing in a handler that cleans up thePayload
before returning an error.Your Environment
The text was updated successfully, but these errors were encountered: