1use crate::{
2 common::{EncodingKind, RawWebSocket, TransportKind, HEADER_CONN_PARAMS, HEADER_ENCODING},
3 connection::{start_connection, ActorConnection, ActorConnectionInner},
4 protocol::{codec, query::*},
5 remote_manager::{GatewayTarget, RemoteManager},
6};
7use anyhow::{anyhow, Result};
8use bytes::Bytes;
9use reqwest::{
10 header::{HeaderMap, HeaderValue},
11 Method, Response,
12};
13use serde::Serialize;
14use serde_json::Value as JsonValue;
15use std::{
16 ops::Deref,
17 sync::{Arc, Mutex},
18 time::Duration,
19};
20
21pub use crate::protocol::codec::{QueueSendResult, QueueSendStatus};
22
23#[derive(Debug, Clone, Copy, Default)]
24pub struct SendOpts {}
25
26#[derive(Debug, Clone, Copy, Default)]
27pub struct SendAndWaitOpts {
28 pub timeout: Option<Duration>,
29}
30
31pub type QueueSendOptions = SendAndWaitOpts;
32
33pub struct ActorHandleStateless {
34 remote_manager: RemoteManager,
35 params: Option<JsonValue>,
36 encoding_kind: EncodingKind,
37 query: Mutex<ActorQuery>,
41}
42
43#[derive(Clone)]
46pub struct ResolvedActorHandle {
47 remote_manager: RemoteManager,
48 actor_id: String,
49}
50
51impl ResolvedActorHandle {
52 pub fn actor_id(&self) -> &str {
53 &self.actor_id
54 }
55
56 pub async fn fetch(
57 &self,
58 path: &str,
59 method: Method,
60 headers: HeaderMap,
61 body: Option<Bytes>,
62 ) -> Result<Response> {
63 let path = normalize_fetch_path(path);
64 let target = GatewayTarget::Direct {
65 actor_id: self.actor_id.clone(),
66 };
67 self.remote_manager
68 .send_request(&target, &path, method, headers, body)
69 .await
70 }
71}
72
73impl ActorHandleStateless {
74 pub fn new(
75 remote_manager: RemoteManager,
76 params: Option<JsonValue>,
77 encoding_kind: EncodingKind,
78 query: ActorQuery,
79 ) -> Self {
80 Self {
81 remote_manager,
82 params,
83 encoding_kind,
84 query: Mutex::new(query),
85 }
86 }
87
88 pub async fn action(&self, name: &str, args: Vec<JsonValue>) -> Result<JsonValue> {
89 let query = self.query.lock().expect("query lock poisoned").clone();
91 let target = self.remote_manager.gateway_target(&query).await?;
92
93 let body = codec::encode_http_action_request(self.encoding_kind, &args)?;
94
95 let headers = self.protocol_headers()?;
96
97 let path = format!("/action/{}", urlencoding::encode(name));
99 let res = self
100 .remote_manager
101 .send_request(
102 &target,
103 &path,
104 Method::POST,
105 headers,
106 Some(Bytes::from(body)),
107 )
108 .await?;
109
110 if !res.status().is_success() {
111 let status = res.status();
112 let body = res.bytes().await?;
113 if let Ok((group, code, message, metadata)) =
114 codec::decode_http_error(self.encoding_kind, &body)
115 {
116 return Err(anyhow!(
117 "action failed ({group}/{code}): {message}, metadata={metadata:?}"
118 ));
119 }
120 return Err(anyhow!("action failed: {status}"));
121 }
122
123 let output = res.bytes().await?;
125 codec::decode_http_action_response(self.encoding_kind, &output)
126 }
127
128 #[deprecated(
129 note = "queues were deprecated in 3.0 and will be removed in a future version. Use actions and store pending work in SQLite. See https://rivet.dev/actors/docs/deprecated/queues"
130 )]
131 pub async fn send(&self, name: &str, body: impl Serialize, _opts: SendOpts) -> Result<()> {
132 self.send_queue(name, &body, false, None).await.map(|_| ())
133 }
134
135 #[deprecated(
136 note = "queues were deprecated in 3.0 and will be removed in a future version. Use actions and store pending work in SQLite. See https://rivet.dev/actors/docs/deprecated/queues"
137 )]
138 pub async fn send_and_wait(
139 &self,
140 name: &str,
141 body: impl Serialize,
142 opts: SendAndWaitOpts,
143 ) -> Result<QueueSendResult> {
144 let result = self.send_queue(name, &body, true, opts.timeout).await?;
145 result.ok_or_else(|| anyhow!("queue wait response missing"))
146 }
147
148 async fn send_queue<T: Serialize>(
149 &self,
150 name: &str,
151 body: &T,
152 wait: bool,
153 timeout: Option<Duration>,
154 ) -> Result<Option<QueueSendResult>> {
155 let query = self.query.lock().expect("query lock poisoned").clone();
156 let target = self.remote_manager.gateway_target(&query).await?;
157 let timeout_ms =
158 timeout.map(|duration| u64::try_from(duration.as_millis()).unwrap_or(u64::MAX));
159 let request_body =
160 codec::encode_http_queue_request(self.encoding_kind, name, body, wait, timeout_ms)?;
161
162 let headers = self.protocol_headers()?;
163
164 let path = format!("/queue/{}", urlencoding::encode(name));
165 let res = self
166 .remote_manager
167 .send_request(
168 &target,
169 &path,
170 Method::POST,
171 headers,
172 Some(Bytes::from(request_body)),
173 )
174 .await?;
175
176 if !res.status().is_success() {
177 let status = res.status();
178 let body = res.bytes().await?;
179 if let Ok((group, code, message, metadata)) =
180 codec::decode_http_error(self.encoding_kind, &body)
181 {
182 return Err(anyhow!(
183 "queue send failed ({group}/{code}): {message}, metadata={metadata:?}"
184 ));
185 }
186 return Err(anyhow!("queue send failed: {status}"));
187 }
188
189 let body = res.bytes().await?;
190 let result = codec::decode_http_queue_response(self.encoding_kind, &body)?;
191 Ok(wait.then_some(result))
192 }
193
194 pub async fn fetch(
195 &self,
196 path: &str,
197 method: Method,
198 headers: HeaderMap,
199 body: Option<Bytes>,
200 ) -> Result<Response> {
201 let query = self.query.lock().expect("query lock poisoned").clone();
202 let target = self.remote_manager.gateway_target(&query).await?;
203 let path = normalize_fetch_path(path);
204 self.remote_manager
205 .send_request(&target, &path, method, headers, body)
206 .await
207 }
208
209 pub async fn resolve_handle(&self) -> Result<ResolvedActorHandle> {
212 let query = self.query.lock().expect("query lock poisoned").clone();
213 let actor_id = self.remote_manager.resolve_actor_id(&query).await?;
214 Ok(ResolvedActorHandle {
215 remote_manager: self.remote_manager.clone(),
216 actor_id,
217 })
218 }
219
220 pub async fn resolve_optional(&self) -> Result<Option<ResolvedActorHandle>> {
224 let query = self.query.lock().expect("query lock poisoned").clone();
225 let actor_id = match &query {
226 ActorQuery::GetForId { get_for_id } => {
227 self.remote_manager
228 .get_for_id(&get_for_id.name, &get_for_id.actor_id)
229 .await?
230 }
231 ActorQuery::GetForKey { get_for_key } => {
232 self.remote_manager
233 .get_with_key(&get_for_key.name, &get_for_key.key)
234 .await?
235 }
236 ActorQuery::GetOrCreateForKey { .. } | ActorQuery::Create { .. } => {
237 return Err(anyhow!(
238 "resolve_optional is only valid for non-creating actor queries"
239 ));
240 }
241 };
242 Ok(actor_id.map(|actor_id| ResolvedActorHandle {
243 remote_manager: self.remote_manager.clone(),
244 actor_id,
245 }))
246 }
247
248 pub async fn web_socket(
249 &self,
250 path: &str,
251 protocols: Option<Vec<String>>,
252 ) -> Result<RawWebSocket> {
253 let query = self.query.lock().expect("query lock poisoned").clone();
254 let target = self.remote_manager.gateway_target(&query).await?;
255 self.remote_manager
256 .open_raw_websocket(&target, path, self.params.clone(), protocols)
257 .await
258 }
259
260 pub fn gateway_url(&self) -> Result<String> {
261 let query = self.query.lock().expect("query lock poisoned").clone();
262 self.remote_manager.gateway_url(&query)
263 }
264
265 pub fn get_gateway_url(&self) -> Result<String> {
266 self.gateway_url()
267 }
268
269 pub async fn reload(&self) -> Result<()> {
270 let query = self.query.lock().expect("query lock poisoned").clone();
271 let target = self.remote_manager.gateway_target(&query).await?;
272 let res = self
273 .remote_manager
274 .send_request(
275 &target,
276 "/dynamic/reload",
277 Method::PUT,
278 HeaderMap::new(),
279 None,
280 )
281 .await?;
282 if !res.status().is_success() {
283 let status = res.status();
284 let body = res.text().await.unwrap_or_default();
285 return Err(anyhow!("reload failed with status {status}: {body}"));
286 }
287 Ok(())
288 }
289
290 pub async fn resolve(&self) -> Result<String> {
291 let query = {
292 let Ok(query) = self.query.lock() else {
293 return Err(anyhow!("Failed to lock actor query"));
294 };
295 query.clone()
296 };
297
298 match query {
299 ActorQuery::Create { .. } => Err(anyhow!("actor query cannot be create")),
300 ActorQuery::GetForId { get_for_id } => Ok(get_for_id.actor_id.clone()),
301 _ => {
302 let actor_id = self.remote_manager.resolve_actor_id(&query).await?;
303
304 let name = match &query {
306 ActorQuery::GetForKey { get_for_key } => get_for_key.name.clone(),
307 ActorQuery::GetOrCreateForKey {
308 get_or_create_for_key,
309 } => get_or_create_for_key.name.clone(),
310 _ => return Err(anyhow!("unexpected query type")),
311 };
312
313 {
314 let Ok(mut query_mut) = self.query.lock() else {
315 return Err(anyhow!("Failed to lock actor query mutably"));
316 };
317
318 *query_mut = ActorQuery::GetForId {
319 get_for_id: GetForIdRequest {
320 name,
321 actor_id: actor_id.clone(),
322 },
323 };
324 }
325
326 Ok(actor_id)
327 }
328 }
329 }
330
331 fn protocol_headers(&self) -> Result<HeaderMap> {
332 let mut headers = HeaderMap::new();
333 headers.insert(
334 HEADER_ENCODING,
335 HeaderValue::from_str(self.encoding_kind.as_str())?,
336 );
337
338 if let Some(params) = &self.params {
339 headers.insert(
340 HEADER_CONN_PARAMS,
341 HeaderValue::from_str(&serde_json::to_string(params)?)?,
342 );
343 }
344
345 Ok(headers)
346 }
347}
348
349fn normalize_fetch_path(path: &str) -> String {
350 let path = path.trim_start_matches('/');
351 if path.is_empty() {
352 "/request".to_string()
353 } else {
354 format!("/request/{path}")
355 }
356}
357
358pub struct ActorHandle {
359 handle: ActorHandleStateless,
360 remote_manager: RemoteManager,
361 params: Option<JsonValue>,
362 query: ActorQuery,
363 client_shutdown_tx: Arc<tokio::sync::broadcast::Sender<()>>,
364 transport_kind: crate::TransportKind,
365 encoding_kind: EncodingKind,
366}
367
368impl ActorHandle {
369 pub fn new(
370 remote_manager: RemoteManager,
371 params: Option<JsonValue>,
372 query: ActorQuery,
373 client_shutdown_tx: Arc<tokio::sync::broadcast::Sender<()>>,
374 transport_kind: TransportKind,
375 encoding_kind: EncodingKind,
376 ) -> Self {
377 let handle = ActorHandleStateless::new(
378 remote_manager.clone(),
379 params.clone(),
380 encoding_kind,
381 query.clone(),
382 );
383
384 Self {
385 handle,
386 remote_manager,
387 params,
388 query,
389 client_shutdown_tx,
390 transport_kind,
391 encoding_kind,
392 }
393 }
394
395 pub fn connect(&self) -> ActorConnection {
396 let conn = ActorConnectionInner::new(
397 self.remote_manager.clone(),
398 self.query.clone(),
399 self.transport_kind,
400 self.encoding_kind,
401 self.params.clone(),
402 );
403
404 let rx = self.client_shutdown_tx.subscribe();
405 start_connection(&conn, rx);
406
407 conn
408 }
409}
410
411impl Deref for ActorHandle {
412 type Target = ActorHandleStateless;
413
414 fn deref(&self) -> &Self::Target {
415 &self.handle
416 }
417}