Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Browser-context HTTP

tab.request() makes an HTTP call that inherits the browser's cookies and session. It needs no feature flag. The builder mirrors pydoll's shape:

use serde_json::json;

// GET (inherits cookies + same-origin CORS of the current page)
let resp = tab.request().get("https://example.com/api/data").send().await?;
println!("{} {}", resp.status(), resp.text()?);

// POST a JSON body
let resp = tab
    .request()
    .post("https://example.com/api/echo")
    .header("X-Trace", "1")
    .json(&json!({ "key": "value" }))?
    .send()
    .await?;
let parsed: serde_json::Value = resp.json()?;

get / post / put / delete / head / patch set the method and URL; header appends a header; body sets raw bytes; json serializes a value and sets Content-Type: application/json. send returns a Response exposing status / headers / text / json / bytes.

A non-2xx status is not an error — the Response carries the status. Only a thrown fetch (network failure, CORS block) or a failed privileged load surfaces a ZendriverError::Request.

Default path: in-page fetch

By default send() runs fetch in the page via evaluate_main, so the request behaves exactly like one the page itself made — same cookies, same CORS rules. Because of that it needs a loaded document of the right origin: navigate to the origin first, then make same-origin calls. On about:blank a cross-origin call is a null-origin request and will be CORS-blocked.

The request URL, headers, and body are embedded into the generated JS via serde_json, so arbitrary url / header / body values can't break out of the JS string — there is no injection surface. The body round-trips as base64 so binary payloads survive intact.

Opt-in: bypass_cors()

bypass_cors routes through Chrome's privileged Network.loadNetworkResource instead — it ignores page CORS and works without a same-origin document, while still inheriting session cookies. It is GET-only in this version; for other methods use the default fetch path.

// Reach a cross-origin endpoint that the in-page fetch would be blocked on.
let resp = tab
    .request()
    .get("https://other-origin.example/resource")
    .bypass_cors()
    .send()
    .await?;

Full example

//! Demonstrates the browser-context HTTP API (`tab.request()`).
//!
//! `tab.request()` runs `fetch` inside the page, so the request inherits the
//! page's cookies and same-origin CORS rules. Use `.bypass_cors()` to route
//! through Chrome's privileged `Network.loadNetworkResource` path instead
//! (GET only; bypasses CORS).
//!
//! Sequence:
//!   1. Navigate to example.com so that cookies / origin context is
//!      established.
//!   2. Issue a GET to `https://httpbin.org/get` — prints status + truncated
//!      body.
//!   3. Issue a POST with a JSON body to `https://httpbin.org/post` — prints
//!      status + truncated body.
//!   4. Close the browser.
//!
//! No special cargo features required:
//! `cargo run --example browser_request`.

use serde_json::json;
use zendriver::Browser;

#[tokio::main]
#[allow(clippy::result_large_err)] // example boundary; users wrap in their own Error
async fn main() -> zendriver::Result<()> {
    tracing_subscriber::fmt::init();

    let browser = Browser::builder().headless(true).launch().await?;
    let tab = browser.main_tab();

    // Navigate first so the page context (cookies / origin) is established.
    tab.goto("https://example.com").await?;
    tab.wait_for_load().await?;

    // ── GET ──────────────────────────────────────────────────────────────────
    let resp = tab.request().get("https://httpbin.org/get").send().await?;

    let body = resp.text()?;
    println!(
        "GET  status={} body_len={} snippet={:.80}",
        resp.status(),
        body.len(),
        body
    );

    // ── POST with JSON body ───────────────────────────────────────────────────
    let resp = tab
        .request()
        .post("https://httpbin.org/post")
        .json(&json!({"hello": "zendriver"}))?
        .send()
        .await?;

    let body = resp.text()?;
    println!(
        "POST status={} body_len={} snippet={:.80}",
        resp.status(),
        body.len(),
        body
    );

    browser.close().await?;
    Ok(())
}