refactor(bonfire): add additional error handling

This commit is contained in:
Paul Makles
2024-06-15 10:51:00 +01:00
parent a31a3e2e65
commit fc9ceb7540

View File

@@ -24,6 +24,7 @@ use async_std::{
sync::{Mutex, RwLock}, sync::{Mutex, RwLock},
}; };
use revolt_result::create_error; use revolt_result::create_error;
use sentry::Level;
use crate::config::{ProtocolConfiguration, WebsocketHandshakeCallback}; use crate::config::{ProtocolConfiguration, WebsocketHandshakeCallback};
use crate::events::state::{State, SubscriptionStateChange}; use crate::events::state::{State, SubscriptionStateChange};
@@ -52,6 +53,7 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
let Ok(mut config) = receiver.await else { let Ok(mut config) = receiver.await else {
return; return;
}; };
info!( info!(
"User {addr:?} provided protocol configuration (version = {}, format = {:?})", "User {addr:?} provided protocol configuration (version = {}, format = {:?})",
config.get_protocol_version(), config.get_protocol_version(),
@@ -63,10 +65,8 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
// If the user has not provided authentication, request information. // If the user has not provided authentication, request information.
if config.get_session_token().is_none() { if config.get_session_token().is_none() {
while let Ok(message) = read.try_next().await { while let Ok(Some(message)) = read.try_next().await {
if let Ok(ClientMessage::Authenticate { token }) = if let Ok(ClientMessage::Authenticate { token }) = config.decode(&message) {
config.decode(message.as_ref().unwrap())
{
config.set_session_token(token); config.set_session_token(token);
break; break;
} }
@@ -75,7 +75,10 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
// Try to authenticate the user. // Try to authenticate the user.
let Some(token) = config.get_session_token().as_ref() else { let Some(token) = config.get_session_token().as_ref() else {
write.send(config.encode(&create_error!(InvalidSession))).await.ok(); write
.send(config.encode(&create_error!(InvalidSession)))
.await
.ok();
return; return;
}; };
@@ -94,20 +97,24 @@ pub async fn client(db: &'static Database, stream: TcpStream, addr: SocketAddr)
let user_id = state.cache.user_id.clone(); let user_id = state.cache.user_id.clone();
// Notify socket we have authenticated. // Notify socket we have authenticated.
if write if let Err(err) = write.send(config.encode(&EventV1::Authenticated)).await {
.send(config.encode(&EventV1::Authenticated)) error!("Failed to write: {err:?}");
.await sentry::capture_error(&err);
.is_err()
{
return; return;
} }
// Download required data to local cache and send Ready payload. // Download required data to local cache and send Ready payload.
let Ok(ready_payload) = state.generate_ready_payload(db).await else { let ready_payload = match state.generate_ready_payload(db).await {
Ok(ready_payload) => ready_payload,
Err(err) => {
sentry::capture_error(&err);
return; return;
}
}; };
if write.send(config.encode(&ready_payload)).await.is_err() { if let Err(err) = write.send(config.encode(&ready_payload)).await {
error!("Failed to write: {err:?}");
sentry::capture_error(&err);
return; return;
} }
@@ -154,23 +161,41 @@ async fn listener(
write: &Mutex<WsWriter>, write: &Mutex<WsWriter>,
) { ) {
let redis_config = RedisConfig::from_url(&REDIS_URI).unwrap(); let redis_config = RedisConfig::from_url(&REDIS_URI).unwrap();
let Ok(subscriber) = fred::types::Builder::from_config(redis_config).build_subscriber_client()
else { let subscriber = match fred::types::Builder::from_config(redis_config).build_subscriber_client()
return; {
}; Ok(subscriber) => subscriber,
if subscriber.init().await.is_err() { Err(err) => {
error!("Failed to build a subscriber: {err:?}");
sentry::capture_error(&err);
return;
}
};
if let Err(err) = subscriber.init().await {
error!("Failed to init subscriber: {err:?}");
sentry::capture_error(&err);
return; return;
}; };
let mut message_rx = subscriber.message_rx(); let mut message_rx = subscriber.message_rx();
loop { loop {
// Check for state changes for subscriptions. // Check for state changes for subscriptions.
match state.apply_state().await { match state.apply_state().await {
SubscriptionStateChange::Reset => { SubscriptionStateChange::Reset => {
subscriber.unsubscribe_all().await.unwrap(); if let Err(err) = subscriber.unsubscribe_all().await {
error!("Unsubscribe all failed: {err:?}");
sentry::capture_error(&err);
return;
}
let subscribed = state.subscribed.read().await; let subscribed = state.subscribed.read().await;
for id in subscribed.iter() { for id in subscribed.iter() {
subscriber.subscribe(id).await.unwrap(); if let Err(err) = subscriber.subscribe(id).await {
error!("Subscribe failed: {err:?}");
sentry::capture_error(&err);
return;
}
} }
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
@@ -181,25 +206,35 @@ async fn listener(
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
info!("{addr:?} unsubscribing from {id}"); info!("{addr:?} unsubscribing from {id}");
subscriber.unsubscribe(id).await.unwrap(); if let Err(err) = subscriber.unsubscribe(id).await {
error!("Unsubscribe failed: {err:?}");
sentry::capture_error(&err);
return;
}
} }
for id in add { for id in add {
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
info!("{addr:?} subscribing to {id}"); info!("{addr:?} subscribing to {id}");
subscriber.subscribe(id).await.unwrap(); if let Err(err) = subscriber.subscribe(id).await {
error!("Subscribe failed: {err:?}");
sentry::capture_error(&err);
return;
}
} }
} }
SubscriptionStateChange::None => {} SubscriptionStateChange::None => {}
} }
// Handle incoming events. // Handle incoming events.
let Ok(message) = message_rx.recv().await.map_err(|e| { let message = match message_rx.recv().await {
warn!("Error while consuming pub/sub messages: {e:?}"); Ok(message) => message,
Err(e) => {
error!("Error while consuming pub/sub messages: {e:?}");
sentry::capture_error(&e); sentry::capture_error(&e);
}) else {
return; return;
}
}; };
let event = match *REDIS_PAYLOAD_TYPE { let event = match *REDIS_PAYLOAD_TYPE {
@@ -218,7 +253,17 @@ async fn listener(
}; };
let Some(mut event) = event else { let Some(mut event) = event else {
warn!("Failed to deserialise an event for {}!", message.channel); let err = format!(
"Failed to deserialise an event for {}! Introspection: `{:?}`",
message.channel,
message
.value
.as_string()
.map(|x| x.chars().take(32).collect::<String>())
);
error!("{}", err);
sentry::capture_message(&err, Level::Error);
return; return;
}; };
@@ -250,13 +295,16 @@ async fn listener(
if let Err(e) = result { if let Err(e) = result {
use async_tungstenite::tungstenite::Error; use async_tungstenite::tungstenite::Error;
if !matches!(e, Error::AlreadyClosed | Error::ConnectionClosed) { if !matches!(e, Error::AlreadyClosed | Error::ConnectionClosed) {
warn!("Error while sending an event to {addr:?}: {e:?}"); let err = format!("Error while sending an event to {addr:?}: {e:?}");
warn!("{}", err);
sentry::capture_message(&err, Level::Warning);
} }
return; return;
} }
if let EventV1::Logout = event { if let EventV1::Logout = event {
info!("User {addr:?} received log out event!");
return; return;
} }
} }
@@ -274,12 +322,19 @@ async fn worker(
let result = read.try_next().await; let result = read.try_next().await;
let msg = match result { let msg = match result {
Ok(Some(msg)) => msg, Ok(Some(msg)) => msg,
Ok(None) => return, Ok(None) => {
warn!("Received a None message!");
sentry::capture_message("Received a None message!", Level::Warning);
return;
}
Err(e) => { Err(e) => {
use async_tungstenite::tungstenite::Error; use async_tungstenite::tungstenite::Error;
if !matches!(e, Error::AlreadyClosed | Error::ConnectionClosed) { if !matches!(e, Error::AlreadyClosed | Error::ConnectionClosed) {
warn!("Error while reading an event from {addr:?}: {e:?}"); let err = format!("Error while reading an event from {addr:?}: {e:?}");
warn!("{}", err);
sentry::capture_message(&err, Level::Warning);
} }
return; return;
} }
}; };