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
Introduce the ability to return 413: payload too large for requests #3040
Conversation
f2cd61e
to
f6bc181
Compare
Trying it out locally # config.rb
http_content_length_limit 100 bundle exec bin/puma -C config.rb test/rackup/hello.ru Sending a curl -kso /dev/null -XPOST http://localhost:9292 -d "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum" -w "==============\n\n
| dnslookup: %{time_namelookup}\n
| connect: %{time_connect}\n
| appconnect: %{time_appconnect}\n
| pretransfer: %{time_pretransfer}\n
| starttransfer: %{time_starttransfer}\n
| total: %{time_total}\n
| size: %{size_download}\n
| HTTPCode=%{http_code}\n\n"
==============
| dnslookup: 0.003017
| connect: 0.003273
| appconnect: 0.000000
| pretransfer: 0.003293
| starttransfer: 0.003995
| total: 0.004048
| size: 17
| HTTPCode=413 With a large json (40mb) curl -kso /dev/null -XPOST http://localhost:9292/1 -d @40mb.json -w "==============\n\n
| dnslookup: %{time_namelookup}\n
| connect: %{time_connect}\n
| appconnect: %{time_appconnect}\n
| pretransfer: %{time_pretransfer}\n
| starttransfer: %{time_starttransfer}\n
| total: %{time_total}\n
| size: %{size_download}\n
| HTTPCode=%{http_code}\n\n"
==============
| dnslookup: 0.002976
| connect: 0.003273
| appconnect: 0.000000
| pretransfer: 0.003293
| starttransfer: 0.003568
| total: 0.004242
| size: 17
| HTTPCode=413 |
f6bc181
to
42059f8
Compare
Great idea. I want to look at the code in a little more depth later, but I think this is a great feature for inclusion. |
@@ -210,6 +215,17 @@ def try_to_parse_proxy_protocol | |||
end | |||
|
|||
def try_to_finish |
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.
This is a very important path so we'll need to be careful with anything we add here.
4889dad
to
8bb886e
Compare
Sounds good, appreciate the review. Yeah, definitely feels like something very useful. Especially when keeping puma workers snappy and fast by rejecting large requests. I have been using it locally with our @tines monolith and works well (but ofc its local only). I will try to see if I can roll this out on some test stacks to test internally (but in prod) as well. |
When recieving large payload objects, the server can often slowdown or get fully exhausted if bunch of requests with large payload body size come in. When request with large payload come, lot of the time is spent reading it into then, writing it to the IO for rack, before the request is passed to the rails app for further processing. While there are some workarounds around limiting large request sizes, like at nginx layer by setting `client_max_body_size`, which would return a `413` back to the client, today that is not possible with puma. This would be a very nice feature to have, especially when there is no reverse proxy in between client and server. This approach - allows a user to set `http_content_length_limit_exceeded` via a config variable (defaults to `nil`). This value is then compared against `Content-Length` http header before reading the body into buffer. If the user value is higher than the header value, the request body is not loaded and an immediate `413` (`Payload too large`) http response is returned, from `Puma::Request.handle_request`. Without having to buffer in the huge request and return the `413` immediately to the clients that send a `Content-Length` - is a nice feature and helpful protection to have.
http header is present.
Co-authored-by: Nate Berkopec <nate.berkopec@gmail.com>
91a0490
to
81938b3
Compare
81938b3
to
dead2b4
Compare
Thank you for your change @shayonj 🙇 |
Thanks @nateberkopec! Any ideas, when we can expect a release with this change? 🙌 |
Probably in about 4 weeks? When I'm not busy, I try to release on a 4-week cadence. We just did a patch release so your commit was the first commit since that. |
Description
When receiving large payload objects, the server can often slowdown or get fully exhausted if bunch of requests with large payload body size come in. When request with large payload comes in, lot of the time is spent reading the body into buffer, writing it to the IO for rack, before the request is passed to the rails app for further processing.
While there are some workarounds around limiting large request sizes, like at nginx layer by setting
client_max_body_size
, which would return a413
back to the client, today that is not possible with puma.This would be a very nice feature to have, especially when there is no reverse proxy in between client and server.
This approach - allows a user to set
http_content_length_limit
via a config variable (defaults tonil
). This value is then compared againstContent-Length
http header (when present) before reading the body into buffer. If the user value is higher than the header value, the request body is not loaded and an immediate413
(Payload too large
) http response is returned, fromPuma::Request.handle_request
.Without having to buffer in the huge request and return the
413
immediately to the clients that send aContent-Length
- could be a nice feature and helpful protection to have.Additionally, when no
Content-Length
http header is present, it is compared against the size of the body of the request.During this time, the body is already loaded, so there isn't much we can do, but it still respects the limit and
returns the
413
status from puma itself, before passing down the request to the app.Your checklist for this pull request
[ci skip]
to the title of the PR.#issue
" to the PR description or my commit messages.