-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Fix RequestProcessor connection reuse with unconsumed requests #7055
Fix RequestProcessor connection reuse with unconsumed requests #7055
Conversation
485598f
to
2f9d571
Compare
I didn't see this PR's diff yet, but I'm now thinking we should change I found this article, which mentions this stackoverflow question. In short, it seems that when you create a client, do a request but you don't read the whole response and close it then keep-alive won't be used. But it still works: internally I guess it creates a new socket, connects and performs a separate request. If you then don't read and close the connection I guess it's closed after some time. So I'm pretty sure there's a connection pool going on. We must do this change. Right now everything's fine if you use the http client in a single fiber, but if you use it in multiple fibers then there's a change that requests are intermixed and this should work by default, it's not good to have to wait for other responses to be read and closed. |
src/http/server/request_processor.cr
Outdated
buffer = uninitialized UInt8[4096] | ||
|
||
4.times do | ||
return true if io.read(buffer.to_slice) < 4096 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
read
isn't guaranteed to read the full slice. You should use read_fully?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this part should be merged anyway. It's better to be strict and don't reuse the connection when there is even only one unconsumed byte.
Now I read the diff. The change seems fine, except that I'd like |
@asterite It took my a while to understand why you're mentioning
That's #6011 We might consider using an auto-consuming IO wrapper for client response. |
@straight-shoota Right. For me the logic should be:
The only problem here is having to eventually close that connection that never consumed the response to avoid leaks. |
This could be implemented in |
2a5edbc
to
5dfadd2
Compare
I added a commit which removes skipping some minor amount of bytes and instead always closes the connection if the request was not entirely consumed. This is more strict, but also more sane and simple to understand. If your handler doesn't consume the request body (if present), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this leaks FDs, it's pretty hard to follow the logic here :(
Yes, the socket is always closed at the end of the loop in the |
I'll also add docs about this to |
I like the behaviour, personally. |
I've added a section about reusing connections to the API docs of See also #7251 for more changes to the existing docs. |
Yes, |
e37f9d2
to
2692d7c
Compare
Rebased and ready to ship. |
Fixes #6924
This removes the
skip_to_end
call fromHTTP::Content#close
, which would involuntary consume a large (or infinite) HTTP body.Instead, the logic is moved to
RequestProcessor
and it closes the connection if the body is too large (in this case>16kb
).I'm not entirely sure if it should skip any body at all automatically. I'm more leaning towards making this the responsibility of the handler. And the rule would just be if the handler doesn't entirely consume the request body, the connection can't be reused. It's probably fine to leave some leeway, though. That's why I included this into the PR. But we can easily remove it and break if the body contains any data at all.
I'll leave that up for discussion.
The handler can also tell the request processor to close the connection by setting
Connection: close
header.