Skip to content

Commit e6db58e

Browse files
quinnjclaude
andcommitted
fix(server): resolve Content-Length header before rejecting closed bodies
A closed response body whose empty payload is declared only via the Content-Length header (not the content_length field) was rejected as invalid reuse, turning master's 200 into a 500. Fall back to parsing the header when the field is unset. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent be030f3 commit e6db58e

2 files changed

Lines changed: 24 additions & 4 deletions

File tree

src/http_server_streams.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -452,10 +452,14 @@ function _write_response_body_to_stream!(stream::Stream, body)::Nothing
452452
return nothing
453453
end
454454
if body isa AbstractBody
455-
if body_closed(body) &&
456-
_server_stream_allows_body(stream) &&
457-
stream.response.content_length != 0
458-
throw(ArgumentError("body is closed"))
455+
if body_closed(body) && _server_stream_allows_body(stream)
456+
# The handler may declare an empty payload via the Content-Length
457+
# header alone; the content_length field stays -1 until startwrite
458+
# resolves it, so consult the header before rejecting the body.
459+
declared = stream.response.content_length >= 0 ?
460+
stream.response.content_length :
461+
_parse_content_length(stream.response.headers)
462+
declared == 0 || throw(ArgumentError("body is closed"))
459463
end
460464
buf = Vector{UInt8}(undef, 16 * 1024)
461465
try

test/http_handlers_tests.jl

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,22 @@ end
220220
end
221221
end
222222

223+
@testset "HTTP streamhandler ignores closed body with Content-Length: 0 header" begin
224+
body = HT.CallbackBody(_ -> 0, () -> nothing)
225+
HT.body_close!(body)
226+
baked = HT.Response(200, ["Content-Length" => "0"], body)
227+
server = HT.listen!(HT.streamhandler(_ -> baked), "127.0.0.1", 0; listenany = true)
228+
address = HT.server_addr(server)
229+
try
230+
resp = HT.get("http://$(address)/"; status_exception = false, retry = false)
231+
@test resp.status == 200
232+
@test isempty(_read_all_handler_bytes(resp.body))
233+
finally
234+
HT.forceclose(server)
235+
wait(server)
236+
end
237+
end
238+
223239
@testset "HTTP router live request handler server" begin
224240
router = HT.Router()
225241
HT.register!(router, "GET", "/hello/{name}", _router_hello_request)

0 commit comments

Comments
 (0)