Skip to main content

quiche_h3/
stream.rs

1//! Front end: streams and connection objects implementing the `h3::quic`
2//! traits (`H3Stream`, `H3SendStream`, `H3RecvStream`, `Connection`,
3//! `StreamOpener`) — design §6.
4//!
5//! Every method here is a **synchronous** `poll_*(cx)` that must never block:
6//! bytes/handoffs are read through non-blocking channel `poll_recv`, terminals
7//! through the race-free [`TerminalCell::poll`], and control commands are sent
8//! over the unbounded control channel (`send`, never `try_send`). Correctness
9//! rests on: exactly-once completion, first-writer-wins terminal cells, the
10//! §5.1 sealing edge (a single byte/accept recheck after observing a terminal),
11//! and producer-coalesced resume bits flipped only on the false→true edge.
12#![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
29/// Convert a worker `u64` stream id into the h3 [`StreamId`]. The worker only
30/// ever allocates/admits valid QUIC varint ids, so this never fails.
31fn stream_id(id: u64) -> StreamId {
32    StreamId::try_from(id).expect("worker allocates only valid QUIC stream ids")
33}
34
35/// Map a published connection terminal to the stream-level h3 error used when a
36/// stream operation is resolved by a connection close (§8.4).
37fn conn_terminal_stream_err(term: &Arc<ConnTerminal>) -> StreamErrorIncoming {
38    StreamErrorIncoming::ConnectionErrorIncoming {
39        connection_error: term.to_h3(),
40    }
41}
42
43// ===================================================================
44// Receive half
45// ===================================================================
46
47/// The `h3::quic::RecvStream` front-end half (§6). Drains the bounded byte
48/// channel first, then reads the out-of-band terminal; a producer-coalesced
49/// resume bit is flipped false→true when capacity is freed. `B` appears only in
50/// the `cmd_tx` type — the received `Buf` is always [`Bytes`].
51pub struct H3RecvStream<B: Buf> {
52    id: u64,
53    bytes: mpsc::Receiver<Bytes>,
54    terminal: TerminalCell<RecvEnd>,
55    resume: Arc<AtomicBool>,
56    /// Shared worker "parked on a full byte channel" flag (SF-2). Gates
57    /// `signal_resume` so a resume command+wake is only emitted when the worker
58    /// had genuinely blocked, not on every consumed chunk.
59    blocked: Arc<AtomicBool>,
60    cmd_tx: mpsc::UnboundedSender<DriverCommand<B>>,
61    /// A terminal has been observed and returned; `Drop` need not stop-send.
62    terminal_seen: bool,
63    /// A `StopSending` was already enqueued (explicitly or by a prior drop path).
64    stop_sent: bool,
65}
66
67impl<B: Buf> H3RecvStream<B> {
68    pub(crate) fn from_handoff(h: RecvHandoff<B>) -> Self {
69        // Conversion succeeded: this stream object now owns drop cleanup (§6.2),
70        // so disarm the handoff's fallback cleanup guard.
71        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    /// Freed one byte-channel slot: nudge the worker **only** if it had genuinely
85    /// parked on a full channel (SF-2). The outer `blocked.swap(false, AcqRel)`
86    /// observes-and-clears the worker's Release-published park flag, so exactly
87    /// one resume is emitted per park and a burst of frees after a single park
88    /// cannot emit more than one. The inner `resume` bit preserves the existing
89    /// producer-coalescing (§5.1) and pairs with the worker's clear in
90    /// `drain_resumed`. Correctness > perf: the worker's capacity re-check under
91    /// the same handshake guarantees it never parks with a slot already free, so
92    /// this gate can never drop a genuine resume — at worst a spurious wake is
93    /// elided when the worker never blocked.
94    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    /// Cache and map an observed terminal: `Fin` → `Ok(None)`, otherwise the
102    /// stream error (§8.4).
103    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        // 1. Drain buffered bytes first.
123        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                // Channel closed. The worker publishes the terminal *before*
130                // dropping the byte sender (§5.1 sealing), so it must be present;
131                // its absence is an adapter bug.
132                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                // Channel open but empty: consult the out-of-band terminal.
141                match self.terminal.poll(cx) {
142                    Poll::Ready(end) => {
143                        // Sealing-edge single recheck (M1): a byte may have raced
144                        // in just before the terminal was observed — yield it
145                        // first so accepted bytes are never truncated by EOF.
146                        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        // Normal local abandonment of an unread recv half → STOP_SENDING(0),
174        // unless it was already stopped or has already ended (§6.2).
175        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
185// ===================================================================
186// Send half
187// ===================================================================
188
189/// The `h3::quic::SendStream` front-end half (§6). Follows the h3 single-slot
190/// send contract: `send_data` stashes exactly one `WriteBuf`, `poll_ready`
191/// flushes it through the worker and reports the recorded completion once, and
192/// `poll_finish`/`reset` drive an idempotent finalization state machine.
193pub struct H3SendStream<B: Buf> {
194    id: u64,
195    status: TerminalCell<SendEnd>,
196    cmd_tx: mpsc::UnboundedSender<DriverCommand<B>>,
197    /// The single pending `WriteBuf` awaiting a `poll_ready` flush.
198    stash: Option<WriteBuf<B>>,
199    /// Reusable per-stream write-completion cell (SF-3): each `Send` reuses this
200    /// `Arc`-shared cell (a refcount bump) instead of allocating a `oneshot` per
201    /// chunk. Completion is generation-guarded (set-if-current-generation).
202    write_completion: WriteCompletion<SendEnd>,
203    /// Generation of the in-flight `Send` awaiting completion, if any. `None`
204    /// once the completion has been consumed (single-outstanding contract).
205    send_gen: Option<u64>,
206    /// Completion of the in-flight `Finish` (still a per-stream one-shot).
207    finish_completion: Option<oneshot::Receiver<Result<(), SendEnd>>>,
208    /// Retained `poll_finish` result, returned on every later poll.
209    finish_result: Option<Result<(), SendEnd>>,
210    /// A FIN/reset/terminal has been chosen: no further op may be enqueued.
211    finalized: bool,
212    /// A locally-issued `reset` terminal, visible immediately (the worker's
213    /// `status` cell is only set asynchronously afterward).
214    local_terminal: Option<SendEnd>,
215    /// Shared aggregate send-byte accounting for cap admission (SF-6, §12 S3).
216    /// `cap == None` (default) makes every reservation succeed immediately, so
217    /// admission is a no-op beyond two relaxed atomics per write.
218    send_accounting: Arc<SendAccounting>,
219}
220
221impl<B: Buf> H3SendStream<B> {
222    pub(crate) fn from_handoff(h: SendHandoff<B>) -> Self {
223        // Conversion succeeded: disarm the handoff fallback cleanup (§6.2).
224        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    /// The sticky send terminal visible right now: a local reset outranks the
241    /// worker's `status` cell, which is consulted race-free (register + recheck).
242    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    /// The sticky send terminal without a context (for `Drop`).
253    fn terminal_now_noctx(&self) -> Option<SendEnd> {
254        self.local_terminal.clone().or_else(|| self.status.get())
255    }
256
257    /// Resolve a failed/cancelled completion through the sticky terminal, or an
258    /// adapter-bug `InternalError` — never a bare cancel (§5.2 M3).
259    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    /// Test-only: the reusable write-completion cell's current generation,
267    /// which advances exactly once per `poll_ready` flush (SF-3 / SC-004).
268    #[cfg(test)]
269    pub(crate) fn write_generation(&self) -> u64 {
270        self.write_completion.generation()
271    }
272
273    /// Map a reusable-cell [`WriteOutcome`] (SF-3) to the `poll_ready` result,
274    /// preserving the old per-write `oneshot` semantics exactly: a delivered
275    /// `Result` is returned as-is; a `Cancelled` carrier (dropped without
276    /// completing) resolves through the sticky terminal — never a bare cancel.
277    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    /// Like [`sticky_or_internal`](Self::sticky_or_internal) but yields a
291    /// [`SendEnd`] so the failure can be **retained** (e.g. as `finish_result`),
292    /// ensuring a later poll returns the same error and never defaults to `Ok`.
293    /// The `Internal` fallback is modeled as `SendEnd::Conn(Internal)`, which
294    /// maps to the same `InternalError` as [`internal_stream_error`].
295    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        // (1) An in-flight write completion outranks everything: report it once.
304        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        // (2) A sticky terminal rejects idle or new work.
314        if let Some(end) = self.terminal_now(cx) {
315            return Poll::Ready(Err(end.to_h3()));
316        }
317        // (3) Nothing stashed → idle readiness fast path (§2.1).
318        let buf = match self.stash.take() {
319            None => return Poll::Ready(Ok(())),
320            Some(buf) => buf,
321        };
322        // (4) Flush the stash as exactly one `Send`. First reserve the write's
323        // bytes against the aggregate send-byte cap (SF-6). Under the default
324        // unlimited config this always succeeds; a finite cap parks the write
325        // (async backpressure) rather than dropping or reordering it (§12 S3).
326        let bytes = buf.remaining();
327        let permit = match self.send_accounting.try_reserve(bytes) {
328            Some(permit) => permit,
329            None => {
330                // Over the cap. Register our waker BEFORE a final re-check so a
331                // permit released between check and park is never missed (SF-2
332                // lost-wake discipline). Re-stash the buffer so a later poll
333                // retries this exact write in order (no data loss/reorder).
334                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        // Admitted: we are no longer parked, so drop any waker we registered on a
345        // prior over-cap poll (keeps the cap's waiter map bounded to genuinely
346        // parked senders; no-op / lock-free under the default unlimited config).
347        self.send_accounting.unregister_waiter(self.id);
348        // Admitted: reuse the per-stream cell — begin a fresh generation (clears
349        // any consumed prior slot — safe under the single-outstanding-write
350        // contract) and hand the worker a completer stamped with it, instead of
351        // allocating a `oneshot` per chunk (SF-3). The `permit` rides with the
352        // command and releases the reserved bytes on the op's completion/drop.
353        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            // The dropped command's completer fires `Cancelled` into the cell,
366            // but we resolve the failure directly via the sticky terminal here.
367            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            // The h3 contract requires a `poll_ready` flush between sends.
384            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        // Retained result: reuse it on every later poll (idempotent).
394        if let Some(result) = &self.finish_result {
395            return Poll::Ready(result.clone().map_err(|e| e.to_h3()));
396        }
397        // In-flight finish completion: poll before sticky status.
398        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                    // Persist the failure so a later poll cannot default to Ok
408                    // via the `finalized` branch below.
409                    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        // Finalized by a prior `reset` (or a channel-closed finish): return the
420        // sticky local terminal rather than enqueueing.
421        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        // First finish: consult sticky status first.
428        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        // Enqueue exactly one `Finish`.
434        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        // One reset only; never overwrite an already-finalized direction (§6.2).
471        if self.finalized {
472            return;
473        }
474        self.finalized = true;
475        // Don't mask a terminal the worker already published (peer STOP_SENDING
476        // or connection close): only install the local reset when none exists
477        // yet, so a conflicting poll reports the earlier peer code, not ours.
478        if self.status.get().is_none() {
479            self.local_terminal = Some(SendEnd::Reset {
480                error_code: reset_code,
481            });
482        }
483        // Does not drop an existing send/finish completion receiver (§5.3a).
484        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        // A dropped send half is no longer a parked admission: unregister its
498        // cap waiter so a cancelled stream cannot retain a waker until the next
499        // release (bounds the cap guard's waiter memory; no-op under the default
500        // unlimited config).
501        self.send_accounting.unregister_waiter(self.id);
502        // Graceful finish-on-drop for an unfinished send half (§6.2). A dropped
503        // completion receiver is harmless: the worker's `reply.send` just fails.
504        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
516// ===================================================================
517// Bidirectional stream
518// ===================================================================
519
520/// A bidirectional stream: an `H3SendStream` + `H3RecvStream` that also
521/// implements `BidiStream` so h3 can `split()` it into its two halves (§6).
522pub 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
578// ===================================================================
579// Stream opener
580// ===================================================================
581
582/// The `h3::quic::OpenStreams` front-end (§6.1). Stream-ID allocation is
583/// worker-owned; `poll_open_*` only submit an `OpenBidi`/`OpenUni` request
584/// through the close-admission submit helper and await the worker's handoff.
585/// A single-slot `pending_*` receiver makes repeated polls idempotent.
586pub 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    /// The terminal handed to a submitter the worker declined: the published
607    /// connection terminal if present, else an adapter-bug `InternalError`
608    /// (never a bare cancel, §5.2 M3).
609    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        // Fresh empty pending slots: an in-flight open belongs to the original
620        // clone (§6.1). This is the exact late-open race the M3 gate closes.
621        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            // Close-admission submit helper (§5.2 M3): a preset terminal or a
640            // failed send resolves *this* poll locally, never stores a doomed
641            // receiver.
642            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                // The worker dropped the reply without answering: fall back to
666                // the published terminal (else InternalError), never a cancel.
667                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
717// ===================================================================
718// Connection
719// ===================================================================
720
721/// The `h3::quic::Connection` front-end (§6): the two bounded accept receivers,
722/// their per-direction accept-terminal cells and resume bits, and an embedded
723/// `StreamOpener` it delegates `OpenStreams` to.
724pub 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    /// Freed one bidi accept-queue slot: flip the bidi accept-resume bit and
757    /// nudge the worker only on the false→true edge (§5.1 coalescing).
758    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    /// Freed one uni accept-queue slot: flip the uni accept-resume bit.
765    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                    // Sealing-edge single recheck (M1): an accepted stream may
817                    // have raced in just before the accept terminal.
818                    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        // Clone → fresh pending slots (§6.1).
859        self.opener.clone()
860    }
861}
862
863impl<B: Buf> Drop for Connection<B> {
864    fn drop(&mut self) {
865        // Enqueue before the accept receivers close, so the worker cleans up
866        // parked peer streams promptly (§6.2, iter9 finding 4).
867        let _ = self.opener.cmd_tx.send(DriverCommand::ConnectionDropped);
868    }
869}
870
871// ===================================================================
872// §11 compile-time trait gate
873// ===================================================================
874
875/// Static assertion that every `h3::quic` trait the bridge must provide is
876/// implemented by the front-end types (design §11). Never called; it fails to
877/// compile if any signature drifts from h3 0.0.8.
878fn _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    // ---- test plumbing ----
900
901    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    /// A waker that sets a shared flag when woken, so a test can assert the
917    /// SF-6 cap release actually re-scheduled a parked `poll_ready`.
918    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    /// Like [`send_half`] but with caller-supplied [`SendAccounting`] so a test
981    /// can drive the SF-6 cap-admission path and inspect residency.
982    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    /// The full wire size (DATA frame header + payload) a `wbuf(payload)` buffers,
1007    /// which is what SF-6 accounting reserves.
1008    fn wire_len(payload: &'static [u8]) -> usize {
1009        wbuf(payload).remaining()
1010    }
1011
1012    /// SF-6 (f): if the worker command channel is already closed when a reserved
1013    /// write is flushed, the dropped `Send` command carries the byte permit, so
1014    /// residency rolls back to its pre-attempt value. A failed enqueue must never
1015    /// leak reserved capacity against the aggregate cap (which would otherwise
1016    /// permanently shrink the usable send budget).
1017    #[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        // Close the worker command channel so the next enqueue fails.
1022        drop(crx);
1023        // A terminal must be published for the front end to resolve the failure.
1024        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    // ---- H3RecvStream ----
1041
1042    #[test]
1043    fn poll_data_delivers_buffered_bytes_before_terminal() {
1044        let (btx, terminal, _resume, _blocked, mut recv, _crx) = recv_channel();
1045        // A byte is buffered AND the terminal is set: bytes win (§5.1 sealing).
1046        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        // Now the queue is drained: the sticky terminal maps to clean EOF.
1054        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        // Fin → Ok(None)
1061        {
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        // Reset → StreamTerminated
1067        {
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        // Conn → ConnectionErrorIncoming
1078        {
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); // channel closed, no terminal published: adapter bug.
1094        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        // SF-2: consuming chunks while the worker was NOT parked must emit no
1106        // RecvResume — the wake is pure overhead if nobody is waiting.
1107        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        // Never blocked → resume bit untouched and no command emitted.
1115        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        // SF-2: when the worker had parked (blocked=true), the first freed slot
1125        // emits exactly one RecvResume and clears the park flag; further frees in
1126        // the same park cycle do not resend.
1127        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        // First drain observes blocked → one RecvResume, blocked cleared.
1133        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        // Second drain: still in the same (now-cleared) cycle → no duplicate.
1144        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    // ---- H3SendStream ----
1170
1171    #[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        // Idle readiness with no stash.
1188        assert!(matches!(send.poll_ready(&mut cx), Poll::Ready(Ok(()))));
1189        // Stash + poll_ready enqueues a Send and awaits its completion.
1190        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        // Worker records success; even if a terminal arrives afterward, the
1197        // recorded result is reported once.
1198        done.complete(Ok(()));
1199        status.set(SendEnd::Stopped { error_code: 7 });
1200        assert!(matches!(send.poll_ready(&mut cx), Poll::Ready(Ok(()))));
1201        // Subsequent idle poll now sees the sticky terminal.
1202        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    /// SF-3 / SC-004: K sequential `send_data`→`poll_ready` cycles reuse a single
1209    /// per-stream completion cell (no per-chunk `oneshot` allocation). Each flush
1210    /// advances the cell one generation and completes exactly once, in order.
1211    #[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            // Flush enqueues a Send and awaits its completion (worker not yet run).
1219            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            // Worker completes this generation; the front end reports it once.
1230            done.complete(Ok(()));
1231            assert!(matches!(send.poll_ready(&mut cx), Poll::Ready(Ok(()))));
1232            // Idle readiness afterward — completion consumed exactly once.
1233            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    /// SF-6: with the default (unlimited) accounting a `poll_ready` flush always
1243    /// admits immediately, reserving the write's bytes and releasing them when
1244    /// the worker completes it — residency is tracked but never bounds admission.
1245    #[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        // Admits immediately (unlimited) and awaits completion; the buffer's wire
1253        // size (frame header + payload) is resident.
1254        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        // Residency persists while the command is in flight (permit held here).
1264        assert_eq!(acct.resident(), hello);
1265        done.complete(Ok(()));
1266        // Dropping the permit at the completion chokepoint releases the bytes.
1267        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    /// SF-6: a finite aggregate cap parks a write that would exceed it (async
1273    /// backpressure — never dropped or reordered) and re-admits it, waking the
1274    /// parked task, once an outstanding permit releases. Two streams share one
1275    /// accounting to exercise the *aggregate* bound.
1276    #[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        // Cap sized so one "hello" exactly fills it; a second write must park.
1281        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        // Stream A fills the cap and awaits completion.
1286        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        // Stream B's write would exceed the cap → parks (Pending) and emits NO
1293        // command. Its waker is registered for the release.
1294        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        // A's write completes; dropping its command releases the permit and wakes
1307        // B's parked task.
1308        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        // B retries (as the runtime would after the wake) and now admits in order.
1316        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    /// SF-3: a `Send` still queued (unapplied) when the connection closes resolves
1327    /// its generation exactly once through the reusable completer — never a bare
1328    /// cancel, never a hang (gpt#7 lifecycle). Here the completer is dropped
1329    /// without completing (mirroring an unapplied command dropped at close), so
1330    /// `poll_ready` resolves through the sticky terminal.
1331    #[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        // Intercept and DROP the Send's completer without completing it, as the
1338        // driver would when a command is dropped unapplied at connection close.
1339        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); // fires Cancelled into the reusable cell
1344                    // A terminal is published (as on_conn_close would). poll_ready resolves
1345                    // the cancelled completion through the sticky terminal exactly once.
1346        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        // No second Finish is enqueued while the first is in flight.
1363        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        // Retained result on every later poll.
1368        assert!(matches!(send.poll_finish(&mut cx), Poll::Ready(Ok(()))));
1369        assert!(crx.try_recv().is_err());
1370    }
1371
1372    // Regression (review finding): a failed poll_finish (command channel closed
1373    // with no sticky terminal) must RETAIN its error; a later poll must not
1374    // default to Ok via the `finalized` branch.
1375    #[test]
1376    fn poll_finish_failure_is_retained_not_success() {
1377        let (_status, mut send, crx) = send_half(0);
1378        drop(crx); // close the control channel → the Finish send fails
1379        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        // The next poll must return the SAME error, never Ok.
1387        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        // Idempotent: a second reset enqueues nothing.
1404        send.reset(9);
1405        assert!(crx.try_recv().is_err(), "must not enqueue a second Reset");
1406        // poll_finish after reset returns the sticky local terminal.
1407        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(); // the Reset
1429        drop(send);
1430        assert!(
1431            crx.try_recv().is_err(),
1432            "finalized send must not finish on drop"
1433        );
1434    }
1435
1436    // Regression (final review, GPT): a materialized handoff dropped BEFORE the
1437    // front end converts it (open cancelled after the worker's reply.send(Ok)
1438    // succeeded, or a queued accepted handoff dropped when Connection drops)
1439    // must enqueue direction-aware cleanup so the stream is not leaked (§6.2).
1440    #[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); // unconsumed → the guard fires
1454        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        // recv_channel()/send_half() convert via from_handoff → the guard is
1480        // disarmed, so conversion enqueues nothing; only the STREAM object's own
1481        // Drop later enqueues cleanup.
1482        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    // ---- StreamOpener ----
1495
1496    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        // No doomed OpenBidi was enqueued.
1526        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        // Submit stores a pending receiver in the original.
1537        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        // The clone starts empty (§6.1).
1541        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        // Fabricate a worker handoff.
1556        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    // ---- Connection ----
1585
1586    #[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        // A queued accepted stream is delivered, flipping the accept-resume bit.
1643        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        // Empty queue + accept terminal → mapped connection error.
1654        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        // Both a queued stream AND the accept terminal are present: the stream
1666        // must win (M1 sealing-edge recheck).
1667        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}