| Crates.io | chromiumoxide_event_stream |
| lib.rs | chromiumoxide_event_stream |
| version | 0.3.0 |
| created_at | 2025-11-05 08:16:56.526314+00 |
| updated_at | 2026-01-12 11:47:28.251422+00 |
| description | Library for capturing network responses from Chromium pages using chromiumoxide CDP (Chrome DevTools Protocol). Stream events back to Rust with optional URL and content-type filtering. |
| homepage | |
| repository | https://github.com/geopin90/chromiumoxide_event_stream |
| max_upload_size | |
| id | 1917572 |
| size | 52,544 |
Capture network responses from Chromium pages using chromiumoxide, with simple JS hooks for fetch/XHR. Stream events back to Rust with optional URL and content-type filtering. Works with any text-based content. It lets you catch all emerging events without missing.
tokio and futures::channel::mpscstart_event_stream, Event)While you could use chromiumoxide's CDP APIs directly, this crate saves you from writing complex coordination code. Also, js webhooks works better than CDP because webhooks catch all the events, and CDP catches only about 50% of them.
Anything text-based returned by fetch/XHR:
text/htmlapplication/jsonapplication/xml, text/xmltext/plain (including logs, NDJSON if sent as text)text/csv, text/tab-separated-valuesapplication/javascript, text/javascript, text/cssimage/svg+xmlapplication/x-www-form-urlencoded, multipart/form-data (text parts only)Binary payloads (e.g., image/png, application/pdf, application/octet-stream) will be coerced via response.text() and get mangled. If you need binary, consider extending the hooks to arrayBuffer() + base64.
One function call:
use chromiumoxide::Browser; // set up your browser/Page as usual
use chromiumoxide_event_stream::{
EventStreamConfig,
start_event_stream,
};
# async fn demo(page: chromiumoxide::page::Page) -> anyhow::Result<()> {
let rx = start_event_stream(
page,
EventStreamConfig {
poll_interval_ms: 300,
url_substring_filter: Some("/Search".to_string()), // e.g. Some("/api/")
content_type_substring_filter: Some("application/json".to_string()), // e.g. Some("text/html")
},
).await?;
tokio::spawn(async move {
let mut rx = rx;
while let Some(ev) = rx.next().await {
println!("{} {} -> {} bytes", ev.status.unwrap_or(0), ev.url, ev.body.len());
}
});
# Ok(()) }
Result: ~150 lines of complex async code → 3 lines of simple API usage
If you're waiting for a specific event after an action (e.g., clicking a button), you may want to avoid hanging indefinitely if the event never arrives. Use wait_for_event_with_timeout instead of rx.next().await:
use std::time::Duration;
use chromiumoxide_event_stream::{start_event_stream, wait_for_event_with_timeout};
let mut rx = start_event_stream(page, config).await?;
// Perform some action (e.g., click a button)
button.click().await?;
// Wait up to 5 seconds for an event
match wait_for_event_with_timeout(&mut rx, Duration::from_secs(5)).await {
Ok(Some(event)) => {
println!("Received event: {}", event.url);
// Process the event
},
Ok(None) => {
println!("Stream closed");
},
Err(_) => {
println!("Timeout: no event received after action");
// Handle the case where no event appeared
}
}
This prevents your program from hanging when the expected event doesn't arrive.
url_substring_filter: only capture events whose URL contains this substring.content_type_substring_filter: only capture events whose content-type contains this substring.Examples:
content_type_substring_filter = Some("text/html".into())content_type_substring_filter = Some("text/csv".into())None and filter client-side.This project was bootstrapped with the assistance of Cursor AI.