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::io;

use super::{Error, Kind};
use crate::h2::H2Error;
#[cfg(feature = "http3")]
use crate::quic::H3SendError;
use crate::tls::TlsError;

impl Error {
    pub(crate) fn from_url_parse(e: url::ParseError) -> Self {
        Error::new(Kind::Url).with_source(e)
    }

    pub(crate) fn from_json(e: serde_json::Error) -> Self {
        Error::new(Kind::Json).with_source(e)
    }

    pub(crate) fn from_request_body(e: io::Error) -> Self {
        Error::new(Kind::Body)
            .with_message("request body stream failed")
            .with_source(e)
    }
}

impl From<url::ParseError> for Error {
    fn from(e: url::ParseError) -> Self {
        Error::from_url_parse(e)
    }
}

impl From<io::Error> for Error {
    fn from(e: io::Error) -> Self {
        if !e.get_ref().is_some_and(|inner| inner.is::<Error>()) {
            return Error::new(Kind::Io).with_source(e);
        }
        match e.into_inner().map(|inner| inner.downcast::<Error>()) {
            Some(Ok(err)) => *err,
            Some(Err(other)) => Error::new(Kind::Io).with_source(io::Error::other(other)),
            None => Error::new(Kind::Io),
        }
    }
}

impl From<TlsError> for Error {
    fn from(e: TlsError) -> Self {
        let kind = match &e {
            TlsError::Dns(_) | TlsError::TcpConnect(_) => Kind::Connect,
            TlsError::Proxy { .. } | TlsError::ProxyTargetUnreachable { .. } => Kind::Proxy,
            TlsError::SslConfig(_) | TlsError::Profile(_) | TlsError::TrustStore(_) => Kind::Config,
            _ => Kind::Tls,
        };
        Error::new(kind).with_source(e)
    }
}

impl From<H2Error> for Error {
    fn from(e: H2Error) -> Self {
        if let Some(limit) = e.body_limit() {
            return limit.error();
        }
        match e {
            H2Error::RequestBody(io) => Error::from_request_body(io),
            other => Error::new(Kind::Http2).with_source(other),
        }
    }
}

#[cfg(feature = "http3")]
impl From<H3SendError> for Error {
    fn from(e: H3SendError) -> Self {
        match e {
            H3SendError::BodyLimit(limit) => limit.error(),
            H3SendError::RequestBody(io) => Error::from_request_body(io),
            H3SendError::NotSent(message) | H3SendError::Rejected(message) => {
                Error::new(Kind::Http3)
                    .with_message(message)
                    .with_source(super::marker::NotProcessed)
            }
            other => Error::new(Kind::Http3).with_message(other.message().into_owned()),
        }
    }
}

impl From<http::Error> for Error {
    fn from(e: http::Error) -> Self {
        Error::new(Kind::Request).with_source(e)
    }
}

impl From<http::header::InvalidHeaderName> for Error {
    fn from(e: http::header::InvalidHeaderName) -> Self {
        Error::new(Kind::Request).with_source(e)
    }
}

impl From<http::header::InvalidHeaderValue> for Error {
    fn from(e: http::header::InvalidHeaderValue) -> Self {
        Error::new(Kind::Request).with_source(e)
    }
}

impl From<http::uri::InvalidUri> for Error {
    fn from(e: http::uri::InvalidUri) -> Self {
        Error::new(Kind::Url).with_source(e)
    }
}