backend: refactor proxy code
This commit is contained in:
@@ -10,7 +10,7 @@ pub struct GamestreamChannels {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn start_connection(
|
pub fn start_connection(
|
||||||
stream: crate::backend::Stream,
|
stream: &crate::backend::Stream,
|
||||||
address: &str,
|
address: &str,
|
||||||
) -> Result<GamestreamChannels> {
|
) -> Result<GamestreamChannels> {
|
||||||
let mut server_info = config::server_info(
|
let mut server_info = config::server_info(
|
||||||
|
|||||||
@@ -1,143 +0,0 @@
|
|||||||
use salvo::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use tokio::{select, sync::RwLock};
|
|
||||||
use tracing::{error, info};
|
|
||||||
|
|
||||||
use crate::common::{AppError, AppResult};
|
|
||||||
|
|
||||||
pub struct Proxy {
|
|
||||||
pub cert_hash: [u8; 32],
|
|
||||||
//pub cert_hash: String,
|
|
||||||
pub stream: RwLock<Option<crate::backend::Stream>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Proxy {
|
|
||||||
pub fn new(cert_hash: [u8; 32]) -> Self {
|
|
||||||
//pub fn new(cert_hash: String) -> Self {
|
|
||||||
Proxy {
|
|
||||||
stream: RwLock::new(None),
|
|
||||||
cert_hash,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct ProxySetupParams {
|
|
||||||
pub stream: crate::backend::Stream,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct ProxySetupResponse {
|
|
||||||
pub cert_hash: [u8; 32],
|
|
||||||
//pub cert_hash: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[craft]
|
|
||||||
impl crate::proxy::Proxy {
|
|
||||||
#[craft(handler)]
|
|
||||||
pub async fn stream_setup(
|
|
||||||
self: ::std::sync::Arc<Self>,
|
|
||||||
body: salvo::oapi::extract::JsonBody<ProxySetupParams>,
|
|
||||||
) -> AppResult<Json<ProxySetupResponse>> {
|
|
||||||
let mut writer = self.stream.write().await;
|
|
||||||
*writer = Some(body.stream.clone());
|
|
||||||
|
|
||||||
info!("Configured proxy with config: {:?}", body.stream);
|
|
||||||
|
|
||||||
Ok(Json(ProxySetupResponse {
|
|
||||||
cert_hash: self.cert_hash,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[craft(handler)]
|
|
||||||
pub async fn stream_connect(self: ::std::sync::Arc<Self>, req: &mut Request) -> AppResult<()> {
|
|
||||||
let standard_error = Err(crate::common::AppError {
|
|
||||||
status_code: StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
description: "Could not start stream".to_string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
info!("WebTransport connection initiated");
|
|
||||||
|
|
||||||
let session = match req.web_transport_mut().await {
|
|
||||||
Ok(w) => w,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Could not initalize WebTransport connection: {e}");
|
|
||||||
return Err(AppError {
|
|
||||||
status_code: StatusCode::BAD_REQUEST,
|
|
||||||
description: "User did not connect with a WebTransport connection".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let stream = match self.stream.read().await.clone() {
|
|
||||||
Some(s) => s,
|
|
||||||
None => {
|
|
||||||
error!("Stream has not been configured, cannot connect to server");
|
|
||||||
return Err(AppError {
|
|
||||||
status_code: StatusCode::BAD_REQUEST,
|
|
||||||
description: "Proxy has not been configured yet: THIS IS A BUG".to_string(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tracing::debug!(
|
|
||||||
"Connecting to stream at address {} with stream config {:?}",
|
|
||||||
stream.server_address,
|
|
||||||
stream
|
|
||||||
);
|
|
||||||
|
|
||||||
let (tx, rx) = tokio::sync::oneshot::channel();
|
|
||||||
let (stop_tx, stop_rx) = tokio::sync::oneshot::channel::<()>();
|
|
||||||
|
|
||||||
let host = stream.server_address.clone();
|
|
||||||
std::thread::spawn(move || {
|
|
||||||
let result = crate::gamestream::start_connection(stream, &host);
|
|
||||||
|
|
||||||
let _ = tx.send(result);
|
|
||||||
|
|
||||||
let _ = stop_rx.blocking_recv();
|
|
||||||
|
|
||||||
crate::gamestream::stop_connection();
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut channels = match rx.await {
|
|
||||||
Ok(r) => match r {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(e) => {
|
|
||||||
error!("Could not get gamestream communication channels: {e}");
|
|
||||||
return standard_error;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
Err(e) => {
|
|
||||||
error!("Could not start connection: {e}");
|
|
||||||
return standard_error;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
loop {
|
|
||||||
select! {
|
|
||||||
recv = channels.decoder_rx.recv() => {
|
|
||||||
match recv {
|
|
||||||
Some(frame) => {
|
|
||||||
info!("Got decoder packet")
|
|
||||||
}
|
|
||||||
None => {
|
|
||||||
error!("Decoder channel is None");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//// Handle bidirectional streams
|
|
||||||
//if let Ok(Some(webtransport::server::AcceptedBi::BidiStream(_, stream))) =
|
|
||||||
// session.accept_bi().await
|
|
||||||
//{
|
|
||||||
// let (send, recv) = salvo::proto::quic::BidiStream::split(stream);
|
|
||||||
// // Process bidirectional stream data
|
|
||||||
//}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
use anyhow::{Result, anyhow};
|
||||||
|
|
||||||
|
use salvo::{
|
||||||
|
prelude::*,
|
||||||
|
proto::quic::BidiStream,
|
||||||
|
webtransport::{self},
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tracing::{error, info};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
backend,
|
||||||
|
common::{AppError, AppResult},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct ProxySetupParams {
|
||||||
|
pub stream: backend::Stream,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
pub struct ProxySetupResponse {
|
||||||
|
pub cert_hash: [u8; 32],
|
||||||
|
//pub cert_hash: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn setup_webtransport(
|
||||||
|
req: &mut Request,
|
||||||
|
) -> Result<(
|
||||||
|
impl tokio::io::AsyncWrite + Send + Sync + 'static,
|
||||||
|
impl tokio::io::AsyncRead + Send + Sync + 'static,
|
||||||
|
//salvo::webtransport::stream::SendStream<
|
||||||
|
// impl salvo::proto::quic::SendStream<salvo::hyper::body::Bytes>,
|
||||||
|
// salvo::hyper::body::Bytes,
|
||||||
|
//>,
|
||||||
|
//salvo::webtransport::stream::RecvStream<
|
||||||
|
// impl salvo::proto::quic::RecvStream,
|
||||||
|
// salvo::hyper::body::Bytes,
|
||||||
|
//>,
|
||||||
|
)> {
|
||||||
|
let session = req.web_transport_mut().await?;
|
||||||
|
let bidirectional_stream = session
|
||||||
|
.accept_bi()
|
||||||
|
.await?
|
||||||
|
.ok_or(anyhow!("No bidirectional stream"))?;
|
||||||
|
|
||||||
|
if let webtransport::server::AcceptedBi::BidiStream(_, stream) = bidirectional_stream {
|
||||||
|
Ok(stream.split())
|
||||||
|
} else {
|
||||||
|
Err(anyhow!("bidirectional stream was of the wrong type"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[craft]
|
||||||
|
impl crate::proxy::Proxy {
|
||||||
|
#[craft(handler)]
|
||||||
|
pub async fn stream_setup(
|
||||||
|
self: ::std::sync::Arc<Self>,
|
||||||
|
body: salvo::oapi::extract::JsonBody<ProxySetupParams>,
|
||||||
|
) -> AppResult<Json<ProxySetupResponse>> {
|
||||||
|
let mut writer = self.stream.write().await;
|
||||||
|
*writer = Some(body.stream.clone());
|
||||||
|
|
||||||
|
info!("Configured proxy with config: {:?}", body.stream);
|
||||||
|
|
||||||
|
Ok(Json(ProxySetupResponse {
|
||||||
|
cert_hash: self.cert_hash,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[craft(handler)]
|
||||||
|
pub async fn stream_connect(self: ::std::sync::Arc<Self>, req: &mut Request) -> AppResult<()> {
|
||||||
|
let standard_error = Err(crate::common::AppError {
|
||||||
|
status_code: StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
description: "Could not start stream".to_string(),
|
||||||
|
});
|
||||||
|
|
||||||
|
info!("WebTransport connection initiated");
|
||||||
|
let (wt_send, wt_recv) = match setup_webtransport(req).await {
|
||||||
|
Ok(w) => w,
|
||||||
|
Err(e) => {
|
||||||
|
error!("Could not upgrade connection to WebTransport: {e}");
|
||||||
|
return standard_error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let stream = match self.stream.read().await.clone() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
error!("Stream has not been configured, cannot connect to server");
|
||||||
|
return Err(AppError {
|
||||||
|
status_code: StatusCode::BAD_REQUEST,
|
||||||
|
description: "Proxy has not been configured yet: THIS IS A BUG".to_string(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
match super::proxy_main(stream, wt_send, wt_recv).await {
|
||||||
|
Ok(()) => Ok(()),
|
||||||
|
Err(e) => {
|
||||||
|
error!("Proxy main loop failed: {e}");
|
||||||
|
standard_error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
use anyhow::{Context, Result};
|
||||||
|
use salvo::{conn::http1::Connection, hyper::body::Bytes, proto::WebTransportSession};
|
||||||
|
use tokio::{io::AsyncReadExt, io::AsyncWriteExt, select, sync::RwLock};
|
||||||
|
use tracing::{debug, error, info};
|
||||||
|
|
||||||
|
use crate::{backend, gamestream};
|
||||||
|
|
||||||
|
pub mod handler;
|
||||||
|
|
||||||
|
pub struct Proxy {
|
||||||
|
pub cert_hash: [u8; 32],
|
||||||
|
//pub cert_hash: String,
|
||||||
|
pub stream: RwLock<Option<backend::Stream>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Proxy {
|
||||||
|
pub fn new(cert_hash: [u8; 32]) -> Self {
|
||||||
|
//pub fn new(cert_hash: String) -> Self {
|
||||||
|
Proxy {
|
||||||
|
stream: RwLock::new(None),
|
||||||
|
cert_hash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Channels {
|
||||||
|
gamestream_channels: gamestream::GamestreamChannels,
|
||||||
|
stop_tx: tokio::sync::oneshot::Sender<()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn proxy_main(
|
||||||
|
stream: backend::Stream,
|
||||||
|
mut wt_send: impl tokio::io::AsyncWrite + Send + Sync + 'static + std::marker::Unpin,
|
||||||
|
mut wt_recv: impl tokio::io::AsyncRead + Send + Sync + 'static + std::marker::Unpin,
|
||||||
|
) -> Result<()> {
|
||||||
|
debug!(
|
||||||
|
"Connecting to stream at address {} with stream config {:?}",
|
||||||
|
stream.server_address, stream
|
||||||
|
);
|
||||||
|
|
||||||
|
let mut channels = spawn_gamestream(stream).await?;
|
||||||
|
|
||||||
|
let mut buffer = vec![0; 65536].into_boxed_slice();
|
||||||
|
|
||||||
|
loop {
|
||||||
|
select! {
|
||||||
|
gamestream_packet = channels.gamestream_channels.decoder_rx.recv() => {
|
||||||
|
match gamestream_packet {
|
||||||
|
Some(frame) => {
|
||||||
|
info!("Got decoder packet");
|
||||||
|
wt_send.write_all(&[0;32]).await?;
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
error!("Decoder channel is None");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
webtransport_packet = wt_recv.read(&mut buffer) => {
|
||||||
|
info!("Got packet from client");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn spawn_gamestream(stream: backend::Stream) -> Result<Channels> {
|
||||||
|
let (tx, rx) = tokio::sync::oneshot::channel();
|
||||||
|
let (stop_tx, stop_rx) = tokio::sync::oneshot::channel::<()>();
|
||||||
|
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
let result = gamestream::start_connection(&stream, &stream.server_address);
|
||||||
|
|
||||||
|
let _ = tx.send(result);
|
||||||
|
|
||||||
|
let _ = stop_rx.blocking_recv();
|
||||||
|
|
||||||
|
gamestream::stop_connection();
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Channels {
|
||||||
|
stop_tx,
|
||||||
|
gamestream_channels: rx
|
||||||
|
.await?
|
||||||
|
.context("Could not get gamestream communication channels")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@ use tracing::{debug, error, info};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{AppError, AppResult, get_url},
|
common::{AppError, AppResult, get_url},
|
||||||
proxy::ProxySetupResponse,
|
proxy,
|
||||||
state::{GamestreamServer, StateReadAccess, StateReader},
|
state::{GamestreamServer, StateReadAccess, StateReader},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,7 +59,10 @@ fn get_server(reader: &StateReadAccess, server: &String) -> Result<Option<Gamest
|
|||||||
Ok(servers.get(server).cloned())
|
Ok(servers.get(server).cloned())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn setup_proxy(port: u16, stream: crate::backend::Stream) -> Result<ProxySetupResponse> {
|
async fn setup_proxy(
|
||||||
|
port: u16,
|
||||||
|
stream: crate::backend::Stream,
|
||||||
|
) -> Result<proxy::handler::ProxySetupResponse> {
|
||||||
let url = url_constructor::UrlConstructor::new()
|
let url = url_constructor::UrlConstructor::new()
|
||||||
.scheme("https")
|
.scheme("https")
|
||||||
.host("localhost")
|
.host("localhost")
|
||||||
@@ -79,10 +82,10 @@ async fn setup_proxy(port: u16, stream: crate::backend::Stream) -> Result<ProxyS
|
|||||||
|
|
||||||
Ok(client
|
Ok(client
|
||||||
.post(url)
|
.post(url)
|
||||||
.json(&crate::proxy::ProxySetupParams { stream })
|
.json(&proxy::handler::ProxySetupParams { stream })
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
.json::<ProxySetupResponse>()
|
.json::<proxy::handler::ProxySetupResponse>()
|
||||||
.await?)
|
.await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user