http: frame the response body by status and request method - #32
Open
yosuke-wolfssl wants to merge 1 commit into
Open
yosuke-wolfssl wants to merge 1 commit into
yosuke-wolfssl wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
🔵 Needs a closer look
Use exact, case-sensitive HEAD comparisons in both blocking and non-blocking response handling.
Pull request overview
Updates HTTP response framing to handle request methods, status codes, interim responses, and empty bodies correctly.
Changes:
- Suppresses bodies for
HEAD, 204, and 304 responses. - Bounds interim 1xx handling and rejects 101 responses.
- Handles empty bodies without allocation.
- Expands transport tests and documentation.
File summaries
| File | Summary |
|---|---|
wolfcert/http.h |
Documents HEAD support and nullable empty bodies. |
tests/unit/test_transport.c |
Adds framing and interim-response coverage. |
src/http.c |
Implements status- and method-aware response framing; exact HEAD comparison remains required in both paths. |
docs/ARCHITECTURE.md |
Documents bodyless response behavior. |
Review details
Suppressed comments (2)
src/http.c:1298
- HTTP method names are case-sensitive, but
ci_cmpclassifieshead,Head, etc. asHEAD. A compliant server may treat such a token as a different method and send a body; this reader would skip it, and on a keep-alive session those bytes would be parsed as the next response. Use an exact comparison forHEADhere (and in the non-blocking path below).
int head_request = (ci_cmp(method, "HEAD") == 0);
src/http.c:2030
- This duplicates the same protocol issue in the non-blocking path: HTTP method names are case-sensitive, so treating mixed/lower-case
headasHEADcan leave a real response body insm_rxand corrupt the next keep-alive response. Use an exact comparison forHEAD.
s->sm_head_request = (ci_cmp(req->method, "HEAD") == 0);
- Files reviewed: 4/4 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
wolfSSL-Fenrir-bot
requested changes
Sep 17, 2026
wolfSSL-Fenrir-bot
left a comment
There was a problem hiding this comment.
Fenrir Automated Review — PR #32
Scan targets checked: wolfcert-src, wolfcert-bugs
Findings: 1
1 finding(s) posted as inline comments (see file-level comments below)
This review was generated automatically by Fenrir. Reported findings require changes before merge.
- http_read_response() takes the request method, matched exactly, and skips read_body() for a HEAD reply and for a 204 or 304. It also drops interim 1xx header blocks until a final status arrives, bounded by the new WOLFCERT_HTTP_MAX_INTERIM, and rejects 101; its frees move to one block at the bottom. status_is_interim() and response_has_no_body() hold both conditions. - inspect_headers() mirrors it: 101 and interim statuses return early, and a bodyless response sets sm_content_length to 0 so SM_READ_HEAD enters SM_READ_BODY_CL. WolfCertHttpSession gains sm_head_request and sm_interim. - read_body() leaves *out NULL and *out_len 0 for an empty body instead of allocating. - http.h documents HEAD and the NULL body. - test_transport.c's Peer gains open_rc and stall, its fetch helpers take a method and a chunk size, and drive_nb() steps one request. - Cases cover an empty body framed by Content-Length and by close, 204, 304, HEAD, a lowercase "head" that keeps its body, a HEAD reply advertising more than max_response_bytes, an interim 1xx whole and split, the interim cap at its ceiling and past it, and 101. The non-blocking path repeats those it shares and adds a byte-at-a-time feed across suspends, an interim block's own header fields, and a session reused after a 204. Issue: F-8020
yosuke-wolfssl
force-pushed
the
fix/f_8020
branch
from
September 17, 2026 04:02
b32c2b5 to
26fde23
Compare
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.
Problem
read_body()chose the response framing fromTransfer-EncodingandContent-Lengthalone, falling through to a read-until-close loop when neither was present. The status line was parsed but never consulted, and the request method was never in scope.RFC 9112 section 6.3 makes a response to
HEAD, and any 1xx, 204 or 304, end at the header block regardless of those fields. On a keep-alive connection the peer correctly holds the socket open, so the blocking client stalled inconn_read()until timeout and the non-blocking session returnedWOLFCERT_ERR_WANT_READindefinitely.This is reachable in deployment. EST
/csrattrsreturns 204 No Content (RFC 7030 section 4.5.2) andwolfcert_client_fetch_meta()already handles it. The in-tree test server happens to sendContent-Length: 0, which is legal but not required — so every in-tree test passed while any third-party EST server omitting it hangs the client.Two adjacent defects in the same path: an interim 1xx was returned as the final response, leaving the real one unread on the connection; and an empty body went through
WOLFCERT_XMALLOC(0), which may return NULL under a static-memory pool and fail a valid response withWOLFCERT_ERR_MEMORY.Closes f-8020.
Fix (
src/http.c)HEADreply, 204, 304GETwould have returned, so it is not applied as a size bound.WOLFCERT_HTTP_MAX_INTERIM(8).WOLFCERT_ERR_PROTOCOL— wolfCert never sendsUpgrade.body = NULL,body_len = 0, no allocation.http_read_response()takes the request method, loops past interim blocks, and skipsread_body()for a bodyless response. Its four scattered free-and-return exits become one cleanup block.inspect_headers()mirrors it, settingsm_content_length = 0soSM_READ_HEADenters the existingSM_READ_BODY_CLcase — no new state-machine state, and the surplus still lands inresidualfor the next request on the session.strcmp, per review: RFC 9110 section 9.1 makes the method token case-sensitive, so a lowercaseheadis a different method whose response carries a body.Tests (
tests/unit/test_transport.c)The scripted peer gains
open_rc(stays connected instead of closing, so a mis-framed body shows up as a deterministic error rather than a hang) andstall(aWANT_READbetween reads). 23 cases cover both framing paths for HEAD/204/304, a lowercasehead, both sides of the interim cap, 101, an advertised length abovemax_response_bytes, interim feeds split byte-by-byte across 118 suspends, and a session reused after a bodyless response.Verification
ctestand autotoolsmake check.HEADcomparison, theresidualstash, the bodyless size bound) are each caught only by their own path's test.Not in this PR
The interim cap and drop mechanics are duplicated between the blocking and non-blocking readers. Those are separate implementations throughout, so deduplicating properly means unifying them; tracked as follow-up work rather than widened here.