| // Copyright (C) 2024, Cloudflare, Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // |
| // * Redistributions in binary form must reproduce the above copyright |
| // notice, this list of conditions and the following disclaimer in the |
| // documentation and/or other materials provided with the distribution. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS |
| // IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| // THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR |
| // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
| // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| //! QUIC and HTTP/3 errors for the h3i client. |
| use inquire::error::InquireResult; |
| use inquire::validator::Validation; |
| use inquire::CustomUserError; |
| use inquire::Select; |
| use inquire::Text; |
| use qlog::events::quic::ErrorSpace; |
| |
| use crate::prompts::h3; |
| |
| use super::SuggestionResult; |
| |
| pub const NO_ERROR: &str = "NO_ERROR"; |
| pub const INTERNAL_ERROR: &str = "INTERNAL_ERROR"; |
| pub const CONNECTION_REFUSED: &str = "CONNECTION_REFUSED"; |
| pub const FLOW_CONTROL_ERROR: &str = "FLOW_CONTROL_ERROR"; |
| pub const STREAM_LIMIT_ERROR: &str = "STREAM_LIMIT_ERROR"; |
| pub const STREAM_STATE_ERROR: &str = "STREAM_STATE_ERROR"; |
| pub const FINAL_SIZE_ERROR: &str = "FINAL_SIZE_ERROR"; |
| pub const FRAME_ENCODING_ERROR: &str = "FRAME_ENCODING_ERROR"; |
| pub const TRANSPORT_PARAMETER_ERROR: &str = "TRANSPORT_PARAMETER_ERROR"; |
| pub const CONNECTION_ID_LIMIT_ERROR: &str = "CONNECTION_ID_LIMIT_ERROR"; |
| pub const PROTOCOL_VIOLATION: &str = "PROTOCOL_VIOLATION"; |
| pub const INVALID_TOKEN: &str = "INVALID_TOKEN"; |
| pub const APPLICATION_ERROR: &str = "APPLICATION_ERROR"; |
| pub const CRYPTO_BUFFER_EXCEEDED: &str = "CRYPTO_BUFFER_EXCEEDED"; |
| pub const KEY_UPDATE_ERROR: &str = "KEY_UPDATE_ERROR"; |
| pub const AEAD_LIMIT_REACHED: &str = "AEAD_LIMIT_REACHED"; |
| pub const NO_VIABLE_PATH: &str = "NO_VIABLE_PATH"; |
| pub const VERSION_NEGOTIATION_ERROR: &str = "VERSION_NEGOTIATION_ERROR"; |
| |
| pub const H3_DATAGRAM_ERROR: &str = "H3_DATAGRAM_ERROR"; |
| pub const H3_NO_ERROR: &str = "H3_NO_ERROR"; |
| pub const H3_GENERAL_PROTOCOL_ERROR: &str = "H3_GENERAL_PROTOCOL_ERROR"; |
| pub const H3_INTERNAL_ERROR: &str = "H3_INTERNAL_ERROR"; |
| pub const H3_STREAM_CREATION_ERROR: &str = "H3_STREAM_CREATION_ERROR"; |
| pub const H3_CLOSED_CRITICAL_STREAM: &str = "H3_CLOSED_CRITICAL_STREAM"; |
| pub const H3_FRAME_UNEXPECTED: &str = "H3_FRAME_UNEXPECTED"; |
| pub const H3_FRAME_ERROR: &str = "H3_FRAME_ERROR"; |
| pub const H3_EXCESSIVE_LOAD: &str = "H3_EXCESSIVE_LOAD"; |
| pub const H3_ID_ERROR: &str = "H3_ID_ERROR"; |
| pub const H3_SETTINGS_ERROR: &str = "H3_SETTINGS_ERROR"; |
| pub const H3_MISSING_SETTINGS: &str = "H3_MISSING_SETTINGS"; |
| pub const H3_REQUEST_REJECTED: &str = "H3_REQUEST_REJECTED"; |
| pub const H3_REQUEST_CANCELLED: &str = "H3_REQUEST_CANCELLED"; |
| pub const H3_REQUEST_INCOMPLETE: &str = "H3_REQUEST_INCOMPLETE"; |
| pub const H3_MESSAGE_ERROR: &str = "H3_MESSAGE_ERROR"; |
| pub const H3_CONNECT_ERROR: &str = "H3_CONNECT_ERROR"; |
| pub const H3_VERSION_FALLBACK: &str = "H3_VERSION_FALLBACK"; |
| pub const QPACK_DECOMPRESSION_FAILED: &str = "QPACK_DECOMPRESSION_FAILED"; |
| pub const QPACK_ENCODER_STREAM_ERROR: &str = "QPACK_ENCODER_STREAM_ERROR"; |
| pub const QPACK_DECODER_STREAM_ERROR: &str = "QPACK_DECODER_STREAM_ERROR"; |
| |
| pub const TRANSPORT: &str = "transport"; |
| pub const APPLICATION: &str = "application"; |
| |
| // TODO: do we want to rely on the qlog enum here? |
| pub fn prompt_transport_or_app_error() -> InquireResult<(ErrorSpace, u64)> { |
| let trans_or_app = prompt_transport_or_app()?; |
| let space = if trans_or_app == TRANSPORT { |
| ErrorSpace::TransportError |
| } else { |
| ErrorSpace::ApplicationError |
| }; |
| |
| let error_code = if matches!(space, ErrorSpace::TransportError) { |
| let error_code = Text::new("error code:") |
| .with_validator(validate_transport_error_code) |
| .with_autocomplete(&transport_error_code_suggestor) |
| .with_page_size(18) |
| .prompt()?; |
| |
| match error_code.as_str() { |
| NO_ERROR => 0x0, |
| INTERNAL_ERROR => 0x1, |
| CONNECTION_REFUSED => 0x2, |
| FLOW_CONTROL_ERROR => 0x3, |
| STREAM_LIMIT_ERROR => 0x4, |
| STREAM_STATE_ERROR => 0x5, |
| FINAL_SIZE_ERROR => 0x6, |
| FRAME_ENCODING_ERROR => 0x7, |
| TRANSPORT_PARAMETER_ERROR => 0x8, |
| CONNECTION_ID_LIMIT_ERROR => 0x9, |
| PROTOCOL_VIOLATION => 0x0a, |
| INVALID_TOKEN => 0x0b, |
| APPLICATION_ERROR => 0x0c, |
| CRYPTO_BUFFER_EXCEEDED => 0x0d, |
| KEY_UPDATE_ERROR => 0x0e, |
| AEAD_LIMIT_REACHED => 0x0f, |
| NO_VIABLE_PATH => 0x10, |
| VERSION_NEGOTIATION_ERROR => 0x11, |
| |
| v => v.parse::<u64>().unwrap(), |
| } |
| } else { |
| let error_code = Text::new("error code:") |
| .with_validator(validate_h3_error_code) |
| .with_autocomplete(&h3_error_code_suggestor) |
| .with_page_size(22) |
| .prompt()?; |
| |
| match error_code.as_str() { |
| H3_DATAGRAM_ERROR => 0x33, |
| H3_NO_ERROR => 0x100, |
| H3_GENERAL_PROTOCOL_ERROR => 0x101, |
| H3_INTERNAL_ERROR => 0x102, |
| H3_STREAM_CREATION_ERROR => 0x103, |
| H3_CLOSED_CRITICAL_STREAM => 0x104, |
| H3_FRAME_UNEXPECTED => 0x105, |
| H3_FRAME_ERROR => 0x106, |
| H3_EXCESSIVE_LOAD => 0x107, |
| H3_ID_ERROR => 0x108, |
| H3_SETTINGS_ERROR => 0x109, |
| H3_MISSING_SETTINGS => 0x10a, |
| H3_REQUEST_REJECTED => 0x10b, |
| H3_REQUEST_CANCELLED => 0x10c, |
| H3_REQUEST_INCOMPLETE => 0x10d, |
| H3_MESSAGE_ERROR => 0x10e, |
| H3_CONNECT_ERROR => 0x10f, |
| H3_VERSION_FALLBACK => 0x110, |
| QPACK_DECOMPRESSION_FAILED => 0x200, |
| QPACK_ENCODER_STREAM_ERROR => 0x201, |
| QPACK_DECODER_STREAM_ERROR => 0x202, |
| |
| v => v.parse::<u64>().unwrap(), |
| } |
| }; |
| |
| Ok((space, error_code)) |
| } |
| |
| fn prompt_transport_or_app() -> InquireResult<String> { |
| Ok( |
| Select::new("transport or application:", vec![TRANSPORT, APPLICATION]) |
| .prompt()? |
| .to_string(), |
| ) |
| } |
| |
| fn validate_transport_error_code( |
| id: &str, |
| ) -> Result<Validation, CustomUserError> { |
| if matches!( |
| id, |
| NO_ERROR | |
| INTERNAL_ERROR | |
| CONNECTION_REFUSED | |
| FLOW_CONTROL_ERROR | |
| STREAM_LIMIT_ERROR | |
| STREAM_STATE_ERROR | |
| FINAL_SIZE_ERROR | |
| FRAME_ENCODING_ERROR | |
| TRANSPORT_PARAMETER_ERROR | |
| CONNECTION_ID_LIMIT_ERROR | |
| PROTOCOL_VIOLATION | |
| INVALID_TOKEN | |
| APPLICATION_ERROR | |
| CRYPTO_BUFFER_EXCEEDED | |
| KEY_UPDATE_ERROR | |
| AEAD_LIMIT_REACHED | |
| NO_VIABLE_PATH | |
| VERSION_NEGOTIATION_ERROR |
| ) { |
| return Ok(Validation::Valid); |
| } |
| |
| h3::validate_varint(id) |
| } |
| |
| fn transport_error_code_suggestor( |
| val: &str, |
| ) -> Result<Vec<String>, CustomUserError> { |
| let suggestions = [ |
| NO_ERROR, |
| INTERNAL_ERROR, |
| CONNECTION_REFUSED, |
| FLOW_CONTROL_ERROR, |
| STREAM_LIMIT_ERROR, |
| STREAM_STATE_ERROR, |
| FINAL_SIZE_ERROR, |
| FRAME_ENCODING_ERROR, |
| TRANSPORT_PARAMETER_ERROR, |
| CONNECTION_ID_LIMIT_ERROR, |
| PROTOCOL_VIOLATION, |
| INVALID_TOKEN, |
| APPLICATION_ERROR, |
| CRYPTO_BUFFER_EXCEEDED, |
| KEY_UPDATE_ERROR, |
| AEAD_LIMIT_REACHED, |
| NO_VIABLE_PATH, |
| VERSION_NEGOTIATION_ERROR, |
| ]; |
| |
| super::squish_suggester(&suggestions, val) |
| } |
| |
| fn validate_h3_error_code(id: &str) -> SuggestionResult<Validation> { |
| if matches!( |
| id, |
| H3_NO_ERROR | |
| H3_GENERAL_PROTOCOL_ERROR | |
| H3_INTERNAL_ERROR | |
| H3_STREAM_CREATION_ERROR | |
| H3_CLOSED_CRITICAL_STREAM | |
| H3_FRAME_UNEXPECTED | |
| H3_FRAME_ERROR | |
| H3_EXCESSIVE_LOAD | |
| H3_ID_ERROR | |
| H3_SETTINGS_ERROR | |
| H3_MISSING_SETTINGS | |
| H3_REQUEST_REJECTED | |
| H3_REQUEST_CANCELLED | |
| H3_REQUEST_INCOMPLETE | |
| H3_MESSAGE_ERROR | |
| H3_CONNECT_ERROR | |
| H3_VERSION_FALLBACK | |
| QPACK_DECOMPRESSION_FAILED | |
| QPACK_ENCODER_STREAM_ERROR | |
| QPACK_DECODER_STREAM_ERROR | |
| H3_DATAGRAM_ERROR |
| ) { |
| return Ok(Validation::Valid); |
| } |
| |
| h3::validate_varint(id) |
| } |
| |
| fn h3_error_code_suggestor(val: &str) -> SuggestionResult<Vec<String>> { |
| let suggestions = [ |
| H3_NO_ERROR, |
| H3_GENERAL_PROTOCOL_ERROR, |
| H3_INTERNAL_ERROR, |
| H3_STREAM_CREATION_ERROR, |
| H3_CLOSED_CRITICAL_STREAM, |
| H3_FRAME_UNEXPECTED, |
| H3_FRAME_ERROR, |
| H3_EXCESSIVE_LOAD, |
| H3_ID_ERROR, |
| H3_SETTINGS_ERROR, |
| H3_MISSING_SETTINGS, |
| H3_REQUEST_REJECTED, |
| H3_REQUEST_CANCELLED, |
| H3_REQUEST_INCOMPLETE, |
| H3_MESSAGE_ERROR, |
| H3_CONNECT_ERROR, |
| H3_VERSION_FALLBACK, |
| QPACK_DECOMPRESSION_FAILED, |
| QPACK_ENCODER_STREAM_ERROR, |
| QPACK_DECODER_STREAM_ERROR, |
| H3_DATAGRAM_ERROR, |
| ]; |
| |
| super::squish_suggester(&suggestions, val) |
| } |