|
12 | 12 | // |
13 | 13 | //===----------------------------------------------------------------------===// |
14 | 14 |
|
15 | | -/// RFC 9114 § 8.1: The following error codes are defined for use when abruptly terminating streams, aborting reading of streams, or immediately closing HTTP/3 connections. |
16 | | -package enum HTTP3ErrorCode: UInt64 { |
| 15 | +/// An HTTP/3 error code. |
| 16 | +/// |
| 17 | +/// HTTP/3 uses error codes to communicate what went wrong when abruptly |
| 18 | +/// terminating streams, aborting reading of streams, or immediately closing |
| 19 | +/// connections. See RFC 9114 § 8.1. |
| 20 | +/// |
| 21 | +/// This is modeled as a struct rather than an enum so that codes registered by |
| 22 | +/// future revisions or extensions can be added without a source-breaking |
| 23 | +/// change, and so that a code received from a peer is preserved verbatim even |
| 24 | +/// when this library does not recognize it (mirroring `HTTP2ErrorCode`). |
| 25 | +public struct HTTP3ErrorCode: Hashable, Sendable { |
| 26 | + /// The underlying error code value (RFC 9114 § 8.1 / RFC 9000 § 20.2). |
| 27 | + public var rawValue: UInt64 |
| 28 | + |
| 29 | + /// Create an ``HTTP3ErrorCode`` from its raw value. |
| 30 | + public init(rawValue: UInt64) { |
| 31 | + self.rawValue = rawValue |
| 32 | + } |
| 33 | + |
17 | 34 | /// No error. This is used when the connection or stream needs to be closed, but there is no error to signal. |
18 | | - case H3_NO_ERROR = 0x0100 |
| 35 | + public static let noError = HTTP3ErrorCode(rawValue: 0x0100) |
19 | 36 | /// Peer violated protocol requirements in a way that does not match a more specific error code or endpoint declines to use the more specific error code. |
20 | | - case H3_GENERAL_PROTOCOL_ERROR = 0x0101 |
| 37 | + public static let generalProtocolError = HTTP3ErrorCode(rawValue: 0x0101) |
21 | 38 | /// An internal error has occurred in the HTTP stack. |
22 | | - case H3_INTERNAL_ERROR = 0x0102 |
| 39 | + public static let internalError = HTTP3ErrorCode(rawValue: 0x0102) |
23 | 40 | /// The endpoint detected that its peer created a stream that it will not accept. |
24 | | - case H3_STREAM_CREATION_ERROR = 0x0103 |
| 41 | + public static let streamCreationError = HTTP3ErrorCode(rawValue: 0x0103) |
25 | 42 | /// A stream required by the HTTP/3 connection was closed or reset. |
26 | | - case H3_CLOSED_CRITICAL_STREAM = 0x0104 |
| 43 | + public static let closedCriticalStream = HTTP3ErrorCode(rawValue: 0x0104) |
27 | 44 | /// A frame was received that was not permitted in the current state or on the current stream. |
28 | | - case H3_FRAME_UNEXPECTED = 0x0105 |
| 45 | + public static let frameUnexpected = HTTP3ErrorCode(rawValue: 0x0105) |
29 | 46 | /// A frame that fails to satisfy layout requirements or with an invalid size was received. |
30 | | - case H3_FRAME_ERROR = 0x0106 |
| 47 | + public static let frameError = HTTP3ErrorCode(rawValue: 0x0106) |
31 | 48 | /// The endpoint detected that its peer is exhibiting a behavior that might be generating excessive load. |
32 | | - case H3_EXCESSIVE_LOAD = 0x0107 |
| 49 | + public static let excessiveLoad = HTTP3ErrorCode(rawValue: 0x0107) |
33 | 50 | /// A stream ID or push ID was used incorrectly, such as exceeding a limit, reducing a limit, or being reused. |
34 | | - case H3_ID_ERROR = 0x0108 |
| 51 | + public static let idError = HTTP3ErrorCode(rawValue: 0x0108) |
35 | 52 | /// An endpoint detected an error in the payload of a SETTINGS frame. |
36 | | - case H3_SETTINGS_ERROR = 0x0109 |
| 53 | + public static let settingsError = HTTP3ErrorCode(rawValue: 0x0109) |
37 | 54 | /// No SETTINGS frame was received at the beginning of the control stream. |
38 | | - case H3_MISSING_SETTINGS = 0x010a |
| 55 | + public static let missingSettings = HTTP3ErrorCode(rawValue: 0x010a) |
39 | 56 | /// A server rejected a request without performing any application processing. |
40 | | - case H3_REQUEST_REJECTED = 0x010b |
| 57 | + public static let requestRejected = HTTP3ErrorCode(rawValue: 0x010b) |
41 | 58 | /// The request or its response (including pushed response) is cancelled. |
42 | | - case H3_REQUEST_CANCELLED = 0x010c |
| 59 | + public static let requestCancelled = HTTP3ErrorCode(rawValue: 0x010c) |
43 | 60 | /// The client's stream terminated without containing a fully formed request. |
44 | | - case H3_REQUEST_INCOMPLETE = 0x010d |
| 61 | + public static let requestIncomplete = HTTP3ErrorCode(rawValue: 0x010d) |
45 | 62 | /// An HTTP message was malformed and cannot be processed. |
46 | | - case H3_MESSAGE_ERROR = 0x010e |
| 63 | + public static let messageError = HTTP3ErrorCode(rawValue: 0x010e) |
47 | 64 | /// The TCP connection established in response to a CONNECT request was reset or abnormally closed. |
48 | | - case H3_CONNECT_ERROR = 0x010f |
| 65 | + public static let connectError = HTTP3ErrorCode(rawValue: 0x010f) |
49 | 66 | /// The requested operation cannot be served over HTTP/3. The peer should retry over HTTP/1.1. |
50 | | - case H3_VERSION_FALLBACK = 0x0110 |
| 67 | + public static let versionFallback = HTTP3ErrorCode(rawValue: 0x0110) |
51 | 68 |
|
52 | 69 | // MARK: QPACK (RFC 9204) |
53 | 70 |
|
54 | 71 | /// The decoder failed to interpret an encoded field section and is not able to continue decoding that field section. |
55 | | - case QPACK_DECOMPRESSION_FAILED = 0x0200 |
| 72 | + public static let qpackDecompressionFailed = HTTP3ErrorCode(rawValue: 0x0200) |
56 | 73 | /// The decoder failed to interpret an encoder instruction received on the encoder stream. |
57 | | - case QPACK_ENCODER_STREAM_ERROR = 0x0201 |
| 74 | + public static let qpackEncoderStreamError = HTTP3ErrorCode(rawValue: 0x0201) |
58 | 75 | /// The encoder failed to interpret a decoder instruction received on the decoder stream. |
59 | | - case QPACK_DECODER_STREAM_ERROR = 0x0202 |
| 76 | + public static let qpackDecoderStreamError = HTTP3ErrorCode(rawValue: 0x0202) |
| 77 | +} |
| 78 | + |
| 79 | +extension HTTP3ErrorCode: CustomStringConvertible { |
| 80 | + public var description: String { |
| 81 | + let name: String |
| 82 | + switch self { |
| 83 | + case .noError: name = "H3_NO_ERROR" |
| 84 | + case .generalProtocolError: name = "H3_GENERAL_PROTOCOL_ERROR" |
| 85 | + case .internalError: name = "H3_INTERNAL_ERROR" |
| 86 | + case .streamCreationError: name = "H3_STREAM_CREATION_ERROR" |
| 87 | + case .closedCriticalStream: name = "H3_CLOSED_CRITICAL_STREAM" |
| 88 | + case .frameUnexpected: name = "H3_FRAME_UNEXPECTED" |
| 89 | + case .frameError: name = "H3_FRAME_ERROR" |
| 90 | + case .excessiveLoad: name = "H3_EXCESSIVE_LOAD" |
| 91 | + case .idError: name = "H3_ID_ERROR" |
| 92 | + case .settingsError: name = "H3_SETTINGS_ERROR" |
| 93 | + case .missingSettings: name = "H3_MISSING_SETTINGS" |
| 94 | + case .requestRejected: name = "H3_REQUEST_REJECTED" |
| 95 | + case .requestCancelled: name = "H3_REQUEST_CANCELLED" |
| 96 | + case .requestIncomplete: name = "H3_REQUEST_INCOMPLETE" |
| 97 | + case .messageError: name = "H3_MESSAGE_ERROR" |
| 98 | + case .connectError: name = "H3_CONNECT_ERROR" |
| 99 | + case .versionFallback: name = "H3_VERSION_FALLBACK" |
| 100 | + case .qpackDecompressionFailed: name = "QPACK_DECOMPRESSION_FAILED" |
| 101 | + case .qpackEncoderStreamError: name = "QPACK_ENCODER_STREAM_ERROR" |
| 102 | + case .qpackDecoderStreamError: name = "QPACK_DECODER_STREAM_ERROR" |
| 103 | + default: name = "UNKNOWN" |
| 104 | + } |
| 105 | + return "HTTP3ErrorCode<0x\(String(self.rawValue, radix: 16)) \(name)>" |
| 106 | + } |
60 | 107 | } |
0 commit comments