Skip to content

Fix three response pipeline bugs#17

Merged
hellerve merged 1 commit into
mainfrom
claude/response-pipeline-fixes
May 23, 2026
Merged

Fix three response pipeline bugs#17
hellerve merged 1 commit into
mainfrom
claude/response-pipeline-fixes

Conversation

@carpentry-agent

Copy link
Copy Markdown
Contributor

Summary

  • Content-Length + chunked encoding (RFC 7230 violation): web-finalize-response unconditionally added a Content-Length header, even when the response already had Transfer-Encoding: chunked. RFC 7230 §3.3.2 says a sender MUST NOT send Content-Length when Transfer-Encoding is present. Fixed by checking for Transfer-Encoding before adding Content-Length.

  • log-after crash from unsafe Long conversion: The logging after-hook used Maybe.unsafe-from on the result of Long.from-string, which panics if log-before wasn't registered (empty string → Nothing → crash). Replaced with a safe match that falls back to 0l.

  • File descriptor leak on fstat failure: In the sendfile path, when IO.Raw.open succeeded but IO.Raw.fstat-size returned a negative value, the fd was stored in ConnState.sf-fds but the error path never closed it. Now closes the fd immediately and returns -1l without storing it.

Test plan

  • Added test: finalize omits Content-Length when Transfer-Encoding is set
  • Added test: finalize adds Content-Length for normal responses
  • Added test: log-after does not crash without _start param
  • All 62 tests pass
  • CI runs on macOS (pre-existing TM.tm_zone const-qualifier issue prevents local carp -x on 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.

@carpentry-reviewer carpentry-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@hellerve hellerve left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rebase.

…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
@carpentry-agent carpentry-agent Bot force-pushed the claude/response-pipeline-fixes branch from 3dae518 to 35d3b4f Compare May 23, 2026 08:43
@carpentry-agent

Copy link
Copy Markdown
Contributor Author

Rebased onto main as requested by @hellerve. Resolved a CHANGELOG.md conflict with the merged PR #18 (WebSocket RFC 6455 compliance) — both the new ### Added entry and this PR's ### Fixed section are preserved in the correct order. No code changes; all three fixes are identical to what was reviewed.

@carpentry-agent carpentry-agent Bot closed this May 23, 2026
@carpentry-agent carpentry-agent Bot force-pushed the claude/response-pipeline-fixes branch from 35d3b4f to 4c83832 Compare May 23, 2026 08:43
@carpentry-agent carpentry-agent Bot reopened this May 23, 2026
@carpentry-agent

Copy link
Copy Markdown
Contributor Author

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.

@carpentry-reviewer carpentry-reviewer Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@hellerve hellerve merged commit 92cf9ea into main May 23, 2026
2 checks passed
@hellerve hellerve deleted the claude/response-pipeline-fixes branch May 23, 2026 10:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant