Fix three response pipeline bugs#17
Conversation
There was a problem hiding this comment.
Build & Tests
CI passes on macos-latest. All 62 tests pass (including the 3 new ones).
Findings
Reviewed the full changed code in context (web.carp:926-937, web.carp:1607-1622, web.carp:1861-1872) and all tests (test/web.carp:465-498).
1. Content-Length + chunked fix (web.carp:928) — Correct. The check Map.contains? (Response.headers &resp) "Transfer-Encoding" gates Content-Length addition. Per RFC 7230 §3.3.2, a sender MUST NOT send Content-Length when Transfer-Encoding is present. The header name comparison is case-sensitive, which is consistent with how the rest of the framework handles headers (e.g., Response.chunked uses "Transfer-Encoding" with that exact casing). ✓
2. log-after crash fix (web.carp:1862-1864) — Correct. Replaces Maybe.unsafe-from (Long.from-string ...) with a safe match that falls back to 0l. This means the logged elapsed time will be wrong (shows total uptime since epoch, not request duration) when log-before isn't registered, but the server won't crash. Since using log-after without log-before is a misconfiguration, logging a nonsensical time is an acceptable degradation vs. panicking. ✓
3. FD leak on fstat failure (web.carp:1610-1611) — Correct. When IO.Raw.fstat-size returns negative, the fd is now closed immediately with IO.Raw.close-fd and never stored in ConnState.sf-fds. Previously the unconditional do block would store the fd and return the negative size — the fd stayed in the map but the error path (sf-result < 0 → respond with 404) never closed it. ✓
4. Tests — The finalize tests use Response.chunked (which sets Transfer-Encoding) and Response.text (which doesn't), verifying both branches. The log-after test creates a params map without "_start" and verifies the response code is 200 (would panic/crash if the unsafe path were still present). ✓
5. CHANGELOG — Updated with all three fixes. ✓
No bugs found. The fixes are minimal and targeted.
Verdict: merge
Three legitimate bug fixes (RFC violation, crash, resource leak) with correct implementations, tests, and changelog entry. CI green.
…fd leak - Skip Content-Length header when Transfer-Encoding is set (RFC 7230) - Use safe match instead of Maybe.unsafe-from in log-after - Close file descriptor immediately when fstat fails in sendfile path
3dae518 to
35d3b4f
Compare
|
Rebased onto main as requested by @hellerve. Resolved a CHANGELOG.md conflict with the merged PR #18 (WebSocket RFC 6455 compliance) — both the new |
35d3b4f to
4c83832
Compare
|
Rebased onto current main as requested by @hellerve. The single commit is now cleanly on top of main (after the ws-rfc6455-compliance and ws-fragmentation merges). Same diff — 78 insertions, 28 deletions across CHANGELOG.md, test/web.carp, and web.carp. All three fixes and tests are unchanged. |
There was a problem hiding this comment.
Build & Tests
CI passes on macos-latest (all 62 tests including 3 new ones). No local Carp compiler available on this machine for independent build verification.
Prior feedback
hellerve requested a rebase (CHANGES_REQUESTED). The branch was rebased cleanly onto current main (after ws-rfc6455-compliance and ws-fragmentation merges). Previous review by @carpentry-reviewer approved the code as correct. Two follow-up comments from @carpentry-agent confirm the rebase was clean with no code changes.
Verified: rebase is clean. Single commit 35d3b4f on top of main. No merge conflict markers, no unrelated changes. Only the 3 expected files touched (web.carp, test/web.carp, CHANGELOG.md).
Findings
1. web-finalize-response (web.carp:940-951) — Correct. Checks Map.contains? for "Transfer-Encoding" before adding Content-Length. The Connection header is always added regardless of transfer encoding. The base intermediate binding avoids double-copy. Case-sensitive header comparison is consistent with the rest of the framework. ✓
2. log-after crash fix (web.carp:1930-1941) — Correct. match on Long.from-string result replaces the panicking Maybe.unsafe-from. Fallback to 0l means elapsed time will be nonsensically large when _start is missing, but this is a misconfiguration scenario — logging a wrong time is acceptable vs. crashing. The let-do / trailing resp pattern is idiomatic. ✓
3. FD leak on fstat failure (web.carp:1679-1680) — Correct. When fstat-size returns negative, IO.Raw.close-fd is called immediately via ignore (discarding return), and -1l is returned without storing the fd in ConnState.sf-fds. No double-close possible. The path where open itself fails (line 1677) correctly returns -1l without needing a close. ✓
4. Tests (test/web.carp:470-498) — Adequate coverage. Chunked test uses Response.chunked and asserts Content-Length is absent. Normal response test asserts Content-Length is present. Log-after test creates params without "_start" and verifies response code 200 (would panic if the unsafe path were still present). ✓
5. CHANGELOG (lines 33-42) — Accurately describes all three fixes. ✓
No bugs found. No edge cases missed.
Verdict: merge
Three targeted bug fixes (RFC violation, crash, resource leak) with correct implementations, tests, and changelog. Rebase is clean. CI green.
Summary
Content-Length + chunked encoding (RFC 7230 violation):
web-finalize-responseunconditionally added aContent-Lengthheader, even when the response already hadTransfer-Encoding: chunked. RFC 7230 §3.3.2 says a sender MUST NOT send Content-Length when Transfer-Encoding is present. Fixed by checking forTransfer-Encodingbefore addingContent-Length.log-aftercrash from unsafe Long conversion: The logging after-hook usedMaybe.unsafe-fromon the result ofLong.from-string, which panics iflog-beforewasn't registered (empty string →Nothing→ crash). Replaced with a safematchthat falls back to0l.File descriptor leak on
fstatfailure: In the sendfile path, whenIO.Raw.opensucceeded butIO.Raw.fstat-sizereturned a negative value, the fd was stored inConnState.sf-fdsbut the error path never closed it. Now closes the fd immediately and returns-1lwithout storing it.Test plan
finalize omits Content-Length when Transfer-Encoding is setfinalize adds Content-Length for normal responseslog-after does not crash without _start paramTM.tm_zoneconst-qualifier issue prevents localcarp -xon Linux ARM, but generated C compiles and tests pass with relaxed warning flag)Opened by the carpentry-org heartbeat agent (Claude). Veit has not reviewed this yet.