diff --git a/crates/lavina-core/src/player.rs b/crates/lavina-core/src/player.rs index 60046b5..30635b0 100644 --- a/crates/lavina-core/src/player.rs +++ b/crates/lavina-core/src/player.rs @@ -616,6 +616,7 @@ pub enum ConnectionMessage { Stop(StopReason), } +#[derive(Debug)] pub enum StopReason { ServerShutdown, InternalError, diff --git a/crates/projection-xmpp/src/lib.rs b/crates/projection-xmpp/src/lib.rs index 04826bc..eec6fc3 100644 --- a/crates/projection-xmpp/src/lib.rs +++ b/crates/projection-xmpp/src/lib.rs @@ -23,7 +23,7 @@ use tokio_rustls::rustls::{Certificate, PrivateKey}; use tokio_rustls::TlsAcceptor; use lavina_core::auth::{Authenticator, Verdict}; -use lavina_core::player::{ConnectionMessage, PlayerConnection, PlayerId, PlayerRegistry}; +use lavina_core::player::{ConnectionMessage, PlayerConnection, PlayerId, PlayerRegistry, StopReason}; use lavina_core::prelude::*; use lavina_core::repo::Storage; use lavina_core::room::RoomRegistry; @@ -31,6 +31,7 @@ use lavina_core::terminator::Terminator; use lavina_core::LavinaCore; use proto_xmpp::bind::{Name, Resource}; use proto_xmpp::stream::*; +use proto_xmpp::streamerror::{StreamError, StreamErrorKind}; use proto_xmpp::xml::{Continuation, FromXml, Parser, ToXml}; use sasl::AuthBody; @@ -404,12 +405,30 @@ async fn socket_final( events.clear(); xml_writer.get_mut().flush().await?; } - Some(ConnectionMessage::Stop(_)) => { - tracing::debug!("Connection is being terminated"); + Some(ConnectionMessage::Stop(reason)) => { + tracing::debug!("Connection is being terminated: {reason:?}"); + let kind = match reason { + StopReason::ServerShutdown => StreamErrorKind::SystemShutdown, + StopReason::InternalError => StreamErrorKind::InternalServerError, + }; + StreamError { kind }.serialize(&mut events); + ServerStreamEnd.serialize(&mut events); + for i in &events { + xml_writer.write_event_async(i).await?; + } + events.clear(); + xml_writer.get_mut().flush().await?; break; } None => { - log::warn!("Player is terminated, must terminate the connection"); + log::error!("Player is terminated, must terminate the connection"); + StreamError { kind: StreamErrorKind::SystemShutdown }.serialize(&mut events); + ServerStreamEnd.serialize(&mut events); + for i in &events { + xml_writer.write_event_async(i).await?; + } + events.clear(); + xml_writer.get_mut().flush().await?; break; } } diff --git a/crates/proto-xmpp/src/lib.rs b/crates/proto-xmpp/src/lib.rs index 1e97a31..d3e25ba 100644 --- a/crates/proto-xmpp/src/lib.rs +++ b/crates/proto-xmpp/src/lib.rs @@ -10,6 +10,7 @@ pub mod sasl; pub mod session; pub mod stanzaerror; pub mod stream; +pub mod streamerror; pub mod tls; pub mod xml; diff --git a/crates/proto-xmpp/src/streamerror.rs b/crates/proto-xmpp/src/streamerror.rs new file mode 100644 index 0000000..0ba71cd --- /dev/null +++ b/crates/proto-xmpp/src/streamerror.rs @@ -0,0 +1,41 @@ +use crate::xml::ToXml; +use quick_xml::events::{BytesEnd, BytesStart, Event}; + +/// Stream error condition +/// +/// [Spec](https://xmpp.org/rfcs/rfc6120.html#streams-error-conditions). +pub enum StreamErrorKind { + /// The server has experienced a misconfiguration or other internal error that prevents it from servicing the stream. + InternalServerError, + /// The server is being shut down and all active streams are being closed. + SystemShutdown, +} +impl StreamErrorKind { + pub fn from_str(s: &str) -> Option { + match s { + "internal-server-error" => Some(Self::InternalServerError), + "system-shutdown" => Some(Self::SystemShutdown), + _ => None, + } + } + pub fn as_str(&self) -> &'static str { + match self { + Self::InternalServerError => "internal-server-error", + Self::SystemShutdown => "system-shutdown", + } + } +} + +pub struct StreamError { + pub kind: StreamErrorKind, +} +impl ToXml for StreamError { + fn serialize(&self, events: &mut Vec>) { + events.push(Event::Start(BytesStart::new("stream:error"))); + events.push(Event::Empty(BytesStart::new(format!( + r#"{} xmlns="urn:ietf:params:xml:ns:xmpp-streams""#, + self.kind.as_str() + )))); + events.push(Event::End(BytesEnd::new("stream:error"))); + } +}