mirror of
https://github.com/lexmount/moli.git
synced 2026-10-06 16:01:00 +00:00
123 lines
3.6 KiB
Rust
123 lines
3.6 KiB
Rust
use axum::extract::ws::{Message, WebSocket};
|
|
use futures_util::StreamExt;
|
|
use std::time::Duration;
|
|
|
|
use crate::{cdp_frontend::CdpFrontendEndpoint, cdp_writer::spawn_socket_sink};
|
|
|
|
pub(super) enum CdpFrontendSocketKind {
|
|
Browser,
|
|
Target { target_id: String },
|
|
}
|
|
|
|
pub(super) async fn run_cdp_frontend_socket(
|
|
socket: WebSocket,
|
|
endpoint: CdpFrontendEndpoint,
|
|
kind: CdpFrontendSocketKind,
|
|
) -> bool {
|
|
let (socket_tx, mut socket_rx) = socket.split();
|
|
let (sink, mut writer_finished_rx) = spawn_socket_sink(socket_tx);
|
|
let socket_sink = sink.clone();
|
|
let frontend_id = match &kind {
|
|
CdpFrontendSocketKind::Browser => endpoint.attach_browser(sink).await,
|
|
CdpFrontendSocketKind::Target { target_id } => {
|
|
endpoint.attach_target(target_id.clone(), sink).await
|
|
}
|
|
};
|
|
let frontend_id = match frontend_id {
|
|
Ok(frontend_id) => frontend_id,
|
|
Err(error) => {
|
|
tracing::warn!(?error, "failed to attach CDP WebSocket frontend");
|
|
return false;
|
|
}
|
|
};
|
|
let mut detach_guard = CdpFrontendDetachGuard {
|
|
endpoint: endpoint.clone(),
|
|
frontend_id: Some(frontend_id),
|
|
kind: match kind {
|
|
CdpFrontendSocketKind::Browser => CdpFrontendKind::Browser,
|
|
CdpFrontendSocketKind::Target { .. } => CdpFrontendKind::Target,
|
|
},
|
|
};
|
|
|
|
let exit = loop {
|
|
tokio::select! {
|
|
_ = endpoint.wait_for_shutdown() => break CdpSocketExit::TransportClose,
|
|
_ = &mut writer_finished_rx => break CdpSocketExit::WriterFinished,
|
|
maybe_message = socket_rx.next() => {
|
|
let Some(Ok(message)) = maybe_message else {
|
|
break CdpSocketExit::TransportClose;
|
|
};
|
|
match message {
|
|
Message::Text(text) => {
|
|
if !endpoint.command(frontend_id, text.to_string()).await {
|
|
break CdpSocketExit::TransportClose;
|
|
}
|
|
}
|
|
Message::Close(_) => break CdpSocketExit::PeerClose,
|
|
_ => {}
|
|
}
|
|
}
|
|
}
|
|
};
|
|
detach_guard.detach();
|
|
match exit {
|
|
CdpSocketExit::WriterFinished => wait_for_peer_close(&mut socket_rx).await,
|
|
CdpSocketExit::PeerClose => {
|
|
socket_sink.peer_close_received();
|
|
let _ = writer_finished_rx.await;
|
|
}
|
|
CdpSocketExit::TransportClose => {
|
|
socket_sink.close();
|
|
let _ = writer_finished_rx.await;
|
|
}
|
|
}
|
|
true
|
|
}
|
|
|
|
enum CdpSocketExit {
|
|
WriterFinished,
|
|
PeerClose,
|
|
TransportClose,
|
|
}
|
|
|
|
async fn wait_for_peer_close(socket_rx: &mut futures_util::stream::SplitStream<WebSocket>) {
|
|
let wait = async {
|
|
while let Some(message) = socket_rx.next().await {
|
|
match message {
|
|
Ok(Message::Close(_)) | Err(_) => break,
|
|
Ok(_) => {}
|
|
}
|
|
}
|
|
};
|
|
let _ = tokio::time::timeout(Duration::from_secs(1), wait).await;
|
|
}
|
|
|
|
enum CdpFrontendKind {
|
|
Browser,
|
|
Target,
|
|
}
|
|
|
|
struct CdpFrontendDetachGuard {
|
|
endpoint: CdpFrontendEndpoint,
|
|
frontend_id: Option<u64>,
|
|
kind: CdpFrontendKind,
|
|
}
|
|
|
|
impl CdpFrontendDetachGuard {
|
|
fn detach(&mut self) {
|
|
let Some(frontend_id) = self.frontend_id.take() else {
|
|
return;
|
|
};
|
|
match self.kind {
|
|
CdpFrontendKind::Browser => self.endpoint.detach_browser(frontend_id),
|
|
CdpFrontendKind::Target => self.endpoint.detach_target(frontend_id),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Drop for CdpFrontendDetachGuard {
|
|
fn drop(&mut self) {
|
|
self.detach();
|
|
}
|
|
}
|