ls_qpack/
header.rs

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