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