rivetkit_core/
websocket.rs1use std::fmt;
2use std::sync::Arc;
3
4use anyhow::Result;
5use futures::future::BoxFuture;
6use parking_lot::RwLock;
7use rivet_envoy_client::config::WebSocketSender;
8
9use crate::actor::context::WebSocketCallbackRegion;
10use crate::error::ActorRuntime;
11use crate::types::WsMessage;
12
13pub(crate) type WebSocketSendCallback = Arc<dyn Fn(WsMessage) -> Result<()> + Send + Sync>;
18pub(crate) type WebSocketCloseCallback =
19 Arc<dyn Fn(Option<u16>, Option<String>) -> BoxFuture<'static, Result<()>> + Send + Sync>;
20pub(crate) type WebSocketMessageEventCallback =
21 Arc<dyn Fn(WsMessage, Option<u16>) -> Result<()> + Send + Sync>;
22pub(crate) type WebSocketCloseEventCallback =
23 Arc<dyn Fn(u16, String, bool) -> BoxFuture<'static, Result<()>> + Send + Sync>;
24pub(crate) type WebSocketCallbackRegionFactory =
25 Arc<dyn Fn() -> WebSocketCallbackRegion + Send + Sync>;
26
27#[derive(Clone)]
28pub struct WebSocket(Arc<WebSocketInner>);
29
30struct WebSocketInner {
31 send_callback: RwLock<Option<WebSocketSendCallback>>,
34 close_callback: RwLock<Option<WebSocketCloseCallback>>,
35 message_event_callback: RwLock<Option<WebSocketMessageEventCallback>>,
36 close_event_callback: RwLock<Option<WebSocketCloseEventCallback>>,
37 close_event_callback_region: RwLock<Option<WebSocketCallbackRegionFactory>>,
38}
39
40impl WebSocket {
41 pub fn new() -> Self {
42 Self(Arc::new(WebSocketInner {
43 send_callback: RwLock::new(None),
44 close_callback: RwLock::new(None),
45 message_event_callback: RwLock::new(None),
46 close_event_callback: RwLock::new(None),
47 close_event_callback_region: RwLock::new(None),
48 }))
49 }
50
51 pub fn from_sender(sender: WebSocketSender) -> Self {
52 let websocket = Self::new();
53 websocket.configure_sender(sender);
54 websocket
55 }
56
57 pub fn send(&self, msg: WsMessage) {
58 if let Err(error) = self.try_send(msg) {
59 tracing::error!(?error, "failed to send websocket message");
60 }
61 }
62
63 pub async fn close(&self, code: Option<u16>, reason: Option<String>) {
64 if let Err(error) = self.try_close(code, reason).await {
65 tracing::error!(?error, "failed to close websocket");
66 }
67 }
68
69 pub fn dispatch_message_event(&self, msg: WsMessage, message_index: Option<u16>) {
70 if let Err(error) = self.try_dispatch_message_event(msg, message_index) {
71 tracing::error!(?error, "failed to dispatch websocket message event");
72 }
73 }
74
75 pub async fn dispatch_close_event(&self, code: u16, reason: String, was_clean: bool) {
76 if let Err(error) = self.try_dispatch_close_event(code, reason, was_clean).await {
77 tracing::error!(?error, "failed to dispatch websocket close event");
78 }
79 self.configure_message_event_callback(None);
80 self.configure_close_event_callback(None);
81 self.configure_close_event_callback_region(None);
82 }
83
84 pub fn configure_sender(&self, sender: WebSocketSender) {
85 let send_sender = sender.clone();
86 let close_sender = sender;
87 self.configure_send_callback(Some(Arc::new(move |message| {
88 match message {
89 WsMessage::Text(text) => send_sender.send_text(&text),
90 WsMessage::Binary(bytes) => send_sender.send(bytes, true),
91 }
92 Ok(())
93 })));
94 self.configure_close_callback(Some(Arc::new(move |code, reason| {
95 let close_sender = close_sender.clone();
96 Box::pin(async move {
97 close_sender.close(code, reason);
98 Ok(())
99 })
100 })));
101 }
102
103 pub(crate) fn configure_send_callback(&self, send_callback: Option<WebSocketSendCallback>) {
104 *self.0.send_callback.write() = send_callback;
105 }
106
107 pub(crate) fn configure_close_callback(&self, close_callback: Option<WebSocketCloseCallback>) {
108 *self.0.close_callback.write() = close_callback;
109 }
110
111 pub fn configure_message_event_callback(
112 &self,
113 message_event_callback: Option<WebSocketMessageEventCallback>,
114 ) {
115 *self.0.message_event_callback.write() = message_event_callback;
116 }
117
118 pub fn configure_close_event_callback(
119 &self,
120 close_event_callback: Option<WebSocketCloseEventCallback>,
121 ) {
122 *self.0.close_event_callback.write() = close_event_callback;
123 }
124
125 pub(crate) fn configure_close_event_callback_region(
126 &self,
127 close_event_callback_region: Option<WebSocketCallbackRegionFactory>,
128 ) {
129 *self.0.close_event_callback_region.write() = close_event_callback_region;
130 }
131
132 pub(crate) fn try_send(&self, msg: WsMessage) -> Result<()> {
133 let callback = self.send_callback()?;
134 callback(msg)
135 }
136
137 pub(crate) async fn try_close(&self, code: Option<u16>, reason: Option<String>) -> Result<()> {
138 let callback = self.close_callback()?;
139 callback(code, reason).await
140 }
141
142 pub(crate) fn try_dispatch_message_event(
143 &self,
144 msg: WsMessage,
145 message_index: Option<u16>,
146 ) -> Result<()> {
147 let callback = self.message_event_callback()?;
148 callback(msg, message_index)
149 }
150
151 pub(crate) async fn try_dispatch_close_event(
152 &self,
153 code: u16,
154 reason: String,
155 was_clean: bool,
156 ) -> Result<()> {
157 let callback = self.close_event_callback()?;
158 let _region = self.close_event_callback_region().map(|create| create());
159 callback(code, reason, was_clean).await
160 }
161
162 fn send_callback(&self) -> Result<WebSocketSendCallback> {
163 self.0
164 .send_callback
165 .read()
166 .clone()
167 .ok_or_else(|| websocket_not_configured("send callback"))
168 }
169
170 fn close_callback(&self) -> Result<WebSocketCloseCallback> {
171 self.0
172 .close_callback
173 .read()
174 .clone()
175 .ok_or_else(|| websocket_not_configured("close callback"))
176 }
177
178 fn message_event_callback(&self) -> Result<WebSocketMessageEventCallback> {
179 self.0
180 .message_event_callback
181 .read()
182 .clone()
183 .ok_or_else(|| websocket_not_configured("message event callback"))
184 }
185
186 fn close_event_callback(&self) -> Result<WebSocketCloseEventCallback> {
187 self.0
188 .close_event_callback
189 .read()
190 .clone()
191 .ok_or_else(|| websocket_not_configured("close event callback"))
192 }
193
194 fn close_event_callback_region(&self) -> Option<WebSocketCallbackRegionFactory> {
195 self.0.close_event_callback_region.read().clone()
196 }
197}
198
199fn websocket_not_configured(component: &str) -> anyhow::Error {
200 ActorRuntime::NotConfigured {
201 component: format!("websocket {component}"),
202 }
203 .build()
204}
205
206impl Default for WebSocket {
207 fn default() -> Self {
208 Self::new()
209 }
210}
211
212impl fmt::Debug for WebSocket {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 f.debug_struct("WebSocket")
215 .field("send_configured", &self.0.send_callback.read().is_some())
216 .field("close_configured", &self.0.close_callback.read().is_some())
217 .field(
218 "message_event_configured",
219 &self.0.message_event_callback.read().is_some(),
220 )
221 .field(
222 "close_event_configured",
223 &self.0.close_event_callback.read().is_some(),
224 )
225 .field(
226 "close_event_region_configured",
227 &self.0.close_event_callback_region.read().is_some(),
228 )
229 .finish()
230 }
231}
232
233