Flush the request body guard cross-process-safe - #2
Merged
Conversation
webmachine_decision_core:finish_response/2's new maybe_flush_req_body
step (added in 1.12) checks mochiweb_request_recv in the process
dictionary to decide whether the body still needs draining. That flag
is set by webmachine_request:call({req_body, ...}) inside
wrq:req_body/1, but applications that dispatch body reads to a
separate worker process end up with the flag on the worker's pdict
while the mochiweb loop process's pdict stays empty.
Consult ReqState#wm_reqstate.bodyfetch as a fallback. bodyfetch is
set to `standard` or `stream` in the same call that would set the
pdict flag, and unlike the pdict it travels with the ReqState value
across process boundaries. Applications that thread the updated
ReqState back to the mochiweb loop process (via the returned
Request's wm_state field) will hit the fallback and short-circuit
the drain.
Keep the existing pdict guard as the fast path for the common
single-process case, and keep FLUSH_TIMEOUT=5000 as the safety
net for edge cases where neither signal was set.
Also keep the 1.12.1-fe base fix: guard the `undefined` branch on
get(mochiweb_request_recv) =:= true and return `true` immediately
in that case (eager reads mark the request as recv'd; there's
nothing left on the socket to drain), and thread ?FLUSH_TIMEOUT
through a new recv_stream_body/3 variant that the drain fallback
uses so a misbehaving peer cannot wedge the pipeline. Legitimate
streaming callers keep ?IDLE_TIMEOUT = infinity via the 2-arg
wrapper.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Merge the
flush-req-body-guardbranch (already tagged as1.12.1-feand consumed downstream) intomainso the fix is on the default branch. Upstream tracking PR: webmachine/webmachine#349 (open).What it does
webmachine_decision_core:finish_response/2'smaybe_flush_req_bodystep (added in 1.12) checksmochiweb_request_recvin the process dictionary to decide whether the body still needs draining. That flag is set bywebmachine_request:call({req_body, ...})insidewrq:req_body/1, but applications that dispatch body reads to a separate worker process end up with the flag on the worker's pdict while the mochiweb loop process's pdict stays empty. The drain then runs and hits theFLUSH_TIMEOUT(5000 ms).Fix: consult
ReqState#wm_reqstate.bodyfetchas a fallback.bodyfetchis set tostandardorstreamin the same call that would set the pdict flag, and unlike the pdict it travels with the ReqState value across process boundaries. Applications that thread the updated ReqState back to the mochiweb loop process (via the returned Request'swm_statefield) hit the fallback and short-circuit the drain.The existing pdict guard remains as the fast path for the common single-process case.
FLUSH_TIMEOUT= 5000 ms stays as the safety net for edge cases where neither signal was set.Also carried on this branch:
undefinedbranch onget(mochiweb_request_recv) =:= true— eager reads mark the request as recv'd; there's nothing left on the socket to drain.?FLUSH_TIMEOUTthrough a newrecv_stream_body/3variant that the drain fallback uses, so a misbehaving peer cannot wedge the pipeline. Legitimate streaming callers keep?IDLE_TIMEOUT = infinityvia the 2-arg wrapper.Files
src/webmachine_request.erl— 93 insertions, 42 deletions.Status
fe.3.2692-erl25-rhel(tag1.12.1-fe), running on all 8 hosts since 2026-07-20. POST parity confirmed against pre-1.12 baseline.webmachine/webmachine:main.