Skip to main content

zenith_http1/
parser.rs

1//! HTTP/1.1 请求解析器
2//!
3//! 实现 RFC 7230/7231 严格解析,增量(流式)处理。
4
5use crate::smuggling::SmugglingDetector;
6use crate::types::{HeaderEntry, HeaderList, Http1Config, Http1Error, HttpRequest, HttpRequestLine};
7
8/// HTTP/1.1 解析器状态
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum ParserState {
11    /// 等待请求
12    WaitingRequest,
13    /// 读取请求行
14    ReadingRequest,
15    /// 读取头部
16    ReadingHeaders,
17    /// 读取请求体(已知 Content-Length 字节流)
18    ReadingBody,
19    /// 读取 chunked 编码的 chunk-size 行(RFC 7230 §4.1)
20    ReadingChunkSize,
21    /// 读取 chunked 编码的 chunk-data 字节(含尾部 CRLF 消费)
22    ReadingChunkData,
23    /// 读取 chunked 编码的 trailer-part(最后一个 0-size chunk 之后)
24    ReadingChunkTrailer,
25    /// 头部+请求体 全部读取完成(可安全返回 HttpRequest)
26    HeadersComplete,
27    /// 错误
28    Error,
29}
30
31/// 在 buf[start..] 中查找 CRLF
32#[inline]
33fn find_crlf_at(buf: &[u8], start: usize) -> Option<usize> {
34    let mut i = start;
35    while i + 1 < buf.len() {
36        if buf[i] == b'\r' && buf[i + 1] == b'\n' {
37            return Some(i);
38        }
39        i += 1;
40    }
41    None
42}
43
44/// RFC 7230 §3.5 容错拒绝:裸 LF(不紧随 CR)属于非法行分隔,
45/// fail-closed 立即拒绝(禁止静默滞留直至 idle_timeout,阻塞 DoS)。
46///
47/// 判定区间仅自 `scanned` 起(已消费部分不再重复扫描,O(n) 单遍)。
48///
49/// HTTP-004:增量 feed 跨边界时,上一 feed 末尾若为 CR、本 feed 以 LF 开头,
50/// 构成合法的 CRLF 而非裸 LF。为此函数引入 `init_prev_cr`(区间起始前一个字节
51/// 是否为 CR),并返回区间末尾字节是否为 CR,供调用方保存跨 feed 状态。
52#[inline]
53fn scan_bare_lf(buf: &[u8], init_prev_cr: bool) -> (bool, bool) {
54    let mut prev_cr = init_prev_cr;
55    for &b in buf {
56        if b == b'\n' && !prev_cr {
57            return (true, false);
58        }
59        prev_cr = b == b'\r';
60    }
61    (false, prev_cr)
62}
63
64/// HTTP/1.1 请求解析器
65#[derive(Debug, Clone)]
66pub struct Http1Parser {
67    config: Http1Config,
68    state: ParserState,
69    /// 头部列表
70    headers: HeaderList,
71    /// 统一缓冲区(累积数据)
72    buf: Vec<u8>,
73    /// 已扫描位置
74    scanned: usize,
75    /// 请求行解析
76    request_line: Option<HttpRequestLine>,
77    /// 走私检测器
78    smuggler: SmugglingDetector,
79    /// 首字节时间戳(毫秒),用于 Slowloris 空闲超时检测
80    first_byte_ms: Option<u64>,
81    /// 最近一次收到字节的时间戳(毫秒),body/chunked 阶段 Slowloris 防护(M-9)
82    last_activity_ms: Option<u64>,
83    /// 期望读取的请求体字节数(Some(cl) = Content-Length,None = 未知/无)
84    body_expected: Option<u64>,
85    /// 已累积的请求体字节(ReadingBody / ReadingChunkData 状态下使用)
86    partial_body: Vec<u8>,
87    /// chunked 编码当前 chunk 剩余待读字节数
88    chunk_remaining: usize,
89    /// HTTP-004:增量 feed 跨边界时,上一 feed 末尾字节是否为 CR,
90    /// 用于裸 LF 检测的状态延续(避免 CRLF 被拆分成两次 feed 时误判为裸 LF)。
91    last_byte_was_cr: bool,
92}
93
94impl Http1Parser {
95    /// 创建解析器
96    #[inline]
97    pub fn new(config: Http1Config) -> Self {
98        Self {
99            config,
100            state: ParserState::WaitingRequest,
101            headers: HeaderList::new(),
102            buf: Vec::with_capacity(256),
103            scanned: 0,
104            request_line: None,
105            smuggler: SmugglingDetector::new(),
106            first_byte_ms: None,
107            last_activity_ms: None,
108            body_expected: None,
109            partial_body: Vec::new(),
110            chunk_remaining: 0,
111            last_byte_was_cr: false,
112        }
113    }
114
115    /// 重置解析器
116    pub fn reset(&mut self) {
117        self.state = ParserState::WaitingRequest;
118        self.headers.clear();
119        self.buf.clear();
120        self.scanned = 0;
121        self.request_line = None;
122        self.first_byte_ms = None;
123        self.last_activity_ms = None;
124        self.body_expected = None;
125        self.partial_body.clear();
126        self.chunk_remaining = 0;
127        self.last_byte_was_cr = false;
128    }
129
130    /// 当前状态
131    #[inline]
132    pub fn state(&self) -> ParserState {
133        self.state
134    }
135
136    /// 检查空闲超时(Slowloris 防护)
137    ///
138    /// 由连接管理层在每个事件循环周期调用。
139    /// 计时基准为最近一次收到字节的时间(`last_activity_ms`,
140    /// 未记录时回退首字节时间):头部阶段自首字节起计时,
141    /// body/chunked 阶段自最近一次活跃起计时——任何阶段超时一律
142    /// fail-closed,杜绝 body 中途 Slowloris 盲区(M-9)。
143    ///
144    /// # 参数
145    /// * `now_ms` - 当前时间戳(毫秒)
146    pub fn check_idle_timeout(&mut self, now_ms: u64) -> Result<(), Http1Error> {
147        // HeadersComplete(已交付上层)与 Error(已终结)不参与空闲计时
148        if matches!(
149            self.state,
150            ParserState::HeadersComplete | ParserState::Error
151        ) {
152            return Ok(());
153        }
154        let deadline_base = self.last_activity_ms.or(self.first_byte_ms);
155        if let Some(base) = deadline_base
156            && now_ms.saturating_sub(base) > self.config.idle_timeout_ms
157        {
158            self.state = ParserState::Error;
159            return Err(Http1Error::IdleTimeout);
160        }
161        Ok(())
162    }
163
164    /// 通知首字节到达(由连接层在收到首个数据时调用)
165    ///
166    /// 仅记录首字节时间;增量数据的活跃刷新请使用
167    /// [`Self::note_activity`](每次收到数据都应调用)。
168    #[inline]
169    pub fn note_first_byte(&mut self, now_ms: u64) {
170        if self.first_byte_ms.is_none() {
171            self.first_byte_ms = Some(now_ms);
172        }
173    }
174
175    /// 通知活动时间(由连接层在**每次**收到数据时调用)
176    ///
177    /// 刷新最近一次活跃时间戳,并兼具首字节记录语义。
178    /// body/chunked 阶段的 Slowloris 防护依赖本时间戳(M-9)。
179    #[inline]
180    pub fn note_activity(&mut self, now_ms: u64) {
181        if self.first_byte_ms.is_none() {
182            self.first_byte_ms = Some(now_ms);
183        }
184        self.last_activity_ms = Some(now_ms);
185    }
186
187    /// 解析输入数据
188    pub fn feed(&mut self, input: &[u8]) -> Result<(Option<HttpRequest>, usize), Http1Error> {
189        if input.is_empty() {
190            // 空输入:若已有完整请求则返回
191            if matches!(self.state, ParserState::HeadersComplete) {
192                let req = self.build_request()?;
193                return Ok((Some(req), 0));
194            }
195            // 无剩余字节可处理 → 需要更多数据
196            if self.scanned >= self.buf.len() {
197                return Ok((None, 0));
198            }
199            // 有剩余字节(pipelining / 跨 feed chunked)→ 继续到扫描循环
200        }
201
202        // 记录加入 buf 前的已处理位置
203        let scanned_before = self.scanned;
204        let old_buf_len = self.buf.len();
205
206        // Slowloris 防护:缓冲区累积上限检查
207        // checked 算术:输入长度由对端控制,溢出按缓冲区超限处理(fail-closed)
208        let new_buf_len = match old_buf_len.checked_add(input.len()) {
209            Some(n) => n,
210            None => {
211                self.state = ParserState::Error;
212                return Err(Http1Error::BufferOverflow);
213            }
214        };
215        if new_buf_len > self.config.max_buffer_size {
216            self.state = ParserState::Error;
217            return Err(Http1Error::BufferOverflow);
218        }
219
220        self.buf.extend_from_slice(input);
221        let new_bytes = self.buf.len() - old_buf_len;
222
223        // 扫描循环:在 self.buf[self.scanned..] 中解析
224        while !matches!(self.state, ParserState::HeadersComplete | ParserState::Error) {
225            match self.state {
226                ParserState::WaitingRequest | ParserState::ReadingRequest => {
227                    if let Some(cp) = find_crlf_at(&self.buf, self.scanned) {
228                        // M-3:找到 CRLF 后同样约束请求行长度上限
229                        // (此前仅约束未完成行,完整超长行可绕过防线)
230                        if cp - self.scanned > self.config.max_request_line_size {
231                            self.state = ParserState::Error;
232                            return Err(Http1Error::RequestLineTooLong);
233                        }
234                        // §6.1.1:行切片直接借用 buf,零行级拷贝
235                        self.request_line =
236                            Some(parse_request_line(&self.buf[self.scanned..cp])?);
237                        self.scanned = cp + 2;
238                        self.state = ParserState::ReadingHeaders;
239                    } else {
240                        // RFC 7230 §3.5:裸 LF 非法行分隔,fail-closed 立即拒绝
241                        // (禁止静默滞留至 idle_timeout,阻塞 DoS)
242                        if self.check_bare_lf(self.scanned) {
243                            self.state = ParserState::Error;
244                            return Err(Http1Error::SyntaxError(
245                                "bare LF in request line not allowed (RFC 7230 §3.5)".into(),
246                            ));
247                        }
248                        // 未完成请求行(未找到 CRLF)超过上限 → 请求行过长
249                        if self.buf.len() - self.scanned > self.config.max_request_line_size {
250                            self.state = ParserState::Error;
251                            return Err(Http1Error::RequestLineTooLong);
252                        }
253                        break;
254                    }
255                }
256                ParserState::ReadingHeaders => {
257                    let mut line_start = self.scanned;
258                    let mut progressed = false;
259                    let mut headers_done = false;
260                    'lines: loop {
261                        if line_start >= self.buf.len() {
262                            break 'lines;
263                        }
264                        let rest = &self.buf[line_start..];
265                        if let Some(cp) = find_crlf(rest) {
266                            let line_end = line_start + cp;
267                            // §6.1.1:行切片直接借用 buf,零行级拷贝
268                            let line_slice = &self.buf[line_start..line_end];
269                            line_start = line_end + 2;
270                            progressed = true;
271                            if line_slice.is_empty() {
272                                self.scanned = line_start;
273                                headers_done = true;
274                                break 'lines;
275                            }
276                            if let Some(entry) = parse_header_line(line_slice, &self.config)? {
277                                self.headers.push(entry);
278                            }
279                            if self.headers.len() > self.config.max_header_count {
280                                self.state = ParserState::Error;
281                                return Err(Http1Error::TooManyHeaders);
282                            }
283                            if self.current_header_size() > self.config.max_header_size {
284                                self.state = ParserState::Error;
285                                return Err(Http1Error::HeaderTooLarge);
286                            }
287                        } else {
288                            // RFC 7230 §3.5:头部区块中的裸 LF 拒绝(见 request line 同款)
289                            if self.check_bare_lf(line_start) {
290                                self.state = ParserState::Error;
291                                return Err(Http1Error::SyntaxError(
292                                    "bare LF in header block not allowed (RFC 7230 §3.5)".into(),
293                                ));
294                            }
295                            break 'lines;
296                        }
297                    }
298                    if !progressed {
299                        if self.buf.len() - self.scanned > self.config.max_header_size {
300                            self.state = ParserState::Error;
301                            return Err(Http1Error::HeaderTooLarge);
302                        }
303                        break;
304                    }
305                    self.scanned = line_start;
306
307                    if headers_done {
308                        // 头部读取完毕 → 先执行严格走私检测(必须在任何 body 分支决策前)
309                        if let Err(e) = self.smuggler.detect_err_already_lowercased(self.headers.as_slice()) {
310                            self.state = ParserState::Error;
311                            return Err(e);
312                        }
313                        // 严格校验:HTTP/1.1 必须有 Host 头(必须在 body 决策前)
314                        if !self.has_host() {
315                            self.state = ParserState::Error;
316                            return Err(Http1Error::MissingHost);
317                        }
318                        // RFC 7230 §5.4/§5.5:absolute-form 目标的 authority
319                        // 必须与 Host 头一致(大小写不敏感)——若按不同来源
320                        // 取主机是经典差异解析走私向量,不一致 fail-closed
321                        self.check_absolute_form_host_consistency()?;
322
323                        // 头部已全部读完,根据 Content-Length / Transfer-Encoding 决定下一步
324                        // S-2:CL 头存在但数值不可解析(超 u64 等)一律 fail-closed,
325                        // 禁止解析失败静默当作"无 body"
326                        let content_length = match parse_content_length(self.headers.as_slice()) {
327                            Ok(v) => v,
328                            Err(e) => {
329                                self.state = ParserState::Error;
330                                return Err(e);
331                            }
332                        };
333                        let te = analyze_transfer_encoding(self.headers.as_slice());
334
335                        // RFC 7230 §3.3.1:Transfer-Encoding 仅由 HTTP/1.1 定义,
336                        // HTTP/1.0 请求出现 TE 一律拒绝(fail-closed)
337                        let is_http10 = self
338                            .request_line
339                            .as_ref()
340                            .map(|l| l.version.as_ref() == "HTTP/1.0")
341                            .unwrap_or(false);
342                        if is_http10 && te != TransferEncodingKind::None {
343                            self.state = ParserState::Error;
344                            return Err(Http1Error::SmugglingDetected(
345                                "Transfer-Encoding in HTTP/1.0 request".into(),
346                            ));
347                        }
348                        // TE 存在但无 chunked 且非 identity → 按走私拒绝(fail-closed),
349                        // 与 SmugglingDetector 语义统一
350                        if te == TransferEncodingKind::Invalid {
351                            self.state = ParserState::Error;
352                            return Err(Http1Error::SmugglingDetected(
353                                "Transfer-Encoding without 'chunked'".into(),
354                            ));
355                        }
356                        // 仅当 TE 含 chunked 时才按 chunked 解析
357                        let chunked = te == TransferEncodingKind::Chunked;
358
359                        if chunked {
360                            // 进入 chunked 请求体解析状态机(RFC 7230 §4.1)
361                            // 后续状态:ReadingChunkSize → ReadingChunkData → ReadingChunkSize → ...
362                            //           → ReadingChunkTrailer → HeadersComplete
363                            self.body_expected = None;
364                            self.partial_body.clear();
365                            self.chunk_remaining = 0;
366                            self.state = ParserState::ReadingChunkSize;
367                        } else if let Some(cl) = content_length {
368                            if cl == 0 {
369                                // 显式声明 Content-Length: 0 → 空 body
370                                self.body_expected = Some(0);
371                                self.partial_body.clear();
372                                self.state = ParserState::HeadersComplete;
373                            } else {
374                                // 需要读取固定长度 body:进入 ReadingBody 状态
375                                self.body_expected = Some(cl);
376                                self.partial_body.clear();
377                                // checked 算术:u64 CL → usize 必须显式转换,
378                                // 禁止 `as usize` 高位静默截断(§4.4 fail-closed)
379                                let cl_usize = match usize::try_from(cl) {
380                                    Ok(n) => n,
381                                    Err(_) => {
382                                        self.state = ParserState::Error;
383                                        return Err(Http1Error::BodyTooLarge);
384                                    }
385                                };
386                                if cl_usize > self.config.max_body_size {
387                                    self.state = ParserState::Error;
388                                    return Err(Http1Error::BodyTooLarge);
389                                }
390                                // 预分配一次,避免多次 realloc;
391                                // 上限 64KB:攻击者声明巨大 CL 时不得触发等额预分配(OOM 防护),
392                                // 超出部分由后续 extend 按需增长
393                                self.partial_body.reserve(core::cmp::min(cl_usize, 64 * 1024));
394                                self.state = ParserState::ReadingBody;
395                                // 若当前 buf 中已有 body 字节,先拷贝一部分
396                                let tail_avail = self.buf.len().saturating_sub(self.scanned);
397                                if tail_avail > 0 {
398                                    let copy_n =
399                                        core::cmp::min(tail_avail, cl_usize);
400                                    self.partial_body
401                                        .extend_from_slice(&self.buf[self.scanned..self.scanned + copy_n]);
402                                    self.scanned += copy_n;
403                                }
404                                // body 已读满 → 直接完成
405                                if self.partial_body.len() as u64 >= cl {
406                                    self.state = ParserState::HeadersComplete;
407                                }
408                            }
409                        } else {
410                            // 无 Content-Length 且非 chunked → 视为无 body(HTTP/1.1 默认)
411                            self.body_expected = None;
412                            self.partial_body.clear();
413                            self.state = ParserState::HeadersComplete;
414                        }
415                    }
416                }
417                ParserState::ReadingBody => {
418                    let cl = match self.body_expected {
419                        Some(c) => c,
420                        None => {
421                            // 意外:ReadingBody 但未设置 body_expected,兜底直接完成
422                            self.state = ParserState::HeadersComplete;
423                            break;
424                        }
425                    };
426                    // cl 进入本状态前已经 try_from(cl) <= max_body_size 校验,
427                    // 在 u64 域内计算剩余量,禁止 `as usize` 截断(§4.4)
428                    let remaining = cl.saturating_sub(self.partial_body.len() as u64);
429                    if remaining == 0 {
430                        self.state = ParserState::HeadersComplete;
431                        break;
432                    }
433                    let tail_avail = self.buf.len().saturating_sub(self.scanned);
434                    if tail_avail == 0 {
435                        // buf 中无更多数据,等待下一次 feed
436                        break;
437                    }
438                    let copy_n = core::cmp::min(remaining, tail_avail as u64) as usize;
439                    self.partial_body
440                        .extend_from_slice(&self.buf[self.scanned..self.scanned + copy_n]);
441                    self.scanned += copy_n;
442                    if self.partial_body.len() as u64 >= cl {
443                        self.state = ParserState::HeadersComplete;
444                    }
445                }
446                // ── chunked 请求体解析状态机(RFC 7230 §4.1) ───────────────────
447                // chunk          = chunk-size [ chunk-ext ] CRLF chunk-data CRLF
448                // last-chunk     = 1*("0") [ chunk-ext ] CRLF
449                // chunked-body   = *chunk last-chunk trailer-part CRLF
450                ParserState::ReadingChunkSize => {
451                    // 读取 chunk-size 行(hex [; ext] CRLF)
452                    if let Some(cp) = find_crlf_at(&self.buf, self.scanned) {
453                        let chunk_size = parse_chunk_size(&self.buf[self.scanned..cp])?;
454                        self.scanned = cp + 2;
455                        if chunk_size == 0 {
456                            // last-chunk → 进入 trailer-part 读取
457                            self.state = ParserState::ReadingChunkTrailer;
458                        } else {
459                            // 累积 body 上限检查(防 OOM)
460                            if self
461                                .partial_body
462                                .len()
463                                .checked_add(chunk_size)
464                                .map(|sum| sum > self.config.max_body_size)
465                                .unwrap_or(true)
466                            {
467                                self.state = ParserState::Error;
468                                return Err(Http1Error::BodyTooLarge);
469                            }
470                            self.chunk_remaining = chunk_size;
471                            self.state = ParserState::ReadingChunkData;
472                        }
473                    } else {
474                        // 需要更多数据
475                        break;
476                    }
477                }
478                ParserState::ReadingChunkData => {
479                    if self.chunk_remaining > 0 {
480                        // 读取 chunk-data 字节
481                        let tail_avail = self.buf.len().saturating_sub(self.scanned);
482                        if tail_avail == 0 {
483                            break;
484                        }
485                        let copy_n = core::cmp::min(self.chunk_remaining, tail_avail);
486                        self.partial_body
487                            .extend_from_slice(&self.buf[self.scanned..self.scanned + copy_n]);
488                        self.scanned += copy_n;
489                        self.chunk_remaining -= copy_n;
490                    }
491                    if self.chunk_remaining == 0 {
492                        // 消费 chunk-data 尾部的 CRLF(RFC 7230 §4.1 强制要求)
493                        let tail_avail = self.buf.len().saturating_sub(self.scanned);
494                        if tail_avail < 2 {
495                            break;
496                        }
497                        if self.buf[self.scanned] != b'\r'
498                            || self.buf[self.scanned + 1] != b'\n'
499                        {
500                            self.state = ParserState::Error;
501                            return Err(Http1Error::ChunkedError(
502                                "missing CRLF after chunk data".into(),
503                            ));
504                        }
505                        self.scanned += 2;
506                        self.state = ParserState::ReadingChunkSize;
507                    }
508                }
509                ParserState::ReadingChunkTrailer => {
510                    // 读取 trailer-part:连续的 header-field CRLF,直到空行 CRLF
511                    if let Some(cp) = find_crlf_at(&self.buf, self.scanned) {
512                        // §6.1.1:行切片直接借用 buf,零行级拷贝
513                        let line_slice = &self.buf[self.scanned..cp];
514                        let trailer_entry = if line_slice.is_empty() {
515                            None
516                        } else {
517                            // §6.2.2 语义说明:trailer 头部与普通头部走同一严格校验函数
518                            parse_header_line(line_slice, &self.config)?
519                        };
520                        self.scanned = cp + 2;
521                        if line_slice.is_empty() {
522                            // trailer-part 结束 → 请求完整
523                            self.state = ParserState::HeadersComplete;
524                        } else {
525                            // 解析 trailer header(与普通 header 相同的严格校验)
526                            if let Some(entry) = trailer_entry {
527                                self.headers.push(entry);
528                            }
529                            if self.headers.len() > self.config.max_header_count {
530                                self.state = ParserState::Error;
531                                return Err(Http1Error::TooManyHeaders);
532                            }
533                            if self.current_header_size() > self.config.max_header_size {
534                                self.state = ParserState::Error;
535                                return Err(Http1Error::HeaderTooLarge);
536                            }
537                        }
538                    } else {
539                        break;
540                    }
541                }
542                ParserState::HeadersComplete | ParserState::Error => break,
543            }
544        }
545
546        // 计算本次输入的消耗数:若已达到 HeadersComplete / ReadingBody 消耗了全部新数据,则算消耗整个 input
547        let processed_after = self.scanned;
548        let processed_delta = processed_after.saturating_sub(scanned_before);
549
550        // consumed 统一语义(L-3 pipelining 依赖精确记账):
551        // - 空输入仅处理既有缓冲,不消耗任何输入字节
552        // - 处理量 >= 新增量 → 全部新字节已消耗
553        // - 否则消耗实际处理量(多余字节属于下一个 pipelined 请求,
554        //   由 parser 内部缓冲保留,调用方按 consumed 推进)
555        let consumed = if new_bytes == 0 {
556            0
557        } else if processed_delta >= new_bytes {
558            new_bytes
559        } else {
560            processed_delta
561        };
562
563        // HTTP-004:记录本 feed 末尾字节是否为 CR,供下一 feed 的裸 LF 检测
564        // 延续状态(避免 CRLF 被拆分成两次 feed 时误判为裸 LF)。
565        self.last_byte_was_cr = self.buf.last() == Some(&b'\r');
566
567        // 清理:若未完成则移除已处理部分(ReadingBody 也需要清理已扫描的 body 前缀字节)
568        if !matches!(self.state, ParserState::HeadersComplete) && self.scanned > 0 {
569            self.buf.drain(..self.scanned);
570            self.scanned = 0;
571        }
572
573        if matches!(self.state, ParserState::HeadersComplete) {
574            let req = self.build_request()?;
575            Ok((Some(req), consumed))
576        } else {
577            Ok((None, consumed))
578        }
579    }
580
581    /// 检查 `buf[region_start..]` 内是否存在裸 LF(HTTP-004)。
582    ///
583    /// 跨 feed 状态延续:当 `region_start == 0`(区间覆盖缓冲起始,即本条数据
584    /// 与上一 feed 的边界)时,以 `last_byte_was_cr`(上一 feed 末尾字节是否为
585    /// CR)作为扫描的初始 `prev_cr`;否则取缓冲内紧邻区间前的字节。扫描后把
586    /// 区间末尾字节的 CR 状态写回 `last_byte_was_cr`,供下一次 feed 使用。
587    fn check_bare_lf(&mut self, region_start: usize) -> bool {
588        let init_prev_cr = if region_start == 0 {
589            self.last_byte_was_cr
590        } else {
591            self.buf[region_start - 1] == b'\r'
592        };
593        let (bare, last_cr) = scan_bare_lf(&self.buf[region_start..], init_prev_cr);
594        self.last_byte_was_cr = last_cr;
595        bare
596    }
597
598    /// 组装请求(假设 self.state == HeadersComplete)
599    fn build_request(&mut self) -> Result<HttpRequest, Http1Error> {
600        let line = self
601            .request_line
602            .take()
603            .ok_or_else(|| Http1Error::Internal("missing request line".into()))?;
604
605        self.smuggler.detect_err_already_lowercased(self.headers.as_slice())?;
606
607        if !self.has_host() {
608            return Err(Http1Error::MissingHost);
609        }
610
611        // S-2:与 feed 路径同一严格解析(CL 存在但非法一律 fail-closed)
612        let content_length = parse_content_length(self.headers.as_slice())?;
613        // 与 feed 路径语义统一:仅当 TE 含 chunked 时才视为 chunked
614        let chunked =
615            analyze_transfer_encoding(self.headers.as_slice()) == TransferEncodingKind::Chunked;
616        // RFC 7230 §6.3 / RFC 1945 §1.3:
617        // - HTTP/1.0 默认关闭连接,仅显式 `Connection: keep-alive` 才保持
618        // - HTTP/1.1 默认保持,仅显式 `Connection: close` 才关闭
619        // M-8:Connection 为逗号分隔 token 列表(如 "close, upgrade"),
620        // 逐 token trim 后大小写不敏感比较,禁止整串精确匹配绕过
621        let keep_alive = if line.version.as_ref() == "HTTP/1.0" {
622            connection_has_token(self.headers.as_slice(), "keep-alive")
623        } else {
624            !connection_has_token(self.headers.as_slice(), "close")
625        };
626
627        // body 取自 ReadingBody 阶段累积的 partial_body
628        let body: Vec<u8> = std::mem::take(&mut self.partial_body);
629
630        // 严格校验:若声明了 Content-Length 且非 chunked,实际 body 长度必须匹配
631        if let Some(cl) = content_length
632            && !chunked
633            && (body.len() as u64) != cl
634        {
635            self.state = ParserState::Error;
636            return Err(Http1Error::SyntaxError(format!(
637                "request body length mismatch: declared Content-Length={cl}, actual bytes={}",
638                body.len()
639            )));
640        }
641
642        let req = HttpRequest {
643            line,
644            headers: std::mem::take(&mut self.headers),
645            body,
646            content_length,
647            chunked,
648            keep_alive,
649        };
650
651        // 重置解析器状态以接受下一个请求(keep-alive / pipelining)
652        // 关键:仅丢弃已扫描的字节,保留 buf 中剩余字节(属于下一个请求)
653        self.state = ParserState::WaitingRequest;
654        if self.scanned > 0 && self.scanned <= self.buf.len() {
655            self.buf.drain(..self.scanned);
656        }
657        self.scanned = 0;
658        self.body_expected = None;
659        self.chunk_remaining = 0;
660        // HTTP-004:新请求重新计时交叉 feed 的 CR 状态
661        self.last_byte_was_cr = false;
662        // 计时字段重置:下一个请求(keep-alive / pipelining)重新计时
663        self.first_byte_ms = None;
664        self.last_activity_ms = None;
665        // partial_body 已通过 std::mem::take 移交给 req,此处无需再次 clear
666
667        Ok(req)
668    }
669
670}
671
672/// 严格解析 Content-Length(S-2 fail-closed)
673///
674/// - 头不存在 → `Ok(None)`
675/// - 头存在且为合法 u64 → `Ok(Some(n))`
676/// - 头存在但数值不可解析(全数字但超 u64 等)→ `Err`
677///
678/// 与"头不存在"严格区分:禁止把解析失败静默当作"无 body"
679/// (等价 400 语义,fail-closed)。
680fn parse_content_length(headers: &[HeaderEntry]) -> Result<Option<u64>, Http1Error> {
681    match headers.iter().find(|(k, _)| k.as_ref() == "content-length") {
682        None => Ok(None),
683        Some((_, v)) => {
684            let cl = v.parse::<u64>().map_err(|_| {
685                Http1Error::SmugglingDetected(format!(
686                    "Content-Length value not parseable as u64: '{}'",
687                    v.as_ref()
688                ))
689            })?;
690            Ok(Some(cl))
691        }
692    }
693}
694
695/// 判定 Connection 头部 token 列表中是否含指定 token(M-8)
696///
697/// RFC 7230 §6.1:Connection = 逗号分隔 token 列表;
698/// 逐 token trim 后大小写不敏感比较(如 "close, upgrade" 含 close 语义)。
699fn connection_has_token(headers: &[HeaderEntry], token: &str) -> bool {
700    headers
701        .iter()
702        .filter(|(k, _)| k.as_ref() == "connection")
703        .flat_map(|(_, v)| v.split(','))
704        .map(str::trim)
705        .any(|t| t.eq_ignore_ascii_case(token))
706}
707
708/// 提取 absolute-form 目标的 authority 部分(RFC 7230 §5.3.2)
709///
710/// - 以 `http://` / `https://` 开头(scheme 大小写不敏感)→ 返回 authority
711///   (到首个 `/`、`?`、`#` 或字符串结尾为止)
712/// - 其余形态(origin-form / authority-form / asterisk-form)→ `None`
713fn absolute_form_authority(target: &str) -> Option<&str> {
714    let after_scheme = if target.len() >= 7 && target[..7].eq_ignore_ascii_case("http://") {
715        &target[7..]
716    } else if target.len() >= 8 && target[..8].eq_ignore_ascii_case("https://") {
717        &target[8..]
718    } else {
719        return None;
720    };
721    let end = after_scheme
722        .find(['/', '?', '#'])
723        .unwrap_or(after_scheme.len());
724    Some(&after_scheme[..end])
725}
726
727/// 解析请求行(自由函数:零行级拷贝,输入为 buf 借用切片)
728///
729/// 产出 `HttpRequestLine`(`Box<str>` 三段物化,§6.1.1)。
730fn parse_request_line(line: &[u8]) -> Result<HttpRequestLine, Http1Error> {
731    let s = std::str::from_utf8(line)
732        .map_err(|_| Http1Error::SyntaxError("request line not UTF-8".into()))?;
733    let s = s.trim_end_matches('\r');
734
735    let mut parts = s.splitn(3, ' ');
736    let method = parts
737        .next()
738        .ok_or_else(|| Http1Error::SyntaxError("missing method".into()))?;
739    let target = parts
740        .next()
741        .ok_or_else(|| Http1Error::SyntaxError("missing target".into()))?;
742    let version = parts
743        .next()
744        .ok_or_else(|| Http1Error::SyntaxError("missing version".into()))?;
745
746    if !is_valid_method(method) {
747        return Err(Http1Error::UnsupportedMethod(method.to_string()));
748    }
749    if version != "HTTP/1.0" && version != "HTTP/1.1" {
750        return Err(Http1Error::UnsupportedVersion(version.to_string()));
751    }
752    if target.chars().any(|c| (c as u32) < 0x20) {
753        return Err(Http1Error::SyntaxError(
754            "target contains control chars".into(),
755        ));
756    }
757
758    Ok(HttpRequestLine {
759        method: method.into(),
760        target: target.into(),
761        version: version.into(),
762    })
763}
764
765/// 解析单条头部(自由函数:零行级拷贝,输入为 buf 借用切片)
766///
767/// 空行返回 `Ok(None)`(头部区结束标记);否则返回 `(小写名, 值)`。
768/// 名称经 `to_ascii_lowercase().into_boxed_str()` 一次分配完成
769/// 小写化与物化,值为 `Box<str>`(§6.1.1)。
770fn parse_header_line(line: &[u8], config: &Http1Config) -> Result<Option<HeaderEntry>, Http1Error> {
771    if line.is_empty() {
772        return Ok(None);
773    }
774    if line.iter().any(|&b| b == b'\r' || b == b'\n') {
775        return Err(Http1Error::SyntaxError(
776            "header line contains CR/LF".into(),
777        ));
778    }
779    // 拒绝 line folding(RFC 7230 已废弃)
780    if line.first() == Some(&b' ') || line.first() == Some(&b'\t') {
781        return Err(Http1Error::SyntaxError(
782            "header line folding is forbidden".into(),
783        ));
784    }
785
786    let colon = line
787        .iter()
788        .position(|&b| b == b':')
789        .ok_or_else(|| Http1Error::SyntaxError("missing colon in header".into()))?;
790
791    let name = std::str::from_utf8(&line[..colon])
792        .map_err(|_| Http1Error::SyntaxError("header name not UTF-8".into()))?;
793    // checked 算术:colon+1 不超 line.len()(colon 来自 position,严格小于 len)
794    let value = std::str::from_utf8(&line[colon + 1..])
795        .map_err(|_| Http1Error::SyntaxError("header value not UTF-8".into()))?;
796
797    if !is_valid_header_name(name) {
798        return Err(Http1Error::SyntaxError(format!(
799            "invalid header name: {name}"
800        )));
801    }
802    if name.len() > config.max_header_name_len {
803        return Err(Http1Error::HeaderTooLarge);
804    }
805
806    // L-7:RFC 7230 §3.2.4 头部值首尾可选空白(OWS = SP/HTAB)两侧均裁剪,
807    // 与客户端 parse_head 的 OWS 处理语义一致
808    let val_trim = value.trim_matches(|c| c == ' ' || c == '\t');
809    if val_trim.len() > config.max_header_value_len {
810        return Err(Http1Error::HeaderTooLarge);
811    }
812    if val_trim.contains('\r') || val_trim.contains('\n') {
813        return Err(Http1Error::SyntaxError(
814            "header value contains CR/LF".into(),
815        ));
816    }
817
818    let name = name.to_ascii_lowercase();
819    Ok(Some((name.into_boxed_str(), val_trim.into())))
820}
821
822impl Http1Parser {
823    /// 累积头部大小
824    fn current_header_size(&self) -> usize {
825        self.headers
826            .iter()
827            .map(|(k, v)| k.len() + v.len() + 2)
828            .sum()
829    }
830
831    /// 是否包含 Host 头
832    #[inline]
833    fn has_host(&self) -> bool {
834        self.headers.iter().any(|(k, _)| k.as_ref() == "host")
835    }
836
837    /// Host 头值(单 Host 已由走私检测器保证)
838    #[inline]
839    fn host_value(&self) -> Option<&str> {
840        self.headers
841            .iter()
842            .find(|(k, _)| k.as_ref() == "host")
843            .map(|(_, v)| v.as_ref())
844    }
845
846    /// RFC 7230 §5.4/§5.5:absolute-form 目标的 authority 必须与 Host 头一致
847    ///
848    /// 大小写不敏感比较;端口不做归一化(authority 与 Host 字符串一致即可,
849    /// 如 `example.com` vs `example.com:80` 视为不一致——宁可拒绝也不放行歧义)。
850    /// 非 absolute-form 目标(origin/authority/asterisk-form)直接通过。
851    fn check_absolute_form_host_consistency(&mut self) -> Result<(), Http1Error> {
852        let target = match self.request_line.as_ref() {
853            Some(line) => line.target.as_ref(),
854            None => return Ok(()),
855        };
856        let authority = match absolute_form_authority(target) {
857            Some(a) => a,
858            None => return Ok(()),
859        };
860        // Host 存在性已由调用方先行校验(has_host),缺省值仅为兜底
861        let host = self.host_value().unwrap_or("");
862        if authority.is_empty() || !authority.eq_ignore_ascii_case(host) {
863            self.state = ParserState::Error;
864            return Err(Http1Error::ProtocolInconsistency(
865                "absolute-form authority does not match Host header".into(),
866            ));
867        }
868        Ok(())
869    }
870}
871
872/// 查找 CRLF 位置
873#[inline]
874fn find_crlf(buf: &[u8]) -> Option<usize> {
875    find_crlf_at(buf, 0)
876}
877
878/// 解析 chunk-size 行(RFC 7230 §4.1)
879///
880/// 格式:`1*HEXDIG [ ";" chunk-ext ]`(CRLF 已被调用方剥离)
881///
882/// # 参数
883/// * `line` - chunk-size 行的字节切片(不含尾部 CRLF)
884///
885/// # 返回
886/// chunk-data 的字节数
887///
888/// 全 crate 唯一实现:服务端 feed 状态机与客户端
889/// [`crate::chunked::ChunkedDecoder`] 共用,禁止第二份 hex/ext 解析副本。
890///
891/// # 错误
892/// - `ChunkedError`:空行、非十六进制字符、数值溢出
893pub(crate) fn parse_chunk_size(line: &[u8]) -> Result<usize, Http1Error> {
894    let line_str = std::str::from_utf8(line)
895        .map_err(|_| Http1Error::ChunkedError("chunk size line not UTF-8".into()))?;
896    // 分离 chunk-ext(分号之后的内容),仅校验 hex 前缀
897    let size_part = line_str.split(';').next().unwrap_or("");
898    let size_part = size_part.trim();
899    if size_part.is_empty() {
900        return Err(Http1Error::ChunkedError("empty chunk size".into()));
901    }
902    if !size_part.chars().all(|c| c.is_ascii_hexdigit()) {
903        return Err(Http1Error::ChunkedError(format!(
904            "invalid chunk size: non-hex digit in '{size_part}'"
905        )));
906    }
907    usize::from_str_radix(size_part, 16)
908        .map_err(|_| Http1Error::ChunkedError(format!("chunk size overflow: {size_part}")))
909}
910
911/// Transfer-Encoding 分析结果(与 SmugglingDetector 语义统一)
912#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
913enum TransferEncodingKind {
914    /// 无 Transfer-Encoding 头
915    None,
916    /// 含 chunked(可能同时含其他编码,chunked 为最后编码由 detector 校验)
917    Chunked,
918    /// 存在 TE 但无 chunked → 走私嫌疑(fail-closed)
919    Invalid,
920}
921
922/// 分析 Transfer-Encoding 头部(可多次出现、逗号分隔多值,大小写不敏感)
923///
924/// HTTP-002:RFC 7230 已废弃 `identity` 作为 TE 值,不再作为合法编码处理,
925/// 因此不含 chunked 的 TE(含 identity)一律归为 `Invalid`(fail-closed)。
926/// 含 `identity` 的 TE 会先被走私检测器拒绝(feed/build_request 均先调用
927/// detect),此处语义与检测器保持一致。
928fn analyze_transfer_encoding(headers: &[HeaderEntry]) -> TransferEncodingKind {
929    let mut saw_te = false;
930    let mut saw_chunked = false;
931    for (k, v) in headers {
932        if k.as_ref() != "transfer-encoding" {
933            continue;
934        }
935        saw_te = true;
936        for tok in v.split(',') {
937            let tok = tok.trim();
938            if tok.is_empty() {
939                continue;
940            }
941            if tok.eq_ignore_ascii_case("chunked") {
942                saw_chunked = true;
943            }
944        }
945    }
946    if !saw_te {
947        TransferEncodingKind::None
948    } else if saw_chunked {
949        TransferEncodingKind::Chunked
950    } else {
951        TransferEncodingKind::Invalid
952    }
953}
954
955/// 合法方法
956///
957/// RFC 7230 §3.1.1:方法区分大小写,仅接受标准大写方法。
958/// 委托 [`zenith_api::Method::from_str`](全库唯一方法解析入口),
959/// 与 normalize 层语义严格一致(拒绝 "get" 等小写变体,消除协议差分)。
960#[inline]
961fn is_valid_method(m: &str) -> bool {
962    m.parse::<zenith_api::Method>().is_ok()
963}
964
965/// 合法头部名称(RFC 7230 §3.2.6 token,非空)
966///
967/// 委托 [`zenith_api::normalize::is_valid_header_name`](全库唯一 tchar 校验实现)。
968#[inline]
969fn is_valid_header_name(s: &str) -> bool {
970    zenith_api::normalize::is_valid_header_name(s)
971}
972
973#[cfg(test)]
974mod tests {
975    use super::*;
976
977    fn make_config() -> Http1Config {
978        Http1Config::new()
979    }
980
981    #[test]
982    fn test_parse_get_request() {
983        let mut p = Http1Parser::new(make_config());
984        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
985        let (req, _) = p.feed(input).unwrap();
986        let req = req.unwrap();
987        assert_eq!(req.line.method.as_ref(), "GET");
988        assert_eq!(req.line.target.as_ref(), "/");
989        assert_eq!(req.line.version.as_ref(), "HTTP/1.1");
990        assert_eq!(req.get_header("host"), Some("example.com"));
991        assert!(req.keep_alive);
992    }
993
994    #[test]
995    fn test_parse_post_with_content_length() {
996        let mut p = Http1Parser::new(make_config());
997        // 发送 headers(Content-Length: 13)+ 真实 13 字节 body,保证 parser 到达 HeadersComplete
998        let input =
999            b"POST /api HTTP/1.1\r\nHost: example.com\r\nContent-Length: 13\r\n\r\nHello, World!";
1000        let (req, _) = p.feed(input).unwrap();
1001        let req = req.unwrap();
1002        assert_eq!(req.content_length, Some(13));
1003        assert_eq!(req.body, b"Hello, World!");
1004        assert!(!req.chunked);
1005    }
1006
1007    #[test]
1008    fn test_parse_chunked_request() {
1009        let mut p = Http1Parser::new(make_config());
1010        // RFC 7230 §4.1 完整 chunked 请求:
1011        // headers + "5\r\nHello\r\n6\r\n World\r\n0\r\n\r\n"
1012        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHello\r\n6\r\n World\r\n0\r\n\r\n";
1013        let (req, _) = p.feed(input).unwrap();
1014        let req = req.unwrap();
1015        assert!(req.chunked);
1016        assert_eq!(req.content_length, None);
1017        assert_eq!(req.body, b"Hello World");
1018    }
1019
1020    #[test]
1021    fn test_parse_chunked_empty_body() {
1022        let mut p = Http1Parser::new(make_config());
1023        // 仅 last-chunk,body 为空
1024        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n";
1025        let (req, _) = p.feed(input).unwrap();
1026        let req = req.unwrap();
1027        assert!(req.chunked);
1028        assert!(req.body.is_empty());
1029    }
1030
1031    #[test]
1032    fn test_parse_chunked_with_chunk_ext() {
1033        let mut p = Http1Parser::new(make_config());
1034        // chunk-ext(分号后内容)应被忽略,不影响 body
1035        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5;name=value\r\nHello\r\n0\r\n\r\n";
1036        let (req, _) = p.feed(input).unwrap();
1037        let req = req.unwrap();
1038        assert_eq!(req.body, b"Hello");
1039    }
1040
1041    #[test]
1042    fn test_parse_chunked_with_trailer() {
1043        let mut p = Http1Parser::new(make_config());
1044        // last-chunk 后有 trailer header,应被解析并追加到 headers
1045        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHello\r\n0\r\nX-Checksum: abc123\r\n\r\n";
1046        let (req, _) = p.feed(input).unwrap();
1047        let req = req.unwrap();
1048        assert_eq!(req.body, b"Hello");
1049        assert_eq!(req.get_header("x-checksum"), Some("abc123"));
1050    }
1051
1052    #[test]
1053    fn test_parse_chunked_multiple_chunks() {
1054        let mut p = Http1Parser::new(make_config());
1055        // 多个 chunk,验证累积拼接正确
1056        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n3\r\nabc\r\n3\r\ndef\r\n3\r\nghi\r\n0\r\n\r\n";
1057        let (req, _) = p.feed(input).unwrap();
1058        let req = req.unwrap();
1059        assert_eq!(req.body, b"abcdefghi");
1060    }
1061
1062    #[test]
1063    fn test_parse_chunked_incremental_byte_by_byte() {
1064        // 逐字节喂入,验证状态机在任意切分点都能正确恢复
1065        let mut p = Http1Parser::new(make_config());
1066        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHello\r\n0\r\n\r\n";
1067        let mut req_opt: Option<HttpRequest> = None;
1068        for i in 0..input.len() {
1069            let (r, _) = p.feed(&input[i..i + 1]).unwrap();
1070            if r.is_some() {
1071                req_opt = r;
1072            }
1073        }
1074        let req = req_opt.expect("should parse complete request after byte-by-byte feed");
1075        assert!(req.chunked);
1076        assert_eq!(req.body, b"Hello");
1077    }
1078
1079    #[test]
1080    fn test_parse_chunked_incremental_split_at_chunks() {
1081        // 在 chunk 边界切分,验证跨 feed 拼接
1082        let mut p = Http1Parser::new(make_config());
1083        let part1 = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHel";
1084        let part2 = b"lo\r\n0\r\n\r\n";
1085
1086        let (r1, _) = p.feed(part1).unwrap();
1087        assert!(r1.is_none(), "first part should not complete");
1088        let (r2, _) = p.feed(part2).unwrap();
1089        let req = r2.expect("second part should complete");
1090        assert_eq!(req.body, b"Hello");
1091    }
1092
1093    #[test]
1094    fn test_parse_chunked_invalid_hex_size() {
1095        let mut p = Http1Parser::new(make_config());
1096        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\nXY\r\nHello\r\n0\r\n\r\n";
1097        let r = p.feed(input);
1098        assert!(matches!(r, Err(Http1Error::ChunkedError(_))));
1099    }
1100
1101    #[test]
1102    fn test_parse_chunked_empty_size_line() {
1103        let mut p = Http1Parser::new(make_config());
1104        // chunk-size 行为空(仅有 CRLF)
1105        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n\r\nHello\r\n0\r\n\r\n";
1106        let r = p.feed(input);
1107        assert!(matches!(r, Err(Http1Error::ChunkedError(_))));
1108    }
1109
1110    #[test]
1111    fn test_parse_chunked_missing_crlf_after_data() {
1112        let mut p = Http1Parser::new(make_config());
1113        // chunk-data 后缺少 CRLF(直接是下一个 chunk-size)
1114        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHelloXX\r\n0\r\n\r\n";
1115        let r = p.feed(input);
1116        assert!(matches!(r, Err(Http1Error::ChunkedError(_))));
1117    }
1118
1119    #[test]
1120    fn test_parse_chunked_body_too_large() {
1121        let config = Http1Config::new().with_max_body_size(10);
1122        let mut p = Http1Parser::new(config);
1123        // 单个 chunk 超过 max_body_size
1124        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\nB\r\nHello World\r\n0\r\n\r\n";
1125        let r = p.feed(input);
1126        assert!(matches!(r, Err(Http1Error::BodyTooLarge)));
1127    }
1128
1129    #[test]
1130    fn test_parse_chunked_large_hex_size() {
1131        let mut p = Http1Parser::new(make_config());
1132        // 使用大写十六进制 + 较大的 chunk size
1133        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\nA\r\n0123456789\r\n0\r\n\r\n";
1134        let (req, _) = p.feed(input).unwrap();
1135        let req = req.unwrap();
1136        assert_eq!(req.body, b"0123456789");
1137    }
1138
1139    #[test]
1140    fn test_parse_chunked_partial_last_chunk_crlf() {
1141        // 验证 "0\r\n" 和 "\r\n" 分两次 feed 的场景
1142        let mut p = Http1Parser::new(make_config());
1143        let part1 = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHello\r\n0\r\n";
1144        let part2 = b"\r\n";
1145
1146        let (r1, _) = p.feed(part1).unwrap();
1147        assert!(r1.is_none(), "should not complete before trailer CRLF");
1148        let (r2, _) = p.feed(part2).unwrap();
1149        assert!(r2.is_some(), "should complete after trailer CRLF");
1150    }
1151
1152    #[test]
1153    fn test_parse_chunked_split_at_chunk_data_crlf() {
1154        // 验证 chunk-data 完整但 CRLF 未到达时的暂停/恢复
1155        let mut p = Http1Parser::new(make_config());
1156        let part1 = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHello";
1157        let part2 = b"\r\n0\r\n\r\n";
1158
1159        let (r1, _) = p.feed(part1).unwrap();
1160        assert!(r1.is_none());
1161        let (r2, _) = p.feed(part2).unwrap();
1162        let req = r2.unwrap();
1163        assert_eq!(req.body, b"Hello");
1164    }
1165
1166    #[test]
1167    fn test_bare_lf_rejected_in_request_line() {
1168        // RFC 7230 §3.5:裸 LF(不紧随 CR)非法行分隔 fail-closed,禁止静默滞留
1169        let mut p = Http1Parser::new(Http1Config::new());
1170        let result = p.feed(b"GET /status HTTP/1.1\nHost: example.com\n\n");
1171        assert!(matches!(result, Err(Http1Error::SyntaxError(_))), "bare LF 必须拒绝: {:?}", result);
1172    }
1173
1174    #[test]
1175    fn test_bare_lf_rejected_in_headers() {
1176        let mut p = Http1Parser::new(Http1Config::new());
1177        let result = p.feed(b"GET /status HTTP/1.1\r\nHost: example.com\nX-Other: yes\r\n\r\n");
1178        assert!(matches!(result, Err(Http1Error::SyntaxError(_))), "header 区块裸 LF 必须拒绝: {:?}", result);
1179    }
1180
1181    #[test]
1182    fn test_crlf_lf_only_edge_accept() {
1183        // CRLF 行分隔不受影响(控制对照)
1184        let mut p = Http1Parser::new(Http1Config::new());
1185        let result = p.feed(b"GET /status HTTP/1.1\r\nHost: example.com\r\n\r\n");
1186        assert!(result.is_ok(), "合法 CRLF 请求不得被拒绝: {:?}", result);
1187    }
1188
1189    // ─── HTTP-004:跨 feed 的 CR 状态延续(增量拆分 CRLF)───────────────
1190
1191    #[test]
1192    fn test_crlf_split_across_feeds_accepted() {
1193        // HTTP-004:CRLF 被拆成两次 feed(上一 feed 以 CR 结尾、下一 feed 以 LF 开头),
1194        // 必须识别为合法 CRLF 而非裸 LF(状态延续)。
1195        let mut p = Http1Parser::new(Http1Config::new());
1196        // 第一段以 CR 结尾(请求行 CRLF 的 CR 部分)
1197        let r1 = p.feed(b"GET /status HTTP/1.1\r");
1198        assert!(r1.is_ok(), "以 CR 结尾的分段不得报错: {:?}", r1);
1199        // 第二段以 LF 开头补全 CRLF,随后完整请求
1200        let (req, _) = p
1201            .feed(b"\nHost: example.com\r\n\r\n")
1202            .expect("补全 CRLF 后应解析成功");
1203        assert!(req.is_some(), "跨 feed 拆分的 CRLF 必须被接受");
1204    }
1205
1206    #[test]
1207    fn test_bare_lf_split_across_feeds_rejected() {
1208        // HTTP-004:上一 feed 不以 CR 结尾、下一 feed 以 LF 开头 → 跨 feed 裸 LF 必须拒绝。
1209        // 单独 feed 一个 LF(缓冲内无其他 CRLF 干扰),确保命中裸 LF 检测而非版本解析。
1210        let mut p = Http1Parser::new(Http1Config::new());
1211        let r1 = p.feed(b"GET /status HTTP/1.1");
1212        assert!(r1.is_ok());
1213        let r2 = p.feed(b"\n");
1214        assert!(
1215            matches!(r2, Err(Http1Error::SyntaxError(_))),
1216            "跨 feed 裸 LF 必须拒绝,实际 {r2:?}"
1217        );
1218    }
1219
1220    #[test]
1221    fn test_bare_lf_split_across_feeds_rejected_full() {
1222        // HTTP-004 对照:跨 feed 裸 LF 出现在请求行内(后续 CRLF 存在)时,
1223        // 请求行无法解析(含 LF)→ 同样 fail-closed 拒绝(本测试验证非误放行)。
1224        let mut p = Http1Parser::new(Http1Config::new());
1225        let r1 = p.feed(b"GET /status HTTP/1.1");
1226        assert!(r1.is_ok());
1227        let r2 = p.feed(b"\nHost: x\r\n\r\n");
1228        assert!(
1229            r2.is_err(),
1230            "请求行内跨 feed 裸 LF 必须拒绝,实际 {r2:?}"
1231        );
1232    }
1233
1234    #[test]
1235    fn test_crlf_split_across_feeds_in_headers_accepted() {
1236        // HTTP-004:头部区块内 CRLF 被拆成两次 feed(footer 行结束的 CR + LF)
1237        let mut p = Http1Parser::new(Http1Config::new());
1238        let r1 = p.feed(b"GET / HTTP/1.1\r\nHost: example.com\r");
1239        assert!(r1.is_ok());
1240        let (req, _) = p
1241            .feed(b"\nX-Test: v\r\n\r\n")
1242            .expect("头部 CRLF 跨 feed 拆分应解析成功");
1243        assert!(req.is_some());
1244    }
1245
1246    #[test]
1247    fn test_missing_host() {
1248        let mut p = Http1Parser::new(make_config());
1249        let input = b"GET / HTTP/1.1\r\n\r\n";
1250        let r = p.feed(input);
1251        assert!(matches!(r, Err(Http1Error::MissingHost)));
1252    }
1253
1254    #[test]
1255    fn test_invalid_method() {
1256        let mut p = Http1Parser::new(make_config());
1257        let input = b"FOOBAR / HTTP/1.1\r\nHost: x\r\n\r\n";
1258        let r = p.feed(input);
1259        assert!(matches!(r, Err(Http1Error::UnsupportedMethod(_))));
1260    }
1261
1262    #[test]
1263    fn test_invalid_version() {
1264        let mut p = Http1Parser::new(make_config());
1265        let input = b"GET / HTTP/2.0\r\nHost: x\r\n\r\n";
1266        let r = p.feed(input);
1267        assert!(matches!(r, Err(Http1Error::UnsupportedVersion(_))));
1268    }
1269
1270    #[test]
1271    fn test_cl_te_smuggling_rejected() {
1272        let mut p = Http1Parser::new(make_config());
1273        let input = b"GET / HTTP/1.1\r\nHost: x\r\nContent-Length: 0\r\nTransfer-Encoding: chunked\r\n\r\n";
1274        let r = p.feed(input);
1275        assert!(matches!(r, Err(Http1Error::SmugglingDetected(_))));
1276    }
1277
1278    #[test]
1279    fn test_incremental_parsing() {
1280        let mut p = Http1Parser::new(make_config());
1281        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
1282        let (req1, consumed1) = p.feed(&input[..10]).unwrap();
1283        assert!(req1.is_none());
1284        assert_eq!(consumed1, 0, "partial input should not be fully consumed");
1285        let (req2, consumed2) = p.feed(&input[10..]).unwrap();
1286        assert!(req2.is_some(), "should parse full request on second call");
1287        assert_eq!(consumed2, input.len() - 10);
1288    }
1289
1290    #[test]
1291    fn test_reset() {
1292        let mut p = Http1Parser::new(make_config());
1293        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
1294        p.feed(input).unwrap();
1295        p.reset();
1296        assert_eq!(p.state(), ParserState::WaitingRequest);
1297    }
1298
1299    #[test]
1300    fn test_invalid_header_name_rejected() {
1301        let mut p = Http1Parser::new(make_config());
1302        let input = b"GET / HTTP/1.1\r\nHost: x\r\nBad\x00: value\r\n\r\n";
1303        let r = p.feed(input);
1304        assert!(r.is_err());
1305    }
1306
1307    #[test]
1308    fn test_smuggling_detected_by_parser() {
1309        let mut p = Http1Parser::new(make_config());
1310        let input = b"GET / HTTP/1.1\r\nHost: x\r\nContent-Length: 0\r\nTransfer-Encoding: chunked\r\n\r\n";
1311        let r = p.feed(input);
1312        assert!(r.is_err(), "expected CL.TE smuggling error, got {r:?}");
1313    }
1314
1315    #[test]
1316    fn test_double_content_length_rejected() {
1317        let mut p = Http1Parser::new(make_config());
1318        let input = b"GET / HTTP/1.1\r\nHost: x\r\nContent-Length: 10\r\nContent-Length: 20\r\n\r\n";
1319        let r = p.feed(input);
1320        assert!(r.is_err(), "expected double CL error, got {r:?}");
1321    }
1322
1323    #[test]
1324    fn test_parser_state_variants() {
1325        let states = [
1326            ParserState::WaitingRequest,
1327            ParserState::ReadingRequest,
1328            ParserState::ReadingHeaders,
1329            ParserState::ReadingBody,
1330            ParserState::ReadingChunkSize,
1331            ParserState::ReadingChunkData,
1332            ParserState::ReadingChunkTrailer,
1333            ParserState::HeadersComplete,
1334            ParserState::Error,
1335        ];
1336        for (i, s) in states.iter().enumerate() {
1337            assert_eq!(*s, states[i]);
1338        }
1339        assert_ne!(ParserState::WaitingRequest, ParserState::Error);
1340    }
1341
1342    #[test]
1343    fn test_parse_http10_request() {
1344        let mut p = Http1Parser::new(make_config());
1345        let input = b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
1346        let (req, _) = p.feed(input).unwrap();
1347        let req = req.unwrap();
1348        assert_eq!(req.line.version.as_ref(), "HTTP/1.0");
1349    }
1350
1351    #[test]
1352    fn test_header_name_lowercase_normalization() {
1353        let mut p = Http1Parser::new(make_config());
1354        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\nContent-Type: text/plain\r\nX-Custom-Header: value\r\n\r\n";
1355        let (req, _) = p.feed(input).unwrap();
1356        let req = req.unwrap();
1357        assert_eq!(req.get_header("host"), Some("example.com"));
1358        assert_eq!(req.get_header("content-type"), Some("text/plain"));
1359        assert_eq!(req.get_header("x-custom-header"), Some("value"));
1360    }
1361
1362    #[test]
1363    fn test_header_value_leading_whitespace_trimmed() {
1364        let mut p = Http1Parser::new(make_config());
1365        let input = b"GET / HTTP/1.1\r\nHost:   example.com\r\nX-Test:\tvalue\r\n\r\n";
1366        let (req, _) = p.feed(input).unwrap();
1367        let req = req.unwrap();
1368        assert_eq!(req.get_header("host"), Some("example.com"));
1369        assert_eq!(req.get_header("x-test"), Some("value"));
1370    }
1371
1372    #[test]
1373    fn test_missing_method_in_request_line() {
1374        let mut p = Http1Parser::new(make_config());
1375        let input = b" / HTTP/1.1\r\nHost: x\r\n\r\n";
1376        let r = p.feed(input);
1377        assert!(r.is_err());
1378    }
1379
1380    #[test]
1381    fn test_missing_version_in_request_line() {
1382        let mut p = Http1Parser::new(make_config());
1383        let input = b"GET /\r\nHost: x\r\n\r\n";
1384        let r = p.feed(input);
1385        assert!(r.is_err());
1386    }
1387
1388    #[test]
1389    fn test_only_method_in_request_line() {
1390        let mut p = Http1Parser::new(make_config());
1391        let input = b"GET\r\nHost: x\r\n\r\n";
1392        let r = p.feed(input);
1393        assert!(r.is_err());
1394    }
1395
1396    #[test]
1397    fn test_header_line_folding_rejected() {
1398        let mut p = Http1Parser::new(make_config());
1399        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\n continued\r\n\r\n";
1400        let r = p.feed(input);
1401        assert!(r.is_err());
1402    }
1403
1404    #[test]
1405    fn test_header_line_folding_tab_rejected() {
1406        let mut p = Http1Parser::new(make_config());
1407        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\n\tcontinued\r\n\r\n";
1408        let r = p.feed(input);
1409        assert!(r.is_err());
1410    }
1411
1412    #[test]
1413    fn test_header_missing_colon() {
1414        let mut p = Http1Parser::new(make_config());
1415        let input = b"GET / HTTP/1.1\r\nHost example.com\r\n\r\n";
1416        let r = p.feed(input);
1417        assert!(r.is_err());
1418    }
1419
1420    #[test]
1421    fn test_empty_input() {
1422        let mut p = Http1Parser::new(make_config());
1423        let (req, consumed) = p.feed(b"").unwrap();
1424        assert!(req.is_none());
1425        assert_eq!(consumed, 0);
1426    }
1427
1428    #[test]
1429    fn test_multiple_headers_same_name() {
1430        let mut p = Http1Parser::new(make_config());
1431        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\nX-Test: first\r\nX-Test: second\r\n\r\n";
1432        let (req, _) = p.feed(input).unwrap();
1433        let req = req.unwrap();
1434        let test_headers: Vec<_> = req.headers.iter().filter(|(k, _)| k.as_ref() == "x-test").collect();
1435        assert_eq!(test_headers.len(), 2);
1436    }
1437
1438    #[test]
1439    fn test_request_line_with_query_string() {
1440        let mut p = Http1Parser::new(make_config());
1441        let input = b"GET /path?query=value&foo=bar HTTP/1.1\r\nHost: example.com\r\n\r\n";
1442        let (req, _) = p.feed(input).unwrap();
1443        let req = req.unwrap();
1444        assert_eq!(req.line.target.as_ref(), "/path?query=value&foo=bar");
1445    }
1446
1447    #[test]
1448    fn test_all_valid_methods() {
1449        let methods = ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE"];
1450        for method in methods.iter() {
1451            let mut p = Http1Parser::new(make_config());
1452            let input = format!("{} / HTTP/1.1\r\nHost: example.com\r\n\r\n", method);
1453            let (req, _) = p.feed(input.as_bytes()).unwrap();
1454            let req = req.unwrap();
1455            assert_eq!(req.line.method.as_ref(), *method);
1456        }
1457    }
1458
1459    #[test]
1460    fn test_lowercase_method_rejected() {
1461        // RFC 7230 §3.1.1:方法区分大小写,"get" 必须拒绝(与 normalize 层一致)
1462        let mut p = Http1Parser::new(make_config());
1463        let input = b"get / HTTP/1.1\r\nHost: example.com\r\n\r\n";
1464        let r = p.feed(input);
1465        assert!(
1466            matches!(r, Err(Http1Error::UnsupportedMethod(_))),
1467            "小写方法必须拒绝,实际 {r:?}"
1468        );
1469        // 混合大小写同样拒绝
1470        let mut p = Http1Parser::new(make_config());
1471        let input = b"Get / HTTP/1.1\r\nHost: example.com\r\n\r\n";
1472        assert!(matches!(p.feed(input), Err(Http1Error::UnsupportedMethod(_))));
1473    }
1474
1475    #[test]
1476    fn test_http10_default_connection_close() {
1477        // RFC 1945:HTTP/1.0 默认关闭连接
1478        let mut p = Http1Parser::new(make_config());
1479        let input = b"GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
1480        let (req, _) = p.feed(input).unwrap();
1481        let req = req.unwrap();
1482        assert!(!req.keep_alive, "HTTP/1.0 无 Connection 头应默认关闭");
1483    }
1484
1485    #[test]
1486    fn test_http10_explicit_keep_alive() {
1487        // HTTP/1.0 仅显式 Connection: keep-alive 才保持
1488        let mut p = Http1Parser::new(make_config());
1489        let input = b"GET / HTTP/1.0\r\nHost: example.com\r\nConnection: keep-alive\r\n\r\n";
1490        let (req, _) = p.feed(input).unwrap();
1491        let req = req.unwrap();
1492        assert!(req.keep_alive);
1493    }
1494
1495    #[test]
1496    fn test_http11_default_keep_alive() {
1497        // HTTP/1.1 默认保持;显式 close 才关闭
1498        let mut p = Http1Parser::new(make_config());
1499        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n";
1500        let (req, _) = p.feed(input).unwrap();
1501        assert!(req.unwrap().keep_alive);
1502        let mut p = Http1Parser::new(make_config());
1503        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";
1504        let (req, _) = p.feed(input).unwrap();
1505        assert!(!req.unwrap().keep_alive);
1506    }
1507
1508    #[test]
1509    fn test_http10_transfer_encoding_rejected() {
1510        // HTTP/1.0 请求出现 Transfer-Encoding 一律拒绝(fail-closed)
1511        let mut p = Http1Parser::new(make_config());
1512        let input = b"POST / HTTP/1.0\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n0\r\n\r\n";
1513        let r = p.feed(input);
1514        assert!(
1515            matches!(r, Err(Http1Error::SmugglingDetected(_))),
1516            "HTTP/1.0 + TE 必须拒绝,实际 {r:?}"
1517        );
1518    }
1519
1520    #[test]
1521    fn test_transfer_encoding_without_chunked_rejected() {
1522        // TE 存在但无 chunked 且非 identity → 走私拒绝(fail-closed)
1523        let mut p = Http1Parser::new(make_config());
1524        let input = b"POST / HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: gzip\r\n\r\n";
1525        let r = p.feed(input);
1526        assert!(
1527            matches!(r, Err(Http1Error::SmugglingDetected(_))),
1528            "TE 无 chunked 必须拒绝,实际 {r:?}"
1529        );
1530    }
1531
1532    #[test]
1533    fn test_transfer_encoding_identity_rejected() {
1534        // HTTP-002:TE: identity 已被 RFC 7230 废弃,必须拒绝(fail-closed)
1535        let mut p = Http1Parser::new(make_config());
1536        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: identity\r\n\r\n";
1537        let r = p.feed(input);
1538        assert!(
1539            matches!(r, Err(Http1Error::SmugglingDetected(_))),
1540            "TE: identity 必须走私拒绝,实际 {r:?}"
1541        );
1542    }
1543
1544    #[test]
1545    fn test_transfer_encoding_gzip_chunked_parsed() {
1546        // 含 chunked 才按 chunked 解析(gzip, chunked 中 chunked 在最后,合法)
1547        let mut p = Http1Parser::new(make_config());
1548        let input = b"POST / HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: gzip, chunked\r\n\r\n5\r\nHello\r\n0\r\n\r\n";
1549        let (req, _) = p.feed(input).unwrap();
1550        let req = req.unwrap();
1551        assert!(req.chunked);
1552        assert_eq!(req.body, b"Hello");
1553    }
1554
1555    #[test]
1556    fn test_header_name_too_long() {
1557        let config = Http1Config::new().with_max_header_size(1024);
1558        let mut p = Http1Parser::new(config);
1559        let long_name = "X-".to_string() + &"A".repeat(100);
1560        let input = format!("GET / HTTP/1.1\r\nHost: x\r\n{}: value\r\n\r\n", long_name);
1561        let r = p.feed(input.as_bytes());
1562        let _ = r;
1563    }
1564
1565    #[test]
1566    fn test_control_char_in_target_rejected() {
1567        let mut p = Http1Parser::new(make_config());
1568        let input = b"GET /path\x01 HTTP/1.1\r\nHost: x\r\n\r\n";
1569        let r = p.feed(input);
1570        assert!(r.is_err());
1571    }
1572
1573    #[test]
1574    fn test_parser_clone() {
1575        let p = Http1Parser::new(make_config());
1576        let p2 = p.clone();
1577        assert_eq!(p.state(), p2.state());
1578    }
1579
1580    #[test]
1581    fn test_parser_debug_format() {
1582        let p = Http1Parser::new(make_config());
1583        let s = format!("{:?}", p);
1584        assert!(!s.is_empty());
1585    }
1586
1587    #[test]
1588    fn test_connection_close_header() {
1589        let mut p = Http1Parser::new(make_config());
1590        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n";
1591        let (req, _) = p.feed(input).unwrap();
1592        let req = req.unwrap();
1593        assert!(!req.keep_alive);
1594    }
1595
1596    // ───────── S-2:Content-Length 解析 fail-closed ─────────
1597
1598    #[test]
1599    fn test_content_length_overflow_u64_rejected() {
1600        // S-2:超 u64 的全数字 CL(23 位 9)必须拒绝,禁止当"无 body"放行
1601        let mut p = Http1Parser::new(make_config());
1602        let input = b"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 99999999999999999999999\r\n\r\n";
1603        let r = p.feed(input);
1604        assert!(
1605            matches!(r, Err(Http1Error::SmugglingDetected(_))),
1606            "超 u64 的 CL 必须 fail-closed 拒绝,实际 {r:?}"
1607        );
1608    }
1609
1610    #[test]
1611    fn test_content_length_valid_still_accepted() {
1612        // 对照组:合法 CL 不受影响
1613        let mut p = Http1Parser::new(make_config());
1614        let input = b"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 5\r\n\r\nhello";
1615        let (req, _) = p.feed(input).unwrap();
1616        let req = req.unwrap();
1617        assert_eq!(req.content_length, Some(5));
1618        assert_eq!(req.body, b"hello");
1619    }
1620
1621    // ───────── M-3:完整请求行长度上限 enforcement ─────────
1622
1623    #[test]
1624    fn test_complete_request_line_too_long_rejected() {
1625        // M-3:已找到 CRLF 的完整请求行超长同样拒绝
1626        let config = Http1Config::new();
1627        let line_limit = config.max_request_line_size;
1628        let mut p = Http1Parser::new(config);
1629        let long_path = "a".repeat(line_limit + 1);
1630        let input = format!("GET /{long_path} HTTP/1.1\r\nHost: x\r\n\r\n");
1631        let r = p.feed(input.as_bytes());
1632        assert!(
1633            matches!(r, Err(Http1Error::RequestLineTooLong)),
1634            "完整超长请求行必须拒绝,实际 {r:?}"
1635        );
1636        assert_eq!(p.state(), ParserState::Error);
1637    }
1638
1639    #[test]
1640    fn test_incomplete_request_line_too_long_rejected() {
1641        // 对照:未完成请求行(无 CRLF)超长同样拒绝
1642        let config = Http1Config::new();
1643        let line_limit = config.max_request_line_size;
1644        let mut p = Http1Parser::new(config);
1645        let junk = "a".repeat(line_limit + 1);
1646        let r = p.feed(junk.as_bytes());
1647        assert!(matches!(r, Err(Http1Error::RequestLineTooLong)), "实际 {r:?}");
1648    }
1649
1650    #[test]
1651    fn test_request_line_at_limit_accepted() {
1652        // 边界:请求行长度恰好等于 max_request_line_size 放行
1653        let config = Http1Config::new();
1654        let line_limit = config.max_request_line_size;
1655        // "GET /" + pad + " HTTP/1.1" 恰好 line_limit 字节
1656        let pad = line_limit - "GET /".len() - " HTTP/1.1".len();
1657        let mut p = Http1Parser::new(config);
1658        let input = format!("GET /{} HTTP/1.1\r\nHost: x\r\n\r\n", "a".repeat(pad));
1659        let (req, _) = p.feed(input.as_bytes()).unwrap();
1660        assert!(req.is_some(), "恰在上限内的请求行必须放行");
1661    }
1662
1663    // ───────── S-1:absolute-form 与 Host 一致性 ─────────
1664
1665    #[test]
1666    fn test_absolute_form_matching_host_accepted() {
1667        // S-1 正向:absolute-form authority 与 Host 一致放行(大小写不敏感)
1668        let mut p = Http1Parser::new(make_config());
1669        let input = b"GET http://example.com/path?q=1 HTTP/1.1\r\nHost: EXAMPLE.com\r\n\r\n";
1670        let (req, _) = p.feed(input).unwrap();
1671        let req = req.unwrap();
1672        assert_eq!(req.line.target.as_ref(), "http://example.com/path?q=1");
1673    }
1674
1675    #[test]
1676    fn test_absolute_form_with_port_matching_host_accepted() {
1677        // S-1 正向:authority 与 Host 均含端口且一致放行
1678        let mut p = Http1Parser::new(make_config());
1679        let input = b"GET https://example.com:8443/x HTTP/1.1\r\nHost: example.com:8443\r\n\r\n";
1680        let (req, _) = p.feed(input).unwrap();
1681        assert!(req.is_some());
1682    }
1683
1684    #[test]
1685    fn test_absolute_form_host_mismatch_rejected() {
1686        // S-1 反向:absolute-form authority 与 Host 不一致 → 协议不一致拒绝
1687        let mut p = Http1Parser::new(make_config());
1688        let input = b"GET http://evil.com/ HTTP/1.1\r\nHost: example.com\r\n\r\n";
1689        let r = p.feed(input);
1690        assert!(
1691            matches!(r, Err(Http1Error::ProtocolInconsistency(_))),
1692            "authority 与 Host 不一致必须拒绝,实际 {r:?}"
1693        );
1694    }
1695
1696    #[test]
1697    fn test_absolute_form_port_mismatch_rejected() {
1698        // S-1 反向:authority 含端口、Host 不含(不做归一化放行歧义)
1699        let mut p = Http1Parser::new(make_config());
1700        let input = b"GET http://example.com:8080/ HTTP/1.1\r\nHost: example.com\r\n\r\n";
1701        let r = p.feed(input);
1702        assert!(matches!(r, Err(Http1Error::ProtocolInconsistency(_))), "实际 {r:?}");
1703    }
1704
1705    #[test]
1706    fn test_origin_form_unaffected_by_host_check() {
1707        // 对照:origin-form 目标不受 absolute-form 校验影响
1708        let mut p = Http1Parser::new(make_config());
1709        let input = b"GET /path HTTP/1.1\r\nHost: example.com\r\n\r\n";
1710        let (req, _) = p.feed(input).unwrap();
1711        assert!(req.is_some());
1712    }
1713
1714    // ───────── M-8:Connection 头 token 列表解析 ─────────
1715
1716    #[test]
1717    fn test_connection_close_among_tokens() {
1718        // "close, upgrade" 必须识别为 close 语义(整串精确比较会漏判)
1719        let mut p = Http1Parser::new(make_config());
1720        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close, upgrade\r\n\r\n";
1721        let (req, _) = p.feed(input).unwrap();
1722        let req = req.unwrap();
1723        assert!(!req.keep_alive, "token 列表中的 close 必须生效");
1724    }
1725
1726    #[test]
1727    fn test_connection_keep_alive_among_tokens_http10() {
1728        // HTTP/1.0 "keep-alive, timeout=5" 必须识别为 keep-alive 语义
1729        let mut p = Http1Parser::new(make_config());
1730        let input =
1731            b"GET / HTTP/1.0\r\nHost: example.com\r\nConnection: keep-alive, timeout=5\r\n\r\n";
1732        let (req, _) = p.feed(input).unwrap();
1733        let req = req.unwrap();
1734        assert!(req.keep_alive, "token 列表中的 keep-alive 必须生效");
1735    }
1736
1737    #[test]
1738    fn test_connection_prefixed_token_not_close() {
1739        // "x-close" 不得误判为 close(逐 token 精确比较,非子串)
1740        let mut p = Http1Parser::new(make_config());
1741        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\nConnection: x-close\r\n\r\n";
1742        let (req, _) = p.feed(input).unwrap();
1743        let req = req.unwrap();
1744        assert!(req.keep_alive);
1745    }
1746
1747    // ───────── L-7:头部值尾部 OWS 裁剪 ─────────
1748
1749    #[test]
1750    fn test_header_value_trailing_ows_trimmed() {
1751        // L-7:尾部 SP/HTAB 同样裁剪(与客户端 OWS 语义一致)
1752        let mut p = Http1Parser::new(make_config());
1753        let input = b"GET / HTTP/1.1\r\nHost: example.com\r\nX-Test: value  \t\r\n\r\n";
1754        let (req, _) = p.feed(input).unwrap();
1755        let req = req.unwrap();
1756        assert_eq!(req.get_header("x-test"), Some("value"));
1757    }
1758
1759    // ───────── M-9:body 阶段 Slowloris 空闲超时 ─────────
1760
1761    #[test]
1762    fn test_idle_timeout_during_body_read() {
1763        // M-9:ReadingBody 阶段超过 idle_timeout 必须返回 IdleTimeout
1764        let config = Http1Config::new().with_idle_timeout_ms(30_000);
1765        let mut p = Http1Parser::new(config);
1766        p.note_activity(1_000);
1767        // 仅头部(CL=10 但 body 未到达)→ 进入 ReadingBody
1768        let input = b"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 10\r\n\r\n";
1769        let (req, _) = p.feed(input).unwrap();
1770        assert!(req.is_none());
1771        assert_eq!(p.state(), ParserState::ReadingBody);
1772        // 超时未再收到 body 字节 → IdleTimeout
1773        let r = p.check_idle_timeout(1_000 + 30_001);
1774        assert!(matches!(r, Err(Http1Error::IdleTimeout)), "实际 {r:?}");
1775        assert_eq!(p.state(), ParserState::Error);
1776    }
1777
1778    #[test]
1779    fn test_idle_timeout_refreshed_by_activity() {
1780        // M-9:活跃刷新后重新计时,未超时放行;持续静默才超时
1781        let config = Http1Config::new().with_idle_timeout_ms(30_000);
1782        let mut p = Http1Parser::new(config);
1783        p.note_activity(0);
1784        let part1 = b"POST / HTTP/1.1\r\nHost: example.com\r\nContent-Length: 10\r\n\r\nhe";
1785        let (r1, _) = p.feed(part1).unwrap();
1786        assert!(r1.is_none());
1787        assert_eq!(p.state(), ParserState::ReadingBody);
1788        // 20s 后又有数据到达(活跃刷新)
1789        p.note_activity(20_000);
1790        assert!(p.check_idle_timeout(45_000).is_ok(), "活跃后 25s 不得超时");
1791        // 但自 20s 后再静默超过 30s → 超时
1792        let r = p.check_idle_timeout(20_000 + 30_001);
1793        assert!(matches!(r, Err(Http1Error::IdleTimeout)));
1794    }
1795
1796    #[test]
1797    fn test_idle_timeout_chunked_phase() {
1798        // M-9:chunked 阶段同样适用 idle 超时
1799        let config = Http1Config::new().with_idle_timeout_ms(30_000);
1800        let mut p = Http1Parser::new(config);
1801        p.note_activity(500);
1802        let input = b"POST / HTTP/1.1\r\nHost: example.com\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nHel";
1803        let (r1, _) = p.feed(input).unwrap();
1804        assert!(r1.is_none());
1805        assert_eq!(p.state(), ParserState::ReadingChunkData);
1806        let r = p.check_idle_timeout(500 + 30_001);
1807        assert!(matches!(r, Err(Http1Error::IdleTimeout)));
1808    }
1809
1810    // ───────── L-3:pipelining consumed 记账 ─────────
1811
1812    #[test]
1813    fn test_pipelining_two_requests_single_feed() {
1814        // L-3:一次 feed 两个完整请求 → 第一个请求 + consumed 恰好为其字节数
1815        let mut p = Http1Parser::new(make_config());
1816        let req1 = b"GET /a HTTP/1.1\r\nHost: example.com\r\n\r\n";
1817        let req2 = b"GET /b HTTP/1.1\r\nHost: example.com\r\n\r\n";
1818        let mut input = Vec::with_capacity(req1.len() + req2.len());
1819        input.extend_from_slice(req1);
1820        input.extend_from_slice(req2);
1821
1822        let (r1, c1) = p.feed(&input).unwrap();
1823        let r1 = r1.expect("first request must complete");
1824        assert_eq!(r1.line.target.as_ref(), "/a");
1825        assert_eq!(c1, req1.len(), "consumed 必须恰好等于第一个请求字节数");
1826
1827        // 第二个请求已在 parser 内部缓冲,空 feed 取出
1828        let (r2, c2) = p.feed(&[]).unwrap();
1829        let r2 = r2.expect("second request must be buffered internally");
1830        assert_eq!(r2.line.target.as_ref(), "/b");
1831        assert_eq!(c2, 0, "空输入不消耗字节");
1832    }
1833
1834    #[test]
1835    fn test_pipelining_body_then_next_request() {
1836        // L-3:带 body 的请求后接 pipelined 请求,consumed 含 body 字节
1837        let mut p = Http1Parser::new(make_config());
1838        let req1 = b"POST /x HTTP/1.1\r\nHost: example.com\r\nContent-Length: 5\r\n\r\nhello";
1839        let req2 = b"GET /y HTTP/1.1\r\nHost: example.com\r\n\r\n";
1840        let mut input = Vec::with_capacity(req1.len() + req2.len());
1841        input.extend_from_slice(req1);
1842        input.extend_from_slice(req2);
1843
1844        let (r1, c1) = p.feed(&input).unwrap();
1845        let r1 = r1.expect("first request must complete");
1846        assert_eq!(r1.body, b"hello");
1847        assert_eq!(c1, req1.len());
1848
1849        let (r2, _) = p.feed(&[]).unwrap();
1850        let r2 = r2.expect("second request must follow");
1851        assert_eq!(r2.line.target.as_ref(), "/y");
1852    }
1853}