use crate::ServerError;
use crate::server::connection::outbound::OutboundWriter;
use crate::server::connection::services::server_error_from_protocol;
use crate::server::connection::state::{ConnectionProcessState, FrameAction, ProcessStatus};
use crate::server::connection::supervisor::relay_pump::apply_frame;
use crate::server::connection::supervisor::runtime::ConnectionRuntime;
use crate::server::participant::ConnectionFateClass;
use liminal::protocol::{ProtocolError, decode};
pub(super) fn process_buffer(
pid: u64,
runtime: &ConnectionRuntime,
state: &mut ConnectionProcessState,
buffer: &mut Vec<u8>,
outbound: &mut OutboundWriter,
) -> Result<ProcessStatus, ServerError> {
loop {
if runtime.force_close_pending(pid) {
return Ok(ProcessStatus::CloseWithFate(
ConnectionFateClass::ServerShutdown,
));
}
if let Some(rejection) = crate::server::participant::preflight_generic_bytes(
buffer,
state.authenticated,
state.participant_session,
) {
let response = crate::server::participant::encode_server_value(
liminal_protocol::wire::ServerValue::ParticipantTransportRejected(rejection),
)
.map_err(|error| ServerError::ListenerAccept {
message: format!("failed to encode participant frame-limit rejection: {error:?}"),
})?;
outbound
.enqueue_frame(&response)
.map_err(|error| ServerError::ListenerAccept {
message: format!(
"failed to enqueue participant frame-limit rejection: {error}"
),
})?;
buffer.clear();
return Ok(ProcessStatus::Close);
}
if let Err(refusal) =
super::super::pre_admission::check_declared_frame(buffer, state.authenticated)
{
tracing::warn!(connection_pid = pid, %refusal, "unadmitted inbound frame refused");
buffer.clear();
if let Err(enqueue_error) =
outbound.enqueue_frame(&super::super::pre_admission::connect_error_frame(refusal))
{
tracing::warn!(
connection_pid = pid,
error = %enqueue_error,
"pre-admission refusal frame could not be enqueued; closing anyway"
);
}
return Ok(ProcessStatus::Close);
}
let (frame, consumed) = match decode(buffer) {
Ok(decoded) => decoded,
Err(
ProtocolError::IncompleteHeader { .. } | ProtocolError::TruncatedPayload { .. },
) => {
return Ok(ProcessStatus::Continue);
}
Err(error) => {
let conversations = state.participant_conversations.tracked_conversations();
if runtime.connection_has_bound_participant(
state.connection_incarnation,
&conversations,
)? {
tracing::warn!(connection_pid = pid, %error, "bound connection protocol refusal");
return Ok(ProcessStatus::CloseWithFate(
ConnectionFateClass::ProtocolError,
));
}
return Err(server_error_from_protocol(&error));
}
};
buffer.drain(..consumed);
match apply_frame(pid, runtime, state, frame) {
FrameAction::Respond(response) => {
outbound
.enqueue_frame(&response)
.map_err(|error| ServerError::ListenerAccept {
message: format!("failed to enqueue connection response: {error}"),
})?;
}
FrameAction::NoResponse => {}
FrameAction::RespondThenClose(response) => {
if let Err(error) = outbound.enqueue_frame(&response) {
tracing::warn!(
connection_pid = pid,
%error,
"auth-rejection frame could not be enqueued; closing anyway"
);
}
return Ok(ProcessStatus::Close);
}
FrameAction::Close => return Ok(ProcessStatus::Close),
FrameAction::CloseWithFate(class) => {
return Ok(ProcessStatus::CloseWithFate(class));
}
}
}
}