Ensure connection is closed when raw SSE stream raises#248
Ensure connection is closed when raw SSE stream raises#248rsamoilov merged 2 commits intorage-rb:mainfrom
Conversation
Add an `ensure` block to `start_raw_stream` so the connection is always closed if the user's Proc raises an exception without calling `close`. This matches the cleanup behavior already present in `start_formatted_stream`, which has `ensure connection.close`. Without this fix, a Proc that raises before closing leaves the Iodine connection open until socket timeout.
rsamoilov
left a comment
There was a problem hiding this comment.
Hey @jsxs0,
Thank you for the contribution! Unfortunately, this commit is a breaking change as it makes a reasonable but incorrect assumption about the synchronous nature of raw SSE streams.
It's a completely valid approach to run the stream on a separate fiber or thread. In such case, the proc that's passed to render sse: finishes executing almost immediately but long before the background fiber/thread has a chance to do the work and write to the connection.
If you check your changes against datastar-example, you'll see how the integration breaks - the Datastar SDK executes streams in separate fibers allowing the SSE stream proc to finish early. Adding the ensure block means the connection is closed before anything has been written to it.
However, if we focus on closing the connection when the SSE stream raises, you idea makes sense and actually fixes the existing issue. We just need to ensure the the connection is closed only on exception.
Address review feedback: the ensure block breaks async patterns where the proc spawns background fibers and returns early (e.g. Datastar SDK). Changed to rescue so the connection is only closed when the proc raises, not when it completes normally. Updated tests: - Proc raises → connection closed - Proc returns without closing (async pattern) → connection stays open - Proc closes itself → no interference
|
Hi, @rsamoilov. Updated in 819f747, switched from Updated tests:
All 12 CI checks pass. Will also verify against |
|
Verified against datastar-example, pointed its The Datastar SDK streams all 13 SSE events correctly ( The connection stays open while background fibers write, and closes normally after completion. |
Summary
Add a
rescueblock toSSE::Application#start_raw_stream, so the Iodine connection is closed when the user's Proc raises an exception.Problem
start_formatted_stream(Enumerator path) hasensure connection.close; the connection always cleans up, even on exception.start_raw_stream(Proc path) does not — if the Proc raises before callingconnection.close, the connection leaks until Iodine's socket timeout.Why not
ensure?Raw SSE streams can be async; the proc may spawn background fibers and return early while the connection stays alive (e.g. Datastar SDK). Using
ensurewould close the connection before background fibers can write to it.Fix
Closes only on exception. The
connection.open?guard avoids double-closing when the Proc already calledclosebefore raising.Test plan
Added
spec/sse/application_spec.rbwith 3 test cases:bundle exec rspec spec/sse/— 47 examples, 0 failures