Skip to main content

weida_protocol/
codes.rs

1//! QUIC application error codes used in `CONNECTION_CLOSE`, `RESET_STREAM` and
2//! `STOP_SENDING` (`docs/PROTOCOL.md` §7).
3//!
4//! These are plain `u64` values: the protocol crate stays free of transport
5//! types, and the `weida` crate wraps them in `quinn::VarInt`.
6
7use weida_core::StopReason;
8
9/// Normal closure.
10pub const NO_ERROR: u64 = 0;
11/// The local side failed internally.
12pub const INTERNAL: u64 = 1;
13/// The peer broke the wire protocol.
14pub const PROTOCOL_VIOLATION: u64 = 2;
15/// The transfer is no longer wanted.
16pub const CANCELED: u64 = 3;
17/// Version or capability negotiation failed.
18pub const NEGOTIATION_FAILED: u64 = 4;
19/// A resource limit was reached.
20pub const LIMIT_EXCEEDED: u64 = 5;
21/// Orderly shutdown of the runtime.
22pub const SHUTDOWN: u64 = 6;
23/// The receiving application declined the transfer.
24pub const REJECTED: u64 = 7;
25/// No endpoint is registered for the requested path.
26pub const UNKNOWN_ENDPOINT: u64 = 8;
27/// The endpoint exists but does not serve this stream kind.
28pub const UNSUPPORTED: u64 = 9;
29
30/// Human-readable name for a code, for logs and close reasons.
31pub const fn name(code: u64) -> &'static str {
32    match code {
33        NO_ERROR => "NO_ERROR",
34        INTERNAL => "INTERNAL",
35        PROTOCOL_VIOLATION => "PROTOCOL_VIOLATION",
36        CANCELED => "CANCELED",
37        NEGOTIATION_FAILED => "NEGOTIATION_FAILED",
38        LIMIT_EXCEEDED => "LIMIT_EXCEEDED",
39        SHUTDOWN => "SHUTDOWN",
40        REJECTED => "REJECTED",
41        UNKNOWN_ENDPOINT => "UNKNOWN_ENDPOINT",
42        UNSUPPORTED => "UNSUPPORTED",
43        _ => "UNKNOWN",
44    }
45}
46
47/// Interprets a `STOP_SENDING` code as a sender-side outcome reason.
48pub const fn stop_reason(code: u64) -> StopReason {
49    match code {
50        REJECTED => StopReason::Rejected,
51        CANCELED => StopReason::Canceled,
52        UNKNOWN_ENDPOINT => StopReason::UnknownEndpoint,
53        UNSUPPORTED => StopReason::Unsupported,
54        LIMIT_EXCEEDED => StopReason::LimitExceeded,
55        SHUTDOWN => StopReason::ShuttingDown,
56        other => StopReason::Other(other),
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn codes_match_the_protocol_document() {
66        assert_eq!(
67            [
68                NO_ERROR,
69                INTERNAL,
70                PROTOCOL_VIOLATION,
71                CANCELED,
72                NEGOTIATION_FAILED,
73                LIMIT_EXCEEDED,
74                SHUTDOWN,
75                REJECTED,
76                UNKNOWN_ENDPOINT,
77                UNSUPPORTED
78            ],
79            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
80        );
81    }
82
83    #[test]
84    fn names_are_defined_for_known_codes() {
85        assert_eq!(name(PROTOCOL_VIOLATION), "PROTOCOL_VIOLATION");
86        assert_eq!(name(NEGOTIATION_FAILED), "NEGOTIATION_FAILED");
87        assert_eq!(name(UNSUPPORTED), "UNSUPPORTED");
88        assert_eq!(name(10), "UNKNOWN");
89    }
90
91    #[test]
92    fn stop_reasons_map_the_refusal_codes() {
93        assert_eq!(stop_reason(REJECTED), StopReason::Rejected);
94        assert_eq!(stop_reason(CANCELED), StopReason::Canceled);
95        assert_eq!(stop_reason(UNKNOWN_ENDPOINT), StopReason::UnknownEndpoint);
96        assert_eq!(stop_reason(UNSUPPORTED), StopReason::Unsupported);
97        // A paired endpoint that already has its peer refuses a newcomer's
98        // stream with this code, so it has to read as a capacity refusal
99        // rather than an unknown number (`docs/PATTERNS.md`, PAIR).
100        assert_eq!(stop_reason(LIMIT_EXCEEDED), StopReason::LimitExceeded);
101        // A draining peer stops a stream that arrived too late with this
102        // code, so a sender must be able to read it as a refusal rather than
103        // as an unknown number (`docs/decisions/0009-drain.md` §4.5).
104        assert_eq!(stop_reason(SHUTDOWN), StopReason::ShuttingDown);
105        assert_eq!(stop_reason(42), StopReason::Other(42));
106    }
107}