1use 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#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39#[non_exhaustive]
40pub enum DecoderErrorKind {
41 DuplicateStreamId,
43 DecodeFailed,
45 FeedFailed,
47 InvalidHeader,
49}
50
51pub struct DecoderError {
53 kind: DecoderErrorKind,
54}
55
56impl DecoderError {
57 pub fn kind(&self) -> DecoderErrorKind {
59 self.kind
60 }
61
62 fn new(kind: DecoderErrorKind) -> Self {
63 Self { kind }
64 }
65}
66
67#[derive(Debug)]
71pub struct BuffersDecoded {
72 headers: Vec<Header>,
73 stream: Box<[u8]>,
74}
75
76impl BuffersDecoded {
77 pub fn headers(&self) -> &Vec<Header> {
79 &self.headers
80 }
81
82 pub fn stream(&self) -> &[u8] {
84 &self.stream
85 }
86}
87
88#[derive(Debug)]
94pub enum DecoderOutput {
95 Done(BuffersDecoded),
97
98 BlockedStream,
102}
103
104impl DecoderOutput {
105 pub fn take(self) -> Option<BuffersDecoded> {
108 match self {
109 Self::Done(v) => Some(v),
110 Self::BlockedStream => None,
111 }
112 }
113
114 pub fn is_blocked(&self) -> bool {
116 matches!(self, Self::BlockedStream)
117 }
118}
119
120pub struct Decoder {
122 inner: Pin<Box<InnerDecoder>>,
123}
124
125impl Decoder {
126 pub fn new(dyn_table_size: u32, max_blocked_streams: u32) -> Self {
131 Self {
132 inner: InnerDecoder::new(dyn_table_size, max_blocked_streams),
133 }
134 }
135
136 pub fn decode<D>(&mut self, stream_id: StreamId, data: D) -> Result<DecoderOutput, DecoderError>
157 where
158 D: AsRef<[u8]>,
159 {
160 self.inner
161 .as_mut()
162 .feed_header_data(stream_id, data.as_ref())
163 }
164
165 pub fn feed<D>(&mut self, data: D) -> Result<(), DecoderError>
167 where
168 D: AsRef<[u8]>,
169 {
170 self.inner.as_mut().feed_encoder_data(data.as_ref())
171 }
172
173 pub fn unblocked(
179 &mut self,
180 stream_id: StreamId,
181 ) -> Option<Result<DecoderOutput, DecoderError>> {
182 self.inner.as_mut().process_decoded_data(stream_id)
183 }
184}
185
186struct InnerDecoder {
187 decoder: ls_qpack_rs_sys::lsqpack_dec,
188 header_blocks: HashMap<StreamId, Pin<Box<callbacks::HeaderBlockCtx>>>,
189 _marker: PhantomPinned,
190}
191
192impl InnerDecoder {
193 fn new(dyn_table_size: u32, max_blocked_streams: u32) -> Pin<Box<Self>> {
194 let mut this = Box::new(Self {
195 decoder: ls_qpack_rs_sys::lsqpack_dec::default(),
196 header_blocks: HashMap::new(),
197 _marker: PhantomPinned,
198 });
199
200 unsafe {
205 ls_qpack_rs_sys::lsqpack_dec_init(
206 &mut this.decoder,
207 std::ptr::null_mut(),
208 dyn_table_size,
209 max_blocked_streams,
210 &callbacks::HSET_IF_CALLBACKS,
211 0,
212 );
213 }
214
215 Box::into_pin(this)
216 }
217
218 fn feed_header_data(
219 self: Pin<&mut Self>,
220 stream_id: StreamId,
221 data: &[u8],
222 ) -> Result<DecoderOutput, DecoderError> {
223 let this = unsafe { self.get_unchecked_mut() };
226
227 if this.header_blocks.contains_key(&stream_id) {
228 return Err(DecoderError::new(DecoderErrorKind::DuplicateStreamId));
229 }
230
231 let mut hblock_ctx =
232 callbacks::HeaderBlockCtx::new(&mut this.decoder, data.to_vec().into_boxed_slice());
233
234 let encoded_cursor = hblock_ctx.as_ref().encoded_cursor();
235 let encoded_cursor_len = encoded_cursor.len();
236 let header_block_len = encoded_cursor.len();
237 let mut cursor_after = encoded_cursor.as_ptr();
238
239 let mut buffer = vec![0; ls_qpack_rs_sys::LSQPACK_LONGEST_SDTC as usize];
240 let mut sdtc_buffer_size = buffer.len();
241
242 let result = unsafe {
247 ls_qpack_rs_sys::lsqpack_dec_header_in(
248 &mut this.decoder,
249 hblock_ctx.as_mut().as_mut_ptr() as *mut libc::c_void,
250 stream_id.value(),
251 header_block_len,
252 &mut cursor_after,
253 encoded_cursor_len,
254 buffer.as_mut_ptr(),
255 &mut sdtc_buffer_size,
256 )
257 };
258
259 match result {
260 ls_qpack_rs_sys::lsqpack_read_header_status_LQRHS_DONE => {
261 debug_assert!(!hblock_ctx.as_ref().is_blocked());
262 debug_assert!(!hblock_ctx.as_ref().is_error());
263
264 let hblock_ctx = unsafe { Pin::into_inner_unchecked(hblock_ctx) };
267
268 buffer.truncate(sdtc_buffer_size);
269 Ok(DecoderOutput::Done(BuffersDecoded {
270 headers: hblock_ctx.decoded_headers(),
271 stream: buffer.into_boxed_slice(),
272 }))
273 }
274
275 ls_qpack_rs_sys::lsqpack_read_header_status_LQRHS_BLOCKED
276 | ls_qpack_rs_sys::lsqpack_read_header_status_LQRHS_NEED => {
277 let offset = unsafe {
281 cursor_after.offset_from(hblock_ctx.as_ref().encoded_cursor().as_ptr())
282 };
283
284 hblock_ctx.as_mut().advance_cursor(offset as usize);
285 hblock_ctx.as_mut().set_blocked(true);
286 this.header_blocks.insert(stream_id, hblock_ctx);
287
288 Ok(DecoderOutput::BlockedStream)
289 }
290
291 _ => Err(DecoderError::new(DecoderErrorKind::DecodeFailed)),
292 }
293 }
294
295 fn feed_encoder_data(self: Pin<&mut Self>, data: &[u8]) -> Result<(), DecoderError> {
296 let this = unsafe { self.get_unchecked_mut() };
298
299 let result = unsafe {
302 ls_qpack_rs_sys::lsqpack_dec_enc_in(&mut this.decoder, data.as_ptr(), data.len())
303 };
304
305 if result == 0 {
306 Ok(())
307 } else {
308 Err(DecoderError::new(DecoderErrorKind::FeedFailed))
309 }
310 }
311
312 fn process_decoded_data(
313 self: Pin<&mut Self>,
314 stream_id: StreamId,
315 ) -> Option<Result<DecoderOutput, DecoderError>> {
316 let this = unsafe { self.get_unchecked_mut() };
318
319 match this.header_blocks.entry(stream_id) {
320 hash_map::Entry::Occupied(hdbk) => {
321 if hdbk.get().as_ref().is_blocked() {
322 debug_assert!(!hdbk.get().as_ref().is_error());
323 return Some(Ok(DecoderOutput::BlockedStream));
324 }
325
326 let hdbk = hdbk.remove();
327
328 if hdbk.as_ref().is_error() {
329 debug_assert!(!hdbk.as_ref().is_blocked());
330 return Some(Err(DecoderError::new(DecoderErrorKind::InvalidHeader)));
331 }
332
333 let hdbk = unsafe { Pin::into_inner_unchecked(hdbk) };
337 Some(Ok(DecoderOutput::Done(BuffersDecoded {
338 headers: hdbk.decoded_headers(),
339 stream: hdbk.stream_data().into_boxed_slice(),
340 })))
341 }
342
343 hash_map::Entry::Vacant(_) => None,
344 }
345 }
346}
347
348impl Drop for InnerDecoder {
349 fn drop(&mut self) {
350 unsafe { ls_qpack_rs_sys::lsqpack_dec_cleanup(&mut self.decoder) }
354 }
355}
356
357unsafe impl Send for InnerDecoder {}
364
365unsafe impl Sync for InnerDecoder {}
369
370const _: () = {
371 fn _assert_send<T: Send>() {}
372 fn _assert_sync<T: Sync>() {}
373 fn _assert_all() {
374 _assert_send::<Decoder>();
375 _assert_sync::<Decoder>();
376 }
377};
378
379mod callbacks {
380 use crate::header::HeaderError;
381 use crate::Header;
382 use std::ffi::c_char;
383 use std::marker::PhantomPinned;
384 use std::pin::Pin;
385
386 pub(super) static HSET_IF_CALLBACKS: ls_qpack_rs_sys::lsqpack_dec_hset_if =
387 ls_qpack_rs_sys::lsqpack_dec_hset_if {
388 dhi_unblocked: Some(dhi_unblocked),
389 dhi_prepare_decode: Some(dhi_prepare_decode),
390 dhi_process_header: Some(dhi_process_header),
391 };
392
393 #[derive(Debug)]
394 pub(super) struct HeaderBlockCtx {
395 decoder: *mut ls_qpack_rs_sys::lsqpack_dec,
396 encoded_data: Box<[u8]>,
397 encoded_data_offset: usize,
398 decoding_buffer: Vec<u8>,
399 header: ls_qpack_rs_sys::lsxpack_header,
400 blocked: bool,
401 error: bool,
402 stream_data: Vec<u8>,
403 decoded_headers: Vec<Header>,
404 _marker: PhantomPinned,
405 }
406
407 impl HeaderBlockCtx {
408 pub(super) fn new(
409 decoder: *mut ls_qpack_rs_sys::lsqpack_dec,
410 encoded_data: Box<[u8]>,
411 ) -> Pin<Box<Self>> {
412 debug_assert!(!decoder.is_null());
413
414 Box::pin(Self {
415 decoder,
416 encoded_data,
417 encoded_data_offset: 0,
418 decoding_buffer: Vec::new(),
419 stream_data: Vec::new(),
420 header: Default::default(),
421 blocked: false,
422 error: false,
423 decoded_headers: Default::default(),
424 _marker: PhantomPinned,
425 })
426 }
427
428 pub(super) unsafe fn as_mut_ptr(mut self: Pin<&mut Self>) -> *mut HeaderBlockCtx {
432 self.as_mut().get_unchecked_mut()
435 }
436
437 pub(super) fn encoded_cursor(self: Pin<&Self>) -> &[u8] {
438 debug_assert!(self.encoded_data_offset < self.encoded_data.len());
439 &self.get_ref().encoded_data[self.encoded_data_offset..]
440 }
441
442 pub(super) fn advance_cursor(self: Pin<&mut Self>, offset: usize) {
443 debug_assert!(offset <= self.encoded_data.len());
444 let this = unsafe { self.get_unchecked_mut() };
446 this.encoded_data_offset += offset;
447 }
448
449 pub(super) fn set_blocked(self: Pin<&mut Self>, blocked: bool) {
450 let this = unsafe { self.get_unchecked_mut() };
452 this.blocked = blocked;
453 }
454
455 pub(super) fn set_stream_data(self: Pin<&mut Self>, data: &[u8]) {
456 let this = unsafe { self.get_unchecked_mut() };
458 this.stream_data = data.to_vec();
459 }
460
461 pub(super) fn enable_error(self: Pin<&mut Self>) {
462 let this = unsafe { self.get_unchecked_mut() };
464 debug_assert!(!this.error);
465 this.error = true;
466 }
467
468 pub(super) fn is_blocked(self: Pin<&Self>) -> bool {
469 self.blocked
470 }
471
472 pub(super) fn is_error(self: Pin<&Self>) -> bool {
473 self.error
474 }
475
476 pub(super) fn decoded_headers(&self) -> Vec<Header> {
477 self.decoded_headers.clone()
478 }
479
480 pub(super) fn stream_data(&self) -> Vec<u8> {
481 self.stream_data.clone()
482 }
483
484 unsafe fn from_void_ptr(ptr: *mut libc::c_void) -> Pin<&'static mut Self> {
500 debug_assert!(!ptr.is_null());
501 Pin::new_unchecked(&mut *(ptr as *mut Self))
505 }
506
507 fn reset_header(self: Pin<&mut Self>) {
508 let this = unsafe { self.get_unchecked_mut() };
511 this.header = Default::default()
512 }
513
514 fn resize_header(self: Pin<&mut Self>, space: u16) {
515 let this = unsafe { self.get_unchecked_mut() };
519 this.decoding_buffer
520 .resize(space as usize, Default::default());
521
522 this.header.buf = this.decoding_buffer.as_mut_ptr() as *mut c_char;
523 this.header.val_len = space;
524 }
525
526 fn header_mut(self: Pin<&mut Self>) -> &mut ls_qpack_rs_sys::lsxpack_header {
527 let this = unsafe { self.get_unchecked_mut() };
529 &mut this.header
530 }
531
532 fn process_header(self: Pin<&mut Self>) -> Result<(), HeaderError> {
533 let this = unsafe { self.get_unchecked_mut() };
536
537 let header = Header::with_buffer(
538 std::mem::take(&mut this.decoding_buffer).into_boxed_slice(),
539 this.header.name_offset as usize,
540 this.header.name_len as usize,
541 this.header.val_offset as usize,
542 this.header.val_len as usize,
543 )?;
544
545 this.decoded_headers.push(header);
546
547 this.header = Default::default();
548
549 Ok(())
550 }
551 }
552
553 unsafe impl Send for HeaderBlockCtx {}
559
560 unsafe impl Sync for HeaderBlockCtx {}
564
565 extern "C" fn dhi_unblocked(hblock_ctx: *mut libc::c_void) {
566 let mut hblock_ctx = unsafe { HeaderBlockCtx::from_void_ptr(hblock_ctx) };
569
570 debug_assert!(hblock_ctx.as_ref().is_blocked());
571 hblock_ctx.as_mut().set_blocked(false);
572
573 let encoded_cursor = hblock_ctx.as_ref().encoded_cursor();
574 let encoded_cursor_len = encoded_cursor.len();
575 let mut cursor_after = encoded_cursor.as_ptr();
576
577 let mut buffer = vec![0; ls_qpack_rs_sys::LSQPACK_LONGEST_SDTC as usize];
578 let mut sdtc_buffer_size = buffer.len();
579
580 let result = unsafe {
584 ls_qpack_rs_sys::lsqpack_dec_header_read(
585 hblock_ctx.decoder,
586 hblock_ctx.as_mut().as_mut_ptr() as *mut libc::c_void,
587 &mut cursor_after,
588 encoded_cursor_len,
589 buffer.as_mut_ptr(),
590 &mut sdtc_buffer_size,
591 )
592 };
593
594 match result {
595 ls_qpack_rs_sys::lsqpack_read_header_status_LQRHS_DONE => {
596 buffer.truncate(sdtc_buffer_size);
597 hblock_ctx.as_mut().set_stream_data(&buffer);
598 }
599
600 ls_qpack_rs_sys::lsqpack_read_header_status_LQRHS_BLOCKED
601 | ls_qpack_rs_sys::lsqpack_read_header_status_LQRHS_NEED => {
602 let offset = unsafe {
606 cursor_after.offset_from(hblock_ctx.as_ref().encoded_cursor().as_ptr())
607 };
608
609 debug_assert!(offset >= 0);
610
611 hblock_ctx.as_mut().advance_cursor(offset as usize);
612 hblock_ctx.as_mut().set_blocked(true);
613 }
614
615 _ => {
616 hblock_ctx.as_mut().enable_error();
617 }
618 }
619 }
620
621 extern "C" fn dhi_prepare_decode(
622 hblock_ctx: *mut libc::c_void,
623 header: *mut ls_qpack_rs_sys::lsxpack_header,
624 space: libc::size_t,
625 ) -> *mut ls_qpack_rs_sys::lsxpack_header {
626 const MAX_SPACE: usize = u16::MAX as usize;
627
628 let mut hblock_ctx = unsafe {
629 HeaderBlockCtx::from_void_ptr(hblock_ctx)
632 };
633
634 if space > MAX_SPACE {
635 return std::ptr::null_mut();
636 }
637
638 let space = space as u16;
639
640 if header.is_null() {
641 hblock_ctx.as_mut().reset_header();
642 } else {
643 assert!(std::ptr::eq(&hblock_ctx.header, header));
644 assert!(space > hblock_ctx.header.val_len);
645 }
646
647 hblock_ctx.as_mut().resize_header(space);
648 hblock_ctx.as_mut().header_mut()
649 }
650
651 extern "C" fn dhi_process_header(
652 hblock_ctx: *mut libc::c_void,
653 header: *mut ls_qpack_rs_sys::lsxpack_header,
654 ) -> libc::c_int {
655 let mut hblock_ctx = unsafe { HeaderBlockCtx::from_void_ptr(hblock_ctx) };
658
659 debug_assert!(!hblock_ctx.blocked);
660 debug_assert_eq!(header as *const _, &hblock_ctx.header);
661
662 match hblock_ctx.as_mut().process_header() {
663 Ok(()) => 0,
664 Err(_) => {
665 hblock_ctx.as_mut().enable_error();
666 -1
667 }
668 }
669 }
670}
671
672impl Debug for DecoderError {
673 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
674 f.debug_struct("DecoderError")
675 .field("kind", &self.kind)
676 .finish()
677 }
678}
679
680impl Display for DecoderError {
681 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
682 match self.kind {
683 DecoderErrorKind::DuplicateStreamId => {
684 write!(f, "stream ID already has a pending header block")
685 }
686 DecoderErrorKind::DecodeFailed => write!(f, "decoding operation failed"),
687 DecoderErrorKind::FeedFailed => write!(f, "failed to process encoder stream data"),
688 DecoderErrorKind::InvalidHeader => {
689 write!(f, "invalid header encountered during decoding")
690 }
691 }
692 }
693}
694
695impl std::error::Error for DecoderError {}
696
697#[cfg(test)]
698mod tests {
699 use super::*;
700 use crate::encoder::Encoder;
701
702 #[test]
704 fn test_decode_static_table_round_trip() {
705 let headers = [(":status", "200"), ("content-type", "text/html")];
706
707 let encoded = Encoder::new()
708 .encode_all(StreamId::new(0), headers)
709 .unwrap();
710
711 let (hdr_data, stream_data) = encoded.into();
712 assert!(
713 stream_data.is_empty(),
714 "static table should produce no stream data"
715 );
716
717 let output = Decoder::new(0, 0)
718 .decode(StreamId::new(0), hdr_data)
719 .unwrap();
720
721 let decoded = output.take().expect("should not be blocked");
722 let hdrs = decoded.headers();
723 assert_eq!(hdrs.len(), 2);
724 assert_eq!(hdrs[0].name(), ":status");
725 assert_eq!(hdrs[0].value(), "200");
726 assert_eq!(hdrs[1].name(), "content-type");
727 assert_eq!(hdrs[1].value(), "text/html");
728 }
729
730 #[test]
732 fn test_decode_multiple_streams() {
733 let mut encoder = Encoder::new();
734 let mut decoder = Decoder::new(0, 0);
735
736 for stream_id in 0..5u64 {
737 let encoded = encoder
738 .encode_all(StreamId::new(stream_id), [(":method", "GET")])
739 .unwrap();
740
741 let (hdr_data, _) = encoded.into();
742
743 let output = decoder.decode(StreamId::new(stream_id), hdr_data).unwrap();
744
745 let decoded = output.take().expect("should not be blocked");
746 assert_eq!(decoded.headers()[0].name(), ":method");
747 assert_eq!(decoded.headers()[0].value(), "GET");
748 }
749 }
750
751 #[test]
756 fn test_decode_duplicate_stream_id_error() {
757 let mut encoder = Encoder::new();
758 let sdtc = encoder.configure(4096, 4096, 100).unwrap();
759
760 let mut decoder = Decoder::new(4096, 100);
761 decoder.feed(sdtc.data()).unwrap();
762
763 let mut hdr_data_blocked = None;
767 let mut enc_stream_blocked = None;
768
769 for i in 0..10u64 {
770 let name = format!("x-unique-{}", i);
771 let value = format!("value-{}", i);
772 let encoded = encoder
773 .encode_all(StreamId::new(i), [(name.as_str(), value.as_str())])
774 .unwrap();
775
776 let (hdr_data, enc_stream) = encoded.into();
777
778 if !enc_stream.is_empty() && hdr_data_blocked.is_none() {
779 hdr_data_blocked = Some((i, hdr_data));
782 enc_stream_blocked = Some(enc_stream);
783 continue;
784 }
785
786 if !enc_stream.is_empty() {
788 decoder.feed(&enc_stream).unwrap();
789 }
790 let _ = decoder.decode(StreamId::new(i), hdr_data);
791 }
792
793 if let Some((stream_id, hdr_data)) = hdr_data_blocked {
794 let output = decoder.decode(StreamId::new(stream_id), &hdr_data).unwrap();
795 assert!(
796 output.is_blocked(),
797 "should be blocked without encoder stream"
798 );
799
800 let err = decoder
802 .decode(StreamId::new(stream_id), &hdr_data)
803 .unwrap_err();
804 assert_eq!(err.kind(), DecoderErrorKind::DuplicateStreamId);
805
806 if let Some(enc_stream) = enc_stream_blocked {
808 decoder.feed(&enc_stream).unwrap();
809 }
810 } else {
811 let encoded = encoder
815 .encode_all(StreamId::new(100), [(":status", "200")])
816 .unwrap();
817 let (hdr_data, enc_stream) = encoded.into();
818 if !enc_stream.is_empty() {
819 decoder.feed(&enc_stream).unwrap();
820 }
821 let _ = decoder.decode(StreamId::new(100), &hdr_data).unwrap();
823 }
825 }
826
827 #[test]
830 fn test_dynamic_table_round_trip() {
831 let mut encoder = Encoder::new();
832 let sdtc = encoder.configure(4096, 4096, 100).unwrap();
833
834 let mut decoder = Decoder::new(4096, 100);
835 decoder.feed(sdtc.data()).unwrap();
836
837 let headers = [
838 (":status", "200"),
839 ("x-custom", "hello"),
840 ("x-another", "world"),
841 ];
842
843 let encoded = encoder.encode_all(StreamId::new(0), headers).unwrap();
844
845 let (hdr_data, enc_stream) = encoded.into();
846
847 decoder.feed(&enc_stream).unwrap();
849
850 let output = decoder.decode(StreamId::new(0), hdr_data).unwrap();
851 let decoded = output
852 .take()
853 .expect("should not be blocked after feeding encoder stream");
854
855 let hdrs = decoded.headers();
856 assert_eq!(hdrs.len(), 3);
857 assert_eq!(hdrs[0].name(), ":status");
858 assert_eq!(hdrs[0].value(), "200");
859 assert_eq!(hdrs[1].name(), "x-custom");
860 assert_eq!(hdrs[1].value(), "hello");
861 assert_eq!(hdrs[2].name(), "x-another");
862 assert_eq!(hdrs[2].value(), "world");
863 }
864
865 #[test]
867 fn test_dynamic_table_blocked_then_unblocked() {
868 let mut encoder = Encoder::new();
869 let sdtc = encoder.configure(4096, 4096, 100).unwrap();
870
871 let mut decoder = Decoder::new(4096, 100);
872 decoder.feed(sdtc.data()).unwrap();
873
874 let mut blocked_info = None;
877
878 for i in 0..10u64 {
879 let name = format!("x-blocked-test-{}", i);
880 let value = format!("value-{}", i);
881 let encoded = encoder
882 .encode_all(StreamId::new(i), [(name.as_str(), value.as_str())])
883 .unwrap();
884
885 let (hdr_data, enc_stream) = encoded.into();
886
887 if !enc_stream.is_empty() && blocked_info.is_none() {
888 let output = decoder.decode(StreamId::new(i), &hdr_data).unwrap();
890 if output.is_blocked() {
891 blocked_info = Some((i, enc_stream, name, value));
892 continue;
893 }
894 }
896
897 if !enc_stream.is_empty() {
898 decoder.feed(&enc_stream).unwrap();
899 }
900 let _ = decoder.decode(StreamId::new(i), hdr_data);
901 }
902
903 if let Some((stream_id, enc_stream, name, value)) = blocked_info {
904 decoder.feed(&enc_stream).unwrap();
906
907 let result = decoder.unblocked(StreamId::new(stream_id));
909 let output = result
910 .expect("should have result for blocked stream")
911 .unwrap();
912 let decoded = output.take().expect("should be unblocked now");
913
914 assert_eq!(decoded.headers().len(), 1);
915 assert_eq!(decoded.headers()[0].name(), name);
916 assert_eq!(decoded.headers()[0].value(), value);
917 }
918 }
921
922 #[test]
924 fn test_decoder_error_display() {
925 let err = DecoderError::new(DecoderErrorKind::DuplicateStreamId);
926 assert_eq!(
927 err.to_string(),
928 "stream ID already has a pending header block"
929 );
930
931 let err = DecoderError::new(DecoderErrorKind::DecodeFailed);
932 assert_eq!(err.to_string(), "decoding operation failed");
933
934 let err = DecoderError::new(DecoderErrorKind::FeedFailed);
935 assert_eq!(err.to_string(), "failed to process encoder stream data");
936
937 let err = DecoderError::new(DecoderErrorKind::InvalidHeader);
938 assert_eq!(
939 err.to_string(),
940 "invalid header encountered during decoding"
941 );
942 }
943
944 #[test]
946 fn test_decoder_error_debug() {
947 let err = DecoderError::new(DecoderErrorKind::DecodeFailed);
948 let debug = format!("{:?}", err);
949 assert!(debug.contains("DecoderError"));
950 assert!(debug.contains("DecodeFailed"));
951 }
952
953 #[test]
955 fn test_decoder_output_variants() {
956 let blocked = DecoderOutput::BlockedStream;
957 assert!(blocked.is_blocked());
958 assert!(blocked.take().is_none());
959
960 let done = DecoderOutput::Done(BuffersDecoded {
961 headers: vec![],
962 stream: vec![].into_boxed_slice(),
963 });
964 assert!(!done.is_blocked());
965 assert!(done.take().is_some());
966 }
967
968 #[test]
970 fn test_decode_produces_stream_data() {
971 let encoded = Encoder::new()
972 .encode_all(StreamId::new(0), [(":method", "GET")])
973 .unwrap();
974
975 let (hdr_data, _) = encoded.into();
976
977 let output = Decoder::new(0, 0)
978 .decode(StreamId::new(0), hdr_data)
979 .unwrap();
980
981 let decoded = output.take().expect("should decode");
982 assert_eq!(decoded.headers().len(), 1);
985 assert_eq!(decoded.headers()[0].name(), ":method");
986 assert_eq!(decoded.headers()[0].value(), "GET");
987 }
988}