feat(procedures): add streaming HTTP response support#4692
Open
philtrem wants to merge 4 commits intoclockworklabs:masterfrom
Open
feat(procedures): add streaming HTTP response support#4692philtrem wants to merge 4 commits intoclockworklabs:masterfrom
philtrem wants to merge 4 commits intoclockworklabs:masterfrom
Conversation
…urning empty Headers fetch() was deserializing the HttpResponse (including headers) from BSATN but then discarding them, always returning `new Headers()`. This made it impossible for procedures to inspect response metadata like Content-Type or retry hints. Add a `deserializeHeaders()` helper that converts the BSATN-decoded HttpHeaders entries into a web-standard Headers object, and use it in fetch().
…modules Add fetchStreaming() to the procedure HTTP client, allowing TypeScript modules to consume HTTP response bodies chunk by chunk without buffering the entire response in memory. Syscall layer: - procedure_http_stream_open: initiates the request and returns a handle plus BSATN-encoded response metadata (status, headers) - procedure_http_stream_next: blocks until the next chunk arrives or the stream ends; guarded by in_tx() to prevent holding a mutable transaction open across network waits - procedure_http_stream_close: closes the handle and aborts the background reader task Runtime: - HttpStreamState stores the mpsc receiver and a tokio AbortHandle; Drop impl aborts the background reader immediately rather than waiting for the next frame or timeout - Streaming response size is metered via the existing procedure_http_response_size_bytes counter (header size at open, per-chunk body size at read) TypeScript bindings: - StreamHandle class with FinalizationRegistry safety net - StreamingResponse interface with Symbol.iterator / Symbol.dispose - fetchStreaming() uses deserializeHeaders() from the preceding commit to expose response headers Tests: - Unit: WouldBlockTransaction guards on http_request and http_stream_open - Unit: HttpStreamState::drop aborts the background task - Smoketest: end-to-end streaming read, tx-blocked iteration, and header preservation against a local chunked HTTP server
Ensures the stream handle is registered with FinalizationRegistry before any code that could throw, preventing a handle leak if HttpResponse.deserialize() ever fails. Co-Authored-By: Claude Opus 4.6 (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.
Description of Changes
Adds
fetchStreaming()to the TypeScript procedure HTTP client, returning aStreamingResponsethat yields body chunks via synchronous iteration. This enables procedures to process large HTTP responses without buffering the entire body in memory.Depends on #4691 (headers fix is the first commit in this branch).
Architecture:
procedure_http_stream_nextprepare_http_request()to reuse IP filtering, DNS filtering, timeout clamping, and redirect policy betweenfetch()andfetchStreaming()stream_nextprevents blocking insidewithTxFinalizationRegistrybackup + explicitSymbol.dispose()+HttpStreamState::dropaborts background taskResourceSlab/ handle pattern used by row iteratorsAPI and ABI breaking changes
None. Adds new
fetchStreaming()method toHttpClientand 3 new V8 syscalls (procedure_http_stream_open,procedure_http_stream_next,procedure_http_stream_close). Existingfetch()API is unchanged.Expected complexity level and risk
3 — Introduces a new streaming code path through the syscall layer, instance environment, and TypeScript bindings. The main interaction concern is blocking the V8 thread per chunk, which stalls all other reducers/procedures for the module instance while waiting. This is documented and is a necessary trade-off until async procedures are supported. The Rust-side changes reuse existing infrastructure (
ResourceSlab,prepare_http_request, metrics) to minimize new surface area.Testing
http_requestblocked during transactionhttp_stream_openblocked during transactionHttpStreamState::dropaborts background taskstream.next()inside transaction throwsWouldBlockTransaction