1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
use junction_api::http::{RouteRetry, RouteTimeouts};
use std::net::SocketAddr;
use xds_api::pb::envoy::config::{core::v3 as xds_core, endpoint::v3 as xds_endpoint};

/// An HTTP endpoint to make a request to.
///
/// Endpoints contain both a target [url][crate::Url] that should be given to an
/// HTTP client and an [address][EndpointAddress] that indicates the address the
/// the hostname in the URL should resolve to. See [EndpointAddress] for more
/// information on how and when to resolve an address.
#[derive(Debug)]
pub struct Endpoint {
    pub url: crate::Url,
    pub timeouts: Option<RouteTimeouts>,
    pub retry: Option<RouteRetry>,
    pub address: EndpointAddress,
}

/// The address of an endpoint.
///
/// Depending on the type of endpoint, addresses may need to be further resolved by
/// a client implementation.
#[derive(Clone, Debug, Hash)]
pub enum EndpointAddress {
    /// A resolved IP address and port. This address can be used for a request
    /// without any further resolution.
    SocketAddr(SocketAddr),

    /// A DNS name and port. The name should be resolved periodically by the HTTP
    /// client and used to direct traffic.
    ///
    /// This name may be different than the hostname part of an [Endpoint]'s `url`.
    DnsName(String, u32),
}

impl std::fmt::Display for EndpointAddress {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            EndpointAddress::SocketAddr(addr) => addr.fmt(f),
            EndpointAddress::DnsName(name, port) => write!(f, "{name}:{port}"),
        }
    }
}

impl EndpointAddress {
    pub(crate) fn from_socket_addr(xds_address: &xds_core::SocketAddress) -> Option<Self> {
        let ip = xds_address.address.parse().ok()?;
        let port: u16 = match xds_address.port_specifier.as_ref()? {
            xds_core::socket_address::PortSpecifier::PortValue(port) => (*port).try_into().ok()?,
            _ => return None,
        };

        Some(Self::SocketAddr(SocketAddr::new(ip, port)))
    }

    pub(crate) fn from_dns_name(xds_address: &xds_core::SocketAddress) -> Option<Self> {
        let address = xds_address.address.clone();
        let port = match xds_address.port_specifier.as_ref()? {
            xds_core::socket_address::PortSpecifier::PortValue(port) => port,
            _ => return None,
        };

        Some(Self::DnsName(address, *port))
    }

    pub(crate) fn from_xds_lb_endpoint<F>(endpoint: &xds_endpoint::LbEndpoint, f: F) -> Option<Self>
    where
        F: Fn(&xds_core::SocketAddress) -> Option<EndpointAddress>,
    {
        let endpoint = match endpoint.host_identifier.as_ref()? {
            xds_endpoint::lb_endpoint::HostIdentifier::Endpoint(ep) => ep,
            xds_endpoint::lb_endpoint::HostIdentifier::EndpointName(_) => return None,
        };

        let address = endpoint.address.as_ref().and_then(|a| a.address.as_ref())?;
        match address {
            xds_core::address::Address::SocketAddress(socket_address) => f(socket_address),
            _ => None,
        }
    }
}