Skip to main content

h3i/prompts/h3/
settings.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
27use inquire::error::InquireResult;
28use inquire::validator::Validation;
29use inquire::Text;
30
31use super::squish_suggester;
32use super::stream::prompt_fin_stream;
33use super::SuggestionResult;
34use crate::actions::h3::Action;
35use crate::prompts::h3;
36
37use crate::quiche;
38
39const QPACK_MAX_TABLE_CAPACITY: &str = "QPACK_MAX_TABLE_CAPACITY";
40const MAX_FIELD_SECTION_SIZE: &str = "MAX_FIELD_SECTION_SIZE";
41const QPACK_BLOCKED_STREAMS: &str = "QPACK_BLOCKED_STREAMS";
42const ENABLE_CONNECT_PROTOCOL: &str = "ENABLE_CONNECT_PROTOCOL";
43const H3_DATAGRAM: &str = "H3_DATAGRAM";
44
45type RawSettings = Vec<(u64, u64)>;
46
47pub fn prompt_settings() -> InquireResult<Action> {
48    let stream_id = h3::prompt_control_stream_id()?;
49    let settings = settings_read_loop();
50
51    let fin_stream = prompt_fin_stream()?;
52
53    let action = Action::SendFrame {
54        stream_id,
55        fin_stream,
56        frame: quiche::h3::frame::Frame::Settings {
57            max_field_section_size: None,
58            qpack_max_table_capacity: None,
59            qpack_blocked_streams: None,
60            connect_protocol_enabled: None,
61            h3_datagram: None,
62            grease: None,
63            raw: None,
64            additional_settings: Some(settings),
65        },
66        expected_result: Default::default(),
67    };
68
69    Ok(action)
70}
71
72fn settings_read_loop() -> RawSettings {
73    let mut settings = vec![];
74
75    loop {
76        let ty = match Text::new("setting type:")
77            .with_validator(validate_setting_type)
78            .with_autocomplete(&settings_type_suggestor)
79            .with_help_message("type 'q!' to stop adding settings")
80            .prompt()
81        {
82            Ok(h) => {
83                if h == "q!" {
84                    break;
85                }
86
87                h
88            },
89            Err(_) => {
90                println!("An error happened, stopping.");
91                break;
92            },
93        };
94
95        let ty = match ty.as_str() {
96            QPACK_MAX_TABLE_CAPACITY => 0x1,
97            MAX_FIELD_SECTION_SIZE => 0x6,
98            QPACK_BLOCKED_STREAMS => 0x7,
99            ENABLE_CONNECT_PROTOCOL => 0x8,
100            H3_DATAGRAM => 0x33,
101
102            v => v.parse::<u64>().unwrap(),
103        };
104
105        let value = Text::new("setting value:")
106            .with_validator(h3::validate_varint)
107            .prompt()
108            .expect("An error happened, stopping.")
109            .parse::<u64>()
110            .unwrap();
111
112        settings.push((ty, value));
113    }
114
115    settings
116}
117
118fn validate_setting_type(id: &str) -> SuggestionResult<Validation> {
119    if matches!(
120        id,
121        "q!" | QPACK_MAX_TABLE_CAPACITY |
122            MAX_FIELD_SECTION_SIZE |
123            QPACK_BLOCKED_STREAMS |
124            ENABLE_CONNECT_PROTOCOL |
125            H3_DATAGRAM
126    ) {
127        return Ok(Validation::Valid);
128    }
129
130    h3::validate_varint(id)
131}
132
133fn settings_type_suggestor(val: &str) -> SuggestionResult<Vec<String>> {
134    let suggestions = [
135        QPACK_MAX_TABLE_CAPACITY,
136        MAX_FIELD_SECTION_SIZE,
137        QPACK_BLOCKED_STREAMS,
138        ENABLE_CONNECT_PROTOCOL,
139        H3_DATAGRAM,
140    ];
141
142    squish_suggester(&suggestions, val)
143}