1mod config;
4mod endpoint;
5mod presence;
6#[cfg(feature = "tracing")]
7mod tracing_support;
8
9pub use config::{
10 ConnectContext, Connector, JoinContext, JoinPayloadLoader, Options, ReconnectAction,
11 ReconnectContext, ReconnectPolicy, Timer, static_join_payload,
12};
13pub use endpoint::{
14 ConnectionConfig, ConnectionConfigLoader, Endpoint, EndpointError, ResolvedEndpoint,
15 static_connection_config,
16};
17pub use presence::{ChannelPresence, PresenceEvent, PresenceStreamError};
18#[cfg(feature = "tracing")]
19pub use tracing_support::tracing_telemetry_hook;
20
21use std::{
22 cell::{Cell, RefCell},
23 collections::{HashMap, HashSet, VecDeque},
24 future::Future,
25 pin::Pin,
26 rc::Rc,
27 task::{Context, Poll},
28 time::Duration,
29};
30
31use futures::{
32 FutureExt, SinkExt, StreamExt,
33 channel::{mpsc, oneshot},
34 future::{LocalBoxFuture, pending},
35 stream::FuturesUnordered,
36};
37use phoenix_channel_runtime::{
38 ChannelState, Codec, EventRoute, Frame, Payload, PayloadError, PhoenixV2Codec, Protocol,
39 ProtocolEvent, ReplyStatus, Transport, TransportClose, TransportCloseRequest, TransportError,
40 TransportErrorKind, TransportEvent, WireMessage,
41};
42use serde::{Serialize, de::DeserializeOwned};
43use serde_json::{Value, json};
44use thiserror::Error;
45
46type RequestId = u64;
47
48#[derive(Clone, Debug, PartialEq)]
50pub enum SocketEvent {
51 Connecting {
53 attempt: u32,
55 },
56 Connected,
58 Disconnected {
60 reason: DisconnectReason,
62 },
63 ReconnectScheduled {
65 attempt: u32,
67 delay: Duration,
69 },
70 ReconnectStopped {
72 attempt: u32,
74 reason: DisconnectReason,
76 },
77 Closed,
79 Lagged {
81 dropped: u64,
83 },
84}
85
86#[derive(Clone, Debug, Error, Eq, PartialEq)]
88pub enum DisconnectReason {
89 #[error("connection closed by request")]
91 Requested,
92 #[error("connection failed: {0}")]
94 Connect(TransportError),
95 #[error("transport failed: {0}")]
97 Transport(TransportError),
98 #[error("connection closed: {0:?}")]
100 Closed(TransportClose),
101 #[error("heartbeat acknowledgement timed out")]
103 HeartbeatTimeout,
104 #[error("protocol failed: {0}")]
106 Protocol(String),
107 #[error("client driver stopped")]
109 DriverStopped,
110}
111
112impl DisconnectReason {
113 fn should_reconnect(&self) -> bool {
114 match self {
115 Self::Closed(close) => close.should_reconnect(),
116 Self::Requested | Self::DriverStopped => false,
117 Self::Connect(_) | Self::Transport(_) | Self::HeartbeatTimeout | Self::Protocol(_) => {
118 true
119 }
120 }
121 }
122}
123
124#[derive(Clone, Copy, Debug, Eq, PartialEq)]
126pub enum SocketStatus {
127 Disconnected,
129 Connecting,
131 Connected,
133 WaitingToReconnect,
135 Closed,
137}
138
139#[derive(Clone, Debug, PartialEq)]
141pub enum ChannelEvent {
142 Protocol(ProtocolEvent),
144 Disconnected,
146 JoinPayloadError(String),
148 Lagged {
152 dropped: u64,
154 },
155}
156
157impl ChannelEvent {
158 pub fn route<R: EventRoute>(&self) -> Result<Option<R::Output>, PayloadError> {
160 match self {
161 Self::Protocol(ProtocolEvent::Message(frame)) => frame.route::<R>(),
162 _ => Ok(None),
163 }
164 }
165}
166
167#[derive(Clone, Copy, Debug, Eq, PartialEq)]
169pub enum ChannelStatus {
170 WaitingForSocket,
172 WaitingToJoin,
174 Joining,
176 Joined,
178 Leaving,
180 Left,
182 Errored,
184 Closed,
186}
187
188#[derive(Clone, Debug, PartialEq)]
190pub struct Reply {
191 pub status: ReplyStatus,
193 pub response: Payload,
195}
196
197impl Reply {
198 pub fn deserialize<T: serde::de::DeserializeOwned>(&self) -> Result<T, PayloadError> {
200 self.response.deserialize()
201 }
202
203 pub fn into_result(self) -> Result<Payload, Payload> {
205 match self.status {
206 ReplyStatus::Ok => Ok(self.response),
207 ReplyStatus::Error => Err(self.response),
208 }
209 }
210
211 pub fn deserialize_ok<T: DeserializeOwned>(self) -> Result<T, ReplyError> {
213 self.into_result()
214 .map_err(ReplyError::Server)?
215 .deserialize()
216 .map_err(ReplyError::Decode)
217 }
218}
219
220#[derive(Debug, Error)]
222pub enum ReplyError {
223 #[error("Phoenix returned an error reply: {0:?}")]
225 Server(Payload),
226 #[error("failed to decode reply payload: {0}")]
228 Decode(PayloadError),
229}
230
231#[derive(Debug, Error)]
233pub enum CallJsonError {
234 #[error(transparent)]
236 Client(#[from] ClientError),
237 #[error("failed to encode request payload: {0}")]
239 Encode(serde_json::Error),
240 #[error(transparent)]
242 Reply(#[from] ReplyError),
243}
244
245#[derive(Clone, Copy, Debug, Eq, PartialEq)]
247pub enum ClientOperation {
248 Join,
250 Call,
252 Cast,
254 Leave,
256 Ping,
258}
259
260impl std::fmt::Display for ClientOperation {
261 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
262 formatter.write_str(match self {
263 Self::Join => "join",
264 Self::Call => "call",
265 Self::Cast => "cast",
266 Self::Leave => "leave",
267 Self::Ping => "ping",
268 })
269 }
270}
271
272#[derive(Clone, Debug, Error, PartialEq)]
274pub enum ClientError {
275 #[error("the managed client driver stopped")]
277 DriverStopped,
278 #[error("the client command queue is full")]
280 CommandQueueFull,
281 #[error("the unsent push buffer is full for topic {0}")]
283 PushBufferFull(String),
284 #[error("a channel already exists for topic {0}")]
286 DuplicateChannel(String),
287 #[error("channel {0} is already joined")]
289 AlreadyJoined(String),
290 #[error("channel {0} must be joined again before sending events")]
292 ChannelNotJoined(String),
293 #[error("the socket must be connected for this operation")]
295 SocketNotConnected,
296 #[error("the active transport does not support binary Phoenix frames")]
298 BinaryNotSupported,
299 #[error("{operation} timed out after {timeout:?}")]
301 Timeout {
302 operation: ClientOperation,
304 timeout: Duration,
306 },
307 #[error("{operation} was interrupted by connection loss")]
309 Interrupted {
310 operation: ClientOperation,
312 },
313 #[error("protocol error: {0}")]
315 Protocol(String),
316 #[error("join payload loader failed for {topic}: {message}")]
318 JoinPayload {
319 topic: String,
321 message: String,
323 },
324 #[error("channel join was rejected for {topic}: {response:?}")]
326 JoinRejected {
327 topic: String,
329 response: Payload,
331 },
332 #[error("unknown channel topic: {0}")]
334 UnknownTopic(String),
335}
336
337struct ObservableStatus<T> {
338 value: Cell<T>,
339 subscribers: RefCell<Vec<mpsc::Sender<()>>>,
340}
341
342impl<T: Copy + Eq> ObservableStatus<T> {
343 fn new(value: T) -> Self {
344 Self {
345 value: Cell::new(value),
346 subscribers: RefCell::new(Vec::new()),
347 }
348 }
349
350 fn get(&self) -> T {
351 self.value.get()
352 }
353
354 fn set(&self, value: T) {
355 if self.value.replace(value) == value {
356 return;
357 }
358 self.subscribers
359 .borrow_mut()
360 .retain_mut(|subscriber| match subscriber.try_send(()) {
361 Ok(()) => true,
362 Err(error) => error.is_full(),
363 });
364 }
365
366 fn subscribe(self: &Rc<Self>) -> StatusChanges<T> {
367 let (sender, receiver) = mpsc::channel(1);
368 self.subscribers.borrow_mut().push(sender);
369 StatusChanges {
370 receiver,
371 status: self.clone(),
372 }
373 }
374}
375
376pub struct StatusChanges<T> {
378 receiver: mpsc::Receiver<()>,
379 status: Rc<ObservableStatus<T>>,
380}
381
382impl<T: Copy + Eq> StatusChanges<T> {
383 pub fn current(&self) -> T {
385 self.status.get()
386 }
387
388 pub async fn changed(&mut self) -> Option<T> {
390 self.receiver.next().await.map(|()| self.status.get())
391 }
392}
393
394pub type SocketStatusChanges = StatusChanges<SocketStatus>;
396pub type ChannelStatusChanges = StatusChanges<ChannelStatus>;
398
399#[derive(Clone, Debug, PartialEq)]
401pub enum TelemetryEvent {
402 Socket(SocketEvent),
404 Channel {
406 topic: String,
408 event: ChannelEvent,
410 },
411 FrameSent {
413 topic: String,
415 event: String,
417 binary: bool,
419 bytes: usize,
421 },
422 FrameReceived {
424 topic: String,
426 event: String,
428 binary: bool,
430 bytes: usize,
432 },
433 ConnectionAttemptFinished {
435 attempt: u32,
437 duration: Duration,
439 connected: bool,
441 },
442 CallCompleted {
444 topic: String,
446 event: String,
448 outcome: CallOutcome,
450 duration: Duration,
452 },
453}
454
455#[derive(Clone, Copy, Debug, Eq, PartialEq)]
457pub enum CallOutcome {
458 Reply(ReplyStatus),
460 Cancelled,
462 Interrupted,
464 Rejected,
466}
467
468pub type TelemetryHook = Rc<dyn Fn(&TelemetryEvent)>;
470
471#[derive(Clone)]
476pub struct Socket {
477 commands: mpsc::Sender<Command>,
478 lifecycle: mpsc::UnboundedSender<LifecycleCommand>,
479 timer: Rc<dyn Timer>,
480 options: Options,
481 request_ids: Rc<Cell<RequestId>>,
482 topics: Rc<RefCell<HashSet<String>>>,
483 status: Rc<ObservableStatus<SocketStatus>>,
484}
485
486impl Socket {
487 pub fn new(
489 connector: impl Connector + 'static,
490 timer: impl Timer + 'static,
491 options: Options,
492 ) -> (Self, Driver) {
493 Self::new_with_codec(
494 connector,
495 timer,
496 options,
497 PhoenixV2Codec::limited(Default::default()),
498 )
499 }
500
501 pub fn new_with_codec(
503 connector: impl Connector + 'static,
504 timer: impl Timer + 'static,
505 options: Options,
506 codec: impl Codec + 'static,
507 ) -> (Self, Driver) {
508 let (commands, command_rx) = mpsc::channel(options.command_capacity);
509 let (lifecycle, lifecycle_rx) = mpsc::unbounded();
510 let timer: Rc<dyn Timer> = Rc::new(timer);
511 let status = Rc::new(ObservableStatus::new(if options.connect_on_start {
512 SocketStatus::Connecting
513 } else {
514 SocketStatus::Disconnected
515 }));
516 let socket = Self {
517 commands,
518 lifecycle: lifecycle.clone(),
519 timer: timer.clone(),
520 options: options.clone(),
521 request_ids: Rc::new(Cell::new(0)),
522 topics: Rc::new(RefCell::new(HashSet::new())),
523 status: status.clone(),
524 };
525 let state = DriverState::new(
526 Rc::new(connector),
527 timer,
528 options,
529 Rc::new(codec),
530 command_rx,
531 lifecycle_rx,
532 status,
533 );
534 let driver = Driver {
535 inner: Box::pin(state.run()),
536 };
537 (socket, driver)
538 }
539
540 pub fn channel(
544 &self,
545 topic: impl Into<String>,
546 payload_loader: JoinPayloadLoader,
547 ) -> Result<Channel, ClientError> {
548 let topic = topic.into();
549 if !self.topics.borrow_mut().insert(topic.clone()) {
550 return Err(ClientError::DuplicateChannel(topic));
551 }
552 let (events, event_rx) = mpsc::channel(self.options.event_capacity);
553 let status = Rc::new(ObservableStatus::new(ChannelStatus::Closed));
554 if self
555 .lifecycle
556 .unbounded_send(LifecycleCommand::Register {
557 topic: topic.clone(),
558 payload_loader,
559 events,
560 status: status.clone(),
561 })
562 .is_err()
563 {
564 self.topics.borrow_mut().remove(&topic);
565 return Err(ClientError::DriverStopped);
566 }
567 Ok(Channel {
568 topic,
569 commands: self.commands.clone(),
570 lifecycle: self.lifecycle.clone(),
571 timer: self.timer.clone(),
572 timeouts: OperationTimeouts {
573 join: self.options.join_timeout,
574 call: self.options.call_timeout,
575 leave: self.options.leave_timeout,
576 },
577 event_capacity: self.options.event_capacity,
578 request_ids: self.request_ids.clone(),
579 topics: self.topics.clone(),
580 events: event_rx,
581 status,
582 })
583 }
584
585 pub fn events(&self) -> Result<SocketEvents, ClientError> {
587 let (events, receiver) = mpsc::channel(self.options.event_capacity);
588 let mut commands = self.commands.clone();
589 commands
590 .try_send(Command::Subscribe { events })
591 .map_err(command_send_error)?;
592 Ok(SocketEvents { receiver })
593 }
594
595 pub fn status(&self) -> SocketStatus {
597 self.status.get()
598 }
599
600 pub fn status_changes(&self) -> SocketStatusChanges {
602 self.status.subscribe()
603 }
604
605 pub async fn connect(&self) -> Result<(), ClientError> {
607 let (response, receiver) = oneshot::channel();
608 let mut commands = self.commands.clone();
609 commands
610 .send(Command::Connect { response })
611 .await
612 .map_err(|_| ClientError::DriverStopped)?;
613 receiver.await.map_err(|_| ClientError::DriverStopped)
614 }
615
616 pub async fn disconnect(&self) -> Result<(), ClientError> {
618 self.disconnect_inner(None).await
619 }
620
621 pub async fn disconnect_with(
623 &self,
624 code: u16,
625 reason: impl Into<String>,
626 ) -> Result<(), ClientError> {
627 self.disconnect_inner(Some(TransportCloseRequest::new(code, reason)))
628 .await
629 }
630
631 async fn disconnect_inner(
632 &self,
633 close: Option<TransportCloseRequest>,
634 ) -> Result<(), ClientError> {
635 let (response, receiver) = oneshot::channel();
636 let mut commands = self.commands.clone();
637 commands
638 .send(Command::Disconnect { close, response })
639 .await
640 .map_err(|_| ClientError::DriverStopped)?;
641 receiver.await.map_err(|_| ClientError::DriverStopped)
642 }
643
644 pub async fn reconnect(&self) -> Result<(), ClientError> {
646 self.disconnect().await?;
647 self.connect().await
648 }
649
650 pub async fn ping(&self) -> Result<Duration, ClientError> {
652 self.ping_with_timeout(self.options.call_timeout).await
653 }
654
655 pub async fn ping_with_timeout(&self, timeout: Duration) -> Result<Duration, ClientError> {
657 let id = self.next_request_id();
658 let (response, receiver) = oneshot::channel();
659 let mut commands = self.commands.clone();
660 commands
661 .send(Command::Ping { id, response })
662 .await
663 .map_err(|_| ClientError::DriverStopped)?;
664 let mut guard = RequestGuard {
665 id,
666 lifecycle: self.lifecycle.clone(),
667 armed: true,
668 };
669 let response = receiver.fuse();
670 let timeout_future = self.timer.sleep(timeout).fuse();
671 futures::pin_mut!(response, timeout_future);
672 futures::select! {
673 response = response => {
674 guard.armed = false;
675 response.map_err(|_| ClientError::DriverStopped)?
676 },
677 () = timeout_future => Err(ClientError::Timeout {
678 operation: ClientOperation::Ping,
679 timeout,
680 }),
681 }
682 }
683
684 fn next_request_id(&self) -> RequestId {
685 let mut id = self.request_ids.get().wrapping_add(1);
686 if id == 0 {
687 id = 1;
688 }
689 self.request_ids.set(id);
690 id
691 }
692
693 pub async fn shutdown(&self) -> Result<(), ClientError> {
695 let (response, receiver) = oneshot::channel();
696 let mut commands = self.commands.clone();
697 commands
698 .send(Command::Shutdown { response })
699 .await
700 .map_err(|_| ClientError::DriverStopped)?;
701 receiver.await.map_err(|_| ClientError::DriverStopped)
702 }
703}
704
705pub struct SocketEvents {
707 receiver: mpsc::Receiver<SocketEvent>,
708}
709
710impl SocketEvents {
711 pub async fn next(&mut self) -> Option<SocketEvent> {
713 self.receiver.next().await
714 }
715}
716
717pub struct Channel {
721 topic: String,
722 commands: mpsc::Sender<Command>,
723 lifecycle: mpsc::UnboundedSender<LifecycleCommand>,
724 timer: Rc<dyn Timer>,
725 timeouts: OperationTimeouts,
726 event_capacity: usize,
727 request_ids: Rc<Cell<RequestId>>,
728 topics: Rc<RefCell<HashSet<String>>>,
729 events: mpsc::Receiver<ChannelEvent>,
730 status: Rc<ObservableStatus<ChannelStatus>>,
731}
732
733#[derive(Clone, Copy)]
734struct OperationTimeouts {
735 join: Duration,
736 call: Duration,
737 leave: Duration,
738}
739
740impl Channel {
741 pub fn topic(&self) -> &str {
743 &self.topic
744 }
745
746 pub fn status(&self) -> ChannelStatus {
748 self.status.get()
749 }
750
751 pub fn status_changes(&self) -> ChannelStatusChanges {
753 self.status.subscribe()
754 }
755
756 pub async fn join(&self) -> Result<Payload, ClientError> {
758 self.join_with_timeout(self.timeouts.join).await
759 }
760
761 pub async fn join_with_timeout(&self, timeout: Duration) -> Result<Payload, ClientError> {
763 let id = self.next_request_id();
764 let (response, receiver) = oneshot::channel();
765 self.send(Command::Join {
766 id,
767 topic: self.topic.clone(),
768 timeout,
769 response,
770 })
771 .await?;
772 self.wait(id, ClientOperation::Join, timeout, receiver)
773 .await
774 }
775
776 pub async fn call(
778 &self,
779 event: impl Into<String>,
780 payload: impl Into<Payload>,
781 ) -> Result<Reply, ClientError> {
782 self.call_with_timeout(event, payload, self.timeouts.call)
783 .await
784 }
785
786 pub async fn call_with_timeout(
788 &self,
789 event: impl Into<String>,
790 payload: impl Into<Payload>,
791 timeout: Duration,
792 ) -> Result<Reply, ClientError> {
793 let id = self.next_request_id();
794 let (response, receiver) = oneshot::channel();
795 self.send(Command::Call {
796 id,
797 topic: self.topic.clone(),
798 event: event.into(),
799 payload: payload.into(),
800 started: self.timer.now(),
801 response,
802 })
803 .await?;
804 self.wait(id, ClientOperation::Call, timeout, receiver)
805 .await
806 }
807
808 pub async fn call_json<Request, Response>(
810 &self,
811 event: impl Into<String>,
812 request: &Request,
813 ) -> Result<Response, CallJsonError>
814 where
815 Request: Serialize + ?Sized,
816 Response: DeserializeOwned,
817 {
818 let payload = serde_json::to_value(request).map_err(CallJsonError::Encode)?;
819 self.call(event, payload)
820 .await?
821 .deserialize_ok()
822 .map_err(Into::into)
823 }
824
825 pub async fn cast(
827 &self,
828 event: impl Into<String>,
829 payload: impl Into<Payload>,
830 ) -> Result<(), ClientError> {
831 self.cast_with_timeout(event, payload, self.timeouts.call)
832 .await
833 }
834
835 pub async fn cast_with_timeout(
838 &self,
839 event: impl Into<String>,
840 payload: impl Into<Payload>,
841 timeout: Duration,
842 ) -> Result<(), ClientError> {
843 let id = self.next_request_id();
844 let (response, receiver) = oneshot::channel();
845 self.send(Command::Cast {
846 id,
847 topic: self.topic.clone(),
848 event: event.into(),
849 payload: payload.into(),
850 response,
851 })
852 .await?;
853 self.wait(id, ClientOperation::Cast, timeout, receiver)
854 .await
855 }
856
857 pub async fn leave(&self) -> Result<Payload, ClientError> {
859 self.leave_with_timeout(self.timeouts.leave).await
860 }
861
862 pub async fn leave_with_timeout(&self, timeout: Duration) -> Result<Payload, ClientError> {
864 let id = self.next_request_id();
865 let (response, receiver) = oneshot::channel();
866 self.send(Command::Leave {
867 id,
868 topic: self.topic.clone(),
869 timeout,
870 response,
871 })
872 .await?;
873 self.wait(id, ClientOperation::Leave, timeout, receiver)
874 .await
875 }
876
877 pub async fn next_event(&mut self) -> Option<ChannelEvent> {
879 self.events.next().await
880 }
881
882 pub fn events(&self) -> Result<ChannelEvents, ClientError> {
884 let (events, receiver) = mpsc::channel(self.event_capacity);
885 let mut commands = self.commands.clone();
886 commands
887 .try_send(Command::SubscribeChannel {
888 topic: self.topic.clone(),
889 events,
890 })
891 .map_err(command_send_error)?;
892 Ok(ChannelEvents { receiver })
893 }
894
895 pub fn subscribe(&self, event: impl Into<String>) -> Result<EventSubscription, ClientError> {
897 Ok(EventSubscription {
898 event: event.into(),
899 events: self.events()?,
900 })
901 }
902
903 pub fn presence(&self) -> Result<ChannelPresence<'_>, ClientError> {
905 ChannelPresence::new(self)
906 }
907
908 async fn send(&self, command: Command) -> Result<(), ClientError> {
909 let mut commands = self.commands.clone();
910 commands
911 .send(command)
912 .await
913 .map_err(|_| ClientError::DriverStopped)
914 }
915
916 fn next_request_id(&self) -> RequestId {
917 let mut id = self.request_ids.get().wrapping_add(1);
918 if id == 0 {
919 id = 1;
920 }
921 self.request_ids.set(id);
922 id
923 }
924
925 async fn wait<T>(
926 &self,
927 id: RequestId,
928 operation: ClientOperation,
929 timeout_duration: Duration,
930 receiver: oneshot::Receiver<Result<T, ClientError>>,
931 ) -> Result<T, ClientError> {
932 let mut guard = RequestGuard {
933 id,
934 lifecycle: self.lifecycle.clone(),
935 armed: true,
936 };
937 let response = receiver.fuse();
938 let timeout = self.timer.sleep(timeout_duration).fuse();
939 futures::pin_mut!(response, timeout);
940 futures::select! {
941 response = response => {
942 guard.armed = false;
943 response.map_err(|_| ClientError::DriverStopped)?
944 },
945 () = timeout => Err(ClientError::Timeout {
946 operation,
947 timeout: timeout_duration,
948 }),
949 }
950 }
951}
952
953pub struct ChannelEvents {
955 receiver: mpsc::Receiver<ChannelEvent>,
956}
957
958#[derive(Clone, Debug, PartialEq)]
960pub enum SubscriptionEvent {
961 Message(Payload),
963 Disconnected,
965 ChannelError(Payload),
967 ChannelClosed(Payload),
969 Lagged {
973 dropped: u64,
975 },
976}
977
978pub struct EventSubscription {
980 event: String,
981 events: ChannelEvents,
982}
983
984impl EventSubscription {
985 pub fn event(&self) -> &str {
987 &self.event
988 }
989
990 pub async fn next(&mut self) -> Option<SubscriptionEvent> {
992 loop {
993 match self.events.next().await? {
994 ChannelEvent::Protocol(ProtocolEvent::Message(frame))
995 if frame.event == self.event =>
996 {
997 return Some(SubscriptionEvent::Message(frame.payload));
998 }
999 ChannelEvent::Protocol(ProtocolEvent::ChannelError { payload, .. }) => {
1000 return Some(SubscriptionEvent::ChannelError(payload));
1001 }
1002 ChannelEvent::Protocol(ProtocolEvent::ChannelClosed { payload, .. }) => {
1003 return Some(SubscriptionEvent::ChannelClosed(payload));
1004 }
1005 ChannelEvent::Disconnected => return Some(SubscriptionEvent::Disconnected),
1006 ChannelEvent::Lagged { dropped } => {
1007 return Some(SubscriptionEvent::Lagged { dropped });
1008 }
1009 ChannelEvent::Protocol(_) | ChannelEvent::JoinPayloadError(_) => {}
1010 }
1011 }
1012 }
1013}
1014
1015impl ChannelEvents {
1016 pub async fn next(&mut self) -> Option<ChannelEvent> {
1018 self.receiver.next().await
1019 }
1020}
1021
1022impl Drop for Channel {
1023 fn drop(&mut self) {
1024 self.status.set(ChannelStatus::Closed);
1025 self.topics.borrow_mut().remove(&self.topic);
1026 let _ = self.lifecycle.unbounded_send(LifecycleCommand::Unregister {
1027 topic: self.topic.clone(),
1028 });
1029 }
1030}
1031
1032struct RequestGuard {
1033 id: RequestId,
1034 lifecycle: mpsc::UnboundedSender<LifecycleCommand>,
1035 armed: bool,
1036}
1037
1038impl Drop for RequestGuard {
1039 fn drop(&mut self) {
1040 if self.armed {
1041 let _ = self
1042 .lifecycle
1043 .unbounded_send(LifecycleCommand::Cancel { id: self.id });
1044 }
1045 }
1046}
1047
1048fn command_send_error<T>(error: mpsc::TrySendError<T>) -> ClientError {
1049 if error.is_full() {
1050 ClientError::CommandQueueFull
1051 } else {
1052 ClientError::DriverStopped
1053 }
1054}
1055
1056#[must_use = "the driver must be spawned or awaited"]
1060pub struct Driver {
1061 inner: LocalBoxFuture<'static, ()>,
1062}
1063
1064impl Future for Driver {
1065 type Output = ();
1066
1067 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1068 self.inner.as_mut().poll(cx)
1069 }
1070}
1071
1072enum LifecycleCommand {
1073 Register {
1074 topic: String,
1075 payload_loader: JoinPayloadLoader,
1076 events: mpsc::Sender<ChannelEvent>,
1077 status: Rc<ObservableStatus<ChannelStatus>>,
1078 },
1079 Unregister {
1080 topic: String,
1081 },
1082 Cancel {
1083 id: RequestId,
1084 },
1085}
1086
1087enum Command {
1088 Connect {
1089 response: oneshot::Sender<()>,
1090 },
1091 Disconnect {
1092 close: Option<TransportCloseRequest>,
1093 response: oneshot::Sender<()>,
1094 },
1095 Subscribe {
1096 events: mpsc::Sender<SocketEvent>,
1097 },
1098 SubscribeChannel {
1099 topic: String,
1100 events: mpsc::Sender<ChannelEvent>,
1101 },
1102 Ping {
1103 id: RequestId,
1104 response: oneshot::Sender<Result<Duration, ClientError>>,
1105 },
1106 Join {
1107 id: RequestId,
1108 topic: String,
1109 timeout: Duration,
1110 response: oneshot::Sender<Result<Payload, ClientError>>,
1111 },
1112 Call {
1113 id: RequestId,
1114 topic: String,
1115 event: String,
1116 payload: Payload,
1117 started: Duration,
1118 response: oneshot::Sender<Result<Reply, ClientError>>,
1119 },
1120 Cast {
1121 id: RequestId,
1122 topic: String,
1123 event: String,
1124 payload: Payload,
1125 response: oneshot::Sender<Result<(), ClientError>>,
1126 },
1127 Leave {
1128 id: RequestId,
1129 topic: String,
1130 timeout: Duration,
1131 response: oneshot::Sender<Result<Payload, ClientError>>,
1132 },
1133 Shutdown {
1134 response: oneshot::Sender<()>,
1135 },
1136}
1137
1138enum QueuedPush {
1139 Call {
1140 id: RequestId,
1141 event: String,
1142 payload: Payload,
1143 started: Duration,
1144 response: oneshot::Sender<Result<Reply, ClientError>>,
1145 },
1146 Cast {
1147 id: RequestId,
1148 event: String,
1149 payload: Payload,
1150 response: oneshot::Sender<Result<(), ClientError>>,
1151 },
1152}
1153
1154impl QueuedPush {
1155 fn id(&self) -> RequestId {
1156 match self {
1157 Self::Call { id, .. } | Self::Cast { id, .. } => *id,
1158 }
1159 }
1160
1161 fn interrupt(self) {
1162 let operation = match &self {
1163 Self::Call { .. } => ClientOperation::Call,
1164 Self::Cast { .. } => ClientOperation::Cast,
1165 };
1166 self.fail(ClientError::Interrupted { operation });
1167 }
1168
1169 fn fail(self, error: ClientError) {
1170 match self {
1171 Self::Call { response, .. } => {
1172 let _ = response.send(Err(error));
1173 }
1174 Self::Cast { response, .. } => {
1175 let _ = response.send(Err(error));
1176 }
1177 }
1178 }
1179}
1180
1181struct ChannelRecord {
1182 payload_loader: JoinPayloadLoader,
1183 subscribers: Vec<EventSubscriber<ChannelEvent>>,
1184 status: Rc<ObservableStatus<ChannelStatus>>,
1185 desired: bool,
1186 ever_joined: bool,
1187 active_payload: Option<u64>,
1188 join_attempt: u32,
1189 join_timeout: Duration,
1190 rejoin_scheduled: bool,
1191 join_waiters: HashMap<RequestId, oneshot::Sender<Result<Payload, ClientError>>>,
1192 queued: VecDeque<QueuedPush>,
1193 deferred_leave: Option<PendingLeave>,
1194}
1195
1196struct EventSubscriber<T> {
1197 sender: mpsc::Sender<T>,
1198 dropped: u64,
1199}
1200
1201struct PendingCall {
1202 id: RequestId,
1203 topic: String,
1204 event: String,
1205 started: Duration,
1206 response: oneshot::Sender<Result<Reply, ClientError>>,
1207}
1208
1209struct PendingPing {
1210 id: RequestId,
1211 started: Duration,
1212 response: oneshot::Sender<Result<Duration, ClientError>>,
1213}
1214
1215struct PendingLeave {
1216 id: RequestId,
1217 timeout: Duration,
1218 response: Option<oneshot::Sender<Result<Payload, ClientError>>>,
1219}
1220
1221type PayloadResult = (String, u64, Result<Value, String>);
1222
1223enum OperationTimeout {
1224 Join {
1225 topic: String,
1226 reference: String,
1227 duration: Duration,
1228 },
1229 Leave {
1230 topic: String,
1231 reference: String,
1232 duration: Duration,
1233 },
1234}
1235
1236struct DriverState {
1237 connector: Rc<dyn Connector>,
1238 timer: Rc<dyn Timer>,
1239 options: Options,
1240 codec: Rc<dyn Codec>,
1241 commands: mpsc::Receiver<Command>,
1242 lifecycle: mpsc::UnboundedReceiver<LifecycleCommand>,
1243 protocol: Protocol,
1244 channels: HashMap<String, ChannelRecord>,
1245 socket_subscribers: Vec<EventSubscriber<SocketEvent>>,
1246 pending_joins: HashMap<String, String>,
1247 pending_calls: HashMap<String, PendingCall>,
1248 pending_pings: HashMap<String, PendingPing>,
1249 pending_leaves: HashMap<String, PendingLeave>,
1250 next_payload_id: u64,
1251 socket_status: Rc<ObservableStatus<SocketStatus>>,
1252}
1253
1254impl DriverState {
1255 fn new(
1256 connector: Rc<dyn Connector>,
1257 timer: Rc<dyn Timer>,
1258 options: Options,
1259 codec: Rc<dyn Codec>,
1260 commands: mpsc::Receiver<Command>,
1261 lifecycle: mpsc::UnboundedReceiver<LifecycleCommand>,
1262 socket_status: Rc<ObservableStatus<SocketStatus>>,
1263 ) -> Self {
1264 Self {
1265 connector,
1266 timer,
1267 options,
1268 codec,
1269 commands,
1270 lifecycle,
1271 protocol: Protocol::new(),
1272 channels: HashMap::new(),
1273 socket_subscribers: Vec::new(),
1274 pending_joins: HashMap::new(),
1275 pending_calls: HashMap::new(),
1276 pending_pings: HashMap::new(),
1277 pending_leaves: HashMap::new(),
1278 next_payload_id: 0,
1279 socket_status,
1280 }
1281 }
1282
1283 async fn run(mut self) {
1284 let mut connection_enabled = self.options.connect_on_start;
1285 let mut attempt = 0;
1286 loop {
1287 if !connection_enabled {
1288 match self.wait_idle().await {
1289 IdleExit::Connect(response) => {
1290 let _ = response.send(());
1291 connection_enabled = true;
1292 attempt = 0;
1293 }
1294 IdleExit::Shutdown(response) => {
1295 let _ = response.send(());
1296 self.emit_socket(SocketEvent::Closed);
1297 return;
1298 }
1299 }
1300 }
1301
1302 self.emit_socket(SocketEvent::Connecting { attempt });
1303 let connection_started = self.timer.now();
1304 let connection = self.connector.connect(ConnectContext { attempt }).fuse();
1305 let connect_timeout = self.timer.sleep(self.options.connect_timeout).fuse();
1306 futures::pin_mut!(connection, connect_timeout);
1307 let connected = loop {
1308 let lifecycle = self.lifecycle.next().fuse();
1309 let command = self.commands.next().fuse();
1310 futures::pin_mut!(lifecycle, command);
1311 futures::select_biased! {
1312 lifecycle = lifecycle => match lifecycle {
1313 Some(command) => self.handle_offline_lifecycle(command),
1314 None => break None,
1315 },
1316 result = connection => break Some(ConnectAttemptExit::Connected(result)),
1317 () = connect_timeout => break Some(ConnectAttemptExit::Connected(Err(TransportError::with_kind(
1318 TransportErrorKind::Connect,
1319 format!("connection attempt timed out after {:?}", self.options.connect_timeout),
1320 )))),
1321 command = command => match command {
1322 Some(Command::Connect { response }) => {
1323 let _ = response.send(());
1324 }
1325 Some(Command::Disconnect { response, .. }) => {
1326 break Some(ConnectAttemptExit::Disconnect(response));
1327 }
1328 Some(Command::Shutdown { response }) => {
1329 break Some(ConnectAttemptExit::Shutdown(response));
1330 }
1331 Some(command) => {
1332 self.handle_offline_command(command);
1333 }
1334 None => break None,
1335 }
1336 }
1337 };
1338 let Some(connected) = connected else {
1339 self.emit_socket(SocketEvent::Closed);
1340 return;
1341 };
1342 self.telemetry(TelemetryEvent::ConnectionAttemptFinished {
1343 attempt,
1344 duration: self.timer.now().saturating_sub(connection_started),
1345 connected: matches!(&connected, ConnectAttemptExit::Connected(Ok(_))),
1346 });
1347
1348 let retry_reason = match connected {
1349 ConnectAttemptExit::Connected(Ok(mut transport)) => {
1350 attempt = 0;
1351 self.emit_socket(SocketEvent::Connected);
1352 match self.run_connected(&mut transport).await {
1353 ConnectedExit::Shutdown(response) => {
1354 let _ = transport.close().await;
1355 let _ = response.send(());
1356 self.emit_socket(SocketEvent::Closed);
1357 return;
1358 }
1359 ConnectedExit::Disconnect { response, close } => {
1360 if let Some(close) = close {
1361 let _ = transport.close_with(close).await;
1362 } else {
1363 let _ = transport.close().await;
1364 }
1365 self.on_disconnect(&DisconnectReason::Requested);
1366 let _ = response.send(());
1367 connection_enabled = false;
1368 continue;
1369 }
1370 ConnectedExit::Disconnected(reason) => {
1371 let _ = transport.close().await;
1372 self.on_disconnect(&reason);
1373 reason
1374 }
1375 }
1376 }
1377 ConnectAttemptExit::Connected(Err(error)) => {
1378 let reason = DisconnectReason::Connect(error);
1379 self.emit_socket(SocketEvent::Disconnected {
1380 reason: reason.clone(),
1381 });
1382 reason
1383 }
1384 ConnectAttemptExit::Disconnect(response) => {
1385 self.on_disconnect(&DisconnectReason::Requested);
1386 let _ = response.send(());
1387 connection_enabled = false;
1388 continue;
1389 }
1390 ConnectAttemptExit::Shutdown(response) => {
1391 let _ = response.send(());
1392 self.emit_socket(SocketEvent::Closed);
1393 return;
1394 }
1395 };
1396
1397 attempt = attempt.saturating_add(1);
1398 let action = self.options.reconnect_policy.as_ref().map_or_else(
1399 || {
1400 if retry_reason.should_reconnect() {
1401 ReconnectAction::RetryAfter((self.options.reconnect_delay)(attempt))
1402 } else {
1403 ReconnectAction::Stop
1404 }
1405 },
1406 |policy| {
1407 policy(ReconnectContext {
1408 attempt,
1409 reason: retry_reason.clone(),
1410 })
1411 },
1412 );
1413 let ReconnectAction::RetryAfter(delay) = action else {
1414 self.emit_socket(SocketEvent::ReconnectStopped {
1415 attempt,
1416 reason: retry_reason,
1417 });
1418 connection_enabled = false;
1419 continue;
1420 };
1421 self.emit_socket(SocketEvent::ReconnectScheduled { attempt, delay });
1422 match self.wait_offline(delay).await {
1423 OfflineExit::Retry => {}
1424 OfflineExit::Disconnect(response) => {
1425 self.on_disconnect(&DisconnectReason::Requested);
1426 let _ = response.send(());
1427 connection_enabled = false;
1428 }
1429 OfflineExit::Shutdown(response) => {
1430 let _ = response.send(());
1431 self.emit_socket(SocketEvent::Closed);
1432 return;
1433 }
1434 }
1435 }
1436 }
1437
1438 async fn wait_idle(&mut self) -> IdleExit {
1439 loop {
1440 let lifecycle = self.lifecycle.next().fuse();
1441 let command = self.commands.next().fuse();
1442 futures::pin_mut!(lifecycle, command);
1443 futures::select_biased! {
1444 lifecycle = lifecycle => match lifecycle {
1445 Some(command) => self.handle_offline_lifecycle(command),
1446 None => return IdleExit::Shutdown(closed_response()),
1447 },
1448 command = command => match command {
1449 Some(Command::Connect { response }) => return IdleExit::Connect(response),
1450 Some(Command::Disconnect { response, .. }) => {
1451 let _ = response.send(());
1452 }
1453 Some(Command::Shutdown { response }) => return IdleExit::Shutdown(response),
1454 Some(command) => {
1455 self.handle_offline_command(command);
1456 }
1457 None => return IdleExit::Shutdown(closed_response()),
1458 }
1459 }
1460 }
1461 }
1462
1463 async fn wait_offline(&mut self, delay: Duration) -> OfflineExit {
1464 let sleep = self.timer.sleep(delay).fuse();
1465 futures::pin_mut!(sleep);
1466 loop {
1467 let lifecycle = self.lifecycle.next().fuse();
1468 let command = self.commands.next().fuse();
1469 futures::pin_mut!(lifecycle, command);
1470 futures::select_biased! {
1471 lifecycle = lifecycle => match lifecycle {
1472 Some(command) => self.handle_offline_lifecycle(command),
1473 None => return OfflineExit::Shutdown(closed_response()),
1474 },
1475 () = sleep => return OfflineExit::Retry,
1476 command = command => match command {
1477 Some(Command::Connect { response }) => {
1478 let _ = response.send(());
1479 }
1480 Some(Command::Disconnect { response, .. }) => {
1481 return OfflineExit::Disconnect(response);
1482 }
1483 Some(Command::Shutdown { response }) => return OfflineExit::Shutdown(response),
1484 Some(command) => {
1485 self.handle_offline_command(command);
1486 }
1487 None => return OfflineExit::Shutdown(closed_response()),
1488 }
1489 }
1490 }
1491 }
1492
1493 async fn run_connected(&mut self, transport: &mut Box<dyn Transport>) -> ConnectedExit {
1494 let mut payloads: FuturesUnordered<LocalBoxFuture<'static, PayloadResult>> =
1495 FuturesUnordered::new();
1496 let mut rejoins: FuturesUnordered<LocalBoxFuture<'static, String>> =
1497 FuturesUnordered::new();
1498 let mut operation_timeouts: FuturesUnordered<LocalBoxFuture<'static, OperationTimeout>> =
1499 FuturesUnordered::new();
1500 for channel in self.channels.values_mut() {
1501 channel.rejoin_scheduled = false;
1502 }
1503 let desired = self
1504 .channels
1505 .iter()
1506 .filter(|(_, channel)| channel.desired)
1507 .map(|(topic, _)| topic.clone())
1508 .collect::<Vec<_>>();
1509 for topic in desired {
1510 if let Some(channel) = self.channels.get(&topic) {
1511 channel.status.set(ChannelStatus::WaitingToJoin);
1512 }
1513 self.load_payload(&topic, &mut payloads);
1514 }
1515
1516 let mut heartbeat = self.timer.sleep(self.options.heartbeat_interval);
1517 let mut heartbeat_reference: Option<String> = None;
1518
1519 loop {
1520 enum Action {
1521 Lifecycle(Option<LifecycleCommand>),
1522 Command(Option<Command>),
1523 Incoming(Result<TransportEvent, TransportError>),
1524 Heartbeat,
1525 Payload(Option<PayloadResult>),
1526 Rejoin(Option<String>),
1527 OperationTimeout(Option<OperationTimeout>),
1528 }
1529
1530 let action = {
1531 let lifecycle = self.lifecycle.next().fuse();
1532 let command = self.commands.next().fuse();
1533 let incoming = transport.receive().fuse();
1534 let heartbeat_wait = heartbeat.as_mut().fuse();
1535 let payload: LocalBoxFuture<'_, Option<PayloadResult>> = if payloads.is_empty() {
1536 Box::pin(pending())
1537 } else {
1538 Box::pin(payloads.next())
1539 };
1540 let payload = payload.fuse();
1541 let rejoin: LocalBoxFuture<'_, Option<String>> = if rejoins.is_empty() {
1542 Box::pin(pending())
1543 } else {
1544 Box::pin(rejoins.next())
1545 };
1546 let rejoin = rejoin.fuse();
1547 let operation_timeout: LocalBoxFuture<'_, Option<OperationTimeout>> =
1548 if operation_timeouts.is_empty() {
1549 Box::pin(pending())
1550 } else {
1551 Box::pin(operation_timeouts.next())
1552 };
1553 let operation_timeout = operation_timeout.fuse();
1554 futures::pin_mut!(
1555 lifecycle,
1556 command,
1557 incoming,
1558 heartbeat_wait,
1559 payload,
1560 rejoin,
1561 operation_timeout
1562 );
1563 futures::select_biased! {
1564 lifecycle = lifecycle => Action::Lifecycle(lifecycle),
1565 command = command => Action::Command(command),
1566 incoming = incoming => Action::Incoming(incoming),
1567 () = heartbeat_wait => Action::Heartbeat,
1568 payload = payload => Action::Payload(payload),
1569 topic = rejoin => Action::Rejoin(topic),
1570 timeout = operation_timeout => Action::OperationTimeout(timeout),
1571 }
1572 };
1573
1574 let result = match action {
1575 Action::Lifecycle(Some(command)) => {
1576 self.handle_connected_lifecycle(command, transport).await
1577 }
1578 Action::Lifecycle(None) => {
1579 return ConnectedExit::Disconnected(DisconnectReason::DriverStopped);
1580 }
1581 Action::Command(Some(Command::Shutdown { response })) => {
1582 return ConnectedExit::Shutdown(response);
1583 }
1584 Action::Command(Some(Command::Disconnect { close, response })) => {
1585 return ConnectedExit::Disconnect { response, close };
1586 }
1587 Action::Command(Some(Command::Connect { response })) => {
1588 let _ = response.send(());
1589 Ok(())
1590 }
1591 Action::Command(Some(command)) => {
1592 self.handle_connected_command(
1593 command,
1594 transport,
1595 &mut payloads,
1596 &mut operation_timeouts,
1597 )
1598 .await
1599 }
1600 Action::Command(None) => {
1601 return ConnectedExit::Disconnected(DisconnectReason::DriverStopped);
1602 }
1603 Action::Incoming(Ok(TransportEvent::Message(message))) => {
1604 let awaiting_heartbeat = heartbeat_reference.is_some();
1605 let result = self
1606 .handle_incoming(
1607 message,
1608 transport,
1609 &mut payloads,
1610 &mut rejoins,
1611 &mut operation_timeouts,
1612 &mut heartbeat_reference,
1613 )
1614 .await;
1615 if result.is_ok() && awaiting_heartbeat && heartbeat_reference.is_none() {
1616 heartbeat = self.timer.sleep(self.options.heartbeat_interval);
1617 }
1618 result
1619 }
1620 Action::Incoming(Ok(TransportEvent::Closed(close))) => {
1621 Err(DisconnectReason::Closed(close))
1622 }
1623 Action::Incoming(Err(error)) => Err(DisconnectReason::Transport(error)),
1624 Action::Heartbeat => {
1625 if heartbeat_reference.is_some() {
1626 Err(DisconnectReason::HeartbeatTimeout)
1627 } else {
1628 let outbound = self.protocol.heartbeat();
1629 heartbeat_reference = Some(outbound.reference);
1630 let result = self.send_frame(transport, outbound.frame).await;
1631 if result.is_ok() {
1632 heartbeat = self.timer.sleep(self.options.heartbeat_timeout);
1633 }
1634 result
1635 }
1636 }
1637 Action::Payload(Some((topic, payload_id, payload))) => {
1638 self.handle_payload(
1639 topic,
1640 payload_id,
1641 payload,
1642 transport,
1643 &mut operation_timeouts,
1644 )
1645 .await
1646 }
1647 Action::Payload(None) => Ok(()),
1648 Action::Rejoin(Some(topic)) => {
1649 let desired = self.channels.get_mut(&topic).is_some_and(|channel| {
1650 channel.rejoin_scheduled = false;
1651 channel.desired
1652 });
1653 if desired {
1654 self.load_payload(&topic, &mut payloads);
1655 }
1656 Ok(())
1657 }
1658 Action::Rejoin(None) => Ok(()),
1659 Action::OperationTimeout(Some(timeout)) => {
1660 self.handle_operation_timeout(timeout, &mut payloads, &mut rejoins)
1661 }
1662 Action::OperationTimeout(None) => Ok(()),
1663 };
1664
1665 if let Err(reason) = result {
1666 return ConnectedExit::Disconnected(reason);
1667 }
1668 }
1669 }
1670
1671 fn handle_offline_lifecycle(&mut self, command: LifecycleCommand) {
1672 match command {
1673 LifecycleCommand::Register {
1674 topic,
1675 payload_loader,
1676 events,
1677 status,
1678 } => self.register(topic, payload_loader, events, status),
1679 LifecycleCommand::Unregister { topic } => self.unregister(&topic),
1680 LifecycleCommand::Cancel { id } => self.cancel(id),
1681 }
1682 }
1683
1684 fn handle_offline_command(&mut self, command: Command) -> bool {
1685 match command {
1686 Command::Connect { .. } | Command::Disconnect { .. } => {
1687 unreachable!("connection controls are handled by the driver loop")
1688 }
1689 Command::Subscribe { events } => self.socket_subscribers.push(EventSubscriber {
1690 sender: events,
1691 dropped: 0,
1692 }),
1693 Command::SubscribeChannel { topic, events } => self.subscribe_channel(&topic, events),
1694 Command::Ping { response, .. } => {
1695 let _ = response.send(Err(ClientError::SocketNotConnected));
1696 }
1697 Command::Join {
1698 id,
1699 topic,
1700 timeout,
1701 response,
1702 } => {
1703 if let Some(channel) = self.channels.get_mut(&topic) {
1704 channel.desired = true;
1705 channel.join_timeout = channel.join_timeout.max(timeout);
1706 channel.status.set(ChannelStatus::WaitingForSocket);
1707 channel.join_waiters.insert(id, response);
1708 } else {
1709 let _ = response.send(Err(ClientError::UnknownTopic(topic)));
1710 }
1711 }
1712 Command::Call {
1713 id,
1714 topic,
1715 event,
1716 payload,
1717 started,
1718 response,
1719 } => self.queue(
1720 &topic,
1721 QueuedPush::Call {
1722 id,
1723 event,
1724 payload,
1725 started,
1726 response,
1727 },
1728 ),
1729 Command::Cast {
1730 id,
1731 topic,
1732 event,
1733 payload,
1734 response,
1735 } => self.queue(
1736 &topic,
1737 QueuedPush::Cast {
1738 id,
1739 event,
1740 payload,
1741 response,
1742 },
1743 ),
1744 Command::Leave {
1745 topic, response, ..
1746 } => {
1747 if let Some(channel) = self.channels.get(&topic) {
1748 channel.status.set(ChannelStatus::Left);
1749 }
1750 self.stop_channel(&topic);
1751 let _ = response.send(Ok(json!({}).into()));
1752 }
1753 Command::Shutdown { response } => {
1754 let _ = response.send(());
1755 return true;
1756 }
1757 }
1758 false
1759 }
1760
1761 async fn handle_connected_lifecycle(
1762 &mut self,
1763 command: LifecycleCommand,
1764 transport: &mut Box<dyn Transport>,
1765 ) -> Result<(), DisconnectReason> {
1766 match command {
1767 LifecycleCommand::Register {
1768 topic,
1769 payload_loader,
1770 events,
1771 status,
1772 } => self.register(topic, payload_loader, events, status),
1773 LifecycleCommand::Unregister { topic } => {
1774 let leave = if self.protocol.channel_state(&topic) == Some(ChannelState::Joined) {
1775 self.protocol
1776 .leave(&topic)
1777 .ok()
1778 .map(|outbound| outbound.frame)
1779 } else {
1780 None
1781 };
1782 self.unregister(&topic);
1783 if let Some(frame) = leave {
1784 self.send_frame(transport, frame).await?;
1785 }
1786 }
1787 LifecycleCommand::Cancel { id } => self.cancel(id),
1788 }
1789 Ok(())
1790 }
1791
1792 async fn handle_connected_command(
1793 &mut self,
1794 command: Command,
1795 transport: &mut Box<dyn Transport>,
1796 payloads: &mut FuturesUnordered<LocalBoxFuture<'static, PayloadResult>>,
1797 operation_timeouts: &mut FuturesUnordered<LocalBoxFuture<'static, OperationTimeout>>,
1798 ) -> Result<(), DisconnectReason> {
1799 match command {
1800 Command::Subscribe { events } => self.socket_subscribers.push(EventSubscriber {
1801 sender: events,
1802 dropped: 0,
1803 }),
1804 Command::SubscribeChannel { topic, events } => self.subscribe_channel(&topic, events),
1805 Command::Ping { id, response } => {
1806 let outbound = self.protocol.heartbeat();
1807 self.pending_pings.insert(
1808 outbound.reference.clone(),
1809 PendingPing {
1810 id,
1811 started: self.timer.now(),
1812 response,
1813 },
1814 );
1815 self.send_frame(transport, outbound.frame).await?;
1816 }
1817 Command::Join {
1818 id,
1819 topic,
1820 timeout,
1821 response,
1822 } => {
1823 let joined = self.protocol.channel_state(&topic) == Some(ChannelState::Joined);
1824 if let Some(channel) = self.channels.get_mut(&topic) {
1825 channel.desired = true;
1826 if joined {
1827 channel.status.set(ChannelStatus::Joined);
1828 let _ = response.send(Err(ClientError::AlreadyJoined(topic)));
1829 } else {
1830 channel.status.set(ChannelStatus::WaitingToJoin);
1831 channel.join_timeout = channel.join_timeout.max(timeout);
1832 channel.join_waiters.insert(id, response);
1833 self.load_payload(&topic, payloads);
1834 }
1835 } else {
1836 let _ = response.send(Err(ClientError::UnknownTopic(topic)));
1837 }
1838 }
1839 Command::Call {
1840 id,
1841 topic,
1842 event,
1843 payload,
1844 started,
1845 response,
1846 } => {
1847 let push = QueuedPush::Call {
1848 id,
1849 event,
1850 payload,
1851 started,
1852 response,
1853 };
1854 if self.protocol.channel_state(&topic) == Some(ChannelState::Joined) {
1855 self.send_push(&topic, push, transport).await?;
1856 } else {
1857 self.queue(&topic, push);
1858 }
1859 }
1860 Command::Cast {
1861 id,
1862 topic,
1863 event,
1864 payload,
1865 response,
1866 } => {
1867 let push = QueuedPush::Cast {
1868 id,
1869 event,
1870 payload,
1871 response,
1872 };
1873 if self.protocol.channel_state(&topic) == Some(ChannelState::Joined) {
1874 self.send_push(&topic, push, transport).await?;
1875 } else {
1876 self.queue(&topic, push);
1877 }
1878 }
1879 Command::Leave {
1880 id,
1881 topic,
1882 timeout,
1883 response,
1884 } => {
1885 let state = self.protocol.channel_state(&topic);
1886 if let Some(channel) = self.channels.get(&topic) {
1887 let status = match state {
1888 Some(ChannelState::Joined | ChannelState::Joining) => {
1889 ChannelStatus::Leaving
1890 }
1891 _ => ChannelStatus::Left,
1892 };
1893 channel.status.set(status);
1894 }
1895 self.stop_channel(&topic);
1896 if state == Some(ChannelState::Joined) {
1897 let outbound = self
1898 .protocol
1899 .leave(&topic)
1900 .map_err(|error| DisconnectReason::Protocol(error.to_string()))?;
1901 self.pending_leaves.insert(
1902 outbound.reference.clone(),
1903 PendingLeave {
1904 id,
1905 timeout,
1906 response: Some(response),
1907 },
1908 );
1909 self.schedule_operation_timeout(
1910 OperationTimeout::Leave {
1911 topic,
1912 reference: outbound.reference.clone(),
1913 duration: timeout,
1914 },
1915 operation_timeouts,
1916 );
1917 self.send_frame(transport, outbound.frame).await?;
1918 } else if state == Some(ChannelState::Joining) {
1919 if let Some(channel) = self.channels.get_mut(&topic) {
1920 channel.deferred_leave = Some(PendingLeave {
1921 id,
1922 timeout,
1923 response: Some(response),
1924 });
1925 } else {
1926 let _ = response.send(Ok(json!({}).into()));
1927 }
1928 } else {
1929 let _ = response.send(Ok(json!({}).into()));
1930 }
1931 }
1932 Command::Shutdown { .. } => unreachable!("handled by run_connected"),
1933 Command::Connect { .. } | Command::Disconnect { .. } => {
1934 unreachable!("connection controls are handled by run_connected")
1935 }
1936 }
1937 Ok(())
1938 }
1939
1940 fn register(
1941 &mut self,
1942 topic: String,
1943 payload_loader: JoinPayloadLoader,
1944 events: mpsc::Sender<ChannelEvent>,
1945 status: Rc<ObservableStatus<ChannelStatus>>,
1946 ) {
1947 self.channels.entry(topic).or_insert(ChannelRecord {
1948 payload_loader,
1949 subscribers: vec![EventSubscriber {
1950 sender: events,
1951 dropped: 0,
1952 }],
1953 status,
1954 desired: false,
1955 ever_joined: false,
1956 active_payload: None,
1957 join_attempt: 0,
1958 join_timeout: self.options.join_timeout,
1959 rejoin_scheduled: false,
1960 join_waiters: HashMap::new(),
1961 queued: VecDeque::new(),
1962 deferred_leave: None,
1963 });
1964 }
1965
1966 fn subscribe_channel(&mut self, topic: &str, events: mpsc::Sender<ChannelEvent>) {
1967 if let Some(channel) = self.channels.get_mut(topic) {
1968 channel.subscribers.push(EventSubscriber {
1969 sender: events,
1970 dropped: 0,
1971 });
1972 }
1973 }
1974
1975 fn unregister(&mut self, topic: &str) {
1976 self.stop_channel(topic);
1977 self.channels.remove(topic);
1978 self.protocol.discard_channel(topic);
1979 }
1980
1981 fn queue(&mut self, topic: &str, push: QueuedPush) {
1982 if let Some(channel) = self.channels.get_mut(topic) {
1983 if channel.ever_joined && !channel.desired {
1984 push.fail(ClientError::ChannelNotJoined(topic.to_owned()));
1985 return;
1986 }
1987 if channel.queued.len() < self.options.push_buffer_capacity {
1988 channel.queued.push_back(push);
1989 } else {
1990 push.fail(ClientError::PushBufferFull(topic.to_owned()));
1991 }
1992 } else {
1993 push.interrupt();
1994 }
1995 }
1996
1997 fn load_payload(
1998 &mut self,
1999 topic: &str,
2000 payloads: &mut FuturesUnordered<LocalBoxFuture<'static, PayloadResult>>,
2001 ) {
2002 let Some(channel) = self.channels.get_mut(topic) else {
2003 return;
2004 };
2005 if !channel.desired || channel.active_payload.is_some() {
2006 return;
2007 }
2008 if matches!(
2009 self.protocol.channel_state(topic),
2010 Some(ChannelState::Joining | ChannelState::Joined | ChannelState::Leaving)
2011 ) {
2012 return;
2013 }
2014 channel.status.set(ChannelStatus::WaitingToJoin);
2015 self.next_payload_id = self.next_payload_id.wrapping_add(1);
2016 if self.next_payload_id == 0 {
2017 self.next_payload_id = 1;
2018 }
2019 let payload_id = self.next_payload_id;
2020 channel.active_payload = Some(payload_id);
2021 let context = JoinContext {
2022 attempt: channel.join_attempt,
2023 is_rejoin: channel.ever_joined,
2024 };
2025 let loader = channel.payload_loader.clone();
2026 let topic = topic.to_owned();
2027 payloads.push(Box::pin(async move {
2028 let result = loader(context).await;
2029 (topic, payload_id, result)
2030 }));
2031 }
2032
2033 async fn handle_payload(
2034 &mut self,
2035 topic: String,
2036 payload_id: u64,
2037 payload: Result<Value, String>,
2038 transport: &mut Box<dyn Transport>,
2039 operation_timeouts: &mut FuturesUnordered<LocalBoxFuture<'static, OperationTimeout>>,
2040 ) -> Result<(), DisconnectReason> {
2041 let Some(channel) = self.channels.get_mut(&topic) else {
2042 return Ok(());
2043 };
2044 if channel.active_payload != Some(payload_id) {
2045 return Ok(());
2046 }
2047 channel.active_payload = None;
2048 if !channel.desired {
2049 return Ok(());
2050 }
2051 let payload = match payload {
2052 Ok(payload) => payload,
2053 Err(error) => {
2054 channel.desired = false;
2055 channel.status.set(ChannelStatus::Errored);
2056 for (_, waiter) in channel.join_waiters.drain() {
2057 let _ = waiter.send(Err(ClientError::JoinPayload {
2058 topic: topic.clone(),
2059 message: error.clone(),
2060 }));
2061 }
2062 self.emit_channel(&topic, ChannelEvent::JoinPayloadError(error));
2063 return Ok(());
2064 }
2065 };
2066 let join_timeout = channel.join_timeout;
2067 let outbound = match self.protocol.channel_state(&topic) {
2068 Some(ChannelState::Disconnected | ChannelState::Errored | ChannelState::Closed) => {
2069 self.protocol.rejoin(&topic, payload)
2070 }
2071 None => self.protocol.join(&topic, payload),
2072 Some(_) => return Ok(()),
2073 }
2074 .map_err(|error| DisconnectReason::Protocol(error.to_string()))?;
2075 if let Some(channel) = self.channels.get(&topic) {
2076 channel.status.set(ChannelStatus::Joining);
2077 }
2078 self.pending_joins
2079 .insert(outbound.reference.clone(), topic.clone());
2080 self.schedule_operation_timeout(
2081 OperationTimeout::Join {
2082 topic,
2083 reference: outbound.reference.clone(),
2084 duration: join_timeout,
2085 },
2086 operation_timeouts,
2087 );
2088 self.send_frame(transport, outbound.frame).await
2089 }
2090
2091 async fn handle_incoming(
2092 &mut self,
2093 message: WireMessage,
2094 transport: &mut Box<dyn Transport>,
2095 payloads: &mut FuturesUnordered<LocalBoxFuture<'static, PayloadResult>>,
2096 rejoins: &mut FuturesUnordered<LocalBoxFuture<'static, String>>,
2097 operation_timeouts: &mut FuturesUnordered<LocalBoxFuture<'static, OperationTimeout>>,
2098 heartbeat_reference: &mut Option<String>,
2099 ) -> Result<(), DisconnectReason> {
2100 let (binary, bytes) = wire_metadata(&message);
2101 let frame = self
2102 .codec
2103 .decode(message)
2104 .map_err(|error| DisconnectReason::Protocol(error.to_string()))?;
2105 self.telemetry(TelemetryEvent::FrameReceived {
2106 topic: frame.topic.clone(),
2107 event: frame.event.clone(),
2108 binary,
2109 bytes,
2110 });
2111 let event = self
2112 .protocol
2113 .receive(frame)
2114 .map_err(|error| DisconnectReason::Protocol(error.to_string()))?;
2115
2116 match &event {
2117 ProtocolEvent::Joined {
2118 topic,
2119 reference,
2120 response,
2121 } => {
2122 self.pending_joins.remove(reference);
2123 let deferred_leave = if let Some(channel) = self.channels.get_mut(topic) {
2124 channel.ever_joined = true;
2125 channel.join_attempt = 0;
2126 channel.rejoin_scheduled = false;
2127 let deferred_leave = channel.deferred_leave.take();
2128 channel.status.set(if deferred_leave.is_some() {
2129 ChannelStatus::Leaving
2130 } else {
2131 ChannelStatus::Joined
2132 });
2133 for (_, waiter) in channel.join_waiters.drain() {
2134 let _ = waiter.send(Ok(response.clone()));
2135 }
2136 deferred_leave
2137 } else {
2138 None
2139 };
2140 self.emit_channel(topic, ChannelEvent::Protocol(event.clone()));
2141 if let Some(pending) = deferred_leave {
2142 let timeout = pending.timeout;
2143 let outbound = self
2144 .protocol
2145 .leave(topic)
2146 .map_err(|error| DisconnectReason::Protocol(error.to_string()))?;
2147 self.pending_leaves
2148 .insert(outbound.reference.clone(), pending);
2149 self.schedule_operation_timeout(
2150 OperationTimeout::Leave {
2151 topic: topic.clone(),
2152 reference: outbound.reference.clone(),
2153 duration: timeout,
2154 },
2155 operation_timeouts,
2156 );
2157 self.send_frame(transport, outbound.frame).await?;
2158 } else {
2159 self.flush(topic, transport).await?;
2160 }
2161 }
2162 ProtocolEvent::JoinError {
2163 topic,
2164 reference,
2165 response,
2166 } => {
2167 self.pending_joins.remove(reference);
2168 if let Some(channel) = self.channels.get_mut(topic) {
2169 channel.status.set(ChannelStatus::Errored);
2170 if let Some(pending) = channel.deferred_leave.take() {
2171 if let Some(response) = pending.response {
2172 let _ = response.send(Ok(json!({}).into()));
2173 }
2174 channel.status.set(ChannelStatus::Left);
2175 }
2176 for (_, waiter) in channel.join_waiters.drain() {
2177 let _ = waiter.send(Err(ClientError::JoinRejected {
2178 topic: topic.clone(),
2179 response: response.clone(),
2180 }));
2181 }
2182 }
2183 self.emit_channel(topic, ChannelEvent::Protocol(event.clone()));
2184 self.schedule_rejoin(topic, rejoins);
2185 }
2186 ProtocolEvent::Reply {
2187 reference,
2188 status,
2189 response,
2190 topic,
2191 event: _,
2192 } => {
2193 if let Some(pending) = self.pending_calls.remove(reference) {
2194 self.telemetry(TelemetryEvent::CallCompleted {
2195 topic: pending.topic.clone(),
2196 event: pending.event.clone(),
2197 outcome: CallOutcome::Reply(*status),
2198 duration: self.timer.now().saturating_sub(pending.started),
2199 });
2200 let _ = pending.response.send(Ok(Reply {
2201 status: *status,
2202 response: response.clone(),
2203 }));
2204 }
2205 self.emit_channel(topic, ChannelEvent::Protocol(event.clone()));
2206 }
2207 ProtocolEvent::Left {
2208 reference,
2209 response,
2210 topic,
2211 } => {
2212 if let Some(pending) = self.pending_leaves.remove(reference) {
2213 if let Some(response_tx) = pending.response {
2214 let _ = response_tx.send(Ok(response.clone()));
2215 }
2216 }
2217 let should_rejoin = self
2218 .channels
2219 .get(topic)
2220 .is_some_and(|channel| channel.desired);
2221 if let Some(channel) = self.channels.get(topic) {
2222 channel.status.set(if should_rejoin {
2223 ChannelStatus::WaitingToJoin
2224 } else {
2225 ChannelStatus::Left
2226 });
2227 }
2228 self.emit_channel(topic, ChannelEvent::Protocol(event.clone()));
2229 if should_rejoin {
2230 self.load_payload(topic, payloads);
2231 }
2232 }
2233 ProtocolEvent::HeartbeatAck { reference, .. } => {
2234 if heartbeat_reference.as_deref() == Some(reference) {
2235 *heartbeat_reference = None;
2236 } else if let Some(ping) = self.pending_pings.remove(reference) {
2237 let elapsed = self.timer.now().saturating_sub(ping.started);
2238 let _ = ping.response.send(Ok(elapsed));
2239 }
2240 }
2241 ProtocolEvent::ChannelError { topic, .. } => {
2242 self.pending_joins
2243 .retain(|_, pending_topic| pending_topic != topic);
2244 if let Some(channel) = self.channels.get(topic) {
2245 channel.status.set(ChannelStatus::Errored);
2246 }
2247 self.emit_channel(topic, ChannelEvent::Protocol(event.clone()));
2248 self.schedule_rejoin(topic, rejoins);
2249 }
2250 ProtocolEvent::ChannelClosed { topic, .. } => {
2251 self.pending_joins
2252 .retain(|_, pending_topic| pending_topic != topic);
2253 if let Some(channel) = self.channels.get_mut(topic) {
2254 channel.desired = false;
2255 channel.status.set(ChannelStatus::Closed);
2256 }
2257 self.emit_channel(topic, ChannelEvent::Protocol(event.clone()));
2258 }
2259 ProtocolEvent::Message(frame) | ProtocolEvent::StaleMessage(frame) => {
2260 self.emit_channel(&frame.topic, ChannelEvent::Protocol(event.clone()));
2261 }
2262 ProtocolEvent::RequestInterrupted { topic, .. } => {
2263 self.emit_channel(topic, ChannelEvent::Protocol(event.clone()));
2264 }
2265 ProtocolEvent::UnmatchedReply(frame) => {
2266 self.emit_channel(&frame.topic, ChannelEvent::Protocol(event.clone()));
2267 }
2268 }
2269
2270 if let ProtocolEvent::ChannelClosed { topic, .. } = &event {
2271 self.stop_channel(topic);
2272 }
2273 Ok(())
2274 }
2275
2276 fn schedule_rejoin(
2277 &mut self,
2278 topic: &str,
2279 rejoins: &mut FuturesUnordered<LocalBoxFuture<'static, String>>,
2280 ) {
2281 let Some(channel) = self.channels.get_mut(topic) else {
2282 return;
2283 };
2284 if !channel.desired || channel.rejoin_scheduled {
2285 return;
2286 }
2287 channel.rejoin_scheduled = true;
2288 channel.join_attempt = channel.join_attempt.saturating_add(1);
2289 let delay = (self.options.rejoin_delay)(channel.join_attempt);
2290 let timer = self.timer.clone();
2291 let topic = topic.to_owned();
2292 rejoins.push(Box::pin(async move {
2293 timer.sleep(delay).await;
2294 topic
2295 }));
2296 }
2297
2298 fn schedule_operation_timeout(
2299 &self,
2300 operation: OperationTimeout,
2301 timeouts: &mut FuturesUnordered<LocalBoxFuture<'static, OperationTimeout>>,
2302 ) {
2303 let timer = self.timer.clone();
2304 let timeout = match operation {
2305 OperationTimeout::Join { duration, .. } | OperationTimeout::Leave { duration, .. } => {
2306 duration
2307 }
2308 };
2309 timeouts.push(Box::pin(async move {
2310 timer.sleep(timeout).await;
2311 operation
2312 }));
2313 }
2314
2315 fn handle_operation_timeout(
2316 &mut self,
2317 timeout: OperationTimeout,
2318 payloads: &mut FuturesUnordered<LocalBoxFuture<'static, PayloadResult>>,
2319 rejoins: &mut FuturesUnordered<LocalBoxFuture<'static, String>>,
2320 ) -> Result<(), DisconnectReason> {
2321 match timeout {
2322 OperationTimeout::Join {
2323 topic,
2324 reference,
2325 duration,
2326 } => {
2327 if self.pending_joins.remove(&reference).as_deref() != Some(topic.as_str()) {
2328 return Ok(());
2329 }
2330 self.protocol.discard_channel(&topic);
2331 let mut should_rejoin = false;
2332 if let Some(channel) = self.channels.get_mut(&topic) {
2333 channel.active_payload = None;
2334 if let Some(pending) = channel.deferred_leave.take() {
2335 if let Some(response) = pending.response {
2336 let _ = response.send(Err(ClientError::Timeout {
2337 operation: ClientOperation::Leave,
2338 timeout: pending.timeout,
2339 }));
2340 }
2341 channel.status.set(ChannelStatus::Left);
2342 } else if channel.desired {
2343 channel.status.set(ChannelStatus::Errored);
2344 for (_, waiter) in channel.join_waiters.drain() {
2345 let _ = waiter.send(Err(ClientError::Timeout {
2346 operation: ClientOperation::Join,
2347 timeout: duration,
2348 }));
2349 }
2350 should_rejoin = true;
2351 } else {
2352 channel.status.set(ChannelStatus::Left);
2353 }
2354 }
2355 if should_rejoin {
2356 self.schedule_rejoin(&topic, rejoins);
2357 }
2358 }
2359 OperationTimeout::Leave {
2360 topic,
2361 reference,
2362 duration,
2363 } => {
2364 let Some(pending) = self.pending_leaves.remove(&reference) else {
2365 return Ok(());
2366 };
2367 if let Some(response) = pending.response {
2368 let _ = response.send(Err(ClientError::Timeout {
2369 operation: ClientOperation::Leave,
2370 timeout: duration,
2371 }));
2372 }
2373 self.protocol.discard_channel(&topic);
2374 let should_rejoin = self
2375 .channels
2376 .get(&topic)
2377 .is_some_and(|channel| channel.desired);
2378 if let Some(channel) = self.channels.get(&topic) {
2379 channel.status.set(if should_rejoin {
2380 ChannelStatus::WaitingToJoin
2381 } else {
2382 ChannelStatus::Left
2383 });
2384 }
2385 if should_rejoin {
2386 self.load_payload(&topic, payloads);
2387 }
2388 }
2389 }
2390 Ok(())
2391 }
2392
2393 async fn flush(
2394 &mut self,
2395 topic: &str,
2396 transport: &mut Box<dyn Transport>,
2397 ) -> Result<(), DisconnectReason> {
2398 loop {
2399 let push = self
2400 .channels
2401 .get_mut(topic)
2402 .and_then(|channel| channel.queued.pop_front());
2403 let Some(push) = push else {
2404 return Ok(());
2405 };
2406 self.send_push(topic, push, transport).await?;
2407 }
2408 }
2409
2410 async fn send_push(
2411 &mut self,
2412 topic: &str,
2413 push: QueuedPush,
2414 transport: &mut Box<dyn Transport>,
2415 ) -> Result<(), DisconnectReason> {
2416 match push {
2417 QueuedPush::Call {
2418 id,
2419 event,
2420 payload,
2421 started,
2422 response,
2423 } => {
2424 if matches!(&payload, Payload::Binary(_)) && !transport.supports_binary() {
2425 self.emit_call_completed(
2426 topic.to_owned(),
2427 event,
2428 started,
2429 CallOutcome::Rejected,
2430 );
2431 let _ = response.send(Err(ClientError::BinaryNotSupported));
2432 return Ok(());
2433 }
2434 let outbound = self
2435 .protocol
2436 .push(topic, event, payload)
2437 .map_err(|error| DisconnectReason::Protocol(error.to_string()))?;
2438 self.pending_calls.insert(
2439 outbound.reference.clone(),
2440 PendingCall {
2441 id,
2442 topic: topic.to_owned(),
2443 event: outbound.frame.event.clone(),
2444 started,
2445 response,
2446 },
2447 );
2448 self.send_frame(transport, outbound.frame).await
2449 }
2450 QueuedPush::Cast {
2451 event,
2452 payload,
2453 response,
2454 ..
2455 } => {
2456 if matches!(&payload, Payload::Binary(_)) && !transport.supports_binary() {
2457 let _ = response.send(Err(ClientError::BinaryNotSupported));
2458 return Ok(());
2459 }
2460 let frame = self
2461 .protocol
2462 .cast(topic, event, payload)
2463 .map_err(|error| DisconnectReason::Protocol(error.to_string()))?;
2464 match self.send_frame(transport, frame).await {
2465 Ok(()) => {
2466 let _ = response.send(Ok(()));
2467 Ok(())
2468 }
2469 Err(error) => {
2470 let _ = response.send(Err(ClientError::Interrupted {
2471 operation: ClientOperation::Cast,
2472 }));
2473 Err(error)
2474 }
2475 }
2476 }
2477 }
2478 }
2479
2480 async fn send_frame(
2481 &mut self,
2482 transport: &mut Box<dyn Transport>,
2483 frame: Frame,
2484 ) -> Result<(), DisconnectReason> {
2485 let topic = frame.topic.clone();
2486 let event = frame.event.clone();
2487 let message = self
2488 .codec
2489 .encode(&frame)
2490 .map_err(|error| DisconnectReason::Protocol(error.to_string()))?;
2491 let (binary, bytes) = wire_metadata(&message);
2492 self.telemetry(TelemetryEvent::FrameSent {
2493 topic,
2494 event,
2495 binary,
2496 bytes,
2497 });
2498 transport
2499 .send(message)
2500 .await
2501 .map_err(DisconnectReason::Transport)
2502 }
2503
2504 fn cancel(&mut self, id: RequestId) {
2505 let cancelled_join = self.channels.iter_mut().find_map(|(topic, channel)| {
2506 channel.join_waiters.remove(&id).map(|_| {
2507 (
2508 topic.clone(),
2509 channel.join_waiters.is_empty() && !channel.ever_joined,
2510 )
2511 })
2512 });
2513 if let Some((topic, stop_join)) = cancelled_join {
2514 if stop_join {
2515 if let Some(channel) = self.channels.get_mut(&topic) {
2516 channel.desired = false;
2517 channel.active_payload = None;
2518 channel.status.set(ChannelStatus::Left);
2519 }
2520 self.pending_joins
2521 .retain(|_, pending_topic| pending_topic != &topic);
2522 self.protocol.discard_channel(&topic);
2523 }
2524 return;
2525 }
2526 let mut removed_queued = false;
2527 let mut cancelled_call = None;
2528 for (topic, channel) in &mut self.channels {
2529 if let Some(pending) = channel
2530 .deferred_leave
2531 .as_mut()
2532 .filter(|pending| pending.id == id)
2533 {
2534 pending.response.take();
2535 return;
2536 }
2537 if let Some(index) = channel.queued.iter().position(|push| push.id() == id) {
2538 if let Some(QueuedPush::Call { event, started, .. }) = channel.queued.remove(index)
2539 {
2540 cancelled_call = Some((topic.clone(), event, started));
2541 }
2542 removed_queued = true;
2543 break;
2544 }
2545 }
2546 if let Some((topic, event, started)) = cancelled_call {
2547 self.emit_call_completed(topic, event, started, CallOutcome::Cancelled);
2548 }
2549 if removed_queued {
2550 return;
2551 }
2552 let reference = self
2553 .pending_calls
2554 .iter()
2555 .find_map(|(reference, pending)| (pending.id == id).then(|| reference.clone()));
2556 if let Some(reference) = reference {
2557 if let Some(pending) = self.pending_calls.remove(&reference) {
2558 self.emit_call_completed(
2559 pending.topic,
2560 pending.event,
2561 pending.started,
2562 CallOutcome::Cancelled,
2563 );
2564 }
2565 self.protocol.forget_push(&reference);
2566 }
2567 let reference = self
2568 .pending_pings
2569 .iter()
2570 .find_map(|(reference, pending)| (pending.id == id).then(|| reference.clone()));
2571 if let Some(reference) = reference {
2572 self.pending_pings.remove(&reference);
2573 self.protocol.forget_heartbeat(&reference);
2574 }
2575 let reference = self
2576 .pending_leaves
2577 .iter()
2578 .find_map(|(reference, pending)| (pending.id == id).then(|| reference.clone()));
2579 if let Some(reference) = reference {
2580 if let Some(pending) = self.pending_leaves.get_mut(&reference) {
2581 pending.response.take();
2582 }
2583 }
2584 }
2585
2586 fn stop_channel(&mut self, topic: &str) {
2587 let mut interrupted_calls = Vec::new();
2588 if let Some(channel) = self.channels.get_mut(topic) {
2589 channel.desired = false;
2590 channel.active_payload = None;
2591 channel.rejoin_scheduled = false;
2592 if let Some(pending) = channel.deferred_leave.take() {
2593 if let Some(response) = pending.response {
2594 let _ = response.send(Err(ClientError::Interrupted {
2595 operation: ClientOperation::Leave,
2596 }));
2597 }
2598 }
2599 for (_, waiter) in channel.join_waiters.drain() {
2600 let _ = waiter.send(Err(ClientError::Interrupted {
2601 operation: ClientOperation::Join,
2602 }));
2603 }
2604 for push in channel.queued.drain(..) {
2605 match push {
2606 QueuedPush::Call {
2607 event,
2608 started,
2609 response,
2610 ..
2611 } => {
2612 interrupted_calls.push((event, started));
2613 let _ = response.send(Err(ClientError::Interrupted {
2614 operation: ClientOperation::Call,
2615 }));
2616 }
2617 push @ QueuedPush::Cast { .. } => push.interrupt(),
2618 }
2619 }
2620 }
2621 for (event, started) in interrupted_calls {
2622 self.emit_call_completed(topic.to_owned(), event, started, CallOutcome::Interrupted);
2623 }
2624 }
2625
2626 fn on_disconnect(&mut self, reason: &DisconnectReason) {
2627 let events = self.protocol.reset_connection();
2628 self.pending_joins.clear();
2629 let interrupted_calls = self
2630 .pending_calls
2631 .drain()
2632 .map(|(_, pending)| pending)
2633 .collect::<Vec<_>>();
2634 for pending in interrupted_calls {
2635 self.emit_call_completed(
2636 pending.topic,
2637 pending.event,
2638 pending.started,
2639 CallOutcome::Interrupted,
2640 );
2641 let _ = pending.response.send(Err(ClientError::Interrupted {
2642 operation: ClientOperation::Call,
2643 }));
2644 }
2645 for (_, pending) in self.pending_pings.drain() {
2646 let _ = pending.response.send(Err(ClientError::Interrupted {
2647 operation: ClientOperation::Ping,
2648 }));
2649 }
2650 for (_, pending) in self.pending_leaves.drain() {
2651 if let Some(response) = pending.response {
2652 let _ = response.send(Err(ClientError::Interrupted {
2653 operation: ClientOperation::Leave,
2654 }));
2655 }
2656 }
2657 let topics = self.channels.keys().cloned().collect::<Vec<_>>();
2658 for channel in self.channels.values_mut() {
2659 channel.active_payload = None;
2660 channel.rejoin_scheduled = false;
2661 if let Some(pending) = channel.deferred_leave.take() {
2662 if let Some(response) = pending.response {
2663 let _ = response.send(Err(ClientError::Interrupted {
2664 operation: ClientOperation::Leave,
2665 }));
2666 }
2667 }
2668 let status = if channel.desired {
2669 ChannelStatus::WaitingForSocket
2670 } else {
2671 ChannelStatus::Left
2672 };
2673 channel.status.set(status);
2674 }
2675 for topic in topics {
2676 self.emit_channel(&topic, ChannelEvent::Disconnected);
2677 }
2678 for event in events {
2679 if let ProtocolEvent::RequestInterrupted { topic, .. } = &event {
2680 self.emit_channel(topic, ChannelEvent::Protocol(event.clone()));
2681 }
2682 }
2683 self.emit_socket(SocketEvent::Disconnected {
2684 reason: reason.clone(),
2685 });
2686 }
2687
2688 fn emit_socket(&mut self, event: SocketEvent) {
2689 self.telemetry(TelemetryEvent::Socket(event.clone()));
2690 let status = match &event {
2691 SocketEvent::Connecting { .. } => SocketStatus::Connecting,
2692 SocketEvent::Connected => SocketStatus::Connected,
2693 SocketEvent::Disconnected { reason } if !reason.should_reconnect() => {
2694 SocketStatus::Disconnected
2695 }
2696 SocketEvent::ReconnectStopped { .. } => SocketStatus::Disconnected,
2697 SocketEvent::Disconnected { .. } | SocketEvent::ReconnectScheduled { .. } => {
2698 SocketStatus::WaitingToReconnect
2699 }
2700 SocketEvent::Closed => SocketStatus::Closed,
2701 SocketEvent::Lagged { .. } => self.socket_status.get(),
2702 };
2703 self.socket_status.set(status);
2704 if status == SocketStatus::Closed {
2705 for channel in self.channels.values() {
2706 channel.status.set(ChannelStatus::Closed);
2707 }
2708 }
2709 self.socket_subscribers
2710 .retain_mut(|subscriber| send_bounded(subscriber, event.clone()));
2711 }
2712
2713 fn emit_channel(&mut self, topic: &str, event: ChannelEvent) {
2714 self.telemetry(TelemetryEvent::Channel {
2715 topic: topic.to_owned(),
2716 event: event.clone(),
2717 });
2718 if let Some(channel) = self.channels.get_mut(topic) {
2719 channel
2720 .subscribers
2721 .retain_mut(|subscriber| send_bounded(subscriber, event.clone()));
2722 }
2723 }
2724
2725 fn telemetry(&self, event: TelemetryEvent) {
2726 if let Some(hook) = &self.options.telemetry {
2727 hook(&event);
2728 }
2729 }
2730
2731 fn emit_call_completed(
2732 &self,
2733 topic: String,
2734 event: String,
2735 started: Duration,
2736 outcome: CallOutcome,
2737 ) {
2738 self.telemetry(TelemetryEvent::CallCompleted {
2739 topic,
2740 event,
2741 outcome,
2742 duration: self.timer.now().saturating_sub(started),
2743 });
2744 }
2745}
2746
2747fn wire_metadata(message: &WireMessage) -> (bool, usize) {
2748 match message {
2749 WireMessage::Text(value) => (false, value.len()),
2750 WireMessage::Binary(value) => (true, value.len()),
2751 }
2752}
2753
2754trait LaggedEvent {
2755 fn lagged(dropped: u64) -> Self;
2756}
2757
2758impl LaggedEvent for SocketEvent {
2759 fn lagged(dropped: u64) -> Self {
2760 Self::Lagged { dropped }
2761 }
2762}
2763
2764impl LaggedEvent for ChannelEvent {
2765 fn lagged(dropped: u64) -> Self {
2766 Self::Lagged { dropped }
2767 }
2768}
2769
2770fn send_bounded<T: Clone + LaggedEvent>(subscriber: &mut EventSubscriber<T>, event: T) -> bool {
2771 if subscriber.dropped > 0 {
2772 match subscriber.sender.try_send(T::lagged(subscriber.dropped)) {
2773 Ok(()) => subscriber.dropped = 0,
2774 Err(error) if error.is_full() => {
2775 subscriber.dropped = subscriber.dropped.saturating_add(1);
2776 return true;
2777 }
2778 Err(_) => return false,
2779 }
2780 }
2781 match subscriber.sender.try_send(event) {
2782 Ok(()) => true,
2783 Err(error) if error.is_full() => {
2784 subscriber.dropped = 1;
2785 true
2786 }
2787 Err(_) => false,
2788 }
2789}
2790
2791enum ConnectedExit {
2792 Shutdown(oneshot::Sender<()>),
2793 Disconnect {
2794 response: oneshot::Sender<()>,
2795 close: Option<TransportCloseRequest>,
2796 },
2797 Disconnected(DisconnectReason),
2798}
2799
2800enum ConnectAttemptExit {
2801 Connected(Result<Box<dyn Transport>, TransportError>),
2802 Disconnect(oneshot::Sender<()>),
2803 Shutdown(oneshot::Sender<()>),
2804}
2805
2806enum OfflineExit {
2807 Retry,
2808 Disconnect(oneshot::Sender<()>),
2809 Shutdown(oneshot::Sender<()>),
2810}
2811
2812enum IdleExit {
2813 Connect(oneshot::Sender<()>),
2814 Shutdown(oneshot::Sender<()>),
2815}
2816
2817fn closed_response() -> oneshot::Sender<()> {
2818 let (response, receiver) = oneshot::channel();
2819 drop(receiver);
2820 response
2821}
2822
2823#[cfg(test)]
2824mod tests;