iroh_relay/server/
client.rs

1//! The server-side representation of an ongoing client relaying connection.
2
3use std::{collections::HashSet, sync::Arc, time::Duration};
4
5use iroh_base::EndpointId;
6use n0_error::{e, stack_error};
7use n0_future::{SinkExt, StreamExt};
8use rand::RngExt;
9use time::{Date, OffsetDateTime};
10use tokio::{
11    sync::{
12        mpsc::{self, error::TrySendError},
13        watch,
14    },
15    time::MissedTickBehavior,
16};
17use tokio_util::{sync::CancellationToken, task::AbortOnDropHandle};
18use tracing::{Instrument, debug, trace, warn};
19
20use crate::{
21    PingTracker,
22    defaults::timeouts::SERVER_WRITE_TIMEOUT,
23    http::ProtocolVersion,
24    protos::{
25        relay::{
26            ClientToRelayMsg, Datagrams, PER_CLIENT_SEND_QUEUE_DEPTH, PING_INTERVAL,
27            RelayToClientMsg, Status,
28        },
29        streams::BytesStreamSink,
30    },
31    server::{
32        ConnectionId, OnDisconnectGuard,
33        clients::Clients,
34        metrics::Metrics,
35        streams::{RecvError as RelayRecvError, RelayedStream, SendError as RelaySendError},
36    },
37};
38
39/// A request to write a dataframe to a Client
40#[derive(Debug, Clone)]
41pub(super) struct Packet {
42    /// The sender of the packet
43    src: EndpointId,
44    /// The data packet bytes.
45    data: Datagrams,
46}
47
48/// Configuration for a client connection.
49///
50/// Generic over the stream type to support different WebSocket implementations.
51#[derive(Debug)]
52#[non_exhaustive]
53pub struct Config<S> {
54    /// Reports the disconnect once the connection ends.
55    ///
56    /// Also the owner of this connection's [`EndpointId`] and [`ConnectionId`].
57    pub guard: OnDisconnectGuard,
58    /// The relayed stream connection
59    pub stream: RelayedStream<S>,
60    /// Write timeout for the client connection
61    pub write_timeout: Duration,
62    /// Channel capacity for internal message queues
63    pub channel_capacity: usize,
64    /// Protocol version negotiated for this client
65    pub protocol_version: ProtocolVersion,
66    /// Optional signal to inform the client when it is being rate-limited.
67    ///
68    /// When set, the connection actor notifies the client once with a
69    /// [`Status::RateLimited`] message when the rate-limited counter first
70    /// becomes non-zero.
71    pub(crate) rate_limited: Option<watch::Receiver<u64>>,
72}
73
74impl<S> Config<S> {
75    /// Creates a new config with sensible default values for `write_timeout` and `channel_capacity`.
76    ///
77    /// The endpoint and connection ids are taken from `guard`.
78    pub fn new(
79        guard: OnDisconnectGuard,
80        stream: RelayedStream<S>,
81        protocol_version: ProtocolVersion,
82    ) -> Self {
83        Self {
84            guard,
85            stream,
86            protocol_version,
87            write_timeout: SERVER_WRITE_TIMEOUT,
88            channel_capacity: PER_CLIENT_SEND_QUEUE_DEPTH,
89            rate_limited: None,
90        }
91    }
92}
93
94/// The [`Server`] side representation of a [`Client`]'s connection.
95///
96/// [`Server`]: crate::server::Server
97/// [`Client`]: crate::client::Client
98#[derive(Debug)]
99pub(super) struct Client {
100    /// Identity of the connected peer.
101    endpoint_id: EndpointId,
102    /// Connection identifier.
103    connection_id: ConnectionId,
104    /// Used to close the connection loop.
105    done: CancellationToken,
106    /// Actor handle.
107    handle: AbortOnDropHandle<()>,
108    /// Channel to send packets intended for the client.
109    packet_queue: mpsc::Sender<Packet>,
110    /// Channel to send non-packet messages to the client.
111    message_queue: mpsc::Sender<RelayToClientMsg>,
112    /// Relay protocol version negotiated for this client.
113    protocol_version: ProtocolVersion,
114}
115
116impl Client {
117    /// Creates a client from a connection & starts a read and write loop to handle io to and from
118    /// the client
119    ///
120    /// The `guard` is moved into the connection actor and reports the disconnect to access
121    /// control once the connection ends.
122    ///
123    /// Call [`Client::shutdown`] to close the read and write loops before dropping the [`Client`]
124    pub(super) fn new<S>(config: Config<S>, clients: &Clients, metrics: Arc<Metrics>) -> Client
125    where
126        S: BytesStreamSink + Send + 'static,
127    {
128        let Config {
129            guard,
130            stream,
131            write_timeout,
132            channel_capacity,
133            protocol_version,
134            rate_limited,
135        } = config;
136        let endpoint_id = guard.endpoint_id;
137        let connection_id = guard.connection_id;
138
139        let (packet_send_queue_s, packet_send_queue_r) = mpsc::channel(channel_capacity);
140        let (message_send_queue_s, message_send_queue_r) = mpsc::channel(channel_capacity);
141        let done = CancellationToken::new();
142
143        let actor = Actor {
144            stream,
145            timeout: write_timeout,
146            packet_send_queue: packet_send_queue_r,
147            message_send_queue: message_send_queue_r,
148            guard,
149            clients: clients.clone(),
150            client_counter: ClientCounter::default(),
151            ping_tracker: PingTracker::default(),
152            protocol_version,
153            rate_limited,
154            metrics,
155        };
156
157        // start io loop
158        let io_done = done.clone();
159        let handle = tokio::task::spawn(actor.run(io_done).instrument(tracing::info_span!(
160            "client-connection-actor",
161            remote_endpoint = %endpoint_id.fmt_short(),
162            connection_id = %connection_id
163        )));
164
165        Client {
166            endpoint_id,
167            connection_id,
168            handle: AbortOnDropHandle::new(handle),
169            done,
170            packet_queue: packet_send_queue_s,
171            message_queue: message_send_queue_s,
172            protocol_version,
173        }
174    }
175
176    pub(super) fn connection_id(&self) -> ConnectionId {
177        self.connection_id
178    }
179
180    /// Shutdown the reader and writer loops and closes the connection.
181    ///
182    /// Any shutdown errors will be logged as warnings.
183    pub(super) async fn shutdown(self) {
184        self.start_shutdown();
185        if let Err(e) = self.handle.await {
186            warn!(
187                remote_endpoint = %self.endpoint_id.fmt_short(),
188                "error closing actor loop: {e:#?}",
189            );
190        };
191    }
192
193    /// Starts the process of shutdown.
194    pub(super) fn start_shutdown(&self) {
195        self.done.cancel();
196    }
197
198    pub(super) fn try_send_packet(
199        &self,
200        src: EndpointId,
201        data: Datagrams,
202    ) -> Result<(), TrySendError<Packet>> {
203        self.packet_queue.try_send(Packet { src, data })
204    }
205
206    pub(super) fn try_send_peer_gone(
207        &self,
208        key: EndpointId,
209    ) -> Result<(), TrySendError<RelayToClientMsg>> {
210        self.message_queue
211            .try_send(RelayToClientMsg::EndpointGone(key))
212    }
213
214    pub(super) fn try_send_health(
215        &self,
216        status: Status,
217    ) -> Result<(), TrySendError<RelayToClientMsg>> {
218        let message = match self.protocol_version {
219            ProtocolVersion::V2 => RelayToClientMsg::Status(status),
220            ProtocolVersion::V1 => RelayToClientMsg::Health {
221                problem: status.to_string(),
222            },
223        };
224        self.message_queue.try_send(message)
225    }
226}
227
228/// Completes when `rx` observes the connection being rate-limited.
229///
230/// Pends forever when `rx` is `None`, i.e. when no rate-limit signal is configured.
231async fn rate_limited_signal(rx: &mut Option<watch::Receiver<u64>>) -> bool {
232    match rx {
233        Some(rx) => match rx.changed().await {
234            Ok(()) => *rx.borrow_and_update() > 0,
235            // The sender lives inside this actor's stream, so it cannot drop while
236            // the actor runs. Report not limited to be safe.
237            Err(_) => false,
238        },
239        None => std::future::pending().await,
240    }
241}
242
243/// Error when handling an incoming frame from a client.
244#[stack_error(derive, add_meta, from_sources)]
245#[allow(missing_docs)]
246#[non_exhaustive]
247pub enum HandleFrameError {
248    #[error(transparent)]
249    ForwardPacket { source: ForwardPacketError },
250    #[error("Stream terminated")]
251    StreamTerminated {},
252    #[error(transparent)]
253    Recv { source: RelayRecvError },
254    #[error(transparent)]
255    Send { source: WriteFrameError },
256}
257
258/// Error when writing a frame to a client.
259#[stack_error(derive, add_meta, from_sources)]
260#[allow(missing_docs)]
261#[non_exhaustive]
262pub enum WriteFrameError {
263    #[error(transparent)]
264    Stream { source: RelaySendError },
265    #[error(transparent)]
266    Timeout {
267        #[error(std_err)]
268        source: tokio::time::error::Elapsed,
269    },
270}
271
272/// Run error
273#[stack_error(derive, add_meta)]
274#[allow(missing_docs)]
275#[non_exhaustive]
276pub enum RunError {
277    #[error(transparent)]
278    ForwardPacket {
279        #[error(from)]
280        source: ForwardPacketError,
281    },
282    #[error("Flush")]
283    Flush {},
284    #[error(transparent)]
285    HandleFrame {
286        #[error(from)]
287        source: HandleFrameError,
288    },
289    #[error("Failed to send packet")]
290    PacketSend { source: WriteFrameError },
291    #[error("Handle was dropped")]
292    HandleDropped {},
293    #[error("Writing a frame failed")]
294    WriteFrame { source: WriteFrameError },
295    #[error("Tick flush")]
296    TickFlush {},
297}
298
299/// Manages all the reads and writes to this client. It periodically sends a `KEEP_ALIVE`
300/// message to the client to keep the connection alive.
301///
302/// Call `run` to manage the input and output to and from the connection and the server.
303/// Once it hits its first write error or error receiving off a channel,
304/// it errors on return.
305/// If writes do not complete in the given `timeout`, it will also error.
306///
307/// On the "write" side, the [`Actor`] can send the client:
308///  - a KEEP_ALIVE frame
309///  - a PEER_GONE frame to inform the client that a peer they have previously sent messages to
310///    is gone from the network
311///  - packets from other peers
312///
313/// On the "read" side, it can:
314///     - receive a ping and write a pong back
315///     to speak to the endpoint ID associated with that client.
316#[derive(Debug)]
317struct Actor<S> {
318    /// IO Stream to talk to the client
319    stream: RelayedStream<S>,
320    /// Maximum time we wait to complete a write to the client
321    timeout: Duration,
322    /// Receiver for packets to be sent to the client.
323    packet_send_queue: mpsc::Receiver<Packet>,
324    /// Receiver for non-packet messages to be sent to the client.
325    message_send_queue: mpsc::Receiver<RelayToClientMsg>,
326    /// Reports the disconnect to access control when dropped.
327    ///
328    /// Also the owner of this connection's [`EndpointId`] and [`ConnectionId`].
329    guard: OnDisconnectGuard,
330    /// Reference to the other connected clients.
331    clients: Clients,
332    /// Statistics about the connected clients
333    client_counter: ClientCounter,
334    ping_tracker: PingTracker,
335    /// Relay protocol version negotiated for this client.
336    protocol_version: ProtocolVersion,
337    /// Optional signal counting how often the connection has been rate-limited.
338    rate_limited: Option<watch::Receiver<u64>>,
339    metrics: Arc<Metrics>,
340}
341
342impl<S> Actor<S>
343where
344    S: BytesStreamSink,
345{
346    async fn run(mut self, done: CancellationToken) {
347        // Note the accept and disconnects metrics must be in a pair.  Technically the
348        // connection is accepted long before this in the HTTP server, but it is clearer to
349        // handle the metric here.
350        self.metrics.accepts.inc();
351        if self.client_counter.update(self.guard.endpoint_id()) {
352            self.metrics.unique_client_keys.inc();
353        }
354        match self.run_inner(done).await {
355            Err(e) => {
356                warn!("actor errored {e:#}, exiting");
357            }
358            Ok(()) => {
359                debug!("actor finished, exiting");
360            }
361        }
362
363        self.clients.unregister(self.guard, &self.metrics);
364        self.metrics.disconnects.inc();
365    }
366
367    async fn run_inner(&mut self, done: CancellationToken) -> Result<(), RunError> {
368        // Add some jitter to ping pong interactions, to avoid all pings being sent at the same time
369        let next_interval = || {
370            let random_secs = rand::rng().random_range(1..=5);
371            Duration::from_secs(random_secs) + PING_INTERVAL
372        };
373
374        let mut ping_interval = tokio::time::interval(next_interval());
375        // ticks immediately
376        ping_interval.set_missed_tick_behavior(MissedTickBehavior::Delay);
377        ping_interval.tick().await;
378
379        let mut rate_limit_notified = false;
380
381        loop {
382            tokio::select! {
383                biased;
384
385                _ = done.cancelled() => {
386                    trace!("actor loop cancelled, exiting");
387                    // final flush
388                    self.stream.flush().await.map_err(|_| e!(RunError::Flush))?;
389                    break;
390                }
391                limited = rate_limited_signal(&mut self.rate_limited), if !rate_limit_notified => {
392                    // Notify the client once per connection.
393                    // V1 clients are not notified.
394                    rate_limit_notified = true;
395                    if limited && self.protocol_version == ProtocolVersion::V2 {
396                        debug!("connection is rate-limited, notifying client");
397                        self.write_frame(RelayToClientMsg::Status(Status::RateLimited))
398                            .await
399                            .map_err(|err| e!(RunError::WriteFrame, err))?;
400                    }
401                }
402                maybe_frame = self.stream.next() => {
403                    self
404                        .handle_frame(maybe_frame)
405                        .await?;
406                    // reset the ping interval, we just received a message
407                    ping_interval.reset();
408                }
409                // Second priority, sending regular packets
410                packet = self.packet_send_queue.recv() => {
411                    let packet = packet.ok_or_else(|| e!(RunError::HandleDropped))?;
412                    self.send_packet(packet)
413                        .await
414                        .map_err(|err| e!(RunError::PacketSend, err))?;
415                }
416                // Last priority, sending other message
417                message = self.message_send_queue.recv() => {
418                    let message = message.ok_or_else(|| e!(RunError::HandleDropped))?;
419                    trace!("send {message:?}");
420                    self.write_frame(message)
421                        .await
422                        .map_err(|err| e!(RunError::WriteFrame, err))?;
423                }
424                _ = self.ping_tracker.timeout() => {
425                    trace!("pong timed out");
426                    break;
427                }
428                _ = ping_interval.tick() => {
429                    trace!("keep alive ping");
430                    // new interval
431                    ping_interval.reset_after(next_interval());
432                    let data = self.ping_tracker.new_ping();
433                    self.write_frame(RelayToClientMsg::Ping(data))
434                        .await
435                        .map_err(|err| e!(RunError::WriteFrame, err))?;
436                }
437            }
438
439            self.stream
440                .flush()
441                .await
442                .map_err(|_| e!(RunError::TickFlush))?;
443        }
444        Ok(())
445    }
446
447    /// Writes the given frame to the connection.
448    ///
449    /// Errors if the send does not happen within the `timeout` duration
450    async fn write_frame(&mut self, frame: RelayToClientMsg) -> Result<(), WriteFrameError> {
451        tokio::time::timeout(self.timeout, self.stream.send(frame)).await??;
452        Ok(())
453    }
454
455    /// Writes contents to the client in a `RECV_PACKET` frame.
456    ///
457    /// Errors if the send does not happen within the `timeout` duration
458    /// Does not flush.
459    async fn send_raw(&mut self, packet: Packet) -> Result<(), WriteFrameError> {
460        let remote_endpoint_id = packet.src;
461        let datagrams = packet.data;
462
463        if let Ok(len) = datagrams.contents.len().try_into() {
464            self.metrics.bytes_sent.inc_by(len);
465        }
466        self.write_frame(RelayToClientMsg::Datagrams {
467            remote_endpoint_id,
468            datagrams,
469        })
470        .await
471    }
472
473    async fn send_packet(&mut self, packet: Packet) -> Result<(), WriteFrameError> {
474        trace!("send packet");
475        match self.send_raw(packet).await {
476            Ok(()) => {
477                self.metrics.send_packets_sent.inc();
478                Ok(())
479            }
480            Err(err) => {
481                self.metrics.send_packets_dropped.inc();
482                Err(err)
483            }
484        }
485    }
486
487    /// Handles frame read results.
488    async fn handle_frame(
489        &mut self,
490        maybe_frame: Option<Result<ClientToRelayMsg, RelayRecvError>>,
491    ) -> Result<(), HandleFrameError> {
492        trace!(?maybe_frame, "handle incoming frame");
493        let frame = match maybe_frame {
494            Some(frame) => frame?,
495            None => return Err(e!(HandleFrameError::StreamTerminated)),
496        };
497
498        match frame {
499            ClientToRelayMsg::Datagrams {
500                dst_endpoint_id: dst_key,
501                datagrams,
502            } => {
503                let packet_len = datagrams.contents.len();
504                if let Err(err @ ForwardPacketError { .. }) =
505                    self.handle_frame_send_packet(dst_key, datagrams)
506                {
507                    warn!("failed to handle send packet frame: {err:#}");
508                }
509                self.metrics.bytes_recv.inc_by(packet_len as u64);
510            }
511            ClientToRelayMsg::Ping(data) => {
512                self.metrics.got_ping.inc();
513                // TODO: add rate limiter
514                self.write_frame(RelayToClientMsg::Pong(data)).await?;
515                self.metrics.sent_pong.inc();
516            }
517            ClientToRelayMsg::Pong(data) => {
518                self.ping_tracker.pong_received(data);
519            }
520        }
521        Ok(())
522    }
523
524    fn handle_frame_send_packet(
525        &self,
526        dst: EndpointId,
527        data: Datagrams,
528    ) -> Result<(), ForwardPacketError> {
529        self.metrics.send_packets_recv.inc();
530        self.clients
531            .send_packet(dst, data, self.guard.endpoint_id(), &self.metrics)?;
532
533        Ok(())
534    }
535}
536
537#[derive(Debug)]
538pub(crate) enum SendError {
539    Full,
540    Closed,
541}
542
543/// Error returned when forwarding a packet to a client fails.
544///
545/// This error occurs when the relay server cannot deliver a packet to its intended
546/// recipient, typically due to the client's send queue being full or the client
547/// disconnecting.
548#[stack_error(derive, add_meta)]
549#[error("failed to forward packet: {reason:?}")]
550pub struct ForwardPacketError {
551    reason: SendError,
552}
553
554/// Tracks how many unique endpoints have been seen during the last day.
555#[derive(Debug)]
556struct ClientCounter {
557    clients: HashSet<EndpointId>,
558    last_clear_date: Date,
559}
560
561impl Default for ClientCounter {
562    fn default() -> Self {
563        Self {
564            clients: HashSet::new(),
565            last_clear_date: OffsetDateTime::now_utc().date(),
566        }
567    }
568}
569
570impl ClientCounter {
571    fn check_and_clear(&mut self) {
572        let today = OffsetDateTime::now_utc().date();
573        if today != self.last_clear_date {
574            self.clients.clear();
575            self.last_clear_date = today;
576        }
577    }
578
579    /// Marks this endpoint as seen, returns whether it is new today or not.
580    fn update(&mut self, client: EndpointId) -> bool {
581        self.check_and_clear();
582        self.clients.insert(client)
583    }
584}
585
586#[cfg(test)]
587mod tests {
588    use iroh_base::SecretKey;
589    use n0_error::{Result, StdResultExt, bail_any};
590    use n0_future::Stream;
591    use n0_tracing_test::traced_test;
592    use rand::SeedableRng;
593    use tracing::info;
594
595    use super::*;
596    use crate::{
597        client::conn::Conn,
598        http::ProtocolVersion,
599        protos::{common::FrameType, relay::Status, streams::WsBytesFramed},
600        server::streams::{MaybeTlsStream, RateLimited, ServerRelayedStream},
601    };
602
603    async fn recv_frame<
604        E: std::error::Error + Sync + Send + 'static,
605        S: Stream<Item = Result<RelayToClientMsg, E>> + Unpin,
606    >(
607        frame_type: FrameType,
608        mut stream: S,
609    ) -> Result<RelayToClientMsg> {
610        match stream.next().await {
611            Some(Ok(frame)) => {
612                if frame_type != frame.typ() {
613                    bail_any!(
614                        "Unexpected frame, got {:?}, but expected {:?}",
615                        frame.typ(),
616                        frame_type
617                    );
618                }
619                Ok(frame)
620            }
621            Some(Err(err)) => Err(err).anyerr(),
622            None => bail_any!("Unexpected EOF, expected frame {frame_type:?}"),
623        }
624    }
625
626    #[tokio::test]
627    #[traced_test]
628    async fn test_client_actor_basic() -> Result {
629        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0u64);
630
631        let (send_queue_s, send_queue_r) = mpsc::channel(10);
632        let (message_s, message_r) = mpsc::channel(10);
633
634        let endpoint_id = SecretKey::from_bytes(&rng.random()).public();
635        let (io, io_rw) = tokio::io::duplex(1024);
636        let mut io_rw = Conn::test(io_rw, Default::default());
637        let stream = RelayedStream::test(io);
638
639        let clients = Clients::default();
640        let metrics = Arc::new(Metrics::default());
641        let actor = Actor {
642            stream,
643            timeout: Duration::from_secs(1),
644            packet_send_queue: send_queue_r,
645            message_send_queue: message_r,
646            guard: OnDisconnectGuard::empty(endpoint_id),
647            clients: clients.clone(),
648            client_counter: ClientCounter::default(),
649            ping_tracker: PingTracker::default(),
650            protocol_version: ProtocolVersion::V2,
651            rate_limited: None,
652            metrics,
653        };
654
655        let done = CancellationToken::new();
656        let io_done = done.clone();
657        let handle = tokio::task::spawn(async move { actor.run(io_done).await });
658
659        // Write tests
660        println!("-- write");
661        let data = b"hello world!";
662
663        // send packet
664        println!("  send packet");
665        let packet = Packet {
666            src: endpoint_id,
667            data: Datagrams::from(&data[..]),
668        };
669        send_queue_s
670            .send(packet.clone())
671            .await
672            .std_context("send")?;
673        let frame = recv_frame(FrameType::RelayToClientDatagram, &mut io_rw)
674            .await
675            .anyerr()?;
676        assert_eq!(
677            frame,
678            RelayToClientMsg::Datagrams {
679                remote_endpoint_id: endpoint_id,
680                datagrams: data.to_vec().into()
681            }
682        );
683
684        // send peer_gone
685        println!("send peer gone");
686        message_s
687            .send(RelayToClientMsg::EndpointGone(endpoint_id))
688            .await
689            .std_context("send")?;
690        let frame = recv_frame(FrameType::EndpointGone, &mut io_rw)
691            .await
692            .anyerr()?;
693        assert_eq!(frame, RelayToClientMsg::EndpointGone(endpoint_id));
694
695        // Read tests
696        println!("--read");
697
698        // send ping, expect pong
699        let data = b"pingpong";
700        io_rw.send(ClientToRelayMsg::Ping(*data)).await?;
701
702        // recv pong
703        println!(" recv pong");
704        let frame = recv_frame(FrameType::Pong, &mut io_rw).await?;
705        assert_eq!(frame, RelayToClientMsg::Pong(*data));
706
707        let target = SecretKey::from_bytes(&rng.random()).public();
708
709        // send packet
710        println!("  send packet");
711        let data = b"hello world!";
712        io_rw
713            .send(ClientToRelayMsg::Datagrams {
714                dst_endpoint_id: target,
715                datagrams: Datagrams::from(data),
716            })
717            .await
718            .std_context("send")?;
719
720        done.cancel();
721        handle.await.std_context("join")?;
722        Ok(())
723    }
724
725    fn test_client_builder(
726        key: EndpointId,
727        protocol_version: ProtocolVersion,
728    ) -> (Config<WsBytesFramed<RateLimited<MaybeTlsStream>>>, Conn) {
729        let (server, client) = tokio::io::duplex(1024);
730        let guard = OnDisconnectGuard::empty(key);
731        let mut config = Config::new(guard, ServerRelayedStream::test(server), protocol_version);
732        config.write_timeout = Duration::from_secs(1);
733        config.channel_capacity = 10;
734        (config, Conn::test(client, protocol_version))
735    }
736
737    #[tokio::test]
738    #[traced_test]
739    async fn test_client_v1_protocol() -> Result {
740        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(42u64);
741        let a_key = SecretKey::from_bytes(&rng.random()).public();
742        let b_key = SecretKey::from_bytes(&rng.random()).public();
743
744        let (builder_a, mut a_rw) = test_client_builder(a_key, ProtocolVersion::V1);
745
746        let clients = Clients::default();
747        let metrics = Arc::new(Metrics::default());
748        clients.register(builder_a, metrics.clone());
749
750        // Verify basic packet delivery works with V1.
751        let data = b"hello world v1!";
752        clients.send_packet(a_key, Datagrams::from(&data[..]), b_key, &metrics)?;
753        let frame = recv_frame(FrameType::RelayToClientDatagram, &mut a_rw).await?;
754        assert_eq!(
755            frame,
756            RelayToClientMsg::Datagrams {
757                remote_endpoint_id: b_key,
758                datagrams: data.to_vec().into(),
759            }
760        );
761
762        clients.shutdown().await;
763        Ok(())
764    }
765
766    #[tokio::test]
767    #[traced_test]
768    async fn test_client_v2_protocol() -> Result {
769        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(42u64);
770        let a_key = SecretKey::from_bytes(&rng.random()).public();
771        let b_key = SecretKey::from_bytes(&rng.random()).public();
772
773        let (builder_a, mut a_rw) = test_client_builder(a_key, ProtocolVersion::V2);
774
775        let clients = Clients::default();
776        let metrics = Arc::new(Metrics::default());
777        clients.register(builder_a, metrics.clone());
778
779        // Verify basic packet delivery works with V2.
780        let data = b"hello world v2!";
781        clients.send_packet(a_key, Datagrams::from(&data[..]), b_key, &metrics)?;
782        let frame = recv_frame(FrameType::RelayToClientDatagram, &mut a_rw).await?;
783        assert_eq!(
784            frame,
785            RelayToClientMsg::Datagrams {
786                remote_endpoint_id: b_key,
787                datagrams: data.to_vec().into(),
788            }
789        );
790
791        clients.shutdown().await;
792        Ok(())
793    }
794
795    /// Test for versioned protocol: v1 client should receive V1Health frame.
796    #[tokio::test]
797    #[traced_test]
798    async fn test_duplicate_endpoint_v1_receives_v1health() -> Result {
799        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(42u64);
800        let key = SecretKey::from_bytes(&rng.random()).public();
801
802        let (builder_first, mut first_rw) = test_client_builder(key, ProtocolVersion::V1);
803
804        let clients = Clients::default();
805        let metrics = Arc::new(Metrics::default());
806        clients.register(builder_first, metrics.clone());
807
808        // Register a second client with the same endpoint ID.
809        // The first client should receive a V1Health message.
810        let (builder_second, _second_rw) = test_client_builder(key, ProtocolVersion::V1);
811        clients.register(builder_second, metrics.clone());
812
813        let frame = recv_frame(FrameType::Health, &mut first_rw).await?;
814        assert!(
815            matches!(frame, RelayToClientMsg::Health { .. }),
816            "expected V1Health frame for V1 client, got {frame:?}"
817        );
818
819        clients.shutdown().await;
820        Ok(())
821    }
822
823    /// Test for versioned protocol: v2 client should receive V2Health frame.
824    #[tokio::test]
825    #[traced_test]
826    async fn test_duplicate_endpoint_v2_receives_health() -> Result {
827        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(42u64);
828        let key = SecretKey::from_bytes(&rng.random()).public();
829
830        let (builder_first, mut first_rw) = test_client_builder(key, ProtocolVersion::V2);
831
832        let clients = Clients::default();
833        let metrics = Arc::new(Metrics::default());
834        clients.register(builder_first, metrics.clone());
835
836        // Register a second client with the same endpoint ID.
837        // The first client should receive a Health message (V2 frame).
838        let (builder_second, _second_rw) = test_client_builder(key, ProtocolVersion::V2);
839        clients.register(builder_second, metrics.clone());
840
841        let frame = recv_frame(FrameType::Status, &mut first_rw).await?;
842        assert_eq!(
843            frame,
844            RelayToClientMsg::Status(Status::SameEndpointIdConnected)
845        );
846
847        clients.shutdown().await;
848        Ok(())
849    }
850
851    #[tokio::test(start_paused = true)]
852    #[traced_test]
853    async fn test_rate_limit() -> Result {
854        const LIMIT: u32 = 50;
855        const MAX_FRAMES: u32 = 100;
856
857        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0u64);
858
859        // Build the rate limited stream.
860        let (io_read, io_write) = tokio::io::duplex((LIMIT * MAX_FRAMES) as _);
861        let mut frame_writer = Conn::test(io_write, Default::default());
862        // Rate limiter allowing LIMIT bytes/s
863        let (mut stream, _limited) = RelayedStream::test_limited(io_read, LIMIT / 10, LIMIT)?;
864
865        // Prepare a frame to send, assert its size.
866        let data = Datagrams::from(b"hello world!!!!!");
867        let target = SecretKey::from_bytes(&rng.random()).public();
868        let frame = ClientToRelayMsg::Datagrams {
869            dst_endpoint_id: target,
870            datagrams: data.clone(),
871        };
872        let frame_len = frame.to_bytes().len();
873        assert_eq!(frame_len, LIMIT as usize);
874
875        // Send a frame, it should arrive.
876        info!("-- send packet");
877        frame_writer.send(frame.clone()).await.std_context("send")?;
878        frame_writer.flush().await.std_context("flush")?;
879        let recv_frame = tokio::time::timeout(Duration::from_millis(500), stream.next())
880            .await
881            .expect("timeout")
882            .expect("option")
883            .expect("ok");
884        assert_eq!(recv_frame, frame);
885
886        // Next frame does not arrive.
887        info!("-- send packet");
888        frame_writer.send(frame.clone()).await.std_context("send")?;
889        frame_writer.flush().await.std_context("flush")?;
890        let res = tokio::time::timeout(Duration::from_millis(100), stream.next()).await;
891        assert!(res.is_err(), "expecting a timeout");
892        info!("-- timeout happened");
893
894        // Wait long enough.
895        info!("-- sleep");
896        tokio::time::sleep(Duration::from_secs(1)).await;
897
898        // Frame arrives.
899        let recv_frame = tokio::time::timeout(Duration::from_millis(500), stream.next())
900            .await
901            .expect("timeout")
902            .expect("option")
903            .expect("ok");
904        assert_eq!(recv_frame, frame);
905
906        Ok(())
907    }
908
909    /// A client whose connection is rate-limited receives a [`Status::RateLimited`] notice.
910    #[tokio::test(start_paused = true)]
911    #[traced_test]
912    async fn test_rate_limit_notice() -> Result {
913        const LIMIT: u32 = 50;
914
915        let mut rng = rand_chacha::ChaCha8Rng::seed_from_u64(0u64);
916        let key = SecretKey::from_bytes(&rng.random()).public();
917        let target = SecretKey::from_bytes(&rng.random()).public();
918
919        let (server_io, client_io) = tokio::io::duplex(4096);
920        // Rate limiter allowing LIMIT bytes/s with a burst size below a single frame.
921        let (stream, limited) = ServerRelayedStream::test_limited(server_io, LIMIT / 10, LIMIT)?;
922        let mut config = Config::new(OnDisconnectGuard::empty(key), stream, ProtocolVersion::V2);
923        config.write_timeout = Duration::from_secs(1);
924        config.channel_capacity = 10;
925        config.rate_limited = Some(limited);
926        let mut conn = Conn::test(client_io, ProtocolVersion::V2);
927
928        let clients = Clients::default();
929        clients.register(config, Arc::new(Metrics::default()));
930
931        // A single frame larger than the burst size trips the rate limiter.
932        conn.send(ClientToRelayMsg::Datagrams {
933            dst_endpoint_id: target,
934            datagrams: Datagrams::from(b"hello world!!!!!"),
935        })
936        .await?;
937
938        let frame = recv_frame(FrameType::Status, &mut conn).await?;
939        assert_eq!(frame, RelayToClientMsg::Status(Status::RateLimited));
940
941        clients.shutdown().await;
942        Ok(())
943    }
944}