Skip to main content

dolang_rpc/
server.rs

1//! The handling side of a bound RPC session.
2//!
3//! [`Server::serve`] dispatches each incoming request to a handler
4//! concurrently with the others, passing it a [`CallContext`] used to send
5//! the response.
6
7#[cfg(windows)]
8use std::{any::TypeId, io, os::windows::io::OwnedHandle};
9use std::{
10    collections::HashMap,
11    marker::PhantomData,
12    sync::{Arc, Mutex},
13};
14
15use futures::{
16    StreamExt,
17    future::{AbortHandle, Abortable},
18    stream::FuturesUnordered,
19};
20use tokio::sync::{mpsc, oneshot};
21
22use crate::{
23    Error, Limits, Protocol,
24    driver::{Drain, DrainSignal, DrainWatch, drain_signal},
25    fragment::{self, Event, Kind, Message},
26    serde::{decode_payload, encode_payload},
27    session::{self, Cite, Gift, InvalidOpaque, OpaqueGuard, OpaqueResource, Session},
28    trailer::{RecvShared, SendShared, TrailerRecv, TrailerSend},
29    transport::{self, EncodeHandles, Receiver, Sender},
30    window::{ControlSink, PayloadBudget, PayloadCharge, SessionWindow},
31};
32#[cfg(windows)]
33use crate::{handle::TakeHandle, session::Inner as OpaqueInner};
34
35#[cfg(windows)]
36struct DecodeHandles<'a> {
37    receiver: &'a transport::AnyReceiver,
38    session: &'a Arc<Session>,
39    count: usize,
40    max_handles: usize,
41}
42
43#[cfg(windows)]
44impl TakeHandle for DecodeHandles<'_> {
45    fn take_handle(&mut self, value: usize) -> io::Result<OwnedHandle> {
46        if self.count == self.max_handles {
47            return Err(io::Error::new(
48                io::ErrorKind::InvalidData,
49                "message contains too many handle attachments",
50            ));
51        }
52        self.count += 1;
53        self.receiver.duplicate_peer_handle(value)
54    }
55
56    fn take_gift(&mut self, owner: u8, id: u64) -> io::Result<OpaqueInner> {
57        self.session
58            .take_gift(owner, id)
59            .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid opaque reference"))
60    }
61
62    fn take_cite(&mut self, owner: u8, id: u64, marker: TypeId) -> io::Result<OpaqueInner> {
63        self.session
64            .take_cite(owner, id, marker)
65            .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "invalid opaque reference"))
66    }
67}
68
69/// A negotiated server endpoint that has not yet been bound to a [`Protocol`].
70///
71/// Inspect its negotiated application protocol, then consume it with
72/// [`bind`](Unbound::bind) to obtain a [`Server`].
73pub use crate::unbound::UnboundServer as Unbound;
74
75/// A server endpoint for one connection.
76///
77/// Consume it with [`serve`](Self::serve) to dispatch requests from the peer.
78pub struct Server<P: Protocol> {
79    sender: transport::AnySender,
80    receiver: transport::AnyReceiver,
81    outgoing: mpsc::UnboundedSender<Outgoing<P::Response>>,
82    outgoing_rx: mpsc::UnboundedReceiver<Outgoing<P::Response>>,
83    /// The other end of `Inner::shutdown`, held until `serve` hands it to
84    /// the receive driver.
85    shutdown_rx: oneshot::Receiver<()>,
86    /// Held until `serve` hands the two ends to the two drivers.
87    drain: (DrainSignal, DrainWatch),
88    shared: Arc<Shared>,
89    marker: PhantomData<fn() -> P>,
90}
91
92enum Outgoing<R> {
93    Response {
94        id: u64,
95        value: R,
96        trailer: fragment::Trailer,
97    },
98    Error {
99        id: u64,
100    },
101    Cancel {
102        id: u64,
103    },
104    /// We stopped reading a request trailer (it arrived unwanted) and want
105    /// to tell the peer to stop sending it. Always results in a wire
106    /// `Kind::Discard` fragment.
107    DiscardTrailer {
108        id: u64,
109    },
110    /// A wire `Kind::Discard` fragment arrived, telling us the peer no
111    /// longer wants our response trailer. Applied to our own active send;
112    /// never re-sent to the peer.
113    PeerDiscarded {
114        id: u64,
115    },
116    Ack {
117        id: u64,
118    },
119    /// We retired `count` bytes of the request trailer on `id` and are
120    /// returning that much credit. Always results in a wire `Kind::Credit`.
121    Credit {
122        id: u64,
123        count: u32,
124    },
125    /// A call released its request payload and is returning `count` bytes of
126    /// quota. Always results in a wire `Kind::PayloadCredit`, which names no
127    /// message: several of these coalesce into one fragment.
128    PayloadCredit {
129        count: u32,
130    },
131    /// Drops `count` of this endpoint's references to the peer's opaque `id`.
132    Release {
133        id: u64,
134        count: u32,
135    },
136}
137
138/// Emits `Release` frames for opaques whose last local handle dropped. The
139/// strong senders stay exactly what they were: `serve`'s own sender and each
140/// live `CallContext`.
141impl<R: Send + 'static> session::ReleaseSink for mpsc::WeakUnboundedSender<Outgoing<R>> {
142    fn release(&self, id: u64, count: u32) {
143        // Called from `Drop`, so a departed channel is not an error: the
144        // writer is already gone and the peer's table dies with the session.
145        if let Some(outgoing) = self.upgrade() {
146            let _ = outgoing.send(Outgoing::Release { id, count });
147        }
148    }
149}
150
151impl<R: Send + 'static> ControlSink for mpsc::WeakUnboundedSender<Outgoing<R>> {
152    fn credit(&self, id: u64, count: u32) {
153        if let Some(outgoing) = self.upgrade() {
154            let _ = outgoing.send(Outgoing::Credit { id, count });
155        }
156    }
157
158    fn payload_credit(&self, count: u32) {
159        // Reached from `PayloadCharge::drop`, which runs on every path a call
160        // can end on — including ones where the session is already tearing
161        // down, where there is no one left to credit.
162        if let Some(outgoing) = self.upgrade() {
163            let _ = outgoing.send(Outgoing::PayloadCredit { count });
164        }
165    }
166
167    fn discard(&self, id: u64) {
168        // Reached from `TrailerRecv::drop`, so a departed channel just means
169        // the connection is already gone and the peer has nothing to stop.
170        if let Some(outgoing) = self.upgrade() {
171            let _ = outgoing.send(Outgoing::DiscardTrailer { id });
172        }
173    }
174}
175
176/// State both connection drivers and every live handler share.
177///
178/// One `Arc` rather than a bundle of them, and held strongly by all three:
179/// nothing in here can keep the session alive on its own, because the ability
180/// to still get a message out is the `outgoing` sender, which stays outside
181/// it. Closing that channel is still what shuts the writer down.
182struct Shared {
183    inner: Mutex<Inner>,
184    session: Arc<Session>,
185    /// Send-side trailer credit shared by every outgoing response trailer on
186    /// this connection. Bounds what the peer must buffer for us in aggregate.
187    trailer_session: Arc<SessionWindow>,
188    /// Send-side payload quota shared by every outgoing response. Bounds the
189    /// postcard bytes the peer must hold for us across all live calls, and is
190    /// charged in full when a response is admitted to the scheduler.
191    ///
192    /// Kept apart from `trailer_session` on purpose; see [`crate::window`].
193    payload_budget: Arc<PayloadBudget>,
194    limits: Limits,
195}
196
197impl Shared {
198    /// Maximum handle attachments one message may carry.
199    fn max_handles(&self) -> usize {
200        // A transport configured to attach no handles to a fragment can
201        // carry none at all.
202        #[cfg(unix)]
203        if self.limits.max_handles_per_fragment == 0 {
204            return 0;
205        }
206        self.limits.max_handles_per_message
207    }
208
209    /// Finishes handle encoding for message `id`, taking custody of whatever
210    /// this platform must keep alive once the message is on the wire.
211    ///
212    /// On macOS that is the file descriptors themselves, escrowed until the
213    /// peer acknowledges receipt. Every other unix passes them with the
214    /// fragment and is done with them.
215    #[cfg(unix)]
216    fn finish_handles(&self, id: u64, handles: EncodeHandles) -> transport::OutgoingHandles {
217        let handles = handles.finish();
218        #[cfg(target_os = "macos")]
219        if handles.needs_ack() {
220            self.inner.lock().unwrap().fd_escrow.register(id);
221        }
222        #[cfg(not(target_os = "macos"))]
223        let _ = id;
224        handles
225    }
226
227    /// Finishes handle encoding for message `_id`. Windows duplicates each
228    /// handle into the peer as it is encoded, so the originals are this
229    /// end's to close and nothing is escrowed.
230    #[cfg(windows)]
231    fn finish_handles(&self, _id: u64, handles: EncodeHandles) -> transport::OutgoingHandles {
232        let (handles, escrow) = handles.finish();
233        drop(escrow);
234        handles
235    }
236
237    /// Decodes a message payload, taking custody of every handle and opaque
238    /// reference it carries.
239    #[cfg(unix)]
240    fn decode<T: ::serde::de::DeserializeOwned>(
241        &self,
242        payload: &[u8],
243        handles: transport::ReceivedHandles,
244        _receiver: &transport::AnyReceiver,
245    ) -> Result<T, Error> {
246        decode_payload(
247            payload,
248            &mut session::SessionHandles {
249                inner: handles,
250                session: &self.session,
251            },
252        )
253    }
254
255    /// Decodes a message payload, taking custody of every handle and opaque
256    /// reference it carries. Windows handles are named by value in the
257    /// payload and duplicated out of the peer as they are decoded, rather
258    /// than arriving attached to the fragment, so `handles` is empty.
259    #[cfg(windows)]
260    fn decode<T: ::serde::de::DeserializeOwned>(
261        &self,
262        payload: &[u8],
263        _handles: transport::ReceivedHandles,
264        receiver: &transport::AnyReceiver,
265    ) -> Result<T, Error> {
266        decode_payload(
267            payload,
268            &mut DecodeHandles {
269                receiver,
270                session: &self.session,
271                count: 0,
272                max_handles: self.max_handles(),
273            },
274        )
275    }
276
277    /// Records the file descriptors for `id` that just reached the wire.
278    #[cfg(target_os = "macos")]
279    fn escrow_sent(&self, id: u64, fds: Vec<std::os::fd::OwnedFd>, done: bool) {
280        self.inner.lock().unwrap().fd_escrow.sent(id, fds, done);
281    }
282
283    /// Forgets the escrow for a message that will never reach the wire.
284    fn discard_unsent_escrow(&self, id: u64) {
285        #[cfg(target_os = "macos")]
286        self.inner.lock().unwrap().fd_escrow.discard_unsent(id);
287        #[cfg(not(target_os = "macos"))]
288        let _ = id;
289    }
290
291    /// Releases the escrow an `Ack` names, returning false when there is
292    /// none — which is every `Ack` on a platform that escrows nothing.
293    fn release_escrow(&self, id: u64) -> bool {
294        #[cfg(target_os = "macos")]
295        return self.inner.lock().unwrap().fd_escrow.release(id);
296        #[cfg(not(target_os = "macos"))]
297        {
298            let _ = id;
299            false
300        }
301    }
302}
303
304struct Inner {
305    outstanding: HashMap<u64, Cancellation>,
306    /// Signals the receive driver to stop accepting new work. Taken by the
307    /// first handler to ask for shutdown, so later ones are no-ops.
308    shutdown: Option<oneshot::Sender<()>>,
309    #[cfg(target_os = "macos")]
310    fd_escrow: crate::escrow::FdEscrow,
311}
312
313struct Cancellation {
314    signal: Option<oneshot::Sender<()>>,
315    abort: AbortHandle,
316}
317
318/// Refuses a fragment the peer had no business sending, before the
319/// reassembler can allocate anything for it.
320///
321/// Unlike the client's gate, this needs no id: a client never asks this end
322/// for anything, so a response in this direction names nothing at all.
323fn check_header(header: &fragment::FragmentHeader) -> Result<(), Error> {
324    if header.kind == Kind::Response {
325        return Err(Error::Protocol(
326            "server received a Response fragment".into(),
327        ));
328    }
329    Ok(())
330}
331
332impl<P: Protocol> Server<P> {
333    /// Builds a `Server` from an already-negotiated transport. Only reachable
334    /// via [`Unbound::bind`] — `Server` has
335    /// no public constructors of its own, so it's never possible to hold one
336    /// that hasn't already completed `fragment::negotiate`, and `serve`
337    /// never needs to negotiate itself.
338    pub(crate) fn from_transport(
339        sender: transport::AnySender,
340        receiver: transport::AnyReceiver,
341        limits: Limits,
342    ) -> Self {
343        let (outgoing, outgoing_rx) = mpsc::unbounded_channel();
344        let (shutdown, shutdown_rx) = oneshot::channel();
345        Self {
346            sender,
347            receiver,
348            shared: Arc::new(Shared {
349                inner: Mutex::new(Inner {
350                    outstanding: HashMap::new(),
351                    shutdown: Some(shutdown),
352                    #[cfg(target_os = "macos")]
353                    fd_escrow: Default::default(),
354                }),
355                session: Session::new(Box::new(outgoing.downgrade())),
356                trailer_session: Arc::new(SessionWindow::new(limits.trailer_session_window)),
357                payload_budget: Arc::new(PayloadBudget::new(limits.max_outstanding_payload)),
358                limits,
359            }),
360            outgoing,
361            outgoing_rx,
362            shutdown_rx,
363            drain: drain_signal(),
364            marker: PhantomData,
365        }
366    }
367
368    /// Serves requests until the peer disconnects, the session fails, or a
369    /// handler requests graceful shutdown.
370    ///
371    /// The handler may be called concurrently for independent requests. Each
372    /// invocation must consume its [`CallContext`] with [`CallContext::respond`]
373    /// or [`CallContext::respond_with_trailer`]; dropping the context without
374    /// responding reports a per-request error to the peer.
375    pub async fn serve<H>(self, handler: H) -> Result<(), Error>
376    where
377        H: AsyncFn(CallContext<P>, P::Request) + Send + Sync + 'static,
378    {
379        let (drain_signal, drain_watch) = self.drain;
380        let send = SendDriver::<P>::new(
381            self.sender,
382            self.outgoing_rx,
383            self.shared.clone(),
384            drain_watch,
385        )
386        .run();
387        tokio::pin!(send);
388        let recv = RecvDriver::new(
389            self.receiver,
390            self.outgoing,
391            self.shutdown_rx,
392            self.shared,
393            handler,
394            drain_signal,
395        )
396        .run();
397        tokio::pin!(recv);
398        let result = tokio::select! {
399            result = &mut recv => result,
400            // A successful send-side exit is not the end of the connection.
401            // In practice graceful mode keeps the driver alive until the
402            // receiver ends, so this only covers the ordinary channel-close
403            // path. A send failure is fatal immediately because no further
404            // receive-side progress can repair it.
405            result = &mut send => {
406                result?;
407                return match recv.await {
408                    Err(Error::ConnectionClosed) => Ok(()),
409                    result => result,
410                };
411            }
412        };
413        // The receive half ended first, so the session is failing or the peer
414        // is gone. It published `Drain::Abrupt` on the way out; the send
415        // driver flushes what it had already committed to the wire and
416        // abandons anything still waiting on credit that can no longer
417        // arrive. Dropping `recv` above also dropped its sender, so nothing
418        // new can be queued behind it either.
419        result.and(send.await)
420    }
421}
422
423/// Drives the receive half of the connection: reassembles inbound fragments,
424/// dispatches requests to the handler, and owns the tear-down that follows
425/// whatever ends the session.
426///
427/// This is the half that runs on the caller's own future rather than a task
428/// of its own, because it is what [`Server::serve`] returns the result of.
429struct RecvDriver<P: Protocol, H> {
430    transport: transport::AnyReceiver,
431    reassembler: fragment::Reassembler,
432    shared: Arc<Shared>,
433    /// Kept alive for the whole run: dropping this and every handler task's
434    /// clone is what closes the send driver's channel and shuts it down.
435    outgoing: mpsc::UnboundedSender<Outgoing<P::Response>>,
436    handler: Arc<H>,
437    /// Fires when a handler has asked to shut down gracefully.
438    shutdown: oneshot::Receiver<()>,
439    /// Tells the send driver how much it still owes; see [`crate::driver`].
440    drain: DrainSignal,
441}
442
443impl<P: Protocol, H> RecvDriver<P, H>
444where
445    H: AsyncFn(CallContext<P>, P::Request) + Send + Sync + 'static,
446{
447    fn new(
448        transport: transport::AnyReceiver,
449        outgoing: mpsc::UnboundedSender<Outgoing<P::Response>>,
450        shutdown: oneshot::Receiver<()>,
451        shared: Arc<Shared>,
452        handler: H,
453        drain: DrainSignal,
454    ) -> Self {
455        let reassembler = fragment::Reassembler::new(shared.limits, Arc::new(outgoing.downgrade()));
456        Self {
457            transport,
458            reassembler,
459            shared,
460            outgoing,
461            handler: Arc::new(handler),
462            shutdown,
463            drain,
464        }
465    }
466
467    /// Applies `max_concurrent_calls` to a call that is arriving or starting
468    /// to arrive.
469    ///
470    /// A concurrent call is one this end has begun receiving and has not yet
471    /// answered, and it passes through two custodians on the way: the
472    /// reassembler holds it while its payload is still fragmented, and
473    /// `outstanding` holds it from dispatch until the response head. The
474    /// limit is on the *sum* — the two counts are disjoint, since a message
475    /// leaves payload phase in the same `accept` call that dispatches it — so
476    /// neither custodian can enforce it alone, and checking them separately
477    /// would admit twice the limit.
478    ///
479    /// `incomplete` is the reassembler's count *including* the call being
480    /// admitted, so callers add one for a call that has already left payload
481    /// phase.
482    fn check_call_admission(&self, id: u64, incomplete: usize) -> Result<(), Error> {
483        let inner = self.shared.inner.lock().unwrap();
484        let duplicate = inner.outstanding.contains_key(&id);
485        let outstanding = inner.outstanding.len();
486        if duplicate {
487            return Err(Error::Protocol(format!("duplicate active request id {id}")));
488        }
489        if outstanding + incomplete > self.shared.limits.max_concurrent_calls {
490            return Err(Error::Protocol("too many concurrent calls".into()));
491        }
492        Ok(())
493    }
494
495    /// Runs until the peer disconnects or the session fails.
496    ///
497    /// A handler asking to shut down does *not* end this — it starts a
498    /// drain. New requests are refused from that point, but the transport
499    /// keeps being read, because the calls already dispatched still have to
500    /// answer and the flow-control credit their responses may be waiting on
501    /// arrives through this half. Once those handlers have finished, this
502    /// driver publishes [`Drain::Graceful`] and keeps reading until the peer
503    /// closes its transport, including after the send driver has emptied its
504    /// scheduler.
505    ///
506    /// Beyond publishing that signal it knows nothing of the send driver.
507    async fn run(mut self) -> Result<(), Error> {
508        let mut tasks = FuturesUnordered::new();
509        // Set when a handler has asked to shut down. From then on the
510        // `shutdown` branch is disarmed (a consumed `oneshot` resolves
511        // immediately and would spin the loop) and new requests are refused.
512        let mut draining = false;
513        let result = 'main: loop {
514            let mut frame = self.transport.recv();
515            // The header/payload reads must not be dropped and restarted
516            // once they've begun: any bytes already consumed from the
517            // transport into their local buffers would otherwise be lost,
518            // desynchronizing the stream. `step` is polled repeatedly by
519            // the inner loop below (never recreated) so that racing it
520            // against `tasks.next()` and `continue`-ing loses no progress.
521            let complete = {
522                let step = async {
523                    let header = fragment::read_fragment_header(&mut frame).await?;
524                    check_header(&header)?;
525                    self.reassembler.accept(header, &mut frame).await
526                };
527                tokio::pin!(step);
528                loop {
529                    tokio::select! {
530                        result = &mut step => break result,
531                        // Handler tasks must keep being polled here: a
532                        // handler reading a request trailer is unblocked by
533                        // the very fragment this read is fetching. Their
534                        // completions are also the trigger for sealing the
535                        // drain, since a drain ends when the last dispatched
536                        // call has answered.
537                        Some(_) = tasks.next(), if !tasks.is_empty() => {
538                            self.drain.seal_if_idle(draining, tasks.is_empty());
539                            continue;
540                        }
541                        _ = &mut self.shutdown, if !draining => {
542                            draining = true;
543                            self.drain.seal_if_idle(draining, tasks.is_empty());
544                            continue;
545                        }
546                    }
547                }
548            };
549            let complete = match complete {
550                Ok(complete) => complete,
551                Err(error) => break 'main Err(error),
552            };
553            let (message, live_trailer) = match complete {
554                Event::None => (None, None),
555                // A request has started arriving. It occupies the same
556                // budget as one already dispatched, so it is admitted on the
557                // same rule, at the earliest point this end knows about it.
558                Event::PayloadIncomplete { id } => {
559                    if let Err(error) =
560                        self.check_call_admission(id, self.reassembler.payload_incomplete())
561                    {
562                        break 'main Err(error);
563                    }
564                    (None, None)
565                }
566                Event::Aborted {
567                    kind: Kind::Request,
568                    ..
569                } => (None, None),
570                Event::Aborted { kind, .. } => {
571                    break 'main Err(Error::Protocol(format!(
572                        "unexpected aborted {kind:?} message"
573                    )));
574                }
575                Event::Message(message) => (Some(message), None),
576                Event::Ack { id, message } => {
577                    let _ = self.outgoing.send(Outgoing::Ack { id });
578                    (message, None)
579                }
580                Event::Trailer {
581                    shared: trailer,
582                    len,
583                    ..
584                } => (None, Some((trailer, len))),
585                Event::Release { id, count } => {
586                    self.shared.session.release(id, count);
587                    (None, None)
588                }
589                Event::Credit { id, count } => {
590                    // Applied here rather than routed through the writer;
591                    // see the client's matching arm.
592                    self.shared.trailer_session.refund(id, count as usize);
593                    (None, None)
594                }
595                Event::PayloadCredit { count } => {
596                    self.shared.payload_budget.credit(count as usize);
597                    (None, None)
598                }
599            };
600            if let Some(Message {
601                kind,
602                id,
603                payload,
604                handles,
605                trailer,
606                charge,
607            }) = message
608            {
609                match kind {
610                    Kind::Request if draining => {
611                        // A drain finishes the calls already dispatched; it
612                        // does not take on new ones. Refusing here rather
613                        // than letting the reassembler reject it keeps the
614                        // decision in one place, and dropping `charge`
615                        // returns the request's payload quota to the peer —
616                        // this end is still reading, so that credit is still
617                        // worth sending.
618                        //
619                        // The trailer is wrapped before being dropped rather
620                        // than dropped as it arrived: `TrailerRecv`'s `Drop`
621                        // is what tells the peer to stop sending, and a
622                        // refused request that left its trailer streaming
623                        // would go on consuming the drain it is not part of.
624                        let _ = self.outgoing.send(Outgoing::Error { id });
625                        drop(trailer.map(TrailerRecv::new));
626                        drop(charge);
627                    }
628                    Kind::Request => {
629                        // This message has already left payload phase, so it
630                        // is no longer in the reassembler's count and has to
631                        // be added back.
632                        if let Err(error) =
633                            self.check_call_admission(id, self.reassembler.payload_incomplete() + 1)
634                        {
635                            break Err(error);
636                        }
637                        let request = match self.shared.decode(&payload, handles, &self.transport) {
638                            Ok(request) => request,
639                            Err(error) => break Err(error),
640                        };
641                        let trailer = trailer.map(TrailerRecv::new);
642                        let handler = self.handler.clone();
643                        let task_shared = self.shared.clone();
644                        let task_outgoing = self.outgoing.clone();
645                        let (abort, registration) = AbortHandle::new_pair();
646                        tasks.push(Abortable::new(
647                            async move {
648                                let context = CallContext {
649                                    id,
650                                    shared: task_shared,
651                                    request_trailer: trailer,
652                                    outgoing: task_outgoing,
653                                    responded: false,
654                                    shutdown_on_respond: false,
655                                    charge: Some(charge),
656                                    marker: PhantomData,
657                                };
658                                handler(context, request).await;
659                            },
660                            registration,
661                        ));
662                        self.shared.inner.lock().unwrap().outstanding.insert(
663                            id,
664                            Cancellation {
665                                signal: None,
666                                abort,
667                            },
668                        );
669                    }
670                    Kind::Cancel => {
671                        let mut state = self.shared.inner.lock().unwrap();
672                        if let Some(signal) = state
673                            .outstanding
674                            .get_mut(&id)
675                            .and_then(|cancel| cancel.signal.take())
676                        {
677                            let _ = signal.send(());
678                        } else if let Some(cancel) = state.outstanding.get(&id) {
679                            cancel.abort.abort();
680                        } else {
681                            let _ = self.outgoing.send(Outgoing::Cancel { id });
682                        }
683                    }
684                    Kind::Discard => {
685                        let _ = self.outgoing.send(Outgoing::PeerDiscarded { id });
686                    }
687                    Kind::Ack => {
688                        if !self.shared.release_escrow(id) {
689                            break Err(Error::Protocol(format!(
690                                "Ack for response {id} with no active escrow"
691                            )));
692                        }
693                    }
694                    _ => {
695                        break Err(Error::Protocol(format!("unexpected {kind:?} frame")));
696                    }
697                }
698            }
699            if let Some((trailer, len)) = live_trailer {
700                let frame = self.transport.recv();
701                // SAFETY: the lease retains the receiver borrow and clears
702                // the erased token before it ends.
703                let lease = unsafe { RecvShared::grant(&trailer, frame, len) };
704                let result = loop {
705                    tokio::select! {
706                        result = RecvShared::wait_fragment(&trailer) => break result,
707                        Some(_) = tasks.next(), if !tasks.is_empty() => {
708                            self.drain.seal_if_idle(draining, tasks.is_empty());
709                            continue;
710                        }
711                        _ = &mut self.shutdown, if !draining => {
712                            draining = true;
713                            self.drain.seal_if_idle(draining, tasks.is_empty());
714                            continue;
715                        }
716                    }
717                };
718                if let Err(error) = result {
719                    break 'main Err(error.into());
720                }
721                lease.complete();
722            }
723        };
724        drop(self.transport);
725        if draining {
726            // A drain was already under way when the transport failed, so the
727            // calls already dispatched still get to finish — their responses
728            // queue up behind the send driver's channel even though most will
729            // no longer reach the peer.
730            while tasks.next().await.is_some() {}
731        }
732        // Reaching here at all means this half is over, so the send driver
733        // must not be left waiting on credit that can now never arrive. This
734        // is deliberately unconditional and deliberately last: it overrides
735        // any `Graceful` already published, including one this very drain
736        // set a moment ago before the transport gave out.
737        self.drain.set(Drain::Abrupt);
738        if draining && matches!(&result, Err(Error::ConnectionClosed)) {
739            Ok(())
740        } else {
741            result
742        }
743    }
744}
745
746/// Drives the send half of the connection: admits queued messages into the
747/// fragment scheduler and advances the scheduler onto the transport.
748///
749/// Runs on [`Server::serve`]'s own future, alongside the receive driver and
750/// the handlers — a response is queued rather than written by the handler
751/// that produced it, so nothing here blocks on anything there.
752struct SendDriver<P: Protocol> {
753    transport: transport::AnySender,
754    outgoing: mpsc::UnboundedReceiver<Outgoing<P::Response>>,
755    shared: Arc<Shared>,
756    scheduler: fragment::Scheduler,
757    /// How much this driver still owes before it may stop; see
758    /// [`crate::driver`].
759    drain: DrainWatch,
760}
761
762impl<P: Protocol> SendDriver<P> {
763    fn new(
764        transport: transport::AnySender,
765        outgoing: mpsc::UnboundedReceiver<Outgoing<P::Response>>,
766        shared: Arc<Shared>,
767        drain: DrainWatch,
768    ) -> Self {
769        let scheduler = fragment::Scheduler::new(&shared.limits, shared.payload_budget.clone());
770        Self {
771            transport,
772            outgoing,
773            shared,
774            scheduler,
775            drain,
776        }
777    }
778
779    /// Runs until the drain signal says this driver owes nothing more.
780    ///
781    /// Three ways that happens, and they differ only in how much counts as
782    /// owed:
783    ///
784    /// * [`Drain::Running`] — the channel closed. Every handle that could
785    ///   queue work is gone, including the receive driver's, so no credit can
786    ///   arrive either; finish what is already started.
787    /// * [`Drain::Graceful`] — shutdown was requested. The receive half is
788    ///   still running, so finish *everything*, quota-blocked sends included,
789    ///   then remain available for control messages until that half ends.
790    /// * [`Drain::Abrupt`] — the receive half is gone. Finish what is
791    ///   already started and abandon the rest.
792    ///
793    /// A committed write is never abandoned in any of them: the scheduler is
794    /// advanced to a fragment boundary before the loop can exit.
795    async fn run(mut self) -> Result<(), Error> {
796        // Holding a clone of `outgoing`'s sender half (the receive driver's,
797        // or a `CallContext`'s) is what represents the ability to still get a
798        // message in, so the channel closing — every clone gone — is one of
799        // the terminal conditions in its own right. It is no longer the only
800        // one: under a graceful drain the receive driver keeps its clone
801        // precisely so it can keep servicing credit, and the drain signal is
802        // what says nothing more will be admitted.
803        let mut closed = false;
804        loop {
805            let mode = self.drain.mode();
806            let done = match mode {
807                Drain::Running => closed && !self.scheduler.has_work(),
808                // Also requires the channel to be drained. A handler queues
809                // its response and *then* completes, and completing is what
810                // seals the drain — so at the instant the signal arrives the
811                // last response may still be sitting in the channel, not yet
812                // admitted to the scheduler, which would leave `has_pending`
813                // reporting nothing to do.
814                // The receive driver retains a sender until its transport
815                // ends. Staying alive until the channel closes preserves the
816                // send transport for rejection and control messages received
817                // after the response drain first becomes quiescent.
818                Drain::Graceful => closed && !self.scheduler.has_pending(),
819                Drain::Abrupt => !self.scheduler.has_work(),
820            };
821            if done {
822                return Ok(());
823            }
824            tokio::select! {
825                message = self.outgoing.recv(), if !closed => {
826                    let Some(message) = message else {
827                        closed = true;
828                        continue;
829                    };
830                    self.admit(message).await?;
831                }
832                // Cancel-safe (a `watch` registration), and re-evaluating the
833                // terminal condition is the whole of the arm — the loop head
834                // above does the work.
835                _ = self.drain.changed() => {}
836                // Not raced against anything — see the matching comment in
837                // client.rs's writer loop. A dropped send future could leave a
838                // committed partial fragment on the transport, or — on
839                // transports whose writes are dispatched to a detached
840                // background task — let an abandoned write complete arbitrarily
841                // later, after the peer has already torn down its end.
842                _ = self.scheduler.ready(), if self.scheduler.has_pending() => {
843                    match self.scheduler.advance(&mut self.transport).await? {
844                        fragment::AdvanceOutcome::None | fragment::AdvanceOutcome::Aborted(_) => {}
845                        #[cfg(target_os = "macos")]
846                        fragment::AdvanceOutcome::Escrow { id, fds, handles_done } => {
847                            self.shared.escrow_sent(id, fds, handles_done);
848                        }
849                    }
850                    // Flush anything sent by the scheduler
851                    let _ = self.transport.flush().await;
852                }
853            }
854        }
855    }
856
857    /// Admits one outgoing item to the fragment scheduler.
858    async fn admit(&mut self, message: Outgoing<P::Response>) -> Result<(), Error> {
859        match message {
860            Outgoing::Response { id, value, trailer } => {
861                let mut ledger = session::Ledger::default();
862                let mut put_handles = session::SessionFrame {
863                    inner: EncodeHandles::new(&self.transport, self.shared.max_handles()),
864                    session: &self.shared.session,
865                    ledger: &mut ledger,
866                };
867                let payload = match encode_payload(&value, &mut put_handles) {
868                    Ok(payload) => payload,
869                    Err(error) => {
870                        drop(put_handles);
871                        // Nothing reached the wire, so undo the gift increments
872                        // rather than letting the ledger's drop commit them.
873                        ledger.rescind();
874                        return Err(error);
875                    }
876                };
877                let handles = self.shared.finish_handles(id, put_handles.inner);
878                self.scheduler
879                    .admit_message(Kind::Response, id, payload, handles, trailer, ledger);
880            }
881            Outgoing::Error { id } => self.scheduler.admit_empty(Kind::Error, id),
882            Outgoing::Cancel { id } => match self.scheduler.try_cancel_active(id) {
883                fragment::AbortOutcome::NotActive => {}
884                fragment::AbortOutcome::Discarded { started, .. } => {
885                    if started {
886                        self.scheduler.admit_abort(id);
887                    }
888                    if !started {
889                        self.shared.discard_unsent_escrow(id);
890                    }
891                }
892            },
893            Outgoing::DiscardTrailer { id } => self.scheduler.admit_empty(Kind::Discard, id),
894            Outgoing::PeerDiscarded { id } => {
895                // The peer will never credit what it just threw away; see the
896                // client's matching arm.
897                self.shared.trailer_session.settle(id);
898                self.scheduler.discard_active_trailer(id);
899            }
900            Outgoing::Ack { id } => self.scheduler.admit_empty(Kind::Ack, id),
901            Outgoing::Release { id, count } => self.scheduler.admit_release(id, count),
902            Outgoing::Credit { id, count } => self.scheduler.admit_credit(id, count),
903            Outgoing::PayloadCredit { count } => self.scheduler.admit_payload_credit(count),
904        }
905        Ok(())
906    }
907}
908
909/// Request-scoped services supplied to a server handler.
910///
911/// A context is not cloneable and must be consumed to send a response.
912pub struct CallContext<P: Protocol> {
913    id: u64,
914    shared: Arc<Shared>,
915    request_trailer: Option<TrailerRecv>,
916    /// A strong sender, so a live handler keeps the writer's channel — and
917    /// with it the connection — open until it has answered.
918    outgoing: mpsc::UnboundedSender<Outgoing<P::Response>>,
919    responded: bool,
920    shutdown_on_respond: bool,
921    /// This request's share of the payload quota, returned to the peer when
922    /// this context is dropped — which is every path a call can end on,
923    /// including a handler that never responds, one aborted by a peer
924    /// cancellation, and one that panics.
925    charge: Option<PayloadCharge>,
926    marker: PhantomData<fn() -> P>,
927}
928
929impl<P: Protocol> CallContext<P> {
930    /// Takes this request's raw-byte trailer, if present.
931    ///
932    /// The returned value implements [`AsyncRead`](tokio::io::AsyncRead).
933    /// Dropping it stops local consumption and immediately tells the peer to
934    /// stop sending, as does responding while the context still holds it.
935    ///
936    /// Taken rather than borrowed, so a handler may keep reading after it
937    /// has responded. Paired with
938    /// [`respond_with_trailer`](Self::respond_with_trailer) that gives a
939    /// duplex byte pipe over one call: each direction is an independent
940    /// stream that ends when its own end says so, and the call itself is
941    /// complete as soon as the response head goes out. Neither direction
942    /// holds a call slot after that, so the pipes are bounded by trailer
943    /// credit rather than by `max_concurrent_calls` — and, as with a socket,
944    /// nothing ties the two halves together: closing one does not close the
945    /// other, and a peer that vanishes is noticed through the transport.
946    pub fn trailer(&mut self) -> Option<TrailerRecv> {
947        self.request_trailer.take()
948    }
949
950    /// Returns this request's raw-byte trailer in manual-credit mode.
951    ///
952    /// The consumer then owes the peer an explicit
953    /// [`TrailerRecv::release`](crate::trailer::TrailerRecv::release) for
954    /// every chunk it finishes with, instead of credit being returned on
955    /// read. Use this when the bytes are being handed somewhere slower than
956    /// this process, so that the peer's send rate follows the real drain
957    /// rate; read [`release`](crate::trailer::TrailerRecv::release) first,
958    /// since manual mode moves a deadlock rule into calling code.
959    ///
960    /// The mode is fixed here rather than switchable afterwards, so a
961    /// trailer cannot be half auto-credited and half not. Taken rather than
962    /// borrowed, exactly as in [`trailer`](Self::trailer).
963    pub fn trailer_manual_credit(&mut self) -> Option<TrailerRecv> {
964        let mut trailer = self.request_trailer.take()?;
965        trailer.set_manual_credit();
966        Some(trailer)
967    }
968
969    /// Sends a response without a trailer and consumes this call context.
970    ///
971    /// A request trailer this context still holds is discarded; one already
972    /// taken by [`trailer`](Self::trailer) is untouched and stays readable.
973    pub fn respond(mut self, response: P::Response) {
974        drop(self.request_trailer.take());
975        self.responded = true;
976        self.shared
977            .inner
978            .lock()
979            .unwrap()
980            .outstanding
981            .remove(&self.id);
982        let _ = self.outgoing.send(Outgoing::Response {
983            id: self.id,
984            value: response,
985            trailer: fragment::Trailer::None,
986        });
987        self.finish_shutdown();
988    }
989
990    /// Sends a response head and returns a writer for its raw-byte trailer.
991    ///
992    /// Call [`TrailerSend::finish`](crate::trailer::TrailerSend::finish), or
993    /// asynchronously shut down the returned writer, to commit the trailer.
994    /// Dropping it without finishing aborts the trailer. A request trailer
995    /// this context still holds is discarded; one already taken by
996    /// [`trailer`](Self::trailer) is untouched, which is what makes the two
997    /// directions a duplex pipe.
998    pub fn respond_with_trailer(mut self, response: P::Response) -> TrailerSend<()> {
999        drop(self.request_trailer.take());
1000        let shared = SendShared::new(
1001            Kind::Response,
1002            self.id,
1003            &self.shared.limits,
1004            self.shared.trailer_session.clone(),
1005        );
1006        self.responded = true;
1007        self.shared
1008            .inner
1009            .lock()
1010            .unwrap()
1011            .outstanding
1012            .remove(&self.id);
1013        let _ = self.outgoing.send(Outgoing::Response {
1014            id: self.id,
1015            value: response,
1016            trailer: fragment::Trailer::Stream(shared.clone()),
1017        });
1018        self.finish_shutdown();
1019        TrailerSend::new(shared, ())
1020    }
1021
1022    /// Returns this request's payload quota to the peer now, rather than when
1023    /// this context is dropped.
1024    ///
1025    /// The quota is charged for the whole call, so a handler that pends for a
1026    /// long time throttles the connection for as long as it pends — which is
1027    /// fine for the small payloads a long-poll usually carries, and is not
1028    /// for a large one. This is the escape hatch: finish with the request,
1029    /// drop whatever you decoded from it, then release. Nothing checks that
1030    /// you did the first two, and releasing while still holding the request's
1031    /// data merely makes the peer's accounting optimistic.
1032    ///
1033    /// Idempotent, and never required — dropping the context releases just
1034    /// the same.
1035    pub fn release_payload(&mut self) {
1036        self.charge = None;
1037    }
1038
1039    /// Requests graceful shutdown after this handler sends its response.
1040    ///
1041    /// The server stops accepting requests once this context is consumed by
1042    /// [`respond`](Self::respond) or [`respond_with_trailer`](Self::respond_with_trailer),
1043    /// then lets already-running handlers finish.
1044    pub fn shutdown(&mut self) {
1045        self.shutdown_on_respond = true;
1046    }
1047
1048    fn finish_shutdown(&self) {
1049        if self.shutdown_on_respond
1050            && let Some(shutdown) = self.shared.inner.lock().unwrap().shutdown.take()
1051        {
1052            let _ = shutdown.send(());
1053        }
1054    }
1055
1056    /// Runs an operation that can observe request cancellation without dropping
1057    /// the handler itself.
1058    ///
1059    /// If the peer cancels while `operation` is running, its future is dropped
1060    /// and this method returns [`RequestCancelled`]. The handler regains the
1061    /// context and may perform cleanup or send an application-level response.
1062    /// Only one cancellation guard may be active at a time; nesting guards
1063    /// panics.
1064    pub async fn cancel_guard<T, F>(&mut self, operation: F) -> Result<T, RequestCancelled>
1065    where
1066        F: AsyncFnOnce(&mut CallContext<P>) -> T,
1067    {
1068        struct Reset {
1069            id: u64,
1070            shared: Arc<Shared>,
1071        }
1072        impl Drop for Reset {
1073            fn drop(&mut self) {
1074                if let Some(cancel) = self
1075                    .shared
1076                    .inner
1077                    .lock()
1078                    .unwrap()
1079                    .outstanding
1080                    .get_mut(&self.id)
1081                {
1082                    cancel.signal = None;
1083                }
1084            }
1085        }
1086        let (signal, cancelled) = oneshot::channel();
1087        {
1088            let mut inner = self.shared.inner.lock().unwrap();
1089            let cancel = inner
1090                .outstanding
1091                .get_mut(&self.id)
1092                .expect("call context is not registered");
1093            assert!(cancel.signal.is_none(), "cancel guard is already active");
1094            cancel.signal = Some(signal);
1095        }
1096        let _reset = Reset {
1097            id: self.id,
1098            shared: self.shared.clone(),
1099        };
1100        let future = operation(&mut *self);
1101        tokio::pin!(future);
1102        tokio::select! {
1103            value = &mut future => Ok(value),
1104            result = cancelled => match result { Ok(()) => Err(RequestCancelled), Err(_) => Ok(future.await) },
1105        }
1106    }
1107
1108    /// Register an opqaue handle.
1109    ///
1110    /// The underlying resource will be automatically dropped when both of
1111    /// the following hold:
1112    /// - It is no longer referenced by the client, or the server has unregistered it
1113    /// - All oustanding [`OpaqueGuard`]s have been dropped
1114    ///
1115    /// # Panics
1116    ///
1117    /// If a different concrete type has already been registered under
1118    /// `T::Marker` on this session.
1119    pub fn register<T: OpaqueResource>(&self, value: T) -> Gift<T::Marker> {
1120        self.shared.session.register(value)
1121    }
1122
1123    /// Acquires a guard an opaque handle citation.
1124    ///
1125    /// Returns [`InvalidOpaque`] if the resource was unregistered while the peer
1126    /// still held a reference to it.
1127    ///
1128    /// # Panics
1129    ///
1130    /// If the handle was minted by a different session.
1131    pub fn acquire<T: OpaqueResource>(
1132        &self,
1133        value: Cite<T::Marker>,
1134    ) -> Result<OpaqueGuard<T>, InvalidOpaque> {
1135        self.shared.session.acquire(value)
1136    }
1137
1138    /// Unregisters an opaque handle
1139    ///
1140    /// If no outstanding [`OpaqueGuard`]s existed, the resource is returned
1141    /// directly; otherwise, `None` is returned and the resource will be
1142    /// dropped with the last `OpaqueGuard`.  In either case, subsequent
1143    /// uses of [`Self::acquire`] will fail.  If the handle has already
1144    /// been unregistered, returns [`InvalidOpaque`].
1145    ///
1146    /// # Panics
1147    ///
1148    /// If the handle was minted by a different session.
1149    pub fn unregister<T: OpaqueResource>(
1150        &self,
1151        value: Cite<T::Marker>,
1152    ) -> Result<Option<T>, InvalidOpaque> {
1153        self.shared.session.unregister::<T>(value)
1154    }
1155
1156    /// Unregisters an opaque handle if not busy
1157    ///
1158    /// The recoverable counterpart of [`unregister`](Self::unregister): if
1159    /// outstanding [`OpaqueGuard`]s exist, the handle is not unregistered,
1160    /// which is signaled by a `None` return value.
1161    ///
1162    /// # Panics
1163    ///
1164    /// If the handle was minted by a different session, as with
1165    /// [`acquire`](Self::acquire).
1166    pub fn try_unregister<T: OpaqueResource>(
1167        &self,
1168        value: Cite<T::Marker>,
1169    ) -> Result<Option<T>, InvalidOpaque> {
1170        self.shared.session.try_unregister::<T>(value)
1171    }
1172}
1173
1174impl<P: Protocol> Drop for CallContext<P> {
1175    fn drop(&mut self) {
1176        if !self.responded {
1177            self.shared
1178                .inner
1179                .lock()
1180                .unwrap()
1181                .outstanding
1182                .remove(&self.id);
1183            let _ = self.outgoing.send(Outgoing::Error { id: self.id });
1184        }
1185    }
1186}
1187
1188/// Indicates that a guarded operation was interrupted by request cancellation.
1189#[derive(Clone, Copy, Debug, thiserror::Error)]
1190#[error("request cancelled")]
1191pub struct RequestCancelled;
1192
1193#[cfg(test)]
1194mod tests {
1195    use std::{future, time::Duration};
1196
1197    use bytes::Bytes;
1198    use tokio::task::JoinHandle;
1199
1200    use super::*;
1201
1202    const APP_PROTOCOL: (&str, &[u16]) = ("test", &[1]);
1203    const FRAGMENT_SIZE: usize = 512;
1204
1205    struct Test;
1206
1207    impl Protocol for Test {
1208        type Request = u32;
1209        type Response = u32;
1210    }
1211
1212    /// A peer that speaks the wire format directly, so a test can send what
1213    /// a real client's own accounting would never let it send.
1214    struct Peer {
1215        transport: transport::AnySender,
1216        scheduler: fragment::Scheduler,
1217        /// Unread: nothing these tests do makes the server say anything back.
1218        _receiver: transport::AnyReceiver,
1219    }
1220
1221    impl Peer {
1222        /// Queues a whole request, small enough to arrive in one fragment
1223        /// and be dispatched the moment it lands.
1224        fn request(&mut self, id: u64, value: u32) {
1225            self.admit(Kind::Request, id, postcard::to_stdvec(&value).unwrap());
1226        }
1227
1228        /// Queues a request far too large for one fragment, so it stays in
1229        /// the server's reassembler for as long as the test declines to send
1230        /// the rest of it. Its bytes are never decoded, so they need not be
1231        /// a valid request.
1232        fn partial_request(&mut self, id: u64) {
1233            self.admit(Kind::Request, id, vec![0; 64 * FRAGMENT_SIZE]);
1234        }
1235
1236        /// Queues a fragment of a kind only a server may send.
1237        fn response(&mut self, id: u64) {
1238            self.admit(Kind::Response, id, postcard::to_stdvec(&0u32).unwrap());
1239        }
1240
1241        fn admit(&mut self, kind: Kind, id: u64, payload: Vec<u8>) {
1242            self.scheduler.admit_message(
1243                kind,
1244                id,
1245                Bytes::from(payload),
1246                Default::default(),
1247                fragment::Trailer::None,
1248                session::Ledger::default(),
1249            );
1250        }
1251
1252        /// Writes `count` fragments. The scheduler round-robins, so a count
1253        /// equal to the number of queued messages puts one fragment of each
1254        /// on the wire.
1255        async fn send(&mut self, count: usize) {
1256            for _ in 0..count {
1257                self.scheduler.advance(&mut self.transport).await.unwrap();
1258            }
1259        }
1260    }
1261
1262    fn endpoint_pair() -> (
1263        (transport::AnySender, transport::AnyReceiver),
1264        (transport::AnySender, transport::AnyReceiver),
1265    ) {
1266        let (a_write, a_read) = tokio::io::duplex(4096);
1267        let (b_write, b_read) = tokio::io::duplex(4096);
1268        let (a_sender, _unused) = transport::generic_duplex(a_write);
1269        let (_unused, a_receiver) = transport::generic_duplex(b_read);
1270        let (b_sender, _unused) = transport::generic_duplex(b_write);
1271        let (_unused, b_receiver) = transport::generic_duplex(a_read);
1272        (
1273            (
1274                transport::AnySender::Generic(a_sender),
1275                transport::AnyReceiver::Generic(a_receiver),
1276            ),
1277            (
1278                transport::AnySender::Generic(b_sender),
1279                transport::AnyReceiver::Generic(b_receiver),
1280            ),
1281        )
1282    }
1283
1284    /// Negotiates a real session, serves one end of it, and hands back the
1285    /// wire-level peer on the other.
1286    ///
1287    /// The handler never responds, so every call it is given stays
1288    /// outstanding until the session ends under it — which is what lets a
1289    /// test hold calls in one custodian while it fills the other.
1290    async fn hostile_session(
1291        max_concurrent_calls: usize,
1292    ) -> (
1293        Peer,
1294        JoinHandle<Result<(), Error>>,
1295        mpsc::UnboundedReceiver<()>,
1296    ) {
1297        hostile_session_with(Limits {
1298            max_concurrent_calls,
1299            max_fragment_size: FRAGMENT_SIZE,
1300            ..Limits::default()
1301        })
1302        .await
1303    }
1304
1305    async fn hostile_session_with(
1306        limits: Limits,
1307    ) -> (
1308        Peer,
1309        JoinHandle<Result<(), Error>>,
1310        mpsc::UnboundedReceiver<()>,
1311    ) {
1312        let ((mut peer_sender, mut peer_receiver), (mut server_sender, mut server_receiver)) =
1313            endpoint_pair();
1314        let (peer, server) = tokio::join!(
1315            fragment::negotiate(
1316                &mut peer_sender,
1317                &mut peer_receiver,
1318                &limits,
1319                APP_PROTOCOL,
1320                None
1321            ),
1322            fragment::negotiate(
1323                &mut server_sender,
1324                &mut server_receiver,
1325                &limits,
1326                APP_PROTOCOL,
1327                None
1328            ),
1329        );
1330        let peer_limits = peer.unwrap().limits;
1331        let server =
1332            Server::<Test>::from_transport(server_sender, server_receiver, server.unwrap().limits);
1333        let (dispatched_tx, dispatched) = mpsc::unbounded_channel();
1334        let serve = tokio::spawn(server.serve(async move |_: CallContext<Test>, _: u32| {
1335            let _ = dispatched_tx.send(());
1336            future::pending::<()>().await
1337        }));
1338        (
1339            Peer {
1340                transport: peer_sender,
1341                // An unbounded budget, so the peer can send what its
1342                // negotiated quota would have stopped it sending. That is
1343                // the whole point of this harness: the server's checks are
1344                // backstops against a peer that ignores what it agreed to,
1345                // and a peer that honoured it could never reach them.
1346                scheduler: fragment::Scheduler::new(
1347                    &peer_limits,
1348                    Arc::new(PayloadBudget::new(usize::MAX)),
1349                ),
1350                _receiver: peer_receiver,
1351            },
1352            serve,
1353            dispatched,
1354        )
1355    }
1356
1357    /// A server that fails to notice a violation wedges rather than fails,
1358    /// and which await it wedges on depends on the violation, so the bound
1359    /// goes around the whole test.
1360    async fn bounded<F: Future>(test: F) -> F::Output {
1361        tokio::time::timeout(Duration::from_secs(5), test)
1362            .await
1363            .expect("the server should have refused the peer by now")
1364    }
1365
1366    /// A call that has only started arriving is charged to the same budget
1367    /// as one already dispatched. The reassembler counting it separately
1368    /// would let a peer hold twice the limit — and twice the reassembly
1369    /// memory the limit exists to bound.
1370    ///
1371    /// Filling the budget exactly, and seeing the second call dispatched
1372    /// anyway, is also what pins the comparison at `>` rather than `>=`.
1373    #[tokio::test]
1374    async fn a_call_still_arriving_counts_against_dispatched_ones() {
1375        bounded(async {
1376            let (mut peer, serve, mut dispatched) = hostile_session(2).await;
1377            peer.request(1, 7);
1378            peer.send(1).await;
1379            dispatched.recv().await.unwrap();
1380            peer.request(2, 8);
1381            peer.send(1).await;
1382            dispatched.recv().await.unwrap();
1383
1384            // Two of two are dispatched and unanswered, so a third is over
1385            // the limit from its very first fragment.
1386            peer.partial_request(3);
1387            peer.send(1).await;
1388
1389            assert!(matches!(
1390                serve.await.unwrap(),
1391                Err(Error::Protocol(message)) if message == "too many concurrent calls"
1392            ));
1393        })
1394        .await;
1395    }
1396
1397    /// The mirror image: a dispatched call is charged to the same budget as
1398    /// one still arriving, so the check at dispatch has to add the
1399    /// reassembler's count to its own.
1400    #[tokio::test]
1401    async fn a_dispatched_call_counts_against_ones_still_arriving() {
1402        bounded(async {
1403            let (mut peer, serve, mut dispatched) = hostile_session(2).await;
1404            peer.request(1, 7);
1405            peer.send(1).await;
1406            dispatched.recv().await.unwrap();
1407
1408            // Two of two: one dispatched and unanswered, one still arriving.
1409            peer.partial_request(2);
1410            peer.send(1).await;
1411
1412            peer.request(3, 9);
1413            peer.send(2).await;
1414
1415            assert!(matches!(
1416                serve.await.unwrap(),
1417                Err(Error::Protocol(message)) if message == "too many concurrent calls"
1418            ));
1419        })
1420        .await;
1421    }
1422
1423    /// The arriving call is already in the count, so a limit of zero admits
1424    /// nothing at all.
1425    #[tokio::test]
1426    async fn a_zero_call_limit_refuses_the_first_request() {
1427        bounded(async {
1428            let (mut peer, serve, _dispatched) = hostile_session(0).await;
1429            peer.request(1, 7);
1430            peer.send(1).await;
1431            assert!(matches!(
1432                serve.await.unwrap(),
1433                Err(Error::Protocol(message)) if message == "too many concurrent calls"
1434            ));
1435        })
1436        .await;
1437    }
1438
1439    #[tokio::test]
1440    async fn a_request_reusing_a_live_call_id_is_refused() {
1441        bounded(async {
1442            let (mut peer, serve, mut dispatched) = hostile_session(4).await;
1443            peer.request(1, 7);
1444            peer.send(1).await;
1445            dispatched.recv().await.unwrap();
1446            peer.request(1, 8);
1447            peer.send(1).await;
1448            assert!(matches!(
1449                serve.await.unwrap(),
1450                Err(Error::Protocol(message)) if message == "duplicate active request id 1"
1451            ));
1452        })
1453        .await;
1454    }
1455
1456    /// The gate in front of the reassembler: nobody calls a client, so a
1457    /// response arriving here answers nothing.
1458    #[tokio::test]
1459    async fn a_response_from_the_peer_is_refused() {
1460        bounded(async {
1461            let (mut peer, serve, _dispatched) = hostile_session(4).await;
1462            peer.response(1);
1463            peer.send(1).await;
1464            assert!(matches!(
1465                serve.await.unwrap(),
1466                Err(Error::Protocol(message)) if message == "server received a Response fragment"
1467            ));
1468        })
1469        .await;
1470    }
1471
1472    /// The attack `max_outstanding_payload` exists to close: open many
1473    /// messages and send one fragment of each, and `max_concurrent_calls`
1474    /// alone admits every one of them — `max_payload_size` bounds each
1475    /// message, and nothing bounds the sum. Here the call count is deliberately
1476    /// generous, so the only thing that can refuse this is the byte quota.
1477    #[tokio::test]
1478    async fn a_peer_that_ignores_its_payload_quota_is_refused() {
1479        bounded(async {
1480            let (mut peer, serve, _dispatched) = hostile_session_with(Limits {
1481                max_concurrent_calls: 64,
1482                max_fragment_size: FRAGMENT_SIZE,
1483                // Both, because negotiation raises the quota to at least the
1484                // per-message cap: a pool that could not carry one legal
1485                // message would be a configuration with no legal traffic.
1486                max_payload_size: 4 * FRAGMENT_SIZE,
1487                max_outstanding_payload: 4 * FRAGMENT_SIZE,
1488                ..Limits::default()
1489            })
1490            .await;
1491
1492            for id in 1..=16 {
1493                peer.partial_request(id);
1494            }
1495            // One fragment of each, round-robin, until the sum of the
1496            // reassembly buffers passes the quota. Driven from its own task
1497            // because the server stops reading the moment it objects, and a
1498            // peer writing into a full pipe would otherwise block forever
1499            // instead of letting the assertion below run.
1500            let sending = tokio::spawn(async move { peer.send(16).await });
1501
1502            assert!(matches!(
1503                serve.await.unwrap(),
1504                Err(Error::Protocol(message)) if message.contains("session payload quota")
1505            ));
1506            sending.abort();
1507        })
1508        .await;
1509    }
1510}