Skip to content

Add a Response constructor family (respond/ok/created/… + with-* helpers)#16

Merged
hellerve merged 1 commit into
masterfrom
claude/response-builders
Jul 14, 2026
Merged

Add a Response constructor family (respond/ok/created/… + with-* helpers)#16
hellerve merged 1 commit into
masterfrom
claude/response-builders

Conversation

@carpentry-agent

Copy link
Copy Markdown

Request has a full constructor family (get/post/put/…) but Response had none — callers had to hand-assemble every response with Response.init, and Status.reason (the reason-phrase table) had no consumer at all. This adds the missing builder surface.

Response constructors

  • respond code headers body — the base: HTTP/1.1, message filled from Status.reason code, and a Content-Length header set to the body's byte length.
  • ok / created / not-found / bad-request[headers body] for the common codes.
  • no-content[headers], empty body. Per RFC 7230 §3.3.2 a 204 carries no Content-Length, so this one deliberately omits it (doesn't route through respond).
  • redirect code location headers — sets a Location header, empty body.

Builder helpers (return an updated Response)

  • with-header name value — appends a header value (keeps existing ones).
  • with-content-type ct — sugar for a Content-Type header.
  • with-cookie c — appends a cookie.

Status

Completed the reason table + constants with the common missing codes: 202 206 303 307 308 406 415 504 505. Status.reason is now live (consumed by respond).

Notes

  • Content-Length uses String.length (byte count), so it's correct for multi-byte UTF-8 bodies (there's a test for héllo6).
  • Purely additive — no existing signatures or behavior changed; Response.str/parse are untouched.

Verification

  • Full suite: 158 passed / 1 failed locally. The 1 failure is the pre-existing Form.parse "" empty-body bug (unrelated, untouched — masked on CI by continue-on-error).
  • 16 new tests covering every constructor/helper, the byte-count Content-Length, the 204-omits-Content-Length rule, the new reason entries, and a strparse round-trip.
  • carp-fmt -c clean, angler clean, gendocs builds.

@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

Checked out claude/response-builders at b1c3393:

  • carp -x test/http.carp158 pass / 1 fail. The 1 failure is the pre-existing Form.parse "" empty-body bug (unrelated, untouched, masked on CI by continue-on-error). All 16 new Response tests pass.
  • angler, carp-fmt --check, gendocs — clean. Matches the green CI. (No CHANGELOG in this repo, so nothing to update.)

Findings

Went past the suite and serialized real responses to inspect the actual wire output:

  • ok/created/not-found/bad-requestHTTP/1.1 <code> <reason> with a single Content-Length matching the body's byte length; hélloContent-Length: 6 (bytes, not codepoints). ✓
  • no-content204 No Content with no Content-Length and an empty body (RFC 7230 §3.3.2). ✓
  • redirectLocation header + Content-Length: 0. ✓
  • with-header appends and both values serialize under the same name. ✓
  • The init arg order matches the Response deftype (code message version cookies headers body), and Response.str emits only the map's headers — no auto Content-Length — so respond injecting it yields exactly one, no duplication. The strparse round-trip test confirms the output re-parses.

One minor, non-blocking design note: with-content-type appends via with-header, so applying it twice would emit two Content-Type headers. Typical single use is correct; flagging only in case you'd prefer set-semantics for singleton headers.

Cleanly mirrors the existing Request constructor family, and the docstrings are concise.

Verdict: merge

Purely additive, correct on the wire, well-tested, all gates green.

@hellerve

Copy link
Copy Markdown
Member

needs a rebase.

Mirror Request's builder family on Response: respond (HTTP/1.1, reason phrase
from Status.reason, auto Content-Length) plus ok/created/no-content/not-found/
bad-request/redirect, and with-header/with-content-type/with-cookie. no-content
omits Content-Length per RFC 7230. Complete the Status reason table and
constants (202/206/303/307/308/406/415/504/505). 16 new tests; suite 158/1 (the
1 is the pre-existing Form.parse empty-body bug).
@carpentry-agent
carpentry-agent Bot force-pushed the claude/response-builders branch from b1c3393 to e60623c Compare July 14, 2026 12:40
@carpentry-agent

Copy link
Copy Markdown
Author

Rebased onto master. The only conflict was the shared deftest tail — #17's chunk asserts and this PR's Response asserts both appended there; kept both. Suite green locally (169/0).

@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

Re-reviewed at e60623c — the rebased HEAD. My earlier review was at the pre-rebase b1c3393, now stale, so this is a fresh pass on the rebased branch. It sits cleanly on the current master (merge-base = origin/master HEAD 21751cc), so #17/#18 are underneath it, not duplicated.

  • carp -x test/http.carp169 pass / 0 fail. The pre-existing Form.parse "" failure I flagged last round is gone: #18 (Form empty-body) landed on master, so the suite is now fully green.
  • carp-fmt --check, angler — clean. Matches the green CI on ubuntu + macos. (No CHANGELOG in this repo.)

Prior feedback

The only ask was hellerve's "needs a rebase." Done — and I verified the rebase itself, since the conflict was in the shared deftest tail:

  • Resolved correctly: #17's "chunk (single chunk) round-trips through dechunk" assert is retained, immediately followed by all 16 new Response asserts, with the deftest-closing paren relocated to the final assert. The 169/0 count (nothing skipped or miscounted) confirms the block is well-formed and every assert runs.
  • The http.carp payload (Response constructor family + Status reason table) is byte-identical to what I reviewed at b1c3393 — the rebase changed only the base and merged the two test tails.

Findings

Nothing new. My prior wire-level checks still hold (single correct byte-count Content-Length, héllo→6, 204 omits Content-Length, redirect sets Location, strparse round-trips). One non-blocking note carries over: with-content-type appends via with-header, so applying it twice emits two Content-Type headers — fine for typical single use; flagging only in case you'd prefer set-semantics for singleton headers.

Verdict: merge

Clean rebase onto current master, conflict correctly resolved, additive Response surface verified correct, suite now fully green (169/0), all gates pass.

@hellerve
hellerve merged commit b5cf7e8 into master Jul 14, 2026
2 checks passed
@hellerve
hellerve deleted the claude/response-builders branch July 14, 2026 14:41
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