1use rivet_envoy_protocol as protocol;
2use std::collections::HashMap;
3use std::time::Duration;
4
5use crate::connection::ws_send;
6use crate::envoy::{BufferedActorMessage, EnvoyContext, HttpRequestRoute, WebSocketRoute};
7
8const HTTP_REQUEST_CANCELLATION_TTL: Duration = Duration::from_secs(60);
9
10#[derive(Clone, Debug, Eq, Hash, PartialEq)]
11pub struct HttpRequestCancellationKey {
12 gateway_id: protocol::GatewayId,
13 request_id: protocol::RequestId,
14 actor_id: String,
15 actor_generation: u32,
16}
17
18fn request_cancellation_key(
19 message_id: &protocol::MessageId,
20 actor_id: &str,
21 actor_generation: u32,
22) -> HttpRequestCancellationKey {
23 HttpRequestCancellationKey {
24 gateway_id: message_id.gateway_id,
25 request_id: message_id.request_id,
26 actor_id: actor_id.to_owned(),
27 actor_generation,
28 }
29}
30
31fn request_abort_cancellation_key(
32 message_id: &protocol::MessageId,
33 abort: &protocol::ToEnvoyRequestAbort,
34) -> Result<Option<HttpRequestCancellationKey>, &'static str> {
35 match (&abort.actor_id, abort.actor_generation) {
36 (Some(actor_id), Some(actor_generation)) => Ok(Some(request_cancellation_key(
37 message_id,
38 actor_id,
39 actor_generation,
40 ))),
41 (None, None) => Ok(None),
42 _ => Err("HTTP request abort must include both actor id and generation"),
43 }
44}
45
46fn prune_http_request_cancellations(ctx: &mut EnvoyContext) {
47 let now = crate::time::Instant::now();
48 ctx.http_request_cancellations.retain(|_, cancelled_at| {
49 now.duration_since(*cancelled_at) < HTTP_REQUEST_CANCELLATION_TTL
50 });
51}
52
53pub(crate) fn make_ws_key(
54 gateway_id: &protocol::GatewayId,
55 request_id: &protocol::RequestId,
56) -> [u8; 8] {
57 let mut key = [0u8; 8];
58 key[..4].copy_from_slice(gateway_id);
59 key[4..].copy_from_slice(request_id);
60 key
61}
62
63fn advance_http_message_index(ctx: &mut EnvoyContext, message_id: &protocol::MessageId) -> bool {
64 let key: [&[u8]; 2] = [&message_id.gateway_id, &message_id.request_id];
65 let Some(expected) = ctx.http_message_indices.get_mut(&key) else {
66 tracing::warn!(
67 message_index = message_id.message_index,
68 "received HTTP tunnel message without request start"
69 );
70 return false;
71 };
72 if message_id.message_index != *expected {
73 tracing::warn!(
74 expected_message_index = *expected,
75 actual_message_index = message_id.message_index,
76 "received reordered HTTP tunnel message"
77 );
78 return false;
79 }
80 *expected = expected.wrapping_add(1);
81 true
82}
83
84pub struct HibernatingWebSocketMetadata {
85 pub gateway_id: protocol::GatewayId,
86 pub request_id: protocol::RequestId,
87 pub envoy_message_index: u16,
88 pub rivet_message_index: u16,
89 pub path: String,
90 pub headers: std::collections::HashMap<String, String>,
91}
92
93pub async fn handle_tunnel_message(
94 ctx: &mut EnvoyContext,
95 connection_session: u64,
96 msg: protocol::ToEnvoyTunnelMessage,
97) {
98 let message_id = msg.message_id;
99 let is_http_continuation = matches!(
100 &msg.message_kind,
101 protocol::ToEnvoyTunnelMessageKind::ToEnvoyRequestChunk(_)
102 | protocol::ToEnvoyTunnelMessageKind::ToEnvoyRequestAbort(_)
103 | protocol::ToEnvoyTunnelMessageKind::ToEnvoyRequestBodyCancel
104 | protocol::ToEnvoyTunnelMessageKind::ToEnvoyResponseBodyWindowUpdate(_)
105 );
106 if is_http_continuation
107 && let Some(route) = ctx
108 .http_request_routes
109 .get(&[&message_id.gateway_id, &message_id.request_id])
110 && route.session != connection_session
111 {
112 handle_http_protocol_violation(
113 ctx,
114 connection_session,
115 message_id,
116 "HTTP request identifier belongs to another connection",
117 )
118 .await;
119 return;
120 }
121 match msg.message_kind {
122 protocol::ToEnvoyTunnelMessageKind::ToEnvoyRequestStart(req) => {
123 handle_request_start(ctx, connection_session, message_id, req).await;
124 }
125 protocol::ToEnvoyTunnelMessageKind::ToEnvoyRequestChunk(chunk) => {
126 if advance_http_message_index(ctx, &message_id) {
127 handle_request_chunk(ctx, message_id, chunk).await;
128 } else {
129 handle_http_protocol_violation(
130 ctx,
131 connection_session,
132 message_id,
133 "invalid HTTP tunnel message sequence",
134 )
135 .await;
136 }
137 }
138 protocol::ToEnvoyTunnelMessageKind::ToEnvoyRequestAbort(abort) => {
139 let cancellation_key = match request_abort_cancellation_key(&message_id, &abort) {
140 Ok(key) => key,
141 Err(detail) => {
142 handle_http_protocol_violation(ctx, connection_session, message_id, detail)
143 .await;
144 return;
145 }
146 };
147 if let Some(cancellation_key) = cancellation_key {
148 let route = ctx
149 .http_request_routes
150 .get(&[&message_id.gateway_id, &message_id.request_id])
151 .map(|route| (route.actor_id.clone(), route.actor_generation));
152 if let Some((route_actor_id, route_actor_generation)) = route
153 && (cancellation_key.actor_id != route_actor_id
154 || Some(cancellation_key.actor_generation) != route_actor_generation)
155 {
156 handle_http_protocol_violation(
157 ctx,
158 connection_session,
159 message_id,
160 "HTTP request abort actor identity does not match request start",
161 )
162 .await;
163 } else {
164 handle_request_abort(ctx, message_id, abort, Some(cancellation_key));
168 }
169 } else if advance_http_message_index(ctx, &message_id) {
170 handle_request_abort(ctx, message_id, abort, None);
172 } else {
173 handle_http_protocol_violation(
174 ctx,
175 connection_session,
176 message_id,
177 "invalid HTTP request-abort sequence",
178 )
179 .await;
180 }
181 }
182 protocol::ToEnvoyTunnelMessageKind::ToEnvoyRequestBodyCancel => {
183 if advance_http_message_index(ctx, &message_id) {
184 handle_request_body_cancel(ctx, message_id);
185 } else {
186 handle_http_protocol_violation(
187 ctx,
188 connection_session,
189 message_id,
190 "invalid HTTP request-body-cancel sequence",
191 )
192 .await;
193 }
194 }
195 protocol::ToEnvoyTunnelMessageKind::ToEnvoyResponseBodyWindowUpdate(update) => {
196 if advance_http_message_index(ctx, &message_id) {
197 handle_response_body_window_update(ctx, message_id, update.consumed_bytes);
198 } else {
199 handle_http_protocol_violation(
200 ctx,
201 connection_session,
202 message_id,
203 "invalid HTTP response-window sequence",
204 )
205 .await;
206 }
207 }
208 protocol::ToEnvoyTunnelMessageKind::ToEnvoyWebSocketOpen(open) => {
209 handle_ws_open(ctx, message_id, open).await;
210 }
211 protocol::ToEnvoyTunnelMessageKind::ToEnvoyWebSocketMessage(msg) => {
212 handle_ws_message(ctx, message_id, msg);
213 }
214 protocol::ToEnvoyTunnelMessageKind::ToEnvoyWebSocketClose(close) => {
215 handle_ws_close(ctx, message_id, close);
216 }
217 }
218}
219
220async fn handle_http_protocol_violation(
221 ctx: &mut EnvoyContext,
222 connection_session: u64,
223 message_id: protocol::MessageId,
224 detail: &'static str,
225) {
226 let key: [&[u8]; 2] = [&message_id.gateway_id, &message_id.request_id];
227 if let Some(route) = ctx.http_request_routes.get(&key) {
228 if route.session != connection_session {
229 send_response_abort_for_session(ctx, connection_session, message_id, detail).await;
230 return;
231 }
232 let actor_id = route.actor_id.clone();
233 let actor_generation = route.actor_generation;
234 if route.actor_admitted
235 && let Some(actor) = ctx.get_actor(&actor_id, actor_generation)
236 {
237 let _ = actor
238 .handle
239 .send(crate::actor::ToActor::ReqProtocolViolation {
240 message_id: message_id.clone(),
241 detail: detail.to_owned(),
242 });
243 }
244 ctx.http_request_routes.remove(&key);
245 ctx.http_message_indices.remove(&key);
246 return;
247 }
248
249 send_response_abort_for_session(ctx, connection_session, message_id, detail).await;
250}
251
252async fn send_response_abort_for_session(
253 ctx: &EnvoyContext,
254 connection_session: u64,
255 mut message_id: protocol::MessageId,
256 detail: &'static str,
257) {
258 message_id.message_index = 0;
259 let _ = crate::connection::ws_send_http_for_session(
260 &ctx.shared,
261 protocol::ToRivet::ToRivetTunnelMessage(protocol::ToRivetTunnelMessage {
262 message_id,
263 message_kind: protocol::ToRivetTunnelMessageKind::ToRivetResponseAbort(
264 protocol::ToRivetResponseAbort {
265 reason: protocol::HttpStreamAbortReason {
266 kind: protocol::HttpStreamAbortReasonKind::InternalError,
267 detail: Some(detail.to_owned()),
268 },
269 },
270 ),
271 }),
272 connection_session,
273 )
274 .await;
275}
276
277fn handle_request_body_cancel(ctx: &mut EnvoyContext, message_id: protocol::MessageId) {
278 let route = ctx
279 .http_request_routes
280 .get(&[&message_id.gateway_id, &message_id.request_id])
281 .map(|route| {
282 (
283 route.actor_id.clone(),
284 route.actor_generation,
285 route.actor_admitted,
286 )
287 });
288 if let Some((actor_id, actor_generation, true)) = &route
289 && let Some(actor) = ctx.get_actor(actor_id, *actor_generation)
290 {
291 let _ = actor.handle.send(crate::actor::ToActor::ReqBodyCancel {
292 message_id: message_id.clone(),
293 });
294 }
295 if matches!(route, Some((_, _, false))) {
296 ctx.http_request_routes
297 .remove(&[&message_id.gateway_id, &message_id.request_id]);
298 ctx.http_message_indices
299 .remove(&[&message_id.gateway_id, &message_id.request_id]);
300 }
301}
302
303fn handle_response_body_window_update(
304 ctx: &mut EnvoyContext,
305 message_id: protocol::MessageId,
306 consumed_bytes: u64,
307) {
308 let route = ctx
309 .http_request_routes
310 .get(&[&message_id.gateway_id, &message_id.request_id])
311 .map(|route| {
312 (
313 route.actor_id.clone(),
314 route.actor_generation,
315 route.actor_admitted,
316 )
317 });
318 if let Some((actor_id, actor_generation, true)) = &route
319 && let Some(actor) = ctx.get_actor(actor_id, *actor_generation)
320 {
321 let _ = actor
322 .handle
323 .send(crate::actor::ToActor::ResponseBodyWindowUpdate {
324 message_id,
325 consumed_bytes,
326 });
327 }
328}
329
330async fn handle_request_start(
331 ctx: &mut EnvoyContext,
332 connection_session: u64,
333 message_id: protocol::MessageId,
334 req: protocol::ToEnvoyRequestStart,
335) {
336 let key: [&[u8]; 2] = [&message_id.gateway_id, &message_id.request_id];
337 let actor_id = req.actor_id.clone();
338 let actor_generation = req.actor_generation;
339 let response_stream = req.response_stream;
340 let request_stream = req.stream;
341 if message_id.message_index != 0 {
342 tracing::warn!(
343 message_index = message_id.message_index,
344 "received invalid HTTP request start sequence"
345 );
346 send_response_abort_for_session(
347 ctx,
348 connection_session,
349 message_id,
350 "HTTP request start must use message index zero",
351 )
352 .await;
353 return;
354 }
355 prune_http_request_cancellations(ctx);
356 if let Some(actor_generation) = actor_generation {
357 let cancellation_key = request_cancellation_key(&message_id, &actor_id, actor_generation);
358 if ctx
359 .http_request_cancellations
360 .contains_key(&cancellation_key)
361 {
362 tracing::debug!(
363 %actor_id,
364 actor_generation,
365 gateway_id = ?message_id.gateway_id,
366 request_id = ?message_id.request_id,
367 "discarding HTTP request start cancelled before delivery"
368 );
369 return;
370 }
371 }
372 if let Some(existing) = ctx.http_request_routes.get(&key) {
373 let same_session = existing.session == connection_session;
374 handle_http_protocol_violation(
375 ctx,
376 connection_session,
377 message_id,
378 if same_session {
379 "duplicate HTTP request start"
380 } else {
381 "HTTP request identifier belongs to another connection"
382 },
383 )
384 .await;
385 return;
386 }
387 let actor_handle = ctx
388 .get_actor_for_admission(&actor_id, actor_generation)
389 .map(|actor| actor.handle.clone());
390
391 let Some(actor_handle) = actor_handle else {
392 let generation_mismatch =
393 actor_generation.is_some() && ctx.get_actor(&actor_id, None).is_some();
394 let (error_code, message) = if generation_mismatch {
395 tracing::warn!(
396 actor_id = %actor_id,
397 ?actor_generation,
398 "received request for stale actor generation"
399 );
400 (
401 "envoy.actor_generation_mismatch",
402 "Actor generation does not match",
403 )
404 } else {
405 tracing::warn!(actor_id = %actor_id, ?actor_generation, "received request for unknown actor");
406 ("envoy.actor_not_found", "Actor not found")
407 };
408 if request_stream {
412 ctx.http_message_indices.insert(&key, 1);
413 ctx.http_request_routes.insert(
414 &key,
415 HttpRequestRoute {
416 actor_id: actor_id.clone(),
417 actor_generation,
418 actor_admitted: false,
419 session: connection_session,
420 gateway_id: message_id.gateway_id,
421 request_id: message_id.request_id,
422 },
423 );
424 }
425 send_error_response(
426 ctx,
427 response_stream.then_some(connection_session),
428 message_id.gateway_id,
429 message_id.request_id,
430 error_code,
431 message,
432 actor_generation.map(|generation| (actor_id.as_str(), generation)),
433 )
434 .await;
435 return;
436 };
437 ctx.http_message_indices
438 .insert(&[&message_id.gateway_id, &message_id.request_id], 1);
439
440 ctx.http_request_routes.insert(
441 &[&message_id.gateway_id, &message_id.request_id],
442 HttpRequestRoute {
443 actor_id: actor_id.clone(),
444 actor_generation,
445 actor_admitted: true,
446 session: connection_session,
447 gateway_id: message_id.gateway_id,
448 request_id: message_id.request_id,
449 },
450 );
451
452 if actor_handle
453 .send(crate::actor::ToActor::ReqStart {
454 message_id: message_id.clone(),
455 req,
456 connection_session,
457 })
458 .is_err()
459 {
460 ctx.http_request_routes
461 .remove(&[&message_id.gateway_id, &message_id.request_id]);
462 ctx.http_message_indices
463 .remove(&[&message_id.gateway_id, &message_id.request_id]);
464 send_error_response(
465 ctx,
466 response_stream.then_some(connection_session),
467 message_id.gateway_id,
468 message_id.request_id,
469 "envoy.actor_not_found",
470 "Actor stopped before accepting request",
471 actor_generation.map(|generation| (actor_id.as_str(), generation)),
472 )
473 .await;
474 }
475}
476
477async fn handle_request_chunk(
478 ctx: &mut EnvoyContext,
479 message_id: protocol::MessageId,
480 chunk: protocol::ToEnvoyRequestChunk,
481) {
482 let route = ctx
483 .http_request_routes
484 .get(&[&message_id.gateway_id, &message_id.request_id])
485 .map(|route| {
486 (
487 route.actor_id.clone(),
488 route.actor_generation,
489 route.actor_admitted,
490 )
491 });
492
493 if let Some((actor_id, actor_generation, actor_admitted)) = &route {
494 if !actor_admitted {
495 if chunk.finish {
496 ctx.http_request_routes
497 .remove(&[&message_id.gateway_id, &message_id.request_id]);
498 ctx.http_message_indices
499 .remove(&[&message_id.gateway_id, &message_id.request_id]);
500 }
501 return;
502 }
503 if let Some(actor) = ctx.get_actor(actor_id, *actor_generation) {
504 let _ = actor.handle.send(crate::actor::ToActor::ReqChunk {
505 message_id: message_id.clone(),
506 chunk,
507 });
508 } else {
509 tracing::warn!(actor_id = %actor_id, "received request chunk for unknown actor");
510 }
511 } else {
512 tracing::warn!(
513 gateway_id = ?message_id.gateway_id,
514 request_id = ?message_id.request_id,
515 message_index = message_id.message_index,
516 "received request chunk without request start"
517 );
518 send_error_response(
519 ctx,
520 None,
521 message_id.gateway_id,
522 message_id.request_id,
523 "envoy.request_not_found",
524 "Request start was not delivered",
525 None,
526 )
527 .await;
528 }
529}
530
531fn handle_request_abort(
532 ctx: &mut EnvoyContext,
533 message_id: protocol::MessageId,
534 abort: protocol::ToEnvoyRequestAbort,
535 cancellation_key: Option<HttpRequestCancellationKey>,
536) {
537 let route = ctx
538 .http_request_routes
539 .get(&[&message_id.gateway_id, &message_id.request_id])
540 .map(|route| {
541 (
542 route.actor_id.clone(),
543 route.actor_generation,
544 route.actor_admitted,
545 )
546 });
547 if let Some((actor_id, actor_generation, true)) = &route {
548 if let Some(actor) = ctx.get_actor(actor_id, *actor_generation) {
549 let _ = actor.handle.send(crate::actor::ToActor::ReqAbort {
550 message_id: message_id.clone(),
551 reason: abort.reason,
552 });
553 }
554 }
555 if let Some(cancellation_key) = cancellation_key {
556 prune_http_request_cancellations(ctx);
557 ctx.http_request_cancellations
558 .insert(cancellation_key, crate::time::Instant::now());
559 tracing::debug!(
560 gateway_id = ?message_id.gateway_id,
561 request_id = ?message_id.request_id,
562 "recorded exact HTTP request cancellation"
563 );
564 }
565
566 ctx.http_request_routes
567 .remove(&[&message_id.gateway_id, &message_id.request_id]);
568 ctx.http_message_indices
569 .remove(&[&message_id.gateway_id, &message_id.request_id]);
570}
571
572async fn handle_ws_open(
573 ctx: &mut EnvoyContext,
574 message_id: protocol::MessageId,
575 open: protocol::ToEnvoyWebSocketOpen,
576) {
577 let actor_id = open.actor_id.clone();
578 let actor_generation = open.actor_generation;
579 let actor_handle = ctx
580 .get_actor_for_admission(&actor_id, actor_generation)
581 .map(|actor| actor.handle.clone());
582
583 let Some(actor_handle) = actor_handle else {
584 let generation_mismatch =
585 actor_generation.is_some() && ctx.get_actor(&actor_id, None).is_some();
586 let reason = if generation_mismatch {
587 tracing::warn!(
588 actor_id = %actor_id,
589 ?actor_generation,
590 "received ws open for stale actor generation"
591 );
592 "envoy.actor_generation_mismatch"
593 } else {
594 tracing::warn!(actor_id = %actor_id, ?actor_generation, "received ws open for unknown actor");
595 "envoy.actor_not_found"
596 };
597
598 ws_send(
599 &ctx.shared,
600 protocol::ToRivet::ToRivetTunnelMessage(protocol::ToRivetTunnelMessage {
601 message_id,
602 message_kind: protocol::ToRivetTunnelMessageKind::ToRivetWebSocketClose(
603 protocol::ToRivetWebSocketClose {
604 code: Some(1011),
605 reason: Some(reason.to_string()),
606 hibernate: false,
607 },
608 ),
609 }),
610 )
611 .await;
612 return;
613 };
614
615 ctx.request_to_actor.insert(
616 &[&message_id.gateway_id, &message_id.request_id],
617 WebSocketRoute {
618 actor_id: actor_id.clone(),
619 actor_generation,
620 },
621 );
622 ctx.shared
623 .live_tunnel_requests
624 .lock()
625 .expect("shared live tunnel request registry poisoned")
626 .insert(
627 make_ws_key(&message_id.gateway_id, &message_id.request_id),
628 actor_id.clone(),
629 );
630
631 let headers = open
633 .headers
634 .iter()
635 .map(|(k, v)| (k.clone(), v.clone()))
636 .collect();
637
638 if actor_handle
639 .send(crate::actor::ToActor::WsOpen {
640 message_id: message_id.clone(),
641 path: open.path,
642 headers,
643 })
644 .is_err()
645 {
646 ctx.request_to_actor
647 .remove(&[&message_id.gateway_id, &message_id.request_id]);
648 ctx.shared
649 .live_tunnel_requests
650 .lock()
651 .expect("shared live tunnel request registry poisoned")
652 .remove(&make_ws_key(&message_id.gateway_id, &message_id.request_id));
653 ws_send(
654 &ctx.shared,
655 protocol::ToRivet::ToRivetTunnelMessage(protocol::ToRivetTunnelMessage {
656 message_id,
657 message_kind: protocol::ToRivetTunnelMessageKind::ToRivetWebSocketClose(
658 protocol::ToRivetWebSocketClose {
659 code: Some(1011),
660 reason: Some("envoy.actor_not_found".to_string()),
661 hibernate: false,
662 },
663 ),
664 }),
665 )
666 .await;
667 }
668}
669
670fn handle_ws_message(
671 ctx: &mut EnvoyContext,
672 message_id: protocol::MessageId,
673 msg: protocol::ToEnvoyWebSocketMessage,
674) {
675 let route = ctx
676 .request_to_actor
677 .get(&[&message_id.gateway_id, &message_id.request_id])
678 .cloned();
679 if let Some(route) = &route {
680 if let Some(actor) = ctx.get_actor(&route.actor_id, route.actor_generation) {
681 let _ = actor
682 .handle
683 .send(crate::actor::ToActor::WsMsg { message_id, msg });
684 } else if route.actor_generation.is_none() {
685 ctx.buffered_actor_messages
686 .entry(route.actor_id.clone())
687 .or_default()
688 .push(BufferedActorMessage::WsMsg { message_id, msg });
689 }
690 }
691}
692
693fn handle_ws_close(
694 ctx: &mut EnvoyContext,
695 message_id: protocol::MessageId,
696 close: protocol::ToEnvoyWebSocketClose,
697) {
698 let route = ctx
699 .request_to_actor
700 .get(&[&message_id.gateway_id, &message_id.request_id])
701 .cloned();
702 if let Some(route) = &route {
703 if let Some(actor) = ctx.get_actor(&route.actor_id, route.actor_generation) {
704 let _ = actor.handle.send(crate::actor::ToActor::WsClose {
705 message_id: message_id.clone(),
706 close,
707 });
708 } else if route.actor_generation.is_none() {
709 ctx.buffered_actor_messages
710 .entry(route.actor_id.clone())
711 .or_default()
712 .push(BufferedActorMessage::WsClose {
713 message_id: message_id.clone(),
714 close,
715 });
716 }
717 }
718
719 ctx.request_to_actor
720 .remove(&[&message_id.gateway_id, &message_id.request_id]);
721 ctx.shared
722 .live_tunnel_requests
723 .lock()
724 .expect("shared live tunnel request registry poisoned")
725 .remove(&make_ws_key(&message_id.gateway_id, &message_id.request_id));
726}
727
728pub fn send_hibernatable_ws_message_ack(
729 ctx: &mut EnvoyContext,
730 gateway_id: protocol::GatewayId,
731 request_id: protocol::RequestId,
732 envoy_message_index: u16,
733) {
734 let route = ctx
735 .request_to_actor
736 .get(&[&gateway_id, &request_id])
737 .cloned();
738 if let Some(route) = &route {
739 if let Some(actor) = ctx.get_actor(&route.actor_id, route.actor_generation) {
740 let _ = actor.handle.send(crate::actor::ToActor::HwsAck {
741 gateway_id,
742 request_id,
743 envoy_message_index,
744 });
745 }
746 }
747}
748
749pub async fn resend_buffered_tunnel_messages(ctx: &mut EnvoyContext) {
750 if ctx.buffered_messages.is_empty() {
751 return;
752 }
753
754 tracing::info!(
755 count = ctx.buffered_messages.len(),
756 "resending buffered tunnel messages"
757 );
758
759 let messages = std::mem::take(&mut ctx.buffered_messages);
760 let mut messages = messages.into_iter();
761 while let Some(msg) = messages.next() {
762 let failed = ws_send(
763 &ctx.shared,
764 protocol::ToRivet::ToRivetTunnelMessage(msg.clone()),
765 )
766 .await;
767 if failed {
768 ctx.buffered_messages.push(msg);
769 ctx.buffered_messages.extend(messages);
770 break;
771 }
772 }
773}
774
775pub async fn send_or_buffer_tunnel_message(
776 ctx: &mut EnvoyContext,
777 msg: protocol::ToRivetTunnelMessage,
778) {
779 let failed = ws_send(
780 &ctx.shared,
781 protocol::ToRivet::ToRivetTunnelMessage(msg.clone()),
782 )
783 .await;
784 if failed {
785 ctx.buffered_messages.push(msg);
786 }
787}
788
789async fn send_error_response(
790 ctx: &EnvoyContext,
791 connection_session: Option<u64>,
792 gateway_id: protocol::GatewayId,
793 request_id: protocol::RequestId,
794 error_code: &str,
795 message: &str,
796 actor: Option<(&str, u32)>,
797) {
798 let code = error_code.strip_prefix("envoy.").unwrap_or(error_code);
799 let mut error = serde_json::json!({
800 "group": "envoy",
801 "code": code,
802 "message": message,
803 });
804 if let Some((actor_id, generation)) = actor {
805 error["actor"] = serde_json::json!({
806 "actorId": actor_id,
807 "generation": generation,
808 });
809 }
810 let body = serde_json::to_vec(&error).expect("serialize canonical HTTP error response");
811 let mut headers = HashMap::new();
812 headers.insert("x-rivet-error".to_string(), error_code.to_owned());
813 headers.insert("content-type".to_string(), "application/json".to_owned());
814 headers.insert("content-length".to_string(), body.len().to_string());
815
816 let response = protocol::ToRivet::ToRivetTunnelMessage(protocol::ToRivetTunnelMessage {
817 message_id: protocol::MessageId {
818 gateway_id,
819 request_id,
820 message_index: 0,
821 },
822 message_kind: protocol::ToRivetTunnelMessageKind::ToRivetResponseStart(
823 protocol::ToRivetResponseStart {
824 status: 503,
825 headers,
826 body: Some(body),
827 stream: false,
828 },
829 ),
830 });
831 match connection_session {
832 Some(session) => {
833 let _ =
834 crate::connection::ws_send_http_for_session(&ctx.shared, response, session).await;
835 }
836 None => {
837 ws_send(&ctx.shared, response).await;
838 }
839 }
840}