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::sync::{Mutex, PoisonError, Weak};

use crate::cookie::Jar;
use crate::core::Result;
use crate::core::session::{Identity, Session, SessionInner};

struct Derived {
    base: Weak<SessionInner>,
    index: usize,
    session: Session,
}

#[derive(Default)]
pub(super) struct IdentitySessions {
    derived: Mutex<Vec<Derived>>,
}

impl std::fmt::Debug for IdentitySessions {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("IdentitySessions")
            .field("sessions", &self.lock().len())
            .finish()
    }
}

impl IdentitySessions {
    pub(super) fn session(
        &self,
        base: &Session,
        index: usize,
        identity: Identity,
    ) -> Result<Session> {
        if let Some(session) = self.find(base, index) {
            return Ok(session);
        }
        let session = base
            .with_identity(identity)?
            .without_proxy_pool()
            .with_cookie_jar(Jar::new());
        Ok(self.insert(base, index, session))
    }

    fn find(&self, base: &Session, index: usize) -> Option<Session> {
        let mut derived = self.lock();
        derived.retain(|entry| entry.base.strong_count() > 0);
        derived
            .iter()
            .find(|entry| entry.index == index && base.is_inner(&entry.base))
            .map(|entry| entry.session.clone())
    }

    fn insert(&self, base: &Session, index: usize, session: Session) -> Session {
        let mut derived = self.lock();
        if let Some(existing) = derived
            .iter()
            .find(|entry| entry.index == index && base.is_inner(&entry.base))
        {
            return existing.session.clone();
        }
        derived.push(Derived {
            base: base.downgrade(),
            index,
            session: session.clone(),
        });
        session
    }

    fn lock(&self) -> std::sync::MutexGuard<'_, Vec<Derived>> {
        self.derived.lock().unwrap_or_else(PoisonError::into_inner)
    }
}