forked from jmug/stoatchat
refactor(bonfire): add additional error handling
This commit is contained in:
@@ -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 {
|
||||||
return;
|
Ok(ready_payload) => ready_payload,
|
||||||
|
Err(err) => {
|
||||||
|
sentry::capture_error(&err);
|
||||||
|
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,
|
||||||
sentry::capture_error(&e);
|
Err(e) => {
|
||||||
}) else {
|
error!("Error while consuming pub/sub messages: {e:?}");
|
||||||
return;
|
sentry::capture_error(&e);
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user