hns-browser-loopback-proxy 0.2.4

Authenticated loopback browser proxy adapters with explicit mobile and Chromium contracts
Documentation
use crate::{ProxyAuthorization, ProxyInstanceId};
use std::fmt;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::sync::Arc;

pub struct ProxyEndpoint {
    instance: ProxyInstanceId,
    address: SocketAddr,
    authorization: Arc<ProxyAuthorization>,
}

impl ProxyEndpoint {
    pub(crate) fn new(
        instance: ProxyInstanceId,
        address: SocketAddr,
        authorization: Arc<ProxyAuthorization>,
    ) -> Self {
        Self {
            instance,
            address,
            authorization,
        }
    }

    pub fn instance(&self) -> &ProxyInstanceId {
        &self.instance
    }

    pub fn address(&self) -> SocketAddr {
        self.address
    }

    pub fn port(&self) -> u16 {
        self.address.port()
    }

    pub fn realm(&self) -> &str {
        self.authorization.realm()
    }

    pub fn username(&self) -> &str {
        self.authorization.username()
    }

    pub fn password(&self) -> &str {
        self.authorization.password()
    }

    pub fn authorization_header_value(&self) -> String {
        self.authorization.authorization_header_value()
    }

    pub fn matches_challenge(&self, host: &str, port: u16, realm: &str) -> bool {
        self.address.ip() == IpAddr::V4(Ipv4Addr::LOCALHOST)
            && port == self.port()
            && self.authorization.matches_challenge(host, realm)
    }
}

impl fmt::Debug for ProxyEndpoint {
    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
        formatter
            .debug_struct("ProxyEndpoint")
            .field("instance", &self.instance)
            .field("address", &self.address)
            .field("realm", &"[REDACTED]")
            .field("username", &"[REDACTED]")
            .field("password", &"[REDACTED]")
            .finish()
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::ProxySessionId;

    #[test]
    fn endpoint_exposes_capability_only_through_explicit_accessors() {
        let authorization = Arc::new(ProxyAuthorization::generate().unwrap());
        let endpoint = ProxyEndpoint::new(
            ProxyInstanceId::new(ProxySessionId::generate().unwrap(), 4),
            "127.0.0.1:43123".parse().unwrap(),
            Arc::clone(&authorization),
        );
        let debug = format!("{endpoint:?}");

        assert!(!debug.contains(endpoint.realm()));
        assert!(!debug.contains(endpoint.username()));
        assert!(!debug.contains(endpoint.password()));
        assert!(authorization.verify_header_value(&endpoint.authorization_header_value()));
        assert!(endpoint.matches_challenge("127.0.0.1", 43123, endpoint.realm()));
        assert!(!endpoint.matches_challenge("127.0.0.1", 43124, endpoint.realm()));
    }
}