1use std::fmt;
4
5use smallvec::SmallVec;
6
7pub type HeaderEntry = (Box<str>, Box<str>);
10
11pub type HeaderList = SmallVec<[HeaderEntry; 16]>;
14
15#[derive(Debug, Clone)]
17pub struct Http1Config {
18 pub max_request_line_size: usize,
20 pub max_header_size: usize,
22 pub max_header_count: usize,
24 pub max_body_size: usize,
26 pub keep_alive: bool,
28 pub max_header_name_len: usize,
30 pub max_header_value_len: usize,
32 pub idle_timeout_ms: u64,
34 pub max_buffer_size: usize,
36}
37
38impl Http1Config {
39 #[inline]
41 pub fn new() -> Self {
42 Self {
43 max_request_line_size: 8192,
44 max_header_size: 8192,
45 max_header_count: 256,
46 max_body_size: 1_048_576,
47 keep_alive: true,
48 max_header_name_len: 64,
49 max_header_value_len: 8192,
50 idle_timeout_ms: 30_000,
51 max_buffer_size: 65_536,
52 }
53 }
54
55 #[inline]
57 pub fn with_max_header_size(mut self, size: usize) -> Self {
58 self.max_header_size = size;
59 self
60 }
61
62 #[inline]
64 pub fn with_max_body_size(mut self, size: usize) -> Self {
65 self.max_body_size = size;
66 self
67 }
68
69 #[inline]
71 pub fn with_max_header_count(mut self, n: usize) -> Self {
72 self.max_header_count = n;
73 self
74 }
75
76 #[inline]
78 pub fn with_idle_timeout_ms(mut self, ms: u64) -> Self {
79 self.idle_timeout_ms = ms;
80 self
81 }
82
83 #[inline]
85 pub fn with_max_buffer_size(mut self, size: usize) -> Self {
86 self.max_buffer_size = size;
87 self
88 }
89}
90
91impl Default for Http1Config {
92 fn default() -> Self {
93 Self::new()
94 }
95}
96
97#[derive(Debug, Clone, PartialEq, Eq)]
99pub enum Http1Error {
100 SyntaxError(String),
102 HeaderTooLarge,
104 RequestLineTooLong,
106 TooManyHeaders,
108 BodyTooLarge,
110 MissingHost,
112 UnsupportedMethod(String),
114 UnsupportedVersion(String),
116 SmugglingDetected(String),
118 ChunkedError(String),
120 ProtocolInconsistency(String),
122 ConnectionClosed,
124 NeedMoreData,
126 IdleTimeout,
128 BufferOverflow,
130 Internal(String),
132}
133
134impl fmt::Display for Http1Error {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 match self {
137 Self::SyntaxError(m) => write!(f, "HTTP 语法错误: {m}"),
138 Self::HeaderTooLarge => write!(f, "HTTP 头部过大"),
139 Self::RequestLineTooLong => write!(f, "HTTP 请求行过长"),
140 Self::TooManyHeaders => write!(f, "HTTP 头部数量超限"),
141 Self::BodyTooLarge => write!(f, "HTTP 请求体过大"),
142 Self::MissingHost => write!(f, "HTTP 缺少 Host 头"),
143 Self::UnsupportedMethod(m) => write!(f, "HTTP 方法不支持: {m}"),
144 Self::UnsupportedVersion(v) => write!(f, "HTTP 版本不支持: {v}"),
145 Self::SmugglingDetected(m) => write!(f, "HTTP 请求走私: {m}"),
146 Self::ChunkedError(m) => write!(f, "HTTP 分块编码错误: {m}"),
147 Self::ProtocolInconsistency(m) => write!(f, "HTTP 协议不一致: {m}"),
148 Self::ConnectionClosed => write!(f, "HTTP 连接已关闭"),
149 Self::NeedMoreData => write!(f, "HTTP 数据不足"),
150 Self::IdleTimeout => write!(f, "HTTP 空闲超时 (Slowloris 防护)"),
151 Self::BufferOverflow => write!(f, "HTTP 缓冲区溢出"),
152 Self::Internal(m) => write!(f, "HTTP 内部错误: {m}"),
153 }
154 }
155}
156
157impl std::error::Error for Http1Error {}
158
159#[derive(Debug, Clone, PartialEq, Eq)]
161pub struct HttpRequestLine {
162 pub method: Box<str>,
164 pub target: Box<str>,
166 pub version: Box<str>,
168}
169
170#[derive(Debug, Clone)]
172pub struct HttpRequest {
173 pub line: HttpRequestLine,
175 pub headers: HeaderList,
177 pub body: Vec<u8>,
179 pub content_length: Option<u64>,
181 pub chunked: bool,
183 pub keep_alive: bool,
185}
186
187impl HttpRequest {
188 #[inline]
194 pub fn new(method: Box<str>, target: Box<str>, version: Box<str>) -> Self {
195 let keep_alive = &*version != "HTTP/1.0";
196 Self {
197 line: HttpRequestLine {
198 method,
199 target,
200 version,
201 },
202 headers: SmallVec::new(),
203 body: Vec::new(),
204 content_length: None,
205 chunked: false,
206 keep_alive,
207 }
208 }
209
210 #[inline]
212 pub fn get_header<'a>(&'a self, name: &str) -> Option<&'a str> {
213 let name = name.to_ascii_lowercase();
214 self.headers
215 .iter()
216 .find(|(k, _)| k.as_ref() == name.as_str())
217 .map(|(_, v)| v.as_ref())
218 }
219
220 #[inline]
222 pub fn host(&self) -> Option<&str> {
223 self.get_header("host")
224 }
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230
231 #[test]
232 fn test_config_default() {
233 let c = Http1Config::new();
234 assert_eq!(c.max_header_size, 8192);
235 assert_eq!(c.max_body_size, 1_048_576);
236 assert_eq!(c.max_header_count, 256);
237 assert!(c.keep_alive);
238 }
239
240 #[test]
241 fn test_config_custom() {
242 let c = Http1Config::new()
243 .with_max_header_size(4096)
244 .with_max_body_size(524_288)
245 .with_max_header_count(128);
246 assert_eq!(c.max_header_size, 4096);
247 assert_eq!(c.max_body_size, 524_288);
248 assert_eq!(c.max_header_count, 128);
249 }
250
251 #[test]
252 fn test_error_display() {
253 assert_eq!(
254 Http1Error::MissingHost.to_string(),
255 "HTTP 缺少 Host 头"
256 );
257 assert!(matches!(Http1Error::SmugglingDetected("CL.TE".into()), Http1Error::SmugglingDetected(_)));
258 }
259
260 #[test]
261 fn test_request_get_header() {
262 let mut req = HttpRequest::new("GET".into(), "/".into(), "HTTP/1.1".into());
263 req.headers.push(("host".into(), "example.com".into()));
264 req.headers.push(("content-length".into(), "0".into()));
265 assert_eq!(req.get_header("host"), Some("example.com"));
266 assert_eq!(req.get_header("Content-Length"), Some("0"));
267 assert_eq!(req.get_header("missing"), None);
268 }
269
270 #[test]
271 fn test_http1_error_all_variants_display() {
272 let errors = vec![
273 Http1Error::SyntaxError("test".into()),
274 Http1Error::HeaderTooLarge,
275 Http1Error::RequestLineTooLong,
276 Http1Error::TooManyHeaders,
277 Http1Error::BodyTooLarge,
278 Http1Error::MissingHost,
279 Http1Error::UnsupportedMethod("FOO".into()),
280 Http1Error::UnsupportedVersion("HTTP/3.0".into()),
281 Http1Error::SmugglingDetected("test".into()),
282 Http1Error::ChunkedError("test".into()),
283 Http1Error::ProtocolInconsistency("test".into()),
284 Http1Error::ConnectionClosed,
285 Http1Error::NeedMoreData,
286 Http1Error::Internal("test".into()),
287 ];
288 for e in errors {
289 let s = e.to_string();
290 assert!(!s.is_empty());
291 }
292 }
293
294 #[test]
295 fn test_http_request_new() {
296 let req = HttpRequest::new("POST".into(), "/api".into(), "HTTP/1.1".into());
297 assert_eq!(req.line.method.as_ref(), "POST");
298 assert_eq!(req.line.target.as_ref(), "/api");
299 assert_eq!(req.line.version.as_ref(), "HTTP/1.1");
300 assert!(req.headers.is_empty());
301 assert!(req.body.is_empty());
302 assert_eq!(req.content_length, None);
303 assert!(!req.chunked);
304 assert!(req.keep_alive);
305 }
306
307 #[test]
308 fn test_http_request_new_http10_default_close() {
309 let req = HttpRequest::new("GET".into(), "/".into(), "HTTP/1.0".into());
311 assert!(!req.keep_alive, "HTTP/1.0 默认 keep_alive 必须为 false");
312 }
313
314 #[test]
315 fn test_http_request_host() {
316 let mut req = HttpRequest::new("GET".into(), "/".into(), "HTTP/1.1".into());
317 assert_eq!(req.host(), None);
318 req.headers.push(("host".into(), "example.com".into()));
319 assert_eq!(req.host(), Some("example.com"));
320 }
321
322 #[test]
323 fn test_http_request_line_clone() {
324 let line = HttpRequestLine {
325 method: "GET".into(),
326 target: "/".into(),
327 version: "HTTP/1.1".into(),
328 };
329 let line2 = line.clone();
330 assert_eq!(line, line2);
331 assert_eq!(format!("{:?}", line), format!("{:?}", line2));
332 }
333
334 #[test]
335 fn test_config_default_trait() {
336 let c1 = Http1Config::new();
337 let c2 = Http1Config::default();
338 assert_eq!(c1.max_header_size, c2.max_header_size);
339 assert_eq!(c1.max_body_size, c2.max_body_size);
340 }
341
342 #[test]
343 fn test_error_debug_and_clone() {
344 let e = Http1Error::SyntaxError("test".into());
345 let e2 = e.clone();
346 assert_eq!(e, e2);
347 let _ = format!("{:?}", e);
348 }
349}