Always send lowlevel_error response to client (#2731)#2
Always send lowlevel_error response to client (#2731)#2MitchLewis930 wants to merge 1 commit intopr_052_beforefrom
Conversation
* Always send lowlevel_error response to client * Add spec for lowlever error handler * Make sure we have a clean buffer when starting response * Simplify test * Rename spec * Add method docs * Tweak the test * Return 500 from lowlevel_error_handler in test Co-authored-by: Patrik Ragnarsson <patrik@starkast.net>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.
| uncork_socket io | ||
|
|
||
| body.close | ||
| client.body.close if client.body |
There was a problem hiding this comment.
After-reply callbacks may be invoked twice on error
Medium Severity
When an exception occurs during write_response (e.g., when the app returns invalid headers like nil or an integer key), the ensure block at line 194 runs after_reply.each { |o| o.call }. The exception then propagates to process_client, which calls client_error, which calls write_response again. The second call's ensure block will invoke the same callbacks again because env[RACK_AFTER_REPLY] still contains the same array. This violates the Rack specification that callbacks are invoked once after the response is sent, and can cause issues for non-idempotent callbacks like logging, metrics, or cleanup operations.
Additional Locations (1)
|
|
||
| status, headers, res_body = lowlevel_error(e, env, 500) | ||
| end | ||
| return false if closed_socket?(io) |
There was a problem hiding this comment.
Early return bypasses resource cleanup in ensure block
Medium Severity
The new closed_socket?(io) check at line 113 returns false before entering the begin/ensure block. If the socket closes after the app executes but before write_response starts, resources are not cleaned up: client.body is not closed (file handle leak), res_body.close is not called, and rack.after_reply callbacks are not invoked (Rack spec violation). In the old code, response writing was inside the begin/ensure block of handle_request, so cleanup always ran.


PR_052
Note
Medium Risk
Touches core request/response and error-handling paths; behavior should be more consistent, but mistakes could impact error responses and connection handling under failure conditions.
Overview
Ensures Puma always sends the
lowlevel_errorRack response back to the client for request/app failures, instead of usingclient.write_erroror potentially dropping the response.This refactors response writing by extracting
Request#write_responseand reusing it from bothRequest#handle_requestandServer#client_error, and hardenslowlevel_errorto tolerate handlers returningnilstatus/headers/body. Tests are updated to expect HTTP/1.0 error status lines and add coverage for a customlowlevel_error_handlerresponse on internal failures.Written by Cursor Bugbot for commit f8acac1. This will update automatically on new commits. Configure here.