Skip to main content

rivetkit_core/actor/
messages.rs

1use std::collections::HashMap;
2use std::ops::{Deref, DerefMut};
3
4use anyhow::Result;
5use serde::{Deserialize, Serialize};
6
7use crate::actor::connection::ConnHandle;
8use crate::actor::lifecycle_hooks::Reply;
9use crate::actor::schedule::ScheduledFireInfo;
10use crate::actor::task_types::ShutdownKind;
11use crate::error::ProtocolError;
12use crate::types::ConnId;
13use crate::websocket::WebSocket;
14
15#[derive(Clone, Debug)]
16pub struct Request(http::Request<Vec<u8>>);
17
18impl Request {
19	pub fn new(body: Vec<u8>) -> Self {
20		Self(http::Request::new(body))
21	}
22
23	pub fn from_parts(
24		method: &str,
25		uri: &str,
26		headers: HashMap<String, String>,
27		body: Vec<u8>,
28	) -> Result<Self> {
29		let method = method
30			.parse::<http::Method>()
31			.map_err(|error| invalid_http_request("method", format!("{method}: {error}")))?;
32		let uri = uri
33			.parse::<http::Uri>()
34			.map_err(|error| invalid_http_request("uri", format!("{uri}: {error}")))?;
35		let mut request = http::Request::builder()
36			.method(method)
37			.uri(uri)
38			.body(body)?;
39
40		for (name, value) in headers {
41			let header_name: http::header::HeaderName = name
42				.parse()
43				.map_err(|error| invalid_http_request("header name", format!("{name}: {error}")))?;
44			let header_value: http::header::HeaderValue = value.parse().map_err(|error| {
45				invalid_http_request("header value", format!("{name}: {error}"))
46			})?;
47			request.headers_mut().insert(header_name, header_value);
48		}
49
50		Ok(Self(request))
51	}
52
53	pub fn to_parts(&self) -> (String, String, HashMap<String, String>, Vec<u8>) {
54		(
55			self.method().to_string(),
56			self.uri().to_string(),
57			self.headers()
58				.iter()
59				.map(|(name, value)| {
60					(
61						name.to_string(),
62						String::from_utf8_lossy(value.as_bytes()).into_owned(),
63					)
64				})
65				.collect(),
66			self.body().clone(),
67		)
68	}
69
70	pub fn into_inner(self) -> http::Request<Vec<u8>> {
71		self.0
72	}
73
74	pub fn into_body(self) -> Vec<u8> {
75		self.0.into_body()
76	}
77}
78
79impl Default for Request {
80	fn default() -> Self {
81		Self::new(Vec::new())
82	}
83}
84
85impl Deref for Request {
86	type Target = http::Request<Vec<u8>>;
87
88	fn deref(&self) -> &Self::Target {
89		&self.0
90	}
91}
92
93impl DerefMut for Request {
94	fn deref_mut(&mut self) -> &mut Self::Target {
95		&mut self.0
96	}
97}
98
99impl From<http::Request<Vec<u8>>> for Request {
100	fn from(value: http::Request<Vec<u8>>) -> Self {
101		Self(value)
102	}
103}
104
105impl From<Request> for http::Request<Vec<u8>> {
106	fn from(value: Request) -> Self {
107		value.0
108	}
109}
110
111#[derive(Clone, Debug)]
112pub struct Response(http::Response<Vec<u8>>);
113
114impl Response {
115	pub fn new(body: Vec<u8>) -> Self {
116		Self(http::Response::new(body))
117	}
118
119	pub fn from_parts(
120		status: u16,
121		headers: HashMap<String, String>,
122		body: Vec<u8>,
123	) -> Result<Self> {
124		let mut response = http::Response::new(body);
125		*response.status_mut() = status
126			.try_into()
127			.map_err(|error| invalid_http_response("status", format!("{status}: {error}")))?;
128
129		for (name, value) in headers {
130			let header_name: http::header::HeaderName = name.parse().map_err(|error| {
131				invalid_http_response("header name", format!("{name}: {error}"))
132			})?;
133			let header_value: http::header::HeaderValue = value.parse().map_err(|error| {
134				invalid_http_response("header value", format!("{name}: {error}"))
135			})?;
136			response.headers_mut().insert(header_name, header_value);
137		}
138
139		Ok(Self(response))
140	}
141
142	pub fn to_parts(&self) -> (u16, HashMap<String, String>, Vec<u8>) {
143		(
144			self.status().as_u16(),
145			self.headers()
146				.iter()
147				.map(|(name, value)| {
148					(
149						name.to_string(),
150						String::from_utf8_lossy(value.as_bytes()).into_owned(),
151					)
152				})
153				.collect(),
154			self.body().clone(),
155		)
156	}
157
158	pub fn into_inner(self) -> http::Response<Vec<u8>> {
159		self.0
160	}
161
162	pub fn into_body(self) -> Vec<u8> {
163		self.0.into_body()
164	}
165}
166
167fn invalid_http_request(field: &str, reason: String) -> anyhow::Error {
168	ProtocolError::InvalidHttpRequest {
169		field: field.to_owned(),
170		reason,
171	}
172	.build()
173}
174
175fn invalid_http_response(field: &str, reason: String) -> anyhow::Error {
176	ProtocolError::InvalidHttpResponse {
177		field: field.to_owned(),
178		reason,
179	}
180	.build()
181}
182
183impl Default for Response {
184	fn default() -> Self {
185		Self::new(Vec::new())
186	}
187}
188
189impl Deref for Response {
190	type Target = http::Response<Vec<u8>>;
191
192	fn deref(&self) -> &Self::Target {
193		&self.0
194	}
195}
196
197impl DerefMut for Response {
198	fn deref_mut(&mut self) -> &mut Self::Target {
199		&mut self.0
200	}
201}
202
203impl From<http::Response<Vec<u8>>> for Response {
204	fn from(value: http::Response<Vec<u8>>) -> Self {
205		Self(value)
206	}
207}
208
209impl From<Response> for http::Response<Vec<u8>> {
210	fn from(value: Response) -> Self {
211		value.0
212	}
213}
214
215#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
216pub enum StateDelta {
217	ActorState(Vec<u8>),
218	ConnHibernation { conn: ConnId, bytes: Vec<u8> },
219	ConnHibernationRemoved(ConnId),
220}
221
222/// One workflow-engine KV write that must commit atomically with its actor
223/// state snapshot.
224#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
225pub struct WorkflowKvWrite {
226	pub key: Vec<u8>,
227	pub value: Vec<u8>,
228}
229
230impl StateDelta {
231	pub(crate) fn payload_len(&self) -> usize {
232		match self {
233			Self::ActorState(bytes) | Self::ConnHibernation { bytes, .. } => bytes.len(),
234			Self::ConnHibernationRemoved(_) => 0,
235		}
236	}
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
240pub enum SerializeStateReason {
241	Save,
242	Inspector,
243}
244
245impl SerializeStateReason {
246	pub(crate) fn label(self) -> &'static str {
247		match self {
248			Self::Save => "save",
249			Self::Inspector => "inspector",
250		}
251	}
252}
253
254#[derive(Clone, Debug, PartialEq, Eq)]
255pub enum QueueSendStatus {
256	Completed,
257	TimedOut,
258}
259
260impl QueueSendStatus {
261	pub(crate) fn as_str(&self) -> &'static str {
262		match self {
263			Self::Completed => "completed",
264			Self::TimedOut => "timedOut",
265		}
266	}
267}
268
269#[derive(Clone, Debug, PartialEq, Eq)]
270pub struct QueueSendResult {
271	pub status: QueueSendStatus,
272	pub response: Option<Vec<u8>>,
273}
274
275#[derive(Debug)]
276pub enum ActorEvent {
277	Action {
278		name: String,
279		args: Vec<u8>,
280		conn: Option<ConnHandle>,
281		scheduled_fire: Option<ScheduledFireInfo>,
282		reply: Reply<Vec<u8>>,
283	},
284	HttpRequest {
285		request: Request,
286		reply: Reply<Response>,
287	},
288	QueueSend {
289		name: String,
290		body: Vec<u8>,
291		conn: ConnHandle,
292		request: Request,
293		wait: bool,
294		timeout_ms: Option<u64>,
295		reply: Reply<QueueSendResult>,
296	},
297	WebSocketOpen {
298		conn: ConnHandle,
299		ws: WebSocket,
300		request: Option<Request>,
301		reply: Reply<()>,
302	},
303	ConnectionPreflight {
304		conn: ConnHandle,
305		params: Vec<u8>,
306		request: Option<Request>,
307		reply: Reply<()>,
308	},
309	ConnectionOpen {
310		conn: ConnHandle,
311		request: Option<Request>,
312		reply: Reply<()>,
313	},
314	ConnectionClosed {
315		conn: ConnHandle,
316	},
317	SubscribeRequest {
318		conn: ConnHandle,
319		event_name: String,
320		reply: Reply<()>,
321	},
322	SerializeState {
323		reason: SerializeStateReason,
324		reply: Reply<Vec<StateDelta>>,
325	},
326	RunGracefulCleanup {
327		reason: ShutdownKind,
328		reply: Reply<()>,
329	},
330	DisconnectConn {
331		conn_id: ConnId,
332		reply: Reply<()>,
333	},
334	#[cfg(test)]
335	BeginSleep,
336	#[cfg(test)]
337	FinalizeSleep {
338		reply: Reply<()>,
339	},
340	#[cfg(test)]
341	Destroy {
342		reply: Reply<()>,
343	},
344	WorkflowHistoryRequested {
345		reply: Reply<Option<Vec<u8>>>,
346	},
347	WorkflowReplayRequested {
348		entry_id: Option<String>,
349		reply: Reply<Option<Vec<u8>>>,
350	},
351}
352
353impl ActorEvent {
354	pub(crate) fn kind(&self) -> &'static str {
355		match self {
356			Self::Action { .. } => "action",
357			Self::HttpRequest { .. } => "http_request",
358			Self::QueueSend { .. } => "queue_send",
359			Self::WebSocketOpen { .. } => "websocket_open",
360			Self::ConnectionPreflight { .. } => "connection_preflight",
361			Self::ConnectionOpen { .. } => "connection_open",
362			Self::ConnectionClosed { .. } => "connection_closed",
363			Self::SubscribeRequest { .. } => "subscribe_request",
364			Self::SerializeState { reason, .. } => match reason {
365				SerializeStateReason::Save => "serialize_state_save",
366				SerializeStateReason::Inspector => "serialize_state_inspector",
367			},
368			Self::RunGracefulCleanup { reason, .. } => match reason {
369				ShutdownKind::Sleep => "run_sleep_cleanup",
370				ShutdownKind::Destroy => "run_destroy_cleanup",
371			},
372			Self::DisconnectConn { .. } => "disconnect_conn",
373			#[cfg(test)]
374			Self::BeginSleep => "begin_sleep",
375			#[cfg(test)]
376			Self::FinalizeSleep { .. } => "finalize_sleep",
377			#[cfg(test)]
378			Self::Destroy { .. } => "destroy",
379			Self::WorkflowHistoryRequested { .. } => "workflow_history_requested",
380			Self::WorkflowReplayRequested { .. } => "workflow_replay_requested",
381		}
382	}
383}
384
385// Test shim keeps moved tests in crate-root tests/ with private-module access.
386#[cfg(test)]
387#[path = "../../tests/messages.rs"]
388mod tests;