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/// The binding closes because its server is being replaced; the address is served again at once.
30pub const RESTARTING: u64 = 10;
31
32/// Human-readable name for a code, for logs and close reasons.
33pub const fn name(code: u64) -> &'static str {
34    match code {
35        NO_ERROR => "NO_ERROR",
36        INTERNAL => "INTERNAL",
37        PROTOCOL_VIOLATION => "PROTOCOL_VIOLATION",
38        CANCELED => "CANCELED",
39        NEGOTIATION_FAILED => "NEGOTIATION_FAILED",
40        LIMIT_EXCEEDED => "LIMIT_EXCEEDED",
41        SHUTDOWN => "SHUTDOWN",
42        REJECTED => "REJECTED",
43        UNKNOWN_ENDPOINT => "UNKNOWN_ENDPOINT",
44        UNSUPPORTED => "UNSUPPORTED",
45        RESTARTING => "RESTARTING",
46        _ => "UNKNOWN",
47    }
48}
49
50/// Interprets a `STOP_SENDING` code as a sender-side outcome reason.
51pub const fn stop_reason(code: u64) -> StopReason {
52    match code {
53        REJECTED => StopReason::Rejected,
54        CANCELED => StopReason::Canceled,
55        UNKNOWN_ENDPOINT => StopReason::UnknownEndpoint,
56        UNSUPPORTED => StopReason::Unsupported,
57        LIMIT_EXCEEDED => StopReason::LimitExceeded,
58        SHUTDOWN => StopReason::ShuttingDown,
59        other => StopReason::Other(other),
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn codes_match_the_protocol_document() {
69        assert_eq!(
70            [
71                NO_ERROR,
72                INTERNAL,
73                PROTOCOL_VIOLATION,
74                CANCELED,
75                NEGOTIATION_FAILED,
76                LIMIT_EXCEEDED,
77                SHUTDOWN,
78                REJECTED,
79                UNKNOWN_ENDPOINT,
80                UNSUPPORTED,
81                RESTARTING
82            ],
83            [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
84        );
85    }
86
87    #[test]
88    fn names_are_defined_for_known_codes() {
89        assert_eq!(name(PROTOCOL_VIOLATION), "PROTOCOL_VIOLATION");
90        assert_eq!(name(NEGOTIATION_FAILED), "NEGOTIATION_FAILED");
91        assert_eq!(name(UNSUPPORTED), "UNSUPPORTED");
92        assert_eq!(name(RESTARTING), "RESTARTING");
93        assert_eq!(name(11), "UNKNOWN");
94    }
95
96    #[test]
97    fn stop_reasons_map_the_refusal_codes() {
98        assert_eq!(stop_reason(REJECTED), StopReason::Rejected);
99        assert_eq!(stop_reason(CANCELED), StopReason::Canceled);
100        assert_eq!(stop_reason(UNKNOWN_ENDPOINT), StopReason::UnknownEndpoint);
101        assert_eq!(stop_reason(UNSUPPORTED), StopReason::Unsupported);
102        // A paired endpoint that already has its peer refuses a newcomer's
103        // stream with this code, so it has to read as a capacity refusal
104        // rather than an unknown number (`docs/PATTERNS.md`, PAIR).
105        assert_eq!(stop_reason(LIMIT_EXCEEDED), StopReason::LimitExceeded);
106        // A draining peer stops a stream that arrived too late with this
107        // code, so a sender must be able to read it as a refusal rather than
108        // as an unknown number (`docs/decisions/0009-drain.md` §4.5).
109        assert_eq!(stop_reason(SHUTDOWN), StopReason::ShuttingDown);
110        assert_eq!(stop_reason(42), StopReason::Other(42));
111    }
112}