liminal/protocol/
error.rs1use super::{FrameType, lifecycle::ConnectionState};
2
3#[derive(Clone, Debug, PartialEq, Eq, thiserror::Error)]
5pub enum ProtocolError {
6 #[error("incomplete frame header")]
8 IncompleteHeader { message: Option<String> },
9
10 #[error("truncated frame payload")]
12 TruncatedPayload { message: Option<String> },
13
14 #[error("unknown frame type {type_id}")]
16 UnknownFrameType {
17 type_id: u8,
18 message: Option<String>,
19 },
20
21 #[error("invalid stream {stream_id} for {frame_type:?}")]
23 InvalidStream {
24 frame_type: FrameType,
25 stream_id: u32,
26 message: Option<String>,
27 },
28
29 #[error("invalid protocol state transition from {current_state:?} with {frame_type:?}")]
31 InvalidStateTransition {
32 current_state: ConnectionState,
33 frame_type: FrameType,
34 message: Option<String>,
35 },
36
37 #[error("authentication failure")]
39 AuthenticationFailure { message: Option<String> },
40
41 #[error("protocol version mismatch")]
43 VersionMismatch { message: Option<String> },
44
45 #[error("schema incompatible")]
47 SchemaIncompatible { message: Option<String> },
48
49 #[error("protocol codec error")]
51 CodecError { message: Option<String> },
52}
53
54impl ProtocolError {
55 pub const INCOMPLETE_HEADER_CODE: u16 = 0x0001;
57 pub const TRUNCATED_PAYLOAD_CODE: u16 = 0x0002;
59 pub const UNKNOWN_FRAME_TYPE_CODE: u16 = 0x0003;
61 pub const INVALID_STREAM_CODE: u16 = 0x0004;
63 pub const INVALID_STATE_TRANSITION_CODE: u16 = 0x0005;
65 pub const AUTHENTICATION_FAILURE_CODE: u16 = 0x0006;
67 pub const VERSION_MISMATCH_CODE: u16 = 0x0007;
69 pub const SCHEMA_INCOMPATIBLE_CODE: u16 = 0x0008;
71 pub const CODEC_ERROR_CODE: u16 = 0x0009;
73
74 #[must_use]
76 pub const fn reason_code(&self) -> u16 {
77 match self {
78 Self::IncompleteHeader { .. } => Self::INCOMPLETE_HEADER_CODE,
79 Self::TruncatedPayload { .. } => Self::TRUNCATED_PAYLOAD_CODE,
80 Self::UnknownFrameType { .. } => Self::UNKNOWN_FRAME_TYPE_CODE,
81 Self::InvalidStream { .. } => Self::INVALID_STREAM_CODE,
82 Self::InvalidStateTransition { .. } => Self::INVALID_STATE_TRANSITION_CODE,
83 Self::AuthenticationFailure { .. } => Self::AUTHENTICATION_FAILURE_CODE,
84 Self::VersionMismatch { .. } => Self::VERSION_MISMATCH_CODE,
85 Self::SchemaIncompatible { .. } => Self::SCHEMA_INCOMPATIBLE_CODE,
86 Self::CodecError { .. } => Self::CODEC_ERROR_CODE,
87 }
88 }
89
90 #[must_use]
92 pub fn message(&self) -> Option<&str> {
93 match self {
94 Self::IncompleteHeader { message }
95 | Self::TruncatedPayload { message }
96 | Self::UnknownFrameType { message, .. }
97 | Self::InvalidStream { message, .. }
98 | Self::InvalidStateTransition { message, .. }
99 | Self::AuthenticationFailure { message }
100 | Self::VersionMismatch { message }
101 | Self::SchemaIncompatible { message }
102 | Self::CodecError { message } => message.as_deref(),
103 }
104 }
105
106 #[must_use]
107 pub(crate) fn invalid_stream(frame_type: FrameType, stream_id: u32) -> Self {
108 Self::InvalidStream {
109 frame_type,
110 stream_id,
111 message: Some("stream id violates frame type invariants".to_owned()),
112 }
113 }
114
115 #[must_use]
116 pub(crate) fn invalid_state_transition(
117 current_state: ConnectionState,
118 frame_type: FrameType,
119 ) -> Self {
120 Self::InvalidStateTransition {
121 current_state,
122 frame_type,
123 message: Some("frame is invalid for the current connection state".to_owned()),
124 }
125 }
126
127 #[must_use]
128 pub(crate) fn codec(message: impl Into<String>) -> Self {
129 Self::CodecError {
130 message: Some(message.into()),
131 }
132 }
133}