rest: pass informational 1xx WriteHeaders through the writer wrappers (#165)#177
Merged
Conversation
…appers (#165) net/http deliberately leaves its wroteHeader latch false for 1xx — the chain's three writer wrappers all disagreed, latching on the FIRST WriteHeader regardless of status class: - statusWriter metered a 103 as the final status - commitWriter sent a 103-then-panic down the re-panic path (connection abort) instead of writing the contract 500 - cacheControlWriter burned the stamp decision on the 103, so the real 200 committed with no Cache-Control All three now treat 100..199 as pass-through via a shared informational() predicate: forward to the delegate, latch nothing — the subsequent final WriteHeader behaves exactly as a first call. One shared table test (informational_internal_test.go) drives the 103 through each wrapper's owning middleware on a real server (wire-faithful 1xx machinery, the forwarded 103 observed via httptrace) — a fourth wrapper joins the table. Latch-era docs (#164 rollback phrasing, the deferred metrics relabel) made precise for the forwarded-1xx case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Post-review fixup. net/http routes 1xx through its non-latching informational path EXCEPT 101 Switching Protocols, where it sets wroteHeader and commits; the predicate now mirrors that carve-out so a post-101 panic re-panics instead of "writing" a contract 500 the stdlib silently drops. 101 row added to the boundary pin (red on the old predicate; dropping the carve-out goes red again). The shared wire table now writes both 100 and 103 so inlined-range drift in any one wrapper fails at wire level, not only in the unit pin. Doc sweep: corrected every "stdlib never latches on 1xx" claim (informational.go, informational_internal_test.go, cachecontrol.go, sentry.go, metrics.go), scoped the latch≡wire phrasing leftovers to the FINAL response (sentry FlushError + tests, cachecontrol rollback premise, commitSnapshot, metrics status()/Help/never-wrote family, gzip Content-Length note), tightened the informational.go preface (Expect:100-continue bypasses the wrappers; legacy statusRecorder latches on 1xx, #176), and noted two pre-existing adjacent holes: statusWriter's flush-blind implied 200 (#174) and gzip's Content-Encoding on interim responses (#175). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Round-2 fixups. TestWriterWrappers_101Latches pins the 101 carve-out at wire level per wrapper (own request each — nothing may follow a 101): commitWriter re-panics a 101-then-panic into an honest connection abort (one sentry event) instead of a clean 101; statusWriter meters the committed status="101", not the unwritten-case 500 relabel; cacheControlWriter's burned commit refuses a post-101 stamp (live-map peek — the stdlib's own Header() snapshot masks that stamp at the wire). Each row kills its wrapper's inlined carve-out-less-range mutant; the commitWriter mutant survived the entire prior suite. Doc sweep: the latching set is "non-1xx, plus 101" — replaced the stale "FINAL (non-1xx)" equation and the two now-false 1xx generalisations (sentry.go rollback note, metrics.go relabel note) with the non-latching-1xx vocabulary and predicate pointers; precision fixes in the satellites. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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.
Closes #165.
net/http deliberately does not treat informational responses as a commit; all three writer wrappers disagreed, latching on the FIRST
WriteHeaderregardless of status class — a 103 would meter as the final status, burn the panic-recovery latch (connection abort instead of the contract 500), and eat the Cache-Control stamp. All three now pass non-latching 1xx through via a sharedinformational()predicate: forward to the delegate, latch nothing; the final WriteHeader behaves as a first call.The carve-out that review caught: the stdlib commits on 101 Switching Protocols (
code != StatusSwitchingProtocolson its informational path) — three review agents independently verified the original[100,199]predicate diverged in the dangerous direction (a 101-then-panic would become a clean 101 to the client, the recovery 500 silently dropped). The predicate now mirrors the stdlib exactly; the old latching path was correct for 101 and stays.httptrace.Got1xxResponse— the recorder has no 1xx machinery); a fourth wrapper joins by adding a rowTwo review rounds; pre-existing adjacent findings routed to #174 (statusWriter flush capture gap, predicate lint), #175 (1xx carries Content-Encoding), #176 (gateway statusRecorder 1xx latch).
🤖 Generated with Claude Code