1use std::convert::TryFrom;
4use std::ffi::c_char;
5use std::fmt::Debug;
6
7pub enum HeaderError {
9 NameTooLong,
10 ValueTooLong,
11 NameInvalidUtf8,
12 ValueInvalidUtf8,
13}
14
15#[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 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 #[inline]
75 pub fn name(&self) -> &str {
76 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 #[inline]
85 pub fn value(&self) -> &str {
86 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
142pub 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}