Fix CodeUnits write ambiguity + 500 fallback after deferred startwrite (#1302, #1303)#1305
Merged
Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1305 +/- ##
==========================================
+ Coverage 87.02% 87.03% +0.01%
==========================================
Files 28 28
Lines 11017 11026 +9
==========================================
+ Hits 9587 9597 +10
+ Misses 1430 1429 -1 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
#1302, #1303) #1302: Base defines write(::IO, ::Base.CodeUnits), which is ambiguous with the Stream/_RequestDeadlineWriteIO StridedVector{UInt8} write methods (CodeUnits <: DenseVector{UInt8}). Add disambiguating CodeUnits methods so write(stream, codeunits(s)) dispatches to the bytes path on both the client and server stream sides. #1303: h1 FIXED mode defers the response head at startwrite while flipping response_started, so a handler throw after startwrite skipped the 500 fallback (gated on !response_started) and silently dropped the connection; clients saw "unexpected EOF". Track head_committed -- set only where head bytes actually reach the transport (h1 and h2 paths) -- and answer with a raw 500 when the handler fails with nothing committed. Once the head is on the wire (e.g. chunked), behavior is unchanged: no second head is emitted. Tests: codeunits round-trip through a real handler; deferred-startwrite throw now yields 500; chunked control asserts no spurious 500 after a committed head. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The raw HTTP/1 probe helper fails with "timed out waiting for first response byte" when the known Windows IOCP wake strand (JuliaServices/Reseau.jl#107) leaves the probe's parked reader unwoken — the same canary test has now struck four unrelated PRs. Until the Reseau fix lands, re-dial fresh and retry (3 attempts, Windows only; other platforms stay strict) with a loud warning so the strand remains visible in logs. Safe for all current callers: none assert on server-side invocation counts. The first-byte timeout is now a typed exception so the retry can unwrap it through the nested watchdog wrappers. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
9d6cc2f to
a718a79
Compare
…te IO
Covers the Stream{true} and _RequestDeadlineWriteIO CodeUnits
disambiguation methods added for #1302.
Co-Authored-By: Claude Fable 5 <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.
Fixes #1302, fixes #1303 (both reported by @PingoLee — thanks for the unusually precise root-cause analysis on both).
#1302 —
write(::Stream, ::Base.CodeUnits)ambiguityBase.CodeUnits <: DenseVector{UInt8}, so Base'swrite(::IO, ::Base.CodeUnits)was ambiguous with theStridedVector{UInt8}methods onStream(and on the internal_RequestDeadlineWriteIO). Added disambiguatingCodeUnits{UInt8}methods delegating to the existing byte paths. Verified through a real handler (write(stream, codeunits("ok"))→ client receivesok) and a clientHTTP.openbody write.#1303 — handler throw after
startwritesilently dropped the connectionExactly as the issue diagnosed: h1 FIXED mode defers the response head at
startwritewhile flippingresponse_started, and the error path gated its 500 fallback on!response_started— so a post-startwritethrow tore down the connection with zero bytes on the wire (ParseError: unexpected EOFclient-side).Fix: a new
head_committedflag onStream, set only at the two places head bytes actually reach the transport (h1 buffered write, h2 HEADERS frame). The h1 error path now answers with a raw500(_try_write_server_error!, same as sibling branches) when the handler fails withresponse_startedset but nothing committed. Once the head is on the wire (e.g. chunked mode writes it atstartwrite), behavior is unchanged — the control test asserts no second head/spurious 500 is emitted.startwrite, FIXED (deferred head)ParseError: unexpected EOF500 Internal Server Errorstartwrite, chunked (head committed)Tests
3 new testsets in
http_server_http1_tests.jl(codeunits round-trip, deferred-startwrite → 500, committed-head control). Locally: h1 189/189, client 472/472, core 162/162.🤖 Generated with Claude Code