use super::{SliceStep, WebSocketConnectionProcess};
use crate::ServerError;
use crate::server::participant::ConnectionFateClass;
use beamr::native::native_process::NativeOutcome;
use beamr::process::ExitReason;
use tungstenite::protocol::frame::CloseFrame;
use tungstenite::protocol::frame::coding::CloseCode;
impl WebSocketConnectionProcess {
pub(super) fn stop_crashed(&mut self, pid: u64) -> NativeOutcome {
self.close_transport_abnormal();
self.release_conversations();
self.runtime
.mark_crashed(pid, ExitReason::Error, self.peer_addr);
NativeOutcome::Stop(ExitReason::Error)
}
pub(super) fn fail_slice(&mut self, pid: u64, error: &ServerError) -> NativeOutcome {
if matches!(error, ServerError::ReadinessClosedByStop { .. }) {
tracing::debug!(
connection_pid = pid,
"connection closed by the server's stop before its readiness registered"
);
} else {
tracing::error!(connection_pid = pid, %error, "connection readiness contract failed");
}
self.stop_crashed(pid)
}
pub(super) fn complete_connection_fate(
&self,
class: ConnectionFateClass,
) -> Result<(), ServerError> {
let conversations = self.state.participant_conversations.tracked_conversations();
self.runtime.complete_connection_fate(
self.state.connection_incarnation,
class,
&conversations,
)
}
pub(super) fn fail_fate(&mut self, pid: u64, error: &ServerError) -> SliceStep {
tracing::error!(connection_pid = pid, %error, "websocket connection fate fold failed");
self.close_transport_abnormal();
self.release_conversations();
self.runtime
.mark_crashed(pid, ExitReason::Error, self.peer_addr);
SliceStep::Stop(ExitReason::Error)
}
pub(super) fn finish_fate_close(&mut self, pid: u64, class: ConnectionFateClass) -> SliceStep {
if let Err(error) = self.drain_outbound(None) {
tracing::debug!(connection_pid = pid, %error, "the close path's best-effort drain did not finish; the peer's half-close ends it");
}
if let Err(error) = self.complete_connection_fate(class) {
return self.fail_fate(pid, &error);
}
self.finish_normal_close(pid)
}
pub(super) fn finish_normal_close(&mut self, pid: u64) -> SliceStep {
if let Err(error) = self.drain_outbound(None) {
tracing::debug!(connection_pid = pid, %error, "the close path's best-effort drain did not finish; the peer's half-close ends it");
}
if let Some(socket) = self.socket.as_mut() {
if let Err(error) = socket.close(Some(CloseFrame {
code: CloseCode::Normal,
reason: "".into(),
})) {
tracing::debug!(connection_pid = pid, %error, "websocket close frame not sent");
}
if let Err(error) = socket.flush() {
tracing::debug!(connection_pid = pid, %error, "websocket close flush incomplete");
}
}
self.release_conversations();
self.runtime.finish(pid);
SliceStep::Stop(ExitReason::Normal)
}
pub(super) fn close_transport_abnormal(&mut self) {
if let Some(socket) = self.socket.as_mut() {
if let Err(error) = socket.close(Some(CloseFrame {
code: CloseCode::Protocol,
reason: "liminal websocket transport contract violation".into(),
})) {
tracing::debug!(%error, "the websocket close frame was not sent; the dropped socket ends it");
}
if let Err(error) = socket.flush() {
tracing::debug!(%error, "the websocket close flush did not finish; the dropped socket ends it");
}
}
}
pub(super) fn release_conversations(&mut self) {
self.release_participant_publication();
self.state.pending_replies.cancel_all();
for (_conversation_id, conversation) in std::mem::take(&mut self.state.conversations) {
conversation.finalize();
}
}
}