Skip to main content

sark_http/h1/field/
transfer.rs

1use crate::h1::{
2    field::{
3        value,
4        value::{Match, Parser},
5    },
6    framing,
7};
8
9/// Stateful Transfer-Encoding validation across one or more field lines.
10/// No coding may follow `chunked`, and `chunked` may occur only once.
11#[derive(Clone, Copy, PartialEq, Eq)]
12pub struct Transfer {
13    codings: u16,
14    last: framing::Transfer,
15}
16
17impl Default for Transfer {
18    fn default() -> Self {
19        Self {
20            codings: 0,
21            last: framing::Transfer::None,
22        }
23    }
24}
25
26impl Transfer {
27    pub fn parse(mut self, value: &[u8]) -> Result<Self, value::Error> {
28        let mut parser = Parser::new(value);
29        parser.skip_ows();
30        if parser.is_done() {
31            return Err(value::Error::Empty);
32        }
33        loop {
34            if matches!(self.last, framing::Transfer::Chunked) {
35                return Err(value::Error::InvalidOrdering);
36            }
37            let coding = if parser.token()?.eq_ignore_ascii_case(b"chunked") {
38                framing::Transfer::Chunked
39            } else {
40                framing::Transfer::Other
41            };
42            parser.skip_ows();
43            let mut param = false;
44            while parser.take(b';') == Match::Taken {
45                param = true;
46                parser.skip_ows();
47                parser.token()?;
48                parser.skip_ows();
49                if parser.take(b'=') == Match::Other {
50                    return Err(value::Error::InvalidByte);
51                }
52                parser.skip_ows();
53                parser.item()?;
54                parser.skip_ows();
55            }
56            if matches!(coding, framing::Transfer::Chunked) && param {
57                return Err(value::Error::InvalidByte);
58            }
59            self.codings = self.codings.checked_add(1).ok_or(value::Error::Overflow)?;
60            self.last = coding;
61            if parser.is_done() {
62                return Ok(self);
63            }
64            if parser.take(b',') == Match::Other {
65                return Err(value::Error::InvalidByte);
66            }
67            parser.skip_ows();
68            if parser.is_done() {
69                return Err(value::Error::Empty);
70            }
71        }
72    }
73
74    pub const fn has_encoding(self) -> bool {
75        self.codings != 0
76    }
77
78    pub const fn is_chunked(self) -> bool {
79        matches!(self.last, framing::Transfer::Chunked)
80    }
81
82    pub(in crate::h1) const fn has_other(self) -> bool {
83        self.codings > 1 || matches!(self.last, framing::Transfer::Other)
84    }
85
86    pub const fn framing(self) -> framing::Transfer {
87        self.last
88    }
89
90    pub(crate) fn try_join(self, value: Self) -> Result<Self, value::Error> {
91        if value.codings == 0 {
92            return Err(value::Error::Empty);
93        }
94        if matches!(self.last, framing::Transfer::Chunked) {
95            return Err(value::Error::InvalidOrdering);
96        }
97        let codings = self
98            .codings
99            .checked_add(value.codings)
100            .ok_or(value::Error::Overflow)?;
101        Ok(Self {
102            codings,
103            last: value.last,
104        })
105    }
106}
107
108const _: () = assert!(core::mem::size_of::<Transfer>() <= 4);