1use crate::quiche;
30use quiche::h3::frame::Frame as QFrame;
31use quiche::h3::Error as H3Error;
32use quiche::h3::Result;
33
34use crate::frame::H3iFrame;
35
36const MAX_STREAM_STATE_SIZE: usize = 2_000_000;
38
39#[derive(Debug, Default, PartialEq, Eq)]
40enum FrameState {
41 #[default]
42 Type,
43 Len,
44 Val,
45}
46
47#[derive(Debug, Eq, PartialEq)]
48pub enum InterruptCause {
50 FinBit,
51 ResetStream(u64),
52}
53
54#[derive(Debug, Eq, PartialEq)]
55pub enum FrameParseResult {
57 Retry,
62 FrameParsed { h3i_frame: H3iFrame, fin: bool },
65 Interrupted(InterruptCause),
68}
69
70pub(crate) struct FrameParser {
79 ty: Option<u64>,
80 len: Option<u64>,
81
82 stream_id: u64,
83
84 curr_state: FrameState,
85 state_buf: Vec<u8>,
86 state_offset: usize,
87 state_len: usize,
88}
89
90impl FrameParser {
91 pub(crate) fn new(stream_id: u64) -> Self {
92 Self {
93 stream_id,
94 ..Default::default()
95 }
96 }
97
98 pub(crate) fn try_parse_frame<F: quiche::BufFactory>(
119 &mut self, qconn: &mut quiche::Connection<F>,
120 ) -> Result<FrameParseResult> {
121 loop {
122 let (len, fin) = match self.try_fill_buffer(qconn, self.stream_id) {
123 Ok((l, f)) => (l, f),
124 Err(H3Error::TransportError(quiche::Error::StreamReset(err))) =>
125 return Ok(FrameParseResult::Interrupted(
126 InterruptCause::ResetStream(err),
127 )),
128 Err(e) => return Err(e),
129 };
130
131 log::trace!(
132 "{} stream={} read bytes={len:?}",
133 qconn.trace_id(),
134 self.stream_id
135 );
136
137 if fin && self.frame_incomplete() {
138 return Ok(FrameParseResult::Interrupted(InterruptCause::FinBit));
139 };
140
141 match self.curr_state {
142 FrameState::Type => {
143 let Ok(varint) = self.try_consume_varint() else {
144 return Ok(FrameParseResult::Retry);
147 };
148
149 self.set_frame_type(varint)?;
150 self.state_transition(FrameState::Len, 1)?;
151 },
152 FrameState::Len => {
153 let Ok(varint) = self.try_consume_varint() else {
154 return Ok(FrameParseResult::Retry);
157 };
158
159 self.set_frame_len(varint)?;
160 self.state_transition(
161 FrameState::Val,
162 self.len.expect("frame len is not set") as usize,
163 )?;
164 },
165 FrameState::Val => {
166 if self.state_buffer_complete() {
167 let h3i_frame = self.build_h3i_frame()?;
168 let ty = match self.ty.unwrap() {
170 0x0 => "DATA".to_string(),
171 0x1 => "HEADERS".to_string(),
172 0x3 => "CANCEL_PUSH".to_string(),
173 0x4 => "SETTINGS".to_string(),
174 0x5 => "PUSH_PROMISE".to_string(),
175 0x7 => "GOAWAY".to_string(),
176 0xd => "MAX_PUSH_AWAY".to_string(),
177 _ => format!("UNKNOWN val={}", self.ty.unwrap()),
178 };
179 log::info!(
180 "{} stream={} frame rx ty={} len={}",
181 qconn.trace_id(),
182 self.stream_id,
183 ty,
184 self.len.unwrap()
185 );
186
187 *self = Self::new(self.stream_id);
189 return Ok(FrameParseResult::FrameParsed {
190 h3i_frame,
191 fin,
192 });
193 };
194
195 return Err(H3Error::TransportError(quiche::Error::Done));
199 },
200 }
201 }
202 }
203
204 fn frame_incomplete(&self) -> bool {
205 !self.state_buf.is_empty() && !self.state_buffer_complete()
206 }
207
208 fn try_fill_buffer<F: quiche::BufFactory>(
209 &mut self, qconn: &mut quiche::Connection<F>, stream_id: u64,
210 ) -> Result<(usize, bool)> {
211 if self.state_buffer_complete() {
212 return Ok((0, qconn.stream_finished(stream_id)));
213 }
214
215 let buf = &mut self.state_buf[self.state_offset..self.state_len];
216 match qconn.stream_recv(stream_id, buf) {
217 Ok((len, fin)) => {
218 self.state_offset += len;
219 Ok((len, fin))
220 },
221 Err(e) => Err(H3Error::TransportError(e)),
222 }
223 }
224
225 fn try_consume_varint(&mut self) -> Result<u64> {
226 if self.state_offset == 1 {
227 self.state_len = octets::varint_parse_len(self.state_buf[0]);
228 self.state_buf.resize(self.state_len, 0);
229 }
230
231 if !self.state_buffer_complete() {
232 return Err(H3Error::TransportError(quiche::Error::Done));
233 }
234
235 let varint = octets::Octets::with_slice(&self.state_buf).get_varint()?;
236 Ok(varint)
237 }
238
239 fn state_buffer_complete(&self) -> bool {
240 self.state_offset == self.state_len
241 }
242
243 fn state_transition(
244 &mut self, new_state: FrameState, expected_len: usize,
245 ) -> Result<()> {
246 if expected_len > MAX_STREAM_STATE_SIZE {
250 return Err(quiche::h3::Error::ExcessiveLoad);
251 }
252
253 self.state_buf.resize(expected_len, 0);
254 self.curr_state = new_state;
255 self.state_offset = 0;
256 self.state_len = expected_len;
257
258 Ok(())
259 }
260
261 fn set_frame_type(&mut self, ty: u64) -> Result<()> {
262 self.ty = Some(ty);
263 self.state_transition(FrameState::Len, 1)?;
264
265 Ok(())
266 }
267
268 fn set_frame_len(&mut self, len: u64) -> Result<()> {
269 self.len = Some(len);
270 self.state_transition(FrameState::Val, len as usize)?;
271
272 Ok(())
273 }
274
275 fn build_h3i_frame(&mut self) -> Result<H3iFrame> {
276 let qframe = QFrame::from_bytes(
277 self.ty.expect("frame ty not set"),
278 self.len.expect("frame len not set"),
279 &self.state_buf,
280 )?;
281
282 match qframe {
283 QFrame::Headers { ref header_block } => {
284 let mut qpack_decoder = quiche::h3::qpack::Decoder::new();
285 let headers = qpack_decoder
286 .decode(header_block, u64::MAX)
287 .map_err(|_| H3Error::QpackDecompressionFailed)?;
288
289 Ok(H3iFrame::Headers(headers.into()))
290 },
291 _ => Ok(qframe.into()),
292 }
293 }
294}
295
296impl std::fmt::Debug for FrameParser {
297 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
298 let s = format!(
299 "FrameParser {{ stream: {}, type: {:?}, length: {:?} }}",
300 self.stream_id, self.ty, self.len,
301 );
302
303 write!(f, "{s}")
304 }
305}
306
307impl Default for FrameParser {
308 fn default() -> Self {
309 Self {
310 ty: None,
311 len: None,
312 curr_state: FrameState::default(),
313 stream_id: 0,
314 state_buf: vec![0],
315 state_offset: 0,
316 state_len: 1,
317 }
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324 use quiche::h3::frame::Frame;
325 use quiche::h3::testing::*;
326
327 fn session() -> Result<Session> {
328 Session::new()
329 }
330
331 #[test]
335 fn simple_case() {
336 let mut s = session().unwrap();
337 s.handshake().unwrap();
338
339 let mut parser = FrameParser::new(0);
340 let expected = Frame::Data {
341 payload: vec![1, 2, 3, 4, 5],
342 };
343 s.send_frame_client(expected.clone(), 0, true)
344 .expect("first");
345
346 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
347 assert_eq!(res, FrameParseResult::FrameParsed {
348 h3i_frame: H3iFrame::QuicheH3(expected),
349 fin: true
350 });
351 }
352
353 #[test]
354 fn type_precedes_split() {
355 let mut s = session().unwrap();
356 s.handshake().unwrap();
357
358 let mut parser = FrameParser::new(0);
359 let expected = Frame::Data {
360 payload: vec![10; 10],
361 };
362 s.send_arbitrary_stream_data_client(&[0], 0, false)
363 .expect("first");
364 let res = parser.try_parse_frame(&mut s.pipe.server);
365 assert_eq!(res, Err(H3Error::TransportError(quiche::Error::Done)));
366
367 s.send_arbitrary_stream_data_client(&[10; 11], 0, true)
368 .expect("second");
369 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
370 assert_eq!(res, FrameParseResult::FrameParsed {
371 h3i_frame: H3iFrame::QuicheH3(expected),
372 fin: true
373 });
374 }
375
376 #[test]
377 fn type_multiple_bytes() {
378 let mut s = session().unwrap();
379 s.handshake().unwrap();
380
381 let mut parser = FrameParser::new(0);
382 let expected = Frame::Unknown {
383 raw_type: 64,
384 payload: vec![1, 2, 3, 4, 5],
385 };
386 s.send_arbitrary_stream_data_client(&[64, 64, 5, 1, 2, 3, 4, 5], 0, true)
387 .expect("first");
388
389 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
390 assert_eq!(res, FrameParseResult::Retry);
391
392 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
393 assert_eq!(res, FrameParseResult::FrameParsed {
394 h3i_frame: H3iFrame::QuicheH3(expected),
395 fin: true
396 });
397 }
398
399 #[test]
400 fn type_multiple_buffers() {
401 let mut s = session().unwrap();
402 s.handshake().unwrap();
403
404 let mut parser = FrameParser::new(0);
405 let expected = Frame::Unknown {
406 raw_type: 64,
407 payload: vec![1, 2, 3, 4, 5],
408 };
409
410 s.send_arbitrary_stream_data_client(&[64], 0, false)
411 .expect("first");
412 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
413 assert_eq!(res, FrameParseResult::Retry);
414
415 let res = parser.try_parse_frame(&mut s.pipe.server);
416 assert_eq!(res, Err(H3Error::TransportError(quiche::Error::Done)));
417
418 s.send_arbitrary_stream_data_client(&[64, 5, 1, 2, 3, 4, 5], 0, true)
419 .expect("second");
420 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
421 assert_eq!(res, FrameParseResult::FrameParsed {
422 h3i_frame: H3iFrame::QuicheH3(expected),
423 fin: true
424 });
425 }
426
427 #[test]
428 fn type_multiple_buffers_precedes_split() {
429 let mut s = session().unwrap();
430 s.handshake().unwrap();
431
432 let mut parser = FrameParser::default();
433 let expected = Frame::Unknown {
434 raw_type: 64,
435 payload: vec![1, 2, 3, 4, 5],
436 };
437
438 s.send_arbitrary_stream_data_client(&[64, 64], 0, false)
439 .expect("first");
440 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
441 assert_eq!(res, FrameParseResult::Retry);
442
443 let res = parser.try_parse_frame(&mut s.pipe.server);
444 assert_eq!(res, Err(H3Error::TransportError(quiche::Error::Done)));
445
446 s.send_arbitrary_stream_data_client(&[5, 1, 2, 3, 4, 5], 0, false)
447 .expect("second");
448 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
449 assert_eq!(res, FrameParseResult::FrameParsed {
450 h3i_frame: H3iFrame::QuicheH3(expected),
451 fin: false
452 });
453 }
454
455 #[test]
456 fn len_precedes_split() {
457 let mut s = session().unwrap();
458 s.handshake().unwrap();
459
460 let mut parser = FrameParser::default();
461 let expected = Frame::Data {
462 payload: vec![57; 10],
463 };
464
465 s.send_arbitrary_stream_data_client(&[0, 10], 0, false)
466 .expect("first");
467 let res = parser.try_parse_frame(&mut s.pipe.server);
468 assert_eq!(res, Err(H3Error::TransportError(quiche::Error::Done)));
469
470 s.send_arbitrary_stream_data_client(&[57; 10], 0, true)
471 .expect("second");
472 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
473 assert_eq!(res, FrameParseResult::FrameParsed {
474 h3i_frame: H3iFrame::QuicheH3(expected),
475 fin: true
476 });
477 }
478
479 #[test]
480 fn len_multiple_bytes() {
481 let mut s = session().unwrap();
482 s.handshake().unwrap();
483
484 let mut b = vec![0, 64, 64];
485 let mut payload = vec![0; 64];
486 let mut parser = FrameParser::default();
487 let expected = Frame::Data {
488 payload: payload.clone(),
489 };
490 b.append(&mut payload);
491
492 s.send_arbitrary_stream_data_client(&b, 0, true)
493 .expect("first");
494 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
495 assert_eq!(res, FrameParseResult::Retry);
496
497 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
498 assert_eq!(res, FrameParseResult::FrameParsed {
499 h3i_frame: H3iFrame::QuicheH3(expected),
500 fin: true
501 });
502 }
503
504 #[test]
505 fn len_multiple_buffers() {
506 let mut s = session().unwrap();
507 s.handshake().unwrap();
508
509 let mut parser = FrameParser::default();
510 let expected = Frame::Data {
511 payload: vec![64; 64],
512 };
513
514 s.send_arbitrary_stream_data_client(&[0, 64], 0, false)
515 .expect("first");
516 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
517 assert_eq!(res, FrameParseResult::Retry);
518
519 let res = parser.try_parse_frame(&mut s.pipe.server);
520 assert_eq!(res, Err(H3Error::TransportError(quiche::Error::Done)));
521
522 s.send_arbitrary_stream_data_client(&[64; 65], 0, true)
523 .expect("second");
524 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
525 assert_eq!(res, FrameParseResult::FrameParsed {
526 h3i_frame: H3iFrame::QuicheH3(expected),
527 fin: true
528 });
529 }
530
531 #[test]
532 fn len_multiple_buffers_precedes_split() {
533 let mut s = session().unwrap();
534 s.handshake().unwrap();
535
536 let mut parser = FrameParser::default();
537 let expected = Frame::Data {
538 payload: vec![0; 64],
539 };
540
541 s.send_arbitrary_stream_data_client(&[0, 64, 64], 0, false)
542 .expect("first");
543 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
544 assert_eq!(res, FrameParseResult::Retry);
545
546 let res = parser.try_parse_frame(&mut s.pipe.server);
547 assert_eq!(res, Err(H3Error::TransportError(quiche::Error::Done)));
548
549 s.send_arbitrary_stream_data_client(&[0; 64], 0, true)
550 .expect("second");
551 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
552 assert_eq!(res, FrameParseResult::FrameParsed {
553 h3i_frame: H3iFrame::QuicheH3(expected),
554 fin: true
555 });
556 }
557
558 #[test]
559 fn no_val() {
560 let mut s = session().unwrap();
561 s.handshake().unwrap();
562
563 let mut parser = FrameParser::default();
564 let first = Frame::Unknown {
565 raw_type: 64,
566 payload: vec![],
567 };
568 let second = Frame::Data {
569 payload: vec![1, 2, 3],
570 };
571
572 s.send_arbitrary_stream_data_client(&[64, 64, 0, 0, 3, 1, 2, 3], 0, true)
573 .expect("first");
574 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
575 assert_eq!(res, FrameParseResult::Retry);
576
577 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
578 assert_eq!(res, FrameParseResult::FrameParsed {
579 h3i_frame: H3iFrame::QuicheH3(first),
580 fin: false
581 });
582
583 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
584 assert_eq!(res, FrameParseResult::FrameParsed {
585 h3i_frame: H3iFrame::QuicheH3(second),
586 fin: true
587 });
588 }
589
590 #[test]
591 fn val_multiple_buffers() {
592 let mut s = session().unwrap();
593 s.handshake().unwrap();
594
595 let mut parser = FrameParser::default();
596 let expected = Frame::Data {
597 payload: vec![1, 2, 3, 4, 5],
598 };
599
600 s.send_arbitrary_stream_data_client(&[0, 5, 1], 0, false)
601 .expect("first");
602 let res = parser.try_parse_frame(&mut s.pipe.server);
603 assert_eq!(res, Err(H3Error::TransportError(quiche::Error::Done)));
604
605 s.send_arbitrary_stream_data_client(&[2, 3, 4, 5], 0, false)
606 .expect("second");
607 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
608 assert_eq!(res, FrameParseResult::FrameParsed {
609 h3i_frame: H3iFrame::QuicheH3(expected),
610 fin: false
611 });
612 }
613
614 #[test]
615 fn val_doesnt_extend_to_buffer_end() {
616 let mut s = session().unwrap();
617 s.handshake().unwrap();
618
619 let mut parser = FrameParser::default();
620 let expected = Frame::Data {
621 payload: vec![1, 2, 3, 4, 5],
622 };
623
624 s.send_arbitrary_stream_data_client(&[0, 5, 1, 2, 3, 4, 5, 0], 0, true)
625 .expect("first");
626 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
627 assert_eq!(res, FrameParseResult::FrameParsed {
628 h3i_frame: H3iFrame::QuicheH3(expected),
629 fin: false
630 });
631
632 let res = parser.try_parse_frame(&mut s.pipe.server);
633 assert_eq!(res, Err(H3Error::TransportError(quiche::Error::Done)));
634 assert_eq!(parser.ty, Some(0));
635 assert_eq!(parser.curr_state, FrameState::Len);
636 }
637
638 #[test]
639 fn multiple_frames_in_buffer() {
640 let mut s = session().unwrap();
641 s.handshake().unwrap();
642
643 let mut parser = FrameParser::default();
644 let first = Frame::Data {
645 payload: vec![1, 2, 3],
646 };
647 let second = Frame::Data {
648 payload: vec![1, 2, 3, 4],
649 };
650
651 s.send_arbitrary_stream_data_client(
652 &[0, 3, 1, 2, 3, 0, 4, 1, 2, 3, 4],
653 0,
654 true,
655 )
656 .expect("first");
657 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
658 assert_eq!(res, FrameParseResult::FrameParsed {
659 h3i_frame: H3iFrame::QuicheH3(first),
660 fin: false
661 });
662 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
663 assert_eq!(res, FrameParseResult::FrameParsed {
664 h3i_frame: H3iFrame::QuicheH3(second),
665 fin: true
666 });
667 }
668
669 #[test]
670 fn multiple_frames_multiple_buffers() {
671 let mut s = session().unwrap();
672 s.handshake().unwrap();
673
674 let mut parser = FrameParser::default();
675 let first = Frame::Data {
676 payload: vec![1, 2, 3],
677 };
678 let second = Frame::Data {
679 payload: vec![1, 2, 3, 4],
680 };
681 let third = Frame::Data {
682 payload: vec![1, 2, 3],
683 };
684
685 s.send_arbitrary_stream_data_client(
686 &[0, 3, 1, 2, 3, 0, 4, 1, 2, 3, 4, 0, 3, 1],
687 0,
688 false,
689 )
690 .expect("first");
691 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
692 assert_eq!(res, FrameParseResult::FrameParsed {
693 h3i_frame: H3iFrame::QuicheH3(first),
694 fin: false
695 });
696 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
697 assert_eq!(res, FrameParseResult::FrameParsed {
698 h3i_frame: H3iFrame::QuicheH3(second),
699 fin: false
700 });
701
702 let res = parser.try_parse_frame(&mut s.pipe.server);
703 assert_eq!(res, Err(H3Error::TransportError(quiche::Error::Done)));
704 assert_eq!(parser.ty, Some(0));
705 assert_eq!(parser.len, Some(3));
706 assert_eq!(parser.state_buf, vec![1, 0, 0]);
707
708 s.send_arbitrary_stream_data_client(&[2, 3], 0, true)
709 .expect("second");
710 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
711 assert_eq!(res, FrameParseResult::FrameParsed {
712 h3i_frame: H3iFrame::QuicheH3(third),
713 fin: true
714 });
715 }
716
717 #[test]
718 fn multiple_frames_nonzero_stream() {
719 let mut s = session().unwrap();
720 s.handshake().unwrap();
721
722 let mut parser = FrameParser::default();
723 let first = Frame::Data {
724 payload: vec![1, 2, 3],
725 };
726 let second = Frame::Data {
727 payload: vec![1, 2, 3, 4, 5],
728 };
729
730 s.send_arbitrary_stream_data_client(&[0, 3, 1, 2, 3], 0, true)
731 .expect("first");
732 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
733 assert_eq!(res, FrameParseResult::FrameParsed {
734 h3i_frame: H3iFrame::QuicheH3(first.clone()),
735 fin: true
736 });
737
738 parser = FrameParser::new(4);
739 s.send_arbitrary_stream_data_client(&[0, 5, 1, 2, 3, 4, 5], 4, false)
740 .expect("second");
741 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
742 assert_eq!(res, FrameParseResult::FrameParsed {
743 h3i_frame: H3iFrame::QuicheH3(second),
744 fin: false
745 });
746
747 s.send_arbitrary_stream_data_client(&[0, 3, 1, 2, 3], 4, true)
748 .expect("third");
749 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
750 assert_eq!(res, FrameParseResult::FrameParsed {
751 h3i_frame: H3iFrame::QuicheH3(first),
752 fin: true
753 });
754 }
755
756 #[test]
757 fn interrupted() {
758 let mut s = session().unwrap();
759 s.handshake().unwrap();
760
761 let mut parser = FrameParser::default();
762 s.send_arbitrary_stream_data_client(&[0, 3, 1, 2], 0, true)
763 .expect("send");
764
765 assert_eq!(
766 parser.try_parse_frame(&mut s.pipe.server),
767 Ok(FrameParseResult::Interrupted(InterruptCause::FinBit))
768 );
769 }
770
771 #[test]
772 fn headers_decode_error() {
773 let mut s = session().unwrap();
774 s.handshake().unwrap();
775
776 let mut parser = FrameParser::default();
777
778 s.send_arbitrary_stream_data_client(&[1, 3, 0, 0, 0x80], 0, true)
781 .expect("send");
782
783 assert_eq!(
784 parser.try_parse_frame(&mut s.pipe.server),
785 Err(H3Error::QpackDecompressionFailed)
786 );
787 }
788
789 #[test]
790 fn stream_reset() {
791 let mut s = session().unwrap();
792 s.handshake().unwrap();
793
794 let mut parser = FrameParser::default();
795 let expected = Frame::Data {
796 payload: vec![1, 2, 3, 4, 5],
797 };
798
799 s.send_arbitrary_stream_data_client(&[0, 5, 1, 2, 3, 4, 5], 0, false)
800 .expect("first");
801 let res = parser.try_parse_frame(&mut s.pipe.server).unwrap();
802 assert_eq!(res, FrameParseResult::FrameParsed {
803 h3i_frame: H3iFrame::QuicheH3(expected),
804 fin: false
805 });
806
807 s.pipe
808 .client
809 .stream_shutdown(0, quiche::Shutdown::Write, 0)
810 .expect("shutdown");
811 s.pipe.advance().expect("advance");
812 assert_eq!(
813 parser.try_parse_frame(&mut s.pipe.server),
814 Ok(FrameParseResult::Interrupted(InterruptCause::ResetStream(
815 0
816 )))
817 );
818 }
819}