ls_qpack/
decoder.rs

1// Copyright 2022 Biagio Festa
2
3//! Module for decoding operations.
4//!
5//! The main struct of this module is [`Decoder`].
6//!
7//! # Example
8//!
9//! ## Only Static Table
10//! ```
11//! use ls_qpack::decoder::Decoder;
12//! use ls_qpack::encoder::Encoder;
13//! use ls_qpack::StreamId;
14//!
15//! let hdr_encoded = Encoder::new()
16//!     .encode_all(StreamId::new(0), [(":status", "404")])
17//!     .unwrap()
18//!     .take()
19//!     .0;
20//!
21//! let header = Decoder::new(0, 0)
22//!     .decode(StreamId::new(0), hdr_encoded)
23//!     .unwrap()
24//!     .take();
25//!
26//! println!("Headers: {:?}", header);
27//! ```
28use crate::Header;
29use crate::StreamId;
30use std::collections::hash_map;
31use std::collections::HashMap;
32use std::fmt::Debug;
33use std::fmt::Display;
34use std::marker::PhantomPinned;
35use std::pin::Pin;
36
37/// Error during decoding operations.
38pub struct DecoderError;
39
40/// The result of a decode operation.
41///
42/// Generally, this is function's output for [`Decoder::decode`].
43///
44/// When header data are decoded,
45pub enum DecoderOutput {
46    /// The header block has been correctly decoded.
47    Done(Vec<Header>),
48
49    /// The deocding stream is blocked.
50    /// More data are needed in order to proceed with decoding operation.
51    /// Generally, you need to feed the encoder via [`Decoder::feed`].
52    BlockedStream,
53}
54
55impl DecoderOutput {
56    /// If the result is unblocked, it will return `Some(Vec<header>)`.
57    /// Otherwise `None`.
58    pub fn take(self) -> Option<Vec<Header>> {
59        match self {
60            Self::Done(v) => Some(v),
61            Self::BlockedStream => None,
62        }
63    }
64
65    /// Checks whether the result is blocked or not.
66    pub fn is_blocked(&self) -> bool {
67        matches!(self, Self::BlockedStream)
68    }
69}
70
71/// A QPACK decoder.
72pub struct Decoder {
73    inner: Pin<Box<InnerDecoder>>,
74}
75
76impl Decoder {
77    /// Creates a new decoder.
78    ///
79    /// Specify the size of the dynamic table (it might be `0`).
80    /// And the max number of blocked streams.
81    pub fn new(dyn_table_size: u32, max_blocked_streams: u32) -> Self {
82        Self {
83            inner: InnerDecoder::new(dyn_table_size, max_blocked_streams),
84        }
85    }
86
87    /// Decodes header data.
88    ///
89    /// It produces an output, see [`DecoderOutput`].
90    ///
91    /// It might happen that the data provided to this method are not sufficient in order
92    /// to complete the decoding operation.
93    /// In that case, more data are needed from the encoder stream (via [`Decoder::feed`]).
94    ///
95    /// # Examples
96    /// ```
97    /// use ls_qpack::decoder::Decoder;
98    /// use ls_qpack::StreamId;
99    ///
100    /// # use ls_qpack::TryIntoHeader;
101    /// # let (data, _) = ls_qpack::encoder::Encoder::new().encode_all(0.into(), [("foo", "bar")]).unwrap().into();
102    ///
103    ///
104    /// let mut decoder = Decoder::new(0, 0);
105    /// let output = decoder.decode(StreamId::new(0), data).unwrap();
106    /// ```
107    pub fn decode<D>(&mut self, stream_id: StreamId, data: D) -> Result<DecoderOutput, DecoderError>
108    where
109        D: AsRef<[u8]>,
110    {
111        self.inner
112            .as_mut()
113            .feed_header_data(stream_id, data.as_ref())
114    }
115
116    /// Feeds data from encoder's buffer stream.
117    pub fn feed<D>(&mut self, data: D) -> Result<(), DecoderError>
118    where
119        D: AsRef<[u8]>,
120    {
121        self.inner.as_mut().feed_encoder_data(data.as_ref())
122    }
123
124    /// Checks whether a header block for a `StreamId` has become unblocked.
125    ///
126    /// # Returns
127    ///   * `None` if the `StreamId` has never been fed.
128    ///   * `Some` if the `StreamId` produced an [`DecoderOutput`].
129    pub fn unblocked(
130        &mut self,
131        stream_id: StreamId,
132    ) -> Option<Result<DecoderOutput, DecoderError>> {
133        self.inner.as_mut().process_decoded_data(stream_id)
134    }
135}
136
137struct InnerDecoder {
138    decoder: ls_qpack_sys::lsqpack_dec,
139    header_blocks: HashMap<StreamId, Pin<Box<callbacks::HeaderBlockCtx>>>,
140    _marker: PhantomPinned,
141}
142
143impl InnerDecoder {
144    fn new(dyn_table_size: u32, max_blocked_streams: u32) -> Pin<Box<Self>> {
145        let mut this = Box::new(Self {
146            decoder: ls_qpack_sys::lsqpack_dec::default(),
147            header_blocks: HashMap::new(),
148            _marker: PhantomPinned,
149        });
150
151        unsafe {
152            ls_qpack_sys::lsqpack_dec_init(
153                &mut this.decoder,
154                std::ptr::null_mut(),
155                dyn_table_size,
156                max_blocked_streams,
157                &callbacks::HSET_IF_CALLBACKS,
158                0,
159            );
160        }
161
162        Box::into_pin(this)
163    }
164
165    fn feed_header_data(
166        self: Pin<&mut Self>,
167        stream_id: StreamId,
168        data: &[u8],
169    ) -> Result<DecoderOutput, DecoderError> {
170        let this = unsafe { self.get_unchecked_mut() };
171
172        if this.header_blocks.contains_key(&stream_id) {
173            todo!()
174        }
175
176        let mut hblock_ctx =
177            callbacks::HeaderBlockCtx::new(&mut this.decoder, data.to_vec().into_boxed_slice());
178
179        let encoded_cursor = hblock_ctx.as_ref().encoded_cursor();
180        let encoded_cursor_len = encoded_cursor.len();
181        let header_block_len = encoded_cursor.len();
182        let mut cursor_after = encoded_cursor.as_ptr();
183
184        let result = unsafe {
185            ls_qpack_sys::lsqpack_dec_header_in(
186                &mut this.decoder,
187                hblock_ctx.as_mut().as_mut_ptr() as *mut libc::c_void,
188                stream_id.value(),
189                header_block_len,
190                &mut cursor_after,
191                encoded_cursor_len,
192                std::ptr::null_mut(),
193                &mut 0,
194            )
195        };
196
197        match result {
198            ls_qpack_sys::lsqpack_read_header_status_LQRHS_DONE => {
199                debug_assert!(!hblock_ctx.as_ref().is_blocked());
200                debug_assert!(!hblock_ctx.as_ref().is_error());
201
202                let hblock_ctx = unsafe { Pin::into_inner_unchecked(hblock_ctx) };
203                Ok(DecoderOutput::Done(hblock_ctx.decoded_headers()))
204            }
205
206            ls_qpack_sys::lsqpack_read_header_status_LQRHS_BLOCKED => {
207                let offset = unsafe {
208                    cursor_after.offset_from(hblock_ctx.as_ref().encoded_cursor().as_ptr())
209                };
210
211                debug_assert!(offset > 0);
212
213                hblock_ctx.as_mut().advance_cursor(offset as usize);
214                hblock_ctx.as_mut().set_blocked(true);
215                this.header_blocks.insert(stream_id, hblock_ctx);
216
217                Ok(DecoderOutput::BlockedStream)
218            }
219
220            ls_qpack_sys::lsqpack_read_header_status_LQRHS_NEED => unimplemented!(),
221
222            _ => Err(DecoderError),
223        }
224    }
225
226    fn feed_encoder_data(self: Pin<&mut Self>, data: &[u8]) -> Result<(), DecoderError> {
227        let this = unsafe { self.get_unchecked_mut() };
228
229        let result = unsafe {
230            ls_qpack_sys::lsqpack_dec_enc_in(&mut this.decoder, data.as_ptr(), data.len())
231        };
232
233        if result == 0 {
234            Ok(())
235        } else {
236            Err(DecoderError)
237        }
238    }
239
240    fn process_decoded_data(
241        self: Pin<&mut Self>,
242        stream_id: StreamId,
243    ) -> Option<Result<DecoderOutput, DecoderError>> {
244        let this = unsafe { self.get_unchecked_mut() };
245
246        match this.header_blocks.entry(stream_id) {
247            hash_map::Entry::Occupied(hdbk) => {
248                if hdbk.get().as_ref().is_blocked() {
249                    debug_assert!(!hdbk.get().as_ref().is_error());
250                    return Some(Ok(DecoderOutput::BlockedStream));
251                }
252
253                let hdbk = hdbk.remove();
254
255                if hdbk.as_ref().is_error() {
256                    debug_assert!(!hdbk.as_ref().is_blocked());
257                    return Some(Err(DecoderError));
258                }
259
260                let hdbk = unsafe { Pin::into_inner_unchecked(hdbk) };
261                Some(Ok(DecoderOutput::Done(hdbk.decoded_headers())))
262            }
263
264            hash_map::Entry::Vacant(_) => None,
265        }
266    }
267}
268
269impl Drop for InnerDecoder {
270    fn drop(&mut self) {
271        unsafe { ls_qpack_sys::lsqpack_dec_cleanup(&mut self.decoder) }
272    }
273}
274
275mod callbacks {
276    use crate::header::HeaderError;
277    use crate::Header;
278    use std::ffi::c_char;
279    use std::marker::PhantomPinned;
280    use std::pin::Pin;
281
282    pub(super) static HSET_IF_CALLBACKS: ls_qpack_sys::lsqpack_dec_hset_if =
283        ls_qpack_sys::lsqpack_dec_hset_if {
284            dhi_unblocked: Some(dhi_unblocked),
285            dhi_prepare_decode: Some(dhi_prepare_decode),
286            dhi_process_header: Some(dhi_process_header),
287        };
288
289    #[derive(Debug)]
290    pub(super) struct HeaderBlockCtx {
291        decoder: *mut ls_qpack_sys::lsqpack_dec, /* TODO(bfesta): maybe &mut and leverage rust checks? */
292        encoded_data: Box<[u8]>,
293        encoded_data_offset: usize,
294        decoding_buffer: Vec<u8>,
295        header: ls_qpack_sys::lsxpack_header,
296        blocked: bool,
297        error: bool,
298        decoded_headers: Vec<Header>,
299        _marker: PhantomPinned,
300    }
301
302    impl HeaderBlockCtx {
303        pub(super) fn new(
304            decoder: *mut ls_qpack_sys::lsqpack_dec,
305            encoded_data: Box<[u8]>,
306        ) -> Pin<Box<Self>> {
307            debug_assert!(!decoder.is_null());
308
309            Box::pin(Self {
310                decoder,
311                encoded_data,
312                encoded_data_offset: 0,
313                decoding_buffer: Vec::new(),
314                header: Default::default(),
315                blocked: false,
316                error: false,
317                decoded_headers: Default::default(),
318                _marker: PhantomPinned,
319            })
320        }
321
322        pub(super) unsafe fn as_mut_ptr(mut self: Pin<&mut Self>) -> *mut HeaderBlockCtx {
323            self.as_mut().get_unchecked_mut()
324        }
325
326        pub(super) fn encoded_cursor<'a>(self: Pin<&'a Self>) -> &'a [u8] {
327            debug_assert!(self.encoded_data_offset < self.encoded_data.len());
328            &self.get_ref().encoded_data[self.encoded_data_offset..]
329        }
330
331        pub(super) fn advance_cursor(self: Pin<&mut Self>, offset: usize) {
332            debug_assert!(offset <= self.encoded_data.len());
333            let this = unsafe { self.get_unchecked_mut() };
334            this.encoded_data_offset += offset;
335        }
336
337        pub(super) fn set_blocked(self: Pin<&mut Self>, blocked: bool) {
338            let this = unsafe { self.get_unchecked_mut() };
339            this.blocked = blocked;
340        }
341
342        pub(super) fn enable_error(self: Pin<&mut Self>) {
343            let this = unsafe { self.get_unchecked_mut() };
344            debug_assert!(!this.error);
345            this.error = true;
346        }
347
348        pub(super) fn is_blocked(self: Pin<&Self>) -> bool {
349            self.blocked
350        }
351
352        pub(super) fn is_error(self: Pin<&Self>) -> bool {
353            self.error
354        }
355
356        pub(super) fn decoded_headers(self) -> Vec<Header> {
357            self.decoded_headers
358        }
359
360        unsafe fn from_void_ptr(ptr: *mut libc::c_void) -> Pin<&'static mut Self> {
361            debug_assert!(!ptr.is_null());
362            Pin::new_unchecked(&mut *(ptr as *mut _))
363        }
364
365        fn reset_header(self: Pin<&mut Self>) {
366            let this = unsafe { self.get_unchecked_mut() };
367            this.header = Default::default()
368        }
369
370        fn resize_header(self: Pin<&mut Self>, space: u16) {
371            let this = unsafe { self.get_unchecked_mut() };
372            this.decoding_buffer
373                .resize(space as usize, Default::default());
374
375            this.header.buf = this.decoding_buffer.as_mut_ptr() as *mut c_char;
376            this.header.val_len = space;
377        }
378
379        fn header_mut(self: Pin<&mut Self>) -> &mut ls_qpack_sys::lsxpack_header {
380            let this = unsafe { self.get_unchecked_mut() };
381            &mut this.header
382        }
383
384        fn process_header(self: Pin<&mut Self>) -> Result<(), HeaderError> {
385            let this = unsafe { self.get_unchecked_mut() };
386
387            let header = Header::with_buffer(
388                std::mem::take(&mut this.decoding_buffer).into_boxed_slice(),
389                this.header.name_offset as usize,
390                this.header.name_len as usize,
391                this.header.val_offset as usize,
392                this.header.val_len as usize,
393            )?;
394
395            this.decoded_headers.push(header);
396
397            this.header = Default::default();
398
399            Ok(())
400        }
401    }
402
403    extern "C" fn dhi_unblocked(hblock_ctx: *mut libc::c_void) {
404        let mut hblock_ctx = unsafe { HeaderBlockCtx::from_void_ptr(hblock_ctx) };
405
406        debug_assert!(hblock_ctx.as_ref().is_blocked());
407        hblock_ctx.as_mut().set_blocked(false);
408
409        let encoded_cursor = hblock_ctx.as_ref().encoded_cursor();
410        let encoded_cursor_len = encoded_cursor.len();
411        let mut cursor_after = encoded_cursor.as_ptr();
412
413        let result = unsafe {
414            ls_qpack_sys::lsqpack_dec_header_read(
415                hblock_ctx.decoder,
416                hblock_ctx.as_mut().as_mut_ptr() as *mut libc::c_void,
417                &mut cursor_after,
418                encoded_cursor_len,
419                std::ptr::null_mut(),
420                std::ptr::null_mut(),
421            )
422        };
423
424        match result {
425            ls_qpack_sys::lsqpack_read_header_status_LQRHS_DONE => {}
426
427            ls_qpack_sys::lsqpack_read_header_status_LQRHS_BLOCKED => {
428                let offset = unsafe {
429                    cursor_after.offset_from(hblock_ctx.as_ref().encoded_cursor().as_ptr())
430                };
431
432                debug_assert!(offset > 0);
433
434                hblock_ctx.as_mut().advance_cursor(offset as usize);
435                hblock_ctx.as_mut().set_blocked(true);
436            }
437
438            ls_qpack_sys::lsqpack_read_header_status_LQRHS_NEED => unimplemented!(),
439
440            _ => {
441                hblock_ctx.as_mut().enable_error();
442            }
443        }
444    }
445
446    extern "C" fn dhi_prepare_decode(
447        hblock_ctx: *mut libc::c_void,
448        header: *mut ls_qpack_sys::lsxpack_header,
449        space: libc::size_t,
450    ) -> *mut ls_qpack_sys::lsxpack_header {
451        const MAX_SPACE: usize = u16::MAX as usize;
452
453        let mut hblock_ctx = unsafe { HeaderBlockCtx::from_void_ptr(hblock_ctx) };
454
455        if space > MAX_SPACE {
456            todo!()
457        }
458
459        let space = space as u16;
460
461        if header.is_null() {
462            hblock_ctx.as_mut().reset_header();
463        } else {
464            assert!(std::ptr::eq(&hblock_ctx.header, header));
465            assert!(space > hblock_ctx.header.val_len);
466        }
467
468        hblock_ctx.as_mut().resize_header(space);
469        hblock_ctx.as_mut().header_mut()
470    }
471
472    extern "C" fn dhi_process_header(
473        hblock_ctx: *mut libc::c_void,
474        header: *mut ls_qpack_sys::lsxpack_header,
475    ) -> libc::c_int {
476        let hblock_ctx = unsafe { HeaderBlockCtx::from_void_ptr(hblock_ctx) };
477
478        debug_assert!(!hblock_ctx.blocked);
479        debug_assert_eq!(header as *const _, &hblock_ctx.header);
480
481        match hblock_ctx.process_header() {
482            Ok(()) => 0,
483            Err(_) => todo!(),
484        }
485    }
486}
487
488impl Debug for DecoderError {
489    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
490        f.debug_struct("DecoderError").finish()
491    }
492}
493
494impl Display for DecoderError {
495    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
496        Debug::fmt(self, f)
497    }
498}
499
500impl std::error::Error for DecoderError {}