fix(cdp): bound IO stream buffers so downloads cannot leak (#387)

* fix(js): fire trusted input/change from setFileInputFiles

DOM.setFileInputFiles dispatched plain input/change events, so
event.isTrusted was false. Upload flows and anti-bot code that gate their
change handler on isTrusted ignored them, which silently broke the exact
case the feature targets. The Input domain already marks its synthesized
clicks/keys trusted via __obscura_markTrusted; do the same here.

Fixes #384.

* fix(cdp): bound IO stream buffers so downloads cannot leak

Fetch.takeResponseBodyAsStream moved a response body out of the page's
LRU-bounded cache into CdpContext.io_streams, a server-lifetime map freed only
by an explicit IO.close. A client that opens a stream and disconnects, errors
mid-download, or never closes it leaked the full buffer forever; no teardown
path (WS close, Browser.close, Target.closeTarget) touched io_streams. That
reintroduced the unbounded memory growth streaming was meant to avoid (#360).

Replace the raw map + counter with an IoStreamStore that caps the number of
open streams and their total bytes (OBSCURA_IO_STREAM_MAX_ENTRIES / _BYTES),
evicting the oldest first and always keeping the just-opened stream. Reading an
evicted handle fails cleanly, which is the right trade against an OOM. Read
chunking is unchanged.

Fixes #386.
This commit is contained in:
Andrey Mnatsakanov authored and GitHub committed 2026-07-11 16:42:57 +02:00
1 parent d4fd2884a2
commit 50e66320b0
3 files changed
+150 -22

No files matched your search

+1 -3
View File
@@ -206,9 +206,7 @@ pub async fn handle(
format!("Fetch.takeResponseBodyAsStream: no cached body for {request_id}")
})?;
let handle = format!("stream-{}", ctx.io_stream_counter);
ctx.io_stream_counter += 1;
ctx.io_streams.insert(handle.clone(), (bytes, 0));
let handle = ctx.io_streams.insert(bytes);
Ok(json!({ "stream": handle }))
}
_ => Err(format!("Unknown Fetch method: {}", method)),