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 std::collections::HashMap;
use std::sync::Arc;
use std::sync::atomic::AtomicBool;

use tokio::sync::mpsc;

use super::{H3Client, H3Driver};
use crate::pool::TlsInfo;
use crate::quic::config::H3Config;
use crate::quic::connection::connect_and_handshake;
use crate::tls::{FingerprintConnector, TlsError, TlsTrustConfig};
use crate::{Error, Kind};

const COMMAND_CHANNEL_CAPACITY: usize = 1024;

const STREAM_REQ_CAPACITY: usize = 64;

pub(crate) async fn open_fresh_h3(
    h3_cfg: &H3Config,
    trust: &TlsTrustConfig,
    connector: &FingerprintConnector,
    host: &str,
    port: u16,
    proxy: Option<&str>,
) -> Result<(H3Client, TlsInfo), Error> {
    let handshake = connect_and_handshake(h3_cfg, trust, connector, host, port, proxy);
    let established = connector
        .with_timeout(async { Ok::<_, TlsError>(handshake.await) })
        .await
        .map_err(|timeout| {
            Error::from(timeout)
                .with_message(format!("h3 handshake to {host}:{port}: connect timeout"))
        })?
        .map_err(|message| Error::new(Kind::Http3).with_message(message))?;
    let tls = established.tls.clone();

    let (tx, command_rx) = mpsc::channel(COMMAND_CHANNEL_CAPACITY);
    let (body_chunk_tx, body_chunk_rx) = mpsc::channel(STREAM_REQ_CAPACITY);
    let closed = Arc::new(AtomicBool::new(false));
    let open_streams = Arc::new(std::sync::atomic::AtomicUsize::new(0));

    let driver = H3Driver {
        established,
        command_rx,
        body_chunk_tx,
        body_chunk_rx,
        closed: Arc::clone(&closed),
        open_streams: Arc::clone(&open_streams),
        streams: HashMap::new(),
    };
    drop(tokio::spawn(driver.run()));

    Ok((
        H3Client {
            tx,
            closed,
            open_streams,
            pseudo_order: h3_cfg.pseudo_order,
        },
        tls,
    ))
}