Skip to main content

ls_qpack_rs/
header.rs

1// Copyright 2022 Biagio Festa
2
3use std::convert::TryFrom;
4use std::ffi::c_char;
5use std::fmt::Debug;
6
7/// Error during Header construction.
8pub enum HeaderError {
9    NameTooLong,
10    ValueTooLong,
11    NameInvalidUtf8,
12    ValueInvalidUtf8,
13}
14
15/// QPACK Header.
16///
17/// A `Header` essentially is a pair composed by two strings. That is, a name and a value.
18#[derive(Clone)]
19pub struct Header {
20    buffer: Box<[u8]>,
21    name_offset: usize,
22    name_len: usize,
23    value_offset: usize,
24    value_len: usize,
25}
26
27impl Header {
28    pub const MAX_LEN_NAME: usize = u16::MAX as usize;
29    pub const MAX_LEN_VALUE: usize = u16::MAX as usize;
30
31    /// Builds a new header from a name and a string.
32    ///
33    /// # Example
34    /// ```
35    /// use ls_qpack_rs::Header;
36    ///
37    /// let header = Header::new("name", "bob");
38    /// ```
39    pub fn new<N, V>(name: N, value: V) -> Result<Self, HeaderError>
40    where
41        N: AsRef<str>,
42        V: AsRef<str>,
43    {
44        let name = name.as_ref();
45        let value = value.as_ref();
46
47        if name.len() > Self::MAX_LEN_NAME {
48            return Err(HeaderError::NameTooLong);
49        }
50
51        if value.len() > Self::MAX_LEN_VALUE {
52            return Err(HeaderError::ValueTooLong);
53        }
54
55        let mut buffer = Vec::with_capacity(name.len() + value.len());
56        buffer.extend(name.as_bytes());
57        buffer.extend(value.as_bytes());
58
59        let name_offset = 0;
60        let name_len = name.len();
61        let value_offset = name.len();
62        let value_len = value.len();
63
64        Ok(Self {
65            buffer: buffer.into_boxed_slice(),
66            name_offset,
67            name_len,
68            value_offset,
69            value_len,
70        })
71    }
72
73    /// Returns the `name` field.
74    #[inline]
75    pub fn name(&self) -> &str {
76        // UTF-8 validity is guaranteed at construction time: `new()` takes `AsRef<str>` and
77        // `with_buffer()` validates with `std::str::from_utf8`. We use the checked version
78        // here as a defense-in-depth measure, avoiding `unsafe`.
79        std::str::from_utf8(&self.buffer[self.name_offset..self.name_offset + self.name_len])
80            .expect("Header name must be valid UTF-8 (validated at construction)")
81    }
82
83    /// Returns the `value` field.
84    #[inline]
85    pub fn value(&self) -> &str {
86        // UTF-8 validity is guaranteed at construction time: `new()` takes `AsRef<str>` and
87        // `with_buffer()` validates with `std::str::from_utf8`. We use the checked version
88        // here as a defense-in-depth measure, avoiding `unsafe`.
89        std::str::from_utf8(&self.buffer[self.value_offset..self.value_offset + self.value_len])
90            .expect("Header value must be valid UTF-8 (validated at construction)")
91    }
92
93    pub(crate) fn with_buffer(
94        buffer: Box<[u8]>,
95        name_offset: usize,
96        name_len: usize,
97        value_offset: usize,
98        value_len: usize,
99    ) -> Result<Self, HeaderError> {
100        if name_len > Self::MAX_LEN_NAME {
101            return Err(HeaderError::NameTooLong);
102        }
103
104        if value_len > Self::MAX_LEN_VALUE {
105            return Err(HeaderError::ValueTooLong);
106        }
107
108        if std::str::from_utf8(&buffer[name_offset..name_offset + name_len]).is_err() {
109            return Err(HeaderError::NameInvalidUtf8);
110        }
111
112        if std::str::from_utf8(&buffer[value_offset..value_offset + value_len]).is_err() {
113            return Err(HeaderError::ValueInvalidUtf8);
114        }
115
116        debug_assert!(name_offset < u16::MAX as usize);
117        debug_assert!(value_offset < u16::MAX as usize);
118
119        Ok(Self {
120            buffer,
121            name_offset,
122            name_len,
123            value_offset,
124            value_len,
125        })
126    }
127
128    pub(crate) fn build_lsxpack_header(&mut self) -> LsxpackHeader<'_> {
129        LsxpackHeader::new(self)
130    }
131}
132
133impl Debug for Header {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        f.debug_struct("Header")
136            .field("name", &self.name())
137            .field("value", &self.value())
138            .finish()
139    }
140}
141
142/// Tries to convert something into a [`Header`].
143///
144/// # Examples
145/// ```
146/// use ls_qpack_rs::TryIntoHeader;
147///
148/// let header = ("name", "bob").try_into_header();
149/// ```
150pub trait TryIntoHeader {
151    fn try_into_header(self) -> Result<Header, HeaderError>;
152}
153
154impl<N, V> TryFrom<(N, V)> for Header
155where
156    N: AsRef<str>,
157    V: AsRef<str>,
158{
159    type Error = HeaderError;
160
161    fn try_from(value: (N, V)) -> Result<Self, Self::Error> {
162        Header::new(value.0, value.1)
163    }
164}
165
166impl<N, V> TryIntoHeader for (N, V)
167where
168    N: AsRef<str>,
169    V: AsRef<str>,
170{
171    fn try_into_header(self) -> Result<Header, HeaderError> {
172        Header::new(self.0, self.1)
173    }
174}
175
176pub(crate) struct LsxpackHeader<'a> {
177    header_sys: ls_qpack_rs_sys::lsxpack_header,
178    #[allow(unused)]
179    header_ref: &'a mut Header,
180}
181
182impl<'a> LsxpackHeader<'a> {
183    pub(crate) fn new(header: &'a mut Header) -> Self {
184        debug_assert!(header.name_offset < u16::MAX as usize);
185        debug_assert!(header.name_len < Header::MAX_LEN_NAME);
186        debug_assert!(header.value_offset < u16::MAX as usize);
187        debug_assert!(header.value_len < Header::MAX_LEN_VALUE);
188
189        let header_sys = ls_qpack_rs_sys::lsxpack_header {
190            buf: header.buffer.as_mut_ptr() as *mut c_char,
191            name_offset: header.name_offset as i32,
192            name_len: header.name_len as u16,
193            val_offset: header.value_offset as i32,
194            val_len: header.value_len as u16,
195            ..Default::default()
196        };
197
198        Self {
199            header_sys,
200            header_ref: header,
201        }
202    }
203}
204
205impl AsRef<ls_qpack_rs_sys::lsxpack_header> for LsxpackHeader<'_> {
206    fn as_ref(&self) -> &ls_qpack_rs_sys::lsxpack_header {
207        &self.header_sys
208    }
209}