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:
1 parent
d4fd2884a2
commit
50e66320b0
3 files changed
+150
-22
No files matched your search
@@ -45,11 +45,12 @@ pub struct CdpContext {
|
||||
pub next_isolated_context_id: i64,
|
||||
pub fetch_intercept: FetchInterceptState,
|
||||
pub intercept_tx: Option<tokio::sync::mpsc::UnboundedSender<InterceptedRequest>>,
|
||||
// Open IO streams for Fetch.takeResponseBodyAsStream: handle -> (bytes, cursor).
|
||||
// Each holds a response body taken out of the page cache so a large download
|
||||
// is streamed chunk-by-chunk via IO.read and freed on IO.close (issue #360).
|
||||
pub io_streams: HashMap<String, (Vec<u8>, usize)>,
|
||||
pub io_stream_counter: u64,
|
||||
// Open IO streams for Fetch.takeResponseBodyAsStream. Each holds a response
|
||||
// body taken out of the page cache so a large download is streamed
|
||||
// chunk-by-chunk via IO.read and freed on IO.close (issue #360). The store
|
||||
// caps how many bodies (and how many bytes) can be held at once, evicting
|
||||
// the oldest, so an abandoned or disconnected stream cannot leak unbounded.
|
||||
pub io_streams: crate::domains::io::IoStreamStore,
|
||||
}
|
||||
|
||||
impl CdpContext {
|
||||
@@ -146,8 +147,7 @@ impl CdpContext {
|
||||
isolated_worlds: Vec::new(),
|
||||
valid_context_ids,
|
||||
next_isolated_context_id: 100,
|
||||
io_streams: HashMap::new(),
|
||||
io_stream_counter: 0,
|
||||
io_streams: crate::domains::io::IoStreamStore::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user