Skip to main content

rivet_envoy_client/
config.rs

1use std::collections::HashMap;
2use std::future::Future;
3use std::pin::Pin;
4use std::sync::Arc;
5use std::sync::Mutex;
6
7use rivet_envoy_protocol as protocol;
8use tokio::sync::{mpsc, oneshot};
9
10use crate::handle::EnvoyHandle;
11
12#[cfg(not(target_arch = "wasm32"))]
13pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T> + Send>>;
14
15#[cfg(target_arch = "wasm32")]
16pub type BoxFuture<T> = Pin<Box<dyn Future<Output = T>>>;
17
18/// HTTP request/response types used by the envoy client.
19pub struct HttpRequest {
20	pub method: String,
21	pub path: String,
22	pub headers: HashMap<String, String>,
23	pub body: Option<Vec<u8>>,
24	/// If the request is streamed, body chunks arrive on this channel.
25	pub body_stream: Option<mpsc::UnboundedReceiver<Vec<u8>>>,
26}
27
28pub struct HttpResponse {
29	pub status: u16,
30	pub headers: HashMap<String, String>,
31	pub body: Option<Vec<u8>>,
32	/// If set, the response is streamed. The envoy client reads chunks and sends
33	/// `ToRivetResponseChunk` for each one.
34	pub body_stream: Option<mpsc::UnboundedReceiver<ResponseChunk>>,
35}
36
37/// A chunk in a streaming HTTP response.
38pub struct ResponseChunk {
39	pub data: Vec<u8>,
40	pub finish: bool,
41}
42
43pub struct EnvoyConfig {
44	pub version: u32,
45	pub endpoint: String,
46	pub token: Option<String>,
47	pub namespace: String,
48	pub pool_name: String,
49	pub prepopulate_actor_names: HashMap<String, ActorName>,
50	pub metadata: Option<serde_json::Value>,
51	/// When `start_envoy` is called, create a new envoy every time instead of using a single global envoy
52	/// instance for the entire runtime.
53	pub not_global: bool,
54
55	/// Debug option to inject artificial latency (in ms) into WebSocket communication.
56	pub debug_latency_ms: Option<u64>,
57
58	pub callbacks: Arc<dyn EnvoyCallbacks>,
59}
60
61pub struct ActorName {
62	pub metadata: serde_json::Value,
63}
64
65/// One-shot completion handle used to defer the final stopped event until teardown is done.
66#[derive(Clone)]
67pub struct ActorStopHandle {
68	tx: Arc<Mutex<Option<oneshot::Sender<anyhow::Result<()>>>>>,
69}
70
71impl ActorStopHandle {
72	pub(crate) fn new(tx: oneshot::Sender<anyhow::Result<()>>) -> Self {
73		Self {
74			tx: Arc::new(Mutex::new(Some(tx))),
75		}
76	}
77
78	pub fn complete(self) -> bool {
79		self.finish(Ok(()))
80	}
81
82	pub fn fail(self, error: anyhow::Error) -> bool {
83		self.finish(Err(error))
84	}
85
86	pub fn finish(self, result: anyhow::Result<()>) -> bool {
87		let mut guard = match self.tx.lock() {
88			Ok(guard) => guard,
89			Err(poisoned) => poisoned.into_inner(),
90		};
91
92		let Some(tx) = guard.take() else {
93			return false;
94		};
95
96		tx.send(result).is_ok()
97	}
98}
99
100/// Callbacks that the consumer of the envoy client must implement.
101pub trait EnvoyCallbacks: Send + Sync + 'static {
102	fn on_connect(&self, _handle: EnvoyHandle) {}
103
104	fn on_disconnect(&self, _handle: EnvoyHandle) {}
105
106	fn on_actor_start(
107		&self,
108		handle: EnvoyHandle,
109		actor_id: String,
110		generation: u32,
111		config: protocol::ActorConfig,
112		preloaded_kv: Option<protocol::PreloadedKv>,
113	) -> BoxFuture<anyhow::Result<()>>;
114
115	fn on_actor_stop(
116		&self,
117		_handle: EnvoyHandle,
118		_actor_id: String,
119		_generation: u32,
120		_reason: protocol::StopActorReason,
121	) -> BoxFuture<anyhow::Result<()>> {
122		Box::pin(async { Ok(()) })
123	}
124
125	fn on_actor_stop_with_completion(
126		&self,
127		handle: EnvoyHandle,
128		actor_id: String,
129		generation: u32,
130		reason: protocol::StopActorReason,
131		stop_handle: ActorStopHandle,
132	) -> BoxFuture<anyhow::Result<()>> {
133		let stop_future = self.on_actor_stop(handle, actor_id, generation, reason);
134
135		Box::pin(async move {
136			stop_future.await?;
137			stop_handle.complete();
138			Ok(())
139		})
140	}
141
142	fn on_shutdown(&self);
143
144	fn fetch(
145		&self,
146		handle: EnvoyHandle,
147		actor_id: String,
148		gateway_id: protocol::GatewayId,
149		request_id: protocol::RequestId,
150		request: HttpRequest,
151	) -> BoxFuture<anyhow::Result<HttpResponse>>;
152
153	fn websocket(
154		&self,
155		handle: EnvoyHandle,
156		actor_id: String,
157		gateway_id: protocol::GatewayId,
158		request_id: protocol::RequestId,
159		request: HttpRequest,
160		path: String,
161		headers: HashMap<String, String>,
162		is_hibernatable: bool,
163		is_restoring_hibernatable: bool,
164		sender: WebSocketSender,
165	) -> BoxFuture<anyhow::Result<WebSocketHandler>>;
166
167	fn can_hibernate(
168		&self,
169		actor_id: &str,
170		gateway_id: &protocol::GatewayId,
171		request_id: &protocol::RequestId,
172		request: &HttpRequest,
173	) -> BoxFuture<anyhow::Result<bool>>;
174}
175
176/// Handler returned by the websocket callback for receiving WebSocket events.
177pub struct WebSocketHandler {
178	pub on_message: Box<dyn Fn(WebSocketMessage) -> BoxFuture<()> + Send + Sync>,
179	pub on_close: Box<dyn Fn(u16, String) -> BoxFuture<()> + Send + Sync>,
180	pub on_open: Option<Box<dyn FnOnce(WebSocketSender) -> BoxFuture<()> + Send>>,
181}
182
183pub struct WebSocketMessage {
184	pub data: Vec<u8>,
185	pub binary: bool,
186	pub gateway_id: protocol::GatewayId,
187	pub request_id: protocol::RequestId,
188	pub message_index: u16,
189	/// Send data back on this WebSocket connection.
190	pub sender: WebSocketSender,
191}
192
193/// Allows sending messages back on a WebSocket connection from within the on_message callback.
194#[derive(Clone)]
195pub struct WebSocketSender {
196	pub(crate) tx: tokio::sync::mpsc::UnboundedSender<WsOutgoing>,
197}
198
199pub(crate) enum WsOutgoing {
200	Message {
201		data: Vec<u8>,
202		binary: bool,
203	},
204	Flush {
205		tx: tokio::sync::oneshot::Sender<()>,
206	},
207	Close {
208		code: Option<u16>,
209		reason: Option<String>,
210	},
211}
212
213impl WebSocketSender {
214	pub fn send(&self, data: Vec<u8>, binary: bool) {
215		let _ = self.tx.send(WsOutgoing::Message { data, binary });
216	}
217
218	pub fn send_text(&self, text: &str) {
219		self.send(text.as_bytes().to_vec(), false);
220	}
221
222	pub async fn flush(&self) {
223		let (tx, rx) = tokio::sync::oneshot::channel();
224		if self.tx.send(WsOutgoing::Flush { tx }).is_ok() {
225			let _ = rx.await;
226		}
227	}
228
229	pub fn close(&self, code: Option<u16>, reason: Option<String>) {
230		let _ = self.tx.send(WsOutgoing::Close { code, reason });
231	}
232}