Skip to main content

proxy_protocol_rs/
config.rs

1// Copyright (C) 2025-2026 Michael S. Klishin and Contributors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt;
16use std::sync::Arc;
17use std::time::Duration;
18
19use crate::policy::{AcceptAll, ConnPolicy};
20use crate::validator::HeaderValidator;
21
22/// Configuration for the Proxy Protocol listener
23pub struct ProxyProtocolConfig {
24    /// Maximum time to wait for the complete PP header after accept;
25    /// default: 5 seconds
26    pub header_timeout: Duration,
27
28    /// Maximum buffer size for reading the PP header;
29    /// default: 4096 bytes
30    pub max_header_size: usize,
31
32    /// Maximum number of connections simultaneously reading PP headers;
33    /// default: 1024
34    pub max_pending_handshakes: usize,
35
36    /// Pre-read connection policy;
37    /// default: `AcceptAll`
38    pub policy: Arc<dyn ConnPolicy>,
39
40    /// Post-parse header validator;
41    /// default: `None`
42    pub validator: Option<Arc<dyn HeaderValidator>>,
43
44    /// Which protocol versions to accept;
45    /// default: `Both`
46    pub version: VersionPreference,
47}
48
49impl Default for ProxyProtocolConfig {
50    fn default() -> Self {
51        Self {
52            header_timeout: Duration::from_secs(5),
53            max_header_size: 4096,
54            max_pending_handshakes: 1024,
55            policy: Arc::new(AcceptAll),
56            validator: None,
57            version: VersionPreference::Both,
58        }
59    }
60}
61
62impl ProxyProtocolConfig {
63    pub fn builder() -> ConfigBuilder {
64        ConfigBuilder::default()
65    }
66}
67
68pub struct ConfigBuilder {
69    header_timeout: Duration,
70    max_header_size: usize,
71    max_pending_handshakes: usize,
72    policy: Arc<dyn ConnPolicy>,
73    validator: Option<Arc<dyn HeaderValidator>>,
74    version: VersionPreference,
75}
76
77impl Default for ConfigBuilder {
78    fn default() -> Self {
79        Self {
80            header_timeout: Duration::from_secs(5),
81            max_header_size: 4096,
82            max_pending_handshakes: 1024,
83            policy: Arc::new(AcceptAll),
84            validator: None,
85            version: VersionPreference::Both,
86        }
87    }
88}
89
90impl ConfigBuilder {
91    pub fn header_timeout(mut self, timeout: Duration) -> Self {
92        self.header_timeout = timeout;
93        self
94    }
95
96    pub fn max_header_size(mut self, size: usize) -> Self {
97        self.max_header_size = size;
98        self
99    }
100
101    pub fn max_pending_handshakes(mut self, limit: usize) -> Self {
102        self.max_pending_handshakes = limit;
103        self
104    }
105
106    pub fn policy(mut self, policy: impl ConnPolicy) -> Self {
107        self.policy = Arc::new(policy);
108        self
109    }
110
111    pub fn validator(mut self, validator: impl HeaderValidator) -> Self {
112        self.validator = Some(Arc::new(validator));
113        self
114    }
115
116    pub fn version(mut self, version: VersionPreference) -> Self {
117        self.version = version;
118        self
119    }
120
121    pub fn build(self) -> Result<ProxyProtocolConfig, ConfigError> {
122        if self.header_timeout.is_zero() {
123            return Err(ConfigError("header_timeout must be greater than zero"));
124        }
125        if self.max_header_size == 0 {
126            return Err(ConfigError("max_header_size must be greater than zero"));
127        }
128        if self.max_pending_handshakes == 0 {
129            return Err(ConfigError(
130                "max_pending_handshakes must be greater than zero",
131            ));
132        }
133        Ok(ProxyProtocolConfig {
134            header_timeout: self.header_timeout,
135            max_header_size: self.max_header_size,
136            max_pending_handshakes: self.max_pending_handshakes,
137            policy: self.policy,
138            validator: self.validator,
139            version: self.version,
140        })
141    }
142}
143
144#[derive(Debug, Clone, PartialEq, Eq)]
145pub struct ConfigError(pub &'static str);
146
147impl fmt::Display for ConfigError {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        f.write_str(self.0)
150    }
151}
152
153impl std::error::Error for ConfigError {}
154
155#[derive(Debug, Clone, Copy, PartialEq, Eq)]
156#[non_exhaustive]
157pub enum VersionPreference {
158    Both,
159    V1Only,
160    /// Accept only v2 headers. V2 supports CRC32c integrity checks and
161    /// binary TLVs, making this the stricter option for hardened deployments.
162    V2Only,
163}