Skip to main content

rama_haproxy/protocol/v1/
mod.rs

1//! Version 1 of the HAProxy protocol (text version).
2//!
3//! See the vendored specification at
4//! `rama-haproxy/specifications/proxy-protocol.txt`, section 2.1
5//! (upstream: <https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt>).
6
7mod error;
8mod model;
9
10pub use crate::protocol::ip::{IPv4, IPv6};
11pub use error::{BinaryParseError, ParseError};
12pub use model::{Addresses, Header, SEPARATOR, TCP4, TCP6, UNKNOWN};
13pub use model::{PROTOCOL_PREFIX, PROTOCOL_SUFFIX};
14use std::borrow::Cow;
15use std::net::{AddrParseError, Ipv4Addr, Ipv6Addr};
16use std::str::{FromStr, from_utf8};
17
18const ZERO: &str = "0";
19const NEWLINE: &str = "\n";
20const CARRIAGE_RETURN: char = '\r';
21
22/// The maximum length of a header in bytes.
23const MAX_LENGTH: usize = 107;
24/// The total number of parts in the header.
25const PARTS: usize = 7;
26
27/// Parses a text PROXY protocol header.
28/// The given string is expected to only include the header and to end in \r\n.
29fn parse_header(header: &str) -> Result<Header<'_>, ParseError> {
30    if header.is_empty() {
31        return Err(ParseError::MissingPrefix);
32    } else if header.len() > MAX_LENGTH {
33        return Err(ParseError::HeaderTooLong);
34    }
35
36    let mut iterator = header
37        .splitn(PARTS, [SEPARATOR, CARRIAGE_RETURN])
38        .peekable();
39
40    let prefix = iterator.next().ok_or(ParseError::MissingPrefix)?;
41
42    if !prefix.is_empty() && PROTOCOL_PREFIX.starts_with(prefix) && header.ends_with(prefix) {
43        return Err(ParseError::Partial);
44    } else if prefix != PROTOCOL_PREFIX {
45        return Err(ParseError::InvalidPrefix);
46    }
47
48    let addresses = match iterator.next() {
49        Some(TCP4) => {
50            let (source_address, destination_address, source_port, destination_port) =
51                parse_addresses::<Ipv4Addr, _>(&mut iterator)?;
52
53            Addresses::Tcp4(IPv4 {
54                source_address,
55                source_port,
56                destination_address,
57                destination_port,
58            })
59        }
60        Some(TCP6) => {
61            let (source_address, destination_address, source_port, destination_port) =
62                parse_addresses::<Ipv6Addr, _>(&mut iterator)?;
63
64            Addresses::Tcp6(IPv6 {
65                source_address,
66                source_port,
67                destination_address,
68                destination_port,
69            })
70        }
71        Some(UNKNOWN) => {
72            while iterator.next_if(|&s| s != NEWLINE).is_some() {}
73
74            Addresses::Unknown
75        }
76        Some(protocol) if protocol.is_empty() && iterator.peek().is_none() => {
77            return Err(ParseError::MissingProtocol);
78        }
79        Some(protocol)
80            if !protocol.is_empty()
81                && header.ends_with(protocol)
82                && (TCP4.starts_with(protocol) || UNKNOWN.starts_with(protocol)) =>
83        {
84            return Err(ParseError::Partial);
85        }
86        Some(_) => return Err(ParseError::InvalidProtocol),
87        None => return Err(ParseError::MissingProtocol),
88    };
89
90    let newline = iterator
91        .next()
92        .filter(|s| !s.is_empty())
93        .ok_or(ParseError::MissingNewLine)?;
94
95    if newline != NEWLINE {
96        return Err(ParseError::InvalidSuffix);
97    }
98
99    Ok(Header {
100        header: Cow::Borrowed(header),
101        addresses,
102    })
103}
104
105/// Parses the addresses and ports from a PROXY protocol header for IPv4 and IPv6.
106fn parse_addresses<'a, T: FromStr<Err = AddrParseError>, I: Iterator<Item = &'a str>>(
107    iterator: &mut I,
108) -> Result<(T, T, u16, u16), ParseError> {
109    let source_address = iterator.next().ok_or(ParseError::MissingSourceAddress)?;
110    let destination_address = iterator
111        .next()
112        .ok_or(ParseError::MissingDestinationAddress)?;
113    let source_port = iterator.next().ok_or(ParseError::MissingSourcePort)?;
114    let destination_port = iterator.next().ok_or(ParseError::MissingDestinationPort)?;
115
116    let source_address = source_address
117        .parse::<T>()
118        .map_err(ParseError::InvalidSourceAddress)?;
119    let destination_address = destination_address
120        .parse::<T>()
121        .map_err(ParseError::InvalidDestinationAddress)?;
122
123    if source_port.starts_with(ZERO) && source_port != ZERO {
124        return Err(ParseError::InvalidSourcePort(None));
125    }
126
127    let source_port = source_port
128        .parse::<u16>()
129        .map_err(|e| ParseError::InvalidSourcePort(Some(e)))?;
130
131    if destination_port.starts_with(ZERO) && destination_port != ZERO {
132        return Err(ParseError::InvalidDestinationPort(None));
133    }
134
135    let destination_port = destination_port
136        .parse::<u16>()
137        .map_err(|e| ParseError::InvalidDestinationPort(Some(e)))?;
138
139    Ok((
140        source_address,
141        destination_address,
142        source_port,
143        destination_port,
144    ))
145}
146
147impl<'a> TryFrom<&'a str> for Header<'a> {
148    type Error = ParseError;
149
150    fn try_from(input: &'a str) -> Result<Self, Self::Error> {
151        let length = match input.find(CARRIAGE_RETURN) {
152            Some(suffix) => {
153                let end = suffix + PROTOCOL_SUFFIX.len();
154                if end > input.len() {
155                    // we found `\r` but the following `\n` is not yet available:
156                    // signal the caller this is a partial header rather than panicking
157                    // on an out-of-bounds slice.
158                    return Err(ParseError::Partial);
159                }
160                end
161            }
162            None if input.len() >= MAX_LENGTH => return Err(ParseError::HeaderTooLong),
163            None => input.len(),
164        };
165
166        parse_header(&input[..length])
167    }
168}
169
170impl<'a> TryFrom<&'a [u8]> for Header<'a> {
171    type Error = BinaryParseError;
172
173    fn try_from(input: &'a [u8]) -> Result<Self, Self::Error> {
174        let length = match input.iter().position(|&c| CARRIAGE_RETURN == (c as char)) {
175            Some(suffix) => {
176                let end = suffix + PROTOCOL_SUFFIX.len();
177                if end > input.len() {
178                    // we found `\r` but the following `\n` is not yet available:
179                    // signal the caller this is a partial header rather than panicking
180                    // on an out-of-bounds slice.
181                    return Err(ParseError::Partial.into());
182                }
183                end
184            }
185            None if input.len() >= MAX_LENGTH => return Err(ParseError::HeaderTooLong.into()),
186            None => input.len(),
187        };
188        let header = from_utf8(&input[..length])?;
189
190        parse_header(header).map_err(BinaryParseError::Parse)
191    }
192}
193
194impl FromStr for Addresses {
195    type Err = ParseError;
196
197    fn from_str(s: &str) -> Result<Self, Self::Err> {
198        Ok(Header::try_from(s)?.addresses)
199    }
200}
201
202impl FromStr for Header<'static> {
203    type Err = ParseError;
204
205    fn from_str(s: &str) -> Result<Self, Self::Err> {
206        Ok(Header::try_from(s)?.to_owned())
207    }
208}
209
210#[cfg(test)]
211mod tests {
212    use super::*;
213
214    #[test]
215    #[expect(invalid_from_utf8)]
216    fn bytes_invalid_utf8() {
217        let text = b"Hello \xF0\x90\x80World\r\n";
218
219        assert_eq!(
220            Header::try_from(&text[..]).unwrap_err(),
221            BinaryParseError::InvalidUtf8(from_utf8(text).unwrap_err())
222        );
223    }
224
225    #[test]
226    fn exact_tcp4() {
227        let ip: Ipv4Addr = "255.255.255.255".parse().unwrap();
228        let port = 65535;
229        let text = "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n";
230        let expected = Header::new(text, Addresses::new_tcp4(ip, ip, port, port));
231
232        assert_eq!(Header::try_from(text), Ok(expected.to_owned()));
233        assert_eq!(Header::try_from(text.as_bytes()), Ok(expected));
234    }
235
236    #[test]
237    fn valid_tcp4() {
238        let ip: Ipv4Addr = "255.255.255.255".parse().unwrap();
239        let port = 65535;
240        let text = "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\nFoobar";
241        let expected = Header::new(
242            "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n",
243            Addresses::new_tcp4(ip, ip, port, port),
244        );
245
246        assert_eq!(Header::try_from(text), Ok(expected.to_owned()));
247        assert_eq!(Header::try_from(text.as_bytes()), Ok(expected));
248    }
249
250    #[test]
251    fn parse_partial() {
252        let text = "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535";
253
254        assert_eq!(
255            Header::try_from(text).unwrap_err(),
256            ParseError::MissingNewLine
257        );
258        assert_eq!(
259            Header::try_from(text.as_bytes()).unwrap_err(),
260            ParseError::MissingNewLine.into()
261        );
262    }
263
264    #[test]
265    fn parse_partial_newline() {
266        // A trailing `\r` whose `\n` has not arrived yet is reported as a partial
267        // header (retry with more bytes) rather than a hard error: rama defers
268        // both the missing-newline and the prefix check until the line is
269        // complete. Upstream ppp later clamped this case to MissingNewLine /
270        // InvalidPrefix; rama's streaming `Partial` is intentional.
271        for text in [
272            "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535\r",
273            "abc\r",
274        ] {
275            assert_eq!(Header::try_from(text).unwrap_err(), ParseError::Partial);
276            assert_eq!(
277                Header::try_from(text.as_bytes()).unwrap_err(),
278                ParseError::Partial.into()
279            );
280        }
281    }
282
283    #[test]
284    fn parse_tcp4_invalid() {
285        let text = "PROXY TCP4 255.255.255.255 256.255.255.255 65535 65535\r\n";
286
287        assert_eq!(
288            Header::try_from(text),
289            Err(ParseError::InvalidDestinationAddress(
290                "".parse::<Ipv4Addr>().unwrap_err()
291            ))
292        );
293        assert_eq!(
294            Header::try_from(text.as_bytes()),
295            Err(ParseError::InvalidDestinationAddress("".parse::<Ipv4Addr>().unwrap_err()).into())
296        );
297    }
298
299    #[test]
300    fn parse_tcp4_leading_zeroes() {
301        let text = "PROXY TCP4 255.0255.255.255 255.255.255.255 65535 65535\r\n";
302
303        assert_eq!(
304            Header::try_from(text),
305            Err(ParseError::InvalidSourceAddress(
306                "".parse::<Ipv4Addr>().unwrap_err()
307            ))
308        );
309        assert_eq!(
310            Header::try_from(text.as_bytes()),
311            Err(ParseError::InvalidSourceAddress("".parse::<Ipv4Addr>().unwrap_err()).into())
312        );
313    }
314
315    #[test]
316    fn parse_unknown_connection() {
317        let text = "PROXY UNKNOWN\r\nTwo";
318
319        assert_eq!(
320            Header::try_from(text),
321            Ok(Header::new("PROXY UNKNOWN\r\n", Addresses::default()))
322        );
323        assert_eq!(
324            Header::try_from(text.as_bytes()),
325            Ok(Header::new("PROXY UNKNOWN\r\n", Addresses::default()))
326        );
327    }
328
329    #[test]
330    fn valid_tcp6() {
331        let ip: Ipv6Addr = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff".parse().unwrap();
332        let port = 65535;
333        let text = "PROXY TCP6 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\nHi!";
334        let expected = Header::new(
335            "PROXY TCP6 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n",
336            Addresses::new_tcp6(ip, ip, port, port),
337        );
338
339        assert_eq!(Header::try_from(text), Ok(expected.to_owned()));
340        assert_eq!(Header::try_from(text.as_bytes()), Ok(expected));
341    }
342
343    #[test]
344    fn valid_tcp6_short() {
345        let ip: Ipv6Addr = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff".parse().unwrap();
346        let port = 65535;
347        let short_ip = "::1".parse().unwrap();
348        let text = "PROXY TCP6 ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\nHi!";
349        let expected = Header::new(
350            "PROXY TCP6 ::1 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n",
351            Addresses::new_tcp6(short_ip, ip, port, port),
352        );
353
354        assert_eq!(Header::try_from(text), Ok(expected.to_owned()));
355        assert_eq!(Header::try_from(text.as_bytes()), Ok(expected));
356    }
357
358    #[test]
359    fn parse_tcp6_invalid() {
360        let text = "PROXY TCP6 ffff:gggg:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n";
361
362        assert_eq!(
363            Header::try_from(text),
364            Err(ParseError::InvalidSourceAddress(
365                "".parse::<Ipv6Addr>().unwrap_err()
366            ))
367        );
368        assert_eq!(
369            Header::try_from(text.as_bytes()),
370            Err(ParseError::InvalidSourceAddress("".parse::<Ipv6Addr>().unwrap_err()).into())
371        );
372    }
373
374    #[test]
375    fn parse_tcp6_leading_zeroes() {
376        let text = "PROXY TCP6 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:0ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n";
377
378        assert_eq!(
379            Header::try_from(text),
380            Err(ParseError::InvalidDestinationAddress(
381                "".parse::<Ipv6Addr>().unwrap_err()
382            ))
383        );
384        assert_eq!(
385            Header::try_from(text.as_bytes()),
386            Err(ParseError::InvalidDestinationAddress("".parse::<Ipv6Addr>().unwrap_err()).into())
387        );
388    }
389
390    #[test]
391    fn parse_tcp6_shortened_connection() {
392        let ip: Ipv6Addr = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff".parse().unwrap();
393        let short_ip = "ffff::ffff".parse().unwrap();
394        let port = 65535;
395        let text = "PROXY TCP6 ffff::ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n";
396        let expected = Header::new(text, Addresses::new_tcp6(short_ip, ip, port, port));
397
398        assert_eq!(Header::try_from(text), Ok(expected.to_owned()));
399        assert_eq!(Header::try_from(text.as_bytes()), Ok(expected));
400    }
401
402    #[test]
403    fn parse_tcp6_single_zero() {
404        let ip: Ipv6Addr = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff".parse().unwrap();
405        let short_ip = "ffff:ffff:ffff:ffff::ffff:ffff:ffff".parse().unwrap();
406        let port = 65535;
407        let text = "PROXY TCP6 ffff:ffff:ffff:ffff::ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n";
408        let expected = Header::new(text, Addresses::new_tcp6(short_ip, ip, port, port));
409
410        assert_eq!(Header::try_from(text), Ok(expected.to_owned()));
411        assert_eq!(Header::try_from(text.as_bytes()), Ok(expected));
412    }
413
414    #[test]
415    fn parse_tcp6_wildcard() {
416        let ip: Ipv6Addr = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff".parse().unwrap();
417        let short_ip = "::".parse().unwrap();
418        let port = 65535;
419        let text = "PROXY TCP6 :: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n";
420        let expected = Header::new(text, Addresses::new_tcp6(short_ip, ip, port, port));
421
422        assert_eq!(Header::try_from(text), Ok(expected.to_owned()));
423        assert_eq!(Header::try_from(text.as_bytes()), Ok(expected));
424    }
425
426    #[test]
427    fn parse_tcp6_implied() {
428        let ip: Ipv6Addr = "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff".parse().unwrap();
429        let short_ip = "ffff::".parse().unwrap();
430        let port = 65535;
431        let text = "PROXY TCP6 ffff:: ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n";
432        let expected = Header::new(text, Addresses::new_tcp6(short_ip, ip, port, port));
433
434        assert_eq!(Header::try_from(text), Ok(expected.to_owned()));
435        assert_eq!(Header::try_from(text.as_bytes()), Ok(expected));
436    }
437
438    #[test]
439    fn parse_tcp6_over_shortened() {
440        let text = "PROXY TCP6 ffff::ffff:ffff:ffff:ffff::ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n";
441
442        assert_eq!(
443            Header::try_from(text),
444            Err(ParseError::InvalidSourceAddress(
445                "".parse::<Ipv6Addr>().unwrap_err()
446            ))
447        );
448        assert_eq!(
449            Header::try_from(text.as_bytes()),
450            Err(ParseError::InvalidSourceAddress("".parse::<Ipv6Addr>().unwrap_err()).into())
451        );
452    }
453
454    #[test]
455    fn parse_worst_case() {
456        let text = "PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535\r\n";
457        let expected = Header::new(text, Addresses::Unknown);
458
459        assert_eq!(Header::try_from(text), Ok(expected.to_owned()));
460        assert_eq!(Header::try_from(text.as_bytes()), Ok(expected));
461    }
462
463    #[test]
464    fn parse_leading_zeroes_in_source_port() {
465        let text = "PROXY TCP4 255.255.255.255 255.255.255.255 05535 65535\r\n";
466
467        assert_eq!(
468            Header::try_from(text),
469            Err(ParseError::InvalidSourcePort(None))
470        );
471        assert_eq!(
472            Header::try_from(text.as_bytes()),
473            Err(ParseError::InvalidSourcePort(None).into())
474        );
475    }
476
477    #[test]
478    fn parse_leading_zeroes_in_destination_port() {
479        let text = "PROXY TCP4 255.255.255.255 255.255.255.255 65535 05535\r\n";
480
481        assert_eq!(
482            Header::try_from(text),
483            Err(ParseError::InvalidDestinationPort(None))
484        );
485        assert_eq!(
486            Header::try_from(text.as_bytes()),
487            Err(ParseError::InvalidDestinationPort(None).into())
488        );
489    }
490
491    #[test]
492    fn parse_source_port_too_large() {
493        let text = "PROXY TCP6 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65536 65535\r\n";
494
495        assert_eq!(
496            Header::try_from(text),
497            Err(ParseError::InvalidSourcePort(Some(
498                "65536".parse::<u16>().unwrap_err()
499            )))
500        );
501        assert_eq!(
502            Header::try_from(text.as_bytes()),
503            Err(ParseError::InvalidSourcePort(Some("65536".parse::<u16>().unwrap_err())).into())
504        );
505    }
506
507    #[test]
508    fn parse_destination_port_too_large() {
509        let text = "PROXY TCP6 ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65536\r\n";
510
511        assert_eq!(
512            Header::try_from(text),
513            Err(ParseError::InvalidDestinationPort(Some(
514                "65536".parse::<u16>().unwrap_err()
515            )))
516        );
517        assert_eq!(
518            Header::try_from(text.as_bytes()),
519            Err(
520                ParseError::InvalidDestinationPort(Some("65536".parse::<u16>().unwrap_err()))
521                    .into()
522            )
523        );
524    }
525
526    #[test]
527    fn parse_lowercase_proxy() {
528        let text = "proxy UNKNOWN\r\n";
529
530        assert_eq!(Header::try_from(text), Err(ParseError::InvalidPrefix));
531        assert_eq!(
532            Header::try_from(text.as_bytes()),
533            Err(ParseError::InvalidPrefix.into())
534        );
535    }
536
537    #[test]
538    fn parse_lowercase_protocol_family() {
539        let text = "PROXY tcp4\r\n";
540
541        assert_eq!(Header::try_from(text), Err(ParseError::InvalidProtocol));
542        assert_eq!(
543            Header::try_from(text.as_bytes()),
544            Err(ParseError::InvalidProtocol.into())
545        );
546    }
547
548    #[test]
549    fn parse_too_long() {
550        let text = "PROXY UNKNOWN ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff 65535 65535  \r\n";
551
552        assert_eq!(Header::try_from(text), Err(ParseError::HeaderTooLong));
553        assert_eq!(
554            Header::try_from(text.as_bytes()),
555            Err(ParseError::HeaderTooLong.into())
556        );
557    }
558
559    #[test]
560    fn parse_more_than_one_space() {
561        let text = "PROXY  TCP4 255.255.255.255 255.255.255.255 65535 65535\r\n";
562
563        assert_eq!(Header::try_from(text), Err(ParseError::InvalidProtocol));
564        assert_eq!(
565            Header::try_from(text.as_bytes()),
566            Err(ParseError::InvalidProtocol.into())
567        );
568    }
569
570    #[test]
571    fn parse_more_than_one_space_source_address() {
572        let text = "PROXY TCP4  255.255.255.255 255.255.255.255 65535 65535\r\n";
573
574        assert_eq!(
575            Header::try_from(text),
576            Err(ParseError::InvalidSourceAddress(
577                "".parse::<Ipv4Addr>().unwrap_err()
578            ))
579        );
580        assert_eq!(
581            Header::try_from(text.as_bytes()),
582            Err(ParseError::InvalidSourceAddress("".parse::<Ipv4Addr>().unwrap_err()).into())
583        );
584    }
585
586    #[test]
587    fn parse_more_than_one_space_destination_address() {
588        let text = "PROXY TCP4 255.255.255.255  255.255.255.255 65535 65535\r\n";
589
590        assert_eq!(
591            Header::try_from(text),
592            Err(ParseError::InvalidDestinationAddress(
593                "".parse::<Ipv4Addr>().unwrap_err()
594            ))
595        );
596        assert_eq!(
597            Header::try_from(text.as_bytes()),
598            Err(ParseError::InvalidDestinationAddress("".parse::<Ipv4Addr>().unwrap_err()).into())
599        );
600    }
601
602    #[test]
603    fn parse_more_than_one_space_source_port() {
604        let text = "PROXY TCP4 255.255.255.255 255.255.255.255  65535 65535\r\n";
605
606        assert_eq!(
607            Header::try_from(text),
608            Err(ParseError::InvalidSourcePort(Some(
609                "".parse::<u16>().unwrap_err()
610            )))
611        );
612        assert_eq!(
613            Header::try_from(text.as_bytes()),
614            Err(ParseError::InvalidSourcePort(Some("".parse::<u16>().unwrap_err())).into())
615        );
616    }
617
618    #[test]
619    fn parse_more_than_one_space_destination_port() {
620        let text = "PROXY TCP4 255.255.255.255 255.255.255.255 65535  65535\r\n";
621
622        assert_eq!(
623            Header::try_from(text),
624            Err(ParseError::InvalidDestinationPort(Some(
625                "".parse::<u16>().unwrap_err()
626            )))
627        );
628        assert_eq!(
629            Header::try_from(text.as_bytes()),
630            Err(ParseError::InvalidDestinationPort(Some("".parse::<u16>().unwrap_err())).into())
631        );
632    }
633
634    #[test]
635    fn parse_more_than_one_space_end() {
636        let text = "PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535 \r\n";
637
638        assert_eq!(Header::try_from(text), Err(ParseError::InvalidSuffix));
639        assert_eq!(
640            Header::try_from(text.as_bytes()),
641            Err(ParseError::InvalidSuffix.into())
642        );
643    }
644
645    #[test]
646    fn parse_partial_prefix() {
647        let text = "PROX\r\n";
648
649        assert_eq!(Header::try_from(text), Err(ParseError::InvalidPrefix));
650        assert_eq!(
651            Header::try_from(text.as_bytes()),
652            Err(ParseError::InvalidPrefix.into())
653        );
654    }
655
656    #[test]
657    fn parse_empty_newline() {
658        let text = "\r\n";
659
660        assert_eq!(Header::try_from(text), Err(ParseError::InvalidPrefix));
661        assert_eq!(
662            Header::try_from(text.as_bytes()),
663            Err(ParseError::InvalidPrefix.into())
664        );
665    }
666
667    #[test]
668    fn parse_partial_prefix_missing_newline() {
669        let text = "PROX";
670
671        assert_eq!(Header::try_from(text), Err(ParseError::Partial));
672        assert_eq!(
673            Header::try_from(text.as_bytes()),
674            Err(ParseError::Partial.into())
675        );
676    }
677
678    #[test]
679    fn parse_partial_protocol_missing_newline() {
680        let text = "PROXY UNKN";
681
682        assert_eq!(Header::try_from(text), Err(ParseError::Partial));
683        assert_eq!(
684            Header::try_from(text.as_bytes()),
685            Err(ParseError::Partial.into())
686        );
687    }
688
689    #[test]
690    fn parse_partial_protocol_with_newline() {
691        let text = "PROXY UNKN\r\n";
692
693        assert_eq!(Header::try_from(text), Err(ParseError::InvalidProtocol));
694        assert_eq!(
695            Header::try_from(text.as_bytes()),
696            Err(ParseError::InvalidProtocol.into())
697        );
698    }
699
700    #[test]
701    fn parse_empty_protocol_with_newline() {
702        let text = "PROXY \r\n";
703
704        assert_eq!(Header::try_from(text), Err(ParseError::InvalidProtocol));
705        assert_eq!(
706            Header::try_from(text.as_bytes()),
707            Err(ParseError::InvalidProtocol.into())
708        );
709    }
710
711    #[test]
712    fn parse_empty() {
713        let text = "";
714
715        assert_eq!(Header::try_from(text), Err(ParseError::MissingPrefix));
716        assert_eq!(
717            Header::try_from(text.as_bytes()),
718            Err(ParseError::MissingPrefix.into())
719        );
720    }
721
722    #[test]
723    fn parse_no_new_line() {
724        let text = "PROXY TCP4 127.0.0.1 192.168.1.1 80 443\r\t";
725
726        assert_eq!(Header::try_from(text), Err(ParseError::InvalidSuffix));
727        assert_eq!(
728            Header::try_from(text.as_bytes()),
729            Err(ParseError::InvalidSuffix.into())
730        );
731    }
732
733    #[test]
734    fn parse_trailing_cr_only_str() {
735        // Regression: previously this caused an out-of-bounds slice panic.
736        let text = "PROXY TCP4 127.0.0.1 192.168.1.1 80 443\r";
737
738        assert_eq!(Header::try_from(text), Err(ParseError::Partial));
739    }
740
741    #[test]
742    fn parse_trailing_cr_only_bytes() {
743        // Regression: previously this caused an out-of-bounds slice panic.
744        let bytes = b"PROXY TCP4 127.0.0.1 192.168.1.1 80 443\r";
745
746        assert_eq!(
747            Header::try_from(&bytes[..]),
748            Err(ParseError::Partial.into())
749        );
750    }
751
752    #[test]
753    fn parse_invalid_prefix_missing_newline() {
754        let text = "PRAX";
755
756        assert_eq!(Header::try_from(text), Err(ParseError::InvalidPrefix));
757        assert_eq!(
758            Header::try_from(text.as_bytes()),
759            Err(ParseError::InvalidPrefix.into())
760        );
761    }
762}