Skip to main content

h3i/prompts/h3/
stream.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 crate::actions::h3::Action;
32use crate::prompts::h3;
33use crate::prompts::h3::errors::prompt_transport_or_app_error;
34use crate::prompts::h3::prompt_yes_no;
35use crate::StreamIdAllocator;
36
37use super::squish_suggester;
38use super::SuggestionResult;
39use super::AUTO_PICK;
40use super::EMPTY_PICKS;
41use super::ESC_TO_RET;
42use super::STREAM_ID_PROMPT;
43
44const CONTROL_STREAM: &str = "Control Stream";
45const PUSH_STREAM: &str = "Push Stream";
46const QPACK_ENCODER: &str = "QPACK Encoder Stream";
47const QPACK_DECODER: &str = "QPACK Decoder Stream";
48
49fn validate_stream_id(id: &str) -> SuggestionResult<Validation> {
50    if id.is_empty() {
51        return Ok(Validation::Valid);
52    }
53
54    h3::validate_varint(id)
55}
56
57pub fn autopick_stream_id(
58    sid_alloc: &mut StreamIdAllocator,
59) -> InquireResult<u64> {
60    let stream_id = Text::new(STREAM_ID_PROMPT)
61        .with_placeholder(EMPTY_PICKS)
62        .with_help_message(ESC_TO_RET)
63        .with_validator(validate_stream_id)
64        .prompt()?;
65
66    Ok(match stream_id.as_str() {
67        "" => {
68            let id = sid_alloc.take_next_id();
69            println!("{AUTO_PICK}={id}");
70            id
71        },
72
73        _ => stream_id.parse::<u64>().unwrap(),
74    })
75}
76
77pub fn prompt_open_uni_stream(
78    sid_alloc: &mut StreamIdAllocator,
79) -> InquireResult<Action> {
80    let stream_id = autopick_stream_id(sid_alloc)?;
81    let stream_type = Text::new("stream type:")
82        .with_validator(validate_stream_type)
83        .with_autocomplete(&stream_type_suggestor)
84        .prompt()?;
85
86    let ty = match stream_type.as_str() {
87        CONTROL_STREAM => 0x0,
88        PUSH_STREAM => 0x1,
89        QPACK_ENCODER => 0x2,
90        QPACK_DECODER => 0x3,
91        _ => stream_type.parse::<u64>().unwrap(),
92    };
93
94    let fin_stream = prompt_fin_stream()?;
95
96    Ok(Action::OpenUniStream {
97        stream_id,
98        fin_stream,
99        stream_type: ty,
100        expected_result: Default::default(),
101    })
102}
103
104fn validate_stream_type(id: &str) -> SuggestionResult<Validation> {
105    if matches!(
106        id,
107        CONTROL_STREAM | PUSH_STREAM | QPACK_ENCODER | QPACK_DECODER
108    ) {
109        return Ok(Validation::Valid);
110    }
111
112    h3::validate_varint(id)
113}
114
115fn stream_type_suggestor(val: &str) -> SuggestionResult<Vec<String>> {
116    let suggestions = [CONTROL_STREAM, PUSH_STREAM, QPACK_ENCODER, QPACK_DECODER];
117
118    squish_suggester(&suggestions, val)
119}
120
121pub fn prompt_fin_stream() -> InquireResult<bool> {
122    prompt_yes_no("fin stream:")
123}
124
125pub fn prompt_reset_stream() -> InquireResult<Action> {
126    let (stream_id, error_code) = prompt_close_stream()?;
127
128    Ok(Action::ResetStream {
129        stream_id,
130        error_code,
131    })
132}
133
134pub fn prompt_stop_sending() -> InquireResult<Action> {
135    let (stream_id, error_code) = prompt_close_stream()?;
136
137    Ok(Action::StopSending {
138        stream_id,
139        error_code,
140    })
141}
142
143fn prompt_close_stream() -> InquireResult<(u64, u64)> {
144    let id = h3::prompt_stream_id()?;
145
146    let (_, error_code) = prompt_transport_or_app_error()?;
147
148    Ok((id, error_code))
149}