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}