[feat] client robust against server disconnects and better logging on the client
This commit is contained in:
parent
89af1f9176
commit
9f8c6d3417
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -2389,6 +2389,7 @@ dependencies = [
|
|||||||
"lanspread-db",
|
"lanspread-db",
|
||||||
"lanspread-proto",
|
"lanspread-proto",
|
||||||
"lanspread-utils",
|
"lanspread-utils",
|
||||||
|
"log",
|
||||||
"s2n-quic",
|
"s2n-quic",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -19,6 +19,7 @@ lanspread-utils = { path = "../lanspread-utils" }
|
|||||||
# external
|
# external
|
||||||
clap = { workspace = true }
|
clap = { workspace = true }
|
||||||
eyre = { workspace = true }
|
eyre = { workspace = true }
|
||||||
|
log = "0.4"
|
||||||
s2n-quic = { workspace = true }
|
s2n-quic = { workspace = true }
|
||||||
serde_json = { workspace = true }
|
serde_json = { workspace = true }
|
||||||
tokio = { workspace = true }
|
tokio = { workspace = true }
|
||||||
|
@ -4,11 +4,7 @@ use lanspread_db::db::Game;
|
|||||||
use lanspread_proto::{Message as _, Request, Response};
|
use lanspread_proto::{Message as _, Request, Response};
|
||||||
use lanspread_utils::maybe_addr;
|
use lanspread_utils::maybe_addr;
|
||||||
use s2n_quic::{client::Connect, provider::limits::Limits, Client as QuicClient};
|
use s2n_quic::{client::Connect, provider::limits::Limits, Client as QuicClient};
|
||||||
use tokio::{
|
use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
|
||||||
io::AsyncWriteExt as _,
|
|
||||||
sync::mpsc::{UnboundedReceiver, UnboundedSender},
|
|
||||||
};
|
|
||||||
// use tracing_subscriber::EnvFilter;
|
|
||||||
|
|
||||||
static CERT_PEM: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../cert.pem"));
|
static CERT_PEM: &str = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/../../cert.pem"));
|
||||||
|
|
||||||
@ -29,103 +25,118 @@ pub async fn run(
|
|||||||
mut rx_control: UnboundedReceiver<ClientCommand>,
|
mut rx_control: UnboundedReceiver<ClientCommand>,
|
||||||
tx_event: UnboundedSender<ClientEvent>,
|
tx_event: UnboundedSender<ClientEvent>,
|
||||||
) -> eyre::Result<()> {
|
) -> eyre::Result<()> {
|
||||||
// tracing_subscriber::fmt()
|
|
||||||
// .with_env_filter(EnvFilter::from_default_env())
|
|
||||||
// .init();
|
|
||||||
|
|
||||||
// blocking wait for remote address
|
// blocking wait for remote address
|
||||||
tracing::debug!("waiting for server address");
|
log::debug!("waiting for server address");
|
||||||
let server_addr = loop {
|
let server_addr = loop {
|
||||||
if let Some(ClientCommand::ServerAddr(addr)) = rx_control.recv().await {
|
if let Some(ClientCommand::ServerAddr(addr)) = rx_control.recv().await {
|
||||||
tracing::info!("got server address: {addr}");
|
log::info!("got server address: {addr}");
|
||||||
break addr;
|
break addr;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let limits = Limits::default().with_max_handshake_duration(Duration::from_secs(3))?;
|
loop {
|
||||||
|
let limits = Limits::default()
|
||||||
|
.with_max_handshake_duration(Duration::from_secs(3))?
|
||||||
|
.with_max_idle_timeout(Duration::from_secs(1))?;
|
||||||
|
|
||||||
let client = QuicClient::builder()
|
let client = QuicClient::builder()
|
||||||
.with_tls(CERT_PEM)?
|
.with_tls(CERT_PEM)?
|
||||||
.with_io("0.0.0.0:0")?
|
.with_io("0.0.0.0:0")?
|
||||||
.with_limits(limits)?
|
.with_limits(limits)?
|
||||||
.start()?;
|
.start()?;
|
||||||
|
|
||||||
let conn = Connect::new(server_addr).with_server_name("localhost");
|
let conn = Connect::new(server_addr).with_server_name("localhost");
|
||||||
let mut conn = client.connect(conn).await?;
|
let mut conn = match client.connect(conn).await {
|
||||||
conn.keep_alive(true)?;
|
Ok(conn) => conn,
|
||||||
|
Err(e) => {
|
||||||
tracing::info!(
|
log::error!("failed to connect to server: {e}");
|
||||||
"connected: (server: {}) (client: {})",
|
tokio::time::sleep(Duration::from_secs(3)).await;
|
||||||
maybe_addr!(conn.remote_addr()),
|
continue;
|
||||||
maybe_addr!(conn.local_addr())
|
}
|
||||||
);
|
|
||||||
|
|
||||||
// tx
|
|
||||||
while let Some(cmd) = rx_control.recv().await {
|
|
||||||
let request = match cmd {
|
|
||||||
ClientCommand::ListGames => Request::ListGames,
|
|
||||||
ClientCommand::GetGame(id) => Request::GetGame { id },
|
|
||||||
ClientCommand::ServerAddr(_) => Request::Invalid(
|
|
||||||
[].into(),
|
|
||||||
"invalid control message (ServerAddr), should not happen".into(),
|
|
||||||
),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let data = request.encode();
|
conn.keep_alive(true)?;
|
||||||
tracing::trace!("encoded data: {}", String::from_utf8_lossy(&data));
|
|
||||||
|
|
||||||
let stream = conn.open_bidirectional_stream().await?;
|
log::info!(
|
||||||
let (mut rx, mut tx) = stream.split();
|
"connected: (server: {}) (client: {})",
|
||||||
|
maybe_addr!(conn.remote_addr()),
|
||||||
|
maybe_addr!(conn.local_addr())
|
||||||
|
);
|
||||||
|
|
||||||
if let Err(e) = tx.write_all(&data).await {
|
// tx
|
||||||
tracing::error!(?e, "failed to send request to server");
|
while let Some(cmd) = rx_control.recv().await {
|
||||||
}
|
let request = match cmd {
|
||||||
|
ClientCommand::ListGames => Request::ListGames,
|
||||||
if let Ok(Some(data)) = rx.receive().await {
|
ClientCommand::GetGame(id) => Request::GetGame { id },
|
||||||
tracing::trace!("server response (raw): {}", String::from_utf8_lossy(&data));
|
ClientCommand::ServerAddr(_) => Request::Invalid(
|
||||||
|
[].into(),
|
||||||
let response = Response::decode(&data);
|
"invalid control message (ServerAddr), should not happen".into(),
|
||||||
tracing::trace!(
|
|
||||||
"server response (decoded): {}",
|
|
||||||
String::from_utf8_lossy(&data)
|
|
||||||
);
|
|
||||||
match response {
|
|
||||||
Response::Games(games) => {
|
|
||||||
for game in &games {
|
|
||||||
tracing::debug!(?game);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Err(e) = tx_event.send(ClientEvent::ListGames(games)) {
|
|
||||||
tracing::error!(?e, "failed to send ClientEvent::ListGames to client");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Response::Game(game) => tracing::debug!(?game, "game received"),
|
|
||||||
Response::GameNotFound(id) => tracing::debug!(?id, "game not found"),
|
|
||||||
Response::InvalidRequest(request_bytes, err) => tracing::error!(
|
|
||||||
"server says our request was invalid (error: {}): {}",
|
|
||||||
err,
|
|
||||||
String::from_utf8_lossy(&request_bytes)
|
|
||||||
),
|
),
|
||||||
Response::EncodingError(err) => {
|
};
|
||||||
tracing::error!("server encoding error: {err}");
|
|
||||||
}
|
let data = request.encode();
|
||||||
Response::DecodingError(data, err) => {
|
log::error!("encoded data: {}", String::from_utf8_lossy(&data));
|
||||||
tracing::error!(
|
|
||||||
"response decoding error: {} (data: {})",
|
let stream = match conn.open_bidirectional_stream().await {
|
||||||
err,
|
Ok(stream) => stream,
|
||||||
String::from_utf8_lossy(&data)
|
Err(e) => {
|
||||||
);
|
log::error!("failed to open stream: {e}");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let (mut rx, mut tx) = stream.split();
|
||||||
|
|
||||||
|
if let Err(e) = tx.send(data).await {
|
||||||
|
log::error!("failed to send request to server {:?}", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Err(err) = tx.close().await {
|
if let Ok(Some(data)) = rx.receive().await {
|
||||||
tracing::error!("failed to close stream: {err}");
|
log::trace!("server response (raw): {}", String::from_utf8_lossy(&data));
|
||||||
|
|
||||||
|
let response = Response::decode(&data);
|
||||||
|
log::trace!(
|
||||||
|
"server response (decoded): {}",
|
||||||
|
String::from_utf8_lossy(&data)
|
||||||
|
);
|
||||||
|
match response {
|
||||||
|
Response::Games(games) => {
|
||||||
|
for game in &games {
|
||||||
|
log::debug!("{game:?}");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(e) = tx_event.send(ClientEvent::ListGames(games)) {
|
||||||
|
log::error!("failed to send ClientEvent::ListGames to client {e:?}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Response::Game(game) => log::debug!("game received: {game:?}"),
|
||||||
|
Response::GameNotFound(id) => log::debug!("game not found {id}"),
|
||||||
|
Response::InvalidRequest(request_bytes, err) => log::error!(
|
||||||
|
"server says our request was invalid (error: {}): {}",
|
||||||
|
err,
|
||||||
|
String::from_utf8_lossy(&request_bytes)
|
||||||
|
),
|
||||||
|
Response::EncodingError(err) => {
|
||||||
|
log::error!("server encoding error: {err}");
|
||||||
|
}
|
||||||
|
Response::DecodingError(data, err) => {
|
||||||
|
log::error!(
|
||||||
|
"response decoding error: {} (data: {})",
|
||||||
|
err,
|
||||||
|
String::from_utf8_lossy(&data)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(err) = tx.close().await {
|
||||||
|
log::error!("failed to close stream: {err}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::info!("server closed connection");
|
// log::info!("server closed connection");
|
||||||
Ok(())
|
// Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
// #[derive(Debug, Parser)]
|
// #[derive(Debug, Parser)]
|
||||||
|
@ -66,14 +66,12 @@ impl Server {
|
|||||||
tracing::info!("{} connected", conn_ctx.remote_addr);
|
tracing::info!("{} connected", conn_ctx.remote_addr);
|
||||||
|
|
||||||
while let Ok(Some(stream)) = connection.accept_bidirectional_stream().await {
|
while let Ok(Some(stream)) = connection.accept_bidirectional_stream().await {
|
||||||
tracing::debug!("{} stream opened: {:?}", conn_ctx.remote_addr, stream);
|
|
||||||
|
|
||||||
let (mut rx, mut tx) = stream.split();
|
let (mut rx, mut tx) = stream.split();
|
||||||
|
|
||||||
let conn_ctx = conn_ctx.clone();
|
let conn_ctx = conn_ctx.clone();
|
||||||
// spawn a new task for the stream
|
// spawn a new task for the stream
|
||||||
tokio::spawn(async move {
|
tokio::spawn(async move {
|
||||||
tracing::debug!("{} stream opened", conn_ctx.remote_addr);
|
tracing::trace!("{} stream opened", conn_ctx.remote_addr);
|
||||||
|
|
||||||
// handle streams
|
// handle streams
|
||||||
while let Ok(Some(data)) = rx.receive().await {
|
while let Ok(Some(data)) = rx.receive().await {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user