leyline-http 0.1.5

HTTP client that impersonates Chrome, Firefox, and Safari on the wire: TLS (JA3/JA4), HTTP/2, and HTTP/3 fingerprints
Documentation
use leyline::{Browser, Session, StopReason};

const IN_STOCK: &[u8] = b"\"availability\":\"InStock\"";
const OUT_OF_STOCK: &[u8] = b"\"availability\":\"OutOfStock\"";
const PAGE_LIMIT: usize = 512 * 1024;

enum Availability {
    InStock,
    OutOfStock,
    Unknown(StopReason),
}

fn contains(body: &[u8], from: usize, marker: &[u8]) -> bool {
    let start = from.saturating_sub(marker.len() - 1);
    body[start..].windows(marker.len()).any(|w| w == marker)
}

#[tokio::main]
async fn main() -> leyline::Result<()> {
    let url = std::env::args()
        .nth(1)
        .unwrap_or_else(|| "https://example.com/".to_string());
    let session = Session::browser(Browser::default());

    let page = session
        .get(&url)
        .error_for_status()
        .read_until(PAGE_LIMIT, |body, from| {
            contains(body, from, IN_STOCK) || contains(body, from, OUT_OF_STOCK)
        })
        .await?;

    let availability = match page.stopped_by {
        StopReason::PredicateMatched if contains(&page.bytes, 0, IN_STOCK) => Availability::InStock,
        StopReason::PredicateMatched => Availability::OutOfStock,
        other => Availability::Unknown(other),
    };
    match availability {
        Availability::InStock => println!("{url}: in stock"),
        Availability::OutOfStock => println!("{url}: out of stock"),
        Availability::Unknown(reason) => println!(
            "{url}: unknown, no marker in {} decoded bytes ({reason:?})",
            page.bytes.len()
        ),
    }
    Ok(())
}