h3i/actions/
h3.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//! Actions specific to HTTP/3 and QUIC
28//!
29//! Actions are small operations such as sending HTTP/3 frames or managing QUIC
30//! streams. Each independent use case for h3i requires its own collection of
31//! Actions, that h3i iterates over in sequence and executes.
32
33use std::collections::HashMap;
34use std::time::Duration;
35
36use quiche;
37use quiche::h3::frame::Frame;
38use quiche::h3::Header;
39use quiche::ConnectionError;
40use serde::Deserialize;
41use serde::Serialize;
42use serde_with::serde_as;
43
44use crate::encode_header_block;
45use crate::encode_header_block_literal;
46
47/// An action which the HTTP/3 client should take.
48///
49/// The client iterates over a vector of said actions, executing each one
50/// sequentially. Note that packets will be flushed when said iteration has
51/// completed, regardless of if an [`Action::FlushPackets`] was the terminal
52/// action.
53#[derive(Clone, Debug, PartialEq, Eq)]
54pub enum Action {
55    /// Send a [quiche::h3::frame::Frame] over a stream.
56    SendFrame {
57        stream_id: u64,
58        fin_stream: bool,
59        frame: Frame,
60    },
61
62    /// Send a HEADERS frame over a stream.
63    SendHeadersFrame {
64        stream_id: u64,
65        fin_stream: bool,
66        literal_headers: bool,
67        headers: Vec<Header>,
68        frame: Frame,
69    },
70
71    /// Send arbitrary bytes over a stream.
72    StreamBytes {
73        stream_id: u64,
74        fin_stream: bool,
75        bytes: Vec<u8>,
76    },
77
78    /// Open a new unidirectional stream.
79    OpenUniStream {
80        stream_id: u64,
81        fin_stream: bool,
82        stream_type: u64,
83    },
84
85    /// Send a RESET_STREAM frame with the given error code.
86    ResetStream {
87        stream_id: u64,
88        error_code: u64,
89    },
90
91    /// Send a STOP_SENDING frame with the given error code.
92    StopSending {
93        stream_id: u64,
94        error_code: u64,
95    },
96
97    /// Send a CONNECTION_CLOSE frame with the given [`ConnectionError`].
98    ConnectionClose {
99        error: ConnectionError,
100    },
101
102    FlushPackets,
103
104    /// Wait for an event. See [WaitType] for the events.
105    Wait {
106        wait_type: WaitType,
107    },
108}
109
110/// Configure the wait behavior for a connection.
111#[serde_as]
112#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
113pub enum WaitType {
114    /// Wait for a time before firing the next action
115    #[serde(rename = "duration")]
116    WaitDuration(
117        #[serde_as(as = "serde_with::DurationMilliSecondsWithFrac<f64>")]
118        Duration,
119    ),
120    /// Wait for some form of a response before firing the next action. This can
121    /// be superseded in several cases:
122    /// 1. The peer resets the specified stream.
123    /// 2. The peer sends a `fin` over the specified stream
124    StreamEvent(StreamEvent),
125}
126
127impl From<WaitType> for Action {
128    fn from(value: WaitType) -> Self {
129        Self::Wait { wait_type: value }
130    }
131}
132
133/// A response event, received over a stream, which will terminate the wait
134/// period.
135///
136/// See [StreamEventType] for the types of events.
137#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
138#[serde(rename = "snake_case")]
139pub struct StreamEvent {
140    pub stream_id: u64,
141    #[serde(rename = "type")]
142    pub event_type: StreamEventType,
143}
144
145/// Response that can terminate a wait period.
146#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
147#[serde(rename_all = "lowercase")]
148pub enum StreamEventType {
149    /// A HEADERS frame was received.
150    Headers,
151    /// A DATA frame was received.
152    Data,
153    /// The stream was somehow finished, either by a RESET_STREAM frame or via
154    /// the `fin` bit being set.
155    Finished,
156}
157
158#[derive(Debug, Default)]
159pub(crate) struct WaitingFor(HashMap<u64, Vec<StreamEvent>>);
160
161impl WaitingFor {
162    pub(crate) fn is_empty(&self) -> bool {
163        self.0.values().all(|v| v.is_empty())
164    }
165
166    pub(crate) fn add_wait(&mut self, stream_event: &StreamEvent) {
167        self.0
168            .entry(stream_event.stream_id)
169            .or_default()
170            .push(*stream_event);
171    }
172
173    pub(crate) fn remove_wait(&mut self, stream_event: StreamEvent) {
174        if let Some(waits) = self.0.get_mut(&stream_event.stream_id) {
175            let old_len = waits.len();
176            waits.retain(|wait| wait != &stream_event);
177            let new_len = waits.len();
178
179            if old_len != new_len {
180                log::info!("No longer waiting for {:?}", stream_event);
181            }
182        }
183    }
184
185    pub(crate) fn clear_waits_on_stream(&mut self, stream_id: u64) {
186        if let Some(waits) = self.0.get_mut(&stream_id) {
187            if !waits.is_empty() {
188                log::info!("Clearing all waits for stream {}", stream_id);
189                waits.clear();
190            }
191        }
192    }
193}
194
195/// Convenience to convert between header-related data and a
196/// [Action::SendHeadersFrame].
197pub fn send_headers_frame(
198    stream_id: u64, fin_stream: bool, headers: Vec<Header>,
199) -> Action {
200    let header_block = encode_header_block(&headers).unwrap();
201
202    Action::SendHeadersFrame {
203        stream_id,
204        fin_stream,
205        headers,
206        literal_headers: false,
207        frame: Frame::Headers { header_block },
208    }
209}
210
211/// Convenience to convert between header-related data and a
212/// [Action::SendHeadersFrame]. Unlike [`send_headers_frame`],
213/// this version encodes the headers literally as they are provided,
214/// not converting the header names to lower-case.
215pub fn send_headers_frame_literal(
216    stream_id: u64, fin_stream: bool, headers: Vec<Header>,
217) -> Action {
218    let header_block = encode_header_block_literal(&headers).unwrap();
219
220    Action::SendHeadersFrame {
221        stream_id,
222        fin_stream,
223        headers,
224        literal_headers: true,
225        frame: Frame::Headers { header_block },
226    }
227}