syncweb_core/media/
bridge.rs1use std::net::SocketAddr;
2
3use futures_util::{SinkExt, StreamExt};
4use tokio::sync::broadcast;
5use tokio::task::JoinHandle;
6use tokio_tungstenite::tungstenite::Message;
7
8use crate::SyncwebError;
9use crate::daemon::{IpcRequest, IpcResponse, IpcServer};
10
11pub const DEFAULT_BRIDGE_LISTEN: &str = "127.0.0.1:9192";
13
14pub struct BridgeServer {
20 addr: SocketAddr,
21 ipc: IpcServer,
22}
23
24impl BridgeServer {
25 pub fn new(listen_addr: &str, ipc: IpcServer) -> Result<Self, SyncwebError> {
31 let addr = listen_addr
32 .parse::<SocketAddr>()
33 .map_err(|error| SyncwebError::operation("invalid websocket bridge listen address", error))?;
34 Ok(Self { addr, ipc })
35 }
36
37 pub async fn run(self, shutdown: broadcast::Sender<()>) -> Result<(), SyncwebError> {
43 let listener = tokio::net::TcpListener::bind(self.addr)
44 .await
45 .map_err(|error| SyncwebError::operation("failed to bind websocket bridge", error))?;
46 let local_addr = listener
47 .local_addr()
48 .map_err(|error| SyncwebError::operation("failed to get websocket bridge local address", error))?;
49 tracing::info!(addr = %local_addr, "websocket bridge started");
50
51 let mut shutdown_rx = shutdown.subscribe();
52 loop {
53 tokio::select! {
54 shutdown_result = shutdown_rx.recv() => {
55 match shutdown_result {
56 Ok(()) | Err(broadcast::error::RecvError::Closed) => break,
57 Err(broadcast::error::RecvError::Lagged(_)) => {}
58 }
59 }
60 accepted = listener.accept() => {
61 let (stream, peer) = accepted.map_err(|error| {
62 SyncwebError::operation("websocket bridge accept failed", error)
63 })?;
64 let ipc = self.ipc.clone();
65 tokio::spawn(async move {
66 if let Err(error) = Box::pin(serve_connection(stream, peer, ipc)).await {
67 tracing::warn!(%peer, %error, "websocket bridge connection failed");
68 }
69 });
70 }
71 }
72 }
73 tracing::debug!("websocket bridge shutdown signal received");
74 Ok(())
75 }
76
77 #[must_use]
78 pub fn spawn(self, shutdown: broadcast::Sender<()>) -> JoinHandle<Result<(), SyncwebError>> {
79 tokio::spawn(async move { self.run(shutdown).await })
80 }
81}
82
83async fn serve_connection(stream: tokio::net::TcpStream, peer: SocketAddr, ipc: IpcServer) -> Result<(), SyncwebError> {
84 let websocket = tokio_tungstenite::accept_async(stream)
85 .await
86 .map_err(|error| SyncwebError::operation("websocket bridge handshake failed", error))?;
87 tracing::debug!(%peer, "websocket bridge connection accepted");
88 let (mut sink, mut incoming) = websocket.split();
89 while let Some(item) = incoming.next().await {
90 let message = item.map_err(|error| SyncwebError::operation("websocket bridge receive failed", error))?;
91 match message {
92 Message::Text(text) => {
93 let response = match serde_json::from_str::<IpcRequest>(text.as_ref()) {
94 Ok(request) => ipc.handle_request(request).await,
95 Err(error) => IpcResponse::Error {
96 message: format!("invalid JSON: {error}"),
97 },
98 };
99 let payload = serde_json::to_string(&response)
100 .map_err(|error| SyncwebError::operation("failed to serialize bridge response", error))?;
101 sink.send(Message::Text(payload.into()))
102 .await
103 .map_err(|error| SyncwebError::operation("websocket bridge send failed", error))?;
104 }
105 Message::Close(_) => break,
106 Message::Binary(_) | Message::Ping(_) | Message::Pong(_) | Message::Frame(_) => {}
107 }
108 }
109 Ok(())
110}