h3i/prompts/h3/
errors.rs

1// Copyright (C) 2024, Cloudflare, Inc.
2// All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8//     * Redistributions of source code must retain the above copyright notice,
9//       this list of conditions and the following disclaimer.
10//
11//     * Redistributions in binary form must reproduce the above copyright
12//       notice, this list of conditions and the following disclaimer in the
13//       documentation and/or other materials provided with the distribution.
14//
15// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
16// IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
17// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
19// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
23// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
24// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
27//! QUIC and HTTP/3 errors for the h3i client.
28use inquire::error::InquireResult;
29use inquire::validator::Validation;
30use inquire::CustomUserError;
31use inquire::Select;
32use inquire::Text;
33use qlog::events::quic::ErrorSpace;
34
35use crate::prompts::h3;
36
37use super::SuggestionResult;
38
39pub const NO_ERROR: &str = "NO_ERROR";
40pub const INTERNAL_ERROR: &str = "INTERNAL_ERROR";
41pub const CONNECTION_REFUSED: &str = "CONNECTION_REFUSED";
42pub const FLOW_CONTROL_ERROR: &str = "FLOW_CONTROL_ERROR";
43pub const STREAM_LIMIT_ERROR: &str = "STREAM_LIMIT_ERROR";
44pub const STREAM_STATE_ERROR: &str = "STREAM_STATE_ERROR";
45pub const FINAL_SIZE_ERROR: &str = "FINAL_SIZE_ERROR";
46pub const FRAME_ENCODING_ERROR: &str = "FRAME_ENCODING_ERROR";
47pub const TRANSPORT_PARAMETER_ERROR: &str = "TRANSPORT_PARAMETER_ERROR";
48pub const CONNECTION_ID_LIMIT_ERROR: &str = "CONNECTION_ID_LIMIT_ERROR";
49pub const PROTOCOL_VIOLATION: &str = "PROTOCOL_VIOLATION";
50pub const INVALID_TOKEN: &str = "INVALID_TOKEN";
51pub const APPLICATION_ERROR: &str = "APPLICATION_ERROR";
52pub const CRYPTO_BUFFER_EXCEEDED: &str = "CRYPTO_BUFFER_EXCEEDED";
53pub const KEY_UPDATE_ERROR: &str = "KEY_UPDATE_ERROR";
54pub const AEAD_LIMIT_REACHED: &str = "AEAD_LIMIT_REACHED";
55pub const NO_VIABLE_PATH: &str = "NO_VIABLE_PATH";
56pub const VERSION_NEGOTIATION_ERROR: &str = "VERSION_NEGOTIATION_ERROR";
57
58pub const H3_DATAGRAM_ERROR: &str = "H3_DATAGRAM_ERROR";
59pub const H3_NO_ERROR: &str = "H3_NO_ERROR";
60pub const H3_GENERAL_PROTOCOL_ERROR: &str = "H3_GENERAL_PROTOCOL_ERROR";
61pub const H3_INTERNAL_ERROR: &str = "H3_INTERNAL_ERROR";
62pub const H3_STREAM_CREATION_ERROR: &str = "H3_STREAM_CREATION_ERROR";
63pub const H3_CLOSED_CRITICAL_STREAM: &str = "H3_CLOSED_CRITICAL_STREAM";
64pub const H3_FRAME_UNEXPECTED: &str = "H3_FRAME_UNEXPECTED";
65pub const H3_FRAME_ERROR: &str = "H3_FRAME_ERROR";
66pub const H3_EXCESSIVE_LOAD: &str = "H3_EXCESSIVE_LOAD";
67pub const H3_ID_ERROR: &str = "H3_ID_ERROR";
68pub const H3_SETTINGS_ERROR: &str = "H3_SETTINGS_ERROR";
69pub const H3_MISSING_SETTINGS: &str = "H3_MISSING_SETTINGS";
70pub const H3_REQUEST_REJECTED: &str = "H3_REQUEST_REJECTED";
71pub const H3_REQUEST_CANCELLED: &str = "H3_REQUEST_CANCELLED";
72pub const H3_REQUEST_INCOMPLETE: &str = "H3_REQUEST_INCOMPLETE";
73pub const H3_MESSAGE_ERROR: &str = "H3_MESSAGE_ERROR";
74pub const H3_CONNECT_ERROR: &str = "H3_CONNECT_ERROR";
75pub const H3_VERSION_FALLBACK: &str = "H3_VERSION_FALLBACK";
76pub const QPACK_DECOMPRESSION_FAILED: &str = "QPACK_DECOMPRESSION_FAILED";
77pub const QPACK_ENCODER_STREAM_ERROR: &str = "QPACK_ENCODER_STREAM_ERROR";
78pub const QPACK_DECODER_STREAM_ERROR: &str = "QPACK_DECODER_STREAM_ERROR";
79
80pub const TRANSPORT: &str = "transport";
81pub const APPLICATION: &str = "application";
82
83// TODO: do we want to rely on the qlog enum here?
84pub fn prompt_transport_or_app_error() -> InquireResult<(ErrorSpace, u64)> {
85    let trans_or_app = prompt_transport_or_app()?;
86    let space = if trans_or_app == TRANSPORT {
87        ErrorSpace::TransportError
88    } else {
89        ErrorSpace::ApplicationError
90    };
91
92    let error_code = if matches!(space, ErrorSpace::TransportError) {
93        let error_code = Text::new("error code:")
94            .with_validator(validate_transport_error_code)
95            .with_autocomplete(&transport_error_code_suggestor)
96            .with_page_size(18)
97            .prompt()?;
98
99        match error_code.as_str() {
100            NO_ERROR => 0x0,
101            INTERNAL_ERROR => 0x1,
102            CONNECTION_REFUSED => 0x2,
103            FLOW_CONTROL_ERROR => 0x3,
104            STREAM_LIMIT_ERROR => 0x4,
105            STREAM_STATE_ERROR => 0x5,
106            FINAL_SIZE_ERROR => 0x6,
107            FRAME_ENCODING_ERROR => 0x7,
108            TRANSPORT_PARAMETER_ERROR => 0x8,
109            CONNECTION_ID_LIMIT_ERROR => 0x9,
110            PROTOCOL_VIOLATION => 0x0a,
111            INVALID_TOKEN => 0x0b,
112            APPLICATION_ERROR => 0x0c,
113            CRYPTO_BUFFER_EXCEEDED => 0x0d,
114            KEY_UPDATE_ERROR => 0x0e,
115            AEAD_LIMIT_REACHED => 0x0f,
116            NO_VIABLE_PATH => 0x10,
117            VERSION_NEGOTIATION_ERROR => 0x11,
118
119            v => v.parse::<u64>().unwrap(),
120        }
121    } else {
122        let error_code = Text::new("error code:")
123            .with_validator(validate_h3_error_code)
124            .with_autocomplete(&h3_error_code_suggestor)
125            .with_page_size(22)
126            .prompt()?;
127
128        match error_code.as_str() {
129            H3_DATAGRAM_ERROR => 0x33,
130            H3_NO_ERROR => 0x100,
131            H3_GENERAL_PROTOCOL_ERROR => 0x101,
132            H3_INTERNAL_ERROR => 0x102,
133            H3_STREAM_CREATION_ERROR => 0x103,
134            H3_CLOSED_CRITICAL_STREAM => 0x104,
135            H3_FRAME_UNEXPECTED => 0x105,
136            H3_FRAME_ERROR => 0x106,
137            H3_EXCESSIVE_LOAD => 0x107,
138            H3_ID_ERROR => 0x108,
139            H3_SETTINGS_ERROR => 0x109,
140            H3_MISSING_SETTINGS => 0x10a,
141            H3_REQUEST_REJECTED => 0x10b,
142            H3_REQUEST_CANCELLED => 0x10c,
143            H3_REQUEST_INCOMPLETE => 0x10d,
144            H3_MESSAGE_ERROR => 0x10e,
145            H3_CONNECT_ERROR => 0x10f,
146            H3_VERSION_FALLBACK => 0x110,
147            QPACK_DECOMPRESSION_FAILED => 0x200,
148            QPACK_ENCODER_STREAM_ERROR => 0x201,
149            QPACK_DECODER_STREAM_ERROR => 0x202,
150
151            v => v.parse::<u64>().unwrap(),
152        }
153    };
154
155    Ok((space, error_code))
156}
157
158fn prompt_transport_or_app() -> InquireResult<String> {
159    Ok(
160        Select::new("transport or application:", vec![TRANSPORT, APPLICATION])
161            .prompt()?
162            .to_string(),
163    )
164}
165
166fn validate_transport_error_code(
167    id: &str,
168) -> Result<Validation, CustomUserError> {
169    if matches!(
170        id,
171        NO_ERROR |
172            INTERNAL_ERROR |
173            CONNECTION_REFUSED |
174            FLOW_CONTROL_ERROR |
175            STREAM_LIMIT_ERROR |
176            STREAM_STATE_ERROR |
177            FINAL_SIZE_ERROR |
178            FRAME_ENCODING_ERROR |
179            TRANSPORT_PARAMETER_ERROR |
180            CONNECTION_ID_LIMIT_ERROR |
181            PROTOCOL_VIOLATION |
182            INVALID_TOKEN |
183            APPLICATION_ERROR |
184            CRYPTO_BUFFER_EXCEEDED |
185            KEY_UPDATE_ERROR |
186            AEAD_LIMIT_REACHED |
187            NO_VIABLE_PATH |
188            VERSION_NEGOTIATION_ERROR
189    ) {
190        return Ok(Validation::Valid);
191    }
192
193    h3::validate_varint(id)
194}
195
196fn transport_error_code_suggestor(
197    val: &str,
198) -> Result<Vec<String>, CustomUserError> {
199    let suggestions = [
200        NO_ERROR,
201        INTERNAL_ERROR,
202        CONNECTION_REFUSED,
203        FLOW_CONTROL_ERROR,
204        STREAM_LIMIT_ERROR,
205        STREAM_STATE_ERROR,
206        FINAL_SIZE_ERROR,
207        FRAME_ENCODING_ERROR,
208        TRANSPORT_PARAMETER_ERROR,
209        CONNECTION_ID_LIMIT_ERROR,
210        PROTOCOL_VIOLATION,
211        INVALID_TOKEN,
212        APPLICATION_ERROR,
213        CRYPTO_BUFFER_EXCEEDED,
214        KEY_UPDATE_ERROR,
215        AEAD_LIMIT_REACHED,
216        NO_VIABLE_PATH,
217        VERSION_NEGOTIATION_ERROR,
218    ];
219
220    super::squish_suggester(&suggestions, val)
221}
222
223fn validate_h3_error_code(id: &str) -> SuggestionResult<Validation> {
224    if matches!(
225        id,
226        H3_NO_ERROR |
227            H3_GENERAL_PROTOCOL_ERROR |
228            H3_INTERNAL_ERROR |
229            H3_STREAM_CREATION_ERROR |
230            H3_CLOSED_CRITICAL_STREAM |
231            H3_FRAME_UNEXPECTED |
232            H3_FRAME_ERROR |
233            H3_EXCESSIVE_LOAD |
234            H3_ID_ERROR |
235            H3_SETTINGS_ERROR |
236            H3_MISSING_SETTINGS |
237            H3_REQUEST_REJECTED |
238            H3_REQUEST_CANCELLED |
239            H3_REQUEST_INCOMPLETE |
240            H3_MESSAGE_ERROR |
241            H3_CONNECT_ERROR |
242            H3_VERSION_FALLBACK |
243            QPACK_DECOMPRESSION_FAILED |
244            QPACK_ENCODER_STREAM_ERROR |
245            QPACK_DECODER_STREAM_ERROR |
246            H3_DATAGRAM_ERROR
247    ) {
248        return Ok(Validation::Valid);
249    }
250
251    h3::validate_varint(id)
252}
253
254fn h3_error_code_suggestor(val: &str) -> SuggestionResult<Vec<String>> {
255    let suggestions = [
256        H3_NO_ERROR,
257        H3_GENERAL_PROTOCOL_ERROR,
258        H3_INTERNAL_ERROR,
259        H3_STREAM_CREATION_ERROR,
260        H3_CLOSED_CRITICAL_STREAM,
261        H3_FRAME_UNEXPECTED,
262        H3_FRAME_ERROR,
263        H3_EXCESSIVE_LOAD,
264        H3_ID_ERROR,
265        H3_SETTINGS_ERROR,
266        H3_MISSING_SETTINGS,
267        H3_REQUEST_REJECTED,
268        H3_REQUEST_CANCELLED,
269        H3_REQUEST_INCOMPLETE,
270        H3_MESSAGE_ERROR,
271        H3_CONNECT_ERROR,
272        H3_VERSION_FALLBACK,
273        QPACK_DECOMPRESSION_FAILED,
274        QPACK_ENCODER_STREAM_ERROR,
275        QPACK_DECODER_STREAM_ERROR,
276        H3_DATAGRAM_ERROR,
277    ];
278
279    super::squish_suggester(&suggestions, val)
280}