Coverage Report

Created: 2025-10-29 07:05

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/rust/registry/src/index.crates.io-1949cf8c6b5b557f/ppp-2.3.0/src/lib.rs
Line
Count
Source
1
//! A Proxy Protocol Parser written in Rust.
2
//! Supports both text and binary versions of the header protocol.
3
4
mod ip;
5
6
pub mod v1;
7
pub mod v2;
8
9
/// The canonical way to determine when a streamed header should be retried in a streaming context.
10
/// The protocol states that servers may choose to support partial headers or to close the connection if the header is not present all at once.
11
pub trait PartialResult {
12
    /// Tests whether this `Result` is successful or whether the error is terminal.
13
    /// A terminal error will not result in a success even with more bytes.
14
    /// Retrying with the same -- or more -- input will not change the result.
15
0
    fn is_complete(&self) -> bool {
16
0
        !self.is_incomplete()
17
0
    }
18
19
    /// Tests whether this `Result` is incomplete.
20
    /// An action that leads to an incomplete result may have a different result with more bytes.
21
    /// Retrying with the same input will not change the result.
22
    fn is_incomplete(&self) -> bool;
23
}
24
25
impl<'a, T, E: PartialResult> PartialResult for Result<T, E> {
26
0
    fn is_incomplete(&self) -> bool {
27
0
        match self {
28
0
            Ok(_) => false,
29
0
            Err(error) => error.is_incomplete(),
30
        }
31
0
    }
Unexecuted instantiation: <core::result::Result<ppp::v1::model::Header, ppp::v1::error::BinaryParseError> as ppp::PartialResult>::is_incomplete
Unexecuted instantiation: <core::result::Result<ppp::v2::model::Header, ppp::v2::error::ParseError> as ppp::PartialResult>::is_incomplete
32
}
33
34
impl<'a> PartialResult for v1::ParseError {
35
0
    fn is_incomplete(&self) -> bool {
36
0
        matches!(
37
0
            self,
38
            v1::ParseError::Partial
39
                | v1::ParseError::MissingPrefix
40
                | v1::ParseError::MissingProtocol
41
                | v1::ParseError::MissingSourceAddress
42
                | v1::ParseError::MissingDestinationAddress
43
                | v1::ParseError::MissingSourcePort
44
                | v1::ParseError::MissingDestinationPort
45
                | v1::ParseError::MissingNewLine
46
        )
47
0
    }
48
}
49
50
impl<'a> PartialResult for v1::BinaryParseError {
51
0
    fn is_incomplete(&self) -> bool {
52
0
        match self {
53
0
            v1::BinaryParseError::Parse(error) => error.is_incomplete(),
54
0
            _ => false,
55
        }
56
0
    }
57
}
58
59
impl<'a> PartialResult for v2::ParseError {
60
0
    fn is_incomplete(&self) -> bool {
61
0
        matches!(
62
0
            self,
63
            v2::ParseError::Incomplete(..) | v2::ParseError::Partial(..)
64
        )
65
0
    }
66
}
67
68
/// An enumeration of the supported header version's parse results.
69
/// Useful for parsing either version 1 or version 2 of the PROXY protocol.
70
///
71
/// ## Examples
72
/// ```rust
73
/// use ppp::{HeaderResult, PartialResult, v1, v2};
74
///
75
/// let input = "PROXY UNKNOWN\r\n";
76
/// let header = HeaderResult::parse(input.as_bytes());
77
///
78
/// assert_eq!(header, Ok(v1::Header::new(input, v1::Addresses::Unknown)).into());
79
/// ```
80
#[derive(Debug, PartialEq)]
81
#[must_use = "this `HeaderResult` may contain a V1 or V2 `Err` variant, which should be handled"]
82
pub enum HeaderResult<'a> {
83
    V1(Result<v1::Header<'a>, v1::BinaryParseError>),
84
    V2(Result<v2::Header<'a>, v2::ParseError>),
85
}
86
87
impl<'a> From<Result<v1::Header<'a>, v1::BinaryParseError>> for HeaderResult<'a> {
88
0
    fn from(result: Result<v1::Header<'a>, v1::BinaryParseError>) -> Self {
89
0
        HeaderResult::V1(result)
90
0
    }
91
}
92
93
impl<'a> From<Result<v2::Header<'a>, v2::ParseError>> for HeaderResult<'a> {
94
0
    fn from(result: Result<v2::Header<'a>, v2::ParseError>) -> Self {
95
0
        HeaderResult::V2(result)
96
0
    }
97
}
98
99
impl<'a> PartialResult for HeaderResult<'a> {
100
0
    fn is_incomplete(&self) -> bool {
101
0
        match self {
102
0
            Self::V1(result) => result.is_incomplete(),
103
0
            Self::V2(result) => result.is_incomplete(),
104
        }
105
0
    }
106
}
107
108
impl<'a> HeaderResult<'a> {
109
    /// Parses a PROXY protocol version 2 `Header`.
110
    /// If the input is not a valid version 2 `Header`, attempts to parse a version 1 `Header`.  
111
0
    pub fn parse(input: &'a [u8]) -> HeaderResult<'a> {
112
0
        let header = v2::Header::try_from(input);
113
114
0
        if header.is_complete() && header.is_err() {
115
0
            v1::Header::try_from(input).into()
116
        } else {
117
0
            header.into()
118
        }
119
0
    }
120
}