Skip to main content

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    #[must_use]
115    pub fn to_owned(&self) -> Header<'static> {
116        Header {
117            header: Cow::Owned::<'static>(self.header.to_string()),
118            addresses: self.addresses,
119        }
120    }
121
122    /// The protocol portion of this `Header`.
123    #[must_use]
124    pub fn protocol(&self) -> &str {
125        self.addresses.protocol()
126    }
127
128    /// The source and destination addresses portion of this `Header`.
129    #[must_use]
130    pub fn addresses_str(&self) -> &str {
131        let start = PROTOCOL_PREFIX.len() + SEPARATOR.len_utf8() + self.protocol().len();
132        let end = self.header.len() - PROTOCOL_SUFFIX.len();
133        let addresses = &self.header[start..end];
134
135        if addresses.starts_with(SEPARATOR) {
136            &addresses[SEPARATOR.len_utf8()..]
137        } else {
138            addresses
139        }
140    }
141}
142
143/// The source and destination of a header.
144/// Includes IP (v4 or v6) addresses and TCP ports.
145///
146/// ## Examples
147/// ### Worst Case
148/// ```rust
149/// use rama_haproxy::protocol::v1::{Addresses, Header, UNKNOWN};
150///
151/// let header = "PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n";
152/// let addresses = Addresses::Unknown;
153///
154/// assert_eq!(addresses, header.parse().unwrap());
155/// assert_ne!(addresses.to_string().as_str(), header);
156/// ```
157///
158/// ### UNKNOWN
159/// ```rust
160/// use rama_haproxy::protocol::v1::Addresses;
161///
162/// let header = "PROXY UNKNOWN\r\n";
163/// let addresses = Addresses::Unknown;
164///
165/// assert_eq!(addresses, header.parse().unwrap());
166/// assert_eq!(addresses.to_string().as_str(), header);
167/// ```
168///
169/// ### TCP4
170/// ```rust
171/// use std::net::Ipv4Addr;
172/// use rama_haproxy::protocol::v1::Addresses;
173///
174/// let header = "PROXY TCP4 127.0.1.2 192.168.1.101 80 443\r\n";
175/// let addresses = Addresses::new_tcp4(Ipv4Addr::new(127, 0, 1, 2), Ipv4Addr::new(192, 168, 1, 101), 80, 443);
176///
177/// assert_eq!(addresses, header.parse().unwrap());
178/// assert_eq!(addresses.to_string().as_str(), header);
179/// ```
180///
181/// ### TCP6
182/// ```rust
183/// use std::net::Ipv6Addr;
184/// use rama_haproxy::protocol::v1::Addresses;
185///
186/// let header = "PROXY TCP6 1234:5678:90ab:cdef:fedc:ba09:8765:4321 4321:8765:ba09:fedc:cdef:90ab:5678:1234 443 65535\r\n";
187/// let addresses = Addresses::new_tcp6(
188///     Ipv6Addr::from([0x1234, 0x5678, 0x90AB, 0xCDEF, 0xFEDC, 0xBA09, 0x8765, 0x4321]),
189///     Ipv6Addr::from([0x4321, 0x8765, 0xBA09, 0xFEDC, 0xCDEF, 0x90AB, 0x5678, 0x01234,]),
190///     443,
191///     65535
192/// );
193///
194/// assert_eq!(addresses, header.parse().unwrap());
195/// assert_eq!(addresses.to_string().as_str(), header);
196/// ```
197///
198/// ### Invalid
199/// ```rust
200/// use rama_haproxy::protocol::v1::{Addresses, ParseError};
201///
202/// assert_eq!(Err(ParseError::InvalidProtocol), "PROXY tcp4\r\n".parse::<Addresses>());
203/// ```
204#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
205pub enum Addresses {
206    #[default]
207    /// The source and destination addresses of the header are unknown.
208    Unknown,
209    /// The source and destination addresses of the header are IPv4.
210    Tcp4(IPv4),
211    /// The source and destination addresses of the header are IPv6.
212    Tcp6(IPv6),
213}
214
215impl Addresses {
216    /// Create a new IPv4 TCP address.
217    pub fn new_tcp4<T: Into<Ipv4Addr>>(
218        source_address: T,
219        destination_address: T,
220        source_port: u16,
221        destination_port: u16,
222    ) -> Self {
223        Self::Tcp4(IPv4 {
224            source_address: source_address.into(),
225            source_port,
226            destination_address: destination_address.into(),
227            destination_port,
228        })
229    }
230
231    /// Create a new IPv6 TCP address.
232    pub fn new_tcp6<T: Into<Ipv6Addr>>(
233        source_address: T,
234        destination_address: T,
235        source_port: u16,
236        destination_port: u16,
237    ) -> Self {
238        Self::Tcp6(IPv6 {
239            source_address: source_address.into(),
240            source_port,
241            destination_address: destination_address.into(),
242            destination_port,
243        })
244    }
245
246    /// The protocol portion of this `Addresses`.
247    #[must_use]
248    pub fn protocol(&self) -> &str {
249        match self {
250            Self::Tcp4(..) => TCP4,
251            Self::Tcp6(..) => TCP6,
252            Self::Unknown => UNKNOWN,
253        }
254    }
255}
256
257impl From<(SocketAddr, SocketAddr)> for Addresses {
258    fn from(addresses: (SocketAddr, SocketAddr)) -> Self {
259        match addresses {
260            (SocketAddr::V4(source), SocketAddr::V4(destination)) => Self::Tcp4(IPv4::new(
261                *source.ip(),
262                *destination.ip(),
263                source.port(),
264                destination.port(),
265            )),
266            (SocketAddr::V6(source), SocketAddr::V6(destination)) => Self::Tcp6(IPv6::new(
267                *source.ip(),
268                *destination.ip(),
269                source.port(),
270                destination.port(),
271            )),
272            _ => Self::Unknown,
273        }
274    }
275}
276
277impl From<IPv4> for Addresses {
278    fn from(addresses: IPv4) -> Self {
279        Self::Tcp4(addresses)
280    }
281}
282
283impl From<IPv6> for Addresses {
284    fn from(addresses: IPv6) -> Self {
285        Self::Tcp6(addresses)
286    }
287}
288
289impl fmt::Display for Header<'_> {
290    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
291        f.write_str(self.header.as_ref())
292    }
293}
294
295impl fmt::Display for Addresses {
296    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
297        match self {
298            Self::Unknown => f.write_str("PROXY UNKNOWN\r\n"),
299            Self::Tcp4(a) => write!(
300                f,
301                "PROXY TCP4 {} {} {} {}\r\n",
302                a.source_address, a.destination_address, a.source_port, a.destination_port
303            ),
304            Self::Tcp6(a) => write!(
305                f,
306                "PROXY TCP6 {} {} {} {}\r\n",
307                a.source_address, a.destination_address, a.source_port, a.destination_port
308            ),
309        }
310    }
311}