feat(fetch): streaming / non-blocking HTTP fetch API#36
Conversation
The existing api_fetch blocks the guest until the whole response body is
downloaded, which freezes the frame loop and makes LLM token streams,
chunked feeds, and progressive downloads impossible. This adds a second,
handle-based fetch API that returns immediately and streams body chunks
into the guest as they arrive.
Host (oxide-browser/src/fetch.rs — new module)
* FetchState: lazily-initialised tokio Runtime + HashMap<u32, Arc<FetchInner>>.
* Each api_fetch_begin spawns an async task that drives reqwest's
Client::send() and then bytes_stream(), pushing chunks into a
Mutex<VecDeque<Vec<u8>>>.
* AtomicU32 ready-state and HTTP status; AtomicBool abort flag checked
between chunks so cancellation is prompt.
New host functions (all in the "oxide" import module):
* api_fetch_begin(method, url, content_type, body) -> u32 handle
* api_fetch_state(id) -> u32
0 PENDING / 1 STREAMING / 2 DONE / 3 ERROR / 4 ABORTED
* api_fetch_status(id) -> u32 (HTTP status, 0 until headers arrive)
* api_fetch_recv(id, ptr, cap) -> i64
>=0 bytes written, -1 pending, -2 EOF, -3 error, -4 unknown handle.
Chunks larger than cap are split and the remainder is re-queued at
the front so no bytes are dropped.
* api_fetch_error(id, ptr, cap) -> i32
* api_fetch_abort(id), api_fetch_remove(id)
The existing synchronous api_fetch is untouched for back-compat.
HostState (oxide-browser/src/capabilities.rs)
* New fetch: Arc<Mutex<Option<crate::fetch::FetchState>>> field,
initialised to None and registered via
crate::fetch::register_fetch_functions at the bottom of
register_host_functions.
SDK (oxide-sdk/src/lib.rs)
* extern "C" imports for all seven api_fetch_* functions.
* Public wrappers: fetch_begin, fetch_begin_get, fetch_state,
fetch_status, fetch_recv (returns FetchChunk::{Data, Pending, End,
Error}), fetch_recv_into (zero-alloc), fetch_error, fetch_abort,
fetch_remove.
* FETCH_* ready-state constants and a new "HTTP (streaming)" row in the
module-level API table.
Example
* examples/stream-fetch-demo: progressive UI with state badge, HTTP
status, byte / chunk counters, live body preview, and Abort / Restart
buttons. Defaults to httpbin.org/drip so the streaming behaviour is
observable by default. Added to the workspace members list.
Roadmap
* Tick "Non-blocking fetch with callback/promise-style API" and
"Streaming response bodies (chunked transfer)" in Phase 4 Async I/O.
Verification
* cargo fmt --all
* cargo clippy --workspace --all-targets -- -D warnings
* cargo test --workspace
* cargo build -p oxide-browser
* cargo build --target wasm32-unknown-unknown --release -p stream-fetch-demo
Made-with: Cursor
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughThis PR introduces a new streaming, non-blocking HTTP fetch API across oxide-browser and oxide-sdk layers, enabling guest WebAssembly modules to perform asynchronous HTTP requests with chunked response handling. A new demo application is included to showcase the functionality, along with workspace and roadmap updates. Changes
Sequence Diagram(s)sequenceDiagram
participant Guest as Guest Module<br/>(Demo App)
participant Host as Host (oxide-browser)
participant Queue as Chunk Queue
participant Network as HTTP Network
Guest->>Host: fetch_begin_get(url)
activate Host
Host->>Host: Allocate request ID<br/>Create FetchInner
Host->>Network: Spawn async Tokio task
deactivate Host
activate Network
Host-->>Guest: Return handle
Guest->>Host: fetch_state(handle)
Host-->>Guest: FETCH_STREAMING
Network->>Queue: Stream response chunks
loop Poll for chunks
Guest->>Host: fetch_recv(handle, capacity)
activate Host
Host->>Queue: Drain up to capacity
Host-->>Guest: Data chunk or sentinel
deactivate Host
end
Network->>Host: Response complete
Network->>Host: Update state → FETCH_DONE
deactivate Network
Guest->>Host: fetch_recv(handle, capacity)
activate Host
Host-->>Guest: End sentinel
deactivate Host
Guest->>Host: fetch_remove(handle)
Host->>Host: Free request resources
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The existing api_fetch blocks the guest until the whole response body is downloaded, which freezes the frame loop and makes LLM token streams, chunked feeds, and progressive downloads impossible. This adds a second, handle-based fetch API that returns immediately and streams body chunks into the guest as they arrive.
Host (oxide-browser/src/fetch.rs — new module)
New host functions (all in the "oxide" import module):
The existing synchronous api_fetch is untouched for back-compat.
HostState (oxide-browser/src/capabilities.rs)
SDK (oxide-sdk/src/lib.rs)
Example
Roadmap
Verification
Made-with: Cursor
Summary by CodeRabbit
New Features
Documentation