Conversation
saghul
commented
Jun 19, 2026
Contributor
- h2: server: decrement rx_content_remain when draining deferred POST body
- h2: capture unknown (custom) headers into the UHO store
- h2: server: complete empty request body signalled by END_STREAM on HEADERS
lws_h2_bind_for_post_before_action() delivers any request-body bytes that were stashed on the stream buflist while the stream was in LRS_DEFERRING_ACTION, decrementing rx_content_length as it goes. But the inline DATA path in lws_read_h1() and the HTTP_BODY_COMPLETION decision both track rx_content_remain (initialized to the full content-length), not rx_content_length. When a request body is split across the DEFERRING_ACTION boundary -- part stashed and dumped here, the rest arriving later on the inline path -- rx_content_remain is never reduced for the stashed part, so it can never reach 0. HTTP_BODY_COMPLETION therefore never fires and the request hangs until it times out. Bodies small enough to be fully stashed complete via the explicit callback at the end of this function, which is why only larger/split POST bodies were affected. Keep rx_content_remain in step with rx_content_length as the stashed body is delivered.
The lws_hdr_custom_*() accessors read unknown headers out of the ah's UHO linked list, but that list was only ever populated by the h1 text header parser; for h2 the accessors early-returned on mux_substream and custom headers were invisible (commit da8995b disabled the speculative h1 path for substreams). The h1 path can't be reused as-is for h2: the header name arrives through hpack one character at a time (fed to lws_parse purely for known-header recognition) and the value never passes through the h1 unknown-value collector at all, so neither the name nor the value were being stored. Build the UHO entry directly in the hpack decoder instead, where both the name and value bytes are available: - reserve the UHO entry header before the name (speculatively, per the h1 approach), - collect the name as it decodes, finalizing it (with the trailing ':' the accessors expect) and linking it into unk_ll when lws does not recognize the header, or dropping it if it does, - collect the value bytes that the IGNORE-entry path otherwise discards, - record the value length when the value completes. To keep a single writer of ah->pos during substream name parsing, the h1 text parser no longer also lays the name bytes down for mux substreams. Relax the mux_substream guard on the three lws_hdr_custom_*() accessors so the now-populated list is readable for h2.
…ADERS A client sends "no request body" over HTTP/2 by setting END_STREAM on the HEADERS frame and omitting Content-Length (e.g. an empty POST). lws_http_action() re-derives the body expectation from the Content-Length header and the method: for a body-bearing method with no Content-Length it defaults rx_content_length to 100MB and waits for DATA frames that, after END_STREAM, will never arrive -- so the request stalls until it times out. (An explicit "Content-Length: 0" works, because that takes the explicit-zero path.) For a mux (h2) substream whose END_STREAM already arrived with the HEADERS and that carried no Content-Length, treat the body as an explicit zero-length body, exactly as "Content-Length: 0" would. h3 already does the equivalent in ops-h3.c. Fixes empty-body POST/PUT/PATCH over h2 hanging server-side (reproduces with `curl --http2 -X POST` against any lws h2 server).
6199df5 to
bf64032
Compare
Member
|
Yeah these are great, thanks, they're pushed on main |
|
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.


