Initial commit: FunConnect project with server, relay, client and admin panel
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
68
client/src/network/relay.rs
Normal file
68
client/src/network/relay.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
/// QUIC relay client — used when P2P hole punch fails
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use quinn::Connection;
|
||||
use std::net::SocketAddr;
|
||||
use std::time::Duration;
|
||||
use tokio::time::timeout;
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::network::quic::open_endpoint;
|
||||
use crate::network::relay_selector::DEFAULT_RELAY_PORT;
|
||||
|
||||
/// Connect to a relay server's QUIC endpoint
|
||||
pub async fn connect_relay(
|
||||
relay_url: &str,
|
||||
room_id: Uuid,
|
||||
access_token: &str,
|
||||
) -> Result<Connection> {
|
||||
let (host, port) = parse_relay_url(relay_url);
|
||||
|
||||
let relay_addr: SocketAddr = {
|
||||
let addrs: Vec<SocketAddr> = tokio::net::lookup_host(format!("{}:{}", host, port))
|
||||
.await?
|
||||
.collect();
|
||||
addrs.into_iter().next().ok_or_else(|| anyhow!("relay DNS failed"))?
|
||||
};
|
||||
|
||||
let ep = open_endpoint("0.0.0.0:0".parse()?)?;
|
||||
|
||||
tracing::info!("Connecting to relay at {}", relay_addr);
|
||||
let connecting = ep.connect(relay_addr, &host)?;
|
||||
let conn = timeout(Duration::from_secs(10), connecting).await
|
||||
.map_err(|_| anyhow!("relay connection timed out"))?
|
||||
.map_err(|e| anyhow!("relay QUIC error: {}", e))?;
|
||||
|
||||
// Send auth handshake on a unidirectional stream using write_chunk
|
||||
let mut stream = conn.open_uni().await?;
|
||||
let handshake = serde_json::json!({
|
||||
"token": access_token,
|
||||
"room_id": room_id.to_string(),
|
||||
});
|
||||
let msg = serde_json::to_vec(&handshake)?;
|
||||
let len = (msg.len() as u32).to_be_bytes();
|
||||
stream.write_chunk(bytes::Bytes::copy_from_slice(&len)).await
|
||||
.map_err(|e| anyhow!("{}", e))?;
|
||||
stream.write_chunk(bytes::Bytes::from(msg)).await
|
||||
.map_err(|e| anyhow!("{}", e))?;
|
||||
let _ = stream.finish();
|
||||
let _ = stream.stopped().await;
|
||||
|
||||
tracing::info!("Relay handshake sent for room {}", room_id);
|
||||
Ok(conn)
|
||||
}
|
||||
|
||||
fn parse_relay_url(url: &str) -> (String, u16) {
|
||||
let cleaned = url
|
||||
.trim_start_matches("https://")
|
||||
.trim_start_matches("http://")
|
||||
.trim_start_matches("quic://");
|
||||
|
||||
if let Some((host, port_str)) = cleaned.rsplit_once(':') {
|
||||
if let Ok(port) = port_str.parse::<u16>() {
|
||||
return (host.to_string(), port);
|
||||
}
|
||||
}
|
||||
|
||||
(cleaned.to_string(), DEFAULT_RELAY_PORT)
|
||||
}
|
||||
Reference in New Issue
Block a user