rama_haproxy/protocol/v1/
model.rs

1//! The data model to represent the test PROXY protocol header.
2
3use crate::protocol::ip::{IPv4, IPv6};
4use std::borrow::Cow;
5use std::fmt;
6use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
7
8/// The prefix of the PROXY protocol header.
9pub const PROTOCOL_PREFIX: &str = "PROXY";
10
11/// The suffix of the PROXY protocol header.
12pub const PROTOCOL_SUFFIX: &str = "\r\n";
13
14/// TCP protocol with IPv4 address family.
15pub const TCP4: &str = "TCP4";
16
17/// TCP protocol with IPv6 address family.
18pub const TCP6: &str = "TCP6";
19
20/// Unknown protocol and address family. Address portion of the header should be ignored.
21pub const UNKNOWN: &str = "UNKNOWN";
22
23/// The separator of the header parts.
24pub const SEPARATOR: char = ' ';
25
26/// A text PROXY protocol header that borrows the input string.
27///
28/// ## Examples
29/// ### Worst Case (from bytes)
30/// ```rust
31/// use rama_haproxy::protocol::v1::{Addresses, Header, UNKNOWN};
32///
33/// let input = "PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n";
34/// let header = Header::try_from(input.as_bytes()).unwrap();
35///
36/// assert_eq!(header, Header::new(input, Addresses::Unknown));
37/// assert_eq!(header.protocol(), UNKNOWN);
38/// assert_eq!(header.addresses_str(), "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535");
39/// ```
40///
41/// ### UNKNOWN
42/// ```rust
43/// use rama_haproxy::protocol::v1::{Addresses, Header, UNKNOWN};
44///
45/// let input = "PROXY UNKNOWN\r\nhello";
46/// let header = Header::try_from(input).unwrap();
47///
48/// assert_eq!(header, Header::new("PROXY UNKNOWN\r\n", Addresses::Unknown));
49/// assert_eq!(header.protocol(), UNKNOWN);
50/// assert_eq!(header.addresses_str(), "");
51/// ```
52///
53/// ### TCP4
54/// ```rust
55/// use std::net::Ipv4Addr;
56/// use rama_haproxy::protocol::v1::{Header, Addresses, TCP4};
57///
58/// let input = "PROXY TCP4 127.0.1.2 192.168.1.101 80 443\r\n";
59/// let header = Header::try_from(input).unwrap();
60///
61/// assert_eq!(header, Header::new(input, Addresses::new_tcp4(Ipv4Addr::new(127, 0, 1, 2), Ipv4Addr::new(192, 168, 1, 101), 80, 443)));
62/// assert_eq!(header.protocol(), TCP4);
63/// assert_eq!(header.addresses_str(), "127.0.1.2 192.168.1.101 80 443");
64/// ```
65///
66/// ### TCP6
67/// ```rust
68/// use std::net::Ipv6Addr;
69/// use rama_haproxy::protocol::v1::{Header, Addresses, TCP6};
70///
71/// let input = "PROXY TCP6 1234:5678:90ab:cdef:fedc:ba09:8765:4321 4321:8765:ba09:fedc:cdef:90ab:5678:1234 443 65535\r\n";
72/// let header = Header::try_from(input).unwrap();
73///
74/// assert_eq!(
75///     header,
76///     Header::new(
77///         input,
78///         Addresses::new_tcp6(
79///             Ipv6Addr::from([0x1234, 0x5678, 0x90AB, 0xCDEF, 0xFEDC, 0xBA09, 0x8765, 0x4321]),
80///             Ipv6Addr::from([0x4321, 0x8765, 0xBA09, 0xFEDC, 0xCDEF, 0x90AB, 0x5678, 0x01234,]),
81///             443,
82///             65535
83///         )
84///     )
85/// );
86/// assert_eq!(header.protocol(), TCP6);
87/// assert_eq!(header.addresses_str(), "1234:5678:90ab:cdef:fedc:ba09:8765:4321 4321:8765:ba09:fedc:cdef:90ab:5678:1234 443 65535");
88/// ```
89///
90/// ### Invalid
91/// ```rust
92/// use rama_haproxy::protocol::v1::{Header, Addresses, ParseError};
93///
94/// assert_eq!(Err(ParseError::InvalidProtocol), "PROXY tcp4\r\n".parse::<Addresses>());
95/// ```
96#[derive(Clone, Debug, PartialEq, Eq, Hash)]
97pub struct Header<'a> {
98    /// The original input string.
99    pub header: Cow<'a, str>,
100    /// The source and destination addresses of the header.
101    pub addresses: Addresses,
102}
103
104impl<'a> Header<'a> {
105    /// Creates a new `Header` with the given addresses and a reference to the original input.
106    pub fn new<H: Into<&'a str>, A: Into<Addresses>>(header: H, addresses: A) -> Self {
107        Header {
108            header: Cow::Borrowed(header.into()),
109            addresses: addresses.into(),
110        }
111    }
112
113    /// Creates an owned clone of this [`Header`].
114    pub fn to_owned(&self) -> Header<'static> {
115        Header {
116            header: Cow::Owned::<'static>(self.header.to_string()),
117            addresses: self.addresses,
118        }
119    }
120
121    /// The protocol portion of this `Header`.
122    pub fn protocol(&self) -> &str {
123        self.addresses.protocol()
124    }
125
126    /// The source and destination addresses portion of this `Header`.
127    pub fn addresses_str(&self) -> &str {
128        let start = PROTOCOL_PREFIX.len() + SEPARATOR.len_utf8() + self.protocol().len();
129        let end = self.header.len() - PROTOCOL_SUFFIX.len();
130        let addresses = &self.header[start..end];
131
132        if addresses.starts_with(SEPARATOR) {
133            &addresses[SEPARATOR.len_utf8()..]
134        } else {
135            addresses
136        }
137    }
138}
139
140/// The source and destination of a header.
141/// Includes IP (v4 or v6) addresses and TCP ports.
142///
143/// ## Examples
144/// ### Worst Case
145/// ```rust
146/// use rama_haproxy::protocol::v1::{Addresses, Header, UNKNOWN};
147///
148/// let header = "PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n";
149/// let addresses = Addresses::Unknown;
150///
151/// assert_eq!(addresses, header.parse().unwrap());
152/// assert_ne!(addresses.to_string().as_str(), header);
153/// ```
154///
155/// ### UNKNOWN
156/// ```rust
157/// use rama_haproxy::protocol::v1::Addresses;
158///
159/// let header = "PROXY UNKNOWN\r\n";
160/// let addresses = Addresses::Unknown;
161///
162/// assert_eq!(addresses, header.parse().unwrap());
163/// assert_eq!(addresses.to_string().as_str(), header);
164/// ```
165///
166/// ### TCP4
167/// ```rust
168/// use std::net::Ipv4Addr;
169/// use rama_haproxy::protocol::v1::Addresses;
170///
171/// let header = "PROXY TCP4 127.0.1.2 192.168.1.101 80 443\r\n";
172/// let addresses = Addresses::new_tcp4(Ipv4Addr::new(127, 0, 1, 2), Ipv4Addr::new(192, 168, 1, 101), 80, 443);
173///
174/// assert_eq!(addresses, header.parse().unwrap());
175/// assert_eq!(addresses.to_string().as_str(), header);
176/// ```
177///
178/// ### TCP6
179/// ```rust
180/// use std::net::Ipv6Addr;
181/// use rama_haproxy::protocol::v1::Addresses;
182///
183/// let header = "PROXY TCP6 1234:5678:90ab:cdef:fedc:ba09:8765:4321 4321:8765:ba09:fedc:cdef:90ab:5678:1234 443 65535\r\n";
184/// let addresses = Addresses::new_tcp6(
185///     Ipv6Addr::from([0x1234, 0x5678, 0x90AB, 0xCDEF, 0xFEDC, 0xBA09, 0x8765, 0x4321]),
186///     Ipv6Addr::from([0x4321, 0x8765, 0xBA09, 0xFEDC, 0xCDEF, 0x90AB, 0x5678, 0x01234,]),
187///     443,
188///     65535
189/// );
190///
191/// assert_eq!(addresses, header.parse().unwrap());
192/// assert_eq!(addresses.to_string().as_str(), header);
193/// ```
194///
195/// ### Invalid
196/// ```rust
197/// use rama_haproxy::protocol::v1::{Addresses, ParseError};
198///
199/// assert_eq!(Err(ParseError::InvalidProtocol), "PROXY tcp4\r\n".parse::<Addresses>());
200/// ```
201#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
202pub enum Addresses {
203    #[default]
204    /// The source and destination addresses of the header are unknown.
205    Unknown,
206    /// The source and destination addresses of the header are IPv4.
207    Tcp4(IPv4),
208    /// The source and destination addresses of the header are IPv6.
209    Tcp6(IPv6),
210}
211
212impl Addresses {
213    /// Create a new IPv4 TCP address.
214    pub fn new_tcp4<T: Into<Ipv4Addr>>(
215        source_address: T,
216        destination_address: T,
217        source_port: u16,
218        destination_port: u16,
219    ) -> Self {
220        Addresses::Tcp4(IPv4 {
221            source_address: source_address.into(),
222            source_port,
223            destination_address: destination_address.into(),
224            destination_port,
225        })
226    }
227
228    /// Create a new IPv6 TCP address.
229    pub fn new_tcp6<T: Into<Ipv6Addr>>(
230        source_address: T,
231        destination_address: T,
232        source_port: u16,
233        destination_port: u16,
234    ) -> Self {
235        Addresses::Tcp6(IPv6 {
236            source_address: source_address.into(),
237            source_port,
238            destination_address: destination_address.into(),
239            destination_port,
240        })
241    }
242
243    /// The protocol portion of this `Addresses`.
244    pub fn protocol(&self) -> &str {
245        match self {
246            Addresses::Tcp4(..) => TCP4,
247            Addresses::Tcp6(..) => TCP6,
248            Addresses::Unknown => UNKNOWN,
249        }
250    }
251}
252
253impl From<(SocketAddr, SocketAddr)> for Addresses {
254    fn from(addresses: (SocketAddr, SocketAddr)) -> Self {
255        match addresses {
256            (SocketAddr::V4(source), SocketAddr::V4(destination)) => Addresses::Tcp4(IPv4::new(
257                *source.ip(),
258                *destination.ip(),
259                source.port(),
260                destination.port(),
261            )),
262            (SocketAddr::V6(source), SocketAddr::V6(destination)) => Addresses::Tcp6(IPv6::new(
263                *source.ip(),
264                *destination.ip(),
265                source.port(),
266                destination.port(),
267            )),
268            _ => Addresses::Unknown,
269        }
270    }
271}
272
273impl From<IPv4> for Addresses {
274    fn from(addresses: IPv4) -> Self {
275        Addresses::Tcp4(addresses)
276    }
277}
278
279impl From<IPv6> for Addresses {
280    fn from(addresses: IPv6) -> Self {
281        Addresses::Tcp6(addresses)
282    }
283}
284
285impl fmt::Display for Header<'_> {
286    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
287        f.write_str(self.header.as_ref())
288    }
289}
290
291impl fmt::Display for Addresses {
292    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293        match self {
294            Self::Unknown => f.write_str("PROXY UNKNOWN\r\n"),
295            Self::Tcp4(a) => write!(
296                f,
297                "PROXY TCP4 {} {} {} {}\r\n",
298                a.source_address, a.destination_address, a.source_port, a.destination_port
299            ),
300            Self::Tcp6(a) => write!(
301                f,
302                "PROXY TCP6 {} {} {} {}\r\n",
303                a.source_address, a.destination_address, a.source_port, a.destination_port
304            ),
305        }
306    }
307}