1#![allow(dead_code)]
13
14use std::future::Future;
15use std::pin::Pin;
16use std::sync::atomic::{AtomicBool, Ordering};
17use std::sync::Arc;
18use std::task::{Context, Poll};
19
20use bytes::{Buf, Bytes};
21use tokio::sync::{mpsc, oneshot};
22
23use h3::quic::{self, ConnectionErrorIncoming, StreamErrorIncoming, StreamId, WriteBuf};
24
25use crate::buffer::{SendAccounting, TerminalCell, WriteCompletion, WriteOutcome};
26use crate::driver::{BidiHandoff, ConnShared, DriverCommand, RecvHandoff, SendHandoff};
27use crate::error::{internal_stream_error, ConnTerminal, RecvEnd, SendEnd};
28
29fn stream_id(id: u64) -> StreamId {
32 StreamId::try_from(id).expect("worker allocates only valid QUIC stream ids")
33}
34
35fn conn_terminal_stream_err(term: &Arc<ConnTerminal>) -> StreamErrorIncoming {
38 StreamErrorIncoming::ConnectionErrorIncoming {
39 connection_error: term.to_h3(),
40 }
41}
42
43pub struct H3RecvStream<B: Buf> {
52 id: u64,
53 bytes: mpsc::Receiver<Bytes>,
54 terminal: TerminalCell<RecvEnd>,
55 resume: Arc<AtomicBool>,
56 blocked: Arc<AtomicBool>,
60 cmd_tx: mpsc::UnboundedSender<DriverCommand<B>>,
61 terminal_seen: bool,
63 stop_sent: bool,
65}
66
67impl<B: Buf> H3RecvStream<B> {
68 pub(crate) fn from_handoff(h: RecvHandoff<B>) -> Self {
69 h.cleanup.disarm();
72 H3RecvStream {
73 id: h.id,
74 bytes: h.bytes,
75 terminal: h.terminal,
76 resume: h.resume,
77 blocked: h.blocked,
78 cmd_tx: h.cmd_tx,
79 terminal_seen: false,
80 stop_sent: false,
81 }
82 }
83
84 fn signal_resume(&self) {
95 if self.blocked.swap(false, Ordering::AcqRel) && !self.resume.swap(true, Ordering::Relaxed)
96 {
97 let _ = self.cmd_tx.send(DriverCommand::RecvResume { id: self.id });
98 }
99 }
100
101 fn resolve_terminal(
104 &mut self,
105 end: RecvEnd,
106 ) -> Poll<Result<Option<Bytes>, StreamErrorIncoming>> {
107 self.terminal_seen = true;
108 match end.to_h3() {
109 None => Poll::Ready(Ok(None)),
110 Some(err) => Poll::Ready(Err(err)),
111 }
112 }
113}
114
115impl<B: Buf> quic::RecvStream for H3RecvStream<B> {
116 type Buf = Bytes;
117
118 fn poll_data(
119 &mut self,
120 cx: &mut Context<'_>,
121 ) -> Poll<Result<Option<Self::Buf>, StreamErrorIncoming>> {
122 match self.bytes.poll_recv(cx) {
124 Poll::Ready(Some(b)) => {
125 self.signal_resume();
126 Poll::Ready(Ok(Some(b)))
127 }
128 Poll::Ready(None) => {
129 match self.terminal.poll(cx) {
133 Poll::Ready(end) => self.resolve_terminal(end),
134 Poll::Pending => Poll::Ready(Err(internal_stream_error(
135 "recv byte channel closed without a published terminal",
136 ))),
137 }
138 }
139 Poll::Pending => {
140 match self.terminal.poll(cx) {
142 Poll::Ready(end) => {
143 if let Poll::Ready(Some(b)) = self.bytes.poll_recv(cx) {
147 self.signal_resume();
148 return Poll::Ready(Ok(Some(b)));
149 }
150 self.resolve_terminal(end)
151 }
152 Poll::Pending => Poll::Pending,
153 }
154 }
155 }
156 }
157
158 fn stop_sending(&mut self, error_code: u64) {
159 self.stop_sent = true;
160 let _ = self.cmd_tx.send(DriverCommand::StopSending {
161 id: self.id,
162 code: error_code,
163 });
164 }
165
166 fn recv_id(&self) -> StreamId {
167 stream_id(self.id)
168 }
169}
170
171impl<B: Buf> Drop for H3RecvStream<B> {
172 fn drop(&mut self) {
173 if self.stop_sent || self.terminal_seen || self.terminal.get().is_some() {
176 return;
177 }
178 let _ = self.cmd_tx.send(DriverCommand::StopSending {
179 id: self.id,
180 code: 0,
181 });
182 }
183}
184
185pub struct H3SendStream<B: Buf> {
194 id: u64,
195 status: TerminalCell<SendEnd>,
196 cmd_tx: mpsc::UnboundedSender<DriverCommand<B>>,
197 stash: Option<WriteBuf<B>>,
199 write_completion: WriteCompletion<SendEnd>,
203 send_gen: Option<u64>,
206 finish_completion: Option<oneshot::Receiver<Result<(), SendEnd>>>,
208 finish_result: Option<Result<(), SendEnd>>,
210 finalized: bool,
212 local_terminal: Option<SendEnd>,
215 send_accounting: Arc<SendAccounting>,
219}
220
221impl<B: Buf> H3SendStream<B> {
222 pub(crate) fn from_handoff(h: SendHandoff<B>) -> Self {
223 h.cleanup.disarm();
225 H3SendStream {
226 id: h.id,
227 status: h.status,
228 cmd_tx: h.cmd_tx,
229 stash: None,
230 write_completion: WriteCompletion::new(),
231 send_gen: None,
232 finish_completion: None,
233 finish_result: None,
234 finalized: false,
235 local_terminal: None,
236 send_accounting: h.send_accounting,
237 }
238 }
239
240 fn terminal_now(&self, cx: &mut Context<'_>) -> Option<SendEnd> {
243 if let Some(end) = &self.local_terminal {
244 return Some(end.clone());
245 }
246 match self.status.poll(cx) {
247 Poll::Ready(end) => Some(end),
248 Poll::Pending => None,
249 }
250 }
251
252 fn terminal_now_noctx(&self) -> Option<SendEnd> {
254 self.local_terminal.clone().or_else(|| self.status.get())
255 }
256
257 fn sticky_or_internal(&self, cx: &mut Context<'_>, msg: &'static str) -> StreamErrorIncoming {
260 match self.terminal_now(cx) {
261 Some(end) => end.to_h3(),
262 None => internal_stream_error(msg),
263 }
264 }
265
266 #[cfg(test)]
269 pub(crate) fn write_generation(&self) -> u64 {
270 self.write_completion.generation()
271 }
272
273 fn resolve_write(
278 &self,
279 outcome: WriteOutcome<SendEnd>,
280 cx: &mut Context<'_>,
281 ) -> Result<(), StreamErrorIncoming> {
282 match outcome {
283 WriteOutcome::Done(result) => result.map_err(|e| e.to_h3()),
284 WriteOutcome::Cancelled => {
285 Err(self.sticky_or_internal(cx, "send completion cancelled without a terminal"))
286 }
287 }
288 }
289
290 fn sticky_send_end_or_internal(&self, cx: &mut Context<'_>, msg: &'static str) -> SendEnd {
296 self.terminal_now(cx)
297 .unwrap_or_else(|| SendEnd::Conn(Arc::new(ConnTerminal::Internal(msg))))
298 }
299}
300
301impl<B: Buf> quic::SendStream<B> for H3SendStream<B> {
302 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), StreamErrorIncoming>> {
303 if let Some(generation) = self.send_gen {
305 match self.write_completion.poll(generation, cx) {
306 Poll::Ready(outcome) => {
307 self.send_gen = None;
308 return Poll::Ready(self.resolve_write(outcome, cx));
309 }
310 Poll::Pending => return Poll::Pending,
311 }
312 }
313 if let Some(end) = self.terminal_now(cx) {
315 return Poll::Ready(Err(end.to_h3()));
316 }
317 let buf = match self.stash.take() {
319 None => return Poll::Ready(Ok(())),
320 Some(buf) => buf,
321 };
322 let bytes = buf.remaining();
327 let permit = match self.send_accounting.try_reserve(bytes) {
328 Some(permit) => permit,
329 None => {
330 self.send_accounting.register_waiter(self.id, cx.waker());
335 match self.send_accounting.try_reserve(bytes) {
336 Some(permit) => permit,
337 None => {
338 self.stash = Some(buf);
339 return Poll::Pending;
340 }
341 }
342 }
343 };
344 self.send_accounting.unregister_waiter(self.id);
348 let generation = self.write_completion.begin();
354 let done = self.write_completion.completer(generation);
355 if self
356 .cmd_tx
357 .send(DriverCommand::Send {
358 id: self.id,
359 buf,
360 done,
361 permit: Some(permit),
362 })
363 .is_err()
364 {
365 return Poll::Ready(Err(
368 self.sticky_or_internal(cx, "send channel closed without a terminal")
369 ));
370 }
371 self.send_gen = Some(generation);
372 match self.write_completion.poll(generation, cx) {
373 Poll::Ready(outcome) => {
374 self.send_gen = None;
375 Poll::Ready(self.resolve_write(outcome, cx))
376 }
377 Poll::Pending => Poll::Pending,
378 }
379 }
380
381 fn send_data<T: Into<WriteBuf<B>>>(&mut self, data: T) -> Result<(), StreamErrorIncoming> {
382 if self.stash.is_some() {
383 return Err(internal_stream_error(
385 "send_data called while a previous write is still pending poll_ready",
386 ));
387 }
388 self.stash = Some(data.into());
389 Ok(())
390 }
391
392 fn poll_finish(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), StreamErrorIncoming>> {
393 if let Some(result) = &self.finish_result {
395 return Poll::Ready(result.clone().map_err(|e| e.to_h3()));
396 }
397 if self.finish_completion.is_some() {
399 match Pin::new(self.finish_completion.as_mut().unwrap()).poll(cx) {
400 Poll::Ready(Ok(result)) => {
401 self.finish_completion = None;
402 self.finish_result = Some(result.clone());
403 return Poll::Ready(result.map_err(|e| e.to_h3()));
404 }
405 Poll::Ready(Err(_)) => {
406 self.finish_completion = None;
407 let end = self.sticky_send_end_or_internal(
410 cx,
411 "finish completion cancelled without a terminal",
412 );
413 self.finish_result = Some(Err(end.clone()));
414 return Poll::Ready(Err(end.to_h3()));
415 }
416 Poll::Pending => return Poll::Pending,
417 }
418 }
419 if self.finalized {
422 return Poll::Ready(match self.terminal_now(cx) {
423 Some(end) => Err(end.to_h3()),
424 None => Ok(()),
425 });
426 }
427 if let Some(end) = self.terminal_now(cx) {
429 self.finalized = true;
430 self.finish_result = Some(Err(end.clone()));
431 return Poll::Ready(Err(end.to_h3()));
432 }
433 let (done_tx, done_rx) = oneshot::channel();
435 self.finalized = true;
436 if self
437 .cmd_tx
438 .send(DriverCommand::Finish {
439 id: self.id,
440 done: done_tx,
441 })
442 .is_err()
443 {
444 let end =
445 self.sticky_send_end_or_internal(cx, "finish channel closed without a terminal");
446 self.finish_result = Some(Err(end.clone()));
447 return Poll::Ready(Err(end.to_h3()));
448 }
449 self.finish_completion = Some(done_rx);
450 match Pin::new(self.finish_completion.as_mut().unwrap()).poll(cx) {
451 Poll::Ready(Ok(result)) => {
452 self.finish_completion = None;
453 self.finish_result = Some(result.clone());
454 Poll::Ready(result.map_err(|e| e.to_h3()))
455 }
456 Poll::Ready(Err(_)) => {
457 self.finish_completion = None;
458 let end = self.sticky_send_end_or_internal(
459 cx,
460 "finish completion cancelled without a terminal",
461 );
462 self.finish_result = Some(Err(end.clone()));
463 Poll::Ready(Err(end.to_h3()))
464 }
465 Poll::Pending => Poll::Pending,
466 }
467 }
468
469 fn reset(&mut self, reset_code: u64) {
470 if self.finalized {
472 return;
473 }
474 self.finalized = true;
475 if self.status.get().is_none() {
479 self.local_terminal = Some(SendEnd::Reset {
480 error_code: reset_code,
481 });
482 }
483 let _ = self.cmd_tx.send(DriverCommand::Reset {
485 id: self.id,
486 code: reset_code,
487 });
488 }
489
490 fn send_id(&self) -> StreamId {
491 stream_id(self.id)
492 }
493}
494
495impl<B: Buf> Drop for H3SendStream<B> {
496 fn drop(&mut self) {
497 self.send_accounting.unregister_waiter(self.id);
502 if self.finalized || self.terminal_now_noctx().is_some() {
505 return;
506 }
507 self.finalized = true;
508 let (done_tx, _done_rx) = oneshot::channel();
509 let _ = self.cmd_tx.send(DriverCommand::Finish {
510 id: self.id,
511 done: done_tx,
512 });
513 }
514}
515
516pub struct H3Stream<B: Buf> {
523 send: H3SendStream<B>,
524 recv: H3RecvStream<B>,
525}
526
527impl<B: Buf> H3Stream<B> {
528 pub(crate) fn from_handoff(h: BidiHandoff<B>) -> Self {
529 H3Stream {
530 send: H3SendStream::from_handoff(h.send),
531 recv: H3RecvStream::from_handoff(h.recv),
532 }
533 }
534}
535
536impl<B: Buf> quic::SendStream<B> for H3Stream<B> {
537 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), StreamErrorIncoming>> {
538 self.send.poll_ready(cx)
539 }
540 fn send_data<T: Into<WriteBuf<B>>>(&mut self, data: T) -> Result<(), StreamErrorIncoming> {
541 self.send.send_data(data)
542 }
543 fn poll_finish(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), StreamErrorIncoming>> {
544 self.send.poll_finish(cx)
545 }
546 fn reset(&mut self, reset_code: u64) {
547 self.send.reset(reset_code)
548 }
549 fn send_id(&self) -> StreamId {
550 self.send.send_id()
551 }
552}
553
554impl<B: Buf> quic::RecvStream for H3Stream<B> {
555 type Buf = Bytes;
556 fn poll_data(
557 &mut self,
558 cx: &mut Context<'_>,
559 ) -> Poll<Result<Option<Self::Buf>, StreamErrorIncoming>> {
560 self.recv.poll_data(cx)
561 }
562 fn stop_sending(&mut self, error_code: u64) {
563 self.recv.stop_sending(error_code)
564 }
565 fn recv_id(&self) -> StreamId {
566 self.recv.recv_id()
567 }
568}
569
570impl<B: Buf> quic::BidiStream<B> for H3Stream<B> {
571 type SendStream = H3SendStream<B>;
572 type RecvStream = H3RecvStream<B>;
573 fn split(self) -> (Self::SendStream, Self::RecvStream) {
574 (self.send, self.recv)
575 }
576}
577
578pub struct StreamOpener<B: Buf> {
587 cmd_tx: mpsc::UnboundedSender<DriverCommand<B>>,
588 shared: Arc<ConnShared>,
589 pending_bidi: Option<oneshot::Receiver<Result<BidiHandoff<B>, Arc<ConnTerminal>>>>,
590 pending_uni: Option<oneshot::Receiver<Result<SendHandoff<B>, Arc<ConnTerminal>>>>,
591}
592
593impl<B: Buf> StreamOpener<B> {
594 pub(crate) fn from_parts(
595 cmd_tx: mpsc::UnboundedSender<DriverCommand<B>>,
596 shared: Arc<ConnShared>,
597 ) -> Self {
598 StreamOpener {
599 cmd_tx,
600 shared,
601 pending_bidi: None,
602 pending_uni: None,
603 }
604 }
605
606 fn submit_terminal(&self) -> StreamErrorIncoming {
610 match self.shared.conn_terminal.get() {
611 Some(term) => conn_terminal_stream_err(&term),
612 None => internal_stream_error("open declined without a published terminal"),
613 }
614 }
615}
616
617impl<B: Buf> Clone for StreamOpener<B> {
618 fn clone(&self) -> Self {
619 StreamOpener {
622 cmd_tx: self.cmd_tx.clone(),
623 shared: Arc::clone(&self.shared),
624 pending_bidi: None,
625 pending_uni: None,
626 }
627 }
628}
629
630impl<B: Buf> quic::OpenStreams<B> for StreamOpener<B> {
631 type BidiStream = H3Stream<B>;
632 type SendStream = H3SendStream<B>;
633
634 fn poll_open_bidi(
635 &mut self,
636 cx: &mut Context<'_>,
637 ) -> Poll<Result<Self::BidiStream, StreamErrorIncoming>> {
638 if self.pending_bidi.is_none() {
639 if let Some(term) = self.shared.conn_terminal.get() {
643 return Poll::Ready(Err(conn_terminal_stream_err(&term)));
644 }
645 let (reply_tx, reply_rx) = oneshot::channel();
646 if self
647 .cmd_tx
648 .send(DriverCommand::OpenBidi { reply: reply_tx })
649 .is_err()
650 {
651 return Poll::Ready(Err(self.submit_terminal()));
652 }
653 self.pending_bidi = Some(reply_rx);
654 }
655 match Pin::new(self.pending_bidi.as_mut().unwrap()).poll(cx) {
656 Poll::Ready(Ok(Ok(handoff))) => {
657 self.pending_bidi = None;
658 Poll::Ready(Ok(H3Stream::from_handoff(handoff)))
659 }
660 Poll::Ready(Ok(Err(term))) => {
661 self.pending_bidi = None;
662 Poll::Ready(Err(conn_terminal_stream_err(&term)))
663 }
664 Poll::Ready(Err(_)) => {
665 self.pending_bidi = None;
668 Poll::Ready(Err(self.submit_terminal()))
669 }
670 Poll::Pending => Poll::Pending,
671 }
672 }
673
674 fn poll_open_send(
675 &mut self,
676 cx: &mut Context<'_>,
677 ) -> Poll<Result<Self::SendStream, StreamErrorIncoming>> {
678 if self.pending_uni.is_none() {
679 if let Some(term) = self.shared.conn_terminal.get() {
680 return Poll::Ready(Err(conn_terminal_stream_err(&term)));
681 }
682 let (reply_tx, reply_rx) = oneshot::channel();
683 if self
684 .cmd_tx
685 .send(DriverCommand::OpenUni { reply: reply_tx })
686 .is_err()
687 {
688 return Poll::Ready(Err(self.submit_terminal()));
689 }
690 self.pending_uni = Some(reply_rx);
691 }
692 match Pin::new(self.pending_uni.as_mut().unwrap()).poll(cx) {
693 Poll::Ready(Ok(Ok(handoff))) => {
694 self.pending_uni = None;
695 Poll::Ready(Ok(H3SendStream::from_handoff(handoff)))
696 }
697 Poll::Ready(Ok(Err(term))) => {
698 self.pending_uni = None;
699 Poll::Ready(Err(conn_terminal_stream_err(&term)))
700 }
701 Poll::Ready(Err(_)) => {
702 self.pending_uni = None;
703 Poll::Ready(Err(self.submit_terminal()))
704 }
705 Poll::Pending => Poll::Pending,
706 }
707 }
708
709 fn close(&mut self, code: h3::error::Code, reason: &[u8]) {
710 let _ = self.cmd_tx.send(DriverCommand::Close {
711 code: code.value(),
712 reason: Bytes::copy_from_slice(reason),
713 });
714 }
715}
716
717pub struct Connection<B: Buf> {
725 accept_bidi_rx: mpsc::Receiver<BidiHandoff<B>>,
726 accept_uni_rx: mpsc::Receiver<RecvHandoff<B>>,
727 accept_terminal_bidi: TerminalCell<Arc<ConnTerminal>>,
728 accept_terminal_uni: TerminalCell<Arc<ConnTerminal>>,
729 accept_bidi_resume: Arc<AtomicBool>,
730 accept_uni_resume: Arc<AtomicBool>,
731 opener: StreamOpener<B>,
732}
733
734impl<B: Buf> Connection<B> {
735 #[allow(clippy::too_many_arguments)]
736 pub(crate) fn from_parts(
737 accept_bidi_rx: mpsc::Receiver<BidiHandoff<B>>,
738 accept_uni_rx: mpsc::Receiver<RecvHandoff<B>>,
739 accept_terminal_bidi: TerminalCell<Arc<ConnTerminal>>,
740 accept_terminal_uni: TerminalCell<Arc<ConnTerminal>>,
741 accept_bidi_resume: Arc<AtomicBool>,
742 accept_uni_resume: Arc<AtomicBool>,
743 opener: StreamOpener<B>,
744 ) -> Self {
745 Connection {
746 accept_bidi_rx,
747 accept_uni_rx,
748 accept_terminal_bidi,
749 accept_terminal_uni,
750 accept_bidi_resume,
751 accept_uni_resume,
752 opener,
753 }
754 }
755
756 fn signal_accept_bidi_resume(&self) {
759 if !self.accept_bidi_resume.swap(true, Ordering::Relaxed) {
760 let _ = self.opener.cmd_tx.send(DriverCommand::AcceptBidiResume);
761 }
762 }
763
764 fn signal_accept_uni_resume(&self) {
766 if !self.accept_uni_resume.swap(true, Ordering::Relaxed) {
767 let _ = self.opener.cmd_tx.send(DriverCommand::AcceptUniResume);
768 }
769 }
770}
771
772impl<B: Buf> quic::OpenStreams<B> for Connection<B> {
773 type BidiStream = H3Stream<B>;
774 type SendStream = H3SendStream<B>;
775
776 fn poll_open_bidi(
777 &mut self,
778 cx: &mut Context<'_>,
779 ) -> Poll<Result<Self::BidiStream, StreamErrorIncoming>> {
780 self.opener.poll_open_bidi(cx)
781 }
782
783 fn poll_open_send(
784 &mut self,
785 cx: &mut Context<'_>,
786 ) -> Poll<Result<Self::SendStream, StreamErrorIncoming>> {
787 self.opener.poll_open_send(cx)
788 }
789
790 fn close(&mut self, code: h3::error::Code, reason: &[u8]) {
791 self.opener.close(code, reason)
792 }
793}
794
795impl<B: Buf> quic::Connection<B> for Connection<B> {
796 type RecvStream = H3RecvStream<B>;
797 type OpenStreams = StreamOpener<B>;
798
799 fn poll_accept_recv(
800 &mut self,
801 cx: &mut Context<'_>,
802 ) -> Poll<Result<Self::RecvStream, ConnectionErrorIncoming>> {
803 match self.accept_uni_rx.poll_recv(cx) {
804 Poll::Ready(Some(handoff)) => {
805 self.signal_accept_uni_resume();
806 Poll::Ready(Ok(H3RecvStream::from_handoff(handoff)))
807 }
808 Poll::Ready(None) => match self.accept_terminal_uni.poll(cx) {
809 Poll::Ready(term) => Poll::Ready(Err(term.to_h3())),
810 Poll::Pending => Poll::Ready(Err(ConnectionErrorIncoming::InternalError(
811 "uni accept channel closed without a published terminal".to_string(),
812 ))),
813 },
814 Poll::Pending => match self.accept_terminal_uni.poll(cx) {
815 Poll::Ready(term) => {
816 if let Poll::Ready(Some(handoff)) = self.accept_uni_rx.poll_recv(cx) {
819 self.signal_accept_uni_resume();
820 return Poll::Ready(Ok(H3RecvStream::from_handoff(handoff)));
821 }
822 Poll::Ready(Err(term.to_h3()))
823 }
824 Poll::Pending => Poll::Pending,
825 },
826 }
827 }
828
829 fn poll_accept_bidi(
830 &mut self,
831 cx: &mut Context<'_>,
832 ) -> Poll<Result<Self::BidiStream, ConnectionErrorIncoming>> {
833 match self.accept_bidi_rx.poll_recv(cx) {
834 Poll::Ready(Some(handoff)) => {
835 self.signal_accept_bidi_resume();
836 Poll::Ready(Ok(H3Stream::from_handoff(handoff)))
837 }
838 Poll::Ready(None) => match self.accept_terminal_bidi.poll(cx) {
839 Poll::Ready(term) => Poll::Ready(Err(term.to_h3())),
840 Poll::Pending => Poll::Ready(Err(ConnectionErrorIncoming::InternalError(
841 "bidi accept channel closed without a published terminal".to_string(),
842 ))),
843 },
844 Poll::Pending => match self.accept_terminal_bidi.poll(cx) {
845 Poll::Ready(term) => {
846 if let Poll::Ready(Some(handoff)) = self.accept_bidi_rx.poll_recv(cx) {
847 self.signal_accept_bidi_resume();
848 return Poll::Ready(Ok(H3Stream::from_handoff(handoff)));
849 }
850 Poll::Ready(Err(term.to_h3()))
851 }
852 Poll::Pending => Poll::Pending,
853 },
854 }
855 }
856
857 fn opener(&self) -> Self::OpenStreams {
858 self.opener.clone()
860 }
861}
862
863impl<B: Buf> Drop for Connection<B> {
864 fn drop(&mut self) {
865 let _ = self.opener.cmd_tx.send(DriverCommand::ConnectionDropped);
868 }
869}
870
871fn _assert_h3_traits<B: Buf>() {
879 fn is_connection<B: Buf, T: quic::Connection<B>>() {}
880 fn is_open_streams<B: Buf, T: quic::OpenStreams<B>>() {}
881 fn is_bidi_stream<B: Buf, T: quic::BidiStream<B>>() {}
882 fn is_send_stream<B: Buf, T: quic::SendStream<B>>() {}
883 fn is_recv_stream<T: quic::RecvStream>() {}
884
885 is_connection::<B, Connection<B>>();
886 is_open_streams::<B, StreamOpener<B>>();
887 is_bidi_stream::<B, H3Stream<B>>();
888 is_send_stream::<B, H3SendStream<B>>();
889 is_recv_stream::<H3RecvStream<B>>();
890}
891
892#[cfg(test)]
893mod tests {
894 use super::*;
895 use crate::error::CloseOrigin;
896 use h3::quic::{Connection as _, OpenStreams as _, RecvStream as _, SendStream as _};
897 use std::task::{RawWaker, RawWakerVTable, Waker};
898
899 fn noop_cx() -> Context<'static> {
902 Context::from_waker(noop_waker_ref())
903 }
904
905 fn noop_waker_ref() -> &'static Waker {
906 static VTABLE: RawWakerVTable = RawWakerVTable::new(
907 |_| RawWaker::new(std::ptr::null(), &VTABLE),
908 |_| {},
909 |_| {},
910 |_| {},
911 );
912 static WAKER: std::sync::OnceLock<Waker> = std::sync::OnceLock::new();
913 WAKER.get_or_init(|| unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), &VTABLE)) })
914 }
915
916 fn flag_waker(flag: Arc<AtomicBool>) -> Waker {
919 let ptr = Arc::into_raw(flag) as *const ();
920 unsafe { Waker::from_raw(RawWaker::new(ptr, &FLAG_VTABLE)) }
921 }
922
923 static FLAG_VTABLE: RawWakerVTable = RawWakerVTable::new(
924 |p| unsafe {
925 let arc = Arc::from_raw(p as *const AtomicBool);
926 let cloned = arc.clone();
927 std::mem::forget(arc);
928 RawWaker::new(Arc::into_raw(cloned) as *const (), &FLAG_VTABLE)
929 },
930 |p| unsafe {
931 let arc = Arc::from_raw(p as *const AtomicBool);
932 arc.store(true, std::sync::atomic::Ordering::SeqCst);
933 },
934 |p| unsafe {
935 let arc = Arc::from_raw(p as *const AtomicBool);
936 arc.store(true, std::sync::atomic::Ordering::SeqCst);
937 std::mem::forget(arc);
938 },
939 |p| unsafe {
940 drop(Arc::from_raw(p as *const AtomicBool));
941 },
942 );
943
944 #[allow(clippy::type_complexity)]
945 fn recv_channel() -> (
946 mpsc::Sender<Bytes>,
947 TerminalCell<RecvEnd>,
948 Arc<AtomicBool>,
949 Arc<AtomicBool>,
950 H3RecvStream<Bytes>,
951 mpsc::UnboundedReceiver<DriverCommand<Bytes>>,
952 ) {
953 let (btx, brx) = mpsc::channel(4);
954 let (ctx, crx) = mpsc::unbounded_channel();
955 let terminal = TerminalCell::new();
956 let resume = Arc::new(AtomicBool::new(false));
957 let blocked = Arc::new(AtomicBool::new(false));
958 let recv = H3RecvStream::from_handoff(RecvHandoff {
959 id: 0,
960 bytes: brx,
961 terminal: terminal.clone(),
962 resume: Arc::clone(&resume),
963 blocked: Arc::clone(&blocked),
964 cmd_tx: ctx.clone(),
965 cleanup: crate::driver::HandoffCleanup::new(0, true, ctx),
966 });
967 (btx, terminal, resume, blocked, recv, crx)
968 }
969
970 fn send_half(
971 id: u64,
972 ) -> (
973 TerminalCell<SendEnd>,
974 H3SendStream<Bytes>,
975 mpsc::UnboundedReceiver<DriverCommand<Bytes>>,
976 ) {
977 send_half_with(id, SendAccounting::new(None))
978 }
979
980 fn send_half_with(
983 id: u64,
984 accounting: Arc<SendAccounting>,
985 ) -> (
986 TerminalCell<SendEnd>,
987 H3SendStream<Bytes>,
988 mpsc::UnboundedReceiver<DriverCommand<Bytes>>,
989 ) {
990 let (ctx, crx) = mpsc::unbounded_channel();
991 let status = TerminalCell::new();
992 let send = H3SendStream::from_handoff(SendHandoff {
993 id,
994 status: status.clone(),
995 cmd_tx: ctx.clone(),
996 send_accounting: accounting,
997 cleanup: crate::driver::HandoffCleanup::new(id, false, ctx),
998 });
999 (status, send, crx)
1000 }
1001
1002 fn wbuf(payload: &'static [u8]) -> WriteBuf<Bytes> {
1003 WriteBuf::from(h3::proto::frame::Frame::Data(Bytes::from_static(payload)))
1004 }
1005
1006 fn wire_len(payload: &'static [u8]) -> usize {
1009 wbuf(payload).remaining()
1010 }
1011
1012 #[test]
1018 fn sf6_enqueue_failure_rolls_back_reserved_bytes() {
1019 let acct = SendAccounting::new(Some(1024));
1020 let (status, mut send, crx) = send_half_with(0, Arc::clone(&acct));
1021 drop(crx);
1023 status.set(SendEnd::Reset { error_code: 9 });
1025
1026 let mut cx = noop_cx();
1027 send.send_data(wbuf(b"hello")).unwrap();
1028 assert_eq!(acct.resident(), 0, "nothing reserved until the flush");
1029 match send.poll_ready(&mut cx) {
1030 Poll::Ready(Err(_)) => {}
1031 other => panic!("expected terminal error on closed channel, got {other:?}"),
1032 }
1033 assert_eq!(
1034 acct.resident(),
1035 0,
1036 "a failed enqueue must not leak the reserved bytes"
1037 );
1038 }
1039
1040 #[test]
1043 fn poll_data_delivers_buffered_bytes_before_terminal() {
1044 let (btx, terminal, _resume, _blocked, mut recv, _crx) = recv_channel();
1045 btx.try_send(Bytes::from_static(b"hi")).unwrap();
1047 terminal.set(RecvEnd::Fin);
1048 let mut cx = noop_cx();
1049 match recv.poll_data(&mut cx) {
1050 Poll::Ready(Ok(Some(b))) => assert_eq!(&b[..], b"hi"),
1051 other => panic!("expected buffered bytes first, got {other:?}"),
1052 }
1053 assert!(matches!(recv.poll_data(&mut cx), Poll::Ready(Ok(None))));
1055 }
1056
1057 #[test]
1058 fn poll_data_maps_fin_reset_conn() {
1059 let mut cx = noop_cx();
1060 {
1062 let (_btx, terminal, _r, _blocked, mut recv, _c) = recv_channel();
1063 terminal.set(RecvEnd::Fin);
1064 assert!(matches!(recv.poll_data(&mut cx), Poll::Ready(Ok(None))));
1065 }
1066 {
1068 let (_btx, terminal, _r, _blocked, mut recv, _c) = recv_channel();
1069 terminal.set(RecvEnd::Reset { error_code: 42 });
1070 match recv.poll_data(&mut cx) {
1071 Poll::Ready(Err(StreamErrorIncoming::StreamTerminated { error_code })) => {
1072 assert_eq!(error_code, 42)
1073 }
1074 other => panic!("expected StreamTerminated, got {other:?}"),
1075 }
1076 }
1077 {
1079 let (_btx, terminal, _r, _blocked, mut recv, _c) = recv_channel();
1080 terminal.set(RecvEnd::Conn(Arc::new(ConnTerminal::Timeout)));
1081 match recv.poll_data(&mut cx) {
1082 Poll::Ready(Err(StreamErrorIncoming::ConnectionErrorIncoming {
1083 connection_error: ConnectionErrorIncoming::Timeout,
1084 })) => {}
1085 other => panic!("expected ConnectionErrorIncoming::Timeout, got {other:?}"),
1086 }
1087 }
1088 }
1089
1090 #[test]
1091 fn poll_data_closed_channel_without_terminal_is_internal_error() {
1092 let (btx, _terminal, _r, _blocked, mut recv, _c) = recv_channel();
1093 drop(btx); let mut cx = noop_cx();
1095 match recv.poll_data(&mut cx) {
1096 Poll::Ready(Err(StreamErrorIncoming::ConnectionErrorIncoming {
1097 connection_error: ConnectionErrorIncoming::InternalError(_),
1098 })) => {}
1099 other => panic!("expected InternalError, got {other:?}"),
1100 }
1101 }
1102
1103 #[test]
1104 fn recv_resume_gated_when_worker_never_blocked() {
1105 let (btx, _terminal, resume, blocked, mut recv, mut crx) = recv_channel();
1108 assert!(!blocked.load(Ordering::Relaxed));
1109 btx.try_send(Bytes::from_static(b"a")).unwrap();
1110 btx.try_send(Bytes::from_static(b"b")).unwrap();
1111 let mut cx = noop_cx();
1112 assert!(matches!(recv.poll_data(&mut cx), Poll::Ready(Ok(Some(_)))));
1113 assert!(matches!(recv.poll_data(&mut cx), Poll::Ready(Ok(Some(_)))));
1114 assert!(!resume.load(Ordering::Relaxed));
1116 assert!(
1117 crx.try_recv().is_err(),
1118 "must not emit RecvResume when worker never blocked"
1119 );
1120 }
1121
1122 #[test]
1123 fn recv_resume_sent_once_when_worker_blocked() {
1124 let (btx, _terminal, resume, blocked, mut recv, mut crx) = recv_channel();
1128 blocked.store(true, Ordering::Release);
1129 btx.try_send(Bytes::from_static(b"a")).unwrap();
1130 btx.try_send(Bytes::from_static(b"b")).unwrap();
1131 let mut cx = noop_cx();
1132 assert!(matches!(recv.poll_data(&mut cx), Poll::Ready(Ok(Some(_)))));
1134 assert!(resume.load(Ordering::Relaxed));
1135 assert!(
1136 !blocked.load(Ordering::Relaxed),
1137 "park flag must be cleared"
1138 );
1139 match crx.try_recv() {
1140 Ok(DriverCommand::RecvResume { id: 0 }) => {}
1141 other => panic!("expected one RecvResume, got {other:?}"),
1142 }
1143 assert!(matches!(recv.poll_data(&mut cx), Poll::Ready(Ok(Some(_)))));
1145 assert!(crx.try_recv().is_err(), "must not resend RecvResume");
1146 }
1147
1148 #[test]
1149 fn recv_drop_enqueues_stop_sending_zero() {
1150 let (_btx, _terminal, _r, _blocked, recv, mut crx) = recv_channel();
1151 drop(recv);
1152 match crx.try_recv() {
1153 Ok(DriverCommand::StopSending { id: 0, code: 0 }) => {}
1154 other => panic!("expected StopSending(0), got {other:?}"),
1155 }
1156 }
1157
1158 #[test]
1159 fn recv_drop_after_terminal_does_not_stop_send() {
1160 let (_btx, terminal, _r, _blocked, recv, mut crx) = recv_channel();
1161 terminal.set(RecvEnd::Fin);
1162 drop(recv);
1163 assert!(
1164 crx.try_recv().is_err(),
1165 "terminal recv must not stop-send on drop"
1166 );
1167 }
1168
1169 #[test]
1172 fn send_data_single_slot_errors_on_double_stash() {
1173 let (_status, mut send, _crx) = send_half(0);
1174 assert!(send.send_data(wbuf(b"one")).is_ok());
1175 match send.send_data(wbuf(b"two")) {
1176 Err(StreamErrorIncoming::ConnectionErrorIncoming {
1177 connection_error: ConnectionErrorIncoming::InternalError(_),
1178 }) => {}
1179 other => panic!("expected InternalError on double stash, got {other:?}"),
1180 }
1181 }
1182
1183 #[test]
1184 fn poll_ready_returns_recorded_completion_once_then_sticky() {
1185 let (status, mut send, mut crx) = send_half(0);
1186 let mut cx = noop_cx();
1187 assert!(matches!(send.poll_ready(&mut cx), Poll::Ready(Ok(()))));
1189 send.send_data(wbuf(b"body")).unwrap();
1191 assert!(matches!(send.poll_ready(&mut cx), Poll::Pending));
1192 let done = match crx.try_recv() {
1193 Ok(DriverCommand::Send { id: 0, done, .. }) => done,
1194 other => panic!("expected Send, got {other:?}"),
1195 };
1196 done.complete(Ok(()));
1199 status.set(SendEnd::Stopped { error_code: 7 });
1200 assert!(matches!(send.poll_ready(&mut cx), Poll::Ready(Ok(()))));
1201 match send.poll_ready(&mut cx) {
1203 Poll::Ready(Err(StreamErrorIncoming::StreamTerminated { error_code: 7 })) => {}
1204 other => panic!("expected sticky StreamTerminated, got {other:?}"),
1205 }
1206 }
1207
1208 #[test]
1212 fn poll_ready_reuses_one_completion_cell_across_writes() {
1213 let (_status, mut send, mut crx) = send_half(0);
1214 let mut cx = noop_cx();
1215 const K: u64 = 6;
1216 for expected_gen in 1..=K {
1217 send.send_data(wbuf(b"chunk")).unwrap();
1218 assert!(matches!(send.poll_ready(&mut cx), Poll::Pending));
1220 assert_eq!(
1221 send.write_generation(),
1222 expected_gen,
1223 "one generation bump per write — the cell is reused, not reallocated"
1224 );
1225 let done = match crx.try_recv() {
1226 Ok(DriverCommand::Send { id: 0, done, .. }) => done,
1227 other => panic!("expected Send, got {other:?}"),
1228 };
1229 done.complete(Ok(()));
1231 assert!(matches!(send.poll_ready(&mut cx), Poll::Ready(Ok(()))));
1232 assert!(matches!(send.poll_ready(&mut cx), Poll::Ready(Ok(()))));
1234 }
1235 assert_eq!(
1236 send.write_generation(),
1237 K,
1238 "one cell reused for all K writes"
1239 );
1240 }
1241
1242 #[test]
1246 fn sf6_unlimited_accounting_tracks_and_releases_bytes() {
1247 let acct = SendAccounting::new(None);
1248 let (_status, mut send, mut crx) = send_half_with(0, Arc::clone(&acct));
1249 let mut cx = noop_cx();
1250 assert_eq!(acct.resident(), 0);
1251 send.send_data(wbuf(b"hello")).unwrap();
1252 assert!(matches!(send.poll_ready(&mut cx), Poll::Pending));
1255 let hello = wire_len(b"hello");
1256 assert_eq!(acct.resident(), hello, "reserved on admission");
1257 let (done, permit) = match crx.try_recv() {
1258 Ok(DriverCommand::Send { done, permit, .. }) => (done, permit),
1259 other => panic!("expected Send, got {other:?}"),
1260 };
1261 assert!(permit.is_some(), "front end carries a byte permit (SF-6)");
1262 assert_eq!(permit.as_ref().unwrap().bytes(), hello);
1263 assert_eq!(acct.resident(), hello);
1265 done.complete(Ok(()));
1266 drop(permit);
1268 assert_eq!(acct.resident(), 0, "released once the permit dropped");
1269 assert!(matches!(send.poll_ready(&mut cx), Poll::Ready(Ok(()))));
1270 }
1271
1272 #[test]
1277 fn sf6_capped_accounting_parks_then_admits_on_release() {
1278 let hello = wire_len(b"hello");
1279 let x = wire_len(b"x");
1280 let acct = SendAccounting::new(Some(hello));
1282 let (_sa, mut send_a, mut crx_a) = send_half_with(0, Arc::clone(&acct));
1283 let (_sb, mut send_b, mut crx_b) = send_half_with(4, Arc::clone(&acct));
1284
1285 let mut cx_a = noop_cx();
1287 send_a.send_data(wbuf(b"hello")).unwrap();
1288 assert!(matches!(send_a.poll_ready(&mut cx_a), Poll::Pending));
1289 assert_eq!(acct.resident(), hello);
1290 let cmd_a = crx_a.try_recv().expect("A admitted");
1291
1292 let woken = Arc::new(AtomicBool::new(false));
1295 let waker = flag_waker(woken.clone());
1296 let mut cx_b = Context::from_waker(&waker);
1297 send_b.send_data(wbuf(b"x")).unwrap();
1298 assert!(matches!(send_b.poll_ready(&mut cx_b), Poll::Pending));
1299 assert!(crx_b.try_recv().is_err(), "B must not enqueue over the cap");
1300 assert_eq!(
1301 acct.resident(),
1302 hello,
1303 "B's bytes not reserved while parked"
1304 );
1305
1306 drop(cmd_a);
1309 assert_eq!(acct.resident(), 0, "A released");
1310 assert!(
1311 woken.load(std::sync::atomic::Ordering::SeqCst),
1312 "release woke B"
1313 );
1314
1315 assert!(matches!(send_b.poll_ready(&mut cx_b), Poll::Pending));
1317 assert_eq!(acct.resident(), x, "B admitted after A freed capacity");
1318 match crx_b.try_recv() {
1319 Ok(DriverCommand::Send { id: 4, permit, .. }) => {
1320 assert_eq!(permit.as_ref().unwrap().bytes(), x);
1321 }
1322 other => panic!("expected B's Send after release, got {other:?}"),
1323 }
1324 }
1325
1326 #[test]
1332 fn poll_ready_unapplied_send_resolves_via_sticky_terminal() {
1333 let (status, mut send, mut crx) = send_half(0);
1334 let mut cx = noop_cx();
1335 send.send_data(wbuf(b"body")).unwrap();
1336 assert!(matches!(send.poll_ready(&mut cx), Poll::Pending));
1337 let done = match crx.try_recv() {
1340 Ok(DriverCommand::Send { id: 0, done, .. }) => done,
1341 other => panic!("expected Send, got {other:?}"),
1342 };
1343 drop(done); status.set(SendEnd::Stopped { error_code: 9 });
1347 match send.poll_ready(&mut cx) {
1348 Poll::Ready(Err(StreamErrorIncoming::StreamTerminated { error_code: 9 })) => {}
1349 other => panic!("expected sticky StreamTerminated, got {other:?}"),
1350 }
1351 }
1352
1353 #[test]
1354 fn poll_finish_idempotent_one_finish() {
1355 let (_status, mut send, mut crx) = send_half(0);
1356 let mut cx = noop_cx();
1357 assert!(matches!(send.poll_finish(&mut cx), Poll::Pending));
1358 let done = match crx.try_recv() {
1359 Ok(DriverCommand::Finish { id: 0, done }) => done,
1360 other => panic!("expected Finish, got {other:?}"),
1361 };
1362 assert!(matches!(send.poll_finish(&mut cx), Poll::Pending));
1364 assert!(crx.try_recv().is_err(), "must not enqueue a second Finish");
1365 done.send(Ok(())).unwrap();
1366 assert!(matches!(send.poll_finish(&mut cx), Poll::Ready(Ok(()))));
1367 assert!(matches!(send.poll_finish(&mut cx), Poll::Ready(Ok(()))));
1369 assert!(crx.try_recv().is_err());
1370 }
1371
1372 #[test]
1376 fn poll_finish_failure_is_retained_not_success() {
1377 let (_status, mut send, crx) = send_half(0);
1378 drop(crx); let mut cx = noop_cx();
1380 match send.poll_finish(&mut cx) {
1381 Poll::Ready(Err(StreamErrorIncoming::ConnectionErrorIncoming {
1382 connection_error: ConnectionErrorIncoming::InternalError(_),
1383 })) => {}
1384 other => panic!("expected InternalError on first poll, got {other:?}"),
1385 }
1386 match send.poll_finish(&mut cx) {
1388 Poll::Ready(Err(StreamErrorIncoming::ConnectionErrorIncoming {
1389 connection_error: ConnectionErrorIncoming::InternalError(_),
1390 })) => {}
1391 other => panic!("finalized failure must not become Ok, got {other:?}"),
1392 }
1393 }
1394
1395 #[test]
1396 fn reset_enqueues_once_and_finalizes() {
1397 let (_status, mut send, mut crx) = send_half(4);
1398 send.reset(7);
1399 match crx.try_recv() {
1400 Ok(DriverCommand::Reset { id: 4, code: 7 }) => {}
1401 other => panic!("expected Reset(7), got {other:?}"),
1402 }
1403 send.reset(9);
1405 assert!(crx.try_recv().is_err(), "must not enqueue a second Reset");
1406 let mut cx = noop_cx();
1408 match send.poll_finish(&mut cx) {
1409 Poll::Ready(Err(StreamErrorIncoming::StreamTerminated { error_code: 7 })) => {}
1410 other => panic!("expected sticky reset terminal, got {other:?}"),
1411 }
1412 }
1413
1414 #[test]
1415 fn send_drop_enqueues_graceful_finish() {
1416 let (_status, send, mut crx) = send_half(0);
1417 drop(send);
1418 match crx.try_recv() {
1419 Ok(DriverCommand::Finish { id: 0, .. }) => {}
1420 other => panic!("expected graceful Finish on drop, got {other:?}"),
1421 }
1422 }
1423
1424 #[test]
1425 fn send_drop_after_finalize_does_not_finish() {
1426 let (_status, mut send, mut crx) = send_half(0);
1427 send.reset(3);
1428 let _ = crx.try_recv(); drop(send);
1430 assert!(
1431 crx.try_recv().is_err(),
1432 "finalized send must not finish on drop"
1433 );
1434 }
1435
1436 #[test]
1441 fn dropped_recv_handoff_enqueues_stop_sending() {
1442 let (_btx, brx) = mpsc::channel(1);
1443 let (ctx, mut crx) = mpsc::unbounded_channel::<DriverCommand<Bytes>>();
1444 let handoff = RecvHandoff {
1445 id: 8,
1446 bytes: brx,
1447 terminal: TerminalCell::new(),
1448 resume: Arc::new(AtomicBool::new(false)),
1449 blocked: Arc::new(AtomicBool::new(false)),
1450 cmd_tx: ctx.clone(),
1451 cleanup: crate::driver::HandoffCleanup::new(8, true, ctx),
1452 };
1453 drop(handoff); match crx.try_recv() {
1455 Ok(DriverCommand::StopSending { id: 8, code: 0 }) => {}
1456 other => panic!("expected StopSending on dropped handoff, got {other:?}"),
1457 }
1458 }
1459
1460 #[test]
1461 fn dropped_send_handoff_enqueues_finish() {
1462 let (ctx, mut crx) = mpsc::unbounded_channel::<DriverCommand<Bytes>>();
1463 let handoff = SendHandoff {
1464 id: 8,
1465 status: TerminalCell::new(),
1466 cmd_tx: ctx.clone(),
1467 send_accounting: SendAccounting::new(None),
1468 cleanup: crate::driver::HandoffCleanup::new(8, false, ctx),
1469 };
1470 drop(handoff);
1471 match crx.try_recv() {
1472 Ok(DriverCommand::Finish { id: 8, .. }) => {}
1473 other => panic!("expected graceful Finish on dropped handoff, got {other:?}"),
1474 }
1475 }
1476
1477 #[test]
1478 fn converted_handoff_disarms_guard() {
1479 let (_btx, _terminal, _resume, _blocked, recv, mut crx) = recv_channel();
1483 assert!(
1484 crx.try_recv().is_err(),
1485 "conversion must not fire the guard"
1486 );
1487 drop(recv);
1488 assert!(
1489 matches!(crx.try_recv(), Ok(DriverCommand::StopSending { .. })),
1490 "stream Drop (not the disarmed guard) enqueues cleanup"
1491 );
1492 }
1493
1494 fn opener() -> (
1497 StreamOpener<Bytes>,
1498 mpsc::UnboundedReceiver<DriverCommand<Bytes>>,
1499 Arc<ConnShared>,
1500 ) {
1501 let (ctx, crx) = mpsc::unbounded_channel();
1502 let shared = ConnShared::new(None);
1503 (
1504 StreamOpener::from_parts(ctx, Arc::clone(&shared)),
1505 crx,
1506 shared,
1507 )
1508 }
1509
1510 #[test]
1511 fn stream_opener_submit_helper_resolves_terminal_when_conn_terminal_preset() {
1512 let (mut op, mut crx, shared) = opener();
1513 shared.conn_terminal.set(Arc::new(ConnTerminal::AppClose {
1514 origin: CloseOrigin::Peer,
1515 error_code: 0x101,
1516 reason: Bytes::new(),
1517 }));
1518 let mut cx = noop_cx();
1519 match op.poll_open_bidi(&mut cx) {
1520 Poll::Ready(Err(StreamErrorIncoming::ConnectionErrorIncoming {
1521 connection_error: ConnectionErrorIncoming::ApplicationClose { error_code: 0x101 },
1522 })) => {}
1523 _ => panic!("expected preset terminal resolution"),
1524 }
1525 assert!(
1527 crx.try_recv().is_err(),
1528 "must not submit under a preset terminal"
1529 );
1530 }
1531
1532 #[test]
1533 fn cloned_opener_has_fresh_pending_slots() {
1534 let (mut op, mut crx, _shared) = opener();
1535 let mut cx = noop_cx();
1536 assert!(matches!(op.poll_open_bidi(&mut cx), Poll::Pending));
1538 assert!(op.pending_bidi.is_some());
1539 assert!(matches!(crx.try_recv(), Ok(DriverCommand::OpenBidi { .. })));
1540 let clone = op.clone();
1542 assert!(clone.pending_bidi.is_none());
1543 assert!(clone.pending_uni.is_none());
1544 }
1545
1546 #[test]
1547 fn opener_open_bidi_resolves_handoff_into_stream() {
1548 let (mut op, mut crx, _shared) = opener();
1549 let mut cx = noop_cx();
1550 assert!(matches!(op.poll_open_bidi(&mut cx), Poll::Pending));
1551 let reply = match crx.try_recv() {
1552 Ok(DriverCommand::OpenBidi { reply }) => reply,
1553 other => panic!("expected OpenBidi, got {other:?}"),
1554 };
1555 let (_btx, brx) = mpsc::channel(1);
1557 let (ictx, _icrx) = mpsc::unbounded_channel();
1558 let handoff = BidiHandoff {
1559 send: SendHandoff {
1560 id: 0,
1561 status: TerminalCell::new(),
1562 cmd_tx: ictx.clone(),
1563 send_accounting: SendAccounting::new(None),
1564 cleanup: crate::driver::HandoffCleanup::new(0, false, ictx.clone()),
1565 },
1566 recv: RecvHandoff {
1567 id: 0,
1568 bytes: brx,
1569 terminal: TerminalCell::new(),
1570 resume: Arc::new(AtomicBool::new(false)),
1571 blocked: Arc::new(AtomicBool::new(false)),
1572 cmd_tx: ictx.clone(),
1573 cleanup: crate::driver::HandoffCleanup::new(0, true, ictx),
1574 },
1575 };
1576 reply.send(Ok(handoff)).ok().expect("deliver handoff");
1577 match op.poll_open_bidi(&mut cx) {
1578 Poll::Ready(Ok(_stream)) => {}
1579 _ => panic!("expected resolved H3Stream"),
1580 }
1581 assert!(op.pending_bidi.is_none(), "slot cleared after resolution");
1582 }
1583
1584 #[allow(clippy::type_complexity)]
1587 fn connection() -> (
1588 Connection<Bytes>,
1589 mpsc::Sender<BidiHandoff<Bytes>>,
1590 TerminalCell<Arc<ConnTerminal>>,
1591 Arc<AtomicBool>,
1592 mpsc::UnboundedReceiver<DriverCommand<Bytes>>,
1593 ) {
1594 let (btx, brx) = mpsc::channel(4);
1595 let (_utx, urx) = mpsc::channel(4);
1596 let (ctx, crx) = mpsc::unbounded_channel();
1597 let at_bidi = TerminalCell::new();
1598 let at_uni = TerminalCell::new();
1599 let rb = Arc::new(AtomicBool::new(false));
1600 let ru = Arc::new(AtomicBool::new(false));
1601 let shared = ConnShared::new(None);
1602 let opener = StreamOpener::from_parts(ctx, shared);
1603 let conn = Connection::from_parts(
1604 brx,
1605 urx,
1606 at_bidi.clone(),
1607 at_uni,
1608 Arc::clone(&rb),
1609 ru,
1610 opener,
1611 );
1612 (conn, btx, at_bidi, rb, crx)
1613 }
1614
1615 fn make_bidi_handoff() -> BidiHandoff<Bytes> {
1616 let (_btx, brx) = mpsc::channel(1);
1617 let (ictx, _icrx) = mpsc::unbounded_channel();
1618 BidiHandoff {
1619 send: SendHandoff {
1620 id: 0,
1621 status: TerminalCell::new(),
1622 cmd_tx: ictx.clone(),
1623 send_accounting: SendAccounting::new(None),
1624 cleanup: crate::driver::HandoffCleanup::new(0, false, ictx.clone()),
1625 },
1626 recv: RecvHandoff {
1627 id: 0,
1628 bytes: brx,
1629 terminal: TerminalCell::new(),
1630 resume: Arc::new(AtomicBool::new(false)),
1631 blocked: Arc::new(AtomicBool::new(false)),
1632 cmd_tx: ictx.clone(),
1633 cleanup: crate::driver::HandoffCleanup::new(0, true, ictx),
1634 },
1635 }
1636 }
1637
1638 #[test]
1639 fn poll_accept_bidi_delivers_then_maps_terminal() {
1640 let (mut conn, btx, at_bidi, rb, mut crx) = connection();
1641 let mut cx = noop_cx();
1642 btx.try_send(make_bidi_handoff()).unwrap();
1644 match conn.poll_accept_bidi(&mut cx) {
1645 Poll::Ready(Ok(_stream)) => {}
1646 _ => panic!("expected accepted stream"),
1647 }
1648 assert!(rb.load(Ordering::Relaxed));
1649 match crx.try_recv() {
1650 Ok(DriverCommand::AcceptBidiResume) => {}
1651 other => panic!("expected AcceptBidiResume, got {other:?}"),
1652 }
1653 at_bidi.set(Arc::new(ConnTerminal::Timeout));
1655 match conn.poll_accept_bidi(&mut cx) {
1656 Poll::Ready(Err(ConnectionErrorIncoming::Timeout)) => {}
1657 _ => panic!("expected Timeout"),
1658 }
1659 }
1660
1661 #[test]
1662 fn poll_accept_bidi_sealing_recheck_yields_queued_stream_before_terminal() {
1663 let (mut conn, btx, at_bidi, _rb, _crx) = connection();
1664 let mut cx = noop_cx();
1665 btx.try_send(make_bidi_handoff()).unwrap();
1668 at_bidi.set(Arc::new(ConnTerminal::Timeout));
1669 match conn.poll_accept_bidi(&mut cx) {
1670 Poll::Ready(Ok(_stream)) => {}
1671 _ => panic!("expected queued stream ahead of terminal"),
1672 }
1673 }
1674
1675 #[test]
1676 fn connection_drop_enqueues_connection_dropped() {
1677 let (conn, _btx, _at, _rb, mut crx) = connection();
1678 drop(conn);
1679 match crx.try_recv() {
1680 Ok(DriverCommand::ConnectionDropped) => {}
1681 other => panic!("expected ConnectionDropped, got {other:?}"),
1682 }
1683 }
1684}