Skip to main content

datum_net/
udp.rs

1//! UDP datagram sources, sinks, and flows.
2//!
3//! [`TokioUdp`] mirrors Akka/Pekko's connectionless UDP shape on top of
4//! `tokio::net::UdpSocket`: unconnected sockets emit [`Datagram`] values with
5//! the payload and remote peer, send sinks write one upstream datagram with one
6//! `send_to`, and connected sockets exchange plain `Vec<u8>` payloads with a
7//! single peer.
8//!
9//! UDP has no reliable delivery and no end-to-end flow control. Datum keeps the
10//! receive side bounded; when the configured in-process buffer is full,
11//! additional datagrams are dropped instead of being buffered indefinitely. The
12//! operating system may also drop datagrams when its socket receive buffer
13//! fills. Applications that require reliable delivery must add their own
14//! acknowledgements, retries, sequencing, and loss handling above UDP.
15
16use crate::async_carrier::{self, AsyncCommandSender, DemandBatcher};
17use datum::{Flow, Keep, NotUsed, Sink, Source, StreamCompletion, StreamError, StreamResult};
18use std::net::SocketAddr;
19use std::sync::{Arc, Mutex, mpsc as std_mpsc};
20use tokio::net::{ToSocketAddrs, UdpSocket};
21use tokio::runtime::Handle;
22use tokio::sync::mpsc as tokio_mpsc;
23use tokio::task::JoinHandle;
24
25/// Default maximum bytes read for a single UDP datagram.
26///
27/// A larger datagram is truncated by the operating system/socket read into this
28/// buffer size. The default is intentionally the full 16-bit UDP packet range
29/// so ordinary IPv4/IPv6 UDP payloads are not split by Datum.
30pub const DEFAULT_MAX_DATAGRAM_SIZE: usize = 65_536;
31
32/// Default number of received datagrams Datum buffers in-process per socket.
33pub const DEFAULT_RECEIVE_BUFFER: usize = 64;
34
35/// One unconnected UDP datagram.
36#[derive(Debug, Clone, PartialEq, Eq)]
37pub struct Datagram {
38    /// The datagram payload.
39    pub payload: Vec<u8>,
40    /// The remote peer address that sent the payload or should receive it.
41    pub remote: SocketAddr,
42}
43
44impl Datagram {
45    /// Creates a datagram with `payload` and `remote`.
46    #[must_use]
47    pub fn new(payload: impl Into<Vec<u8>>, remote: SocketAddr) -> Self {
48        Self {
49            payload: payload.into(),
50            remote,
51        }
52    }
53
54    /// Returns the payload bytes.
55    #[must_use]
56    pub fn payload(&self) -> &[u8] {
57        &self.payload
58    }
59
60    /// Returns the remote peer address.
61    #[must_use]
62    pub fn remote(&self) -> SocketAddr {
63        self.remote
64    }
65
66    /// Splits the datagram into payload and remote address.
67    #[must_use]
68    pub fn into_parts(self) -> (Vec<u8>, SocketAddr) {
69        (self.payload, self.remote)
70    }
71
72    /// Returns the payload bytes, consuming the datagram.
73    #[must_use]
74    pub fn into_payload(self) -> Vec<u8> {
75        self.payload
76    }
77}
78
79/// A materialized UDP socket binding.
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub struct UdpBinding {
82    pub local_addr: SocketAddr,
83}
84
85impl UdpBinding {
86    #[must_use]
87    pub fn local_addr(&self) -> SocketAddr {
88        self.local_addr
89    }
90}
91
92/// A materialized connected UDP socket.
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub struct UdpConnection {
95    pub local_addr: SocketAddr,
96    pub remote_addr: SocketAddr,
97}
98
99impl UdpConnection {
100    #[must_use]
101    pub fn local_addr(&self) -> SocketAddr {
102        self.local_addr
103    }
104
105    #[must_use]
106    pub fn remote_addr(&self) -> SocketAddr {
107        self.remote_addr
108    }
109}
110
111/// UDP stream entry points backed by `tokio::net::UdpSocket`.
112pub struct TokioUdp;
113
114/// Alias for [`TokioUdp`].
115pub type Udp = TokioUdp;
116
117enum ReceiveResponse<T> {
118    Item(T),
119    Error(StreamError),
120}
121
122enum QueueOutcome {
123    Queued,
124    Dropped,
125    Closed,
126}
127
128struct ReceiveResource<T> {
129    receiver: std_mpsc::Receiver<ReceiveResponse<T>>,
130    carrier: UdpCarrier<T>,
131    demand: DemandBatcher,
132}
133
134impl<T> Drop for ReceiveResource<T> {
135    fn drop(&mut self) {
136        self.carrier.close_read();
137    }
138}
139
140enum UdpCarrierCommand<T> {
141    Demand(usize),
142    SendOne(T),
143    SendBatch(Vec<T>),
144    CloseRead,
145    CloseWrite {
146        ack: std_mpsc::Sender<StreamResult<()>>,
147    },
148}
149
150#[derive(Clone)]
151struct UdpCarrier<T> {
152    inner: Arc<UdpCarrierInner<T>>,
153}
154
155struct UdpCarrierInner<T> {
156    commands: AsyncCommandSender<UdpCarrierCommand<T>>,
157    send_errors: Mutex<std_mpsc::Receiver<StreamError>>,
158    task: Mutex<Option<JoinHandle<()>>>,
159}
160
161impl<T> Drop for UdpCarrierInner<T> {
162    fn drop(&mut self) {
163        if let Some(task) = self
164            .task
165            .lock()
166            .unwrap_or_else(|poison| poison.into_inner())
167            .take()
168        {
169            task.abort();
170        }
171    }
172}
173
174impl<T> UdpCarrier<T> {
175    fn close_read(&self) {
176        let _ = self.inner.commands.try_send(UdpCarrierCommand::CloseRead);
177    }
178}
179
180impl<T> UdpCarrier<T>
181where
182    T: Send + 'static,
183{
184    fn request_demand(&self, demand: usize) -> StreamResult<()> {
185        self.inner
186            .commands
187            .send_or_blocking(UdpCarrierCommand::Demand(demand))
188    }
189
190    fn send_items(&self, items: Vec<T>) -> StreamResult<()> {
191        self.inner
192            .commands
193            .send_or_blocking(UdpCarrierCommand::SendBatch(items))
194    }
195
196    fn send_one(&self, item: T) -> StreamResult<()> {
197        self.inner
198            .commands
199            .send_or_blocking(UdpCarrierCommand::SendOne(item))
200    }
201
202    fn close_write(&self) -> StreamResult<()> {
203        self.check_send_error()?;
204        let (ack_sender, ack_receiver) = std_mpsc::channel();
205        self.inner
206            .commands
207            .send_or_blocking(UdpCarrierCommand::CloseWrite { ack: ack_sender })?;
208        match ack_receiver.recv() {
209            Ok(result) => result,
210            Err(_) => Err(abrupt_termination()),
211        }?;
212        self.check_send_error()
213    }
214
215    fn check_send_error(&self) -> StreamResult<()> {
216        match self
217            .inner
218            .send_errors
219            .lock()
220            .expect("UDP carrier send error receiver poisoned")
221            .try_recv()
222        {
223            Ok(error) => Err(error),
224            Err(std_mpsc::TryRecvError::Empty) | Err(std_mpsc::TryRecvError::Disconnected) => {
225                Ok(())
226            }
227        }
228    }
229}
230
231struct SendResource<T> {
232    carrier: UdpCarrier<T>,
233    pending: Vec<T>,
234    batch_size: usize,
235}
236
237type UdpCarrierParts<T> = (
238    UdpCarrier<T>,
239    Option<std_mpsc::Receiver<ReceiveResponse<T>>>,
240);
241
242fn io_error(error: std::io::Error) -> StreamError {
243    StreamError::Failed(error.to_string())
244}
245
246fn abrupt_termination() -> StreamError {
247    StreamError::AbruptTermination
248}
249
250impl TokioUdp {
251    /// Binds an unconnected UDP socket and emits received datagrams.
252    ///
253    /// The socket is bound when the source is materialized and first pulled.
254    /// Each successful `recv_from` produces exactly one [`Datagram`], preserving
255    /// datagram boundaries. Datum buffers at most `receive_buffer` datagrams in
256    /// process; when that buffer is full, newly received datagrams are dropped.
257    /// UDP itself may also drop datagrams before Datum sees them.
258    #[must_use]
259    pub fn bind<A>(
260        addr: A,
261        max_datagram_size: usize,
262        receive_buffer: usize,
263    ) -> Source<Datagram, StreamCompletion<UdpBinding>>
264    where
265        A: ToSocketAddrs + Clone + Send + Sync + 'static,
266    {
267        assert!(
268            max_datagram_size > 0,
269            "maximum datagram size must be greater than zero"
270        );
271        assert!(
272            receive_buffer > 0,
273            "receive buffer must be greater than zero"
274        );
275        Source::lazy_future_source(move || {
276            let addr = addr.clone();
277            async move {
278                let handle = Handle::current();
279                let socket = UdpSocket::bind(addr).await.map_err(io_error)?;
280                let local_addr = socket.local_addr().map_err(io_error)?;
281                Ok(datagram_source_from_socket(
282                    socket,
283                    local_addr,
284                    handle,
285                    max_datagram_size,
286                    receive_buffer,
287                ))
288            }
289        })
290    }
291
292    /// Binds an unconnected UDP socket using the default datagram size and
293    /// receive buffer.
294    #[must_use]
295    pub fn bind_default<A>(addr: A) -> Source<Datagram, StreamCompletion<UdpBinding>>
296    where
297        A: ToSocketAddrs + Clone + Send + Sync + 'static,
298    {
299        Self::bind(addr, DEFAULT_MAX_DATAGRAM_SIZE, DEFAULT_RECEIVE_BUFFER)
300    }
301
302    /// Creates a sink that binds `local_addr` and sends each upstream datagram
303    /// to its [`Datagram::remote`] with one `send_to` call.
304    ///
305    /// A send failure fails the stream with [`StreamError`]. Successful sends
306    /// only mean the datagram was handed to the operating system; UDP provides
307    /// no delivery acknowledgement.
308    #[must_use]
309    pub fn send_sink<A>(local_addr: A) -> Sink<Datagram, StreamCompletion<NotUsed>>
310    where
311        A: ToSocketAddrs + Clone + Send + Sync + 'static,
312    {
313        Flow::<Datagram, NotUsed>::future_flow(move || {
314            let local_addr = local_addr.clone();
315            async move {
316                let handle = Handle::current();
317                let socket = UdpSocket::bind(local_addr).await.map_err(io_error)?;
318                let carrier = start_datagram_carrier(
319                    socket,
320                    handle,
321                    DEFAULT_MAX_DATAGRAM_SIZE,
322                    1,
323                    false,
324                    true,
325                );
326                Ok(datagram_send_flow_from_carrier(carrier.0, 1))
327            }
328        })
329        .to_mat(Sink::ignore(), Keep::right)
330    }
331
332    /// Binds a UDP socket as a bidirectional datagram flow.
333    ///
334    /// Network datagrams are emitted downstream as [`Datagram`] values.
335    /// Upstream datagrams are sent through the same socket to their `remote`
336    /// addresses. This mirrors the Pekko Connectors `bindFlow` echo-server
337    /// shape while preserving UDP's lossy, bounded-buffer semantics.
338    #[must_use]
339    pub fn bind_flow<A>(
340        addr: A,
341        max_datagram_size: usize,
342        receive_buffer: usize,
343    ) -> Flow<Datagram, Datagram, StreamCompletion<UdpBinding>>
344    where
345        A: ToSocketAddrs + Clone + Send + Sync + 'static,
346    {
347        assert!(
348            max_datagram_size > 0,
349            "maximum datagram size must be greater than zero"
350        );
351        assert!(
352            receive_buffer > 0,
353            "receive buffer must be greater than zero"
354        );
355        Flow::<Datagram, Datagram>::future_flow(move || {
356            let addr = addr.clone();
357            async move {
358                let handle = Handle::current();
359                let socket = UdpSocket::bind(addr).await.map_err(io_error)?;
360                let local_addr = socket.local_addr().map_err(io_error)?;
361                let (carrier, receiver) = start_datagram_carrier(
362                    socket,
363                    handle,
364                    max_datagram_size,
365                    receive_buffer,
366                    true,
367                    true,
368                );
369                let sink = datagram_send_flow_from_carrier(carrier.clone(), 1)
370                    .to_mat(Sink::ignore(), Keep::right);
371                let source = datagram_source_from_carrier(
372                    carrier,
373                    receiver.expect("UDP bind_flow receiver exists"),
374                    local_addr,
375                    receive_buffer,
376                );
377                Ok(Flow::from_sink_and_source(sink, source)
378                    .map_materialized_value(move |_| UdpBinding { local_addr }))
379            }
380        })
381    }
382
383    /// Binds a UDP bidirectional flow using default datagram size and receive
384    /// buffer.
385    #[must_use]
386    pub fn bind_flow_default<A>(addr: A) -> Flow<Datagram, Datagram, StreamCompletion<UdpBinding>>
387    where
388        A: ToSocketAddrs + Clone + Send + Sync + 'static,
389    {
390        Self::bind_flow(addr, DEFAULT_MAX_DATAGRAM_SIZE, DEFAULT_RECEIVE_BUFFER)
391    }
392
393    /// Binds a UDP socket, connects it to `peer`, and exchanges byte payloads.
394    ///
395    /// Connected UDP still uses datagrams and still provides no reliability.
396    /// The connection only fixes the peer used by `send`/`recv` and lets the OS
397    /// filter datagrams from other remotes. One upstream `Vec<u8>` maps to one
398    /// UDP send and one socket `recv` maps to one downstream `Vec<u8>`.
399    #[must_use]
400    pub fn connect<A, P>(
401        local_addr: A,
402        peer: P,
403        max_datagram_size: usize,
404        receive_buffer: usize,
405    ) -> Flow<Vec<u8>, Vec<u8>, StreamCompletion<UdpConnection>>
406    where
407        A: ToSocketAddrs + Clone + Send + Sync + 'static,
408        P: ToSocketAddrs + Clone + Send + Sync + 'static,
409    {
410        assert!(
411            max_datagram_size > 0,
412            "maximum datagram size must be greater than zero"
413        );
414        assert!(
415            receive_buffer > 0,
416            "receive buffer must be greater than zero"
417        );
418        Flow::<Vec<u8>, Vec<u8>>::future_flow(move || {
419            let local_addr = local_addr.clone();
420            let peer = peer.clone();
421            async move {
422                let handle = Handle::current();
423                let socket = UdpSocket::bind(local_addr).await.map_err(io_error)?;
424                socket.connect(peer).await.map_err(io_error)?;
425                let connection = UdpConnection {
426                    local_addr: socket.local_addr().map_err(io_error)?,
427                    remote_addr: socket.peer_addr().map_err(io_error)?,
428                };
429                let (carrier, receiver) = start_connected_carrier(
430                    socket,
431                    handle,
432                    max_datagram_size,
433                    receive_buffer,
434                    true,
435                    true,
436                );
437                let sink = connected_send_flow_from_carrier(carrier.clone(), 1)
438                    .to_mat(Sink::ignore(), Keep::right);
439                let source = connected_source_from_carrier(
440                    carrier,
441                    receiver.expect("connected UDP receiver exists"),
442                    receive_buffer,
443                );
444                Ok(Flow::from_sink_and_source(sink, source)
445                    .map_materialized_value(move |_| connection))
446            }
447        })
448    }
449
450    /// Creates a connected UDP flow using default datagram size and receive
451    /// buffer.
452    #[must_use]
453    pub fn connect_default<A, P>(
454        local_addr: A,
455        peer: P,
456    ) -> Flow<Vec<u8>, Vec<u8>, StreamCompletion<UdpConnection>>
457    where
458        A: ToSocketAddrs + Clone + Send + Sync + 'static,
459        P: ToSocketAddrs + Clone + Send + Sync + 'static,
460    {
461        Self::connect(
462            local_addr,
463            peer,
464            DEFAULT_MAX_DATAGRAM_SIZE,
465            DEFAULT_RECEIVE_BUFFER,
466        )
467    }
468}
469
470fn datagram_source_from_socket(
471    socket: UdpSocket,
472    local_addr: SocketAddr,
473    handle: Handle,
474    max_datagram_size: usize,
475    receive_buffer: usize,
476) -> Source<Datagram, UdpBinding> {
477    let (carrier, receiver) = start_datagram_carrier(
478        socket,
479        handle,
480        max_datagram_size,
481        receive_buffer,
482        true,
483        false,
484    );
485    datagram_source_from_carrier(
486        carrier,
487        receiver.expect("UDP bind receiver exists"),
488        local_addr,
489        receive_buffer,
490    )
491}
492
493fn datagram_source_from_carrier(
494    carrier: UdpCarrier<Datagram>,
495    receiver: std_mpsc::Receiver<ReceiveResponse<Datagram>>,
496    local_addr: SocketAddr,
497    receive_buffer: usize,
498) -> Source<Datagram, UdpBinding> {
499    let receiver = Arc::new(Mutex::new(Some(receiver)));
500    Source::unfold_resource(
501        move || {
502            let receiver = receiver
503                .lock()
504                .expect("UDP receive resource receiver poisoned")
505                .take()
506                .ok_or_else(|| StreamError::Failed("UDP receive source already used".to_owned()))?;
507            let demand = DemandBatcher::new(receive_buffer);
508            carrier.request_demand(demand.initial())?;
509            Ok(ReceiveResource {
510                receiver,
511                carrier: carrier.clone(),
512                demand,
513            })
514        },
515        receive_next_item,
516        close_receive_resource,
517    )
518    .map_materialized_value(move |_| UdpBinding { local_addr })
519}
520
521fn connected_source_from_carrier(
522    carrier: UdpCarrier<Vec<u8>>,
523    receiver: std_mpsc::Receiver<ReceiveResponse<Vec<u8>>>,
524    receive_buffer: usize,
525) -> Source<Vec<u8>, NotUsed> {
526    let receiver = Arc::new(Mutex::new(Some(receiver)));
527    Source::unfold_resource(
528        move || {
529            let receiver = receiver
530                .lock()
531                .expect("connected UDP receive resource receiver poisoned")
532                .take()
533                .ok_or_else(|| {
534                    StreamError::Failed("connected UDP receive source already used".to_owned())
535                })?;
536            let demand = DemandBatcher::new(receive_buffer);
537            carrier.request_demand(demand.initial())?;
538            Ok(ReceiveResource {
539                receiver,
540                carrier: carrier.clone(),
541                demand,
542            })
543        },
544        receive_next_item,
545        close_receive_resource,
546    )
547}
548
549fn receive_next_item<T>(resource: &mut ReceiveResource<T>) -> StreamResult<Option<T>>
550where
551    T: Send + 'static,
552{
553    match resource.receiver.recv() {
554        Ok(ReceiveResponse::Item(item)) => {
555            if let Some(demand) = resource.demand.record_consumed() {
556                resource.carrier.request_demand(demand)?;
557            }
558            Ok(Some(item))
559        }
560        Ok(ReceiveResponse::Error(error)) => Err(error),
561        Err(_) => Err(abrupt_termination()),
562    }
563}
564
565fn close_receive_resource<T>(resource: ReceiveResource<T>) -> StreamResult<()>
566where
567    T: Send + 'static,
568{
569    resource.carrier.close_read();
570    Ok(())
571}
572
573fn start_datagram_carrier(
574    socket: UdpSocket,
575    handle: Handle,
576    max_datagram_size: usize,
577    receive_buffer: usize,
578    read_open: bool,
579    write_open: bool,
580) -> UdpCarrierParts<Datagram> {
581    let command_capacity = async_carrier::DEFAULT_COMMAND_BUFFER.max(receive_buffer);
582    let (commands, command_receiver) = async_carrier::command_channel(command_capacity, "UDP");
583    let (send_error_sender, send_error_receiver) = std_mpsc::channel();
584    let (receive_sender, receive_receiver) = if read_open {
585        let (sender, receiver) = std_mpsc::sync_channel(receive_buffer.saturating_add(1));
586        (Some(sender), Some(receiver))
587    } else {
588        (None, None)
589    };
590    let task = handle.spawn(run_datagram_carrier_task(
591        socket,
592        max_datagram_size,
593        receive_sender,
594        send_error_sender,
595        command_receiver,
596        read_open,
597        write_open,
598    ));
599    (
600        UdpCarrier {
601            inner: Arc::new(UdpCarrierInner {
602                commands,
603                send_errors: Mutex::new(send_error_receiver),
604                task: Mutex::new(Some(task)),
605            }),
606        },
607        receive_receiver,
608    )
609}
610
611fn start_connected_carrier(
612    socket: UdpSocket,
613    handle: Handle,
614    max_datagram_size: usize,
615    receive_buffer: usize,
616    read_open: bool,
617    write_open: bool,
618) -> UdpCarrierParts<Vec<u8>> {
619    let command_capacity = async_carrier::DEFAULT_COMMAND_BUFFER.max(receive_buffer);
620    let (commands, command_receiver) =
621        async_carrier::command_channel(command_capacity, "connected UDP");
622    let (send_error_sender, send_error_receiver) = std_mpsc::channel();
623    let (receive_sender, receive_receiver) = if read_open {
624        let (sender, receiver) = std_mpsc::sync_channel(receive_buffer.saturating_add(1));
625        (Some(sender), Some(receiver))
626    } else {
627        (None, None)
628    };
629    let task = handle.spawn(run_connected_carrier_task(
630        socket,
631        max_datagram_size,
632        receive_sender,
633        send_error_sender,
634        command_receiver,
635        read_open,
636        write_open,
637    ));
638    (
639        UdpCarrier {
640            inner: Arc::new(UdpCarrierInner {
641                commands,
642                send_errors: Mutex::new(send_error_receiver),
643                task: Mutex::new(Some(task)),
644            }),
645        },
646        receive_receiver,
647    )
648}
649
650async fn run_datagram_carrier_task(
651    socket: UdpSocket,
652    max_datagram_size: usize,
653    receive_sender: Option<std_mpsc::SyncSender<ReceiveResponse<Datagram>>>,
654    send_error_sender: std_mpsc::Sender<StreamError>,
655    mut commands: tokio_mpsc::Receiver<UdpCarrierCommand<Datagram>>,
656    mut read_open: bool,
657    mut write_open: bool,
658) {
659    let mut buffer = vec![0_u8; max_datagram_size];
660    let mut requested = 0_usize;
661    loop {
662        if !read_open && !write_open {
663            return;
664        }
665
666        if read_open && requested > 0 {
667            tokio::select! {
668                biased;
669                command = commands.recv() => {
670                    let Some(command) = command else { return; };
671                    if !handle_datagram_command(
672                        &socket,
673                        command,
674                        &receive_sender,
675                        &send_error_sender,
676                        &mut read_open,
677                        &mut write_open,
678                        &mut requested,
679                    ).await {
680                        return;
681                    }
682                }
683                received = socket.recv_from(&mut buffer) => {
684                    match received {
685                        Ok((read, remote)) => {
686                            let datagram = Datagram::new(buffer[..read].to_vec(), remote);
687                            match try_send_received_item(&receive_sender, datagram) {
688                                QueueOutcome::Queued => {
689                                    requested = requested.saturating_sub(1);
690                                }
691                                QueueOutcome::Dropped => {
692                                    requested = 0;
693                                    if let Err(error) = drain_ready_datagrams(&socket, &mut buffer) {
694                                        report_carrier_error(&receive_sender, &send_error_sender, error);
695                                        return;
696                                    }
697                                }
698                                QueueOutcome::Closed => {
699                                    read_open = false;
700                                }
701                            }
702                        }
703                        Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
704                        Err(error) => {
705                            report_carrier_error(&receive_sender, &send_error_sender, io_error(error));
706                            return;
707                        }
708                    }
709                }
710            }
711        } else {
712            let Some(command) = commands.recv().await else {
713                return;
714            };
715            if !handle_datagram_command(
716                &socket,
717                command,
718                &receive_sender,
719                &send_error_sender,
720                &mut read_open,
721                &mut write_open,
722                &mut requested,
723            )
724            .await
725            {
726                return;
727            }
728        }
729    }
730}
731
732async fn run_connected_carrier_task(
733    socket: UdpSocket,
734    max_datagram_size: usize,
735    receive_sender: Option<std_mpsc::SyncSender<ReceiveResponse<Vec<u8>>>>,
736    send_error_sender: std_mpsc::Sender<StreamError>,
737    mut commands: tokio_mpsc::Receiver<UdpCarrierCommand<Vec<u8>>>,
738    mut read_open: bool,
739    mut write_open: bool,
740) {
741    let mut buffer = vec![0_u8; max_datagram_size];
742    let mut requested = 0_usize;
743    loop {
744        if !read_open && !write_open {
745            return;
746        }
747
748        if read_open && requested > 0 {
749            tokio::select! {
750                biased;
751                command = commands.recv() => {
752                    let Some(command) = command else { return; };
753                    if !handle_connected_command(
754                        &socket,
755                        command,
756                        &receive_sender,
757                        &send_error_sender,
758                        &mut read_open,
759                        &mut write_open,
760                        &mut requested,
761                    ).await {
762                        return;
763                    }
764                }
765                received = socket.recv(&mut buffer) => {
766                    match received {
767                        Ok(read) => {
768                            match try_send_received_item(&receive_sender, buffer[..read].to_vec()) {
769                                QueueOutcome::Queued => {
770                                    requested = requested.saturating_sub(1);
771                                }
772                                QueueOutcome::Dropped => {
773                                    requested = 0;
774                                    if let Err(error) = drain_ready_connected_datagrams(&socket, &mut buffer) {
775                                        report_carrier_error(&receive_sender, &send_error_sender, error);
776                                        return;
777                                    }
778                                }
779                                QueueOutcome::Closed => {
780                                    read_open = false;
781                                }
782                            }
783                        }
784                        Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
785                        Err(error) => {
786                            report_carrier_error(&receive_sender, &send_error_sender, io_error(error));
787                            return;
788                        }
789                    }
790                }
791            }
792        } else {
793            let Some(command) = commands.recv().await else {
794                return;
795            };
796            if !handle_connected_command(
797                &socket,
798                command,
799                &receive_sender,
800                &send_error_sender,
801                &mut read_open,
802                &mut write_open,
803                &mut requested,
804            )
805            .await
806            {
807                return;
808            }
809        }
810    }
811}
812
813async fn handle_datagram_command(
814    socket: &UdpSocket,
815    command: UdpCarrierCommand<Datagram>,
816    receive_sender: &Option<std_mpsc::SyncSender<ReceiveResponse<Datagram>>>,
817    send_error_sender: &std_mpsc::Sender<StreamError>,
818    read_open: &mut bool,
819    write_open: &mut bool,
820    requested: &mut usize,
821) -> bool {
822    match command {
823        UdpCarrierCommand::Demand(demand) => {
824            *requested = requested.saturating_add(demand);
825            true
826        }
827        UdpCarrierCommand::SendOne(datagram) => {
828            if !*write_open {
829                let error = StreamError::Failed("UDP write side is closed".to_owned());
830                report_carrier_error(receive_sender, send_error_sender, error);
831                return false;
832            }
833            send_one_datagram(socket, receive_sender, send_error_sender, datagram).await
834        }
835        UdpCarrierCommand::SendBatch(datagrams) => {
836            if !*write_open {
837                let error = StreamError::Failed("UDP write side is closed".to_owned());
838                report_carrier_error(receive_sender, send_error_sender, error);
839                return false;
840            }
841            for datagram in datagrams {
842                if !send_one_datagram(socket, receive_sender, send_error_sender, datagram).await {
843                    return false;
844                }
845            }
846            true
847        }
848        UdpCarrierCommand::CloseRead => {
849            *read_open = false;
850            true
851        }
852        UdpCarrierCommand::CloseWrite { ack } => {
853            *write_open = false;
854            let _ = ack.send(Ok(()));
855            true
856        }
857    }
858}
859
860async fn handle_connected_command(
861    socket: &UdpSocket,
862    command: UdpCarrierCommand<Vec<u8>>,
863    receive_sender: &Option<std_mpsc::SyncSender<ReceiveResponse<Vec<u8>>>>,
864    send_error_sender: &std_mpsc::Sender<StreamError>,
865    read_open: &mut bool,
866    write_open: &mut bool,
867    requested: &mut usize,
868) -> bool {
869    match command {
870        UdpCarrierCommand::Demand(demand) => {
871            *requested = requested.saturating_add(demand);
872            true
873        }
874        UdpCarrierCommand::SendOne(payload) => {
875            if !*write_open {
876                let error = StreamError::Failed("connected UDP write side is closed".to_owned());
877                report_carrier_error(receive_sender, send_error_sender, error);
878                return false;
879            }
880            send_one_connected_payload(socket, receive_sender, send_error_sender, payload).await
881        }
882        UdpCarrierCommand::SendBatch(payloads) => {
883            if !*write_open {
884                let error = StreamError::Failed("connected UDP write side is closed".to_owned());
885                report_carrier_error(receive_sender, send_error_sender, error);
886                return false;
887            }
888            for payload in payloads {
889                if !send_one_connected_payload(socket, receive_sender, send_error_sender, payload)
890                    .await
891                {
892                    return false;
893                }
894            }
895            true
896        }
897        UdpCarrierCommand::CloseRead => {
898            *read_open = false;
899            true
900        }
901        UdpCarrierCommand::CloseWrite { ack } => {
902            *write_open = false;
903            let _ = ack.send(Ok(()));
904            true
905        }
906    }
907}
908
909async fn send_one_datagram(
910    socket: &UdpSocket,
911    receive_sender: &Option<std_mpsc::SyncSender<ReceiveResponse<Datagram>>>,
912    send_error_sender: &std_mpsc::Sender<StreamError>,
913    datagram: Datagram,
914) -> bool {
915    let expected = datagram.payload.len();
916    match socket.send_to(&datagram.payload, datagram.remote).await {
917        Ok(sent) if sent == expected => true,
918        Ok(sent) => {
919            report_carrier_error(
920                receive_sender,
921                send_error_sender,
922                short_send_error(sent, expected),
923            );
924            false
925        }
926        Err(error) => {
927            report_carrier_error(receive_sender, send_error_sender, io_error(error));
928            false
929        }
930    }
931}
932
933async fn send_one_connected_payload(
934    socket: &UdpSocket,
935    receive_sender: &Option<std_mpsc::SyncSender<ReceiveResponse<Vec<u8>>>>,
936    send_error_sender: &std_mpsc::Sender<StreamError>,
937    payload: Vec<u8>,
938) -> bool {
939    let expected = payload.len();
940    match socket.send(&payload).await {
941        Ok(sent) if sent == expected => true,
942        Ok(sent) => {
943            report_carrier_error(
944                receive_sender,
945                send_error_sender,
946                short_send_error(sent, expected),
947            );
948            false
949        }
950        Err(error) => {
951            report_carrier_error(receive_sender, send_error_sender, io_error(error));
952            false
953        }
954    }
955}
956
957fn try_send_received_item<T>(
958    sender: &Option<std_mpsc::SyncSender<ReceiveResponse<T>>>,
959    item: T,
960) -> QueueOutcome
961where
962    T: Send + 'static,
963{
964    let Some(sender) = sender else {
965        return QueueOutcome::Closed;
966    };
967    match sender.try_send(ReceiveResponse::Item(item)) {
968        Ok(()) => QueueOutcome::Queued,
969        Err(std_mpsc::TrySendError::Full(_)) => QueueOutcome::Dropped,
970        Err(std_mpsc::TrySendError::Disconnected(_)) => QueueOutcome::Closed,
971    }
972}
973
974fn drain_ready_datagrams(socket: &UdpSocket, buffer: &mut [u8]) -> StreamResult<()> {
975    loop {
976        match socket.try_recv_from(buffer) {
977            Ok((_read, _remote)) => {}
978            Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => return Ok(()),
979            Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
980            Err(error) => return Err(io_error(error)),
981        }
982    }
983}
984
985fn drain_ready_connected_datagrams(socket: &UdpSocket, buffer: &mut [u8]) -> StreamResult<()> {
986    loop {
987        match socket.try_recv(buffer) {
988            Ok(_read) => {}
989            Err(error) if error.kind() == std::io::ErrorKind::WouldBlock => return Ok(()),
990            Err(error) if error.kind() == std::io::ErrorKind::Interrupted => {}
991            Err(error) => return Err(io_error(error)),
992        }
993    }
994}
995
996fn report_carrier_error<T>(
997    receive_sender: &Option<std_mpsc::SyncSender<ReceiveResponse<T>>>,
998    send_error_sender: &std_mpsc::Sender<StreamError>,
999    error: StreamError,
1000) where
1001    T: Send + 'static,
1002{
1003    let _ = send_error_sender.send(error.clone());
1004    if let Some(receive_sender) = receive_sender {
1005        let _ = receive_sender.try_send(ReceiveResponse::Error(error));
1006    }
1007}
1008
1009fn datagram_send_flow_from_carrier(
1010    carrier: UdpCarrier<Datagram>,
1011    batch_size: usize,
1012) -> Flow<Datagram, NotUsed, NotUsed> {
1013    Flow::<Datagram, Datagram>::identity().map_with_resource(
1014        move || {
1015            Ok(SendResource {
1016                carrier: carrier.clone(),
1017                pending: Vec::with_capacity(batch_size),
1018                batch_size,
1019            })
1020        },
1021        |resource, datagram| {
1022            send_datagram(resource, datagram)?;
1023            Ok(NotUsed)
1024        },
1025        close_send_resource,
1026    )
1027}
1028
1029fn connected_send_flow_from_carrier(
1030    carrier: UdpCarrier<Vec<u8>>,
1031    batch_size: usize,
1032) -> Flow<Vec<u8>, NotUsed, NotUsed> {
1033    Flow::<Vec<u8>, Vec<u8>>::identity().map_with_resource(
1034        move || {
1035            Ok(SendResource {
1036                carrier: carrier.clone(),
1037                pending: Vec::with_capacity(batch_size),
1038                batch_size,
1039            })
1040        },
1041        |resource, payload| {
1042            send_connected_payload(resource, payload)?;
1043            Ok(NotUsed)
1044        },
1045        close_send_resource,
1046    )
1047}
1048
1049fn close_send_resource<T>(mut resource: SendResource<T>) -> StreamResult<Option<NotUsed>>
1050where
1051    T: Send + 'static,
1052{
1053    flush_send_resource(&mut resource)?;
1054    resource.carrier.close_write()?;
1055    Ok(None)
1056}
1057
1058fn send_datagram(resource: &mut SendResource<Datagram>, datagram: Datagram) -> StreamResult<()> {
1059    send_item(resource, datagram)
1060}
1061
1062fn send_connected_payload(
1063    resource: &mut SendResource<Vec<u8>>,
1064    payload: Vec<u8>,
1065) -> StreamResult<()> {
1066    send_item(resource, payload)
1067}
1068
1069fn send_item<T>(resource: &mut SendResource<T>, item: T) -> StreamResult<()>
1070where
1071    T: Send + 'static,
1072{
1073    if resource.batch_size <= 1 {
1074        return resource.carrier.send_one(item);
1075    }
1076    resource.pending.push(item);
1077    if resource.pending.len() >= resource.batch_size {
1078        flush_send_resource(resource)?;
1079    }
1080    Ok(())
1081}
1082
1083fn flush_send_resource<T>(resource: &mut SendResource<T>) -> StreamResult<()>
1084where
1085    T: Send + 'static,
1086{
1087    if resource.pending.is_empty() {
1088        return resource.carrier.check_send_error();
1089    }
1090    let pending = std::mem::take(&mut resource.pending);
1091    resource.carrier.send_items(pending)
1092}
1093
1094fn short_send_error(sent: usize, expected: usize) -> StreamError {
1095    StreamError::Failed(format!(
1096        "UDP socket sent {sent} bytes from {expected}-byte datagram"
1097    ))
1098}