Skip to main content

dolang_rpc/
client.rs

1//! The calling side of a bound RPC session.
2//!
3//! [`Client::call`] sends a request and returns a [`Call`] future that
4//! resolves to the peer's response. Multiple calls may be outstanding
5//! concurrently on one [`Client`]; each is dispatched and matched to its
6//! response independently.
7
8use std::{
9    collections::{HashMap, HashSet, VecDeque},
10    future::Future,
11    mem,
12    pin::Pin,
13    sync::{Arc, Mutex, Weak},
14    task::{Context, Poll},
15};
16
17#[cfg(windows)]
18use std::{any::TypeId, io};
19
20use futures::{FutureExt, future::BoxFuture};
21use tokio::sync::{mpsc, oneshot};
22
23#[cfg(windows)]
24use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
25
26#[cfg(windows)]
27use windows_sys::Win32::System::Threading::GetProcessId;
28
29#[cfg(unix)]
30use crate::session::SessionHandles;
31use crate::{
32    Error, Limits, Protocol,
33    fragment::{
34        self, AbortOutcome, Event, Flags, FragmentHeader, Kind, Message, Reassembler, Scheduler,
35        Trailer,
36    },
37    serde::{decode_payload, encode_payload},
38    session::{Ledger, ReleaseSink, Session, SessionFrame},
39    trailer::{RecvShared, SendShared, TrailerRecv, TrailerSend},
40    transport::{self, EncodeHandles, Receiver, Sender},
41    window::{ControlSink, PayloadBudget, PayloadCharge, SessionWindow},
42};
43#[cfg(windows)]
44use crate::{handle::TakeHandle, session::Inner as OpaqueInner};
45
46/// A negotiated client endpoint that has not yet been bound to a [`Protocol`].
47///
48/// Inspect its negotiated application protocol, then consume it with
49/// [`bind`](Unbound::bind) to obtain a [`Client`].
50pub use crate::unbound::UnboundClient as Unbound;
51
52type Responder<R> = oneshot::Sender<Result<CallResult<R>, Error>>;
53
54/// The registry of calls awaiting a response, which is poisoned once the
55/// session has authoritatively failed.
56///
57/// The reader task is the only thing that can ever deliver a response, so a
58/// call registered after it has gone would wait forever. Poisoning is what
59/// makes that impossible: the same lock that fails the calls already waiting
60/// records why, and every later registration is refused with that error
61/// instead of being parked on a response nobody will send.
62enum Pending<R> {
63    Live(HashMap<u64, Responder<R>>),
64    Failed(Error),
65}
66
67impl<R> Pending<R> {
68    fn new() -> Self {
69        Self::Live(HashMap::new())
70    }
71
72    /// Registers a call, or settles it immediately with the error the session
73    /// failed with. Returns whether it was registered.
74    #[must_use]
75    fn register(&mut self, id: u64, tx: Responder<R>) -> bool {
76        match self {
77            Self::Live(calls) => {
78                calls.insert(id, tx);
79                true
80            }
81            Self::Failed(error) => {
82                let _ = tx.send(Err(error.copy()));
83                false
84            }
85        }
86    }
87
88    fn remove(&mut self, id: u64) -> Option<Responder<R>> {
89        match self {
90            Self::Live(calls) => calls.remove(&id),
91            Self::Failed(_) => None,
92        }
93    }
94
95    fn contains(&self, id: u64) -> bool {
96        match self {
97            Self::Live(calls) => calls.contains_key(&id),
98            Self::Failed(_) => false,
99        }
100    }
101}
102
103/// State the reader and writer tasks share.
104///
105/// Both hold this strongly, which is safe precisely because nothing in it can
106/// keep an API handle alive: everything a [`Client`] handle owns — pending
107/// calls, the id counter, the task handles, and the only strong sender into
108/// the writer — stays in [`Inner`], so dropping the last handle still closes
109/// the writer's channel and still shuts the connection down.
110struct Shared {
111    session: Arc<Session>,
112    /// Send-side trailer credit shared by every outgoing trailer on this
113    /// connection. Bounds what the peer must buffer for us in aggregate.
114    trailer_session: Arc<SessionWindow>,
115    /// Send-side payload quota shared by every outgoing request. Bounds the
116    /// postcard bytes the peer must hold for us across all live calls, and is
117    /// charged in full when a request is admitted to the scheduler.
118    ///
119    /// Kept apart from `trailer_session` on purpose; see [`crate::window`].
120    payload_budget: Arc<PayloadBudget>,
121    /// Calls whose request reached the scheduler and whose terminal message
122    /// has not yet arrived.
123    ///
124    /// Each task asks it a different question: the writer bounds admissions
125    /// by its size, and the reader treats it as the set of ids the peer is
126    /// entitled to respond to. Membership starts in `SendDriver::admit_request`
127    /// and ends wherever the terminal message is observed, which for
128    /// everything but a locally cancelled request is the reader.
129    active_calls: Mutex<HashSet<u64>>,
130    /// The route trailers and payload charges use to credit and discard
131    /// themselves. Weak, since a `TrailerRecv` or a held payload credit can
132    /// outlive the session and must not keep the writer alive just to say it
133    /// is going away.
134    sink: Arc<dyn ControlSink>,
135    #[cfg(windows)]
136    handle_escrow: Mutex<HashMap<u64, Vec<OwnedHandle>>>,
137    #[cfg(target_os = "macos")]
138    fd_escrow: Mutex<crate::escrow::FdEscrow>,
139    limits: Limits,
140}
141
142impl Shared {
143    /// Maximum handle attachments one message may carry.
144    fn max_handles(&self) -> usize {
145        // A transport configured to attach no handles to a fragment can
146        // carry none at all.
147        #[cfg(unix)]
148        if self.limits.max_handles_per_fragment == 0 {
149            return 0;
150        }
151        self.limits.max_handles_per_message
152    }
153
154    /// Finishes handle encoding for message `id`, taking custody of whatever
155    /// this platform must keep alive once the message is on the wire.
156    ///
157    /// On macOS that is the file descriptors themselves, escrowed until the
158    /// peer acknowledges receipt. Every other unix passes them with the
159    /// fragment and is done with them.
160    #[cfg(unix)]
161    fn finish_handles(&self, id: u64, handles: EncodeHandles) -> transport::OutgoingHandles {
162        let handles = handles.finish();
163        #[cfg(target_os = "macos")]
164        if handles.needs_ack() {
165            self.fd_escrow.lock().unwrap().register(id);
166        }
167        #[cfg(not(target_os = "macos"))]
168        let _ = id;
169        handles
170    }
171
172    /// Finishes handle encoding for message `id`, escrowing the originals of
173    /// the handles duplicated into the peer until the call is settled.
174    #[cfg(windows)]
175    fn finish_handles(&self, id: u64, handles: EncodeHandles) -> transport::OutgoingHandles {
176        let (handles, escrow) = handles.finish();
177        if !escrow.is_empty() {
178            self.handle_escrow.lock().unwrap().insert(id, escrow);
179        }
180        handles
181    }
182
183    /// Records the file descriptors for `id` that just reached the wire.
184    #[cfg(target_os = "macos")]
185    fn escrow_sent(&self, id: u64, fds: Vec<std::os::fd::OwnedFd>, done: bool) {
186        self.fd_escrow.lock().unwrap().sent(id, fds, done);
187    }
188
189    /// Forgets the escrow for a request that will never reach the wire.
190    fn discard_unsent_escrow(&self, id: u64) {
191        #[cfg(target_os = "macos")]
192        self.fd_escrow.lock().unwrap().discard_unsent(id);
193        #[cfg(not(target_os = "macos"))]
194        let _ = id;
195    }
196
197    /// Releases the escrow an `Ack` names, returning false when there is
198    /// none — which is every `Ack` on a platform that escrows nothing.
199    fn release_escrow(&self, id: u64) -> bool {
200        #[cfg(target_os = "macos")]
201        return self.fd_escrow.lock().unwrap().release(id);
202        #[cfg(not(target_os = "macos"))]
203        {
204            let _ = id;
205            false
206        }
207    }
208
209    /// Drops the handles escrowed for `id`. A no-op where the platform keeps
210    /// nothing back for the peer to duplicate.
211    fn drop_escrowed_handles(&self, id: u64) {
212        #[cfg(windows)]
213        self.handle_escrow.lock().unwrap().remove(&id);
214        #[cfg(not(windows))]
215        let _ = id;
216    }
217
218    /// Decodes a message payload, taking custody of every handle and opaque
219    /// reference it carries.
220    #[cfg(unix)]
221    fn decode<T: ::serde::de::DeserializeOwned>(
222        &self,
223        payload: &[u8],
224        handles: transport::ReceivedHandles,
225    ) -> Result<T, Error> {
226        decode_payload(
227            payload,
228            &mut SessionHandles {
229                inner: handles,
230                session: &self.session,
231            },
232        )
233    }
234
235    /// Decodes a message payload, taking custody of every handle and opaque
236    /// reference it carries. Windows handles arrive already duplicated into
237    /// this process, named by value in the payload rather than attached to
238    /// the fragment, so `handles` is empty.
239    #[cfg(windows)]
240    fn decode<T: ::serde::de::DeserializeOwned>(
241        &self,
242        payload: &[u8],
243        _handles: transport::ReceivedHandles,
244    ) -> Result<T, Error> {
245        decode_payload(
246            payload,
247            &mut DecodeHandles::new(self.max_handles(), &self.session),
248        )
249    }
250
251    /// Retires call `id` now that its terminal message has settled it: the
252    /// call slot goes back, and so does anything escrowed for its request.
253    fn finish_call(&self, id: u64) {
254        self.drop_escrowed_handles(id);
255        self.active_calls.lock().unwrap().remove(&id);
256    }
257}
258
259#[cfg(windows)]
260struct DecodeHandles<'a> {
261    consumed: HashSet<usize>,
262    count: usize,
263    max_handles: usize,
264    session: &'a Arc<Session>,
265}
266
267#[cfg(windows)]
268impl<'a> DecodeHandles<'a> {
269    fn new(max_handles: usize, session: &'a Arc<Session>) -> Self {
270        Self {
271            consumed: HashSet::new(),
272            count: 0,
273            max_handles,
274            session,
275        }
276    }
277}
278
279#[cfg(windows)]
280impl TakeHandle for DecodeHandles<'_> {
281    fn take_handle(&mut self, value: usize) -> io::Result<OwnedHandle> {
282        if !self.consumed.insert(value) {
283            return Err(io::Error::new(
284                io::ErrorKind::InvalidData,
285                "handle value was already consumed",
286            ));
287        }
288        self.count += 1;
289        // SAFETY: the trusted server created this value in our process with
290        // DuplicateHandle before transmitting it.
291        Ok(unsafe { OwnedHandle::from_raw_handle(value as _) })
292    }
293
294    fn finish(&mut self) -> io::Result<()> {
295        if self.count > self.max_handles {
296            return Err(io::Error::new(
297                io::ErrorKind::InvalidData,
298                "message contains too many handle attachments",
299            ));
300        }
301        Ok(())
302    }
303
304    fn take_gift(&mut self, owner: u8, id: u64) -> io::Result<OpaqueInner> {
305        self.session
306            .take_gift(owner, id)
307            .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid opaque reference"))
308    }
309
310    fn take_cite(&mut self, owner: u8, id: u64, marker: TypeId) -> io::Result<OpaqueInner> {
311        self.session
312            .take_cite(owner, id, marker)
313            .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid opaque reference"))
314    }
315}
316
317/// `(id, response receiver, cancel_sent)`, returned by `Client::begin`.
318type BeginResult<P> = (
319    u64,
320    oneshot::Receiver<Result<CallResult<<P as Protocol>::Response>, Error>>,
321    bool,
322);
323
324enum Outgoing<Q> {
325    Request {
326        id: u64,
327        value: Q,
328        trailer: Trailer,
329    },
330    Cancel {
331        id: u64,
332    },
333    /// We stopped reading a response trailer (it arrived unwanted) and want
334    /// to tell the peer to stop sending it. Always results in a wire
335    /// `Kind::Discard` fragment — this connection never has an active
336    /// outgoing send under a response id to abort locally instead.
337    DiscardTrailer {
338        id: u64,
339    },
340    /// A wire `Kind::Discard` fragment arrived, telling us the peer no
341    /// longer wants our request trailer. Applied to our own active send;
342    /// never re-sent to the peer.
343    PeerDiscarded {
344        id: u64,
345    },
346    Ack {
347        id: u64,
348    },
349    /// Drops `count` of this endpoint's references to the peer's opaque `id`.
350    Release {
351        id: u64,
352        count: u32,
353    },
354    /// We retired `count` bytes of the response trailer on `id` and are
355    /// returning that much credit. Always results in a wire `Kind::Credit`.
356    Credit {
357        id: u64,
358        count: u32,
359    },
360    /// A call released its request payload and is returning `count` bytes of
361    /// quota. Always results in a wire `Kind::PayloadCredit`, which names no
362    /// message: several of these coalesce into one fragment.
363    PayloadCredit {
364        count: u32,
365    },
366    /// A call ended, freeing a slot. The reader has already dropped the id
367    /// from [`ActiveCalls`] itself — it has to, so that a second `Response`
368    /// for the same id is rejected — so this carries nothing and exists only
369    /// to wake the writer to promote whatever was waiting on that slot.
370    Terminal,
371}
372
373impl<Q: Send + 'static> ReleaseSink for mpsc::WeakUnboundedSender<Outgoing<Q>> {
374    fn release(&self, id: u64, count: u32) {
375        // Called from `Drop`, so a departed channel is not an error: the
376        // writer is already gone and the peer's table dies with the session.
377        if let Some(outgoing) = self.upgrade() {
378            let _ = outgoing.send(Outgoing::Release { id, count });
379        }
380    }
381}
382
383impl<Q: Send + 'static> ControlSink for mpsc::WeakUnboundedSender<Outgoing<Q>> {
384    fn credit(&self, id: u64, count: u32) {
385        if let Some(outgoing) = self.upgrade() {
386            let _ = outgoing.send(Outgoing::Credit { id, count });
387        }
388    }
389
390    fn payload_credit(&self, count: u32) {
391        // Reached from `PayloadCharge::drop`, which runs on every path a call
392        // can end on — including ones where the session is already tearing
393        // down, where there is no one left to credit.
394        if let Some(outgoing) = self.upgrade() {
395            let _ = outgoing.send(Outgoing::PayloadCredit { count });
396        }
397    }
398
399    fn discard(&self, id: u64) {
400        // Reached from `TrailerRecv::drop`, so a departed channel just means
401        // the connection is already gone and the peer has nothing to stop.
402        if let Some(outgoing) = self.upgrade() {
403            let _ = outgoing.send(Outgoing::DiscardTrailer { id });
404        }
405    }
406}
407
408struct Inner<P: Protocol> {
409    // Holding a clone of this sender represents the ability to still get a
410    // message into the writer, so closing the channel — clearing this to
411    // `None` — is itself the writer's shutdown signal (see `SendDriver::run`):
412    // no separate oneshot needed. This is the only strong sender; the
413    // session's release sink holds a weak one so that it cannot keep the
414    // channel open past this point.
415    outgoing: Mutex<Option<mpsc::UnboundedSender<Outgoing<P::Request>>>>,
416    pending: Mutex<Pending<P::Response>>,
417    next_id: Mutex<u64>,
418    reader_shutdown: Mutex<Option<oneshot::Sender<()>>>,
419    tasks: Mutex<Option<Tasks>>,
420    requires_full_close: bool,
421    shared: Arc<Shared>,
422    #[cfg(windows)]
423    _peer_process: Option<OwnedHandle>,
424}
425
426/// Drives the send half of the connection: admits queued items into the
427/// fragment scheduler and advances the scheduler onto the transport.
428struct SendDriver<P: Protocol> {
429    transport: transport::AnySender,
430    outgoing: mpsc::UnboundedReceiver<Outgoing<P::Request>>,
431    /// Weak: the API handles decide when the session ends, and a writer that
432    /// held them alive would wait forever on a channel it kept open itself.
433    inner: Weak<Inner<P>>,
434    shared: Arc<Shared>,
435    scheduler: Scheduler,
436    /// Requests held back because `Shared::active_calls` is at the limit,
437    /// promoted in arrival order as slots free up.
438    waiting: VecDeque<Outgoing<P::Request>>,
439}
440
441/// Drives the receive half of the connection: reassembles inbound fragments
442/// and settles the calls they answer.
443struct RecvDriver<P: Protocol> {
444    transport: transport::AnyReceiver,
445    /// Weak, as on the writer.
446    inner: Weak<Inner<P>>,
447    shared: Arc<Shared>,
448}
449
450/// A driver task's completion, awaitable any number of times.
451///
452/// A `JoinHandle` can only be awaited once, and it is not `Clone`: `close`,
453/// `abort` and `Drop` all need to observe the same task ending, possibly from
454/// several client clones at once. `Shared` gives every awaiter its own handle
455/// on one join, and hands the polling on to another awaiter if the one
456/// currently driving it is dropped — which is exactly what happens when
457/// `abort` on one clone interrupts a `close` waiting on another.
458type Done = futures::future::Shared<BoxFuture<'static, ()>>;
459
460fn done(handle: tokio::task::JoinHandle<impl Send + 'static>) -> Done {
461    handle.map(|_| ()).boxed().shared()
462}
463
464#[derive(Clone)]
465struct Tasks {
466    writer_done: Done,
467    reader_done: Done,
468}
469
470impl Tasks {
471    async fn writer(&self) {
472        self.writer_done.clone().await;
473    }
474
475    async fn reader(&self) {
476        self.reader_done.clone().await;
477    }
478
479    async fn join(&self) {
480        tokio::join!(self.writer(), self.reader());
481    }
482}
483
484impl<P: Protocol> Drop for Inner<P> {
485    fn drop(&mut self) {
486        // Close the writer's channel first — see the comment on `outgoing`.
487        self.outgoing.lock().unwrap().take();
488        if let Some(shutdown) = self.reader_shutdown.get_mut().unwrap().take() {
489            let _ = shutdown.send(());
490        }
491        self.fail(Error::ConnectionClosed);
492    }
493}
494
495impl<P: Protocol> Inner<P> {
496    /// Best-effort send: silently dropped if the writer's channel has
497    /// already been closed.
498    fn send(&self, message: Outgoing<P::Request>) {
499        if let Some(sender) = self.outgoing.lock().unwrap().as_ref() {
500            let _ = sender.send(message);
501        }
502    }
503
504    fn complete(&self, id: u64, result: Result<CallResult<P::Response>, Error>) {
505        if let Some(tx) = self.pending.lock().unwrap().remove(id) {
506            let _ = tx.send(result);
507        }
508    }
509
510    /// Fails every waiting call and poisons the registry against later ones.
511    ///
512    /// The first error wins: it is the one that says why the session actually
513    /// ended, where anything after it is the teardown that followed.
514    fn fail(&self, error: Error) {
515        let waiting = {
516            let mut pending = self.pending.lock().unwrap();
517            match mem::replace(&mut *pending, Pending::Failed(error.copy())) {
518                Pending::Live(calls) => calls,
519                already @ Pending::Failed(_) => {
520                    *pending = already;
521                    return;
522                }
523            }
524        };
525        for (_, tx) in waiting {
526            let _ = tx.send(Err(error.copy()));
527        }
528    }
529}
530
531/// RPC client handle.
532pub struct Client<P: Protocol> {
533    inner: Arc<Inner<P>>,
534}
535
536impl<P: Protocol> Clone for Client<P> {
537    fn clone(&self) -> Self {
538        Self {
539            inner: self.inner.clone(),
540        }
541    }
542}
543
544impl<P: Protocol> Client<P> {
545    /// Returns whether both clients refer to the same RPC session.
546    pub fn is_same_session(&self, other: &Self) -> bool {
547        Arc::ptr_eq(&self.inner, &other.inner)
548    }
549
550    /// Builds a `Client` from an already-negotiated transport. Only reachable
551    /// via [`Unbound::bind`] — `Client` has
552    /// no public constructors of its own, so every `Client<P>` has already
553    /// completed `fragment::negotiate` by the time it exists.
554    pub(crate) fn from_transport(
555        sender: transport::AnySender,
556        receiver: transport::AnyReceiver,
557        limits: Limits,
558        #[cfg(windows)] peer_process: Option<OwnedHandle>,
559    ) -> Self {
560        let requires_full_close = sender.requires_full_close();
561        let (outgoing, outgoing_rx) = mpsc::unbounded_channel();
562        let session = Session::new(Box::new(outgoing.downgrade()));
563        let shared = Arc::new(Shared {
564            session,
565            trailer_session: Arc::new(SessionWindow::new(limits.trailer_session_window)),
566            payload_budget: Arc::new(PayloadBudget::new(limits.max_outstanding_payload)),
567            active_calls: Default::default(),
568            sink: Arc::new(outgoing.downgrade()),
569            #[cfg(windows)]
570            handle_escrow: Mutex::new(HashMap::new()),
571            #[cfg(target_os = "macos")]
572            fd_escrow: Mutex::new(Default::default()),
573            limits,
574        });
575        let inner = Arc::new(Inner {
576            outgoing: Mutex::new(Some(outgoing)),
577            pending: Mutex::new(Pending::new()),
578            next_id: Mutex::new(0),
579            reader_shutdown: Mutex::new(None),
580            tasks: Mutex::new(None),
581            requires_full_close,
582            shared: shared.clone(),
583            #[cfg(windows)]
584            _peer_process: peer_process,
585        });
586        let (reader_shutdown, reader_stop) = oneshot::channel();
587        let writer = tokio::spawn(
588            SendDriver {
589                transport: sender,
590                outgoing: outgoing_rx,
591                inner: Arc::downgrade(&inner),
592                shared: shared.clone(),
593                scheduler: Scheduler::new(&limits, shared.payload_budget.clone()),
594                waiting: VecDeque::new(),
595            }
596            .run(),
597        );
598        let reader = tokio::spawn(
599            RecvDriver {
600                transport: receiver,
601                inner: Arc::downgrade(&inner),
602                shared,
603            }
604            .run(reader_stop),
605        );
606        *inner.reader_shutdown.lock().unwrap() = Some(reader_shutdown);
607        *inner.tasks.lock().unwrap() = Some(Tasks {
608            writer_done: done(writer),
609            reader_done: done(reader),
610        });
611        Self { inner }
612    }
613
614    /// Gracefully closes the session.
615    ///
616    /// This prevents new calls from being sent and completes all pending calls
617    /// with [`Error::ConnectionClosed`]. It then drains committed writes,
618    /// closes the outgoing transport, and waits for the peer to close its
619    /// outgoing transport. It affects every clone of this client handle.
620    ///
621    /// This operation has no timeout. A peer that does not close its transport
622    /// can make it wait indefinitely; callers that need a bound should apply a
623    /// timeout and use [`Client::abort`] if the peer does not cooperate.
624    /// Windows named pipes do not support half-close, so after draining the
625    /// writer this closes the shared pipe instead of waiting for peer EOF.
626    pub async fn close(self) {
627        let tasks = self.inner.tasks.lock().unwrap().clone();
628        // Close the writer's channel first — see the comment on `outgoing`.
629        self.inner.outgoing.lock().unwrap().take();
630        self.inner.fail(Error::ConnectionClosed);
631        if let Some(tasks) = tasks {
632            // The reader must remain alive while the writer drains because it
633            // may still deliver the credit needed by a committed write. Once
634            // the writer exits, dropping its transport tells the peer that no
635            // more input is coming; natural EOF from the peer then ends the
636            // reader when the transport supports half-close.
637            tasks.writer().await;
638            if self.inner.requires_full_close
639                && let Some(shutdown) = self.inner.reader_shutdown.lock().unwrap().take()
640            {
641                // Windows named pipes have no write-side half-close. Both
642                // drivers share one pipe handle, so after the writer drains
643                // the reader must release its half to make the peer observe
644                // EOF. Keeping it alive through the drain still preserves
645                // flow-control progress and committed writes.
646                let _ = shutdown.send(());
647            }
648            tasks.reader().await;
649        }
650        // Keep the sender alive until after natural EOF. Besides documenting
651        // that intent in the ownership, this lets `abort` on another clone
652        // interrupt a `close` that is waiting for an uncooperative peer.
653        self.inner.reader_shutdown.lock().unwrap().take();
654    }
655
656    /// Abruptly closes the session.
657    ///
658    /// This prevents new calls from being sent, completes all pending calls
659    /// with [`Error::ConnectionClosed`], and stops the reader without waiting
660    /// for the peer to close its transport. Committed writes are allowed to
661    /// finish their current fragments before the writer exits. It affects
662    /// every clone of this client handle.
663    pub async fn abort(self) {
664        let tasks = self.inner.tasks.lock().unwrap().clone();
665        // Close the writer's channel before the reader, matching `Drop` and
666        // preserving the writer's committed-fragment drain.
667        self.inner.outgoing.lock().unwrap().take();
668        self.inner.fail(Error::ConnectionClosed);
669        if let Some(shutdown) = self.inner.reader_shutdown.lock().unwrap().take() {
670            let _ = shutdown.send(());
671        }
672        if let Some(tasks) = tasks {
673            tasks.join().await;
674        }
675    }
676
677    /// Issue a call request.
678    pub fn call(&self, request: P::Request) -> Call<P> {
679        let ((id, rx, cancel_sent), ()) = self.begin(|id| {
680            (
681                Outgoing::Request {
682                    id,
683                    value: request,
684                    trailer: Trailer::None,
685                },
686                (),
687            )
688        });
689        Call {
690            id,
691            rx,
692            inner: self.inner.clone(),
693            cancel_sent,
694        }
695    }
696
697    /// Issue a call request with a byte trailer.
698    ///
699    /// Write the trailer through the returned [`TrailerSend`], then call
700    /// [`TrailerSend::finish`] to obtain the
701    /// [`Call`]. Dropping it sender without finishing aborts the trailer and
702    /// cancels the partially sent request.
703    pub fn call_with_trailer(&self, request: P::Request) -> TrailerSend<Call<P>> {
704        let ((id, rx, cancel_sent), shared) = self.begin(|id| {
705            let shared = SendShared::new(
706                Kind::Request,
707                id,
708                &self.inner.shared.limits,
709                self.inner.shared.trailer_session.clone(),
710            );
711            (
712                Outgoing::Request {
713                    id,
714                    value: request,
715                    trailer: Trailer::Stream(shared.clone()),
716                },
717                shared,
718            )
719        });
720        if cancel_sent {
721            SendShared::discard(&shared);
722        }
723        TrailerSend::new(
724            shared,
725            Call {
726                id,
727                rx,
728                inner: self.inner.clone(),
729                cancel_sent,
730            },
731        )
732    }
733
734    /// Shared id-allocation/pending-registration logic for `call` and
735    /// `call_with_trailer`. `build` constructs the outgoing message once the
736    /// id is known. Returns the id, the response receiver, and whether a
737    /// cancel has effectively already been sent (nothing left to cancel).
738    fn begin<T>(
739        &self,
740        build: impl FnOnce(u64) -> (Outgoing<P::Request>, T),
741    ) -> (BeginResult<P>, T) {
742        let (tx, rx) = oneshot::channel();
743        let id = {
744            let mut next = self.inner.next_id.lock().unwrap();
745            let id = *next;
746            *next = id.checked_add(1).expect("request identifiers exhausted");
747            id
748        };
749        let (message, value) = build(id);
750        // Registration first, and under the lock that poisoning takes: a
751        // session that has already failed refuses the call here rather than
752        // letting it queue a request that no surviving reader will ever
753        // answer.
754        let registered = self.inner.pending.lock().unwrap().register(id, tx);
755        let queued = registered
756            && self
757                .inner
758                .outgoing
759                .lock()
760                .unwrap()
761                .as_ref()
762                .is_some_and(|sender| sender.send(message).is_ok());
763        if registered && !queued {
764            self.inner.complete(id, Err(Error::ConnectionClosed));
765        }
766        ((id, rx, !queued), value)
767    }
768}
769
770#[cfg(windows)]
771pub(crate) fn validate_peer_process(
772    peer_process: &OwnedHandle,
773    pipe_peer_pid: u32,
774) -> io::Result<()> {
775    let process_pid = unsafe { GetProcessId(peer_process.as_raw_handle() as _) };
776    if process_pid == 0 {
777        return Err(io::Error::last_os_error());
778    }
779    if process_pid != pipe_peer_pid {
780        return Err(io::Error::new(
781            io::ErrorKind::PermissionDenied,
782            "named-pipe peer does not match the expected process",
783        ));
784    }
785    Ok(())
786}
787
788/// A completed call's response and possible trailer.
789///
790/// Use [`into_response`](Self::into_response) when the trailer is not needed,
791/// or [`into_response_trailer`](Self::into_response_trailer) to retain it —
792/// or [`into_response_trailer_manual_credit`](Self::into_response_trailer_manual_credit)
793/// to retain it and take charge of returning its credit.
794pub struct CallResult<R> {
795    response: R,
796    trailer: Option<TrailerRecv>,
797    /// This response's share of the payload quota, released when this value
798    /// is decomposed or dropped — unless the caller took it out with
799    /// [`take_payload_credit`](Self::take_payload_credit) first.
800    charge: Option<PayloadCharge>,
801}
802
803/// A held share of the session payload quota.
804///
805/// Obtained from [`CallResult::take_payload_credit`] by a caller that wants
806/// the quota released later than the `CallResult` it came from — typically
807/// because the deserialized response is potentially large.  Holding it
808/// prevents it being returned to the server's pool, limited by
809/// [`Builder::max_outstanding_payload()`](crate::Builder::max_outstanding_payload())
810pub struct PayloadCredit(Option<PayloadCharge>);
811
812impl PayloadCredit {
813    /// Returns the quota now. Identical to dropping this value, and offered
814    /// only so the intent can be stated where it happens.
815    pub fn release(self) {
816        drop(self.0);
817    }
818}
819
820impl<R> CallResult<R> {
821    /// Takes charge of releasing this call response's payload quota.
822    ///
823    /// Calling this more than once yields a token that releases nothing.
824    pub fn take_payload_credit(&mut self) -> PayloadCredit {
825        PayloadCredit(self.charge.take())
826    }
827
828    /// Discards any response trailer and returns just the response.
829    pub fn into_response(self) -> R {
830        self.response
831    }
832
833    /// Decomposes into the response and its readable trailer, if present.
834    pub fn into_response_trailer(self) -> (R, Option<TrailerRecv>) {
835        (self.response, self.trailer)
836    }
837
838    /// Decomposes into the response and its trailer in manual-credit mode.
839    ///
840    /// The consumer then owes the server an explicit
841    /// [`TrailerRecv::release`](crate::trailer::TrailerRecv::release) for
842    /// every chunk it finishes using, instead of credit being returned on
843    /// read.
844    pub fn into_response_trailer_manual_credit(self) -> (R, Option<TrailerRecv>) {
845        let mut trailer = self.trailer;
846        if let Some(trailer) = trailer.as_mut() {
847            trailer.set_manual_credit();
848        }
849        (self.response, trailer)
850    }
851}
852
853/// An in-progress RPC call.
854///
855/// Await this future to receive the response and its optional trailer, or an
856/// [`Error`]. Dropping it before completion sends best-effort cancellation to
857/// the peer.
858pub struct Call<P: Protocol> {
859    id: u64,
860    rx: oneshot::Receiver<Result<CallResult<P::Response>, Error>>,
861    inner: Arc<Inner<P>>,
862    cancel_sent: bool,
863}
864
865impl<P: Protocol> Call<P> {
866    /// Requests best-effort cancellation and leaves the call awaitable.
867    ///
868    /// Idempotent. A call that races with cancellation may still
869    /// complete successfully.
870    pub fn cancel(&mut self) {
871        if !self.cancel_sent {
872            self.cancel_sent = true;
873            self.inner.send(Outgoing::Cancel { id: self.id });
874        }
875    }
876}
877
878impl<P: Protocol> Future for Call<P> {
879    type Output = Result<CallResult<P::Response>, Error>;
880
881    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
882        match Pin::new(&mut self.rx).poll(cx) {
883            Poll::Ready(Ok(Ok(result))) => Poll::Ready(Ok(result)),
884            Poll::Ready(Ok(Err(e))) => Poll::Ready(Err(e)),
885            Poll::Ready(Err(_)) => Poll::Ready(Err(Error::ConnectionClosed)),
886            Poll::Pending => Poll::Pending,
887        }
888    }
889}
890
891impl<P: Protocol> Drop for Call<P> {
892    fn drop(&mut self) {
893        if self.inner.pending.lock().unwrap().remove(self.id).is_some() {
894            self.cancel();
895        }
896    }
897}
898
899impl<P: Protocol> SendDriver<P> {
900    /// Best-effort completion of a pending call with an error; a no-op if
901    /// the session is already gone. Also drops any handles escrowed until
902    /// the peer has had an opportunity to duplicate them.
903    fn complete_err(&self, id: u64, error: Error) {
904        // The escrow lives in `Shared`, which outlives the handles, so it is
905        // cleaned up whether or not anyone is still waiting for the result.
906        self.shared.drop_escrowed_handles(id);
907        if let Some(inner) = self.inner.upgrade() {
908            inner.complete(id, Err(error));
909        }
910    }
911
912    /// Admits one queued item into the scheduler. Returns `Err` on a fatal
913    /// transport/protocol error, which the caller must treat as fatal for
914    /// the whole session, not just this one message.
915    async fn admit(&mut self, message: Outgoing<P::Request>) -> Result<(), Error> {
916        match message {
917            Outgoing::Request { id, value, trailer } => {
918                self.admit_request(id, value, trailer).await
919            }
920            Outgoing::Cancel { id } => {
921                self.admit_cancel(id);
922                Ok(())
923            }
924            Outgoing::DiscardTrailer { id } => {
925                self.scheduler.admit_empty(Kind::Discard, id);
926                Ok(())
927            }
928            Outgoing::PeerDiscarded { id } => {
929                // The peer will never credit what it just threw away, so
930                // settle by id rather than through the send, which may
931                // already have finished and left the scheduler.
932                self.shared.trailer_session.settle(id);
933                self.scheduler.discard_active_trailer(id);
934                Ok(())
935            }
936            Outgoing::Ack { id } => {
937                self.scheduler.admit_empty(Kind::Ack, id);
938                Ok(())
939            }
940            Outgoing::Release { id, count } => {
941                self.scheduler.admit_release(id, count);
942                Ok(())
943            }
944            Outgoing::Credit { id, count } => {
945                self.scheduler.admit_credit(id, count);
946                Ok(())
947            }
948            Outgoing::PayloadCredit { count } => {
949                self.scheduler.admit_payload_credit(count);
950                Ok(())
951            }
952            Outgoing::Terminal => unreachable!("handled by the writer loop"),
953        }
954    }
955
956    async fn admit_request(
957        &mut self,
958        id: u64,
959        value: P::Request,
960        trailer: Trailer,
961    ) -> Result<(), Error> {
962        // Every handle is gone, so nothing is waiting on this call's result
963        // and its pending entry went with them: drop it rather than encode it.
964        if self.inner.upgrade().is_none() {
965            return Ok(());
966        }
967        let mut ledger = Ledger::default();
968        let mut put_handles = SessionFrame {
969            inner: EncodeHandles::new(&self.transport, self.shared.max_handles()),
970            session: &self.shared.session,
971            ledger: &mut ledger,
972        };
973        let payload = match encode_payload(&value, &mut put_handles) {
974            Ok(payload) => payload,
975            Err(err) => {
976                // The ledger drops here without committing. Nothing of this
977                // message reached the wire, so any gift it named is rescinded
978                // by that drop path, not by a commit.
979                drop(put_handles);
980                ledger.rescind();
981                self.complete_err(id, err);
982                return Ok(());
983            }
984        };
985        let handles = self.shared.finish_handles(id, put_handles.inner);
986        self.scheduler
987            .admit_message(Kind::Request, id, payload, handles, trailer, ledger);
988        // The call takes its slot here, where its request actually reaches
989        // the scheduler — never on the way in. A request that fails to encode
990        // is completed locally above and never goes out, so no response and
991        // no `Terminal` will ever come back to release a slot taken earlier.
992        self.shared.active_calls.lock().unwrap().insert(id);
993        Ok(())
994    }
995
996    fn admit_cancel(&mut self, id: u64) -> bool {
997        match self.scheduler.try_cancel_active(id) {
998            AbortOutcome::NotActive => {
999                self.scheduler.admit_empty(Kind::Cancel, id);
1000                false
1001            }
1002            AbortOutcome::Discarded {
1003                started,
1004                dispatched,
1005            } => {
1006                if started {
1007                    self.scheduler.admit_abort(id);
1008                }
1009                if !started {
1010                    self.shared.discard_unsent_escrow(id);
1011                }
1012                if dispatched {
1013                    self.scheduler.admit_empty(Kind::Cancel, id);
1014                    false
1015                } else {
1016                    self.complete_err(id, Error::Cancelled);
1017                    true
1018                }
1019            }
1020        }
1021    }
1022
1023    async fn promote_waiting(&mut self) -> Result<(), Error> {
1024        while self.shared.active_calls.lock().unwrap().len()
1025            < self.shared.limits.max_concurrent_calls
1026        {
1027            let Some(message) = self.waiting.pop_front() else {
1028                break;
1029            };
1030            let Outgoing::Request { id, .. } = &message else {
1031                unreachable!("only requests wait for call admission")
1032            };
1033            let id = *id;
1034            let Some(inner) = self.inner.upgrade() else {
1035                break;
1036            };
1037            if !inner.pending.lock().unwrap().contains(id) {
1038                continue;
1039            }
1040            self.admit(message).await?;
1041        }
1042        Ok(())
1043    }
1044
1045    async fn run(mut self) -> Result<(), Error> {
1046        // Holding a clone of `Inner::outgoing` is what represents the
1047        // ability to still get a message in (see its doc comment), so the
1048        // channel closing — every clone gone — doubles as the shutdown
1049        // signal: once `recv()` reports no more messages will ever arrive,
1050        // admission of new work stops, and the loop keeps advancing the
1051        // scheduler until it's fully drained before exiting, never
1052        // abandoning a write already committed to it.
1053        //
1054        // "Fully drained" here means `has_work`, not `has_pending`: shutdown
1055        // finishes writes already committed to the wire, but abandons work
1056        // still parked on quota or concurrency admission. `Client::close`
1057        // keeps the reader alive while this happens, so credit can still
1058        // arrive for committed writes; `Client::abort` and dropping the last
1059        // handle stop it. Widening this condition would make abrupt shutdown
1060        // wait for credit that can no longer arrive.
1061        let mut closed = false;
1062        while !closed || self.scheduler.has_work() {
1063            tokio::select! {
1064                message = self.outgoing.recv(), if !closed => {
1065                    let Some(message) = message else {
1066                        closed = true;
1067                        self.waiting.clear();
1068                        continue;
1069                    };
1070                    // No blanket `fail_all` here: `admit` already fails just
1071                    // the one call whose request it couldn't get onto the
1072                    // transport (see `admit_request`). Every other pending
1073                    // call's request either already made it out, or is still
1074                    // queued for a later turn of this same loop — a write
1075                    // failure on one message doesn't mean every other one is
1076                    // doomed, only that this connection is. The reader is
1077                    // what authoritatively decides that (see the comment
1078                    // after this loop).
1079                    match message {
1080                        request @ Outgoing::Request { .. } => self.waiting.push_back(request),
1081                        Outgoing::Cancel { id } => {
1082                            if let Some(pos) = self.waiting.iter().position(
1083                                |message| matches!(message, Outgoing::Request { id: waiting_id, .. } if *waiting_id == id),
1084                            ) {
1085                                self.waiting.remove(pos);
1086                                self.complete_err(id, Error::Cancelled);
1087                            } else if self.shared.active_calls.lock().unwrap().contains(&id)
1088                                && self.admit_cancel(id)
1089                            {
1090                                // Cancelled before its request ever reached
1091                                // the wire, so no terminal message is coming
1092                                // and the reader will never free this slot.
1093                                self.shared.active_calls.lock().unwrap().remove(&id);
1094                            }
1095                        }
1096                        // The slot is already free; this only prompts the
1097                        // promotion below.
1098                        Outgoing::Terminal => {}
1099                        message => self.admit(message).await?,
1100                    }
1101                    self.promote_waiting().await?;
1102                }
1103                // Not raced against anything: once ready, a fragment write
1104                // is committed to the scheduler and must run to completion.
1105                // A dropped send future could otherwise leave a committed
1106                // partial fragment on the transport, or — on transports
1107                // whose writes are dispatched to a detached background task
1108                // (e.g. the blocking-pool-backed Windows pipe transport) —
1109                // let an abandoned write complete arbitrarily later,
1110                // potentially after the peer has already torn down its end.
1111                _ = self.scheduler.ready(), if self.scheduler.has_pending() => {
1112                    let result = self.scheduler.advance(&mut self.transport).await;
1113                    // Flush anything sent by the scheduler
1114                    let _ = self.transport.flush().await;
1115                    match result {
1116                        // A streaming trailer producer was dropped mid-message.
1117                        Ok(fragment::AdvanceOutcome::Aborted(id)) => {
1118                            // The postcard payload has already reached the peer
1119                            // before a streaming trailer can abort. Cancel the
1120                            // dispatched handler and retain its call slot until
1121                            // the resulting terminal message arrives.
1122                            self.scheduler.admit_empty(Kind::Cancel, id);
1123                        }
1124                        Ok(fragment::AdvanceOutcome::None) => {}
1125                        #[cfg(target_os = "macos")]
1126                        Ok(fragment::AdvanceOutcome::Escrow { id, fds, handles_done }) => {
1127                            self.shared.escrow_sent(id, fds, handles_done);
1128                        }
1129                        // No blanket `fail_all`: a write failure here means
1130                        // this connection is broken, not that every pending
1131                        // call's already-sent request was never delivered.
1132                        // The reader observes the same broken connection
1133                        // (see the comment after this loop) and is what
1134                        // authoritatively fails pending calls.
1135                        Err(err) => {
1136                            return Err(err);
1137                        }
1138                    }
1139                }
1140            }
1141        }
1142        self.transport.shutdown().await?;
1143        Ok(())
1144    }
1145}
1146
1147impl<P: Protocol> RecvDriver<P> {
1148    /// Applies a reassembled message.
1149    ///
1150    /// Everything that belongs to `Shared` — the call slot, the handle
1151    /// escrows — is settled unconditionally; only handing the result to a
1152    /// waiting caller needs the handle side to still be alive, so the
1153    /// upgrade is taken per arm rather than up front.
1154    fn dispatch(&self, message: Message) -> Result<(), Error> {
1155        let Message {
1156            kind,
1157            id,
1158            payload,
1159            handles,
1160            trailer,
1161            charge,
1162        } = message;
1163        match kind {
1164            Kind::Response => {
1165                // Decoding happens here, in the reader task, *before*
1166                // `complete` discovers whether anyone still wants this
1167                // response. Decoding is what mirrors any opaque the payload
1168                // carries; the resulting `CallResult` is then dropped
1169                // normally when the call was cancelled, which releases those
1170                // references back to the peer. Skipping the decode for a
1171                // response nobody is waiting on would look like an
1172                // optimization and would silently leak every handle in it.
1173                let response = self.shared.decode(&payload, handles)?;
1174                let trailer = trailer.map(TrailerRecv::new);
1175                self.shared.finish_call(id);
1176                if let Some(inner) = self.inner.upgrade() {
1177                    inner.complete(
1178                        id,
1179                        Ok(CallResult {
1180                            response,
1181                            trailer,
1182                            charge: Some(charge),
1183                        }),
1184                    );
1185                    inner.send(Outgoing::Terminal);
1186                }
1187            }
1188            Kind::Error => {
1189                self.shared.finish_call(id);
1190                if let Some(inner) = self.inner.upgrade() {
1191                    inner.complete(id, Err(Error::Cancelled));
1192                    inner.send(Outgoing::Terminal);
1193                }
1194            }
1195            Kind::Ack => {
1196                if !self.shared.release_escrow(id) {
1197                    return Err(Error::Protocol(format!(
1198                        "Ack for request {id} with no active escrow"
1199                    )));
1200                }
1201            }
1202            Kind::Discard => {
1203                if let Some(inner) = self.inner.upgrade() {
1204                    inner.send(Outgoing::PeerDiscarded { id });
1205                }
1206            }
1207            kind => return Err(Error::Protocol(format!("unexpected {kind:?} frame"))),
1208        }
1209        Ok(())
1210    }
1211
1212    /// Refuses a fragment the peer had no business sending, before the
1213    /// reassembler can allocate anything for it.
1214    ///
1215    /// Only the first fragment of a message is checked: a later one names a
1216    /// message this end already accepted, and the reassembler rejects an id it
1217    /// has no entry for.
1218    fn check_header(
1219        active_calls: &Mutex<HashSet<u64>>,
1220        header: &FragmentHeader,
1221    ) -> Result<(), Error> {
1222        match header.kind {
1223            // A server cannot originate a call, so this is never admissible in
1224            // this direction, whatever id it names.
1225            Kind::Request => Err(Error::Protocol("client received a Request fragment".into())),
1226            // Ids are minted by this end, so an id that names no live call is
1227            // fabricated. Tolerating it would let a peer open unbounded
1228            // reassembly buffers for calls that were never made.
1229            Kind::Response
1230                if header.flags.contains(Flags::FIRST)
1231                    && !active_calls.lock().unwrap().contains(&header.id) =>
1232            {
1233                Err(Error::Protocol(format!(
1234                    "Response for message {} with no call outstanding",
1235                    header.id
1236                )))
1237            }
1238            _ => Ok(()),
1239        }
1240    }
1241
1242    async fn run(mut self, mut shutdown: oneshot::Receiver<()>) {
1243        let mut reassembler = Reassembler::new(self.shared.limits, self.shared.sink.clone());
1244        loop {
1245            let mut frame = self.transport.recv();
1246            let header = tokio::select! {
1247                header = fragment::read_fragment_header(&mut frame) => header,
1248                _ = &mut shutdown => return,
1249            };
1250            let header = match header {
1251                Ok(header) => header,
1252                Err(error) => {
1253                    fail(&self.inner, error);
1254                    return;
1255                }
1256            };
1257            if let Err(error) = Self::check_header(&self.shared.active_calls, &header) {
1258                fail(&self.inner, error);
1259                return;
1260            }
1261            let accepted = tokio::select! {
1262                accepted = reassembler.accept(header, &mut frame) => accepted,
1263                _ = &mut shutdown => return,
1264            };
1265            let complete = match accepted {
1266                Ok(complete) => complete,
1267                Err(error) => {
1268                    fail(&self.inner, error);
1269                    return;
1270                }
1271            };
1272            match complete {
1273                Event::None => {}
1274                // Nothing to admit: a client answers no calls, and the header
1275                // gate already refused every fragment that does not belong to
1276                // one this end made. So the reassembler holds at most one
1277                // entry per live call, and `max_concurrent_calls` bounds
1278                // those where they are issued.
1279                Event::PayloadIncomplete { .. } => {}
1280                Event::Aborted {
1281                    kind,
1282                    id,
1283                    dispatched,
1284                } => {
1285                    // The header gate already refused every kind a client
1286                    // may not receive, and the reassembler only ever aborts a
1287                    // message it opened.
1288                    debug_assert_eq!(kind, Kind::Response, "aborted a non-response message");
1289                    if !dispatched {
1290                        self.shared.finish_call(id);
1291                        if let Some(inner) = self.inner.upgrade() {
1292                            inner.complete(id, Err(Error::Cancelled));
1293                            inner.send(Outgoing::Terminal);
1294                        }
1295                    }
1296                }
1297                Event::Message(message) => {
1298                    if let Err(error) = self.dispatch(message) {
1299                        fail(&self.inner, error);
1300                        return;
1301                    }
1302                }
1303                Event::Ack { id, message } => {
1304                    if let Some(inner) = self.inner.upgrade() {
1305                        inner.send(Outgoing::Ack { id });
1306                    }
1307                    if let Some(message) = message
1308                        && let Err(error) = self.dispatch(message)
1309                    {
1310                        fail(&self.inner, error);
1311                        return;
1312                    }
1313                }
1314                Event::Trailer { shared, len, .. } => {
1315                    let frame = self.transport.recv();
1316                    // SAFETY: the lease retains the receiver borrow and
1317                    // clears the erased token before it ends.
1318                    let lease = unsafe { RecvShared::grant(&shared, frame, len) };
1319                    if let Err(error) = RecvShared::wait_fragment(&shared).await {
1320                        fail(&self.inner, error.into());
1321                        return;
1322                    }
1323                    lease.complete();
1324                }
1325                Event::Release { id, count } => self.shared.session.release(id, count),
1326                Event::Credit { id, count } => {
1327                    // Applied here rather than routed through the writer:
1328                    // the pool is shared state on `Shared`, and the refund
1329                    // needs nothing the writer owns. It is keyed by id and
1330                    // lands whether or not the send still exists, since a
1331                    // trailer's last credits routinely arrive after it has
1332                    // finished and left the scheduler, and dropping those
1333                    // would shrink the pool on every transfer.
1334                    self.shared.trailer_session.refund(id, count as usize);
1335                }
1336                Event::PayloadCredit { count } => {
1337                    // Applied here for the same reason as trailer credit, and
1338                    // with the same indifference to whether the send it pays
1339                    // for still exists: the pool lives on `Shared`, and
1340                    // crediting it wakes the writer parked on it. Nothing is
1341                    // keyed by id, so nothing has to still be around.
1342                    self.shared.payload_budget.credit(count as usize);
1343                }
1344            }
1345        }
1346    }
1347}
1348
1349/// Fails every pending call. Takes `inner` by reference (rather than a
1350/// `RecvDriver` method borrowing `&self`) so it can be called while another
1351/// field (e.g. a `RecvFrame` token borrowing `self.transport`) is still
1352/// mutably borrowed.
1353fn fail<P: Protocol>(inner: &Weak<Inner<P>>, error: Error) {
1354    if let Some(inner) = inner.upgrade() {
1355        inner.fail(error);
1356    }
1357}
1358
1359#[cfg(test)]
1360mod tests {
1361    use super::*;
1362
1363    struct Test;
1364
1365    impl Protocol for Test {
1366        type Request = u8;
1367        type Response = u8;
1368    }
1369
1370    fn header(kind: Kind, flags: Flags, id: u64) -> FragmentHeader {
1371        FragmentHeader {
1372            flags,
1373            kind,
1374            id,
1375            payload_len: 0,
1376        }
1377    }
1378
1379    /// The gate in front of the reassembler: a client answers calls it made
1380    /// and nothing else.
1381    #[test]
1382    fn header_gate_refuses_requests_and_responses_to_calls_never_made() {
1383        let active = Mutex::new(HashSet::from([7u64]));
1384        let check = |header| RecvDriver::<Test>::check_header(&active, &header);
1385
1386        assert!(matches!(
1387            check(header(Kind::Request, Flags::FIRST | Flags::LAST, 7)),
1388            Err(Error::Protocol(_)),
1389        ));
1390        assert!(matches!(
1391            check(header(Kind::Response, Flags::FIRST, 9)),
1392            Err(Error::Protocol(_)),
1393        ));
1394        assert!(check(header(Kind::Response, Flags::FIRST, 7)).is_ok());
1395        // A continuation names a message this end already accepted, so the
1396        // reassembler owns rejecting an id it has no entry for.
1397        assert!(check(header(Kind::Response, Flags::LAST, 9)).is_ok());
1398        // Trailer credit and opaque releases outlive the call they name, so
1399        // control frames are not gated on it — and payload credit names no
1400        // call at all.
1401        assert!(check(header(Kind::Credit, Flags::FIRST | Flags::LAST, 9)).is_ok());
1402        assert!(check(header(Kind::PayloadCredit, Flags::FIRST | Flags::LAST, 0)).is_ok());
1403    }
1404
1405    fn test_shared(outgoing: &mpsc::UnboundedSender<Outgoing<u8>>) -> Arc<Shared> {
1406        Arc::new(Shared {
1407            session: Session::new(Box::new(outgoing.downgrade())),
1408            trailer_session: Arc::new(SessionWindow::new(Limits::default().trailer_session_window)),
1409            payload_budget: Arc::new(PayloadBudget::new(
1410                Limits::default().max_outstanding_payload,
1411            )),
1412            active_calls: Default::default(),
1413            sink: Arc::new(outgoing.downgrade()),
1414            #[cfg(windows)]
1415            handle_escrow: Mutex::new(HashMap::new()),
1416            #[cfg(target_os = "macos")]
1417            fd_escrow: Mutex::new(Default::default()),
1418            limits: Limits::default(),
1419        })
1420    }
1421
1422    fn pending_call() -> (Call<Test>, mpsc::UnboundedReceiver<Outgoing<u8>>) {
1423        let (outgoing, outgoing_rx) = mpsc::unbounded_channel();
1424        let inner = Arc::new(Inner {
1425            outgoing: Mutex::new(Some(outgoing.clone())),
1426            pending: Mutex::new(Pending::new()),
1427            next_id: Mutex::new(1),
1428            reader_shutdown: Mutex::new(None),
1429            tasks: Mutex::new(None),
1430            requires_full_close: false,
1431            shared: test_shared(&outgoing),
1432            #[cfg(windows)]
1433            _peer_process: None,
1434        });
1435        let (tx, rx) = oneshot::channel();
1436        assert!(inner.pending.lock().unwrap().register(0, tx));
1437        (
1438            Call {
1439                id: 0,
1440                rx,
1441                inner,
1442                cancel_sent: false,
1443            },
1444            outgoing_rx,
1445        )
1446    }
1447
1448    #[tokio::test]
1449    async fn completed_call_does_not_send_cancel_when_dropped() {
1450        let (call, mut outgoing) = pending_call();
1451        let inner = call.inner.clone();
1452        call.inner.complete(
1453            call.id,
1454            Ok(CallResult {
1455                response: 7,
1456                trailer: None,
1457                charge: None,
1458            }),
1459        );
1460        assert_eq!(call.await.unwrap().into_response(), 7);
1461        assert!(matches!(
1462            outgoing.try_recv(),
1463            Err(mpsc::error::TryRecvError::Empty)
1464        ));
1465        drop(inner);
1466    }
1467
1468    #[test]
1469    fn dropped_pending_call_sends_cancel() {
1470        let (call, mut outgoing) = pending_call();
1471        drop(call);
1472        assert!(matches!(
1473            outgoing.try_recv(),
1474            Ok(Outgoing::Cancel { id: 0 })
1475        ));
1476    }
1477
1478    #[cfg(windows)]
1479    #[tokio::test]
1480    async fn complete_err_clears_handle_escrow() {
1481        let (outgoing, _outgoing_rx) = mpsc::unbounded_channel();
1482        let shared = test_shared(&outgoing);
1483        let inner = Arc::new(Inner {
1484            outgoing: Mutex::new(Some(outgoing.clone())),
1485            pending: Mutex::new(Pending::new()),
1486            next_id: Mutex::new(1),
1487            reader_shutdown: Mutex::new(None),
1488            tasks: Mutex::new(None),
1489            requires_full_close: true,
1490            shared: shared.clone(),
1491            _peer_process: None,
1492        });
1493        let (tx, _rx) = oneshot::channel();
1494        assert!(inner.pending.lock().unwrap().register(0, tx));
1495        shared.handle_escrow.lock().unwrap().insert(0, Vec::new());
1496        shared.handle_escrow.lock().unwrap().insert(1, Vec::new());
1497
1498        let (dummy_write, _unused) = tokio::io::duplex(64);
1499        let (sender, _unused) = transport::generic_duplex(dummy_write);
1500        let (_unused_tx, outgoing_rx) = mpsc::unbounded_channel();
1501        let writer = SendDriver::<Test> {
1502            transport: transport::AnySender::Generic(sender),
1503            outgoing: outgoing_rx,
1504            inner: Arc::downgrade(&inner),
1505            shared: shared.clone(),
1506            scheduler: Scheduler::new(
1507                &Limits::default(),
1508                Arc::new(PayloadBudget::new(
1509                    Limits::default().max_outstanding_payload,
1510                )),
1511            ),
1512            waiting: VecDeque::new(),
1513        };
1514
1515        writer.complete_err(0, Error::Cancelled);
1516        assert!(!shared.handle_escrow.lock().unwrap().contains_key(&0));
1517
1518        // The escrow outlives the handle side, so it is still cleaned up
1519        // once nothing is left to deliver the error to.
1520        drop(inner);
1521        writer.complete_err(1, Error::Cancelled);
1522        assert!(shared.handle_escrow.lock().unwrap().is_empty());
1523    }
1524}
1525
1526#[cfg(all(test, windows))]
1527mod windows_tests {
1528    use std::os::windows::io::FromRawHandle;
1529
1530    use windows_sys::Win32::System::Threading::{
1531        GetCurrentProcessId, OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_SYNCHRONIZE,
1532    };
1533
1534    use super::*;
1535
1536    fn current_process_handle() -> OwnedHandle {
1537        let handle = unsafe {
1538            OpenProcess(
1539                PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SYNCHRONIZE,
1540                0,
1541                GetCurrentProcessId(),
1542            )
1543        };
1544        assert!(!handle.is_null());
1545        unsafe { OwnedHandle::from_raw_handle(handle as _) }
1546    }
1547
1548    #[test]
1549    fn validates_named_pipe_peer_process() {
1550        let process = current_process_handle();
1551        let pid = unsafe { GetCurrentProcessId() };
1552        validate_peer_process(&process, pid).unwrap();
1553        assert_eq!(
1554            validate_peer_process(&process, !pid).unwrap_err().kind(),
1555            io::ErrorKind::PermissionDenied
1556        );
1557    }
1558}