http_pack/
h1.rs

1use bytes::{Buf, BytesMut};
2use http::{HeaderName, HeaderValue, Method, StatusCode};
3use httparse::Status;
4use std::collections::VecDeque;
5
6use crate::{
7    stream::{
8        StreamBody, StreamEnd, StreamFrame, StreamHeaders, StreamRequestHeaders,
9        StreamResponseHeaders,
10    },
11    HeaderField, HttpVersion, PackedMessage, PackedRequest, PackedResponse,
12};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum H1MessageKind {
16    Request,
17    Response,
18}
19
20#[derive(Debug)]
21pub enum H1DecodeError {
22    InvalidStartLine,
23    InvalidVersion(u8),
24    InvalidHeader,
25    TooManyHeaders(usize),
26    InvalidMethod,
27    InvalidPath,
28    InvalidStatus,
29    InvalidHeaderName,
30    InvalidHeaderValue,
31    InvalidContentLength,
32    InvalidChunkedEncoding,
33}
34
35impl std::fmt::Display for H1DecodeError {
36    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37        match self {
38            H1DecodeError::InvalidStartLine => write!(f, "invalid start line"),
39            H1DecodeError::InvalidVersion(version) => {
40                write!(f, "unsupported http version: {}", version)
41            }
42            H1DecodeError::InvalidHeader => write!(f, "invalid header"),
43            H1DecodeError::TooManyHeaders(count) => write!(f, "too many headers: {}", count),
44            H1DecodeError::InvalidMethod => write!(f, "invalid method"),
45            H1DecodeError::InvalidPath => write!(f, "invalid path"),
46            H1DecodeError::InvalidStatus => write!(f, "invalid status"),
47            H1DecodeError::InvalidHeaderName => write!(f, "invalid header name"),
48            H1DecodeError::InvalidHeaderValue => write!(f, "invalid header value"),
49            H1DecodeError::InvalidContentLength => write!(f, "invalid content-length"),
50            H1DecodeError::InvalidChunkedEncoding => write!(f, "invalid chunked encoding"),
51        }
52    }
53}
54
55impl std::error::Error for H1DecodeError {}
56
57pub struct H1Decoder {
58    kind: H1MessageKind,
59    buf: BytesMut,
60}
61
62pub struct H1StreamDecoder {
63    kind: H1MessageKind,
64    stream_id: u64,
65    buf: BytesMut,
66    out: VecDeque<StreamFrame>,
67    state: H1StreamState,
68}
69
70enum H1StreamState {
71    ReadingHeaders,
72    ReadingBody(BodyReader),
73    Done,
74}
75
76struct BodyReader {
77    kind: BodyKind,
78}
79
80enum BodyKind {
81    None,
82    Length { remaining: usize },
83    Chunked { phase: ChunkPhase },
84}
85
86enum ChunkPhase {
87    SizeLine,
88    Data { remaining: usize },
89    DataCrlf,
90    Trailers,
91}
92
93impl H1StreamDecoder {
94    pub fn new(kind: H1MessageKind, stream_id: u64) -> Self {
95        Self {
96            kind,
97            stream_id,
98            buf: BytesMut::new(),
99            out: VecDeque::new(),
100            state: H1StreamState::ReadingHeaders,
101        }
102    }
103
104    pub fn push(&mut self, data: &[u8]) {
105        self.buf.extend_from_slice(data);
106    }
107
108    pub fn try_decode(&mut self) -> Result<Option<StreamFrame>, H1DecodeError> {
109        if let Some(frame) = self.out.pop_front() {
110            return Ok(Some(frame));
111        }
112        self.fill_out()?;
113        Ok(self.out.pop_front())
114    }
115
116    pub fn try_decode_message(
117        &mut self,
118    ) -> Result<Option<crate::packetizer::HttpPackStreamMessage>, H1DecodeError> {
119        match self.try_decode()? {
120            Some(frame) => Ok(Some(crate::packetizer::HttpPackStreamMessage::from_frame(
121                &frame,
122            ))),
123            None => Ok(None),
124        }
125    }
126
127    pub fn buffer_len(&self) -> usize {
128        self.buf.len()
129    }
130
131    fn fill_out(&mut self) -> Result<(), H1DecodeError> {
132        if !self.out.is_empty() {
133            return Ok(());
134        }
135
136        match &mut self.state {
137            H1StreamState::ReadingHeaders => {
138                let (headers, body_kind) = match self.kind {
139                    H1MessageKind::Request => {
140                        let (method, path, header_len, headers) =
141                            match parse_request_headers(&self.buf)? {
142                                Some(value) => value,
143                                None => return Ok(()),
144                            };
145                        self.buf.advance(header_len);
146                        let authority =
147                            find_header_value(&headers, b"host").map(|value| value.to_vec());
148                        let path = if path.is_empty() {
149                            b"/".as_slice()
150                        } else {
151                            path.as_slice()
152                        };
153                        let body_kind = body_kind_from_headers(&headers)?;
154                        let header = StreamHeaders::Request(StreamRequestHeaders {
155                            stream_id: self.stream_id,
156                            version: HttpVersion::Http11,
157                            method,
158                            scheme: None,
159                            authority,
160                            path: path.to_vec(),
161                            headers,
162                        });
163                        (header, body_kind)
164                    }
165                    H1MessageKind::Response => {
166                        let (status, header_len, headers) =
167                            match parse_response_headers(&self.buf)? {
168                                Some(value) => value,
169                                None => return Ok(()),
170                            };
171                        self.buf.advance(header_len);
172                        let body_kind = body_kind_from_headers(&headers)?;
173                        let header = StreamHeaders::Response(StreamResponseHeaders {
174                            stream_id: self.stream_id,
175                            version: HttpVersion::Http11,
176                            status,
177                            headers,
178                        });
179                        (header, body_kind)
180                    }
181                };
182
183                self.out.push_back(StreamFrame::Headers(headers));
184                match body_kind {
185                    BodyKind::None => {
186                        self.out.push_back(StreamFrame::End(StreamEnd {
187                            stream_id: self.stream_id,
188                        }));
189                        self.state = H1StreamState::Done;
190                    }
191                    BodyKind::Length { remaining } if remaining == 0 => {
192                        self.out.push_back(StreamFrame::End(StreamEnd {
193                            stream_id: self.stream_id,
194                        }));
195                        self.state = H1StreamState::Done;
196                    }
197                    body_kind => {
198                        self.state = H1StreamState::ReadingBody(BodyReader { kind: body_kind });
199                    }
200                }
201            }
202            H1StreamState::ReadingBody(reader) => {
203                if let Some(frame) = read_body_frame(&mut self.buf, reader, self.stream_id)? {
204                    self.out.push_back(frame);
205                    if matches!(reader.kind, BodyKind::None) {
206                        self.state = H1StreamState::Done;
207                    }
208                } else if matches!(reader.kind, BodyKind::None) {
209                    self.state = H1StreamState::Done;
210                }
211            }
212            H1StreamState::Done => {}
213        }
214
215        Ok(())
216    }
217}
218
219impl H1Decoder {
220    pub fn new(kind: H1MessageKind) -> Self {
221        Self {
222            kind,
223            buf: BytesMut::new(),
224        }
225    }
226
227    pub fn push(&mut self, data: &[u8]) {
228        self.buf.extend_from_slice(data);
229    }
230
231    pub fn try_decode(&mut self) -> Result<Option<PackedMessage>, H1DecodeError> {
232        match decode_message_from_prefix(&self.buf, self.kind)? {
233            Some((message, consumed)) => {
234                self.buf.advance(consumed);
235                Ok(Some(message))
236            }
237            None => Ok(None),
238        }
239    }
240
241    pub fn buffer_len(&self) -> usize {
242        self.buf.len()
243    }
244}
245
246pub fn decode_request(bytes: &[u8]) -> Result<Option<(PackedRequest, usize)>, H1DecodeError> {
247    match decode_message_from_prefix(bytes, H1MessageKind::Request)? {
248        Some((PackedMessage::Request(req), consumed)) => Ok(Some((req, consumed))),
249        Some(_) => Err(H1DecodeError::InvalidStartLine),
250        None => Ok(None),
251    }
252}
253
254pub fn decode_response(bytes: &[u8]) -> Result<Option<(PackedResponse, usize)>, H1DecodeError> {
255    match decode_message_from_prefix(bytes, H1MessageKind::Response)? {
256        Some((PackedMessage::Response(resp), consumed)) => Ok(Some((resp, consumed))),
257        Some(_) => Err(H1DecodeError::InvalidStartLine),
258        None => Ok(None),
259    }
260}
261
262fn decode_message_from_prefix(
263    bytes: &[u8],
264    kind: H1MessageKind,
265) -> Result<Option<(PackedMessage, usize)>, H1DecodeError> {
266    match kind {
267        H1MessageKind::Request => decode_request_from_prefix(bytes).map(|opt| {
268            opt.map(|(req, consumed)| (PackedMessage::Request(req), consumed))
269        }),
270        H1MessageKind::Response => decode_response_from_prefix(bytes).map(|opt| {
271            opt.map(|(resp, consumed)| (PackedMessage::Response(resp), consumed))
272        }),
273    }
274}
275
276fn decode_request_from_prefix(
277    bytes: &[u8],
278) -> Result<Option<(PackedRequest, usize)>, H1DecodeError> {
279    let (method, path, header_len, headers) = match parse_request_headers(bytes)? {
280        Some(value) => value,
281        None => return Ok(None),
282    };
283
284    let authority = find_header_value(&headers, b"host").map(|value| value.to_vec());
285    let path = if path.is_empty() {
286        b"/".as_slice()
287    } else {
288        path.as_slice()
289    };
290
291    let (body, body_len) = match decode_body(bytes, header_len, &headers)? {
292        Some(value) => value,
293        None => return Ok(None),
294    };
295
296    Ok(Some((
297        PackedRequest {
298            version: HttpVersion::Http11,
299            method: method.to_vec(),
300            scheme: None,
301            authority,
302            path: path.to_vec(),
303            headers,
304            body,
305        },
306        header_len + body_len,
307    )))
308}
309
310fn decode_response_from_prefix(
311    bytes: &[u8],
312) -> Result<Option<(PackedResponse, usize)>, H1DecodeError> {
313    let (status, header_len, headers) = match parse_response_headers(bytes)? {
314        Some(value) => value,
315        None => return Ok(None),
316    };
317
318    let (body, body_len) = match decode_body(bytes, header_len, &headers)? {
319        Some(value) => value,
320        None => return Ok(None),
321    };
322
323    Ok(Some((
324        PackedResponse {
325            version: HttpVersion::Http11,
326            status,
327            headers,
328            body,
329        },
330        header_len + body_len,
331    )))
332}
333
334fn parse_request_headers(
335    bytes: &[u8],
336) -> Result<Option<(Vec<u8>, Vec<u8>, usize, Vec<HeaderField>)>, H1DecodeError> {
337    let mut header_cap = 32usize;
338    loop {
339        let mut headers = vec![httparse::EMPTY_HEADER; header_cap];
340        let mut req = httparse::Request::new(&mut headers);
341        let status = match req.parse(bytes) {
342            Ok(status) => status,
343            Err(err) => {
344                if let httparse::Error::TooManyHeaders = err {
345                    header_cap = grow_header_cap(header_cap)?;
346                    continue;
347                }
348                return Err(map_parse_error(err));
349            }
350        };
351
352        let header_len = match status {
353            Status::Complete(len) => len,
354            Status::Partial => return Ok(None),
355        };
356
357        let method = req.method.ok_or(H1DecodeError::InvalidStartLine)?;
358        let path = req.path.ok_or(H1DecodeError::InvalidStartLine)?;
359        let version = req.version.ok_or(H1DecodeError::InvalidStartLine)?;
360        if version != 1 {
361            return Err(H1DecodeError::InvalidVersion(version));
362        }
363
364        Method::from_bytes(method.as_bytes()).map_err(|_| H1DecodeError::InvalidMethod)?;
365        if path.is_empty() || crate::has_crlf(path.as_bytes()) {
366            return Err(H1DecodeError::InvalidPath);
367        }
368
369        let header_fields = collect_headers(&req.headers)?;
370
371        return Ok(Some((
372            method.as_bytes().to_vec(),
373            path.as_bytes().to_vec(),
374            header_len,
375            header_fields,
376        )));
377    }
378}
379
380fn parse_response_headers(
381    bytes: &[u8],
382) -> Result<Option<(u16, usize, Vec<HeaderField>)>, H1DecodeError> {
383    let mut header_cap = 32usize;
384    loop {
385        let mut headers = vec![httparse::EMPTY_HEADER; header_cap];
386        let mut resp = httparse::Response::new(&mut headers);
387        let status = match resp.parse(bytes) {
388            Ok(status) => status,
389            Err(err) => {
390                if let httparse::Error::TooManyHeaders = err {
391                    header_cap = grow_header_cap(header_cap)?;
392                    continue;
393                }
394                return Err(map_parse_error(err));
395            }
396        };
397
398        let header_len = match status {
399            Status::Complete(len) => len,
400            Status::Partial => return Ok(None),
401        };
402
403        let version = resp.version.ok_or(H1DecodeError::InvalidStartLine)?;
404        if version != 1 {
405            return Err(H1DecodeError::InvalidVersion(version));
406        }
407
408        let status_code = resp.code.ok_or(H1DecodeError::InvalidStatus)?;
409        if StatusCode::from_u16(status_code).is_err() {
410            return Err(H1DecodeError::InvalidStatus);
411        }
412
413        let header_fields = collect_headers(&resp.headers)?;
414
415        return Ok(Some((status_code, header_len, header_fields)));
416    }
417}
418
419fn collect_headers(headers: &[httparse::Header<'_>]) -> Result<Vec<HeaderField>, H1DecodeError> {
420    if headers.len() > crate::MAX_HEADERS as usize {
421        return Err(H1DecodeError::TooManyHeaders(headers.len()));
422    }
423
424    let mut out = Vec::with_capacity(headers.len());
425    for header in headers {
426        let name = header.name.as_bytes().to_vec();
427        let value = header.value.to_vec();
428        HeaderName::from_bytes(&name).map_err(|_| H1DecodeError::InvalidHeaderName)?;
429        HeaderValue::from_bytes(&value).map_err(|_| H1DecodeError::InvalidHeaderValue)?;
430        out.push(HeaderField { name, value });
431    }
432
433    Ok(out)
434}
435
436fn grow_header_cap(current: usize) -> Result<usize, H1DecodeError> {
437    let next = current.saturating_mul(2);
438    if next == current || next > crate::MAX_HEADERS as usize {
439        return Err(H1DecodeError::TooManyHeaders(current));
440    }
441    Ok(next)
442}
443
444fn map_parse_error(err: httparse::Error) -> H1DecodeError {
445    match err {
446        httparse::Error::Version => H1DecodeError::InvalidVersion(0),
447        httparse::Error::Status => H1DecodeError::InvalidStatus,
448        httparse::Error::Token => H1DecodeError::InvalidMethod,
449        httparse::Error::HeaderName => H1DecodeError::InvalidHeaderName,
450        httparse::Error::HeaderValue => H1DecodeError::InvalidHeaderValue,
451        _ => H1DecodeError::InvalidHeader,
452    }
453}
454
455fn decode_body(
456    bytes: &[u8],
457    header_len: usize,
458    headers: &[HeaderField],
459) -> Result<Option<(Vec<u8>, usize)>, H1DecodeError> {
460    let body_bytes = &bytes[header_len..];
461    if has_chunked_encoding(headers) {
462        return decode_chunked_body(body_bytes);
463    }
464
465    if let Some(len) = content_length(headers)? {
466        if body_bytes.len() < len {
467            return Ok(None);
468        }
469        return Ok(Some((body_bytes[..len].to_vec(), len)));
470    }
471
472    Ok(Some((Vec::new(), 0)))
473}
474
475fn body_kind_from_headers(headers: &[HeaderField]) -> Result<BodyKind, H1DecodeError> {
476    if has_chunked_encoding(headers) {
477        return Ok(BodyKind::Chunked {
478            phase: ChunkPhase::SizeLine,
479        });
480    }
481
482    if let Some(len) = content_length(headers)? {
483        return Ok(BodyKind::Length { remaining: len });
484    }
485
486    Ok(BodyKind::None)
487}
488
489fn read_body_frame(
490    buf: &mut BytesMut,
491    reader: &mut BodyReader,
492    stream_id: u64,
493) -> Result<Option<StreamFrame>, H1DecodeError> {
494    match &mut reader.kind {
495        BodyKind::None => Ok(None),
496        BodyKind::Length { remaining } => {
497            if *remaining == 0 {
498                reader.kind = BodyKind::None;
499                return Ok(Some(StreamFrame::End(StreamEnd { stream_id })));
500            }
501            if buf.is_empty() {
502                return Ok(None);
503            }
504            let take = (*remaining).min(buf.len());
505            let chunk = buf.split_to(take).to_vec();
506            *remaining -= take;
507            Ok(Some(StreamFrame::Body(StreamBody {
508                stream_id,
509                data: chunk.into(),
510            })))
511        }
512        BodyKind::Chunked { phase } => {
513            let frame = read_chunked_frame(buf, phase, stream_id)?;
514            if matches!(frame, Some(StreamFrame::End(_))) {
515                reader.kind = BodyKind::None;
516            }
517            Ok(frame)
518        }
519    }
520}
521
522fn read_chunked_frame(
523    buf: &mut BytesMut,
524    phase: &mut ChunkPhase,
525    stream_id: u64,
526) -> Result<Option<StreamFrame>, H1DecodeError> {
527    loop {
528        match phase {
529            ChunkPhase::SizeLine => {
530                let line_end = match find_crlf(buf, 0) {
531                    Some(value) => value,
532                    None => return Ok(None),
533                };
534                let line = &buf[..line_end];
535                let size = parse_chunk_size(line)?;
536                buf.advance(line_end + 2);
537                if size == 0 {
538                    *phase = ChunkPhase::Trailers;
539                    continue;
540                }
541                let size = usize::try_from(size)
542                    .map_err(|_| H1DecodeError::InvalidChunkedEncoding)?;
543                *phase = ChunkPhase::Data { remaining: size };
544            }
545            ChunkPhase::Data { remaining } => {
546                if buf.is_empty() {
547                    return Ok(None);
548                }
549                let take = (*remaining).min(buf.len());
550                let chunk = buf.split_to(take).to_vec();
551                *remaining -= take;
552                if *remaining == 0 {
553                    *phase = ChunkPhase::DataCrlf;
554                }
555                if chunk.is_empty() {
556                    return Ok(None);
557                }
558                return Ok(Some(StreamFrame::Body(StreamBody {
559                    stream_id,
560                    data: chunk.into(),
561                })));
562            }
563            ChunkPhase::DataCrlf => {
564                if buf.len() < 2 {
565                    return Ok(None);
566                }
567                if buf[0] != b'\r' || buf[1] != b'\n' {
568                    return Err(H1DecodeError::InvalidChunkedEncoding);
569                }
570                buf.advance(2);
571                *phase = ChunkPhase::SizeLine;
572            }
573            ChunkPhase::Trailers => {
574                let trailer_end = match find_double_crlf(buf, 0) {
575                    Some(value) => value,
576                    None => return Ok(None),
577                };
578                buf.advance(trailer_end + 4);
579                return Ok(Some(StreamFrame::End(StreamEnd { stream_id })));
580            }
581        }
582    }
583}
584
585fn content_length(headers: &[HeaderField]) -> Result<Option<usize>, H1DecodeError> {
586    let mut value = None;
587    for header in headers {
588        if !crate::eq_ignore_ascii_case(&header.name, b"content-length") {
589            continue;
590        }
591        let trimmed = trim_ascii(&header.value);
592        if trimmed.is_empty() {
593            return Err(H1DecodeError::InvalidContentLength);
594        }
595        let parsed = parse_usize_ascii(trimmed)?;
596        if let Some(existing) = value {
597            if existing != parsed {
598                return Err(H1DecodeError::InvalidContentLength);
599            }
600        } else {
601            value = Some(parsed);
602        }
603    }
604    Ok(value)
605}
606
607fn has_chunked_encoding(headers: &[HeaderField]) -> bool {
608    headers.iter().any(|header| {
609        crate::eq_ignore_ascii_case(&header.name, b"transfer-encoding")
610            && contains_token(&header.value, b"chunked")
611    })
612}
613
614fn contains_token(value: &[u8], token: &[u8]) -> bool {
615    let mut start = 0;
616    while start <= value.len() {
617        let mut end = start;
618        while end < value.len() && value[end] != b',' {
619            end += 1;
620        }
621        let part = trim_ascii(&value[start..end]);
622        if crate::eq_ignore_ascii_case(part, token) {
623            return true;
624        }
625        if end == value.len() {
626            break;
627        }
628        start = end + 1;
629    }
630    false
631}
632
633fn decode_chunked_body(bytes: &[u8]) -> Result<Option<(Vec<u8>, usize)>, H1DecodeError> {
634    let mut out = Vec::new();
635    let mut offset = 0usize;
636
637    loop {
638        let line_end = match find_crlf(bytes, offset) {
639            Some(value) => value,
640            None => return Ok(None),
641        };
642        let line = &bytes[offset..line_end];
643        let size = parse_chunk_size(line)?;
644        offset = line_end + 2;
645
646        if size == 0 {
647            if bytes.len() < offset + 2 {
648                return Ok(None);
649            }
650            if bytes.get(offset) == Some(&b'\r') && bytes.get(offset + 1) == Some(&b'\n') {
651                offset += 2;
652                break;
653            }
654            let trailer_end = match find_double_crlf(bytes, offset) {
655                Some(value) => value,
656                None => return Ok(None),
657            };
658            offset = trailer_end + 4;
659            break;
660        }
661
662        let size_usize = usize::try_from(size).map_err(|_| H1DecodeError::InvalidChunkedEncoding)?;
663        if bytes.len() < offset + size_usize + 2 {
664            return Ok(None);
665        }
666        out.extend_from_slice(&bytes[offset..offset + size_usize]);
667        offset += size_usize;
668        if bytes.get(offset) != Some(&b'\r') || bytes.get(offset + 1) != Some(&b'\n') {
669            return Err(H1DecodeError::InvalidChunkedEncoding);
670        }
671        offset += 2;
672    }
673
674    Ok(Some((out, offset)))
675}
676
677fn parse_chunk_size(line: &[u8]) -> Result<u64, H1DecodeError> {
678    let mut end = line.len();
679    for (idx, byte) in line.iter().enumerate() {
680        if *byte == b';' {
681            end = idx;
682            break;
683        }
684    }
685    let trimmed = trim_ascii(&line[..end]);
686    if trimmed.is_empty() {
687        return Err(H1DecodeError::InvalidChunkedEncoding);
688    }
689
690    let mut value: u64 = 0;
691    for &byte in trimmed {
692        let digit = match byte {
693            b'0'..=b'9' => (byte - b'0') as u64,
694            b'a'..=b'f' => (byte - b'a' + 10) as u64,
695            b'A'..=b'F' => (byte - b'A' + 10) as u64,
696            _ => return Err(H1DecodeError::InvalidChunkedEncoding),
697        };
698        value = value
699            .checked_mul(16)
700            .and_then(|v| v.checked_add(digit))
701            .ok_or(H1DecodeError::InvalidChunkedEncoding)?;
702    }
703
704    Ok(value)
705}
706
707fn find_crlf(bytes: &[u8], start: usize) -> Option<usize> {
708    let mut idx = start;
709    while idx + 1 < bytes.len() {
710        if bytes[idx] == b'\r' && bytes[idx + 1] == b'\n' {
711            return Some(idx);
712        }
713        idx += 1;
714    }
715    None
716}
717
718fn find_double_crlf(bytes: &[u8], start: usize) -> Option<usize> {
719    let mut idx = start;
720    while idx + 3 < bytes.len() {
721        if bytes[idx] == b'\r'
722            && bytes[idx + 1] == b'\n'
723            && bytes[idx + 2] == b'\r'
724            && bytes[idx + 3] == b'\n'
725        {
726            return Some(idx);
727        }
728        idx += 1;
729    }
730    None
731}
732
733fn trim_ascii(value: &[u8]) -> &[u8] {
734    let mut start = 0;
735    let mut end = value.len();
736    while start < end {
737        let byte = value[start];
738        if byte != b' ' && byte != b'\t' {
739            break;
740        }
741        start += 1;
742    }
743    while end > start {
744        let byte = value[end - 1];
745        if byte != b' ' && byte != b'\t' {
746            break;
747        }
748        end -= 1;
749    }
750    &value[start..end]
751}
752
753fn parse_usize_ascii(value: &[u8]) -> Result<usize, H1DecodeError> {
754    if value.is_empty() {
755        return Err(H1DecodeError::InvalidContentLength);
756    }
757    let mut out: usize = 0;
758    for &byte in value {
759        if !byte.is_ascii_digit() {
760            return Err(H1DecodeError::InvalidContentLength);
761        }
762        let digit = (byte - b'0') as usize;
763        out = out
764            .checked_mul(10)
765            .and_then(|v| v.checked_add(digit))
766            .ok_or(H1DecodeError::InvalidContentLength)?;
767    }
768    Ok(out)
769}
770
771fn find_header_value<'a>(headers: &'a [HeaderField], name: &[u8]) -> Option<&'a [u8]> {
772    headers
773        .iter()
774        .find(|header| crate::eq_ignore_ascii_case(&header.name, name))
775        .map(|header| header.value.as_slice())
776}
777
778#[cfg(test)]
779mod tests {
780    use super::*;
781
782    #[test]
783    fn decode_simple_request() {
784        let input = b"GET /hello HTTP/1.1\r\nHost: example.com\r\n\r\n";
785        let (req, consumed) = decode_request(input).unwrap().unwrap();
786
787        assert_eq!(consumed, input.len());
788        assert_eq!(req.method, b"GET".to_vec());
789        assert_eq!(req.path, b"/hello".to_vec());
790        assert_eq!(req.authority.unwrap(), b"example.com".to_vec());
791        assert!(req.body.is_empty());
792    }
793
794    #[test]
795    fn decode_request_with_body() {
796        let input = b"POST /submit HTTP/1.1\r\nHost: example.com\r\nContent-Length: 5\r\n\r\nhello";
797        let (req, consumed) = decode_request(input).unwrap().unwrap();
798
799        assert_eq!(consumed, input.len());
800        assert_eq!(req.method, b"POST".to_vec());
801        assert_eq!(req.body, b"hello".to_vec());
802    }
803
804    #[test]
805    fn decode_chunked_response() {
806        let input = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n4\r\nWiki\r\n5\r\npedia\r\n0\r\n\r\n";
807        let (resp, consumed) = decode_response(input).unwrap().unwrap();
808
809        assert_eq!(consumed, input.len());
810        assert_eq!(resp.status, 200);
811        assert_eq!(resp.body, b"Wikipedia".to_vec());
812    }
813
814    #[test]
815    fn decoder_streaming() {
816        let input = b"GET /a HTTP/1.1\r\nHost: example.com\r\n\r\n";
817        let mut decoder = H1Decoder::new(H1MessageKind::Request);
818        decoder.push(&input[..10]);
819        assert!(decoder.try_decode().unwrap().is_none());
820
821        decoder.push(&input[10..]);
822        let msg = decoder.try_decode().unwrap();
823        assert!(matches!(msg, Some(PackedMessage::Request(_))));
824        assert_eq!(decoder.buffer_len(), 0);
825    }
826
827    #[test]
828    fn stream_decoder_content_length() {
829        let input = b"POST /upload HTTP/1.1\r\nHost: example.com\r\nContent-Length: 5\r\n\r\nhello";
830        let mut decoder = H1StreamDecoder::new(H1MessageKind::Request, 42);
831        decoder.push(input);
832
833        let frame = decoder.try_decode().unwrap();
834        assert!(matches!(frame, Some(StreamFrame::Headers(_))));
835
836        let frame = decoder.try_decode().unwrap();
837        match frame {
838            Some(StreamFrame::Body(body)) => {
839                assert_eq!(body.stream_id, 42);
840                assert_eq!(body.data, b"hello".to_vec());
841            }
842            _ => panic!("expected body frame"),
843        }
844
845        let frame = decoder.try_decode().unwrap();
846        assert!(matches!(frame, Some(StreamFrame::End(_))));
847    }
848}