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#[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#[cfg(test)]
387#[path = "../../tests/messages.rs"]
388mod tests;