Skip to main content

rivetkit_core/actor/
messages.rs

1use std::collections::HashMap;
2use std::ops::{Deref, DerefMut};
3use std::sync::Arc;
4
5use anyhow::Result;
6use parking_lot::Mutex;
7use rivet_envoy_client::config::{HttpRequestBodyStream, ResponseChunk};
8use serde::{Deserialize, Serialize};
9use tokio::sync::mpsc;
10use tokio_util::sync::CancellationToken;
11
12use crate::actor::connection::ConnHandle;
13use crate::actor::lifecycle_hooks::Reply;
14use crate::actor::schedule::ScheduledFireInfo;
15use crate::actor::task_types::ShutdownKind;
16use crate::error::ProtocolError;
17use crate::telemetry::ActorInvocationTelemetry;
18use crate::types::ConnId;
19use crate::websocket::WebSocket;
20
21#[derive(Clone, Debug)]
22pub struct Request {
23	inner: http::Request<Vec<u8>>,
24	body_stream: Option<Arc<Mutex<Option<HttpRequestBodyStream>>>>,
25	cancel_token: CancellationToken,
26}
27
28impl Request {
29	pub fn new(body: Vec<u8>) -> Self {
30		Self {
31			inner: http::Request::new(body),
32			body_stream: None,
33			cancel_token: CancellationToken::new(),
34		}
35	}
36
37	pub fn from_parts(
38		method: &str,
39		uri: &str,
40		headers: HashMap<String, String>,
41		body: Vec<u8>,
42	) -> Result<Self> {
43		Self::from_parts_with_stream(method, uri, headers, body, None)
44	}
45
46	pub fn from_parts_with_stream(
47		method: &str,
48		uri: &str,
49		headers: HashMap<String, String>,
50		body: Vec<u8>,
51		body_stream: Option<HttpRequestBodyStream>,
52	) -> Result<Self> {
53		let method = method
54			.parse::<http::Method>()
55			.map_err(|error| invalid_http_request("method", format!("{method}: {error}")))?;
56		let uri = uri
57			.parse::<http::Uri>()
58			.map_err(|error| invalid_http_request("uri", format!("{uri}: {error}")))?;
59		let mut request = http::Request::builder()
60			.method(method)
61			.uri(uri)
62			.body(body)?;
63
64		for (name, value) in headers {
65			let header_name: http::header::HeaderName = name
66				.parse()
67				.map_err(|error| invalid_http_request("header name", format!("{name}: {error}")))?;
68			let header_value: http::header::HeaderValue = value.parse().map_err(|error| {
69				invalid_http_request("header value", format!("{name}: {error}"))
70			})?;
71			request.headers_mut().insert(header_name, header_value);
72		}
73
74		Ok(Self {
75			inner: request,
76			body_stream: body_stream.map(|rx| Arc::new(Mutex::new(Some(rx)))),
77			cancel_token: CancellationToken::new(),
78		})
79	}
80
81	pub fn to_parts(&self) -> (String, String, HashMap<String, String>, Vec<u8>) {
82		(
83			self.method().to_string(),
84			self.uri().to_string(),
85			self.headers()
86				.iter()
87				.map(|(name, value)| {
88					(
89						name.to_string(),
90						String::from_utf8_lossy(value.as_bytes()).into_owned(),
91					)
92				})
93				.collect(),
94			self.body().clone(),
95		)
96	}
97
98	pub fn has_body_stream(&self) -> bool {
99		self.body_stream
100			.as_ref()
101			.is_some_and(|body_stream| body_stream.lock().is_some())
102	}
103
104	pub fn take_body_stream(&self) -> Option<HttpRequestBodyStream> {
105		self.body_stream
106			.as_ref()
107			.and_then(|body_stream| body_stream.lock().take())
108	}
109
110	pub fn cancellation_token(&self) -> CancellationToken {
111		self.cancel_token.clone()
112	}
113
114	pub async fn into_buffered(mut self) -> Result<Self> {
115		if let Some(body_stream) = &self.body_stream {
116			let mut body_stream = body_stream.lock().take();
117			if let Some(mut body_stream) = body_stream.take() {
118				while let Some(chunk) = body_stream.recv().await? {
119					self.inner.body_mut().extend_from_slice(&chunk);
120				}
121			}
122		}
123		self.body_stream = None;
124		Ok(self)
125	}
126
127	pub fn into_inner(self) -> http::Request<Vec<u8>> {
128		self.inner
129	}
130
131	pub fn into_body(self) -> Vec<u8> {
132		self.inner.into_body()
133	}
134}
135
136impl Default for Request {
137	fn default() -> Self {
138		Self::new(Vec::new())
139	}
140}
141
142impl Deref for Request {
143	type Target = http::Request<Vec<u8>>;
144
145	fn deref(&self) -> &Self::Target {
146		&self.inner
147	}
148}
149
150impl DerefMut for Request {
151	fn deref_mut(&mut self) -> &mut Self::Target {
152		&mut self.inner
153	}
154}
155
156impl From<http::Request<Vec<u8>>> for Request {
157	fn from(value: http::Request<Vec<u8>>) -> Self {
158		Self {
159			inner: value,
160			body_stream: None,
161			cancel_token: CancellationToken::new(),
162		}
163	}
164}
165
166impl From<Request> for http::Request<Vec<u8>> {
167	fn from(value: Request) -> Self {
168		value.inner
169	}
170}
171
172#[derive(Clone, Debug)]
173pub struct Response(http::Response<Vec<u8>>);
174
175impl Response {
176	pub fn new(body: Vec<u8>) -> Self {
177		Self(http::Response::new(body))
178	}
179
180	pub fn from_parts(
181		status: u16,
182		headers: HashMap<String, String>,
183		body: Vec<u8>,
184	) -> Result<Self> {
185		let mut response = http::Response::new(body);
186		*response.status_mut() = status
187			.try_into()
188			.map_err(|error| invalid_http_response("status", format!("{status}: {error}")))?;
189
190		for (name, value) in headers {
191			let header_name: http::header::HeaderName = name.parse().map_err(|error| {
192				invalid_http_response("header name", format!("{name}: {error}"))
193			})?;
194			let header_value: http::header::HeaderValue = value.parse().map_err(|error| {
195				invalid_http_response("header value", format!("{name}: {error}"))
196			})?;
197			response.headers_mut().insert(header_name, header_value);
198		}
199
200		Ok(Self(response))
201	}
202
203	pub fn to_parts(&self) -> (u16, HashMap<String, String>, Vec<u8>) {
204		(
205			self.status().as_u16(),
206			self.headers()
207				.iter()
208				.map(|(name, value)| {
209					(
210						name.to_string(),
211						String::from_utf8_lossy(value.as_bytes()).into_owned(),
212					)
213				})
214				.collect(),
215			self.body().clone(),
216		)
217	}
218
219	pub fn into_inner(self) -> http::Response<Vec<u8>> {
220		self.0
221	}
222
223	pub fn into_body(self) -> Vec<u8> {
224		self.0.into_body()
225	}
226}
227
228fn invalid_http_request(field: &str, reason: String) -> anyhow::Error {
229	ProtocolError::InvalidHttpRequest {
230		field: field.to_owned(),
231		reason,
232	}
233	.build()
234}
235
236fn invalid_http_response(field: &str, reason: String) -> anyhow::Error {
237	ProtocolError::InvalidHttpResponse {
238		field: field.to_owned(),
239		reason,
240	}
241	.build()
242}
243
244impl Default for Response {
245	fn default() -> Self {
246		Self::new(Vec::new())
247	}
248}
249
250impl Deref for Response {
251	type Target = http::Response<Vec<u8>>;
252
253	fn deref(&self) -> &Self::Target {
254		&self.0
255	}
256}
257
258impl DerefMut for Response {
259	fn deref_mut(&mut self) -> &mut Self::Target {
260		&mut self.0
261	}
262}
263
264impl From<http::Response<Vec<u8>>> for Response {
265	fn from(value: http::Response<Vec<u8>>) -> Self {
266		Self(value)
267	}
268}
269
270impl From<Response> for http::Response<Vec<u8>> {
271	fn from(value: Response) -> Self {
272		value.0
273	}
274}
275
276pub struct StreamingResponse {
277	status: u16,
278	headers: HashMap<String, String>,
279	body_stream: mpsc::Receiver<ResponseChunk>,
280}
281
282impl StreamingResponse {
283	pub fn from_parts(
284		status: u16,
285		headers: HashMap<String, String>,
286		body_stream: mpsc::Receiver<ResponseChunk>,
287	) -> Result<Self> {
288		let status_code: http::StatusCode = status
289			.try_into()
290			.map_err(|error| invalid_http_response("status", format!("{status}: {error}")))?;
291		for (name, value) in &headers {
292			let _: http::header::HeaderName = name.parse().map_err(|error| {
293				invalid_http_response("header name", format!("{name}: {error}"))
294			})?;
295			let _: http::header::HeaderValue = value.parse().map_err(|error| {
296				invalid_http_response("header value", format!("{name}: {error}"))
297			})?;
298		}
299
300		Ok(Self {
301			status: status_code.as_u16(),
302			headers,
303			body_stream,
304		})
305	}
306
307	pub fn into_parts(self) -> (u16, HashMap<String, String>, mpsc::Receiver<ResponseChunk>) {
308		(self.status, self.headers, self.body_stream)
309	}
310}
311
312pub enum ActorHttpResponse {
313	Buffered(Response),
314	Stream(StreamingResponse),
315}
316
317impl ActorHttpResponse {
318	pub fn status(&self) -> u16 {
319		match self {
320			Self::Buffered(response) => response.status().as_u16(),
321			Self::Stream(response) => response.status,
322		}
323	}
324}
325
326impl From<Response> for ActorHttpResponse {
327	fn from(value: Response) -> Self {
328		Self::Buffered(value)
329	}
330}
331
332impl From<StreamingResponse> for ActorHttpResponse {
333	fn from(value: StreamingResponse) -> Self {
334		Self::Stream(value)
335	}
336}
337
338#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
339pub enum StateDelta {
340	ActorState(Vec<u8>),
341	ConnHibernation { conn: ConnId, bytes: Vec<u8> },
342	ConnHibernationRemoved(ConnId),
343}
344
345/// One workflow-engine KV write that must commit atomically with its actor
346/// state snapshot.
347#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
348pub struct WorkflowKvWrite {
349	pub key: Vec<u8>,
350	pub value: Vec<u8>,
351}
352
353impl StateDelta {
354	pub(crate) fn payload_len(&self) -> usize {
355		match self {
356			Self::ActorState(bytes) | Self::ConnHibernation { bytes, .. } => bytes.len(),
357			Self::ConnHibernationRemoved(_) => 0,
358		}
359	}
360}
361
362#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
363pub enum SerializeStateReason {
364	Save,
365	Inspector,
366}
367
368impl SerializeStateReason {
369	pub(crate) fn label(self) -> &'static str {
370		match self {
371			Self::Save => "save",
372			Self::Inspector => "inspector",
373		}
374	}
375}
376
377#[derive(Clone, Debug, PartialEq, Eq)]
378pub enum QueueSendStatus {
379	Completed,
380	TimedOut,
381}
382
383impl QueueSendStatus {
384	pub(crate) fn as_str(&self) -> &'static str {
385		match self {
386			Self::Completed => "completed",
387			Self::TimedOut => "timedOut",
388		}
389	}
390}
391
392#[derive(Clone, Debug, PartialEq, Eq)]
393pub struct QueueSendResult {
394	pub status: QueueSendStatus,
395	pub response: Option<Vec<u8>>,
396}
397
398#[derive(Debug)]
399pub enum ActorEvent {
400	Action {
401		name: String,
402		args: Vec<u8>,
403		conn: Option<ConnHandle>,
404		scheduled_fire: Option<ScheduledFireInfo>,
405		/// Telemetry of the invocation this action runs as, for the host
406		/// runtime to bind onto the context it hands the action. Absent when
407		/// the dispatch opened no invocation.
408		invocation_telemetry: Option<ActorInvocationTelemetry>,
409		reply: Reply<Vec<u8>>,
410	},
411	HttpRequest {
412		request: Request,
413		/// Telemetry of the invocation this request runs as. See `Action`.
414		invocation_telemetry: Option<crate::ActorInvocationTelemetry>,
415		reply: Reply<ActorHttpResponse>,
416	},
417	QueueSend {
418		name: String,
419		body: Vec<u8>,
420		conn: ConnHandle,
421		request: Request,
422		wait: bool,
423		timeout_ms: Option<u64>,
424		/// Telemetry of the invocation this send runs as. See `Action`.
425		invocation_telemetry: Option<crate::ActorInvocationTelemetry>,
426		reply: Reply<QueueSendResult>,
427	},
428	WebSocketOpen {
429		conn: ConnHandle,
430		ws: WebSocket,
431		request: Option<Request>,
432		reply: Reply<()>,
433	},
434	ConnectionPreflight {
435		conn: ConnHandle,
436		params: Vec<u8>,
437		request: Option<Request>,
438		reply: Reply<()>,
439	},
440	ConnectionOpen {
441		conn: ConnHandle,
442		request: Option<Request>,
443		reply: Reply<()>,
444	},
445	ConnectionClosed {
446		conn: ConnHandle,
447	},
448	SubscribeRequest {
449		conn: ConnHandle,
450		event_name: String,
451		reply: Reply<()>,
452	},
453	SerializeState {
454		reason: SerializeStateReason,
455		reply: Reply<Vec<StateDelta>>,
456	},
457	RunGracefulCleanup {
458		reason: ShutdownKind,
459		reply: Reply<()>,
460	},
461	DisconnectConn {
462		conn_id: ConnId,
463		reply: Reply<()>,
464	},
465	#[cfg(test)]
466	BeginSleep,
467	#[cfg(test)]
468	FinalizeSleep {
469		reply: Reply<()>,
470	},
471	#[cfg(test)]
472	Destroy {
473		reply: Reply<()>,
474	},
475	WorkflowHistoryRequested {
476		reply: Reply<Option<Vec<u8>>>,
477	},
478	WorkflowReplayRequested {
479		entry_id: Option<String>,
480		reply: Reply<Option<Vec<u8>>>,
481	},
482	/// The persisted logical run deadline became due. Foreign runtimes ensure
483	/// their run callback is active without restarting the actor runtime itself.
484	RunWake {
485		wake_at: i64,
486		wake_revision: u64,
487		reply: Reply<()>,
488	},
489}
490
491impl ActorEvent {
492	pub(crate) fn kind(&self) -> &'static str {
493		match self {
494			Self::Action { .. } => "action",
495			Self::HttpRequest { .. } => "http_request",
496			Self::QueueSend { .. } => "queue_send",
497			Self::WebSocketOpen { .. } => "websocket_open",
498			Self::ConnectionPreflight { .. } => "connection_preflight",
499			Self::ConnectionOpen { .. } => "connection_open",
500			Self::ConnectionClosed { .. } => "connection_closed",
501			Self::SubscribeRequest { .. } => "subscribe_request",
502			Self::SerializeState { reason, .. } => match reason {
503				SerializeStateReason::Save => "serialize_state_save",
504				SerializeStateReason::Inspector => "serialize_state_inspector",
505			},
506			Self::RunGracefulCleanup { reason, .. } => match reason {
507				ShutdownKind::Sleep => "run_sleep_cleanup",
508				ShutdownKind::Destroy => "run_destroy_cleanup",
509			},
510			Self::DisconnectConn { .. } => "disconnect_conn",
511			#[cfg(test)]
512			Self::BeginSleep => "begin_sleep",
513			#[cfg(test)]
514			Self::FinalizeSleep { .. } => "finalize_sleep",
515			#[cfg(test)]
516			Self::Destroy { .. } => "destroy",
517			Self::WorkflowHistoryRequested { .. } => "workflow_history_requested",
518			Self::WorkflowReplayRequested { .. } => "workflow_replay_requested",
519			Self::RunWake { .. } => "run_wake",
520		}
521	}
522}
523
524// Test shim keeps moved tests in crate-root tests/ with private-module access.
525#[cfg(test)]
526#[path = "../../tests/messages.rs"]
527mod tests;