o_sfu/runtime/websocket_server/
controller.rs1use std::sync::Arc;
7
8use axum::{
9 extract::{FromRef, State, ws::WebSocketUpgrade},
10 http::StatusCode,
11 response::{IntoResponse, Response},
12};
13use tokio_util::{sync::CancellationToken, task::TaskTracker};
14use tracing::warn;
15
16use super::{admission::PreAuthWebSocketAdmissionRejection, io::MAX_CLIENT_FRAME_BYTES, session};
17use crate::{
18 config::{AuthConfig, UserConfig},
19 core::prelude::SfuCore,
20 runtime::{
21 RuntimeMetrics, RuntimeState, request_origin::RequestOrigin, room::RoomManager,
22 telemetry::schema::event as telemetry_event,
23 },
24};
25
26pub(crate) struct WebSocketServices {
27 pub(super) auth: AuthConfig,
28 pub(super) user: UserConfig,
29 pub(super) room_manager: Arc<RoomManager>,
30 pub(super) sfu_core: SfuCore,
31 pub(super) metrics: Arc<RuntimeMetrics>,
32 pub(super) shutdown: CancellationToken,
33 sessions: TaskTracker,
34 pre_auth_websocket_admission: super::PreAuthWebSocketAdmission,
35}
36
37impl FromRef<RuntimeState> for WebSocketServices {
38 fn from_ref(state: &RuntimeState) -> Self {
39 Self {
40 auth: state.config.auth.clone(),
41 user: state.config.user,
42 room_manager: Arc::clone(&state.room_manager),
43 sfu_core: state.sfu_core.clone(),
44 metrics: Arc::clone(&state.metrics),
45 shutdown: state.session_shutdown.clone(),
46 sessions: state.session_tasks.clone(),
47 pre_auth_websocket_admission: state.pre_auth_websocket_admission.clone(),
48 }
49 }
50}
51
52pub(crate) async fn upgrade(
53 State(services): State<WebSocketServices>,
54 origin: RequestOrigin,
55 websocket: WebSocketUpgrade,
56) -> Response {
57 let remote_address = Arc::<str>::from(origin.remote_address);
58 let pre_auth_permit = match services
59 .pre_auth_websocket_admission
60 .try_acquire(Arc::clone(&remote_address))
61 {
62 Ok(permit) => permit,
63 Err(rejection) => {
64 reject_pre_auth_admission(&services, remote_address.as_ref(), rejection);
65 return StatusCode::SERVICE_UNAVAILABLE.into_response();
66 }
67 };
68 let session_task = services.sessions.token();
69 websocket
70 .max_message_size(MAX_CLIENT_FRAME_BYTES)
71 .max_frame_size(MAX_CLIENT_FRAME_BYTES)
72 .on_upgrade(move |socket| async move {
73 session::run(socket, services, remote_address, pre_auth_permit).await;
74 drop(session_task);
75 })
76}
77
78fn reject_pre_auth_admission(
79 services: &WebSocketServices,
80 remote_address: &str,
81 rejection: PreAuthWebSocketAdmissionRejection,
82) {
83 match rejection {
84 PreAuthWebSocketAdmissionRejection::Global => {
85 warn!(
86 event = telemetry_event::WS_HANDSHAKE_REJECTED,
87 remote_address,
88 max_pre_auth_websocket_sessions = services.auth.max_pre_auth_websocket_sessions,
89 "rejecting websocket upgrade because global pre-auth admission is full"
90 );
91 }
92 PreAuthWebSocketAdmissionRejection::Origin => {
93 warn!(
94 event = telemetry_event::WS_HANDSHAKE_REJECTED,
95 remote_address,
96 max_pre_auth_websocket_sessions_per_origin =
97 services.auth.max_pre_auth_websocket_sessions_per_origin,
98 "rejecting websocket upgrade because origin pre-auth admission is full"
99 );
100 }
101 }
102}