diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 14:29:10 +0000 |
commit | 2aa4a82499d4becd2284cdb482213d541b8804dd (patch) | |
tree | b80bf8bf13c3766139fbacc530efd0dd9d54394c /third_party/rust/neqo-http3 | |
parent | Initial commit. (diff) | |
download | firefox-upstream.tar.xz firefox-upstream.zip |
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/neqo-http3')
21 files changed, 11870 insertions, 0 deletions
diff --git a/third_party/rust/neqo-http3/.cargo-checksum.json b/third_party/rust/neqo-http3/.cargo-checksum.json new file mode 100644 index 0000000000..6ac02f1e11 --- /dev/null +++ b/third_party/rust/neqo-http3/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"68ef7bb6147bae527c3a803278291e93cfa5263692f1c3b7ecc90e978390aa35","src/client_events.rs":"87e7c323ec2ceb759da7d6f2bf24e774f6f2e3833f63454d09cc09f47fccfa8e","src/connection.rs":"1230a12e446ef029bd5987270cc54247124094c5420becb04ace84c7644b68cf","src/connection_client.rs":"e6bceaddc41ccf245c384dd055b27cfe5adb9a47fe155a5fb16bb43e6a35d524","src/connection_server.rs":"b3f4d42a984f7093fca737c64bb49c569f8f95d1586a6c02d4dba58f290ce92e","src/control_stream_local.rs":"2e9483d79dc00a3e5ef51ea2b0f28fda2b67348996c47729947c70a8be3007ed","src/control_stream_remote.rs":"1dfac4956a7d6971e2cef2c83963d838e73aa3bf3286b7bde97099978c41d527","src/hframe.rs":"3620c6d114e6d5b98447a2dd2a7cb6872562ebda2cc6de828177b745c8dcbf4d","src/lib.rs":"f0866bb1750c36a6413c48f1a7789599208d2a57842398b8355c22996a1455a4","src/push_controller.rs":"70811f87bf0562630a3a68dc0783fce3c4694ab12de9cac835a803e1115431a5","src/push_stream.rs":"5f3a5c6d72c0a8e4d1c1c5041125f7aff9a65950fa60c77f42cd4b35c768f585","src/qlog.rs":"29c0e3c4c9571eb7fe905967edeb1c4bc236b1e35a0e0f11a4a847f1d246681d","src/recv_message.rs":"975fe666de73493501987bdd285efbdef6efce8e721e3a3411af9baf9599c4aa","src/send_message.rs":"e83080b06748ce65ac737fab91f675c8292f84cdf43af64543ef1acf06ad502a","src/server.rs":"9be3d7df02ed14ec2e83b279f9a537ecf585cbe786dca26fd6b6ce4ca9e1ee81","src/server_connection_events.rs":"762ddb87f700abe91ae1ae78ebbb87a88da0c0a341748b0751b01099870e9985","src/server_events.rs":"e780daa0d19d9a5594eee73f7ff27d56151a5a2ea969f2b4dcc64253e9570dab","src/settings.rs":"127a51fa7857b870718baa14340b0461d86a67e59bf1a8cb42d7bae0240c0ef1","src/stream_type_reader.rs":"aacb2e865f79b3ac55a887fd670f2286d8ffef94f7d8b3ecfa7e0cccbfa9ec04","tests/httpconn.rs":"1a97a80f7abe11c6ba0bd9b41003be6b293049164daa21e907365d93b00a782f"},"package":null}
\ No newline at end of file diff --git a/third_party/rust/neqo-http3/Cargo.toml b/third_party/rust/neqo-http3/Cargo.toml new file mode 100644 index 0000000000..3aed2c0b16 --- /dev/null +++ b/third_party/rust/neqo-http3/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "neqo-http3" +version = "0.4.19" +authors = ["Dragana Damjanovic <dragana.damjano@gmail.com>"] +edition = "2018" +license = "MIT/Apache-2.0" + +[dependencies] +neqo-common = { path = "./../neqo-common" } +neqo-crypto = { path = "./../neqo-crypto" } +neqo-transport = { path = "./../neqo-transport" } +neqo-qpack = { path = "./../neqo-qpack" } +log = {version = "0.4.0", default-features = false} +smallvec = "1.0.0" +qlog = "0.4.0" + +[dev-dependencies] +test-fixture = { path = "../test-fixture" } + +[features] +default = ["deny-warnings"] +deny-warnings = [] diff --git a/third_party/rust/neqo-http3/src/client_events.rs b/third_party/rust/neqo-http3/src/client_events.rs new file mode 100644 index 0000000000..dc7d86126f --- /dev/null +++ b/third_party/rust/neqo-http3/src/client_events.rs @@ -0,0 +1,255 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(clippy::module_name_repetitions)] + +use crate::connection::Http3State; +use crate::send_message::SendMessageEvents; +use crate::Header; +use crate::RecvMessageEvents; + +use neqo_common::event::Provider as EventProvider; +use neqo_crypto::ResumptionToken; +use neqo_transport::{AppError, StreamType}; + +use std::cell::RefCell; +use std::collections::VecDeque; +use std::rc::Rc; + +#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone)] +pub enum Http3ClientEvent { + /// Response headers are received. + HeaderReady { + stream_id: u64, + headers: Vec<Header>, + interim: bool, + fin: bool, + }, + /// A stream can accept new data. + DataWritable { stream_id: u64 }, + /// New bytes available for reading. + DataReadable { stream_id: u64 }, + /// Peer reset the stream or there was an parsing error. + Reset { + stream_id: u64, + error: AppError, + local: bool, + }, + /// Peer has sent a STOP_SENDING. + StopSending { stream_id: u64, error: AppError }, + /// A new push promise. + PushPromise { + push_id: u64, + request_stream_id: u64, + headers: Vec<Header>, + }, + /// A push response headers are ready. + PushHeaderReady { + push_id: u64, + headers: Vec<Header>, + interim: bool, + fin: bool, + }, + /// New bytes are available on a push stream for reading. + PushDataReadable { push_id: u64 }, + /// A push has been canceled. + PushCanceled { push_id: u64 }, + /// A push stream was been reset due to a HttpGeneralProtocol error. + /// Most common dase are malformed response headers. + PushReset { push_id: u64, error: AppError }, + /// New stream can be created + RequestsCreatable, + /// Cert authentication needed + AuthenticationNeeded, + /// A new resumption token. + ResumptionToken(ResumptionToken), + /// Zero Rtt has been rejected. + ZeroRttRejected, + /// Client has received a GOAWAY frame + GoawayReceived, + /// Connection state change. + StateChange(Http3State), +} + +#[derive(Debug, Default, Clone)] +pub struct Http3ClientEvents { + events: Rc<RefCell<VecDeque<Http3ClientEvent>>>, +} + +impl RecvMessageEvents for Http3ClientEvents { + /// Add a new `HeaderReady` event. + fn header_ready(&self, stream_id: u64, headers: Vec<Header>, interim: bool, fin: bool) { + self.insert(Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + }); + } + + /// Add a new `DataReadable` event + fn data_readable(&self, stream_id: u64) { + self.insert(Http3ClientEvent::DataReadable { stream_id }); + } + + /// Add a new `Reset` event. + fn reset(&self, stream_id: u64, error: AppError, local: bool) { + self.remove(|evt| { + matches!(evt, + Http3ClientEvent::HeaderReady { stream_id: x, .. } + | Http3ClientEvent::DataReadable { stream_id: x } + | Http3ClientEvent::PushPromise { request_stream_id: x, .. } + | Http3ClientEvent::Reset { stream_id: x, .. } if *x == stream_id) + }); + self.insert(Http3ClientEvent::Reset { + stream_id, + error, + local, + }); + } +} + +impl SendMessageEvents for Http3ClientEvents { + /// Add a new `DataWritable` event. + fn data_writable(&self, stream_id: u64) { + self.insert(Http3ClientEvent::DataWritable { stream_id }); + } + + fn remove_send_side_event(&self, stream_id: u64) { + self.remove(|evt| { + matches!(evt, + Http3ClientEvent::DataWritable { stream_id: x } + | Http3ClientEvent::StopSending { stream_id: x, .. } if *x == stream_id) + }); + } + + /// Add a new `StopSending` event + fn stop_sending(&self, stream_id: u64, error: AppError) { + // The stream has received a STOP_SENDING frame, we should remove any DataWritable event. + self.remove_send_side_event(stream_id); + self.insert(Http3ClientEvent::StopSending { stream_id, error }); + } +} + +impl Http3ClientEvents { + pub fn push_promise(&self, push_id: u64, request_stream_id: u64, headers: Vec<Header>) { + self.insert(Http3ClientEvent::PushPromise { + push_id, + request_stream_id, + headers, + }); + } + + pub fn push_canceled(&self, push_id: u64) { + self.remove_events_for_push_id(push_id); + self.insert(Http3ClientEvent::PushCanceled { push_id }); + } + + pub fn push_reset(&self, push_id: u64, error: AppError) { + self.remove_events_for_push_id(push_id); + self.insert(Http3ClientEvent::PushReset { push_id, error }); + } + + /// Add a new `RequestCreatable` event + pub(crate) fn new_requests_creatable(&self, stream_type: StreamType) { + if stream_type == StreamType::BiDi { + self.insert(Http3ClientEvent::RequestsCreatable); + } + } + + /// Add a new `AuthenticationNeeded` event + pub(crate) fn authentication_needed(&self) { + self.insert(Http3ClientEvent::AuthenticationNeeded); + } + + /// Add a new resumption token event. + pub(crate) fn resumption_token(&self, token: ResumptionToken) { + self.insert(Http3ClientEvent::ResumptionToken(token)); + } + + /// Add a new `ZeroRttRejected` event. + pub(crate) fn zero_rtt_rejected(&self) { + self.insert(Http3ClientEvent::ZeroRttRejected); + } + + /// Add a new `GoawayReceived` event. + pub(crate) fn goaway_received(&self) { + self.remove(|evt| matches!(evt, Http3ClientEvent::RequestsCreatable)); + self.insert(Http3ClientEvent::GoawayReceived); + } + + pub fn insert(&self, event: Http3ClientEvent) { + self.events.borrow_mut().push_back(event); + } + + fn remove<F>(&self, f: F) + where + F: Fn(&Http3ClientEvent) -> bool, + { + self.events.borrow_mut().retain(|evt| !f(evt)) + } + + /// Add a new `StateChange` event. + pub(crate) fn connection_state_change(&self, state: Http3State) { + match state { + // If closing, existing events no longer relevant. + Http3State::Closing { .. } | Http3State::Closed(_) => self.events.borrow_mut().clear(), + Http3State::Connected => { + self.remove(|evt| { + matches!(evt, Http3ClientEvent::StateChange(Http3State::ZeroRtt)) + }); + } + _ => (), + } + self.insert(Http3ClientEvent::StateChange(state)); + } + + /// Remove all events for a stream + pub(crate) fn remove_events_for_stream_id(&self, stream_id: u64) { + self.remove(|evt| { + matches!(evt, + Http3ClientEvent::HeaderReady { stream_id: x, .. } + | Http3ClientEvent::DataWritable { stream_id: x } + | Http3ClientEvent::DataReadable { stream_id: x } + | Http3ClientEvent::PushPromise { request_stream_id: x, .. } + | Http3ClientEvent::Reset { stream_id: x, .. } + | Http3ClientEvent::StopSending { stream_id: x, .. } if *x == stream_id) + }); + } + + pub fn has_push(&self, push_id: u64) -> bool { + for iter in self.events.borrow().iter() { + if matches!(iter, Http3ClientEvent::PushPromise{push_id:x, ..} if *x == push_id) { + return true; + } + } + false + } + + pub fn remove_events_for_push_id(&self, push_id: u64) { + self.remove(|evt| { + matches!(evt, + Http3ClientEvent::PushPromise{ push_id: x, .. } + | Http3ClientEvent::PushHeaderReady{ push_id: x, .. } + | Http3ClientEvent::PushDataReadable{ push_id: x, .. } + | Http3ClientEvent::PushCanceled{ push_id: x, .. } if *x == push_id) + }); + } +} + +impl EventProvider for Http3ClientEvents { + type Event = Http3ClientEvent; + + /// Check if there is any event present. + fn has_events(&self) -> bool { + !self.events.borrow().is_empty() + } + + /// Take the first event. + fn next_event(&mut self) -> Option<Self::Event> { + self.events.borrow_mut().pop_front() + } +} diff --git a/third_party/rust/neqo-http3/src/connection.rs b/third_party/rust/neqo-http3/src/connection.rs new file mode 100644 index 0000000000..6da7d2a959 --- /dev/null +++ b/third_party/rust/neqo-http3/src/connection.rs @@ -0,0 +1,682 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(clippy::module_name_repetitions)] + +use crate::control_stream_local::{ControlStreamLocal, HTTP3_UNI_STREAM_TYPE_CONTROL}; +use crate::control_stream_remote::ControlStreamRemote; +use crate::hframe::HFrame; +use crate::send_message::SendMessage; +use crate::settings::{HSetting, HSettingType, HSettings, HttpZeroRttChecker}; +use crate::stream_type_reader::NewStreamTypeReader; +use crate::{RecvStream, ResetType}; +use neqo_common::{qdebug, qerror, qinfo, qtrace, qwarn}; +use neqo_qpack::decoder::{QPackDecoder, QPACK_UNI_STREAM_TYPE_DECODER}; +use neqo_qpack::encoder::{QPackEncoder, QPACK_UNI_STREAM_TYPE_ENCODER}; +use neqo_qpack::QpackSettings; +use neqo_transport::{AppError, CloseError, Connection, State, StreamType}; +use std::collections::{BTreeSet, HashMap}; +use std::fmt::Debug; +use std::mem; + +use crate::{Error, Res}; + +const HTTP3_UNI_STREAM_TYPE_PUSH: u64 = 0x1; +const QPACK_TABLE_SIZE_LIMIT: u64 = 1 << 30; + +pub(crate) enum HandleReadableOutput { + StreamNotFound, + NoOutput, + PushStream, + ControlFrames(Vec<HFrame>), +} + +#[derive(Debug)] +enum Http3RemoteSettingsState { + NotReceived, + Received(HSettings), + ZeroRtt(HSettings), +} + +#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone)] +pub enum Http3State { + Initializing, + ZeroRtt, + Connected, + GoingAway(u64), + Closing(CloseError), + Closed(CloseError), +} + +impl Http3State { + #[must_use] + pub fn active(&self) -> bool { + matches!(self, Http3State::Connected | Http3State::GoingAway(_) | Http3State::ZeroRtt) + } +} + +#[derive(Debug)] +pub(crate) struct Http3Connection { + pub state: Http3State, + local_qpack_settings: QpackSettings, + control_stream_local: ControlStreamLocal, + control_stream_remote: ControlStreamRemote, + new_streams: HashMap<u64, NewStreamTypeReader>, + pub qpack_encoder: QPackEncoder, + pub qpack_decoder: QPackDecoder, + settings_state: Http3RemoteSettingsState, + streams_have_data_to_send: BTreeSet<u64>, + pub send_streams: HashMap<u64, SendMessage>, + pub recv_streams: HashMap<u64, Box<dyn RecvStream>>, +} + +impl ::std::fmt::Display for Http3Connection { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Http3 connection") + } +} + +impl Http3Connection { + /// Create a new connection. + pub fn new(local_qpack_settings: QpackSettings) -> Self { + if (local_qpack_settings.max_table_size_encoder >= QPACK_TABLE_SIZE_LIMIT) + || (local_qpack_settings.max_table_size_decoder >= QPACK_TABLE_SIZE_LIMIT) + { + panic!("Wrong max_table_size"); + } + Self { + state: Http3State::Initializing, + local_qpack_settings, + control_stream_local: ControlStreamLocal::new(), + control_stream_remote: ControlStreamRemote::new(), + new_streams: HashMap::new(), + qpack_encoder: QPackEncoder::new(local_qpack_settings, true), + qpack_decoder: QPackDecoder::new(local_qpack_settings), + settings_state: Http3RemoteSettingsState::NotReceived, + streams_have_data_to_send: BTreeSet::new(), + send_streams: HashMap::new(), + recv_streams: HashMap::new(), + } + } + + fn initialize_http3_connection(&mut self, conn: &mut Connection) -> Res<()> { + qinfo!([self], "Initialize the http3 connection."); + self.control_stream_local.create(conn)?; + + self.send_settings(); + self.create_qpack_streams(conn)?; + Ok(()) + } + + fn send_settings(&mut self) { + qdebug!([self], "Send settings."); + self.control_stream_local.queue_frame(&HFrame::Settings { + settings: HSettings::new(&[ + HSetting { + setting_type: HSettingType::MaxTableCapacity, + value: self.qpack_decoder.get_max_table_size(), + }, + HSetting { + setting_type: HSettingType::BlockedStreams, + value: self.qpack_decoder.get_blocked_streams().into(), + }, + ]), + }); + self.control_stream_local.queue_frame(&HFrame::Grease); + } + + /// Save settings for adding to the session ticket. + pub(crate) fn save_settings(&self) -> Vec<u8> { + HttpZeroRttChecker::save(self.local_qpack_settings) + } + + fn create_qpack_streams(&mut self, conn: &mut Connection) -> Res<()> { + qdebug!([self], "create_qpack_streams."); + self.qpack_encoder + .add_send_stream(conn.stream_create(StreamType::UniDi)?); + self.qpack_decoder + .add_send_stream(conn.stream_create(StreamType::UniDi)?); + Ok(()) + } + + /// Inform a `HttpConnection` that a stream has data to send and that `send` should be called for the stream. + pub fn insert_streams_have_data_to_send(&mut self, stream_id: u64) { + self.streams_have_data_to_send.insert(stream_id); + } + + /// Return true if there is a stream that needs to send data. + pub fn has_data_to_send(&self) -> bool { + !self.streams_have_data_to_send.is_empty() + } + + /// Call `send` for all streams that need to send data. + pub fn process_sending(&mut self, conn: &mut Connection) -> Res<()> { + // check if control stream has data to send. + self.control_stream_local.send(conn)?; + + let to_send = mem::replace(&mut self.streams_have_data_to_send, BTreeSet::new()); + for stream_id in to_send { + let mut remove = false; + if let Some(s) = &mut self.send_streams.get_mut(&stream_id) { + s.send(conn, &mut self.qpack_encoder)?; + if s.has_data_to_send() { + self.streams_have_data_to_send.insert(stream_id); + } + remove = s.done(); + } + if remove { + self.send_streams.remove(&stream_id); + } + } + self.qpack_decoder.send(conn)?; + match self.qpack_encoder.send(conn) { + Ok(()) + | Err(neqo_qpack::Error::EncoderStreamBlocked) + | Err(neqo_qpack::Error::DynamicTableFull) => {} + Err(e) => return Err(Error::QpackError(e)), + } + Ok(()) + } + + /// We have a resumption token which remembers previous settings. Update the setting. + pub fn set_0rtt_settings(&mut self, conn: &mut Connection, settings: HSettings) -> Res<()> { + self.initialize_http3_connection(conn)?; + self.set_qpack_settings(&settings)?; + self.settings_state = Http3RemoteSettingsState::ZeroRtt(settings); + self.state = Http3State::ZeroRtt; + Ok(()) + } + + /// Returns the settings for a connection. This is used for creating a resumption token. + pub fn get_settings(&self) -> Option<HSettings> { + if let Http3RemoteSettingsState::Received(settings) = &self.settings_state { + Some(settings.clone()) + } else { + None + } + } + + /// This function adds a new unidi stream and try to read its type. `Http3Connection` can handle + /// a Http3 Control stream, Qpack streams and an unknown stream, but it cannot handle a Push stream. + /// If a Push stream has been discovered, return true and let the `Http3Client`/`Server` handle it. + pub fn handle_new_unidi_stream(&mut self, conn: &mut Connection, stream_id: u64) -> Res<bool> { + qtrace!([self], "A new stream: {}.", stream_id); + let stream_type; + let fin; + { + let ns = self + .new_streams + .entry(stream_id) + .or_insert_with(NewStreamTypeReader::new); + stream_type = ns.get_type(conn, stream_id); + fin = ns.fin(); + } + + if fin { + self.new_streams.remove(&stream_id); + Ok(false) + } else { + stream_type.map_or(Ok(false), |t| { + self.new_streams.remove(&stream_id); + self.decode_new_stream(conn, t, stream_id) + }) + } + } + + fn recv_decoder(&mut self, conn: &mut Connection, stream_id: u64) -> Res<bool> { + if self.qpack_decoder.is_recv_stream(stream_id) { + qdebug!( + [self], + "The qpack decoder stream ({}) is readable.", + stream_id + ); + let unblocked_streams = self.qpack_decoder.receive(conn, stream_id)?; + for stream_id in unblocked_streams { + qdebug!([self], "Stream {} is unblocked", stream_id); + self.handle_read_stream(conn, stream_id, true)?; + } + Ok(true) + } else { + Ok(false) + } + } + + fn recv_control(&mut self, conn: &mut Connection, stream_id: u64) -> Res<HandleReadableOutput> { + assert!(self.control_stream_remote.is_recv_stream(stream_id)); + let mut control_frames = Vec::new(); + + loop { + if let Some(f) = self.control_stream_remote.receive(conn)? { + if let Some(f) = self.handle_control_frame(f)? { + control_frames.push(f); + } + } else if control_frames.is_empty() { + return Ok(HandleReadableOutput::NoOutput); + } else { + return Ok(HandleReadableOutput::ControlFrames(control_frames)); + } + } + } + + /// This function handles reading from all streams, i.e. control, qpack, request/response + /// stream and unidi stream that are still do not have a type. + /// The function cannot handle: + /// 1) a Push stream (if an unknown unidi stream is decoded to be a push stream) + /// 2) frames `MaxPushId` or `Goaway` must be handled by `Http3Client`/`Server`. + /// The function returns `HandleReadableOutput`. + pub fn handle_stream_readable( + &mut self, + conn: &mut Connection, + stream_id: u64, + ) -> Res<HandleReadableOutput> { + qtrace!([self], "Readable stream {}.", stream_id); + + let label = ::neqo_common::log_subject!(::log::Level::Debug, self); + if self.handle_read_stream(conn, stream_id, false)? { + qdebug!([label], "Request/response stream {} read.", stream_id); + Ok(HandleReadableOutput::NoOutput) + } else if self.control_stream_remote.is_recv_stream(stream_id) { + qdebug!( + [self], + "The remote control stream ({}) is readable.", + stream_id + ); + self.recv_control(conn, stream_id) + } else if self.qpack_encoder.recv_if_encoder_stream(conn, stream_id)? { + qdebug!( + [self], + "The qpack encoder stream ({}) is readable.", + stream_id + ); + Ok(HandleReadableOutput::NoOutput) + } else if self.recv_decoder(conn, stream_id)? { + Ok(HandleReadableOutput::NoOutput) + } else if let Some(ns) = self.new_streams.get_mut(&stream_id) { + let stream_type = ns.get_type(conn, stream_id); + let fin = ns.fin(); + if fin { + self.new_streams.remove(&stream_id); + } + if let Some(t) = stream_type { + self.new_streams.remove(&stream_id); + self.decode_new_stream(conn, t, stream_id)?; + // Make sure to read from this stream because DataReadable will not be set again. + return match t { + HTTP3_UNI_STREAM_TYPE_CONTROL => self.recv_control(conn, stream_id), + QPACK_UNI_STREAM_TYPE_DECODER => { + self.qpack_encoder.recv_if_encoder_stream(conn, stream_id)?; + Ok(HandleReadableOutput::NoOutput) + } + QPACK_UNI_STREAM_TYPE_ENCODER => { + self.recv_decoder(conn, stream_id)?; + Ok(HandleReadableOutput::NoOutput) + } + HTTP3_UNI_STREAM_TYPE_PUSH => Ok(HandleReadableOutput::PushStream), + _ => Ok(HandleReadableOutput::NoOutput), + }; + } + + Ok(HandleReadableOutput::NoOutput) + } else { + // For a new stream we receive NewStream event and a + // RecvStreamReadable event. + // In most cases we decode a new stream already on the NewStream + // event and remove it from self.new_streams. + // Therefore, while processing RecvStreamReadable there will be no + // entry for the stream in self.new_streams. + // This can be a push stream as well. + Ok(HandleReadableOutput::StreamNotFound) + } + } + + fn is_critical_stream(&self, stream_id: u64) -> bool { + self.qpack_encoder + .local_stream_id() + .iter() + .chain(self.qpack_encoder.remote_stream_id().iter()) + .chain(self.qpack_decoder.local_stream_id().iter()) + .chain(self.qpack_decoder.remote_stream_id().iter()) + .chain(self.control_stream_local.stream_id().iter()) + .chain(self.control_stream_remote.stream_id().iter()) + .any(|id| stream_id == *id) + } + + /// This is called when a RESET frame has been received. + pub fn handle_stream_reset(&mut self, stream_id: u64, app_error: AppError) -> Res<()> { + qinfo!( + [self], + "Handle a stream reset stream_id={} app_err={}", + stream_id, + app_error + ); + + if let Some(s) = self.recv_streams.remove(&stream_id) { + s.stream_reset(app_error, &mut self.qpack_decoder, ResetType::Remote); + Ok(()) + } else if self.is_critical_stream(stream_id) { + Err(Error::HttpClosedCriticalStream) + } else { + Ok(()) + } + } + + pub fn handle_stream_stop_sending(&mut self, stream_id: u64, app_error: AppError) -> Res<()> { + qinfo!( + [self], + "Handle stream_stop_sending stream_id={} app_err={}", + stream_id, + app_error + ); + + if let Some(mut s) = self.send_streams.remove(&stream_id) { + s.stop_sending(app_error); + Ok(()) + } else if self.is_critical_stream(stream_id) { + Err(Error::HttpClosedCriticalStream) + } else { + Ok(()) + } + } + + /// This is called when `neqo_transport::Connection` state has been change to take proper actions in + /// the HTTP3 layer. + pub fn handle_state_change(&mut self, conn: &mut Connection, state: &State) -> Res<bool> { + qdebug!([self], "Handle state change {:?}", state); + match state { + State::Connected => { + debug_assert!(matches!( + self.state, + Http3State::Initializing | Http3State::ZeroRtt + )); + if self.state == Http3State::Initializing { + self.initialize_http3_connection(conn)?; + } + self.state = Http3State::Connected; + Ok(true) + } + State::Closing { error, .. } | State::Draining { error, .. } => { + if matches!(self.state, Http3State::Closing(_)| Http3State::Closed(_)) { + Ok(false) + } else { + self.state = Http3State::Closing(error.clone().into()); + Ok(true) + } + } + State::Closed(error) => { + if matches!(self.state, Http3State::Closed(_)) { + Ok(false) + } else { + self.state = Http3State::Closed(error.clone().into()); + Ok(true) + } + } + _ => Ok(false), + } + } + + /// This is called when 0RTT has been reseted to clear `send_streams`, `recv_streams` and settings. + pub fn handle_zero_rtt_rejected(&mut self) -> Res<()> { + if self.state == Http3State::ZeroRtt { + self.state = Http3State::Initializing; + self.control_stream_local = ControlStreamLocal::new(); + self.control_stream_remote = ControlStreamRemote::new(); + self.new_streams.clear(); + self.qpack_encoder = QPackEncoder::new(self.local_qpack_settings, true); + self.qpack_decoder = QPackDecoder::new(self.local_qpack_settings); + self.settings_state = Http3RemoteSettingsState::NotReceived; + self.streams_have_data_to_send.clear(); + // TODO: investigate whether this code can automatically retry failed transactions. + self.send_streams.clear(); + self.recv_streams.clear(); + Ok(()) + } else { + debug_assert!(false, "Zero rtt rejected in the wrong state."); + Err(Error::HttpInternal) + } + } + + fn handle_read_stream( + &mut self, + conn: &mut Connection, + stream_id: u64, + header_unblocked: bool, + ) -> Res<bool> { + let label = ::neqo_common::log_subject!(::log::Level::Info, self); + + let r = self.recv_streams.get_mut(&stream_id); + + if r.is_none() { + return Ok(false); + } + + let recv_stream = r.unwrap(); + qinfo!( + [label], + "Request/response stream {} is readable.", + stream_id + ); + if header_unblocked { + recv_stream.header_unblocked(conn, &mut self.qpack_decoder)?; + } else { + recv_stream.receive(conn, &mut self.qpack_decoder)?; + } + if recv_stream.done() { + self.recv_streams.remove(&stream_id); + } + Ok(true) + } + + /// Returns true if it is a push stream. + fn decode_new_stream( + &mut self, + conn: &mut Connection, + stream_type: u64, + stream_id: u64, + ) -> Res<bool> { + match stream_type { + HTTP3_UNI_STREAM_TYPE_CONTROL => { + self.control_stream_remote.add_remote_stream(stream_id)?; + Ok(false) + } + + HTTP3_UNI_STREAM_TYPE_PUSH => { + qinfo!([self], "A new push stream {}.", stream_id); + Ok(true) + } + QPACK_UNI_STREAM_TYPE_ENCODER => { + qinfo!([self], "A new remote qpack encoder stream {}", stream_id); + Error::map_error( + self.qpack_decoder.add_recv_stream(stream_id), + Error::HttpStreamCreation, + )?; + Ok(false) + } + QPACK_UNI_STREAM_TYPE_DECODER => { + qinfo!([self], "A new remote qpack decoder stream {}", stream_id); + Error::map_error( + self.qpack_encoder.add_recv_stream(stream_id), + Error::HttpStreamCreation, + )?; + Ok(false) + } + _ => { + conn.stream_stop_sending(stream_id, Error::HttpStreamCreation.code())?; + Ok(false) + } + } + } + + /// This is called when an application closes the connection. + pub fn close(&mut self, error: AppError) { + qinfo!([self], "Close connection error {:?}.", error); + self.state = Http3State::Closing(CloseError::Application(error)); + if (!self.send_streams.is_empty() || !self.recv_streams.is_empty()) && (error == 0) { + qwarn!("close(0) called when streams still active"); + } + self.send_streams.clear(); + self.recv_streams.clear(); + } + + /// This is called when an application resets a stream. + /// The application reset will close both sides. + pub fn stream_reset( + &mut self, + conn: &mut Connection, + stream_id: u64, + error: AppError, + ) -> Res<()> { + qinfo!([self], "Reset stream {} error={}.", stream_id, error); + + let mut found = self.send_streams.remove(&stream_id).is_some(); + if let Some(s) = self.recv_streams.remove(&stream_id) { + s.stream_reset(error, &mut self.qpack_decoder, ResetType::App); + found = true; + } + + // Stream maybe already be closed and we may get an error here, but we do not care. + let _ = conn.stream_reset_send(stream_id, error); + // Stream maybe already be closed and we may get an error here, but we do not care. + let _ = conn.stream_stop_sending(stream_id, error); + if found { + Ok(()) + } else { + Err(Error::InvalidStreamId) + } + } + + /// This is called when an application wants to close the sending side of a stream. + pub fn stream_close_send(&mut self, conn: &mut Connection, stream_id: u64) -> Res<()> { + qinfo!([self], "Close the sending side for stream {}.", stream_id); + debug_assert!(self.state.active()); + let send_stream = self + .send_streams + .get_mut(&stream_id) + .ok_or(Error::InvalidStreamId)?; + // The following funcion may return InvalidStreamId from the transport layer if the stream has been cloesd + // already. It is ok to ignore it here. + let _ = send_stream.close(conn); + if send_stream.done() { + self.send_streams.remove(&stream_id); + } + Ok(()) + } + + // If the control stream has received frames MaxPushId or Goaway which handling is specific to + // the client and server, we must give them to the specific client/server handler. + fn handle_control_frame(&mut self, f: HFrame) -> Res<Option<HFrame>> { + qinfo!([self], "Handle a control frame {:?}", f); + if !matches!(f, HFrame::Settings { .. }) + && !matches!(self.settings_state, Http3RemoteSettingsState::Received{..}) + { + return Err(Error::HttpMissingSettings); + } + match f { + HFrame::Settings { settings } => { + self.handle_settings(settings)?; + Ok(None) + } + HFrame::Goaway { .. } | HFrame::MaxPushId { .. } | HFrame::CancelPush { .. } => { + Ok(Some(f)) + } + _ => Err(Error::HttpFrameUnexpected), + } + } + + fn set_qpack_settings(&mut self, settings: &[HSetting]) -> Res<()> { + for s in settings { + qinfo!([self], " {:?} = {:?}", s.setting_type, s.value); + match s.setting_type { + HSettingType::MaxTableCapacity => self.qpack_encoder.set_max_capacity(s.value)?, + HSettingType::BlockedStreams => { + self.qpack_encoder.set_max_blocked_streams(s.value)? + } + HSettingType::MaxHeaderListSize => (), + } + } + Ok(()) + } + + fn handle_settings(&mut self, new_settings: HSettings) -> Res<()> { + qinfo!([self], "Handle SETTINGS frame."); + match &self.settings_state { + Http3RemoteSettingsState::NotReceived => { + self.set_qpack_settings(&new_settings)?; + self.settings_state = Http3RemoteSettingsState::Received(new_settings); + Ok(()) + } + Http3RemoteSettingsState::ZeroRtt(settings) => { + let mut qpack_changed = false; + for st in &[ + HSettingType::MaxHeaderListSize, + HSettingType::MaxTableCapacity, + HSettingType::BlockedStreams, + ] { + let zero_rtt_value = settings.get(*st); + let new_value = new_settings.get(*st); + if zero_rtt_value == new_value { + continue; + } + if zero_rtt_value > new_value { + qerror!( + [self], + "The new({}) and the old value({}) of setting {:?} do not match", + new_value, + zero_rtt_value, + st + ); + return Err(Error::HttpSettings); + } + + match st { + HSettingType::MaxTableCapacity => { + if zero_rtt_value != 0 { + return Err(Error::QpackError(neqo_qpack::Error::DecoderStream)); + } + qpack_changed = true; + } + HSettingType::BlockedStreams => qpack_changed = true, + _ => (), + } + } + if qpack_changed { + qdebug!([self], "Settings after zero rtt differ."); + self.set_qpack_settings(&(new_settings))?; + } + self.settings_state = Http3RemoteSettingsState::Received(new_settings); + Ok(()) + } + Http3RemoteSettingsState::Received { .. } => Err(Error::HttpFrameUnexpected), + } + } + + /// Return the current state on `Http3Connection`. + pub fn state(&self) -> Http3State { + self.state.clone() + } + + /// Adds a new send and receive stream. + pub fn add_streams( + &mut self, + stream_id: u64, + send_stream: SendMessage, + recv_stream: Box<dyn RecvStream>, + ) { + if send_stream.has_data_to_send() { + self.streams_have_data_to_send.insert(stream_id); + } + self.send_streams.insert(stream_id, send_stream); + self.recv_streams.insert(stream_id, recv_stream); + } + + /// Add a new recv stream. This is used for push streams. + pub fn add_recv_stream(&mut self, stream_id: u64, recv_stream: Box<dyn RecvStream>) { + self.recv_streams.insert(stream_id, recv_stream); + } + + pub fn queue_control_frame(&mut self, frame: &HFrame) { + self.control_stream_local.queue_frame(frame); + } +} diff --git a/third_party/rust/neqo-http3/src/connection_client.rs b/third_party/rust/neqo-http3/src/connection_client.rs new file mode 100644 index 0000000000..3366c5e0fe --- /dev/null +++ b/third_party/rust/neqo-http3/src/connection_client.rs @@ -0,0 +1,6164 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::client_events::{Http3ClientEvent, Http3ClientEvents}; +use crate::connection::{HandleReadableOutput, Http3Connection, Http3State}; +use crate::hframe::HFrame; +use crate::push_controller::PushController; +use crate::push_stream::PushStream; +use crate::recv_message::{MessageType, RecvMessage}; +use crate::send_message::{SendMessage, SendMessageEvents}; +use crate::settings::HSettings; +use crate::{Header, RecvMessageEvents, ResetType}; +use neqo_common::{ + event::Provider as EventProvider, hex, hex_with_len, qdebug, qinfo, qlog::NeqoQlog, qtrace, + Datagram, Decoder, Encoder, Role, +}; +use neqo_crypto::{agent::CertificateInfo, AuthenticationStatus, ResumptionToken, SecretAgentInfo}; +use neqo_qpack::{QpackSettings, Stats as QpackStats}; +use neqo_transport::{ + AppError, Connection, ConnectionEvent, ConnectionId, ConnectionIdManager, ConnectionParameters, + Output, QuicVersion, Stats as TransportStats, StreamId, StreamType, ZeroRttState, +}; +use std::cell::RefCell; +use std::fmt::Display; +use std::net::SocketAddr; +use std::rc::Rc; +use std::time::Instant; + +use crate::{Error, Res}; + +// This is used for filtering send_streams and recv_Streams with a stream_ids greater than or equal a given id. +// Only the same type (bidirectional or unidirectionsl) streams are filtered. +fn id_gte<U>(base: StreamId) -> impl FnMut((&u64, &U)) -> Option<u64> + 'static +where + U: ?Sized, +{ + move |(id, _)| { + if *id >= base.as_u64() && !(StreamId::from(*id).is_bidi() ^ base.is_bidi()) { + Some(*id) + } else { + None + } + } +} + +// This is used for filtering send_streams and recv_Streams with a stream_ids less than a given id. +// Only the same type (bidirectional or unidirectional) streams are filtered. +fn id_lt<U>(base: StreamId) -> impl FnMut(&u64, &mut U) -> bool +where + U: ?Sized, +{ + let mut f = id_gte(base); + move |id, v| f((id, v)).is_none() +} + +fn alpn_from_quic_version(version: QuicVersion) -> &'static str { + match version { + QuicVersion::Draft27 => "h3-27", + QuicVersion::Draft28 => "h3-28", + QuicVersion::Draft29 => "h3-29", + QuicVersion::Draft30 => "h3-30", + QuicVersion::Draft31 => "h3-31", + QuicVersion::Draft32 => "h3-32", + } +} + +pub struct Http3Parameters { + pub qpack_settings: QpackSettings, + pub max_concurrent_push_streams: u64, +} + +pub struct Http3Client { + conn: Connection, + base_handler: Http3Connection, + events: Http3ClientEvents, + push_handler: Rc<RefCell<PushController>>, +} + +impl Display for Http3Client { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Http3 client") + } +} + +impl Http3Client { + /// # Errors + /// Making a `neqo-transport::connection` may produce an error. This can only be a crypto error if + /// the socket can't be created or configured. + pub fn new( + server_name: &str, + cid_manager: Rc<RefCell<dyn ConnectionIdManager>>, + local_addr: SocketAddr, + remote_addr: SocketAddr, + conn_params: &ConnectionParameters, + http3_parameters: &Http3Parameters, + ) -> Res<Self> { + Ok(Self::new_with_conn( + Connection::new_client( + server_name, + &[alpn_from_quic_version(conn_params.get_quic_version())], + cid_manager, + local_addr, + remote_addr, + conn_params, + )?, + http3_parameters, + )) + } + + #[must_use] + pub fn new_with_conn(c: Connection, http3_parameters: &Http3Parameters) -> Self { + let events = Http3ClientEvents::default(); + Self { + conn: c, + base_handler: Http3Connection::new(http3_parameters.qpack_settings), + events: events.clone(), + push_handler: Rc::new(RefCell::new(PushController::new( + http3_parameters.max_concurrent_push_streams, + events, + ))), + } + } + + #[must_use] + pub fn role(&self) -> Role { + self.conn.role() + } + + #[must_use] + pub fn state(&self) -> Http3State { + self.base_handler.state() + } + + #[must_use] + pub fn tls_info(&self) -> Option<&SecretAgentInfo> { + self.conn.tls_info() + } + + /// Get the peer's certificate. + #[must_use] + pub fn peer_certificate(&self) -> Option<CertificateInfo> { + self.conn.peer_certificate() + } + + /// This called when peer certificates have been verified. + pub fn authenticated(&mut self, status: AuthenticationStatus, now: Instant) { + self.conn.authenticated(status, now); + } + + pub fn set_qlog(&mut self, qlog: NeqoQlog) { + self.conn.set_qlog(qlog); + } + + /// Get the connection id, which is useful for disambiguating connections to + /// the same origin. + #[must_use] + pub fn connection_id(&self) -> &ConnectionId { + &self.conn.odcid().expect("Client always has odcid") + } + + /// A resumption token encodes transport and settings parameter as well. + fn create_resumption_token(&mut self, token: &ResumptionToken) { + if let Some(settings) = self.base_handler.get_settings() { + let mut enc = Encoder::default(); + settings.encode_frame_contents(&mut enc); + enc.encode(token.as_ref()); + self.events + .resumption_token(ResumptionToken::new(enc.into(), token.expiration_time())); + } + } + + /// This may be call if an application has a resumption token. This must be called before connection starts. + /// # Errors + /// An error is return if token cannot be decoded or a connection is is a wrong state. + pub fn enable_resumption(&mut self, now: Instant, token: impl AsRef<[u8]>) -> Res<()> { + if self.base_handler.state != Http3State::Initializing { + return Err(Error::InvalidState); + } + let mut dec = Decoder::from(token.as_ref()); + let settings_slice = match dec.decode_vvec() { + Some(v) => v, + None => return Err(Error::InvalidResumptionToken), + }; + qtrace!([self], " settings {}", hex_with_len(&settings_slice)); + let mut dec_settings = Decoder::from(settings_slice); + let mut settings = HSettings::default(); + Error::map_error( + settings.decode_frame_contents(&mut dec_settings), + Error::InvalidResumptionToken, + )?; + let tok = dec.decode_remainder(); + qtrace!([self], " Transport token {}", hex(&tok)); + self.conn.enable_resumption(now, tok)?; + if self.conn.state().closed() { + let state = self.conn.state().clone(); + debug_assert!( + Ok(true) + == self + .base_handler + .handle_state_change(&mut self.conn, &state) + ); + return Err(Error::FatalError); + } + if *self.conn.zero_rtt_state() == ZeroRttState::Sending { + self.base_handler + .set_0rtt_settings(&mut self.conn, settings)?; + self.events + .connection_state_change(self.base_handler.state()); + self.push_handler + .borrow_mut() + .maybe_send_max_push_id_frame(&mut self.base_handler); + } + Ok(()) + } + + /// This is call to close a connection. + pub fn close<S>(&mut self, now: Instant, error: AppError, msg: S) + where + S: AsRef<str> + Display, + { + qinfo!([self], "Close the connection error={} msg={}.", error, msg); + if !matches!(self.base_handler.state, Http3State::Closing(_)| Http3State::Closed(_)) { + self.push_handler.borrow_mut().clear(); + self.conn.close(now, error, msg); + self.base_handler.close(error); + self.events + .connection_state_change(self.base_handler.state()); + } + } + + /// Attempt to force a key update. + /// # Errors + /// If the connection isn't confirmed, or there is an outstanding key update, this + /// returns `Err(Error::TransportError(neqo_transport::Error::KeyUpdateBlocked))`. + pub fn initiate_key_update(&mut self) -> Res<()> { + self.conn.initiate_key_update()?; + Ok(()) + } + + // API: Request/response + + /// This is call to make a new http request. Each request can have headers and they are added when request + /// is created. A response body may be added by calling `send_request_body`. + /// # Errors + /// If a new stream cannot be created an error will be return. + pub fn fetch( + &mut self, + now: Instant, + method: &str, + scheme: &str, + host: &str, + path: &str, + headers: &[Header], + ) -> Res<u64> { + qinfo!( + [self], + "Fetch method={}, scheme={}, host={}, path={}", + method, + scheme, + host, + path + ); + // Requests cannot be created when a connection is in states: Initializing, GoingAway, Closing and Closed. + match self.base_handler.state() { + Http3State::GoingAway(..) | Http3State::Closing(..) | Http3State::Closed(..) => { + return Err(Error::AlreadyClosed) + } + Http3State::Initializing => return Err(Error::Unavailable), + _ => {} + } + + let id = self + .conn + .stream_create(StreamType::BiDi) + .map_err(|e| Error::map_stream_create_errors(&e))?; + + // Transform pseudo-header fields + let mut final_headers = Vec::new(); + final_headers.push((":method".into(), method.to_owned())); + final_headers.push((":scheme".into(), scheme.to_owned())); + final_headers.push((":authority".into(), host.to_owned())); + final_headers.push((":path".into(), path.to_owned())); + final_headers.extend_from_slice(headers); + + self.base_handler.add_streams( + id, + SendMessage::new_with_headers(id, final_headers, Box::new(self.events.clone())), + Box::new(RecvMessage::new( + MessageType::Response, + id, + Box::new(self.events.clone()), + Some(self.push_handler.clone()), + )), + ); + + // Call immediately send so that at least headers get sent. This will make Firefox faster, since + // it can send request body immediatly in most cases and does not need to do a complete process loop. + if let Err(e) = self + .base_handler + .send_streams + .get_mut(&id) + .ok_or(Error::InvalidStreamId)? + .send(&mut self.conn, &mut self.base_handler.qpack_encoder) + { + if e.connection_error() { + self.close(now, e.code(), ""); + } + return Err(e); + } + + Ok(id) + } + + /// An application may reset a stream(request). + /// Both sides, sending and receiving side, will be closed. + /// # Errors + /// An error will be return if a stream does not exist. + pub fn stream_reset(&mut self, stream_id: u64, error: AppError) -> Res<()> { + qinfo!([self], "reset_stream {} error={}.", stream_id, error); + self.base_handler + .stream_reset(&mut self.conn, stream_id, error)?; + self.events.remove_events_for_stream_id(stream_id); + Ok(()) + } + + /// This is call when application is done sending a request. + /// # Errors + /// An error will be return if stream does not exist. + pub fn stream_close_send(&mut self, stream_id: u64) -> Res<()> { + qinfo!([self], "Close sending side stream={}.", stream_id); + self.base_handler + .stream_close_send(&mut self.conn, stream_id) + } + + /// To supply a request body this function is called (headers are supplied through the `fetch` function.) + /// # Errors + /// `InvalidStreamId` if thee stream does not exist, + /// `AlreadyClosed` if the stream has already been closed. + /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if `process_output` + /// has not been called when needed, and HTTP3 layer has not picked up the info that the stream has been closed.) + /// `InvalidInput` if an empty buffer has been supplied. + pub fn send_request_body(&mut self, stream_id: u64, buf: &[u8]) -> Res<usize> { + qinfo!( + [self], + "send_request_body from stream {} sending {} bytes.", + stream_id, + buf.len() + ); + self.base_handler + .send_streams + .get_mut(&stream_id) + .ok_or(Error::InvalidStreamId)? + .send_body(&mut self.conn, buf) + } + + /// Response data are read directly into a buffer supplied as a parameter of this function to avoid copying + /// data. + /// # Errors + /// It returns an error if a stream does not exist or an error happen while reading a stream, e.g. + /// early close, protocol error, etc. + pub fn read_response_data( + &mut self, + now: Instant, + stream_id: u64, + buf: &mut [u8], + ) -> Res<(usize, bool)> { + qinfo!([self], "read_data from stream {}.", stream_id); + let recv_stream = self + .base_handler + .recv_streams + .get_mut(&stream_id) + .ok_or(Error::InvalidStreamId)?; + + let res = recv_stream.read_data(&mut self.conn, &mut self.base_handler.qpack_decoder, buf); + match res { + Ok((amount, fin)) => { + if recv_stream.done() { + self.base_handler.recv_streams.remove(&stream_id); + } + Ok((amount, fin)) + } + Err(e) => { + if e.stream_reset_error() { + self.reset_stream_on_error(stream_id, e.code()); + Ok((0, false)) + } else if e.connection_error() { + self.close(now, e.code(), ""); + Err(e) + } else { + Err(e) + } + } + } + } + + // API: Push streams + + /// Cancel a push + /// # Errors + /// `InvalidStreamId` if the stream does not exist. + pub fn cancel_push(&mut self, push_id: u64) -> Res<()> { + self.push_handler + .borrow_mut() + .cancel(push_id, &mut self.conn, &mut self.base_handler) + } + + /// Push response data are read directly into a buffer supplied as a parameter of this function + /// to avoid copying data. + /// # Errors + /// It returns an error if a stream does not exist(`InvalidStreamId`) or an error has happened while + /// reading a stream, e.g. early close, protocol error, etc. + pub fn push_read_data( + &mut self, + now: Instant, + push_id: u64, + buf: &mut [u8], + ) -> Res<(usize, bool)> { + let stream_id = self.push_handler.borrow_mut().get_active_stream_id(push_id); + stream_id.map_or(Err(Error::InvalidStreamId), |id| { + self.read_response_data(now, id, buf) + }) + } + + pub fn process(&mut self, dgram: Option<Datagram>, now: Instant) -> Output { + qtrace!([self], "Process."); + if let Some(d) = dgram { + self.process_input(d, now); + } + self.process_output(now) + } + + /// Supply an incoming QUIC packet. + pub fn process_input(&mut self, dgram: Datagram, now: Instant) { + qtrace!([self], "Process input."); + self.conn.process_input(dgram, now); + self.process_http3(now); + } + + // Only used by neqo-interop + pub fn conn(&mut self) -> &mut Connection { + &mut self.conn + } + + /// Process HTTP3 layer. + fn process_http3(&mut self, now: Instant) { + qtrace!([self], "Process http3 internal."); + match self.base_handler.state() { + Http3State::ZeroRtt | Http3State::Connected | Http3State::GoingAway(..) => { + let res = self.check_connection_events(); + if self.check_result(now, &res) { + return; + } + self.push_handler + .borrow_mut() + .maybe_send_max_push_id_frame(&mut self.base_handler); + let res = self.base_handler.process_sending(&mut self.conn); + self.check_result(now, &res); + } + Http3State::Closed { .. } => {} + _ => { + let res = self.check_connection_events(); + let _ = self.check_result(now, &res); + } + } + } + + /// Get packet that need to be written into a UDP socket or a timer value if there is no data to send. + /// This function should be called repeatedly until timer value is returned. + pub fn process_output(&mut self, now: Instant) -> Output { + qtrace!([self], "Process output."); + + // Maybe send() stuff on http3-managed streams + self.process_http3(now); + + let out = self.conn.process_output(now); + + // Update H3 for any transport state changes and events + self.process_http3(now); + + out + } + + // This function takes the provided result and check for an error. + // An error results in closing the connection. + fn check_result<ERR>(&mut self, now: Instant, res: &Res<ERR>) -> bool { + match &res { + Err(Error::HttpGoaway) => { + qinfo!([self], "Connection error: goaway stream_id increased."); + self.close( + now, + Error::HttpGeneralProtocol.code(), + "Connection error: goaway stream_id increased", + ); + true + } + Err(e) => { + qinfo!([self], "Connection error: {}.", e); + self.close(now, e.code(), &format!("{}", e)); + true + } + _ => false, + } + } + + // If this return an error the connection must be closed. + fn check_connection_events(&mut self) -> Res<()> { + qtrace!([self], "Check connection events."); + while let Some(e) = self.conn.next_event() { + qdebug!([self], "check_connection_events - event {:?}.", e); + match e { + ConnectionEvent::NewStream { stream_id } => match stream_id.stream_type() { + StreamType::BiDi => return Err(Error::HttpStreamCreation), + StreamType::UniDi => { + if self + .base_handler + .handle_new_unidi_stream(&mut self.conn, stream_id.as_u64())? + { + // Do not force read from the stream because RecvStreamReadable will trigger a read. + // If we read the stream here we may post multiple read events. + self.handle_new_push_stream(stream_id.as_u64(), false)?; + } + } + }, + ConnectionEvent::SendStreamWritable { stream_id } => { + if let Some(s) = self.base_handler.send_streams.get_mut(&stream_id.as_u64()) { + s.stream_writable(); + } + } + ConnectionEvent::RecvStreamReadable { stream_id } => { + if let Err(e) = self.handle_stream_readable(stream_id) { + if e.stream_reset_error() { + self.reset_stream_on_error(stream_id, e.code()); + } else { + return Err(e); + } + } + } + ConnectionEvent::RecvStreamReset { + stream_id, + app_error, + } => self + .base_handler + .handle_stream_reset(stream_id, app_error)?, + ConnectionEvent::SendStreamStopSending { + stream_id, + app_error, + } => self + .base_handler + .handle_stream_stop_sending(stream_id, app_error)?, + ConnectionEvent::SendStreamComplete { .. } => {} + ConnectionEvent::SendStreamCreatable { stream_type } => { + self.events.new_requests_creatable(stream_type) + } + ConnectionEvent::AuthenticationNeeded => self.events.authentication_needed(), + ConnectionEvent::StateChange(state) => { + if self + .base_handler + .handle_state_change(&mut self.conn, &state)? + { + self.events + .connection_state_change(self.base_handler.state()); + } + } + ConnectionEvent::ZeroRttRejected => { + self.base_handler.handle_zero_rtt_rejected()?; + self.events.zero_rtt_rejected(); + self.push_handler.borrow_mut().handle_zero_rtt_rejected(); + } + ConnectionEvent::ResumptionToken(token) => { + self.create_resumption_token(&token); + } + } + } + Ok(()) + } + + fn handle_stream_readable(&mut self, stream_id: u64) -> Res<()> { + match self + .base_handler + .handle_stream_readable(&mut self.conn, stream_id)? + { + HandleReadableOutput::PushStream => self.handle_new_push_stream(stream_id, true), + HandleReadableOutput::ControlFrames(control_frames) => { + for f in control_frames { + match f { + HFrame::CancelPush { push_id } => self + .push_handler + .borrow_mut() + .handle_cancel_push(push_id, &mut self.conn, &mut self.base_handler), + HFrame::MaxPushId { .. } => Err(Error::HttpFrameUnexpected), + HFrame::Goaway { stream_id } => self.handle_goaway(stream_id), + _ => { + unreachable!( + "we should only put MaxPushId and Goaway into control_frames." + ); + } + }?; + } + Ok(()) + } + _ => Ok(()), + } + } + + fn handle_new_push_stream(&mut self, stream_id: u64, force_read: bool) -> Res<()> { + if self.push_handler.borrow().can_receive_push() { + self.base_handler.add_recv_stream( + stream_id, + Box::new(PushStream::new( + stream_id, + self.push_handler.clone(), + self.events.clone(), + )), + ); + if force_read { + let res2 = self + .base_handler + .handle_stream_readable(&mut self.conn, stream_id)?; + debug_assert!(matches!(res2, HandleReadableOutput::NoOutput)); + } + Ok(()) + } else { + Err(Error::HttpId) + } + } + + fn handle_goaway(&mut self, goaway_stream_id: u64) -> Res<()> { + qinfo!([self], "handle_goaway {}", goaway_stream_id); + + let id = StreamId::from(goaway_stream_id); + if id.is_uni() || id.is_server_initiated() { + return Err(Error::HttpId); + } + + match self.base_handler.state { + Http3State::Connected => { + self.base_handler.state = Http3State::GoingAway(goaway_stream_id); + } + Http3State::GoingAway(ref mut stream_id) => { + if goaway_stream_id > *stream_id { + return Err(Error::HttpGoaway); + } + *stream_id = goaway_stream_id; + } + Http3State::Closing(..) | Http3State::Closed(..) => {} + _ => unreachable!("Should not receive Goaway frame in this state."), + } + + let goaway_stream_id = StreamId::from(goaway_stream_id); + // Issue reset events for streams >= goaway stream id + for id in self + .base_handler + .send_streams + .iter() + .filter_map(id_gte(goaway_stream_id)) + { + self.events + .reset(id, Error::HttpRequestRejected.code(), false); + } + + for id in self + .base_handler + .recv_streams + .iter() + .filter_map(id_gte(goaway_stream_id)) + { + self.events + .stop_sending(id, Error::HttpRequestRejected.code()); + } + + self.events.goaway_received(); + + // Actually remove (i.e. don't retain) these streams + self.base_handler + .send_streams + .retain(id_lt(goaway_stream_id)); + + self.base_handler + .recv_streams + .retain(id_lt(goaway_stream_id)); + + Ok(()) + } + + #[must_use] + pub fn qpack_decoder_stats(&self) -> QpackStats { + self.base_handler.qpack_decoder.stats() + } + + #[must_use] + pub fn qpack_encoder_stats(&self) -> QpackStats { + self.base_handler.qpack_encoder.stats() + } + + #[must_use] + pub fn transport_stats(&self) -> TransportStats { + self.conn.stats() + } + + fn reset_stream_on_error(&mut self, stream_id: u64, app_error: AppError) { + let _ = self.conn.stream_stop_sending(stream_id, app_error); + if let Some(rs) = self.base_handler.recv_streams.remove(&stream_id) { + rs.stream_reset( + app_error, + &mut self.base_handler.qpack_decoder, + ResetType::Local, + ); + } + } +} + +impl EventProvider for Http3Client { + type Event = Http3ClientEvent; + + /// Return true if there are outstanding events. + fn has_events(&self) -> bool { + self.events.has_events() + } + + /// Get events that indicate state changes on the connection. This method + /// correctly handles cases where handling one event can obsolete + /// previously-queued events, or cause new events to be generated. + fn next_event(&mut self) -> Option<Self::Event> { + self.events.next_event() + } +} + +#[cfg(test)] +mod tests { + use super::{ + AuthenticationStatus, Connection, Error, HSettings, Header, Http3Client, Http3ClientEvent, + Http3Parameters, Http3State, QpackSettings, Rc, RefCell, StreamType, + }; + use crate::hframe::{HFrame, H3_FRAME_TYPE_SETTINGS, H3_RESERVED_FRAME_TYPES}; + use crate::settings::{HSetting, HSettingType, H3_RESERVED_SETTINGS}; + use crate::Http3Server; + use neqo_common::{event::Provider, Datagram, Decoder, Encoder}; + use neqo_crypto::{AllowZeroRtt, AntiReplay, ResumptionToken}; + use neqo_qpack::encoder::QPackEncoder; + use neqo_transport::tparams::{self, TransportParameter}; + use neqo_transport::{ + CloseError, ConnectionEvent, ConnectionParameters, FixedConnectionIdManager, Output, State, + RECV_BUFFER_SIZE, SEND_BUFFER_SIZE, + }; + use std::convert::TryFrom; + use std::time::Duration; + use test_fixture::{ + anti_replay, default_server_h3, fixture_init, loopback, now, DEFAULT_ALPN_H3, DEFAULT_KEYS, + DEFAULT_SERVER_NAME, + }; + + fn assert_closed(client: &Http3Client, expected: &Error) { + match client.state() { + Http3State::Closing(err) | Http3State::Closed(err) => { + assert_eq!(err, CloseError::Application(expected.code())) + } + _ => panic!("Wrong state {:?}", client.state()), + }; + } + + /// Create a http3 client with default configuration. + pub fn default_http3_client() -> Http3Client { + default_http3_client_param(100) + } + + pub fn default_http3_client_param(max_table_size: u64) -> Http3Client { + fixture_init(); + Http3Client::new( + DEFAULT_SERVER_NAME, + Rc::new(RefCell::new(FixedConnectionIdManager::new(3))), + loopback(), + loopback(), + &ConnectionParameters::default(), + &Http3Parameters { + qpack_settings: QpackSettings { + max_table_size_encoder: max_table_size, + max_table_size_decoder: max_table_size, + max_blocked_streams: 100, + }, + max_concurrent_push_streams: 5, + }, + ) + .expect("create a default client") + } + + const CONTROL_STREAM_TYPE: &[u8] = &[0x0]; + + // Encoder stream data + const ENCODER_STREAM_DATA: &[u8] = &[0x2]; + + // Encoder stream data with a change capacity instruction(0x3f, 0x45 = change capacity to 100) + // This data will be send when 0-RTT is used and we already have a max_table_capacity from + // resumed settings. + const ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION: &[u8] = &[0x2, 0x3f, 0x45]; + + const ENCODER_STREAM_DATA_WITH_CAP_INST_AND_ENCODING_INST: &[u8] = &[ + 0x2, 0x3f, 0x45, 0x67, 0xa7, 0xd4, 0xe5, 0x1c, 0x85, 0xb1, 0x1f, 0x86, 0xa7, 0xd7, 0x71, + 0xd1, 0x69, 0x7f, + ]; + + // Decoder stream data + const DECODER_STREAM_DATA: &[u8] = &[0x3]; + + const PUSH_STREAM_TYPE: &[u8] = &[0x1]; + + const CLIENT_SIDE_CONTROL_STREAM_ID: u64 = 2; + const CLIENT_SIDE_ENCODER_STREAM_ID: u64 = 6; + const CLIENT_SIDE_DECODER_STREAM_ID: u64 = 10; + + struct TestServer { + settings: HFrame, + conn: Connection, + control_stream_id: Option<u64>, + encoder: QPackEncoder, + encoder_stream_id: Option<u64>, + decoder_stream_id: Option<u64>, + } + + impl TestServer { + pub fn new() -> Self { + Self::new_with_settings(&[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ]) + } + + pub fn new_with_settings(server_settings: &[HSetting]) -> Self { + fixture_init(); + Self { + settings: HFrame::Settings { + settings: HSettings::new(server_settings), + }, + conn: default_server_h3(), + control_stream_id: None, + encoder: QPackEncoder::new( + QpackSettings { + max_table_size_encoder: 128, + max_table_size_decoder: 128, + max_blocked_streams: 0, + }, + true, + ), + encoder_stream_id: None, + decoder_stream_id: None, + } + } + + pub fn new_with_conn(conn: Connection) -> Self { + Self { + settings: HFrame::Settings { + settings: HSettings::new(&[]), + }, + conn, + control_stream_id: None, + encoder: QPackEncoder::new( + QpackSettings { + max_table_size_encoder: 128, + max_table_size_decoder: 128, + max_blocked_streams: 0, + }, + true, + ), + encoder_stream_id: None, + decoder_stream_id: None, + } + } + + pub fn create_qpack_streams(&mut self) { + // Create a QPACK encoder stream + self.encoder_stream_id = Some(self.conn.stream_create(StreamType::UniDi).unwrap()); + self.encoder + .add_send_stream(self.encoder_stream_id.unwrap()); + self.encoder.send(&mut self.conn).unwrap(); + + // Create decoder stream + self.decoder_stream_id = Some(self.conn.stream_create(StreamType::UniDi).unwrap()); + assert_eq!( + self.conn + .stream_send(self.decoder_stream_id.unwrap(), DECODER_STREAM_DATA) + .unwrap(), + 1 + ); + self.encoder + .add_recv_stream(CLIENT_SIDE_DECODER_STREAM_ID) + .unwrap(); + } + + pub fn create_control_stream(&mut self) { + // Create control stream + self.control_stream_id = Some(self.conn.stream_create(StreamType::UniDi).unwrap()); + // Send stream type on the control stream. + assert_eq!( + self.conn + .stream_send(self.control_stream_id.unwrap(), CONTROL_STREAM_TYPE) + .unwrap(), + 1 + ); + + // Encode a settings frame and send it. + let mut enc = Encoder::default(); + self.settings.encode(&mut enc); + assert_eq!( + self.conn + .stream_send(self.control_stream_id.unwrap(), &enc[..]) + .unwrap(), + enc[..].len() + ); + } + + pub fn check_client_control_qpack_streams_no_resumption(&mut self) { + self.check_client_control_qpack_streams( + ENCODER_STREAM_DATA, + EXPECTED_REQUEST_HEADER_FRAME, + false, + true, + ); + } + + pub fn check_control_qpack_request_streams_resumption( + &mut self, + expect_encoder_stream_data: &[u8], + expect_request_header: &[u8], + expect_request: bool, + ) { + self.check_client_control_qpack_streams( + expect_encoder_stream_data, + expect_request_header, + expect_request, + false, + ); + } + + // Check that server has received correct settings and qpack streams. + pub fn check_client_control_qpack_streams( + &mut self, + expect_encoder_stream_data: &[u8], + expect_request_header: &[u8], + expect_request: bool, + expect_connected: bool, + ) { + let mut connected = false; + let mut control_stream = false; + let mut qpack_decoder_stream = false; + let mut qpack_encoder_stream = false; + let mut request = false; + while let Some(e) = self.conn.next_event() { + match e { + ConnectionEvent::NewStream { stream_id } + | ConnectionEvent::SendStreamWritable { stream_id } => { + if expect_request { + assert!(matches!(stream_id.as_u64(), 2 | 6 | 10 | 0)); + } else { + assert!(matches!(stream_id.as_u64(), 2 | 6 | 10)); + } + } + ConnectionEvent::RecvStreamReadable { stream_id } => { + if stream_id == CLIENT_SIDE_CONTROL_STREAM_ID { + self.check_control_stream(); + control_stream = true; + } else if stream_id == CLIENT_SIDE_ENCODER_STREAM_ID { + // the qpack encoder stream + self.read_and_check_stream_data( + stream_id, + expect_encoder_stream_data, + false, + ); + qpack_encoder_stream = true; + } else if stream_id == CLIENT_SIDE_DECODER_STREAM_ID { + // the qpack decoder stream + self.read_and_check_stream_data(stream_id, DECODER_STREAM_DATA, false); + qpack_decoder_stream = true; + } else if stream_id == 0 { + assert!(expect_request); + self.read_and_check_stream_data(stream_id, expect_request_header, true); + request = true; + } else { + panic!("unexpected event"); + } + } + ConnectionEvent::StateChange(State::Connected) => connected = true, + ConnectionEvent::StateChange(_) => {} + _ => panic!("unexpected event"), + } + } + assert_eq!(connected, expect_connected); + assert!(control_stream); + assert!(qpack_encoder_stream); + assert!(qpack_decoder_stream); + assert_eq!(request, expect_request); + } + + // Check that the control stream contains default values. + // Expect a SETTINGS frame, some grease, and a MAX_PUSH_ID frame. + // The default test configuration uses: + // - max_table_capacity = 100 + // - max_blocked_streams = 100 + // and a maximum of 5 push streams. + fn check_control_stream(&mut self) { + let mut buf = [0_u8; 100]; + let (amount, fin) = self + .conn + .stream_recv(CLIENT_SIDE_CONTROL_STREAM_ID, &mut buf) + .unwrap(); + let mut dec = Decoder::from(&buf[..amount]); + assert_eq!(dec.decode_varint().unwrap(), 0); // control stream type + assert_eq!(dec.decode_varint().unwrap(), 4); // SETTINGS + assert_eq!(dec.decode_vvec().unwrap(), &[1, 0x40, 0x64, 7, 0x40, 0x64]); + + assert_eq!((dec.decode_varint().unwrap() - 0x21) % 0x1f, 0); // Grease + assert!(dec.decode_vvec().unwrap().len() < 8); + + assert_eq!(dec.decode_varint().unwrap(), 0xd); // MAX_PUSH_ID + assert_eq!(dec.decode_vvec().unwrap(), &[5]); + + assert_eq!(dec.remaining(), 0); + assert!(!fin); + } + + pub fn read_and_check_stream_data( + &mut self, + stream_id: u64, + expected_data: &[u8], + expected_fin: bool, + ) { + let mut buf = [0_u8; 100]; + let (amount, fin) = self.conn.stream_recv(stream_id, &mut buf).unwrap(); + assert_eq!(fin, expected_fin); + assert_eq!(amount, expected_data.len()); + assert_eq!(&buf[..amount], expected_data); + } + + pub fn encode_headers( + &mut self, + stream_id: u64, + headers: &[Header], + encoder: &mut Encoder, + ) { + let header_block = self + .encoder + .encode_header_block(&mut self.conn, &headers, stream_id) + .unwrap(); + let hframe = HFrame::Headers { + header_block: header_block.to_vec(), + }; + hframe.encode(encoder); + } + + pub fn set_max_uni_stream(&mut self, max_stream: u64) { + self.conn + .set_local_tparam( + tparams::INITIAL_MAX_STREAMS_UNI, + TransportParameter::Integer(max_stream), + ) + .unwrap(); + } + } + + fn handshake_only(client: &mut Http3Client, server: &mut TestServer) -> Output { + assert_eq!(client.state(), Http3State::Initializing); + let out = client.process(None, now()); + assert_eq!(client.state(), Http3State::Initializing); + + assert_eq!(*server.conn.state(), State::Init); + let out = server.conn.process(out.dgram(), now()); + assert_eq!(*server.conn.state(), State::Handshaking); + + let out = client.process(out.dgram(), now()); + let out = server.conn.process(out.dgram(), now()); + assert!(out.as_dgram_ref().is_none()); + + let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded); + assert!(client.events().any(authentication_needed)); + client.authenticated(AuthenticationStatus::Ok, now()); + out + } + + // Perform only Quic transport handshake. + fn connect_only_transport_with(client: &mut Http3Client, server: &mut TestServer) { + let out = handshake_only(client, server); + + let out = client.process(out.dgram(), now()); + let connected = |e| matches!(e, Http3ClientEvent::StateChange(Http3State::Connected)); + assert!(client.events().any(connected)); + + assert_eq!(client.state(), Http3State::Connected); + let _ = server.conn.process(out.dgram(), now()); + assert!(server.conn.state().connected()); + } + + // Perform only Quic transport handshake. + fn connect_only_transport() -> (Http3Client, TestServer) { + let mut client = default_http3_client(); + let mut server = TestServer::new(); + connect_only_transport_with(&mut client, &mut server); + (client, server) + } + + fn send_and_receive_client_settings(client: &mut Http3Client, server: &mut TestServer) { + // send and receive client settings + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + server.check_client_control_qpack_streams_no_resumption(); + } + + // Perform Quic transport handshake and exchange Http3 settings. + fn connect_with(client: &mut Http3Client, server: &mut TestServer) { + connect_only_transport_with(client, server); + + send_and_receive_client_settings(client, server); + + server.create_control_stream(); + + server.create_qpack_streams(); + // Send the server's control and qpack streams data. + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + // assert no error occured. + assert_eq!(client.state(), Http3State::Connected); + } + + // Perform Quic transport handshake and exchange Http3 settings. + fn connect() -> (Http3Client, TestServer) { + let mut client = default_http3_client(); + let mut server = TestServer::new(); + connect_with(&mut client, &mut server); + (client, server) + } + + // Fetch request fetch("GET", "https", "something.com", "/", headers). + fn make_request(client: &mut Http3Client, close_sending_side: bool, headers: &[Header]) -> u64 { + let request_stream_id = client + .fetch(now(), "GET", "https", "something.com", "/", headers) + .unwrap(); + if close_sending_side { + let _ = client.stream_close_send(request_stream_id); + } + request_stream_id + } + + // For fetch request fetch("GET", "https", "something.com", "/", &[]) + // the following request header frame will be sent: + const EXPECTED_REQUEST_HEADER_FRAME: &[u8] = &[ + 0x01, 0x10, 0x00, 0x00, 0xd1, 0xd7, 0x50, 0x89, 0x41, 0xe9, 0x2a, 0x67, 0x35, 0x53, 0x2e, + 0x43, 0xd3, 0xc1, + ]; + + // For fetch request fetch("GET", "https", "something.com", "/", &[(String::from("myheaders"), String::from("myvalue"))]) + // the following request header frame will be sent: + const EXPECTED_REQUEST_HEADER_FRAME_VERSION2: &[u8] = &[ + 0x01, 0x11, 0x02, 0x80, 0xd1, 0xd7, 0x50, 0x89, 0x41, 0xe9, 0x2a, 0x67, 0x35, 0x53, 0x2e, + 0x43, 0xd3, 0xc1, 0x10, + ]; + + const HTTP_HEADER_FRAME_0: &[u8] = &[0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x30]; + + // The response header from HTTP_HEADER_FRAME (0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x30) are + // decoded into: + fn check_response_header_0(header: &[Header]) { + let expected_response_header_0 = &[ + (String::from(":status"), String::from("200")), + (String::from("content-length"), String::from("0")), + ]; + assert_eq!(header, expected_response_header_0); + } + + const HTTP_RESPONSE_1: &[u8] = &[ + // headers + 0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x37, // the first data frame + 0x0, 0x3, 0x61, 0x62, 0x63, // the second data frame + 0x0, 0x4, 0x64, 0x65, 0x66, 0x67, + ]; + + const HTTP_RESPONSE_HEADER_ONLY_1: &[u8] = &[ + // headers + 0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x37, + ]; + const HTTP_RESPONSE_DATA_FRAME_1_ONLY_1: &[u8] = &[0x0, 0x3, 0x61, 0x62, 0x63]; + + const HTTP_RESPONSE_DATA_FRAME_2_ONLY_1: &[u8] = &[0x0, 0x4, 0x64, 0x65, 0x66, 0x67]; + + // The response header from HTTP_RESPONSE_1 (0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x36) are + // decoded into: + fn check_response_header_1(header: &[Header]) { + let expected_response_header_1 = &[ + (String::from(":status"), String::from("200")), + (String::from("content-length"), String::from("7")), + ]; + assert_eq!(header, expected_response_header_1); + } + + const EXPECTED_RESPONSE_DATA_1: &[u8] = &[0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67]; + + const HTTP_RESPONSE_2: &[u8] = &[ + // headers + 0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x33, // the data frame + 0x0, 0x3, 0x61, 0x62, 0x63, + ]; + + const HTTP_RESPONSE_HEADER_ONLY_2: &[u8] = &[ + // headers + 0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x33, + ]; + + const HTTP_RESPONSE_DATA_FRAME_ONLY_2: &[u8] = &[ + // the data frame + 0x0, 0x3, 0x61, 0x62, 0x63, + ]; + + // The response header from HTTP_RESPONSE_2 (0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x36) are + // decoded into: + fn check_response_header_2(header: &[Header]) { + let expected_response_header_2 = &[ + (String::from(":status"), String::from("200")), + (String::from("content-length"), String::from("3")), + ]; + assert_eq!(header, expected_response_header_2); + } + + // The data frame payload from HTTP_RESPONSE_2 is: + const EXPECTED_RESPONSE_DATA_2_FRAME_1: &[u8] = &[0x61, 0x62, 0x63]; + + fn make_request_and_exchange_pkts( + client: &mut Http3Client, + server: &mut TestServer, + close_sending_side: bool, + ) -> u64 { + let request_stream_id = make_request(client, close_sending_side, &[]); + + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + + // find the new request/response stream and send frame v on it. + while let Some(e) = server.conn.next_event() { + match e { + ConnectionEvent::NewStream { stream_id } => { + assert_eq!(stream_id.as_u64(), request_stream_id); + assert_eq!(stream_id.stream_type(), StreamType::BiDi); + } + ConnectionEvent::RecvStreamReadable { stream_id } => { + assert_eq!(stream_id, request_stream_id); + server.read_and_check_stream_data( + stream_id, + EXPECTED_REQUEST_HEADER_FRAME, + close_sending_side, + ); + } + _ => {} + } + } + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + request_stream_id + } + + fn connect_and_send_request(close_sending_side: bool) -> (Http3Client, TestServer, u64) { + let (mut client, mut server) = connect(); + let request_stream_id = + make_request_and_exchange_pkts(&mut client, &mut server, close_sending_side); + assert_eq!(request_stream_id, 0); + + (client, server, request_stream_id) + } + + fn server_send_response_and_exchange_packet( + client: &mut Http3Client, + server: &mut TestServer, + stream_id: u64, + response: &[u8], + close_stream: bool, + ) { + let _ = server.conn.stream_send(stream_id, response).unwrap(); + if close_stream { + server.conn.stream_close_send(stream_id).unwrap(); + } + let out = server.conn.process(None, now()); + let out = client.process(out.dgram(), now()); + let _ = server.conn.process(out.dgram(), now()); + } + + const PUSH_PROMISE_DATA: &[u8] = &[ + 0x00, 0x00, 0xd1, 0xd7, 0x50, 0x89, 0x41, 0xe9, 0x2a, 0x67, 0x35, 0x53, 0x2e, 0x43, 0xd3, + 0xc1, + ]; + + fn check_pushpromise_header(header: &[Header]) { + let expected_response_header_1 = &[ + (String::from(":method"), String::from("GET")), + (String::from(":scheme"), String::from("https")), + (String::from(":authority"), String::from("something.com")), + (String::from(":path"), String::from("/")), + ]; + assert_eq!(header, expected_response_header_1); + } + + // Send a push promise with push_id and request_stream_id. + fn send_push_promise(conn: &mut Connection, stream_id: u64, push_id: u64) { + let frame = HFrame::PushPromise { + push_id, + header_block: PUSH_PROMISE_DATA.to_vec(), + }; + let mut d = Encoder::default(); + frame.encode(&mut d); + let _ = conn.stream_send(stream_id, &d).unwrap(); + } + + fn send_push_data_and_exchange_packets( + client: &mut Http3Client, + server: &mut TestServer, + push_id: u8, + close_push_stream: bool, + ) -> u64 { + let push_stream_id = send_push_data(&mut server.conn, push_id, close_push_stream); + + let out = server.conn.process(None, now()); + let out = client.process(out.dgram(), now()); + let _ = server.conn.process(out.dgram(), now()); + + push_stream_id + } + + fn send_push_promise_and_exchange_packets( + client: &mut Http3Client, + server: &mut TestServer, + stream_id: u64, + push_id: u64, + ) { + send_push_promise(&mut server.conn, stream_id, push_id); + + let out = server.conn.process(None, now()); + let out = client.process(out.dgram(), now()); + let _ = server.conn.process(out.dgram(), now()); + } + + fn send_cancel_push_and_exchange_packets( + client: &mut Http3Client, + server: &mut TestServer, + push_id: u64, + ) { + let frame = HFrame::CancelPush { push_id }; + let mut d = Encoder::default(); + frame.encode(&mut d); + server + .conn + .stream_send(server.control_stream_id.unwrap(), &d) + .unwrap(); + + let out = server.conn.process(None, now()); + let out = client.process(out.dgram(), now()); + let _ = server.conn.process(out.dgram(), now()); + } + + const PUSH_DATA: &[u8] = &[ + // headers + 0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x34, // the data frame. + 0x0, 0x4, 0x61, 0x62, 0x63, 0x64, + ]; + + // The response header from PUSH_DATA (0x01, 0x06, 0x00, 0x00, 0xd9, 0x54, 0x01, 0x34) are + // decoded into: + fn check_push_response_header(header: &[Header]) { + let expected_push_response_header = vec![ + (String::from(":status"), String::from("200")), + (String::from("content-length"), String::from("4")), + ]; + assert_eq!(header, &expected_push_response_header[..]); + } + + // The data frame payload from PUSH_DATA is: + const EXPECTED_PUSH_RESPONSE_DATA_FRAME: &[u8] = &[0x61, 0x62, 0x63, 0x64]; + + // Send push data on a push stream: + // 1) push_stream_type PUSH_STREAM_TYPE + // 2) push_id + // 3) PUSH_DATA that contains encoded headers and a data frame. + // This function can only handle small push_id numbers that fit in a varint of length 1 byte. + fn send_data_on_push( + conn: &mut Connection, + push_stream_id: u64, + push_id: u8, + data: &[u8], + close_push_stream: bool, + ) { + // send data + let _ = conn.stream_send(push_stream_id, PUSH_STREAM_TYPE).unwrap(); + let _ = conn.stream_send(push_stream_id, &[push_id]).unwrap(); + let _ = conn.stream_send(push_stream_id, data).unwrap(); + if close_push_stream { + conn.stream_close_send(push_stream_id).unwrap(); + } + } + + // Send push data on a push stream: + // 1) push_stream_type PUSH_STREAM_TYPE + // 2) push_id + // 3) PUSH_DATA that contains encoded headers and a data frame. + // This function can only handle small push_id numbers that fit in a varint of length 1 byte. + fn send_push_data(conn: &mut Connection, push_id: u8, close_push_stream: bool) -> u64 { + send_push_with_data(conn, push_id, PUSH_DATA, close_push_stream) + } + + // Send push data on a push stream: + // 1) push_stream_type PUSH_STREAM_TYPE + // 2) push_id + // 3) and supplied push data. + // This function can only handle small push_id numbers that fit in a varint of length 1 byte. + fn send_push_with_data( + conn: &mut Connection, + push_id: u8, + data: &[u8], + close_push_stream: bool, + ) -> u64 { + // create a push stream + let push_stream_id = conn.stream_create(StreamType::UniDi).unwrap(); + // send data + send_data_on_push(conn, push_stream_id, push_id, data, close_push_stream); + push_stream_id + } + + struct PushPromiseInfo { + pub push_id: u64, + pub ref_stream_id: u64, + } + + // Helper function: read response when a server sends: + // - HTTP_RESPONSE_2 on the request_stream_id stream, + // - a number of push promises described by a list of PushPromiseInfo. + // - and a push streams with push_id in the push_streams list. + // All push stream contain PUSH_DATA that decodes to headers (that can be checked by calling + // check_push_response_header) and EXPECTED_PUSH_RESPONSE_DATA_FRAME + fn read_response_and_push_events( + client: &mut Http3Client, + push_promises: &[PushPromiseInfo], + push_streams: &[u64], + response_stream_id: u64, + ) { + let mut num_push_promises = 0; + let mut num_push_stream_headers = 0; + let mut num_push_stream_data = 0; + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::PushPromise { + push_id, + request_stream_id, + headers, + } => { + assert!(push_promises + .iter() + .any(|p| p.push_id == push_id && p.ref_stream_id == request_stream_id)); + check_pushpromise_header(&headers[..]); + num_push_promises += 1; + } + Http3ClientEvent::PushHeaderReady { + push_id, + headers, + interim, + fin, + } => { + assert!(push_streams.contains(&push_id)); + check_push_response_header(&headers); + num_push_stream_headers += 1; + assert_eq!(fin, false); + assert_eq!(interim, false); + } + Http3ClientEvent::PushDataReadable { push_id } => { + assert!(push_streams.contains(&push_id)); + let mut buf = [0_u8; 100]; + let (amount, fin) = client.push_read_data(now(), push_id, &mut buf).unwrap(); + assert_eq!(fin, true); + assert_eq!(amount, EXPECTED_PUSH_RESPONSE_DATA_FRAME.len()); + assert_eq!(&buf[..amount], EXPECTED_PUSH_RESPONSE_DATA_FRAME); + num_push_stream_data += 1; + } + Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } => { + assert_eq!(stream_id, response_stream_id); + check_response_header_2(&headers); + assert_eq!(fin, false); + assert_eq!(interim, false); + } + Http3ClientEvent::DataReadable { stream_id } => { + assert_eq!(stream_id, response_stream_id); + let mut buf = [0_u8; 100]; + let (amount, _) = client + .read_response_data(now(), stream_id, &mut buf) + .unwrap(); + assert_eq!(amount, EXPECTED_RESPONSE_DATA_2_FRAME_1.len()); + assert_eq!(&buf[..amount], EXPECTED_RESPONSE_DATA_2_FRAME_1); + } + _ => {} + } + } + + assert_eq!(num_push_promises, push_promises.len()); + assert_eq!(num_push_stream_headers, push_streams.len()); + assert_eq!(num_push_stream_data, push_streams.len()); + } + + // Client: Test receiving a new control stream and a SETTINGS frame. + #[test] + fn test_client_connect_and_exchange_qpack_and_control_streams() { + let _ = connect(); + } + + // Client: Test that the connection will be closed if control stream + // has been closed. + #[test] + fn test_client_close_control_stream() { + let (mut client, mut server) = connect(); + server + .conn + .stream_close_send(server.control_stream_id.unwrap()) + .unwrap(); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + assert_closed(&client, &Error::HttpClosedCriticalStream); + } + + // Client: Test that the connection will be closed if the local control stream + // has been reset. + #[test] + fn test_client_reset_control_stream() { + let (mut client, mut server) = connect(); + server + .conn + .stream_reset_send(server.control_stream_id.unwrap(), Error::HttpNoError.code()) + .unwrap(); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + assert_closed(&client, &Error::HttpClosedCriticalStream); + } + + // Client: Test that the connection will be closed if the server side encoder stream + // has been reset. + #[test] + fn test_client_reset_server_side_encoder_stream() { + let (mut client, mut server) = connect(); + server + .conn + .stream_reset_send(server.encoder_stream_id.unwrap(), Error::HttpNoError.code()) + .unwrap(); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + assert_closed(&client, &Error::HttpClosedCriticalStream); + } + + // Client: Test that the connection will be closed if the server side decoder stream + // has been reset. + #[test] + fn test_client_reset_server_side_decoder_stream() { + let (mut client, mut server) = connect(); + server + .conn + .stream_reset_send(server.decoder_stream_id.unwrap(), Error::HttpNoError.code()) + .unwrap(); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + assert_closed(&client, &Error::HttpClosedCriticalStream); + } + + // Client: Test that the connection will be closed if the local control stream + // has received a stop_sending. + #[test] + fn test_client_stop_sending_control_stream() { + let (mut client, mut server) = connect(); + server + .conn + .stream_stop_sending(CLIENT_SIDE_CONTROL_STREAM_ID, Error::HttpNoError.code()) + .unwrap(); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + assert_closed(&client, &Error::HttpClosedCriticalStream); + } + + // Client: Test that the connection will be closed if the client side encoder stream + // has received a stop_sending. + #[test] + fn test_client_stop_sending_encoder_stream() { + let (mut client, mut server) = connect(); + server + .conn + .stream_stop_sending(CLIENT_SIDE_ENCODER_STREAM_ID, Error::HttpNoError.code()) + .unwrap(); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + assert_closed(&client, &Error::HttpClosedCriticalStream); + } + + // Client: Test that the connection will be closed if the client side decoder stream + // has received a stop_sending. + #[test] + fn test_client_stop_sending_decoder_stream() { + let (mut client, mut server) = connect(); + server + .conn + .stream_stop_sending(CLIENT_SIDE_DECODER_STREAM_ID, Error::HttpNoError.code()) + .unwrap(); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + assert_closed(&client, &Error::HttpClosedCriticalStream); + } + + // Client: test missing SETTINGS frame + // (the first frame sent is a garbage frame). + #[test] + fn test_client_missing_settings() { + let (mut client, mut server) = connect_only_transport(); + // Create server control stream. + let control_stream = server.conn.stream_create(StreamType::UniDi).unwrap(); + // Send a HEADERS frame instead (which contains garbage). + let sent = server + .conn + .stream_send(control_stream, &[0x0, 0x1, 0x3, 0x0, 0x1, 0x2]); + assert_eq!(sent, Ok(6)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + assert_closed(&client, &Error::HttpMissingSettings); + } + + // Client: receiving SETTINGS frame twice causes connection close + // with error HTTP_UNEXPECTED_FRAME. + #[test] + fn test_client_receive_settings_twice() { + let (mut client, mut server) = connect(); + // send the second SETTINGS frame. + let sent = server.conn.stream_send( + server.control_stream_id.unwrap(), + &[0x4, 0x6, 0x1, 0x40, 0x64, 0x7, 0x40, 0x64], + ); + assert_eq!(sent, Ok(8)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + assert_closed(&client, &Error::HttpFrameUnexpected); + } + + fn test_wrong_frame_on_control_stream(v: &[u8]) { + let (mut client, mut server) = connect(); + + // send a frame that is not allowed on the control stream. + let _ = server + .conn + .stream_send(server.control_stream_id.unwrap(), v); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + assert_closed(&client, &Error::HttpFrameUnexpected); + } + + // send DATA frame on a cortrol stream + #[test] + fn test_data_frame_on_control_stream() { + test_wrong_frame_on_control_stream(&[0x0, 0x2, 0x1, 0x2]); + } + + // send HEADERS frame on a cortrol stream + #[test] + fn test_headers_frame_on_control_stream() { + test_wrong_frame_on_control_stream(&[0x1, 0x2, 0x1, 0x2]); + } + + // send PUSH_PROMISE frame on a cortrol stream + #[test] + fn test_push_promise_frame_on_control_stream() { + test_wrong_frame_on_control_stream(&[0x5, 0x2, 0x1, 0x2]); + } + + fn test_wrong_frame_on_push_stream(v: &[u8]) { + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + send_push_promise(&mut server.conn, request_stream_id, 0); + // Create a push stream + let push_stream_id = server.conn.stream_create(StreamType::UniDi).unwrap(); + + // Send the push stream type byte, push_id and frame v. + let _ = server + .conn + .stream_send(push_stream_id, &[0x01, 0x0]) + .unwrap(); + let _ = server.conn.stream_send(push_stream_id, v).unwrap(); + + let out = server.conn.process(None, now()); + let out = client.process(out.dgram(), now()); + let _ = server.conn.process(out.dgram(), now()); + + assert_closed(&client, &Error::HttpFrameUnexpected); + } + + #[test] + fn test_cancel_push_frame_on_push_stream() { + test_wrong_frame_on_push_stream(&[0x3, 0x1, 0x5]); + } + + #[test] + fn test_settings_frame_on_push_stream() { + test_wrong_frame_on_push_stream(&[0x4, 0x4, 0x6, 0x4, 0x8, 0x4]); + } + + #[test] + fn test_push_promise_frame_on_push_stream() { + test_wrong_frame_on_push_stream(&[0x5, 0x2, 0x1, 0x2]); + } + + #[test] + fn test_goaway_frame_on_push_stream() { + test_wrong_frame_on_push_stream(&[0x7, 0x1, 0x5]); + } + + #[test] + fn test_max_push_id_frame_on_push_stream() { + test_wrong_frame_on_push_stream(&[0xd, 0x1, 0x5]); + } + + // send DATA frame before a header frame + #[test] + fn test_data_frame_on_push_stream() { + test_wrong_frame_on_push_stream(&[0x0, 0x2, 0x1, 0x2]); + } + + // Client: receive unknown stream type + // This function also tests getting stream id that does not fit into a single byte. + #[test] + fn test_client_received_unknown_stream() { + let (mut client, mut server) = connect(); + + // create a stream with unknown type. + let new_stream_id = server.conn.stream_create(StreamType::UniDi).unwrap(); + let _ = server + .conn + .stream_send(new_stream_id, &[0x41, 0x19, 0x4, 0x4, 0x6, 0x0, 0x8, 0x0]); + let out = server.conn.process(None, now()); + let out = client.process(out.dgram(), now()); + let _ = server.conn.process(out.dgram(), now()); + + // check for stop-sending with Error::HttpStreamCreation. + let mut stop_sending_event_found = false; + while let Some(e) = server.conn.next_event() { + if let ConnectionEvent::SendStreamStopSending { + stream_id, + app_error, + } = e + { + stop_sending_event_found = true; + assert_eq!(stream_id, new_stream_id); + assert_eq!(app_error, Error::HttpStreamCreation.code()); + } + } + assert!(stop_sending_event_found); + assert_eq!(client.state(), Http3State::Connected); + } + + // Test wrong frame on req/rec stream + fn test_wrong_frame_on_request_stream(v: &[u8]) { + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + let _ = server.conn.stream_send(request_stream_id, v); + + // Generate packet with the above bad h3 input + let out = server.conn.process(None, now()); + // Process bad input and close the connection. + let _ = client.process(out.dgram(), now()); + + assert_closed(&client, &Error::HttpFrameUnexpected); + } + + #[test] + fn test_cancel_push_frame_on_request_stream() { + test_wrong_frame_on_request_stream(&[0x3, 0x1, 0x5]); + } + + #[test] + fn test_settings_frame_on_request_stream() { + test_wrong_frame_on_request_stream(&[0x4, 0x4, 0x6, 0x4, 0x8, 0x4]); + } + + #[test] + fn test_goaway_frame_on_request_stream() { + test_wrong_frame_on_request_stream(&[0x7, 0x1, 0x5]); + } + + #[test] + fn test_max_push_id_frame_on_request_stream() { + test_wrong_frame_on_request_stream(&[0xd, 0x1, 0x5]); + } + + // Test reading of a slowly streamed frame. bytes are received one by one + #[test] + fn test_frame_reading() { + let (mut client, mut server) = connect_only_transport(); + + // create a control stream. + let control_stream = server.conn.stream_create(StreamType::UniDi).unwrap(); + + // send the stream type + let mut sent = server.conn.stream_send(control_stream, &[0x0]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + // start sending SETTINGS frame + sent = server.conn.stream_send(control_stream, &[0x4]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + sent = server.conn.stream_send(control_stream, &[0x4]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + sent = server.conn.stream_send(control_stream, &[0x6]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + sent = server.conn.stream_send(control_stream, &[0x0]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + sent = server.conn.stream_send(control_stream, &[0x8]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + sent = server.conn.stream_send(control_stream, &[0x0]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + assert_eq!(client.state(), Http3State::Connected); + + // Now test PushPromise + sent = server.conn.stream_send(control_stream, &[0x5]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + sent = server.conn.stream_send(control_stream, &[0x5]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + sent = server.conn.stream_send(control_stream, &[0x4]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + sent = server.conn.stream_send(control_stream, &[0x61]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + sent = server.conn.stream_send(control_stream, &[0x62]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + sent = server.conn.stream_send(control_stream, &[0x63]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + sent = server.conn.stream_send(control_stream, &[0x64]); + assert_eq!(sent, Ok(1)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + // PUSH_PROMISE on a control stream will cause an error + assert_closed(&client, &Error::HttpFrameUnexpected); + } + + #[test] + fn fetch_basic() { + // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // send response - 200 Content-Length: 7 + // with content: 'abcdefg'. + // The content will be send in 2 DATA frames. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_1, + true, + ); + + let http_events = client.events().collect::<Vec<_>>(); + assert_eq!(http_events.len(), 2); + for e in http_events { + match e { + Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } => { + assert_eq!(stream_id, request_stream_id); + check_response_header_1(&headers); + assert_eq!(fin, false); + assert_eq!(interim, false); + } + Http3ClientEvent::DataReadable { stream_id } => { + assert_eq!(stream_id, request_stream_id); + let mut buf = [0_u8; 100]; + let (amount, fin) = client + .read_response_data(now(), stream_id, &mut buf) + .unwrap(); + assert_eq!(fin, true); + assert_eq!(amount, EXPECTED_RESPONSE_DATA_1.len()); + assert_eq!(&buf[..amount], EXPECTED_RESPONSE_DATA_1); + } + _ => {} + } + } + + // after this stream will be removed from hcoon. We will check this by trying to read + // from the stream and that should fail. + let mut buf = [0_u8; 100]; + let res = client.read_response_data(now(), request_stream_id, &mut buf); + assert_eq!(res.unwrap_err(), Error::InvalidStreamId); + + client.close(now(), 0, ""); + } + + // Helper function: read response when a server sends HTTP_RESPONSE_2. + fn read_response(client: &mut Http3Client, server: &mut Connection, request_stream_id: u64) { + let out = server.process(None, now()); + client.process(out.dgram(), now()); + + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } => { + assert_eq!(stream_id, request_stream_id); + check_response_header_2(&headers); + assert_eq!(fin, false); + assert_eq!(interim, false); + } + Http3ClientEvent::DataReadable { stream_id } => { + assert_eq!(stream_id, request_stream_id); + let mut buf = [0_u8; 100]; + let (amount, fin) = client + .read_response_data(now(), stream_id, &mut buf) + .unwrap(); + assert_eq!(fin, true); + assert_eq!(amount, EXPECTED_RESPONSE_DATA_2_FRAME_1.len()); + assert_eq!(&buf[..amount], EXPECTED_RESPONSE_DATA_2_FRAME_1); + } + _ => {} + } + } + + // after this stream will be removed from client. We will check this by trying to read + // from the stream and that should fail. + let mut buf = [0_u8; 100]; + let res = client.read_response_data(now(), request_stream_id, &mut buf); + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), Error::InvalidStreamId); + + client.close(now(), 0, ""); + } + + // Data sent with a request: + const REQUEST_BODY: &[u8] = &[0x64, 0x65, 0x66]; + // Corresponding data frame that server will receive. + const EXPECTED_REQUEST_BODY_FRAME: &[u8] = &[0x0, 0x3, 0x64, 0x65, 0x66]; + + // Send a request with the request body. + #[test] + fn fetch_with_data() { + // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + // Get DataWritable for the request stream so that we can write the request body. + let data_writable = |e| matches!(e, Http3ClientEvent::DataWritable { .. }); + assert!(client.events().any(data_writable)); + let sent = client + .send_request_body(request_stream_id, REQUEST_BODY) + .unwrap(); + assert_eq!(sent, REQUEST_BODY.len()); + let _ = client.stream_close_send(request_stream_id); + + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + + // find the new request/response stream and send response on it. + while let Some(e) = server.conn.next_event() { + match e { + ConnectionEvent::NewStream { stream_id } => { + assert_eq!(stream_id.as_u64(), request_stream_id); + assert_eq!(stream_id.stream_type(), StreamType::BiDi); + } + ConnectionEvent::RecvStreamReadable { stream_id } => { + assert_eq!(stream_id, request_stream_id); + + // Read request body. + let mut buf = [0_u8; 100]; + let (amount, fin) = server.conn.stream_recv(stream_id, &mut buf).unwrap(); + assert_eq!(fin, true); + assert_eq!(amount, EXPECTED_REQUEST_BODY_FRAME.len()); + assert_eq!(&buf[..amount], EXPECTED_REQUEST_BODY_FRAME); + + // send response - 200 Content-Length: 3 + // with content: 'abc'. + let _ = server.conn.stream_send(stream_id, HTTP_RESPONSE_2); + server.conn.stream_close_send(stream_id).unwrap(); + } + _ => {} + } + } + + read_response(&mut client, &mut server.conn, request_stream_id); + } + + // send a request with request body containing request_body. We expect to receive expected_data_frame_header. + fn fetch_with_data_length_xbytes(request_body: &[u8], expected_data_frame_header: &[u8]) { + // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + // Get DataWritable for the request stream so that we can write the request body. + let data_writable = |e| matches!(e, Http3ClientEvent::DataWritable { .. }); + assert!(client.events().any(data_writable)); + let sent = client.send_request_body(request_stream_id, request_body); + assert_eq!(sent, Ok(request_body.len())); + + // Close stream. + let _ = client.stream_close_send(request_stream_id); + + // We need to loop a bit until all data has been sent. + let mut out = client.process(None, now()); + for _i in 0..20 { + out = server.conn.process(out.dgram(), now()); + out = client.process(out.dgram(), now()); + } + + // check request body is received. + // Then send a response. + while let Some(e) = server.conn.next_event() { + if let ConnectionEvent::RecvStreamReadable { stream_id } = e { + if stream_id == request_stream_id { + // Read the DATA frame. + let mut buf = vec![1_u8; RECV_BUFFER_SIZE]; + let (amount, fin) = server.conn.stream_recv(stream_id, &mut buf).unwrap(); + assert_eq!(fin, true); + assert_eq!( + amount, + request_body.len() + expected_data_frame_header.len() + ); + + // Check the DATA frame header + assert_eq!( + &buf[..expected_data_frame_header.len()], + expected_data_frame_header + ); + + // Check data. + assert_eq!(&buf[expected_data_frame_header.len()..amount], request_body); + + // send response - 200 Content-Length: 3 + // with content: 'abc'. + let _ = server.conn.stream_send(stream_id, HTTP_RESPONSE_2); + server.conn.stream_close_send(stream_id).unwrap(); + } + } + } + + read_response(&mut client, &mut server.conn, request_stream_id); + } + + // send a request with 63 bytes. The DATA frame length field will still have 1 byte. + #[test] + fn fetch_with_data_length_63bytes() { + fetch_with_data_length_xbytes(&[0_u8; 63], &[0x0, 0x3f]); + } + + // send a request with 64 bytes. The DATA frame length field will need 2 byte. + #[test] + fn fetch_with_data_length_64bytes() { + fetch_with_data_length_xbytes(&[0_u8; 64], &[0x0, 0x40, 0x40]); + } + + // send a request with 16383 bytes. The DATA frame length field will still have 2 byte. + #[test] + fn fetch_with_data_length_16383bytes() { + fetch_with_data_length_xbytes(&[0_u8; 16383], &[0x0, 0x7f, 0xff]); + } + + // send a request with 16384 bytes. The DATA frame length field will need 4 byte. + #[test] + fn fetch_with_data_length_16384bytes() { + fetch_with_data_length_xbytes(&[0_u8; 16384], &[0x0, 0x80, 0x0, 0x40, 0x0]); + } + + // Send 2 data frames so that the second one cannot fit into the send_buf and it is only + // partialy sent. We check that the sent data is correct. + #[allow(clippy::useless_vec)] + fn fetch_with_two_data_frames( + first_frame: &[u8], + expected_first_data_frame_header: &[u8], + expected_second_data_frame_header: &[u8], + expected_second_data_frame: &[u8], + ) { + // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + // Get DataWritable for the request stream so that we can write the request body. + let data_writable = |e| matches!(e, Http3ClientEvent::DataWritable { .. }); + assert!(client.events().any(data_writable)); + + // Send the first frame. + let sent = client.send_request_body(request_stream_id, first_frame); + assert_eq!(sent, Ok(first_frame.len())); + + // The second frame cannot fit. + let sent = client.send_request_body(request_stream_id, &vec![0_u8; SEND_BUFFER_SIZE]); + assert_eq!(sent, Ok(expected_second_data_frame.len())); + + // Close stream. + let _ = client.stream_close_send(request_stream_id); + + let mut out = client.process(None, now()); + // We need to loop a bit until all data has been sent. Once for every 1K + // of data. + for _i in 0..SEND_BUFFER_SIZE / 1000 { + out = server.conn.process(out.dgram(), now()); + out = client.process(out.dgram(), now()); + } + + // check received frames and send a response. + while let Some(e) = server.conn.next_event() { + if let ConnectionEvent::RecvStreamReadable { stream_id } = e { + if stream_id == request_stream_id { + // Read DATA frames. + let mut buf = vec![1_u8; RECV_BUFFER_SIZE]; + let (amount, fin) = server.conn.stream_recv(stream_id, &mut buf).unwrap(); + assert_eq!(fin, true); + assert_eq!( + amount, + expected_first_data_frame_header.len() + + first_frame.len() + + expected_second_data_frame_header.len() + + expected_second_data_frame.len() + ); + + // Check the first DATA frame header + let end = expected_first_data_frame_header.len(); + assert_eq!(&buf[..end], expected_first_data_frame_header); + + // Check the first frame data. + let start = end; + let end = end + first_frame.len(); + assert_eq!(&buf[start..end], first_frame); + + // Check the second DATA frame header + let start2 = end; + let end2 = end + expected_second_data_frame_header.len(); + assert_eq!(&buf[start2..end2], expected_second_data_frame_header); + + // Check the second frame data. + let start3 = end2; + let end3 = end2 + expected_second_data_frame.len(); + assert_eq!(&buf[start3..end3], expected_second_data_frame); + + // send response - 200 Content-Length: 3 + // with content: 'abc'. + let _ = server.conn.stream_send(stream_id, HTTP_RESPONSE_2); + server.conn.stream_close_send(stream_id).unwrap(); + } + } + } + + read_response(&mut client, &mut server.conn, request_stream_id); + } + + fn alloc_buffer(size: usize) -> (Vec<u8>, Vec<u8>) { + let data_frame = HFrame::Data { len: size as u64 }; + let mut enc = Encoder::default(); + data_frame.encode(&mut enc); + + (vec![0_u8; size], enc.to_vec()) + } + + // Send 2 frames. For the second one we can only send 63 bytes. + // After the first frame there is exactly 63+2 bytes left in the send buffer. + #[test] + fn fetch_two_data_frame_second_63bytes() { + let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 88); + fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x3f], &[0_u8; 63]); + } + + // Send 2 frames. For the second one we can only send 63 bytes. + // After the first frame there is exactly 63+3 bytes left in the send buffer, + // but we can only send 63 bytes. + #[test] + fn fetch_two_data_frame_second_63bytes_place_for_66() { + let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 89); + fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x3f], &[0_u8; 63]); + } + + // Send 2 frames. For the second one we can only send 64 bytes. + // After the first frame there is exactly 64+3 bytes left in the send buffer, + // but we can only send 64 bytes. + #[test] + fn fetch_two_data_frame_second_64bytes_place_for_67() { + let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 90); + fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x40, 0x40], &[0_u8; 64]); + } + + // Send 2 frames. For the second one we can only send 16383 bytes. + // After the first frame there is exactly 16383+3 bytes left in the send buffer. + #[test] + fn fetch_two_data_frame_second_16383bytes() { + let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 16409); + fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x7f, 0xff], &[0_u8; 16383]); + } + + // Send 2 frames. For the second one we can only send 16383 bytes. + // After the first frame there is exactly 16383+4 bytes left in the send buffer, but we can only send 16383 bytes. + #[test] + fn fetch_two_data_frame_second_16383bytes_place_for_16387() { + let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 16410); + fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x7f, 0xff], &[0_u8; 16383]); + } + + // Send 2 frames. For the second one we can only send 16383 bytes. + // After the first frame there is exactly 16383+5 bytes left in the send buffer, but we can only send 16383 bytes. + #[test] + fn fetch_two_data_frame_second_16383bytes_place_for_16388() { + let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 16411); + fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x7f, 0xff], &[0_u8; 16383]); + } + + // Send 2 frames. For the second one we can send 16384 bytes. + // After the first frame there is exactly 16384+5 bytes left in the send buffer, but we can send 16384 bytes. + #[test] + fn fetch_two_data_frame_second_16384bytes_place_for_16389() { + let (buf, hdr) = alloc_buffer(SEND_BUFFER_SIZE - 16412); + fetch_with_two_data_frames(&buf, &hdr, &[0x0, 0x80, 0x0, 0x40, 0x0], &[0_u8; 16384]); + } + + // Test receiving STOP_SENDING with the HttpNoError error code. + #[test] + fn test_stop_sending_early_response() { + // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + // Stop sending with early_response. + assert_eq!( + Ok(()), + server + .conn + .stream_stop_sending(request_stream_id, Error::HttpNoError.code()) + ); + + // send response - 200 Content-Length: 3 + // with content: 'abc'. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_2, + true, + ); + + let mut stop_sending = false; + let mut response_headers = false; + let mut response_body = false; + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::StopSending { stream_id, error } => { + assert_eq!(stream_id, request_stream_id); + assert_eq!(error, Error::HttpNoError.code()); + // assert that we cannot send any more request data. + assert_eq!( + Err(Error::InvalidStreamId), + client.send_request_body(request_stream_id, &[0_u8; 10]) + ); + stop_sending = true; + } + Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } => { + assert_eq!(stream_id, request_stream_id); + check_response_header_2(&headers); + assert_eq!(fin, false); + assert_eq!(interim, false); + response_headers = true; + } + Http3ClientEvent::DataReadable { stream_id } => { + assert_eq!(stream_id, request_stream_id); + let mut buf = [0_u8; 100]; + let (amount, fin) = client + .read_response_data(now(), stream_id, &mut buf) + .unwrap(); + assert_eq!(fin, true); + assert_eq!(amount, EXPECTED_RESPONSE_DATA_2_FRAME_1.len()); + assert_eq!(&buf[..amount], EXPECTED_RESPONSE_DATA_2_FRAME_1); + response_body = true; + } + _ => {} + } + } + assert!(response_headers); + assert!(response_body); + assert!(stop_sending); + + // after this stream will be removed from client. We will check this by trying to read + // from the stream and that should fail. + let mut buf = [0_u8; 100]; + let res = client.read_response_data(now(), request_stream_id, &mut buf); + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), Error::InvalidStreamId); + + client.close(now(), 0, ""); + } + + // Server sends stop sending and reset. + #[test] + fn test_stop_sending_other_error_with_reset() { + // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + // Stop sending with RequestRejected. + assert_eq!( + Ok(()), + server + .conn + .stream_stop_sending(request_stream_id, Error::HttpRequestRejected.code()) + ); + // also reset with RequestRejected. + assert_eq!( + Ok(()), + server + .conn + .stream_reset_send(request_stream_id, Error::HttpRequestRejected.code()) + ); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + let mut reset = false; + let mut stop_sending = false; + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::StopSending { stream_id, error } => { + assert_eq!(stream_id, request_stream_id); + assert_eq!(error, Error::HttpRequestRejected.code()); + stop_sending = true; + } + Http3ClientEvent::Reset { + stream_id, + error, + local, + } => { + assert_eq!(stream_id, request_stream_id); + assert_eq!(error, Error::HttpRequestRejected.code()); + assert_eq!(local, false); + reset = true; + } + Http3ClientEvent::HeaderReady { .. } | Http3ClientEvent::DataReadable { .. } => { + panic!("We should not get any headers or data"); + } + _ => {} + } + } + + assert!(reset); + assert!(stop_sending); + + // after this stream will be removed from client. We will check this by trying to read + // from the stream and that should fail. + let mut buf = [0_u8; 100]; + let res = client.read_response_data(now(), request_stream_id, &mut buf); + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), Error::InvalidStreamId); + + client.close(now(), 0, ""); + } + + // Server sends stop sending with RequestRejected, but it does not send reset. + #[test] + fn test_stop_sending_other_error_wo_reset() { + // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + // Stop sending with RequestRejected. + assert_eq!( + Ok(()), + server + .conn + .stream_stop_sending(request_stream_id, Error::HttpRequestRejected.code()) + ); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + let mut stop_sending = false; + + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::StopSending { stream_id, error } => { + assert_eq!(stream_id, request_stream_id); + assert_eq!(error, Error::HttpRequestRejected.code()); + stop_sending = true; + } + Http3ClientEvent::Reset { .. } => { + panic!("We should not get StopSending."); + } + Http3ClientEvent::HeaderReady { .. } | Http3ClientEvent::DataReadable { .. } => { + panic!("We should not get any headers or data"); + } + _ => {} + } + } + + assert!(stop_sending); + + // after this we can still read from a stream. + let mut buf = [0_u8; 100]; + let res = client.read_response_data(now(), request_stream_id, &mut buf); + assert!(res.is_ok()); + + client.close(now(), 0, ""); + } + + // Server sends stop sending and reset. We have some events for that stream already + // in client.events. The events will be removed. + #[test] + fn test_stop_sending_and_reset_other_error_with_events() { + // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + // send response - 200 Content-Length: 3 + // with content: 'abc'. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_2, + false, + ); + // At this moment we have some new events, i.e. a HeadersReady event + + // Send a stop sending and reset. + assert_eq!( + Ok(()), + server + .conn + .stream_stop_sending(request_stream_id, Error::HttpRequestCancelled.code()) + ); + assert_eq!( + Ok(()), + server + .conn + .stream_reset_send(request_stream_id, Error::HttpRequestCancelled.code()) + ); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + let mut reset = false; + + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::StopSending { stream_id, error } => { + assert_eq!(stream_id, request_stream_id); + assert_eq!(error, Error::HttpRequestCancelled.code()); + } + Http3ClientEvent::Reset { + stream_id, + error, + local, + } => { + assert_eq!(stream_id, request_stream_id); + assert_eq!(error, Error::HttpRequestCancelled.code()); + assert_eq!(local, false); + reset = true; + } + Http3ClientEvent::HeaderReady { .. } | Http3ClientEvent::DataReadable { .. } => { + panic!("We should not get any headers or data"); + } + _ => {} + } + } + + assert!(reset); + + // after this stream will be removed from client. We will check this by trying to read + // from the stream and that should fail. + let mut buf = [0_u8; 100]; + let res = client.read_response_data(now(), request_stream_id, &mut buf); + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), Error::InvalidStreamId); + + client.close(now(), 0, ""); + } + + // Server sends stop sending with code that is not HttpNoError. + // We have some events for that stream already in the client.events. + // The events will be removed. + #[test] + fn test_stop_sending_other_error_with_events() { + // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + // send response - 200 Content-Length: 3 + // with content: 'abc'. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_2, + false, + ); + // At this moment we have some new event, i.e. a HeadersReady event + + // Send a stop sending. + assert_eq!( + Ok(()), + server + .conn + .stream_stop_sending(request_stream_id, Error::HttpRequestCancelled.code()) + ); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + let mut stop_sending = false; + let mut header_ready = false; + + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::StopSending { stream_id, error } => { + assert_eq!(stream_id, request_stream_id); + assert_eq!(error, Error::HttpRequestCancelled.code()); + stop_sending = true; + } + Http3ClientEvent::Reset { .. } => { + panic!("We should not get StopSending."); + } + Http3ClientEvent::HeaderReady { .. } | Http3ClientEvent::DataReadable { .. } => { + header_ready = true; + } + _ => {} + } + } + + assert!(stop_sending); + assert!(header_ready); + + // after this, we can sill read data from a sttream. + let mut buf = [0_u8; 100]; + let (amount, fin) = client + .read_response_data(now(), request_stream_id, &mut buf) + .unwrap(); + assert_eq!(fin, false); + assert_eq!(amount, EXPECTED_RESPONSE_DATA_2_FRAME_1.len()); + assert_eq!(&buf[..amount], EXPECTED_RESPONSE_DATA_2_FRAME_1); + + client.close(now(), 0, ""); + } + + // Server sends a reset. We will close sending side as well. + #[test] + fn test_reset_wo_stop_sending() { + // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + // Send a reset. + assert_eq!( + Ok(()), + server + .conn + .stream_reset_send(request_stream_id, Error::HttpRequestCancelled.code()) + ); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + let mut reset = false; + + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::StopSending { .. } => { + panic!("We should not get StopSending."); + } + Http3ClientEvent::Reset { + stream_id, + error, + local, + } => { + assert_eq!(stream_id, request_stream_id); + assert_eq!(error, Error::HttpRequestCancelled.code()); + assert_eq!(local, false); + reset = true; + } + Http3ClientEvent::HeaderReady { .. } | Http3ClientEvent::DataReadable { .. } => { + panic!("We should not get any headers or data"); + } + _ => {} + } + } + + assert!(reset); + + // after this stream will be removed from client. We will check this by trying to read + // from the stream and that should fail. + let mut buf = [0_u8; 100]; + let res = client.read_response_data(now(), request_stream_id, &mut buf); + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), Error::InvalidStreamId); + + client.close(now(), 0, ""); + } + + fn test_incomplet_frame(buf: &[u8], error: &Error) { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + buf, + true, + ); + + while let Some(e) = client.next_event() { + if let Http3ClientEvent::DataReadable { stream_id } = e { + assert_eq!(stream_id, request_stream_id); + let mut buf_res = [0_u8; 100]; + let res = client.read_response_data(now(), stream_id, &mut buf_res); + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), Error::HttpFrame); + } + } + assert_closed(&client, &error); + } + + // Incomplete DATA frame + #[test] + fn test_incomplet_data_frame() { + test_incomplet_frame(&HTTP_RESPONSE_2[..12], &Error::HttpFrame); + } + + // Incomplete HEADERS frame + #[test] + fn test_incomplet_headers_frame() { + test_incomplet_frame(&HTTP_RESPONSE_2[..7], &Error::HttpFrame); + } + + #[test] + fn test_incomplet_unknown_frame() { + test_incomplet_frame(&[0x21], &Error::HttpFrame); + } + + // test goaway + #[test] + fn test_goaway() { + let (mut client, mut server) = connect(); + let request_stream_id_1 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_1, 0); + let request_stream_id_2 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_2, 4); + let request_stream_id_3 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_3, 8); + + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + + let _ = server + .conn + .stream_send(server.control_stream_id.unwrap(), &[0x7, 0x1, 0x8]); + + // find the new request/response stream and send frame v on it. + while let Some(e) = server.conn.next_event() { + if let ConnectionEvent::RecvStreamReadable { stream_id } = e { + let mut buf = [0_u8; 100]; + let _ = server.conn.stream_recv(stream_id, &mut buf).unwrap(); + if (stream_id == request_stream_id_1) || (stream_id == request_stream_id_2) { + // send response - 200 Content-Length: 7 + // with content: 'abcdefg'. + // The content will be send in 2 DATA frames. + let _ = server.conn.stream_send(stream_id, HTTP_RESPONSE_1); + server.conn.stream_close_send(stream_id).unwrap(); + } + } + } + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + let mut stream_reset = false; + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::HeaderReady { headers, fin, .. } => { + check_response_header_1(&headers); + assert_eq!(fin, false); + } + Http3ClientEvent::DataReadable { stream_id } => { + assert!( + (stream_id == request_stream_id_1) || (stream_id == request_stream_id_2) + ); + let mut buf = [0_u8; 100]; + assert_eq!( + (EXPECTED_RESPONSE_DATA_1.len(), true), + client + .read_response_data(now(), stream_id, &mut buf) + .unwrap() + ); + } + Http3ClientEvent::Reset { + stream_id, + error, + local, + } => { + assert_eq!(stream_id, request_stream_id_3); + assert_eq!(error, Error::HttpRequestRejected.code()); + assert_eq!(local, false); + stream_reset = true; + } + _ => {} + } + } + + assert!(stream_reset); + assert_eq!(client.state(), Http3State::GoingAway(8)); + + // Check that a new request cannot be made. + assert_eq!( + client.fetch(now(), "GET", "https", "something.com", "/", &[]), + Err(Error::AlreadyClosed) + ); + + client.close(now(), 0, ""); + } + + #[test] + fn multiple_goaways() { + let (mut client, mut server) = connect(); + let request_stream_id_1 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_1, 0); + let request_stream_id_2 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_2, 4); + let request_stream_id_3 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_3, 8); + + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + + // First send a Goaway frame with an higher number + let _ = server + .conn + .stream_send(server.control_stream_id.unwrap(), &[0x7, 0x1, 0x8]); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + // Check that there is one reset for stream_id 8 + let mut stream_reset_1 = 0; + while let Some(e) = client.next_event() { + if let Http3ClientEvent::Reset { + stream_id, + error, + local, + } = e + { + assert_eq!(stream_id, request_stream_id_3); + assert_eq!(error, Error::HttpRequestRejected.code()); + assert_eq!(local, false); + stream_reset_1 += 1; + } + } + + assert_eq!(stream_reset_1, 1); + assert_eq!(client.state(), Http3State::GoingAway(8)); + + // Server sends another GOAWAY frame + let _ = server + .conn + .stream_send(server.control_stream_id.unwrap(), &[0x7, 0x1, 0x4]); + + // Send response for stream 0 + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id_1, + HTTP_RESPONSE_1, + true, + ); + + let mut stream_reset_2 = 0; + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::HeaderReady { headers, fin, .. } => { + check_response_header_1(&headers); + assert_eq!(fin, false); + } + Http3ClientEvent::DataReadable { stream_id } => { + assert!(stream_id == request_stream_id_1); + let mut buf = [0_u8; 100]; + assert_eq!( + (EXPECTED_RESPONSE_DATA_1.len(), true), + client + .read_response_data(now(), stream_id, &mut buf) + .unwrap() + ); + } + Http3ClientEvent::Reset { + stream_id, + error, + local, + } => { + assert_eq!(stream_id, request_stream_id_2); + assert_eq!(error, Error::HttpRequestRejected.code()); + assert_eq!(local, false); + stream_reset_2 += 1; + } + _ => {} + } + } + + assert_eq!(stream_reset_2, 1); + assert_eq!(client.state(), Http3State::GoingAway(4)); + } + + #[test] + fn multiple_goaways_stream_id_increased() { + let (mut client, mut server) = connect(); + let request_stream_id_1 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_1, 0); + let request_stream_id_2 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_2, 4); + let request_stream_id_3 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_3, 8); + + // First send a Goaway frame with a smaller number + let _ = server + .conn + .stream_send(server.control_stream_id.unwrap(), &[0x7, 0x1, 0x4]); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + assert_eq!(client.state(), Http3State::GoingAway(4)); + + // Now send a Goaway frame with an higher number + let _ = server + .conn + .stream_send(server.control_stream_id.unwrap(), &[0x7, 0x1, 0x8]); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + assert_closed(&client, &Error::HttpGeneralProtocol); + } + + #[test] + fn goaway_wrong_stream_id() { + let (mut client, mut server) = connect(); + + let _ = server + .conn + .stream_send(server.control_stream_id.unwrap(), &[0x7, 0x1, 0x9]); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + assert_closed(&client, &Error::HttpId); + } + + // Close stream before headers. + #[test] + fn test_stream_fin_wo_headers() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + // send fin before sending any data. + server.conn.stream_close_send(request_stream_id).unwrap(); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + // Recv HeaderReady wo headers with fin. + let e = client.events().next().unwrap(); + assert_eq!( + e, + Http3ClientEvent::Reset { + stream_id: request_stream_id, + error: Error::HttpGeneralProtocolStream.code(), + local: true, + } + ); + + // Stream should now be closed and gone + let mut buf = [0_u8; 100]; + assert_eq!( + client.read_response_data(now(), 0, &mut buf), + Err(Error::InvalidStreamId) + ); + } + + // Close stream imemediately after headers. + #[test] + fn test_stream_fin_after_headers() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_HEADER_ONLY_2, + true, + ); + + // Recv HeaderReady with headers and fin. + let e = client.events().next().unwrap(); + if let Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } = e + { + assert_eq!(stream_id, request_stream_id); + check_response_header_2(&headers); + assert_eq!(fin, true); + assert_eq!(interim, false); + } else { + panic!("wrong event type"); + } + + // Stream should now be closed and gone + let mut buf = [0_u8; 100]; + assert_eq!( + client.read_response_data(now(), 0, &mut buf), + Err(Error::InvalidStreamId) + ); + } + + // Send headers, read headers and than close stream. + // We should get HeaderReady and a DataReadable + #[test] + fn test_stream_fin_after_headers_are_read_wo_data_frame() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + // Send some good data wo fin + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_HEADER_ONLY_2, + false, + ); + + // Recv headers wo fin + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } => { + assert_eq!(stream_id, request_stream_id); + check_response_header_2(&headers); + assert_eq!(fin, false); + assert_eq!(interim, false); + } + Http3ClientEvent::DataReadable { .. } => { + panic!("We should not receive a DataGeadable event!"); + } + _ => {} + }; + } + + // ok NOW send fin + server.conn.stream_close_send(request_stream_id).unwrap(); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + // Recv DataReadable wo data with fin + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::HeaderReady { .. } => { + panic!("We should not get another HeaderReady!"); + } + Http3ClientEvent::DataReadable { stream_id } => { + assert_eq!(stream_id, request_stream_id); + let mut buf = [0_u8; 100]; + let res = client.read_response_data(now(), stream_id, &mut buf); + let (len, fin) = res.expect("should read"); + assert_eq!(0, len); + assert_eq!(fin, true); + } + _ => {} + }; + } + + // Stream should now be closed and gone + let mut buf = [0_u8; 100]; + assert_eq!( + client.read_response_data(now(), 0, &mut buf), + Err(Error::InvalidStreamId) + ); + } + + // Send headers and an empty data frame, then close the stream. + #[test] + fn test_stream_fin_after_headers_and_a_empty_data_frame() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send headers. + let _ = server + .conn + .stream_send(request_stream_id, HTTP_RESPONSE_HEADER_ONLY_2); + // Send an empty data frame. + let _ = server.conn.stream_send(request_stream_id, &[0x00, 0x00]); + // ok NOW send fin + server.conn.stream_close_send(request_stream_id).unwrap(); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + // Recv HeaderReady with fin. + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } => { + assert_eq!(stream_id, request_stream_id); + check_response_header_2(&headers); + assert_eq!(fin, false); + assert_eq!(interim, false); + } + Http3ClientEvent::DataReadable { stream_id } => { + assert_eq!(stream_id, request_stream_id); + let mut buf = [0_u8; 100]; + assert_eq!( + Ok((0, true)), + client.read_response_data(now(), stream_id, &mut buf) + ); + } + _ => {} + }; + } + + // Stream should now be closed and gone + let mut buf = [0_u8; 100]; + assert_eq!( + client.read_response_data(now(), request_stream_id, &mut buf), + Err(Error::InvalidStreamId) + ); + } + + // Send headers and an empty data frame. Read headers and then close the stream. + // We should get a HeaderReady without fin and a DataReadable wo data and with fin. + #[test] + fn test_stream_fin_after_headers_an_empty_data_frame_are_read() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + // Send some good data wo fin + // Send headers. + let _ = server + .conn + .stream_send(request_stream_id, HTTP_RESPONSE_HEADER_ONLY_2); + // Send an empty data frame. + let _ = server.conn.stream_send(request_stream_id, &[0x00, 0x00]); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + // Recv headers wo fin + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } => { + assert_eq!(stream_id, request_stream_id); + check_response_header_2(&headers); + assert_eq!(fin, false); + assert_eq!(interim, false); + } + Http3ClientEvent::DataReadable { .. } => { + panic!("We should not receive a DataGeadable event!"); + } + _ => {} + }; + } + + // ok NOW send fin + server.conn.stream_close_send(request_stream_id).unwrap(); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + // Recv no data, but do get fin + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::HeaderReady { .. } => { + panic!("We should not get another HeaderReady!"); + } + Http3ClientEvent::DataReadable { stream_id } => { + assert_eq!(stream_id, request_stream_id); + let mut buf = [0_u8; 100]; + let res = client.read_response_data(now(), stream_id, &mut buf); + let (len, fin) = res.expect("should read"); + assert_eq!(0, len); + assert_eq!(fin, true); + } + _ => {} + }; + } + + // Stream should now be closed and gone + let mut buf = [0_u8; 100]; + assert_eq!( + client.read_response_data(now(), 0, &mut buf), + Err(Error::InvalidStreamId) + ); + } + + #[test] + fn test_stream_fin_after_a_data_frame() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + // Send some good data wo fin + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_2, + false, + ); + + // Recv some good data wo fin + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } => { + assert_eq!(stream_id, request_stream_id); + check_response_header_2(&headers); + assert_eq!(fin, false); + assert_eq!(interim, false); + } + Http3ClientEvent::DataReadable { stream_id } => { + assert_eq!(stream_id, request_stream_id); + let mut buf = [0_u8; 100]; + let res = client.read_response_data(now(), stream_id, &mut buf); + let (len, fin) = res.expect("should have data"); + assert_eq!(len, EXPECTED_RESPONSE_DATA_2_FRAME_1.len()); + assert_eq!(&buf[..len], EXPECTED_RESPONSE_DATA_2_FRAME_1); + assert_eq!(fin, false); + } + _ => {} + }; + } + + // ok NOW send fin + server.conn.stream_close_send(request_stream_id).unwrap(); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + // fin wo data should generate DataReadable + let e = client.events().next().unwrap(); + if let Http3ClientEvent::DataReadable { stream_id } = e { + assert_eq!(stream_id, request_stream_id); + let mut buf = [0; 100]; + let res = client.read_response_data(now(), stream_id, &mut buf); + let (len, fin) = res.expect("should read"); + assert_eq!(0, len); + assert_eq!(fin, true); + } else { + panic!("wrong event type"); + } + + // Stream should now be closed and gone + let mut buf = [0_u8; 100]; + assert_eq!( + client.read_response_data(now(), 0, &mut buf), + Err(Error::InvalidStreamId) + ); + } + + #[test] + fn test_multiple_data_frames() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send two data frames with fin + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_1, + true, + ); + + // Read first frame + match client.events().nth(1).unwrap() { + Http3ClientEvent::DataReadable { stream_id } => { + assert_eq!(stream_id, request_stream_id); + let mut buf = [0_u8; 100]; + assert_eq!( + (EXPECTED_RESPONSE_DATA_1.len(), true), + client + .read_response_data(now(), stream_id, &mut buf) + .unwrap() + ); + } + x => { + eprintln!("event {:?}", x); + panic!() + } + } + + // Stream should now be closed and gone + let mut buf = [0_u8; 100]; + assert_eq!( + client.read_response_data(now(), 0, &mut buf), + Err(Error::InvalidStreamId) + ); + } + + #[test] + fn test_receive_grease_before_response() { + // Construct an unknown frame. + const UNKNOWN_FRAME_LEN: usize = 832; + + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + let mut enc = Encoder::with_capacity(UNKNOWN_FRAME_LEN + 4); + enc.encode_varint(1028_u64); // Arbitrary type. + enc.encode_varint(UNKNOWN_FRAME_LEN as u64); + let mut buf: Vec<_> = enc.into(); + buf.resize(UNKNOWN_FRAME_LEN + buf.len(), 0); + let _ = server.conn.stream_send(request_stream_id, &buf).unwrap(); + + // Send a headers and a data frame with fin + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_2, + true, + ); + + // Read first frame + match client.events().nth(1).unwrap() { + Http3ClientEvent::DataReadable { stream_id } => { + assert_eq!(stream_id, request_stream_id); + let mut buf = [0_u8; 100]; + let (len, fin) = client + .read_response_data(now(), stream_id, &mut buf) + .unwrap(); + assert_eq!(len, EXPECTED_RESPONSE_DATA_2_FRAME_1.len()); + assert_eq!(&buf[..len], EXPECTED_RESPONSE_DATA_2_FRAME_1); + assert_eq!(fin, true); + } + x => { + eprintln!("event {:?}", x); + panic!() + } + } + // Stream should now be closed and gone + let mut buf = [0_u8; 100]; + assert_eq!( + client.read_response_data(now(), 0, &mut buf), + Err(Error::InvalidStreamId) + ); + } + + #[test] + fn test_read_frames_header_blocked() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let headers = vec![ + (String::from(":status"), String::from("200")), + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("3")), + ]; + let encoded_headers = server + .encoder + .encode_header_block(&mut server.conn, &headers, request_stream_id) + .unwrap(); + let hframe = HFrame::Headers { + header_block: encoded_headers.to_vec(), + }; + + // Send the encoder instructions, but delay them so that the stream is blocked on decoding headers. + let encoder_inst_pkt = server.conn.process(None, now()); + + // Send response + let mut d = Encoder::default(); + hframe.encode(&mut d); + let d_frame = HFrame::Data { len: 3 }; + d_frame.encode(&mut d); + d.encode(&[0x61, 0x62, 0x63]); + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + &d, + true, + ); + + let header_ready_event = |e| matches!(e, Http3ClientEvent::HeaderReady { .. }); + assert!(!client.events().any(header_ready_event)); + + // Let client receive the encoder instructions. + let _ = client.process(encoder_inst_pkt.dgram(), now()); + + let out = server.conn.process(None, now()); + let _ = client.process(out.dgram(), now()); + let _ = client.process(None, now()); + + let mut recv_header = false; + let mut recv_data = false; + // Now the stream is unblocked and both headers and data will be consumed. + while let Some(e) = client.next_event() { + match e { + Http3ClientEvent::HeaderReady { stream_id, .. } => { + assert_eq!(stream_id, request_stream_id); + recv_header = true; + } + Http3ClientEvent::DataReadable { stream_id } => { + recv_data = true; + assert_eq!(stream_id, request_stream_id); + } + x => { + eprintln!("event {:?}", x); + panic!() + } + } + } + assert!(recv_header && recv_data); + } + + #[test] + fn test_read_frames_header_blocked_with_fin_after_headers() { + let (mut hconn, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut hconn, &mut server); + + let sent_headers = vec![ + (String::from(":status"), String::from("200")), + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("0")), + ]; + let encoded_headers = server + .encoder + .encode_header_block(&mut server.conn, &sent_headers, request_stream_id) + .unwrap(); + let hframe = HFrame::Headers { + header_block: encoded_headers.to_vec(), + }; + + // Send the encoder instructions, but delay them so that the stream is blocked on decoding headers. + let encoder_inst_pkt = server.conn.process(None, now()); + + let mut d = Encoder::default(); + hframe.encode(&mut d); + + server_send_response_and_exchange_packet( + &mut hconn, + &mut server, + request_stream_id, + &d, + true, + ); + + let header_ready_event = |e| matches!(e, Http3ClientEvent::HeaderReady { .. }); + assert!(!hconn.events().any(header_ready_event)); + + // Let client receive the encoder instructions. + let _out = hconn.process(encoder_inst_pkt.dgram(), now()); + + let mut recv_header = false; + // Now the stream is unblocked. After headers we will receive a fin. + while let Some(e) = hconn.next_event() { + if let Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } = e + { + assert_eq!(stream_id, request_stream_id); + assert_eq!(headers, sent_headers); + assert_eq!(fin, true); + assert_eq!(interim, false); + recv_header = true; + } else { + panic!("event {:?}", e); + } + } + assert!(recv_header); + } + + fn exchange_token(client: &mut Http3Client, server: &mut Connection) -> ResumptionToken { + server.send_ticket(now(), &[]).expect("can send ticket"); + let out = server.process_output(now()); + assert!(out.as_dgram_ref().is_some()); + client.process_input(out.dgram().unwrap(), now()); + // We do not have a token so we need to wait for a resumption token timer to trigger. + client.process_output(now() + Duration::from_millis(250)); + assert_eq!(client.state(), Http3State::Connected); + client + .events() + .find_map(|e| { + if let Http3ClientEvent::ResumptionToken(token) = e { + Some(token) + } else { + None + } + }) + .unwrap() + } + + fn start_with_0rtt() -> (Http3Client, TestServer) { + let (mut client, mut server) = connect(); + let token = exchange_token(&mut client, &mut server.conn); + + let mut client = default_http3_client(); + + let server = TestServer::new(); + + assert_eq!(client.state(), Http3State::Initializing); + client + .enable_resumption(now(), &token) + .expect("Set resumption token."); + + assert_eq!(client.state(), Http3State::ZeroRtt); + let zerortt_event = |e| matches!(e, Http3ClientEvent::StateChange(Http3State::ZeroRtt)); + assert!(client.events().any(zerortt_event)); + + (client, server) + } + + #[test] + fn zero_rtt_negotiated() { + let (mut client, mut server) = start_with_0rtt(); + + let out = client.process(None, now()); + + assert_eq!(client.state(), Http3State::ZeroRtt); + assert_eq!(*server.conn.state(), State::Init); + let out = server.conn.process(out.dgram(), now()); + + // Check that control and qpack streams are received and a + // SETTINGS frame has been received. + // Also qpack encoder stream will send "change capacity" instruction because it has + // the peer settings already. + server.check_control_qpack_request_streams_resumption( + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + EXPECTED_REQUEST_HEADER_FRAME, + false, + ); + + assert_eq!(*server.conn.state(), State::Handshaking); + let out = client.process(out.dgram(), now()); + assert_eq!(client.state(), Http3State::Connected); + + let _ = server.conn.process(out.dgram(), now()); + assert!(server.conn.state().connected()); + + assert!(client.tls_info().unwrap().resumed()); + assert!(server.conn.tls_info().unwrap().resumed()); + } + + #[test] + fn zero_rtt_send_request() { + let (mut client, mut server) = start_with_0rtt(); + + let request_stream_id = make_request( + &mut client, + true, + &[(String::from("myheaders"), String::from("myvalue"))], + ); + assert_eq!(request_stream_id, 0); + + let out = client.process(None, now()); + + assert_eq!(client.state(), Http3State::ZeroRtt); + assert_eq!(*server.conn.state(), State::Init); + let out = server.conn.process(out.dgram(), now()); + + // Check that control and qpack streams are received and a + // SETTINGS frame has been received. + // Also qpack encoder stream will send "change capacity" instruction because it has + // the peer settings already. + server.check_control_qpack_request_streams_resumption( + ENCODER_STREAM_DATA_WITH_CAP_INST_AND_ENCODING_INST, + EXPECTED_REQUEST_HEADER_FRAME_VERSION2, + true, + ); + + assert_eq!(*server.conn.state(), State::Handshaking); + let out = client.process(out.dgram(), now()); + assert_eq!(client.state(), Http3State::Connected); + let out = server.conn.process(out.dgram(), now()); + assert!(server.conn.state().connected()); + let out = client.process(out.dgram(), now()); + assert!(out.as_dgram_ref().is_none()); + + // After the server has been connected, send a response. + let res = server.conn.stream_send(request_stream_id, HTTP_RESPONSE_2); + assert_eq!(res, Ok(HTTP_RESPONSE_2.len())); + server.conn.stream_close_send(request_stream_id).unwrap(); + + read_response(&mut client, &mut server.conn, request_stream_id); + + assert!(client.tls_info().unwrap().resumed()); + assert!(server.conn.tls_info().unwrap().resumed()); + } + + #[test] + fn zero_rtt_before_resumption_token() { + let mut client = default_http3_client(); + assert!(client + .fetch(now(), "GET", "https", "something.com", "/", &[]) + .is_err()); + } + + #[test] + fn zero_rtt_send_reject() { + let (mut client, mut server) = connect(); + let token = exchange_token(&mut client, &mut server.conn); + + let mut client = default_http3_client(); + let mut server = Connection::new_server( + test_fixture::DEFAULT_KEYS, + test_fixture::DEFAULT_ALPN_H3, + Rc::new(RefCell::new(FixedConnectionIdManager::new(10))), + &ConnectionParameters::default(), + ) + .unwrap(); + // Using a freshly initialized anti-replay context + // should result in the server rejecting 0-RTT. + let ar = AntiReplay::new(now(), test_fixture::ANTI_REPLAY_WINDOW, 1, 3) + .expect("setup anti-replay"); + server + .server_enable_0rtt(&ar, AllowZeroRtt {}) + .expect("enable 0-RTT"); + + assert_eq!(client.state(), Http3State::Initializing); + client + .enable_resumption(now(), &token) + .expect("Set resumption token."); + let zerortt_event = |e| matches!(e, Http3ClientEvent::StateChange(Http3State::ZeroRtt)); + assert!(client.events().any(zerortt_event)); + + // Send ClientHello. + let client_hs = client.process(None, now()); + assert!(client_hs.as_dgram_ref().is_some()); + + // Create a request + let request_stream_id = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id, 0); + + let client_0rtt = client.process(None, now()); + assert!(client_0rtt.as_dgram_ref().is_some()); + + let server_hs = server.process(client_hs.dgram(), now()); + assert!(server_hs.as_dgram_ref().is_some()); // Should produce ServerHello etc... + let server_ignored = server.process(client_0rtt.dgram(), now()); + assert!(server_ignored.as_dgram_ref().is_none()); + + // The server shouldn't receive that 0-RTT data. + let recvd_stream_evt = |e| matches!(e, ConnectionEvent::NewStream { .. }); + assert!(!server.events().any(recvd_stream_evt)); + + // Client should get a rejection. + let client_out = client.process(server_hs.dgram(), now()); + assert!(client_out.as_dgram_ref().is_some()); + let recvd_0rtt_reject = |e| e == Http3ClientEvent::ZeroRttRejected; + assert!(client.events().any(recvd_0rtt_reject)); + + // ...and the client stream should be gone. + let res = client.stream_close_send(request_stream_id); + assert!(res.is_err()); + assert_eq!(res.unwrap_err(), Error::InvalidStreamId); + + // Client will send Setting frame and open new qpack streams. + let _ = server.process(client_out.dgram(), now()); + TestServer::new_with_conn(server).check_client_control_qpack_streams_no_resumption(); + + // Check that we can send a request and that the stream_id starts again from 0. + assert_eq!(make_request(&mut client, false, &[]), 0); + } + + // Connect to a server, get token and reconnect using 0-rtt. Seerver sends new Settings. + fn zero_rtt_change_settings( + original_settings: &[HSetting], + resumption_settings: &[HSetting], + expected_client_state: &Http3State, + expected_encoder_stream_data: &[u8], + ) { + let mut client = default_http3_client(); + let mut server = TestServer::new_with_settings(original_settings); + // Connect and get a token + connect_with(&mut client, &mut server); + let token = exchange_token(&mut client, &mut server.conn); + + let mut client = default_http3_client(); + let mut server = TestServer::new_with_settings(resumption_settings); + assert_eq!(client.state(), Http3State::Initializing); + client + .enable_resumption(now(), &token) + .expect("Set resumption token."); + assert_eq!(client.state(), Http3State::ZeroRtt); + let out = client.process(None, now()); + + assert_eq!(client.state(), Http3State::ZeroRtt); + assert_eq!(*server.conn.state(), State::Init); + let out = server.conn.process(out.dgram(), now()); + + // Check that control and qpack streams anda SETTINGS frame are received. + // Also qpack encoder stream will send "change capacity" instruction because it has + // the peer settings already. + server.check_control_qpack_request_streams_resumption( + expected_encoder_stream_data, + EXPECTED_REQUEST_HEADER_FRAME, + false, + ); + + assert_eq!(*server.conn.state(), State::Handshaking); + let out = client.process(out.dgram(), now()); + assert_eq!(client.state(), Http3State::Connected); + + let _ = server.conn.process(out.dgram(), now()); + assert!(server.conn.state().connected()); + + assert!(client.tls_info().unwrap().resumed()); + assert!(server.conn.tls_info().unwrap().resumed()); + + // Send new settings. + let control_stream = server.conn.stream_create(StreamType::UniDi).unwrap(); + let mut enc = Encoder::default(); + server.settings.encode(&mut enc); + let mut sent = server.conn.stream_send(control_stream, CONTROL_STREAM_TYPE); + assert_eq!(sent.unwrap(), CONTROL_STREAM_TYPE.len()); + sent = server.conn.stream_send(control_stream, &enc); + assert_eq!(sent.unwrap(), enc.len()); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + assert_eq!(&client.state(), expected_client_state); + assert!(server.conn.state().connected()); + } + + #[test] + fn zero_rtt_new_server_setting_are_the_same() { + // Send a new server settings that are the same as the old one. + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &Http3State::Connected, + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn zero_rtt_new_server_setting_omit_max_table() { + // Send a new server settings without MaxTableCapacity + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &Http3State::Closing(CloseError::Application(265)), + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn zero_rtt_new_server_setting_omit_blocked_streams() { + // Send a new server settings without BlockedStreams + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &Http3State::Closing(CloseError::Application(265)), + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn zero_rtt_new_server_setting_omit_header_list_size() { + // Send a new server settings without MaxHeaderListSize + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + ], + &Http3State::Connected, + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn zero_rtt_new_server_setting_max_table_size_bigger() { + // Send a new server settings MaxTableCapacity=200 + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 200), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &Http3State::Closing(CloseError::Application(514)), + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn zero_rtt_new_server_setting_max_table_size_smaller() { + // Send a new server settings MaxTableCapacity=50 + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 50), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &Http3State::Closing(CloseError::Application(265)), + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn zero_rtt_new_server_setting_blocked_streams_bigger() { + // Send a new server settings withBlockedStreams=200 + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 200), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &Http3State::Connected, + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn zero_rtt_new_server_setting_blocked_streams_smaller() { + // Send a new server settings withBlockedStreams=50 + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 50), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &Http3State::Closing(CloseError::Application(265)), + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn zero_rtt_new_server_setting_max_header_size_bigger() { + // Send a new server settings with MaxHeaderListSize=20000 + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 20000), + ], + &Http3State::Connected, + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn zero_rtt_new_server_setting_max_headers_size_smaller() { + // Send the new server settings with MaxHeaderListSize=5000 + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 5000), + ], + &Http3State::Closing(CloseError::Application(265)), + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn zero_rtt_max_table_size_first_omitted() { + // send server original settings without MaxTableCapacity + // send new server setting with MaxTableCapacity + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &Http3State::Connected, + ENCODER_STREAM_DATA, + ); + } + + #[test] + fn zero_rtt_blocked_streams_first_omitted() { + // Send server original settings without BlockedStreams + // Send the new server settings with BlockedStreams + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &Http3State::Connected, + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn zero_rtt_max_header_size_first_omitted() { + // Send server settings without MaxHeaderListSize + // Send new settings with MaxHeaderListSize. + zero_rtt_change_settings( + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 10000), + ], + &[ + HSetting::new(HSettingType::MaxTableCapacity, 100), + HSetting::new(HSettingType::BlockedStreams, 100), + HSetting::new(HSettingType::MaxHeaderListSize, 10000), + ], + &Http3State::Closing(CloseError::Application(265)), + ENCODER_STREAM_DATA_WITH_CAP_INSTRUCTION, + ); + } + + #[test] + fn test_trailers_with_fin_after_headers() { + // Make a new connection. + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send HEADER frame. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_HEADER_FRAME_0, + false, + ); + + // Check response headers. + let mut response_headers = false; + while let Some(e) = client.next_event() { + if let Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } = e + { + assert_eq!(stream_id, request_stream_id); + check_response_header_0(&headers); + assert_eq!(fin, false); + assert_eq!(interim, false); + response_headers = true; + } + } + assert!(response_headers); + + // Send trailers + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_HEADER_FRAME_0, + true, + ); + + let events: Vec<Http3ClientEvent> = client.events().collect(); + + // We already had HeaderReady + let header_ready: fn(&Http3ClientEvent) -> _ = + |e| matches!(*e, Http3ClientEvent::HeaderReady { .. }); + assert!(!events.iter().any(header_ready)); + + // Check that we have a DataReady event. Reading from the stream will return fin=true. + let data_readable: fn(&Http3ClientEvent) -> _ = + |e| matches!(*e, Http3ClientEvent::DataReadable { .. }); + assert!(events.iter().any(data_readable)); + let mut buf = [0_u8; 100]; + let (len, fin) = client + .read_response_data(now(), request_stream_id, &mut buf) + .unwrap(); + assert_eq!(0, len); + assert_eq!(fin, true) + } + + #[test] + fn test_trailers_with_later_fin_after_headers() { + // Make a new connection. + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send HEADER frame. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_HEADER_FRAME_0, + false, + ); + + // Check response headers. + let mut response_headers = false; + while let Some(e) = client.next_event() { + if let Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } = e + { + assert_eq!(stream_id, request_stream_id); + check_response_header_0(&headers); + assert_eq!(fin, false); + assert_eq!(interim, false); + response_headers = true; + } + } + assert!(response_headers); + + // Send trailers + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_HEADER_FRAME_0, + false, + ); + + // Check that we do not have a DataReady event. + let data_readable = |e| matches!(e, Http3ClientEvent::DataReadable { .. }); + assert!(!client.events().any(data_readable)); + + server.conn.stream_close_send(request_stream_id).unwrap(); + + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + let events: Vec<Http3ClientEvent> = client.events().collect(); + + // We already had HeaderReady + let header_ready: fn(&Http3ClientEvent) -> _ = + |e| matches!(*e, Http3ClientEvent::HeaderReady { .. }); + assert!(!events.iter().any(header_ready)); + + // Check that we have a DataReady event. Reading from the stream will return fin=true. + let data_readable_fn: fn(&Http3ClientEvent) -> _ = + |e| matches!(*e, Http3ClientEvent::DataReadable { .. }); + assert!(events.iter().any(data_readable_fn)); + let mut buf = [0_u8; 100]; + let (len, fin) = client + .read_response_data(now(), request_stream_id, &mut buf) + .unwrap(); + assert_eq!(0, len); + assert_eq!(fin, true); + } + + #[test] + fn test_data_after_trailers_after_headers() { + // Make a new connection. + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send HEADER frame. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_HEADER_FRAME_0, + false, + ); + + // Check response headers. + let mut response_headers = false; + while let Some(e) = client.next_event() { + if let Http3ClientEvent::HeaderReady { + stream_id, + headers, + interim, + fin, + } = e + { + assert_eq!(stream_id, request_stream_id); + check_response_header_0(&headers); + assert_eq!(fin, false); + assert_eq!(interim, false); + response_headers = true; + } + } + assert!(response_headers); + + // Send trailers + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_HEADER_FRAME_0, + false, + ); + + // Check that we do not have a DataReady event. + let data_readable = |e| matches!(e, Http3ClientEvent::DataReadable { .. }); + assert!(!client.events().any(data_readable)); + + // Send Data frame. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + &[0x0, 0x3, 0x61, 0x62, 0x63], // a data frame + false, + ); + + assert_closed(&client, &Error::HttpFrameUnexpected); + } + + #[test] + fn transport_stream_readable_event_after_all_data() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(false); + + // Send headers. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_2, + false, + ); + + // Send an empty data frame and a fin + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + &[0x0, 0x0], + true, + ); + + let mut buf = [0_u8; 100]; + assert_eq!(client.read_response_data(now(), 0, &mut buf), Ok((3, true))); + + client.process(None, now()); + } + + #[test] + fn no_data_ready_events_after_fin() { + // Connect exchange headers and send a request. Also check if the correct header frame has been sent. + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // send response - 200 Content-Length: 7 + // with content: 'abcdefg'. + // The content will be send in 2 DATA frames. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_1, + true, + ); + + let data_readable_event = |e| matches!(e, Http3ClientEvent::DataReadable { stream_id } if stream_id == request_stream_id); + assert!(client.events().any(data_readable_event)); + + let mut buf = [0_u8; 100]; + assert_eq!( + (EXPECTED_RESPONSE_DATA_1.len(), true), + client + .read_response_data(now(), request_stream_id, &mut buf) + .unwrap() + ); + + assert!(!client.events().any(data_readable_event)); + } + + #[test] + fn reading_small_chunks_of_data() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // send response - 200 Content-Length: 7 + // with content: 'abcdefg'. + // The content will be send in 2 DATA frames. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_1, + true, + ); + + let data_readable_event = |e| matches!(e, Http3ClientEvent::DataReadable { stream_id } if stream_id == request_stream_id); + assert!(client.events().any(data_readable_event)); + + let mut buf1 = [0_u8; 1]; + assert_eq!( + (1, false), + client + .read_response_data(now(), request_stream_id, &mut buf1) + .unwrap() + ); + assert!(!client.events().any(data_readable_event)); + + // Now read only until the end of the first frame. The firs framee has 3 bytes. + let mut buf2 = [0_u8; 2]; + assert_eq!( + (2, false), + client + .read_response_data(now(), request_stream_id, &mut buf2) + .unwrap() + ); + assert!(!client.events().any(data_readable_event)); + + // Read a half of the second frame. + assert_eq!( + (2, false), + client + .read_response_data(now(), request_stream_id, &mut buf2) + .unwrap() + ); + assert!(!client.events().any(data_readable_event)); + + // Read the rest. + // Read a half of the second frame. + assert_eq!( + (2, true), + client + .read_response_data(now(), request_stream_id, &mut buf2) + .unwrap() + ); + assert!(!client.events().any(data_readable_event)); + } + + #[test] + fn zero_length_data_at_end() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // send response - 200 Content-Length: 7 + // with content: 'abcdefg'. + // The content will be send in 2 DATA frames. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_1, + false, + ); + // Send a zero-length frame at the end of the stream. + let _ = server.conn.stream_send(request_stream_id, &[0, 0]).unwrap(); + server.conn.stream_close_send(request_stream_id).unwrap(); + let dgram = server.conn.process_output(now()).dgram(); + let dgram = client.process(dgram, now()).dgram(); + server.conn.process_input(dgram.unwrap(), now()); + + let data_readable_event = |e: &_| matches!(e, Http3ClientEvent::DataReadable { stream_id } if *stream_id == request_stream_id); + assert_eq!(client.events().filter(data_readable_event).count(), 1); + + let mut buf = [0_u8; 10]; + assert_eq!( + (7, true), + client + .read_response_data(now(), request_stream_id, &mut buf) + .unwrap() + ); + assert!(!client.events().any(|e| data_readable_event(&e))); + } + + #[test] + fn stream_blocked_no_remote_encoder_stream() { + let (mut client, mut server) = connect_only_transport(); + + send_and_receive_client_settings(&mut client, &mut server); + + server.create_control_stream(); + // Send the server's control stream data. + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + server.create_qpack_streams(); + let qpack_pkt1 = server.conn.process(None, now()); + // delay delivery of this packet. + + let request_stream_id = make_request(&mut client, true, &[]); + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + + setup_server_side_encoder(&mut client, &mut server); + + let headers = vec![ + (String::from(":status"), String::from("200")), + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("3")), + ]; + let encoded_headers = server + .encoder + .encode_header_block(&mut server.conn, &headers, request_stream_id) + .unwrap(); + let hframe = HFrame::Headers { + header_block: encoded_headers.to_vec(), + }; + + // Send the encoder instructions, + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + + // Send response + let mut d = Encoder::default(); + hframe.encode(&mut d); + let d_frame = HFrame::Data { len: 3 }; + d_frame.encode(&mut d); + d.encode(&[0x61, 0x62, 0x63]); + let _ = server.conn.stream_send(request_stream_id, &d[..]); + server.conn.stream_close_send(request_stream_id).unwrap(); + + let out = server.conn.process(None, now()); + let _ = client.process(out.dgram(), now()); + + let header_ready_event = |e| matches!(e, Http3ClientEvent::HeaderReady { .. }); + assert!(!client.events().any(header_ready_event)); + + // Let client receive the encoder instructions. + let _ = client.process(qpack_pkt1.dgram(), now()); + + assert!(client.events().any(header_ready_event)); + } + + // Client: receive a push stream + #[test] + fn push_single() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send a push promise. + send_push_promise(&mut server.conn, request_stream_id, 0); + + // create a push stream. + let _ = send_push_data(&mut server.conn, 0, true); + + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_2, + true, + ); + + read_response_and_push_events( + &mut client, + &[PushPromiseInfo { + push_id: 0, + ref_stream_id: request_stream_id, + }], + &[0], + request_stream_id, + ); + + assert_eq!(client.state(), Http3State::Connected); + + // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); + } + + #[test] + fn push_multiple() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send a push promise. + send_push_promise(&mut server.conn, request_stream_id, 0); + send_push_promise(&mut server.conn, request_stream_id, 1); + + // create a push stream. + let _ = send_push_data(&mut server.conn, 0, true); + + // create a second push stream. + let _ = send_push_data(&mut server.conn, 1, true); + + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_2, + true, + ); + + read_response_and_push_events( + &mut client, + &[ + PushPromiseInfo { + push_id: 0, + ref_stream_id: request_stream_id, + }, + PushPromiseInfo { + push_id: 1, + ref_stream_id: request_stream_id, + }, + ], + &[0, 1], + request_stream_id, + ); + + assert_eq!(client.state(), Http3State::Connected); + + // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); + assert_eq!(client.cancel_push(1), Err(Error::InvalidStreamId)); + } + + #[test] + fn push_after_headers() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send response headers + let _ = server + .conn + .stream_send(request_stream_id, HTTP_RESPONSE_HEADER_ONLY_2); + + // Send a push promise. + send_push_promise(&mut server.conn, request_stream_id, 0); + + // create a push stream. + let _ = send_push_data(&mut server.conn, 0, true); + + // Send response data + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_DATA_FRAME_ONLY_2, + true, + ); + + read_response_and_push_events( + &mut client, + &[PushPromiseInfo { + push_id: 0, + ref_stream_id: request_stream_id, + }], + &[0], + request_stream_id, + ); + + assert_eq!(client.state(), Http3State::Connected); + } + + #[test] + fn push_after_response() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send response headers and data frames + let _ = server.conn.stream_send(request_stream_id, HTTP_RESPONSE_2); + + // Send a push promise. + send_push_promise(&mut server.conn, request_stream_id, 0); + // create a push stream. + send_push_data_and_exchange_packets(&mut client, &mut server, 0, true); + + read_response_and_push_events( + &mut client, + &[PushPromiseInfo { + push_id: 0, + ref_stream_id: request_stream_id, + }], + &[0], + request_stream_id, + ); + + assert_eq!(client.state(), Http3State::Connected); + } + + fn check_push_events(client: &mut Http3Client) -> bool { + let any_push_event = |e| { + matches!( + e, + Http3ClientEvent::PushPromise{..} + | Http3ClientEvent::PushHeaderReady{..} + | Http3ClientEvent::PushDataReadable{..}) + }; + client.events().any(any_push_event) + } + + fn check_data_readable(client: &mut Http3Client) -> bool { + let any_data_event = |e| { + matches!( + e, + Http3ClientEvent::DataReadable{..}) + }; + client.events().any(any_data_event) + } + + fn check_header_ready(client: &mut Http3Client) -> bool { + let any_event = |e| { + matches!( + e, + Http3ClientEvent::HeaderReady{..}) + }; + client.events().any(any_event) + } + + fn check_header_ready_and_push_promise(client: &mut Http3Client) -> bool { + let any_event = |e| { + matches!( + e, + Http3ClientEvent::HeaderReady{..} + | Http3ClientEvent::PushPromise{..}) + }; + client.events().any(any_event) + } + + #[test] + fn push_stream_before_promise() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // create a push stream. + send_push_data_and_exchange_packets(&mut client, &mut server, 0, true); + + // Assert that we do not have any push event. + assert!(!check_push_events(&mut client)); + + // Now send push_promise + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 0); + + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_2, + true, + ); + + read_response_and_push_events( + &mut client, + &[PushPromiseInfo { + push_id: 0, + ref_stream_id: request_stream_id, + }], + &[0], + request_stream_id, + ); + + assert_eq!(client.state(), Http3State::Connected); + } + + // Test receiving pushes out of order. + // Push_id 5 is received first, therefore Push_id 3 will be in the PushState:Init state. + // Start push_id 3 by receiving a push_promise and then a push stream with the push_id 3. + #[test] + fn push_out_of_order_1() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 5); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 3); + // Start a push stream with push_id 3. + send_push_data_and_exchange_packets(&mut client, &mut server, 3, true); + + assert_eq!(client.state(), Http3State::Connected); + + read_response_and_push_events( + &mut client, + &[ + PushPromiseInfo { + push_id: 5, + ref_stream_id: request_stream_id, + }, + PushPromiseInfo { + push_id: 3, + ref_stream_id: request_stream_id, + }, + ], + &[3], + request_stream_id, + ); + assert_eq!(client.state(), Http3State::Connected); + } + + // Test receiving pushes out of order. + // Push_id 5 is received first, therefore Push_id 3 will be in the PushState:Init state. + // Start push_id 3 by receiving a push stream with push_id 3 and then a push_promise. + #[test] + fn push_out_of_order_2() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 5); + + send_push_data_and_exchange_packets(&mut client, &mut server, 3, true); + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 3); + + read_response_and_push_events( + &mut client, + &[ + PushPromiseInfo { + push_id: 5, + ref_stream_id: request_stream_id, + }, + PushPromiseInfo { + push_id: 3, + ref_stream_id: request_stream_id, + }, + ], + &[3], + request_stream_id, + ); + assert_eq!(client.state(), Http3State::Connected); + } + + // Test receiving pushes out of order. + // Push_id 5 is received first and read so that it is removed from the list, + // therefore Push_id 3 will be in the PushState:Init state. + // Start push_id 3 by receiving a push stream with the push_id 3 and then a push_promise. + #[test] + fn push_out_of_order_3() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 5); + send_push_data_and_exchange_packets(&mut client, &mut server, 5, true); + assert_eq!(client.state(), Http3State::Connected); + + // Read push stream with push_id 5 to make it change to closed state. + read_response_and_push_events( + &mut client, + &[PushPromiseInfo { + push_id: 5, + ref_stream_id: request_stream_id, + }], + &[5], + request_stream_id, + ); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 3); + send_push_data_and_exchange_packets(&mut client, &mut server, 3, true); + + read_response_and_push_events( + &mut client, + &[PushPromiseInfo { + push_id: 3, + ref_stream_id: request_stream_id, + }], + &[3], + request_stream_id, + ); + assert_eq!(client.state(), Http3State::Connected); + } + + // The next test is for receiving a second PushPromise when Push is in the PushPromise state. + #[test] + fn multiple_push_promise() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 5); + + // make a second request. + let request_stream_id_2 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_2, 4); + + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id_2, 5); + + read_response_and_push_events( + &mut client, + &[ + PushPromiseInfo { + push_id: 5, + ref_stream_id: request_stream_id, + }, + PushPromiseInfo { + push_id: 5, + ref_stream_id: request_stream_id_2, + }, + ], + &[], + request_stream_id, + ); + assert_eq!(client.state(), Http3State::Connected); + } + + // The next test is for receiving a second PushPromise when Push is in the Active state. + #[test] + fn multiple_push_promise_active() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 5); + send_push_data_and_exchange_packets(&mut client, &mut server, 5, true); + + // make a second request. + let request_stream_id_2 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_2, 4); + + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id_2, 5); + + read_response_and_push_events( + &mut client, + &[ + PushPromiseInfo { + push_id: 5, + ref_stream_id: request_stream_id, + }, + PushPromiseInfo { + push_id: 5, + ref_stream_id: request_stream_id_2, + }, + ], + &[5], + request_stream_id, + ); + assert_eq!(client.state(), Http3State::Connected); + } + + // The next test is for receiving a second PushPromise when the push is already closed. + // PushPromise will be ignored for the push streams that are consumed. + #[test] + fn multiple_push_promise_closed() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 5); + // Start a push stream with push_id 5. + send_push_data_and_exchange_packets(&mut client, &mut server, 5, true); + + read_response_and_push_events( + &mut client, + &[PushPromiseInfo { + push_id: 5, + ref_stream_id: request_stream_id, + }], + &[5], + request_stream_id, + ); + + // make a second request. + let request_stream_id_2 = make_request(&mut client, false, &[]); + assert_eq!(request_stream_id_2, 4); + + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id_2, 5); + + // Check that we do not have a Http3ClientEvent::PushPromise. + let push_event = |e| matches!(e, Http3ClientEvent::PushPromise{ .. }); + assert!(!client.events().any(push_event)); + } + + // Test that max_push_id is enforced when a push promise frame is received. + #[test] + fn exceed_max_push_id_promise() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send a push promise. max_push_id is set to 5, to trigger an error we send push_id=6. + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 6); + + assert_closed(&client, &Error::HttpId); + } + + // Test that max_push_id is enforced when a push stream is received. + #[test] + fn exceed_max_push_id_push_stream() { + // Connect and send a request + let (mut client, mut server) = connect(); + + // Send a push stream. max_push_id is set to 5, to trigger an error we send push_id=6. + send_push_data_and_exchange_packets(&mut client, &mut server, 6, true); + + assert_closed(&client, &Error::HttpId); + } + + // Test that max_push_id is enforced when a cancel push frame is received. + #[test] + fn exceed_max_push_id_cancel_push() { + // Connect and send a request + let (mut client, mut server, _request_stream_id) = connect_and_send_request(true); + + // Send CANCEL_PUSH for push_id 6. + send_cancel_push_and_exchange_packets(&mut client, &mut server, 6); + + assert_closed(&client, &Error::HttpId); + } + + // Test that max_push_id is enforced when an app calls cancel_push. + #[test] + fn exceed_max_push_id_cancel_api() { + // Connect and send a request + let (mut client, _, _) = connect_and_send_request(true); + + assert_eq!(client.cancel_push(6), Err(Error::HttpId)); + assert_eq!(client.state(), Http3State::Connected); + } + + #[test] + fn test_max_push_id_frame_update_is_sent() { + const MAX_PUSH_ID_FRAME: &[u8] = &[0xd, 0x1, 0x8]; + + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send 3 push promises. + send_push_promise(&mut server.conn, request_stream_id, 0); + send_push_promise(&mut server.conn, request_stream_id, 1); + send_push_promise(&mut server.conn, request_stream_id, 2); + + // create 3 push streams. + send_push_data(&mut server.conn, 0, true); + send_push_data(&mut server.conn, 1, true); + send_push_data_and_exchange_packets(&mut client, &mut server, 2, true); + + read_response_and_push_events( + &mut client, + &[ + PushPromiseInfo { + push_id: 0, + ref_stream_id: request_stream_id, + }, + PushPromiseInfo { + push_id: 1, + ref_stream_id: request_stream_id, + }, + PushPromiseInfo { + push_id: 2, + ref_stream_id: request_stream_id, + }, + ], + &[0, 1, 2], + request_stream_id, + ); + + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + + // Check max_push_id frame has been received + let control_stream_readable = + |e| matches!(e, ConnectionEvent::RecvStreamReadable{stream_id: x} if x == 2); + assert!(server.conn.events().any(control_stream_readable)); + let mut buf = [0_u8; 100]; + let (amount, fin) = server.conn.stream_recv(2, &mut buf).unwrap(); + assert_eq!(fin, false); + + assert_eq!(amount, MAX_PUSH_ID_FRAME.len()); + assert_eq!(&buf[..3], MAX_PUSH_ID_FRAME); + + // Check that we can send push_id=8 now + send_push_promise(&mut server.conn, request_stream_id, 8); + send_push_data(&mut server.conn, 8, true); + + let out = server.conn.process(None, now()); + let out = client.process(out.dgram(), now()); + let _ = server.conn.process(out.dgram(), now()); + + assert_eq!(client.state(), Http3State::Connected); + + read_response_and_push_events( + &mut client, + &[PushPromiseInfo { + push_id: 8, + ref_stream_id: request_stream_id, + }], + &[8], + request_stream_id, + ); + + assert_eq!(client.state(), Http3State::Connected); + } + + // Test that 2 push streams with the same push_id are caught. + #[test] + fn duplicate_push_stream() { + // Connect and send a request + let (mut client, mut server, _request_stream_id) = connect_and_send_request(true); + + // Start a push stream with push_id 0. + send_push_data_and_exchange_packets(&mut client, &mut server, 0, true); + + // Send it again + send_push_data_and_exchange_packets(&mut client, &mut server, 0, true); + + assert_closed(&client, &Error::HttpId); + } + + // Test that 2 push streams with the same push_id are caught. + #[test] + fn duplicate_push_stream_active() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise(&mut server.conn, request_stream_id, 0); + send_push_data_and_exchange_packets(&mut client, &mut server, 0, true); + // Now the push_stream is in the PushState::Active state + + send_push_data_and_exchange_packets(&mut client, &mut server, 0, true); + + assert_closed(&client, &Error::HttpId); + } + + fn assert_stop_sending_event( + server: &mut TestServer, + push_stream_id: u64, + expected_error: u64, + ) { + assert!(server.conn.events().any(|e| matches!( + e, + ConnectionEvent::SendStreamStopSending { + stream_id, + app_error, + } if stream_id == push_stream_id && app_error == expected_error + ))); + } + + // Test CANCEL_PUSH frame: after cancel push any new PUSH_PROMISE or push stream will be ignored. + #[test] + fn cancel_push_ignore_promise() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_cancel_push_and_exchange_packets(&mut client, &mut server, 0); + + send_push_promise(&mut server.conn, request_stream_id, 0); + // Start a push stream with push_id 0. + let push_stream_id = + send_push_data_and_exchange_packets(&mut client, &mut server, 0, false); + + // Assert that we do not have any push event. + assert!(!check_push_events(&mut client)); + + // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); + + // Check that the push has been canceled by the client. + assert_stop_sending_event( + &mut server, + push_stream_id, + Error::HttpRequestCancelled.code(), + ); + + assert_eq!(client.state(), Http3State::Connected); + } + + // Test CANCEL_PUSH frame: after cancel push any already received PUSH_PROMISE or push stream + // events will be removed. + #[test] + fn cancel_push_removes_push_events() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise(&mut server.conn, request_stream_id, 0); + let push_stream_id = + send_push_data_and_exchange_packets(&mut client, &mut server, 0, false); + + send_cancel_push_and_exchange_packets(&mut client, &mut server, 0); + + // Assert that we do not have any push event. + assert!(!check_push_events(&mut client)); + + // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); + + // Check that the push has been canceled by the client. + assert_stop_sending_event( + &mut server, + push_stream_id, + Error::HttpRequestCancelled.code(), + ); + + assert_eq!(client.state(), Http3State::Connected); + } + + // Test CANCEL_PUSH frame: after cancel push any already received push stream will be canceled. + #[test] + fn cancel_push_frame_after_push_stream() { + // Connect and send a request + let (mut client, mut server, _) = connect_and_send_request(true); + + // Start a push stream with push_id 0. + let push_stream_id = + send_push_data_and_exchange_packets(&mut client, &mut server, 0, false); + + send_cancel_push_and_exchange_packets(&mut client, &mut server, 0); + + // Assert that we do not have any push event. + assert!(!check_push_events(&mut client)); + + // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); + + // Check that the push has been canceled by the client. + assert_stop_sending_event( + &mut server, + push_stream_id, + Error::HttpRequestCancelled.code(), + ); + + assert_eq!(client.state(), Http3State::Connected); + } + + // Test a push stream reset after a new PUSH_PROMISE or/and push stream. The events will be ignored. + #[test] + fn cancel_push_stream_after_push_promise_and_push_stream() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise(&mut server.conn, request_stream_id, 0); + // Start a push stream with push_id 0. + let push_stream_id = + send_push_data_and_exchange_packets(&mut client, &mut server, 0, false); + + server + .conn + .stream_reset_send(push_stream_id, Error::HttpRequestCancelled.code()) + .unwrap(); + let out = server.conn.process(None, now()).dgram(); + client.process(out, now()); + + // Assert that we do not have any push event. + assert!(!check_push_events(&mut client)); + + // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); + + assert_eq!(client.state(), Http3State::Connected); + } + + // Test that a PUSH_PROMISE will be ignored after a push stream reset. + #[test] + fn cancel_push_stream_before_push_promise() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Start a push stream with push_id 0. + let push_stream_id = + send_push_data_and_exchange_packets(&mut client, &mut server, 0, false); + + server + .conn + .stream_reset_send(push_stream_id, Error::HttpRequestCancelled.code()) + .unwrap(); + let out = server.conn.process(None, now()).dgram(); + client.process(out, now()); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 0); + + // Assert that we do not have any push event. + assert!(!check_push_events(&mut client)); + + // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); + + assert_eq!(client.state(), Http3State::Connected); + } + + // Test that push_promise events will be removed after application calls cancel_push. + #[test] + fn app_cancel_push_after_push_promise() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 0); + + assert!(client.cancel_push(0).is_ok()); + + // Assert that we do not have any push event. + assert!(!check_push_events(&mut client)); + + // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); + + assert_eq!(client.state(), Http3State::Connected); + } + + // Test that push_promise and push data events will be removed after application calls cancel_push. + #[test] + fn app_cancel_push_after_push_promise_and_push_stream() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 0); + let push_stream_id = + send_push_data_and_exchange_packets(&mut client, &mut server, 0, false); + + assert!(client.cancel_push(0).is_ok()); + let out = client.process(None, now()).dgram(); + let _ = server.conn.process(out, now()); + + // Assert that we do not have any push event. + assert!(!check_push_events(&mut client)); + + // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); + + // Check that the push has been canceled by the client. + assert_stop_sending_event( + &mut server, + push_stream_id, + Error::HttpRequestCancelled.code(), + ); + + assert_eq!(client.state(), Http3State::Connected); + } + + // Test that push_promise events will be ignored after application calls cancel_push. + #[test] + fn app_cancel_push_before_push_promise() { + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 0); + let push_stream_id = + send_push_data_and_exchange_packets(&mut client, &mut server, 0, false); + + assert!(client.cancel_push(0).is_ok()); + let out = client.process(None, now()).dgram(); + let _ = server.conn.process(out, now()); + + send_push_promise_and_exchange_packets(&mut client, &mut server, request_stream_id, 0); + + // Assert that we do not have any push event. + assert!(!check_push_events(&mut client)); + + // Check that the push has been closed, e.g. calling cancel_push should return InvalidStreamId. + assert_eq!(client.cancel_push(0), Err(Error::InvalidStreamId)); + + // Check that the push has been canceled by the client. + assert_stop_sending_event( + &mut server, + push_stream_id, + Error::HttpRequestCancelled.code(), + ); + + assert_eq!(client.state(), Http3State::Connected); + } + + fn setup_server_side_encoder(client: &mut Http3Client, server: &mut TestServer) { + server.encoder.set_max_capacity(100).unwrap(); + server.encoder.set_max_blocked_streams(100).unwrap(); + server.encoder.send(&mut server.conn).unwrap(); + let out = server.conn.process(None, now()); + let _ = client.process(out.dgram(), now()); + } + + fn send_push_promise_using_encoder( + client: &mut Http3Client, + server: &mut TestServer, + stream_id: u64, + push_id: u64, + ) -> Option<Datagram> { + let headers = vec![ + (String::from(":method"), String::from("GET")), + (String::from(":scheme"), String::from("https")), + (String::from(":authority"), String::from("something.com")), + (String::from(":path"), String::from("/")), + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("3")), + ]; + let encoded_headers = server + .encoder + .encode_header_block(&mut server.conn, &headers, stream_id) + .unwrap(); + let push_promise_frame = HFrame::PushPromise { + push_id, + header_block: encoded_headers.to_vec(), + }; + + // Send the encoder instructions, but delay them so that the stream is blocked on decoding headers. + let encoder_inst_pkt = server.conn.process(None, now()).dgram(); + assert!(encoder_inst_pkt.is_some()); + + let mut d = Encoder::default(); + push_promise_frame.encode(&mut d); + server_send_response_and_exchange_packet(client, server, stream_id, &d, false); + + encoder_inst_pkt + } + + #[test] + fn push_promise_header_decoder_block() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let encoder_inst_pkt = + send_push_promise_using_encoder(&mut client, &mut server, request_stream_id, 0); + + // PushPromise is blocked wathing for encoder instructions. + assert!(!check_push_events(&mut client)); + + // Let client receive the encoder instructions. + let _out = client.process(encoder_inst_pkt, now()); + + // PushPromise is blocked wathing for encoder instructions. + assert!(check_push_events(&mut client)); + } + + // If PushPromise is blocked, stream data can still be received. + #[test] + fn push_promise_blocked_but_stream_is_not_blocked() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + // Send response headers + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_HEADER_ONLY_1, + false, + ); + + let encoder_inst_pkt = + send_push_promise_using_encoder(&mut client, &mut server, request_stream_id, 0); + + // PushPromise is blocked wathing for encoder instructions. + assert!(!check_push_events(&mut client)); + + // Stream data can be still read + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_DATA_FRAME_1_ONLY_1, + false, + ); + + assert!(check_data_readable(&mut client)); + + // Let client receive the encoder instructions. + let _out = client.process(encoder_inst_pkt, now()); + + // PushPromise is blocked wathing for encoder instructions. + assert!(check_push_events(&mut client)); + + // Stream data can be still read + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_DATA_FRAME_2_ONLY_1, + false, + ); + + assert!(check_data_readable(&mut client)); + } + + // The response Headers are not block if they do not refer the dynamic table. + #[test] + fn push_promise_does_not_block_headers() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let encoder_inst_pkt = + send_push_promise_using_encoder(&mut client, &mut server, request_stream_id, 0); + + // PushPromise is blocked wathing for encoder instructions. + assert!(!check_push_events(&mut client)); + + // Send response headers + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_HEADER_ONLY_1, + false, + ); + + assert!(check_header_ready(&mut client)); + + // Let client receive the encoder instructions. + let _out = client.process(encoder_inst_pkt, now()); + + // PushPromise is blocked wathing for encoder instructions. + assert!(check_push_events(&mut client)); + } + + // The response Headers are blocked if they refer a dynamic table entry. + #[test] + fn push_promise_block_headers() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + // Insert an elemet into a dynamic table. + // insert "content-length: 1234 + server + .encoder + .send_and_insert(&mut server.conn, b"content-length", b"1234") + .unwrap(); + let encoder_inst_pkt1 = server.conn.process(None, now()).dgram(); + let _out = client.process(encoder_inst_pkt1, now()); + + // Send a PushPromise that is blocked until encoder_inst_pkt2 is process by the client. + let encoder_inst_pkt2 = + send_push_promise_using_encoder(&mut client, &mut server, request_stream_id, 0); + + // PushPromise is blocked wathing for encoder instructions. + assert!(!check_push_events(&mut client)); + + let response_headers = vec![ + (String::from(":status"), String::from("200")), + (String::from("content-length"), String::from("1234")), + ]; + let encoded_headers = server + .encoder + .encode_header_block(&mut server.conn, &response_headers, request_stream_id) + .unwrap(); + let header_hframe = HFrame::Headers { + header_block: encoded_headers.to_vec(), + }; + let mut d = Encoder::default(); + header_hframe.encode(&mut d); + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + &d, + false, + ); + + // The response headers are blocked. + assert!(!check_header_ready(&mut client)); + + // Let client receive the encoder instructions. + let _out = client.process(encoder_inst_pkt2, now()); + + // The response headers are blocked. + assert!(check_header_ready_and_push_promise(&mut client)); + } + + // The PushPromise blocked on header decoding will be canceled if the stream is closed. + #[test] + fn blocked_push_promises_canceled() { + const STREAM_CANCELED_ID_0: &[u8] = &[0x40]; + + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let _encoder_inst_pkt = + send_push_promise_using_encoder(&mut client, &mut server, request_stream_id, 0); + + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_1, + true, + ); + + // Read response that will make stream change to closed state. + assert!(check_header_ready(&mut client)); + let mut buf = [0_u8; 100]; + let _ = client + .read_response_data(now(), request_stream_id, &mut buf) + .unwrap(); + + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + // Check that encoder got stream_canceled instruction. + let mut inst = [0_u8; 100]; + let (amount, fin) = server + .conn + .stream_recv(CLIENT_SIDE_DECODER_STREAM_ID, &mut inst) + .unwrap(); + assert_eq!(fin, false); + assert_eq!(amount, STREAM_CANCELED_ID_0.len()); + assert_eq!(&inst[..amount], STREAM_CANCELED_ID_0); + } + + #[test] + fn data_readable_in_decoder_blocked_state() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let headers = vec![ + (String::from(":status"), String::from("200")), + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("0")), + ]; + let encoded_headers = server + .encoder + .encode_header_block(&mut server.conn, &headers, request_stream_id) + .unwrap(); + let hframe = HFrame::Headers { + header_block: encoded_headers.to_vec(), + }; + + // Delay encoder instruction so that the stream will be blocked. + let encoder_insts = server.conn.process(None, now()); + + // Send response headers. + let mut d = Encoder::default(); + hframe.encode(&mut d); + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + &d, + false, + ); + + // Headers are blocked waiting fro the encoder instructions. + let header_ready_event = |e| matches!(e, Http3ClientEvent::HeaderReady { .. }); + assert!(!client.events().any(header_ready_event)); + + // Now send data frame. This will trigger DataRead event. + let mut d = Encoder::default(); + hframe.encode(&mut d); + let d_frame = HFrame::Data { len: 0 }; + d_frame.encode(&mut d); + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + &d, + true, + ); + + // Now read headers. + let _ = client.process(encoder_insts.dgram(), now()); + } + + #[test] + fn qpack_stream_reset() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + setup_server_side_encoder(&mut client, &mut server); + // Cancel request. + let _ = client.stream_reset(request_stream_id, Error::HttpRequestCancelled.code()); + assert_eq!(server.encoder.stats().stream_cancelled_recv, 0); + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + let _ = server + .encoder + .recv_if_encoder_stream(&mut server.conn, CLIENT_SIDE_DECODER_STREAM_ID); + assert_eq!(server.encoder.stats().stream_cancelled_recv, 1); + } + + fn send_headers_using_encoder( + client: &mut Http3Client, + server: &mut TestServer, + request_stream_id: u64, + headers: &[(String, String)], + data: &[u8], + ) -> Option<Datagram> { + let encoded_headers = server + .encoder + .encode_header_block(&mut server.conn, &headers, request_stream_id) + .unwrap(); + let hframe = HFrame::Headers { + header_block: encoded_headers.to_vec(), + }; + + let out = server.conn.process(None, now()); + + // Send response + let mut d = Encoder::default(); + hframe.encode(&mut d); + let d_frame = HFrame::Data { + len: u64::try_from(data.len()).unwrap(), + }; + d_frame.encode(&mut d); + d.encode(data); + server_send_response_and_exchange_packet(client, server, request_stream_id, &d, true); + + out.dgram() + } + + #[test] + fn qpack_stream_reset_recv() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + setup_server_side_encoder(&mut client, &mut server); + + // Cancel request. + let _ = server + .conn + .stream_reset_send(request_stream_id, Error::HttpRequestCancelled.code()); + assert_eq!(server.encoder.stats().stream_cancelled_recv, 0); + let out = server.conn.process(None, now()); + let out = client.process(out.dgram(), now()); + let _ = server.conn.process(out.dgram(), now()); + let _ = server + .encoder + .recv_if_encoder_stream(&mut server.conn, CLIENT_SIDE_DECODER_STREAM_ID); + assert_eq!(server.encoder.stats().stream_cancelled_recv, 1); + } + + #[test] + fn qpack_stream_reset_during_header_qpack_blocked() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let _ = send_headers_using_encoder( + &mut client, + &mut server, + request_stream_id, + &[ + (String::from(":status"), String::from("200")), + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("3")), + ], + &[0x61, 0x62, 0x63], + ); + + let header_ready_event = |e| matches!(e, Http3ClientEvent::HeaderReady { .. }); + assert!(!client.events().any(header_ready_event)); + + // Cancel request. + let _ = client.stream_reset(request_stream_id, Error::HttpRequestCancelled.code()); + + assert_eq!(server.encoder.stats().stream_cancelled_recv, 0); + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + let _ = server + .encoder + .recv_if_encoder_stream(&mut server.conn, CLIENT_SIDE_DECODER_STREAM_ID); + assert_eq!(server.encoder.stats().stream_cancelled_recv, 1); + } + + #[test] + fn qpack_no_stream_cancelled_after_fin() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let encoder_instruct = send_headers_using_encoder( + &mut client, + &mut server, + request_stream_id, + &[ + (String::from(":status"), String::from("200")), + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("3")), + ], + &[], + ); + + // Exchange encoder instructions + let _ = client.process(encoder_instruct, now()); + + let header_ready_event = |e| matches!(e, Http3ClientEvent::HeaderReady { .. }); + assert!(client.events().any(header_ready_event)); + // After this the recv_stream is in ClosePending state + + // Cancel request. + let _ = client.stream_reset(request_stream_id, Error::HttpRequestCancelled.code()); + + assert_eq!(server.encoder.stats().stream_cancelled_recv, 0); + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + let _ = server + .encoder + .recv_if_encoder_stream(&mut server.conn, CLIENT_SIDE_DECODER_STREAM_ID); + assert_eq!(server.encoder.stats().stream_cancelled_recv, 0); + } + + #[test] + fn qpack_stream_reset_push_promise_header_decoder_block() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let headers = vec![ + (String::from(":status"), String::from("200")), + (String::from("content-length"), String::from("3")), + ]; + let encoded_headers = server + .encoder + .encode_header_block(&mut server.conn, &headers, request_stream_id) + .unwrap(); + let hframe = HFrame::Headers { + header_block: encoded_headers.to_vec(), + }; + + // Send the encoder instructions. + let out = server.conn.process(None, now()); + let _ = client.process(out.dgram(), now()); + + // Send PushPromise that will be blocked waiting for decoder instructions. + let _ = send_push_promise_using_encoder(&mut client, &mut server, request_stream_id, 0); + + // Send response + let mut d = Encoder::default(); + hframe.encode(&mut d); + let d_frame = HFrame::Data { len: 0 }; + d_frame.encode(&mut d); + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + &d, + true, + ); + + let header_ready_event = |e| matches!(e, Http3ClientEvent::HeaderReady { .. }); + assert!(client.events().any(header_ready_event)); + + // Cancel request. + let _ = client.stream_reset(request_stream_id, Error::HttpRequestCancelled.code()); + + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + let _ = server + .encoder + .recv_if_encoder_stream(&mut server.conn, CLIENT_SIDE_DECODER_STREAM_ID); + assert_eq!(server.encoder.stats().stream_cancelled_recv, 1); + } + + #[test] + fn qpack_stream_reset_dynamic_table_zero() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + // Cancel request. + let _ = client.stream_reset(request_stream_id, Error::HttpRequestCancelled.code()); + assert_eq!(server.encoder.stats().stream_cancelled_recv, 0); + let out = client.process(None, now()); + let _ = server.conn.process(out.dgram(), now()); + let _ = server + .encoder + .recv_if_encoder_stream(&mut server.conn, CLIENT_SIDE_DECODER_STREAM_ID); + assert_eq!(server.encoder.stats().stream_cancelled_recv, 0); + } + + #[test] + fn multiple_streams_in_decoder_blocked_state() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let headers = vec![ + (String::from(":status"), String::from("200")), + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("0")), + ]; + let encoded_headers = server + .encoder + .encode_header_block(&mut server.conn, &headers, request_stream_id) + .unwrap(); + let hframe = HFrame::Headers { + header_block: encoded_headers.to_vec(), + }; + + // Delay encoder instruction so that the stream will be blocked. + let encoder_insts = server.conn.process(None, now()); + + // Send response headers. + let mut d = Encoder::default(); + hframe.encode(&mut d); + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + &d, + true, + ); + + // Headers are blocked waiting for the encoder instructions. + let header_ready_event = |e| matches!(e, Http3ClientEvent::HeaderReady { .. }); + assert!(!client.events().any(header_ready_event)); + + // Make another request. + let request2 = make_request_and_exchange_pkts(&mut client, &mut server, true); + // Send response headers. + server_send_response_and_exchange_packet(&mut client, &mut server, request2, &d, true); + + // Headers on the second request are blocked as well are blocked + // waiting for the encoder instructions. + assert!(!client.events().any(header_ready_event)); + + // Now make the encoder instructions available. + let _ = client.process(encoder_insts.dgram(), now()); + + // Header blocks for both streams should be ready. + let mut count_responses = 0; + while let Some(e) = client.next_event() { + if let Http3ClientEvent::HeaderReady { stream_id, .. } = e { + assert!((stream_id == request_stream_id) || (stream_id == request2)); + count_responses += 1; + } + } + assert_eq!(count_responses, 2); + } + + #[test] + fn reserved_frames() { + for f in H3_RESERVED_FRAME_TYPES { + let mut enc = Encoder::default(); + enc.encode_varint(*f); + test_wrong_frame_on_control_stream(&enc); + test_wrong_frame_on_push_stream(&enc); + test_wrong_frame_on_request_stream(&enc); + } + } + + #[test] + fn send_reserved_settings() { + for s in H3_RESERVED_SETTINGS { + let (mut client, mut server) = connect_only_transport(); + let control_stream = server.conn.stream_create(StreamType::UniDi).unwrap(); + // Send the control stream type(0x0). + let _ = server.conn.stream_send(control_stream, CONTROL_STREAM_TYPE); + // Create a settings frame of length 2. + let mut enc = Encoder::default(); + enc.encode_varint(H3_FRAME_TYPE_SETTINGS); + enc.encode_varint(2_u64); + // The settings frame contains a reserved settings type and some value (0x1). + enc.encode_varint(*s); + enc.encode_varint(1_u64); + let sent = server.conn.stream_send(control_stream, &enc); + assert_eq!(sent, Ok(4)); + let out = server.conn.process(None, now()); + client.process(out.dgram(), now()); + assert_closed(&client, &Error::HttpSettings); + } + } + + #[test] + fn response_w_1xx() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let mut d = Encoder::default(); + let headers1xx = vec![(String::from(":status"), String::from("103"))]; + server.encode_headers(request_stream_id, &headers1xx, &mut d); + + let headers200 = vec![ + (String::from(":status"), String::from("200")), + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("3")), + ]; + server.encode_headers(request_stream_id, &headers200, &mut d); + + // Send 1xx and 200 headers response. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + &d, + false, + ); + + // Sending response data. + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_DATA_FRAME_ONLY_2, + true, + ); + + let mut events = client.events().filter_map(|e| { + if let Http3ClientEvent::HeaderReady { + stream_id, + interim, + headers, + .. + } = e + { + Some((stream_id, interim, headers)) + } else { + None + } + }); + let (stream_id_1xx_rec, interim1xx_rec, headers1xx_rec) = events.next().unwrap(); + assert_eq!( + (stream_id_1xx_rec, interim1xx_rec, headers1xx_rec), + (request_stream_id, true, headers1xx) + ); + + let (stream_id_200_rec, interim200_rec, headers200_rec) = events.next().unwrap(); + assert_eq!( + (stream_id_200_rec, interim200_rec, headers200_rec), + (request_stream_id, false, headers200) + ); + assert!(events.next().is_none()); + } + + #[test] + fn response_wo_status() { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let mut d = Encoder::default(); + let headers = vec![ + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("3")), + ]; + server.encode_headers(request_stream_id, &headers, &mut d); + + // Send response + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + &d, + false, + ); + + // Stream has been reset because of the malformed headers. + let e = client.events().next().unwrap(); + assert_eq!( + e, + Http3ClientEvent::Reset { + stream_id: request_stream_id, + error: Error::InvalidHeader.code(), + local: true, + } + ); + + let out = client.process(None, now()).dgram(); + let _ = server.conn.process(out, now()); + + // Check that server has received a reset. + let stop_sending_event = |e| { + matches!(e, ConnectionEvent::SendStreamStopSending { + stream_id, + app_error + } if stream_id == request_stream_id && app_error == Error::InvalidHeader.code()) + }; + assert!(server.conn.events().any(stop_sending_event)); + + // Stream should now be closed and gone + let mut buf = [0_u8; 100]; + assert_eq!( + client.read_response_data(now(), 0, &mut buf), + Err(Error::InvalidStreamId) + ); + } + + // Client: receive a push stream + #[test] + fn push_single_with_1xx() { + const FIRST_PUSH_ID: u64 = 0; + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send a push promise. + send_push_promise(&mut server.conn, request_stream_id, FIRST_PUSH_ID); + // Create a push stream + let push_stream_id = server.conn.stream_create(StreamType::UniDi).unwrap(); + + let mut d = Encoder::default(); + let headers1xx = vec![(String::from(":status"), String::from("101"))]; + server.encode_headers(push_stream_id, &headers1xx, &mut d); + + let headers200 = vec![ + (String::from(":status"), String::from("200")), + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("3")), + ]; + server.encode_headers(push_stream_id, &headers200, &mut d); + + // create a push stream. + send_data_on_push( + &mut server.conn, + push_stream_id, + u8::try_from(FIRST_PUSH_ID).unwrap(), + &d, + true, + ); + + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_2, + true, + ); + + let mut events = client.events().filter_map(|e| { + if let Http3ClientEvent::PushHeaderReady { + push_id, + interim, + headers, + .. + } = e + { + Some((push_id, interim, headers)) + } else { + None + } + }); + + let (push_id_1xx_rec, interim1xx_rec, headers1xx_rec) = events.next().unwrap(); + assert_eq!( + (push_id_1xx_rec, interim1xx_rec, headers1xx_rec), + (FIRST_PUSH_ID, true, headers1xx) + ); + + let (push_id_200_rec, interim200_rec, headers200_rec) = events.next().unwrap(); + assert_eq!( + (push_id_200_rec, interim200_rec, headers200_rec), + (FIRST_PUSH_ID, false, headers200) + ); + assert!(events.next().is_none()); + } + + // Client: receive a push stream + #[test] + fn push_single_wo_status() { + const FIRST_PUSH_ID: u64 = 0; + // Connect and send a request + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + // Send a push promise. + send_push_promise(&mut server.conn, request_stream_id, FIRST_PUSH_ID); + // Create a push stream + let push_stream_id = server.conn.stream_create(StreamType::UniDi).unwrap(); + + let mut d = Encoder::default(); + let headers = vec![ + (String::from("my-header"), String::from("my-header")), + (String::from("content-length"), String::from("3")), + ]; + server.encode_headers(request_stream_id, &headers, &mut d); + + send_data_on_push( + &mut server.conn, + push_stream_id, + u8::try_from(FIRST_PUSH_ID).unwrap(), + &d, + false, + ); + + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + HTTP_RESPONSE_2, + true, + ); + + // Stream has been reset because of thei malformed headers. + let push_reset_event = |e| { + matches!(e, Http3ClientEvent::PushReset { + push_id, + error, + } if push_id == FIRST_PUSH_ID && error == Error::InvalidHeader.code()) + }; + + assert!(client.events().any(push_reset_event)); + + let out = client.process(None, now()).dgram(); + let _ = server.conn.process(out, now()); + + // Check that server has received a reset. + let stop_sending_event = |e| { + matches!(e, ConnectionEvent::SendStreamStopSending { + stream_id, + app_error + } if stream_id == push_stream_id && app_error == Error::InvalidHeader.code()) + }; + assert!(server.conn.events().any(stop_sending_event)); + } + + fn handshake_client_error(client: &mut Http3Client, server: &mut TestServer, error: &Error) { + let out = handshake_only(client, server); + client.process(out.dgram(), now()); + assert_closed(&client, error); + } + + /// Client fails to create a control stream, since server does not allow it. + #[test] + fn client_control_stream_create_failed() { + let mut client = default_http3_client(); + let mut server = TestServer::new(); + server.set_max_uni_stream(0); + handshake_client_error(&mut client, &mut server, &Error::StreamLimitError); + } + + /// 2 streams isn't enough for control and QPACK streams. + #[test] + fn client_qpack_stream_create_failed() { + let mut client = default_http3_client(); + let mut server = TestServer::new(); + server.set_max_uni_stream(2); + handshake_client_error(&mut client, &mut server, &Error::StreamLimitError); + } + + fn do_malformed_response_test(headers: &[Header]) { + let (mut client, mut server, request_stream_id) = connect_and_send_request(true); + + setup_server_side_encoder(&mut client, &mut server); + + let mut d = Encoder::default(); + server.encode_headers(request_stream_id, &headers, &mut d); + + // Send response + server_send_response_and_exchange_packet( + &mut client, + &mut server, + request_stream_id, + &d, + false, + ); + + // Stream has been reset because of the malformed headers. + let e = client.events().next().unwrap(); + assert_eq!( + e, + Http3ClientEvent::Reset { + stream_id: request_stream_id, + error: Error::InvalidHeader.code(), + local: true, + } + ); + } + + #[test] + fn malformed_response_pseudo_header_after_regular_header() { + do_malformed_response_test(&[ + (String::from("content-type"), String::from("text/plain")), + (String::from(":status"), String::from("100")), + ]); + } + + #[test] + fn malformed_response_undefined_pseudo_header() { + do_malformed_response_test(&[ + (String::from(":status"), String::from("200")), + (String::from(":cheese"), String::from("200")), + ]); + } + + #[test] + fn malformed_response_duplicate_pseudo_header() { + do_malformed_response_test(&[ + (String::from(":status"), String::from("200")), + (String::from(":status"), String::from("100")), + (String::from("content-type"), String::from("text/plain")), + ]); + } + + #[test] + fn malformed_response_uppercase_header() { + do_malformed_response_test(&[ + (String::from(":status"), String::from("200")), + (String::from("content-Type"), String::from("text/plain")), + ]); + } + + #[test] + fn malformed_response_excluded_header() { + do_malformed_response_test(&[ + (String::from(":status"), String::from("200")), + (String::from("content-type"), String::from("text/plain")), + (String::from("connection"), String::from("close")), + ]); + } + + #[test] + fn malformed_response_excluded_byte_in_header() { + do_malformed_response_test(&[ + (String::from(":status"), String::from("200")), + (String::from("content:type"), String::from("text/plain")), + ]); + } + + #[test] + fn malformed_response_request_header_in_response() { + do_malformed_response_test(&[ + (String::from(":status"), String::from("200")), + (String::from(":method"), String::from("GET")), + (String::from("content-type"), String::from("text/plain")), + ]); + } + + fn maybe_authenticate(conn: &mut Http3Client) { + let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded); + if conn.events().any(authentication_needed) { + conn.authenticated(AuthenticationStatus::Ok, now()); + } + } + + const MAX_TABLE_SIZE: u64 = 65536; + const MAX_BLOCKED_STREAMS: u16 = 5; + + fn get_resumption_token(server: &mut Http3Server) -> ResumptionToken { + let mut client = default_http3_client_param(MAX_TABLE_SIZE); + + let mut datagram = None; + let is_done = |c: &Http3Client| matches!(c.state(), Http3State::Connected); + while !is_done(&mut client) { + maybe_authenticate(&mut client); + datagram = client.process(datagram, now()).dgram(); + datagram = server.process(datagram, now()).dgram(); + } + + // exchange qpack settings, server will send a token as well. + datagram = client.process(datagram, now()).dgram(); + datagram = server.process(datagram, now()).dgram(); + let _ = client.process(datagram, now()).dgram(); + + client + .events() + .find_map(|e| { + if let Http3ClientEvent::ResumptionToken(token) = e { + Some(token) + } else { + None + } + }) + .unwrap() + } + + // Test that decoder stream type is always sent before any other instruction also + // in case when 0RTT is used. + // A client will send a request that uses the dynamic table. This will trigger a header-ack + // from a server. We will use stats to check that a header-ack has been received. + #[test] + fn zerortt_request_use_dynamic_table() { + let mut server = Http3Server::new( + now(), + DEFAULT_KEYS, + DEFAULT_ALPN_H3, + anti_replay(), + Rc::new(RefCell::new(FixedConnectionIdManager::new(5))), + QpackSettings { + max_table_size_encoder: MAX_TABLE_SIZE, + max_table_size_decoder: MAX_TABLE_SIZE, + max_blocked_streams: MAX_BLOCKED_STREAMS, + }, + ) + .unwrap(); + + let token = get_resumption_token(&mut server); + // Make a new connection. + let mut client = default_http3_client_param(MAX_TABLE_SIZE); + assert_eq!(client.state(), Http3State::Initializing); + client + .enable_resumption(now(), &token) + .expect("Set resumption token."); + + assert_eq!(client.state(), Http3State::ZeroRtt); + let zerortt_event = |e| matches!(e, Http3ClientEvent::StateChange(Http3State::ZeroRtt)); + assert!(client.events().any(zerortt_event)); + + // Make a request that uses the dynamic table. + let _ = make_request( + &mut client, + true, + &[(String::from("myheaders"), String::from("myvalue"))], + ); + // Assert that the request has used dynamic table. That will trigger a header_ack. + assert_eq!(client.qpack_encoder_stats().dynamic_table_references, 1); + + // Exchange packets until header-ack is received. + // These many packet exchange is needed, to get a header-ack. + // TODO this may be optimize at Http3Server. + let out = client.process(None, now()).dgram(); + let out = server.process(out, now()).dgram(); + let out = client.process(out, now()).dgram(); + let out = server.process(out, now()).dgram(); + let out = client.process(out, now()).dgram(); + let out = server.process(out, now()).dgram(); + let out = client.process(out, now()).dgram(); + let out = server.process(out, now()).dgram(); + let _ = client.process(out, now()); + + // The header ack for the first request has been received. + assert_eq!(client.qpack_encoder_stats().header_acks_recv, 1); + } +} diff --git a/third_party/rust/neqo-http3/src/connection_server.rs b/third_party/rust/neqo-http3/src/connection_server.rs new file mode 100644 index 0000000000..f0b18dd14c --- /dev/null +++ b/third_party/rust/neqo-http3/src/connection_server.rs @@ -0,0 +1,239 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::connection::{HandleReadableOutput, Http3Connection, Http3State}; +use crate::hframe::HFrame; +use crate::recv_message::{MessageType, RecvMessage}; +use crate::send_message::SendMessage; +use crate::server_connection_events::{Http3ServerConnEvent, Http3ServerConnEvents}; +use crate::{Error, Header, Res}; +use neqo_common::{event::Provider, qdebug, qinfo, qtrace}; +use neqo_qpack::QpackSettings; +use neqo_transport::{AppError, Connection, ConnectionEvent, StreamType}; +use std::time::Instant; + +#[derive(Debug)] +pub struct Http3ServerHandler { + base_handler: Http3Connection, + events: Http3ServerConnEvents, + needs_processing: bool, +} + +impl ::std::fmt::Display for Http3ServerHandler { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Http3 server connection") + } +} + +impl Http3ServerHandler { + pub(crate) fn new(qpack_settings: QpackSettings) -> Self { + Self { + base_handler: Http3Connection::new(qpack_settings), + events: Http3ServerConnEvents::default(), + needs_processing: false, + } + } + + /// Supply a response for a request. + pub(crate) fn set_response( + &mut self, + stream_id: u64, + headers: &[Header], + data: &[u8], + ) -> Res<()> { + self.base_handler + .send_streams + .get_mut(&stream_id) + .ok_or(Error::InvalidStreamId)? + .set_message(headers, Some(data))?; + self.base_handler + .insert_streams_have_data_to_send(stream_id); + Ok(()) + } + + /// Reset a request. + pub fn stream_reset( + &mut self, + conn: &mut Connection, + stream_id: u64, + app_error: AppError, + ) -> Res<()> { + self.base_handler.stream_reset(conn, stream_id, app_error)?; + self.events.remove_events_for_stream_id(stream_id); + self.needs_processing = true; + Ok(()) + } + + /// Process HTTTP3 layer. + pub fn process_http3(&mut self, conn: &mut Connection, now: Instant) { + qtrace!([self], "Process http3 internal."); + if matches!(self.base_handler.state(), Http3State::Closed(..)) { + return; + } + + let res = self.check_connection_events(conn, now); + if !self.check_result(conn, now, &res) && self.base_handler.state().active() { + let res = self.base_handler.process_sending(conn); + self.check_result(conn, now, &res); + } + } + + /// Take the next available event. + pub(crate) fn next_event(&mut self) -> Option<Http3ServerConnEvent> { + self.events.next_event() + } + + /// Whether this connection has events to process or data to send. + pub(crate) fn should_be_processed(&mut self) -> bool { + if self.needs_processing { + self.needs_processing = false; + return true; + } + self.base_handler.has_data_to_send() | self.events.has_events() + } + + // This function takes the provided result and check for an error. + // An error results in closing the connection. + fn check_result<ERR>(&mut self, conn: &mut Connection, now: Instant, res: &Res<ERR>) -> bool { + match &res { + Err(e) => { + self.close(conn, now, e); + true + } + _ => false, + } + } + + fn close(&mut self, conn: &mut Connection, now: Instant, err: &Error) { + qinfo!([self], "Connection error: {}.", err); + conn.close(now, err.code(), &format!("{}", err)); + self.base_handler.close(err.code()); + self.events + .connection_state_change(self.base_handler.state()); + } + + // If this return an error the connection must be closed. + fn check_connection_events(&mut self, conn: &mut Connection, now: Instant) -> Res<()> { + qtrace!([self], "Check connection events."); + while let Some(e) = conn.next_event() { + qdebug!([self], "check_connection_events - event {:?}.", e); + match e { + ConnectionEvent::NewStream { stream_id } => match stream_id.stream_type() { + StreamType::BiDi => self.base_handler.add_streams( + stream_id.as_u64(), + SendMessage::new(stream_id.as_u64(), Box::new(self.events.clone())), + Box::new(RecvMessage::new( + MessageType::Request, + stream_id.as_u64(), + Box::new(self.events.clone()), + None, + )), + ), + StreamType::UniDi => { + if self + .base_handler + .handle_new_unidi_stream(conn, stream_id.as_u64())? + { + return Err(Error::HttpStreamCreation); + } + } + }, + ConnectionEvent::RecvStreamReadable { stream_id } => { + self.handle_stream_readable(conn, stream_id)? + } + ConnectionEvent::RecvStreamReset { + stream_id, + app_error, + } => { + self.base_handler + .handle_stream_reset(stream_id, app_error)?; + } + ConnectionEvent::SendStreamStopSending { + stream_id, + app_error, + } => self + .base_handler + .handle_stream_stop_sending(stream_id, app_error)?, + ConnectionEvent::StateChange(state) => { + if self.base_handler.handle_state_change(conn, &state)? { + if self.base_handler.state() == Http3State::Connected { + let settings = self.base_handler.save_settings(); + conn.send_ticket(now, &settings)?; + } + self.events + .connection_state_change(self.base_handler.state()); + } + } + ConnectionEvent::AuthenticationNeeded + | ConnectionEvent::ZeroRttRejected + | ConnectionEvent::ResumptionToken(..) => return Err(Error::HttpInternal), + ConnectionEvent::SendStreamWritable { .. } + | ConnectionEvent::SendStreamComplete { .. } + | ConnectionEvent::SendStreamCreatable { .. } => {} + } + } + Ok(()) + } + + fn handle_stream_readable(&mut self, conn: &mut Connection, stream_id: u64) -> Res<()> { + match self.base_handler.handle_stream_readable(conn, stream_id)? { + HandleReadableOutput::PushStream => Err(Error::HttpStreamCreation), + HandleReadableOutput::ControlFrames(control_frames) => { + for f in control_frames { + match f { + HFrame::MaxPushId { .. } => { + // TODO implement push + Ok(()) + } + HFrame::Goaway { .. } | HFrame::CancelPush { .. } => { + Err(Error::HttpFrameUnexpected) + } + _ => unreachable!( + "we should only put MaxPushId and Goaway into control_frames." + ), + }?; + } + Ok(()) + } + _ => Ok(()), + } + } + + /// Response data are read directly into a buffer supplied as a parameter of this function to avoid copying + /// data. + /// # Errors + /// It returns an error if a stream does not exist or an error happen while reading a stream, e.g. + /// early close, protocol error, etc. + pub fn read_request_data( + &mut self, + conn: &mut Connection, + now: Instant, + stream_id: u64, + buf: &mut [u8], + ) -> Res<(usize, bool)> { + qinfo!([self], "read_data from stream {}.", stream_id); + match self.base_handler.recv_streams.get_mut(&stream_id) { + None => { + self.close(conn, now, &Error::Internal); + Err(Error::Internal) + } + Some(recv_stream) => { + match recv_stream.read_data(conn, &mut self.base_handler.qpack_decoder, buf) { + Ok((amount, fin)) => { + if recv_stream.done() { + self.base_handler.recv_streams.remove(&stream_id); + } + Ok((amount, fin)) + } + Err(e) => { + self.close(conn, now, &e); + Err(e) + } + } + } + } + } +} diff --git a/third_party/rust/neqo-http3/src/control_stream_local.rs b/third_party/rust/neqo-http3/src/control_stream_local.rs new file mode 100644 index 0000000000..2d6e693327 --- /dev/null +++ b/third_party/rust/neqo-http3/src/control_stream_local.rs @@ -0,0 +1,71 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::hframe::HFrame; +use crate::Res; +use neqo_common::{qtrace, Encoder}; +use neqo_transport::{Connection, StreamType}; +use std::convert::TryFrom; + +pub const HTTP3_UNI_STREAM_TYPE_CONTROL: u64 = 0x0; + +// The local control stream, responsible for encoding frames and sending them +#[derive(Debug)] +pub(crate) struct ControlStreamLocal { + stream_id: Option<u64>, + buf: Vec<u8>, +} + +impl ::std::fmt::Display for ControlStreamLocal { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Local control stream {:?}", self.stream_id) + } +} + +impl ControlStreamLocal { + pub fn new() -> Self { + Self { + stream_id: None, + buf: vec![u8::try_from(HTTP3_UNI_STREAM_TYPE_CONTROL).unwrap()], + } + } + + /// Add a new frame that needs to be send. + pub fn queue_frame(&mut self, f: &HFrame) { + let mut enc = Encoder::default(); + f.encode(&mut enc); + self.buf.append(&mut enc.into()); + } + + /// Send control data if available. + pub fn send(&mut self, conn: &mut Connection) -> Res<()> { + if let Some(stream_id) = self.stream_id { + if !self.buf.is_empty() { + qtrace!([self], "sending data."); + let sent = conn.stream_send(stream_id, &self.buf[..])?; + if sent == self.buf.len() { + self.buf.clear(); + } else { + let b = self.buf.split_off(sent); + self.buf = b; + } + } + } + Ok(()) + } + + /// Create a control stream. + pub fn create(&mut self, conn: &mut Connection) -> Res<()> { + qtrace!([self], "Create a control stream."); + self.stream_id = Some(conn.stream_create(StreamType::UniDi)?); + Ok(()) + } + + #[must_use] + pub fn stream_id(&self) -> Option<u64> { + self.stream_id + } +} diff --git a/third_party/rust/neqo-http3/src/control_stream_remote.rs b/third_party/rust/neqo-http3/src/control_stream_remote.rs new file mode 100644 index 0000000000..69ca4712c2 --- /dev/null +++ b/third_party/rust/neqo-http3/src/control_stream_remote.rs @@ -0,0 +1,68 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::hframe::{HFrame, HFrameReader}; +use crate::{Error, Res}; +use neqo_common::{qdebug, qinfo}; +use neqo_transport::Connection; + +/// The remote control stream is responsible only for reading frames. The frames are handled by `Http3Connection`. +#[derive(Debug)] +pub(crate) struct ControlStreamRemote { + stream_id: Option<u64>, + frame_reader: HFrameReader, + fin: bool, +} + +impl ::std::fmt::Display for ControlStreamRemote { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Http3 remote control stream {:?}", self.stream_id) + } +} + +impl ControlStreamRemote { + pub fn new() -> Self { + Self { + stream_id: None, + frame_reader: HFrameReader::new(), + fin: false, + } + } + + /// A remote control stream has been received. Inform `ControlStreamRemote`. + pub fn add_remote_stream(&mut self, stream_id: u64) -> Res<()> { + qinfo!([self], "A new control stream {}.", stream_id); + if self.stream_id.is_some() { + qdebug!([self], "A control stream already exists"); + return Err(Error::HttpStreamCreation); + } + self.stream_id = Some(stream_id); + Ok(()) + } + + /// Check if `stream_id` is the remote control stream. + pub fn is_recv_stream(&self, stream_id: u64) -> bool { + matches!(self.stream_id, Some(id) if id == stream_id) + } + + /// Check if a stream is the control stream and read received data. + pub fn receive(&mut self, conn: &mut Connection) -> Res<Option<HFrame>> { + assert!(self.stream_id.is_some()); + qdebug!([self], "Receiving data."); + match self.frame_reader.receive(conn, self.stream_id.unwrap())? { + (_, true) => { + self.fin = true; + Err(Error::HttpClosedCriticalStream) + } + (s, false) => Ok(s), + } + } + + #[must_use] + pub fn stream_id(&self) -> Option<u64> { + self.stream_id + } +} diff --git a/third_party/rust/neqo-http3/src/hframe.rs b/third_party/rust/neqo-http3/src/hframe.rs new file mode 100644 index 0000000000..f8043527e7 --- /dev/null +++ b/third_party/rust/neqo-http3/src/hframe.rs @@ -0,0 +1,874 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::settings::HSettings; +use neqo_common::{ + hex_with_len, qtrace, Decoder, Encoder, IncrementalDecoderBuffer, IncrementalDecoderIgnore, + IncrementalDecoderUint, +}; +use neqo_crypto::random; +use neqo_transport::Connection; +use std::convert::TryFrom; +use std::mem; + +use crate::{Error, Res}; + +pub(crate) type HFrameType = u64; + +pub(crate) const H3_FRAME_TYPE_DATA: HFrameType = 0x0; +pub(crate) const H3_FRAME_TYPE_HEADERS: HFrameType = 0x1; +const H3_FRAME_TYPE_CANCEL_PUSH: HFrameType = 0x3; +pub(crate) const H3_FRAME_TYPE_SETTINGS: HFrameType = 0x4; +const H3_FRAME_TYPE_PUSH_PROMISE: HFrameType = 0x5; +const H3_FRAME_TYPE_GOAWAY: HFrameType = 0x7; +const H3_FRAME_TYPE_MAX_PUSH_ID: HFrameType = 0xd; + +pub const H3_RESERVED_FRAME_TYPES: &[HFrameType] = &[0x2, 0x6, 0x8, 0x9]; + +const MAX_READ_SIZE: usize = 4096; +// data for DATA frame is not read into HFrame::Data. +#[derive(PartialEq, Debug)] +pub enum HFrame { + Data { + len: u64, // length of the data + }, + Headers { + header_block: Vec<u8>, + }, + CancelPush { + push_id: u64, + }, + Settings { + settings: HSettings, + }, + PushPromise { + push_id: u64, + header_block: Vec<u8>, + }, + Goaway { + stream_id: u64, + }, + MaxPushId { + push_id: u64, + }, + Grease, +} + +impl HFrame { + fn get_type(&self) -> HFrameType { + match self { + Self::Data { .. } => H3_FRAME_TYPE_DATA, + Self::Headers { .. } => H3_FRAME_TYPE_HEADERS, + Self::CancelPush { .. } => H3_FRAME_TYPE_CANCEL_PUSH, + Self::Settings { .. } => H3_FRAME_TYPE_SETTINGS, + Self::PushPromise { .. } => H3_FRAME_TYPE_PUSH_PROMISE, + Self::Goaway { .. } => H3_FRAME_TYPE_GOAWAY, + Self::MaxPushId { .. } => H3_FRAME_TYPE_MAX_PUSH_ID, + Self::Grease => { + let r = random(7); + Decoder::from(&r).decode_uint(7).unwrap() * 0x1f + 0x21 + } + } + } + + pub fn encode(&self, enc: &mut Encoder) { + enc.encode_varint(self.get_type()); + + match self { + Self::Data { len } => { + // DATA frame only encode the length here. + enc.encode_varint(*len); + } + Self::Headers { header_block } => { + enc.encode_vvec(header_block); + } + Self::CancelPush { push_id } => { + enc.encode_vvec_with(|enc_inner| { + enc_inner.encode_varint(*push_id); + }); + } + Self::Settings { settings } => { + settings.encode_frame_contents(enc); + } + Self::PushPromise { + push_id, + header_block, + } => { + enc.encode_varint((header_block.len() + (Encoder::varint_len(*push_id))) as u64); + enc.encode_varint(*push_id); + enc.encode(header_block); + } + Self::Goaway { stream_id } => { + enc.encode_vvec_with(|enc_inner| { + enc_inner.encode_varint(*stream_id); + }); + } + Self::MaxPushId { push_id } => { + enc.encode_vvec_with(|enc_inner| { + enc_inner.encode_varint(*push_id); + }); + } + Self::Grease => { + // Encode some number of random bytes. + let r = random(8); + enc.encode_vvec(&r[1..usize::from(1 + (r[0] & 0x7))]); + } + } + } +} + +#[derive(Clone, Debug)] +enum HFrameReaderState { + GetType { decoder: IncrementalDecoderUint }, + GetLength { decoder: IncrementalDecoderUint }, + GetData { decoder: IncrementalDecoderBuffer }, + UnknownFrameDischargeData { decoder: IncrementalDecoderIgnore }, +} + +#[derive(Debug)] +pub struct HFrameReader { + state: HFrameReaderState, + hframe_type: u64, + hframe_len: u64, + payload: Vec<u8>, +} + +impl Default for HFrameReader { + fn default() -> Self { + Self::new() + } +} + +impl HFrameReader { + #[must_use] + pub fn new() -> Self { + Self { + state: HFrameReaderState::GetType { + decoder: IncrementalDecoderUint::default(), + }, + hframe_type: 0, + hframe_len: 0, + payload: Vec::new(), + } + } + + fn reset(&mut self) { + self.state = HFrameReaderState::GetType { + decoder: IncrementalDecoderUint::default(), + }; + } + + fn min_remaining(&self) -> usize { + match &self.state { + HFrameReaderState::GetType { decoder } | HFrameReaderState::GetLength { decoder } => { + decoder.min_remaining() + } + HFrameReaderState::GetData { decoder } => decoder.min_remaining(), + HFrameReaderState::UnknownFrameDischargeData { decoder } => decoder.min_remaining(), + } + } + + fn decoding_in_progress(&self) -> bool { + if let HFrameReaderState::GetType { decoder } = &self.state { + decoder.decoding_in_progress() + } else { + true + } + } + + /// returns true if quic stream was closed. + /// # Errors + /// May return `HttpFrame` if a frame cannot be decoded. + /// and `TransportStreamDoesNotExist` if `stream_recv` fails. + pub fn receive( + &mut self, + conn: &mut Connection, + stream_id: u64, + ) -> Res<(Option<HFrame>, bool)> { + loop { + let to_read = std::cmp::min(self.min_remaining(), MAX_READ_SIZE); + let mut buf = vec![0; to_read]; + let (output, read, fin) = match conn + .stream_recv(stream_id, &mut buf) + .map_err(|e| Error::map_stream_recv_errors(&e))? + { + (0, f) => (None, false, f), + (amount, f) => { + qtrace!( + [conn], + "HFrameReader::receive: reading {} byte, fin={}", + amount, + f + ); + (self.consume(Decoder::from(&buf[..amount]))?, true, f) + } + }; + + if output.is_some() { + break Ok((output, fin)); + } + + if fin { + if self.decoding_in_progress() { + break Err(Error::HttpFrame); + } else { + break Ok((None, fin)); + } + } + + if !read { + // There was no new data, exit the loop. + break Ok((None, false)); + } + } + } + + /// # Errors + /// May return `HttpFrame` if a frame cannot be decoded. + fn consume(&mut self, mut input: Decoder) -> Res<Option<HFrame>> { + match &mut self.state { + HFrameReaderState::GetType { decoder } => { + if let Some(v) = decoder.consume(&mut input) { + qtrace!("HFrameReader::receive: read frame type {}", v); + self.hframe_type = v; + if H3_RESERVED_FRAME_TYPES.contains(&self.hframe_type) { + return Err(Error::HttpFrameUnexpected); + } + self.state = HFrameReaderState::GetLength { + decoder: IncrementalDecoderUint::default(), + }; + } + } + + HFrameReaderState::GetLength { decoder } => { + if let Some(len) = decoder.consume(&mut input) { + qtrace!( + "HFrameReader::receive: frame type {} length {}", + self.hframe_type, + len + ); + self.hframe_len = len; + self.state = match self.hframe_type { + // DATA payload are left on the quic stream and picked up separately + H3_FRAME_TYPE_DATA => { + return Ok(Some(self.get_frame()?)); + } + + // for other frames get all data before decoding. + H3_FRAME_TYPE_CANCEL_PUSH + | H3_FRAME_TYPE_SETTINGS + | H3_FRAME_TYPE_GOAWAY + | H3_FRAME_TYPE_MAX_PUSH_ID + | H3_FRAME_TYPE_PUSH_PROMISE + | H3_FRAME_TYPE_HEADERS => { + if len == 0 { + return Ok(Some(self.get_frame()?)); + } else { + HFrameReaderState::GetData { + decoder: IncrementalDecoderBuffer::new( + usize::try_from(len).or(Err(Error::HttpFrame))?, + ), + } + } + } + _ => { + if len == 0 { + HFrameReaderState::GetType { + decoder: IncrementalDecoderUint::default(), + } + } else { + HFrameReaderState::UnknownFrameDischargeData { + decoder: IncrementalDecoderIgnore::new( + usize::try_from(len).or(Err(Error::HttpFrame))?, + ), + } + } + } + }; + } + } + HFrameReaderState::GetData { decoder } => { + if let Some(data) = decoder.consume(&mut input) { + qtrace!( + "received frame {}: {}", + self.hframe_type, + hex_with_len(&data[..]) + ); + self.payload = data; + return Ok(Some(self.get_frame()?)); + } + } + HFrameReaderState::UnknownFrameDischargeData { decoder } => { + if decoder.consume(&mut input) { + self.reset(); + } + } + } + Ok(None) + } + + /// # Errors + /// May return `HttpFrame` if a frame cannot be decoded. + fn get_frame(&mut self) -> Res<HFrame> { + let payload = mem::replace(&mut self.payload, Vec::new()); + let mut dec = Decoder::from(&payload[..]); + let f = match self.hframe_type { + H3_FRAME_TYPE_DATA => HFrame::Data { + len: self.hframe_len, + }, + H3_FRAME_TYPE_HEADERS => HFrame::Headers { + header_block: dec.decode_remainder().to_vec(), + }, + H3_FRAME_TYPE_CANCEL_PUSH => HFrame::CancelPush { + push_id: dec.decode_varint().ok_or(Error::HttpFrame)?, + }, + H3_FRAME_TYPE_SETTINGS => { + let mut settings = HSettings::default(); + settings.decode_frame_contents(&mut dec).map_err(|e| { + if e == Error::HttpSettings { + e + } else { + Error::HttpFrame + } + })?; + HFrame::Settings { settings } + } + H3_FRAME_TYPE_PUSH_PROMISE => HFrame::PushPromise { + push_id: dec.decode_varint().ok_or(Error::HttpFrame)?, + header_block: dec.decode_remainder().to_vec(), + }, + H3_FRAME_TYPE_GOAWAY => HFrame::Goaway { + stream_id: dec.decode_varint().ok_or(Error::HttpFrame)?, + }, + H3_FRAME_TYPE_MAX_PUSH_ID => HFrame::MaxPushId { + push_id: dec.decode_varint().ok_or(Error::HttpFrame)?, + }, + _ => panic!("We should not be calling this function with unknown frame type!"), + }; + self.reset(); + Ok(f) + } +} + +#[cfg(test)] +mod tests { + use super::{Decoder, Encoder, Error, HFrame, HFrameReader, HSettings}; + use crate::settings::{HSetting, HSettingType}; + use neqo_crypto::AuthenticationStatus; + use neqo_transport::{Connection, StreamType}; + use test_fixture::{connect, default_client, default_server, fixture_init, now}; + + #[allow(clippy::many_single_char_names)] + fn enc_dec(f: &HFrame, st: &str, remaining: usize) { + let mut d = Encoder::default(); + + f.encode(&mut d); + + // For data, headers and push_promise we do not read all bytes from the buffer + let d2 = Encoder::from_hex(st); + assert_eq!(&d[..], &d2[..d.len()]); + + let mut conn_c = default_client(); + let mut conn_s = default_server(); + let out = conn_c.process(None, now()); + let out = conn_s.process(out.dgram(), now()); + let out = conn_c.process(out.dgram(), now()); + let _ = conn_s.process(out.dgram(), now()); + conn_c.authenticated(AuthenticationStatus::Ok, now()); + let out = conn_c.process(None, now()); + let _ = conn_s.process(out.dgram(), now()); + + // create a stream + let stream_id = conn_s.stream_create(StreamType::BiDi).unwrap(); + + let mut fr: HFrameReader = HFrameReader::new(); + + // conver string into u8 vector + let buf = Encoder::from_hex(st); + conn_s.stream_send(stream_id, &buf[..]).unwrap(); + let out = conn_s.process(None, now()); + let _ = conn_c.process(out.dgram(), now()); + + let (frame, fin) = fr.receive(&mut conn_c, stream_id).unwrap(); + assert_eq!(fin, false); + assert!(frame.is_some()); + assert_eq!(*f, frame.unwrap()); + + // Check remaining data. + let mut buf = [0_u8; 100]; + let (amount, _) = conn_c.stream_recv(stream_id, &mut buf).unwrap(); + assert_eq!(amount, remaining); + } + + #[test] + fn test_data_frame() { + let f = HFrame::Data { len: 3 }; + enc_dec(&f, "0003010203", 3); + } + + #[test] + fn test_headers_frame() { + let f = HFrame::Headers { + header_block: vec![0x01, 0x02, 0x03], + }; + enc_dec(&f, "0103010203", 0); + } + + #[test] + fn test_cancel_push_frame4() { + let f = HFrame::CancelPush { push_id: 5 }; + enc_dec(&f, "030105", 0); + } + + #[test] + fn test_settings_frame4() { + let f = HFrame::Settings { + settings: HSettings::new(&[HSetting::new(HSettingType::MaxHeaderListSize, 4)]), + }; + enc_dec(&f, "04020604", 0); + } + + #[test] + fn test_push_promise_frame4() { + let f = HFrame::PushPromise { + push_id: 4, + header_block: vec![0x61, 0x62, 0x63, 0x64], + }; + enc_dec(&f, "05050461626364", 0); + } + + #[test] + fn test_goaway_frame4() { + let f = HFrame::Goaway { stream_id: 5 }; + enc_dec(&f, "070105", 0); + } + + #[test] + fn test_max_push_id_frame4() { + let f = HFrame::MaxPushId { push_id: 5 }; + enc_dec(&f, "0d0105", 0); + } + + #[test] + fn grease() { + fn make_grease() -> u64 { + let mut enc = Encoder::default(); + HFrame::Grease.encode(&mut enc); + let mut dec = Decoder::from(&enc); + let ft = dec.decode_varint().unwrap(); + assert_eq!((ft - 0x21) % 0x1f, 0); + let body = dec.decode_vvec().unwrap(); + assert!(body.len() <= 7); + ft + } + + fixture_init(); + let t1 = make_grease(); + let t2 = make_grease(); + assert_ne!(t1, t2); + } + + struct HFrameReaderTest { + pub fr: HFrameReader, + pub conn_c: Connection, + pub conn_s: Connection, + pub stream_id: u64, + } + + impl HFrameReaderTest { + pub fn new() -> Self { + let (conn_c, mut conn_s) = connect(); + let stream_id = conn_s.stream_create(StreamType::BiDi).unwrap(); + Self { + fr: HFrameReader::new(), + conn_c, + conn_s, + stream_id, + } + } + + fn process(&mut self, v: &[u8]) -> Option<HFrame> { + self.conn_s.stream_send(self.stream_id, v).unwrap(); + let out = self.conn_s.process(None, now()); + let _ = self.conn_c.process(out.dgram(), now()); + let (frame, fin) = self.fr.receive(&mut self.conn_c, self.stream_id).unwrap(); + assert_eq!(fin, false); + frame + } + } + + // Test receiving byte by byte for a SETTINGS frame. + #[test] + fn test_frame_reading_with_stream_settings1() { + let mut fr = HFrameReaderTest::new(); + + // Send and read settings frame 040406040804 + assert!(fr.process(&[0x4]).is_none()); + assert!(fr.process(&[0x4]).is_none()); + assert!(fr.process(&[0x6]).is_none()); + assert!(fr.process(&[0x4]).is_none()); + assert!(fr.process(&[0x8]).is_none()); + let frame = fr.process(&[0x4]); + + assert!(frame.is_some()); + if let HFrame::Settings { settings } = frame.unwrap() { + assert!(settings.len() == 1); + assert!(settings[0] == HSetting::new(HSettingType::MaxHeaderListSize, 4)); + } else { + panic!("wrong frame type"); + } + } + + // Test receiving byte by byte for a SETTINGS frame with larger varints + #[test] + fn test_frame_reading_with_stream_settings2() { + let mut fr = HFrameReaderTest::new(); + + // Read settings frame 400406064004084100 + for i in &[0x40, 0x04, 0x06, 0x06, 0x40, 0x04, 0x08, 0x41] { + assert!(fr.process(&[*i]).is_none()); + } + let frame = fr.process(&[0x0]); + + assert!(frame.is_some()); + if let HFrame::Settings { settings } = frame.unwrap() { + assert!(settings.len() == 1); + assert!(settings[0] == HSetting::new(HSettingType::MaxHeaderListSize, 4)); + } else { + panic!("wrong frame type"); + } + } + + // Test receiving byte by byte for a PUSH_PROMISE frame. + #[test] + fn test_frame_reading_with_stream_push_promise() { + let mut fr = HFrameReaderTest::new(); + + // Read push-promise frame 05054101010203 + for i in &[0x05, 0x05, 0x41, 0x01, 0x01, 0x02] { + assert!(fr.process(&[*i]).is_none()); + } + let frame = fr.process(&[0x3]); + + assert!(frame.is_some()); + if let HFrame::PushPromise { + push_id, + header_block, + } = frame.unwrap() + { + assert_eq!(push_id, 257); + assert_eq!(header_block, &[0x1, 0x2, 0x3]); + } else { + panic!("wrong frame type"); + } + } + + // Test DATA + #[test] + fn test_frame_reading_with_stream_data() { + let mut fr = HFrameReaderTest::new(); + + // Read data frame 0003010203 + let frame = fr.process(&[0x0, 0x3, 0x1, 0x2, 0x3]).unwrap(); + assert!(matches!(frame, HFrame::Data { len } if len == 3)); + + // payloead is still on the stream. + // assert that we have 3 bytes in the stream + let mut buf = [0_u8; 100]; + let (amount, _) = fr.conn_c.stream_recv(fr.stream_id, &mut buf).unwrap(); + assert_eq!(amount, 3); + } + + // Test an unknown frame + #[test] + fn test_unknown_frame() { + // Construct an unknown frame. + const UNKNOWN_FRAME_LEN: usize = 832; + + let mut fr = HFrameReaderTest::new(); + + let mut enc = Encoder::with_capacity(UNKNOWN_FRAME_LEN + 4); + enc.encode_varint(1028_u64); // Arbitrary type. + enc.encode_varint(UNKNOWN_FRAME_LEN as u64); + let mut buf: Vec<_> = enc.into(); + buf.resize(UNKNOWN_FRAME_LEN + buf.len(), 0); + assert!(fr.process(&buf).is_none()); + + // now receive a CANCEL_PUSH fram to see that frame reader is ok. + let frame = fr.process(&[0x03, 0x01, 0x05]); + assert!(frame.is_some()); + if let HFrame::CancelPush { push_id } = frame.unwrap() { + assert!(push_id == 5); + } else { + panic!("wrong frame type"); + } + } + + enum FrameReadingTestSend { + OnlyData, + DataWithFin, + DataThenFin, + } + + enum FrameReadingTestExpect { + Error, + Incomplete, + FrameComplete, + FrameAndStreamComplete, + StreamDoneWithoutFrame, + } + + fn test_reading_frame( + buf: &[u8], + test_to_send: &FrameReadingTestSend, + expected_result: &FrameReadingTestExpect, + ) { + let mut fr = HFrameReaderTest::new(); + + fr.conn_s.stream_send(fr.stream_id, &buf).unwrap(); + if let FrameReadingTestSend::DataWithFin = test_to_send { + fr.conn_s.stream_close_send(fr.stream_id).unwrap(); + } + + let out = fr.conn_s.process(None, now()); + let _ = fr.conn_c.process(out.dgram(), now()); + + if let FrameReadingTestSend::DataThenFin = test_to_send { + fr.conn_s.stream_close_send(fr.stream_id).unwrap(); + let out = fr.conn_s.process(None, now()); + let _ = fr.conn_c.process(out.dgram(), now()); + } + + let rv = fr.fr.receive(&mut fr.conn_c, fr.stream_id); + + match expected_result { + FrameReadingTestExpect::Error => assert_eq!(Err(Error::HttpFrame), rv), + FrameReadingTestExpect::Incomplete => { + assert_eq!(Ok((None, false)), rv); + } + FrameReadingTestExpect::FrameComplete => { + let (f, fin) = rv.unwrap(); + assert_eq!(fin, false); + assert!(f.is_some()); + } + FrameReadingTestExpect::FrameAndStreamComplete => { + let (f, fin) = rv.unwrap(); + assert_eq!(fin, true); + assert!(f.is_some()); + } + FrameReadingTestExpect::StreamDoneWithoutFrame => { + let (f, fin) = rv.unwrap(); + assert_eq!(fin, true); + assert!(f.is_none()); + } + }; + } + + #[test] + fn test_complete_and_incomplete_unknown_frame() { + // Construct an unknown frame. + const UNKNOWN_FRAME_LEN: usize = 832; + let mut enc = Encoder::with_capacity(UNKNOWN_FRAME_LEN + 4); + enc.encode_varint(1028_u64); // Arbitrary type. + enc.encode_varint(UNKNOWN_FRAME_LEN as u64); + let mut buf: Vec<_> = enc.into(); + buf.resize(UNKNOWN_FRAME_LEN + buf.len(), 0); + + let len = std::cmp::min(buf.len() - 1, 10); + for i in 1..len { + test_reading_frame( + &buf[..i], + &FrameReadingTestSend::OnlyData, + &FrameReadingTestExpect::Incomplete, + ); + test_reading_frame( + &buf[..i], + &FrameReadingTestSend::DataWithFin, + &FrameReadingTestExpect::Error, + ); + test_reading_frame( + &buf[..i], + &FrameReadingTestSend::DataThenFin, + &FrameReadingTestExpect::Error, + ); + } + test_reading_frame( + &buf, + &FrameReadingTestSend::OnlyData, + &FrameReadingTestExpect::Incomplete, + ); + test_reading_frame( + &buf, + &FrameReadingTestSend::DataWithFin, + &FrameReadingTestExpect::StreamDoneWithoutFrame, + ); + test_reading_frame( + &buf, + &FrameReadingTestSend::DataThenFin, + &FrameReadingTestExpect::StreamDoneWithoutFrame, + ); + } + + // if we read more than done_state bytes HFrameReader will be in done state. + fn test_complete_and_incomplete_frame(buf: &[u8], done_state: usize) { + use std::cmp::Ordering; + // Let's consume partial frames. It is enough to test partal frames + // up to 10 byte. 10 byte is greater than frame type and frame + // length and bit of data. + let len = std::cmp::min(buf.len() - 1, 10); + for i in 1..len { + test_reading_frame( + &buf[..i], + &FrameReadingTestSend::OnlyData, + if i >= done_state { + &FrameReadingTestExpect::FrameComplete + } else { + &FrameReadingTestExpect::Incomplete + }, + ); + test_reading_frame( + &buf[..i], + &FrameReadingTestSend::DataWithFin, + match i.cmp(&done_state) { + Ordering::Greater => &FrameReadingTestExpect::FrameComplete, + Ordering::Equal => &FrameReadingTestExpect::FrameAndStreamComplete, + Ordering::Less => &FrameReadingTestExpect::Error, + }, + ); + test_reading_frame( + &buf[..i], + &FrameReadingTestSend::DataThenFin, + match i.cmp(&done_state) { + Ordering::Greater => &FrameReadingTestExpect::FrameComplete, + Ordering::Equal => &FrameReadingTestExpect::FrameAndStreamComplete, + Ordering::Less => &FrameReadingTestExpect::Error, + }, + ); + } + test_reading_frame( + buf, + &FrameReadingTestSend::OnlyData, + &FrameReadingTestExpect::FrameComplete, + ); + test_reading_frame( + buf, + &FrameReadingTestSend::DataWithFin, + if buf.len() == done_state { + &FrameReadingTestExpect::FrameAndStreamComplete + } else { + &FrameReadingTestExpect::FrameComplete + }, + ); + test_reading_frame( + buf, + &FrameReadingTestSend::DataThenFin, + if buf.len() == done_state { + &FrameReadingTestExpect::FrameAndStreamComplete + } else { + &FrameReadingTestExpect::FrameComplete + }, + ); + } + + #[test] + fn test_complete_and_incomplete_frames() { + const FRAME_LEN: usize = 10; + const HEADER_BLOCK: &[u8] = &[0x01, 0x02, 0x03, 0x04]; + + // H3_FRAME_TYPE_DATA len=0 + let f = HFrame::Data { len: 0 }; + let mut enc = Encoder::with_capacity(2); + f.encode(&mut enc); + let buf: Vec<_> = enc.into(); + test_complete_and_incomplete_frame(&buf, 2); + + // H3_FRAME_TYPE_DATA len=FRAME_LEN + let f = HFrame::Data { + len: FRAME_LEN as u64, + }; + let mut enc = Encoder::with_capacity(2); + f.encode(&mut enc); + let mut buf: Vec<_> = enc.into(); + buf.resize(FRAME_LEN + buf.len(), 0); + test_complete_and_incomplete_frame(&buf, 2); + + // H3_FRAME_TYPE_HEADERS empty header block + let f = HFrame::Headers { + header_block: Vec::new(), + }; + let mut enc = Encoder::default(); + f.encode(&mut enc); + let buf: Vec<_> = enc.into(); + test_complete_and_incomplete_frame(&buf, 2); + + // H3_FRAME_TYPE_HEADERS + let f = HFrame::Headers { + header_block: HEADER_BLOCK.to_vec(), + }; + let mut enc = Encoder::default(); + f.encode(&mut enc); + let buf: Vec<_> = enc.into(); + test_complete_and_incomplete_frame(&buf, buf.len()); + + // H3_FRAME_TYPE_CANCEL_PUSH + let f = HFrame::CancelPush { push_id: 5 }; + let mut enc = Encoder::default(); + f.encode(&mut enc); + let buf: Vec<_> = enc.into(); + test_complete_and_incomplete_frame(&buf, buf.len()); + + // H3_FRAME_TYPE_SETTINGS + let f = HFrame::Settings { + settings: HSettings::new(&[HSetting::new(HSettingType::MaxHeaderListSize, 4)]), + }; + let mut enc = Encoder::default(); + f.encode(&mut enc); + let buf: Vec<_> = enc.into(); + test_complete_and_incomplete_frame(&buf, buf.len()); + + // H3_FRAME_TYPE_PUSH_PROMISE + let f = HFrame::PushPromise { + push_id: 4, + header_block: HEADER_BLOCK.to_vec(), + }; + let mut enc = Encoder::default(); + f.encode(&mut enc); + let buf: Vec<_> = enc.into(); + test_complete_and_incomplete_frame(&buf, buf.len()); + + // H3_FRAME_TYPE_GOAWAY + let f = HFrame::Goaway { stream_id: 5 }; + let mut enc = Encoder::default(); + f.encode(&mut enc); + let buf: Vec<_> = enc.into(); + test_complete_and_incomplete_frame(&buf, buf.len()); + + // H3_FRAME_TYPE_MAX_PUSH_ID + let f = HFrame::MaxPushId { push_id: 5 }; + let mut enc = Encoder::default(); + f.encode(&mut enc); + let buf: Vec<_> = enc.into(); + test_complete_and_incomplete_frame(&buf, buf.len()); + } + + // Test closing a stream before any frame is sent should not cause an error. + #[test] + fn test_frame_reading_when_stream_is_closed_before_sending_data() { + let mut fr = HFrameReaderTest::new(); + + fr.conn_s.stream_send(fr.stream_id, &[0x00]).unwrap(); + let out = fr.conn_s.process(None, now()); + let _ = fr.conn_c.process(out.dgram(), now()); + + assert_eq!(Ok(()), fr.conn_c.stream_close_send(fr.stream_id)); + let out = fr.conn_c.process(None, now()); + let _ = fr.conn_s.process(out.dgram(), now()); + assert_eq!( + Ok((None, true)), + fr.fr.receive(&mut fr.conn_s, fr.stream_id) + ); + } +} diff --git a/third_party/rust/neqo-http3/src/lib.rs b/third_party/rust/neqo-http3/src/lib.rs new file mode 100644 index 0000000000..4d9d3c6dff --- /dev/null +++ b/third_party/rust/neqo-http3/src/lib.rs @@ -0,0 +1,290 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg_attr(feature = "deny-warnings", deny(warnings))] +#![warn(clippy::pedantic)] +#![allow(clippy::pub_enum_variant_names)] + +mod client_events; +mod connection; +pub mod connection_client; +mod connection_server; +mod control_stream_local; +mod control_stream_remote; +pub mod hframe; +mod push_controller; +mod push_stream; +mod qlog; +mod recv_message; +mod send_message; +pub mod server; +mod server_connection_events; +mod server_events; +mod settings; +mod stream_type_reader; + +use neqo_qpack::decoder::QPackDecoder; +use neqo_qpack::Error as QpackError; +pub use neqo_transport::Output; +use neqo_transport::{AppError, Connection, Error as TransportError}; +use std::fmt::Debug; + +pub use client_events::Http3ClientEvent; +pub use connection::Http3State; +pub use connection_client::Http3Client; +pub use connection_client::Http3Parameters; +pub use hframe::HFrameReader; +pub use neqo_qpack::Header; +pub use server::Http3Server; +pub use server_events::Http3ServerEvent; + +type Res<T> = Result<T, Error>; + +#[derive(Clone, Debug, PartialEq)] +pub enum Error { + HttpNoError, + HttpGeneralProtocol, + HttpGeneralProtocolStream, //this is the same as the above but it should only close a stream not a connection. + HttpInternal, + HttpStreamCreation, + HttpClosedCriticalStream, + HttpFrameUnexpected, + HttpFrame, + HttpExcessiveLoad, + HttpId, + HttpSettings, + HttpMissingSettings, + HttpRequestRejected, + HttpRequestCancelled, + HttpRequestIncomplete, + HttpConnect, + HttpVersionFallback, + QpackError(neqo_qpack::Error), + + // Internal errors from here. + AlreadyClosed, + AlreadyInitialized, + DecodingFrame, + HttpGoaway, + Internal, + InvalidResumptionToken, + InvalidStreamId, + InvalidState, + NoMoreData, + NotEnoughData, + TransportError(TransportError), + Unavailable, + Unexpected, + StreamLimitError, + TransportStreamDoesNotExist, + InvalidInput, + FatalError, + InvalidHeader, +} + +impl Error { + #[must_use] + pub fn code(&self) -> AppError { + match self { + Self::HttpNoError => 0x100, + Self::HttpGeneralProtocol | Self::HttpGeneralProtocolStream | Self::InvalidHeader => { + 0x101 + } + Self::HttpInternal => 0x102, + Self::HttpStreamCreation => 0x103, + Self::HttpClosedCriticalStream => 0x104, + Self::HttpFrameUnexpected => 0x105, + Self::HttpFrame => 0x106, + Self::HttpExcessiveLoad => 0x107, + Self::HttpId => 0x108, + Self::HttpSettings => 0x109, + Self::HttpMissingSettings => 0x10a, + Self::HttpRequestRejected => 0x10b, + Self::HttpRequestCancelled => 0x10c, + Self::HttpRequestIncomplete => 0x10d, + Self::HttpConnect => 0x10f, + Self::HttpVersionFallback => 0x110, + Self::QpackError(e) => e.code(), + // These are all internal errors. + _ => 3, + } + } + + #[must_use] + pub fn connection_error(&self) -> bool { + matches!( + self, + Self::HttpGeneralProtocol + | Self::HttpInternal + | Self::HttpStreamCreation + | Self::HttpClosedCriticalStream + | Self::HttpFrameUnexpected + | Self::HttpFrame + | Self::HttpExcessiveLoad + | Self::HttpId + | Self::HttpSettings + | Self::HttpMissingSettings + | Self::QpackError(QpackError::EncoderStream) + | Self::QpackError(QpackError::DecoderStream) + ) + } + + #[must_use] + pub fn stream_reset_error(&self) -> bool { + matches!(self, Self::HttpGeneralProtocolStream | Self::InvalidHeader) + } + + #[must_use] + pub fn map_stream_send_errors(err: &TransportError) -> Self { + match err { + TransportError::InvalidStreamId | TransportError::FinalSizeError => { + Error::TransportStreamDoesNotExist + } + TransportError::InvalidInput => Error::InvalidInput, + _ => { + debug_assert!(false, "Unexpected error"); + Error::TransportStreamDoesNotExist + } + } + } + + #[must_use] + pub fn map_stream_create_errors(err: &TransportError) -> Self { + match err { + TransportError::ConnectionState => Error::Unavailable, + TransportError::StreamLimitError => Error::StreamLimitError, + _ => { + debug_assert!(false, "Unexpected error"); + Error::TransportStreamDoesNotExist + } + } + } + + #[must_use] + pub fn map_stream_recv_errors(err: &TransportError) -> Self { + match err { + TransportError::NoMoreData => { + debug_assert!( + false, + "Do not call stream_recv if FIN has been previously read" + ); + } + TransportError::InvalidStreamId => {} + _ => { + debug_assert!(false, "Unexpected error"); + } + }; + Error::TransportStreamDoesNotExist + } + + #[must_use] + pub fn map_set_resumption_errors(err: &TransportError) -> Self { + match err { + TransportError::ConnectionState => Error::InvalidState, + _ => Error::InvalidResumptionToken, + } + } + + /// # Errors + /// Any error is mapped to the indicated type. + fn map_error<R>(r: Result<R, impl Into<Self>>, err: Self) -> Result<R, Self> { + Ok(r.map_err(|e| { + debug_assert!(!matches!(e.into(), Self::HttpInternal)); + debug_assert!(!matches!(err, Self::HttpInternal)); + err + })?) + } +} + +impl From<TransportError> for Error { + fn from(err: TransportError) -> Self { + Self::TransportError(err) + } +} + +impl From<QpackError> for Error { + fn from(err: QpackError) -> Self { + match err { + QpackError::ClosedCriticalStream => Error::HttpClosedCriticalStream, + QpackError::InternalError => Error::HttpInternal, + e => Self::QpackError(e), + } + } +} + +impl From<AppError> for Error { + fn from(error: AppError) -> Self { + match error { + 0x100 => Self::HttpNoError, + 0x101 => Self::HttpGeneralProtocol, + 0x103 => Self::HttpStreamCreation, + 0x104 => Self::HttpClosedCriticalStream, + 0x105 => Self::HttpFrameUnexpected, + 0x106 => Self::HttpFrame, + 0x107 => Self::HttpExcessiveLoad, + 0x108 => Self::HttpId, + 0x109 => Self::HttpSettings, + 0x10a => Self::HttpMissingSettings, + 0x10b => Self::HttpRequestRejected, + 0x10c => Self::HttpRequestCancelled, + 0x10d => Self::HttpRequestIncomplete, + 0x10f => Self::HttpConnect, + 0x110 => Self::HttpVersionFallback, + 0x200 => Self::QpackError(QpackError::DecompressionFailed), + 0x201 => Self::QpackError(QpackError::EncoderStream), + 0x202 => Self::QpackError(QpackError::DecoderStream), + _ => Self::HttpInternal, + } + } +} + +impl ::std::error::Error for Error { + fn source(&self) -> Option<&(dyn ::std::error::Error + 'static)> { + match self { + Self::TransportError(e) => Some(e), + Self::QpackError(e) => Some(e), + _ => None, + } + } +} + +impl ::std::fmt::Display for Error { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "HTTP/3 error: {:?}", self) + } +} + +pub trait RecvStream: Debug { + fn stream_reset(&self, error: AppError, decoder: &mut QPackDecoder, reset_type: ResetType); + /// # Errors + /// An error may happen while reading a stream, e.g. early close, protocol error, etc. + fn receive(&mut self, conn: &mut Connection, decoder: &mut QPackDecoder) -> Res<()>; + /// # Errors + /// An error may happen while reading a stream, e.g. early close, protocol error, etc. + fn header_unblocked(&mut self, conn: &mut Connection, decoder: &mut QPackDecoder) -> Res<()>; + fn done(&self) -> bool; + /// # Errors + /// An error may happen while reading a stream, e.g. early close, protocol error, etc. + fn read_data( + &mut self, + conn: &mut Connection, + decoder: &mut QPackDecoder, + buf: &mut [u8], + ) -> Res<(usize, bool)>; +} + +pub(crate) trait RecvMessageEvents: Debug { + fn header_ready(&self, stream_id: u64, headers: Vec<Header>, interim: bool, fin: bool); + fn data_readable(&self, stream_id: u64); + fn reset(&self, stream_id: u64, error: AppError, local: bool); +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ResetType { + App, + Remote, + Local, +} diff --git a/third_party/rust/neqo-http3/src/push_controller.rs b/third_party/rust/neqo-http3/src/push_controller.rs new file mode 100644 index 0000000000..2c39073f97 --- /dev/null +++ b/third_party/rust/neqo-http3/src/push_controller.rs @@ -0,0 +1,485 @@ +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::client_events::{Http3ClientEvent, Http3ClientEvents}; +use crate::connection::Http3Connection; +use crate::hframe::HFrame; +use crate::{Error, Header, Res}; +use crate::{RecvMessageEvents, ResetType}; +use neqo_common::{qerror, qinfo, qtrace}; +use neqo_transport::{AppError, Connection}; +use std::cell::RefCell; +use std::collections::VecDeque; +use std::convert::TryFrom; +use std::fmt::Debug; +use std::fmt::Display; +use std::mem; +use std::rc::Rc; +use std::slice::SliceIndex; + +/// `PushStates`: +/// `Init`: there is no push stream nor a push promise. This state is only used to keep track of opened and closed +/// push streams. +/// `PushPromise`: the push has only ever receive a pushpromise frame +/// `OnlyPushStream`: there is only a push stream. All push stream events, i.e. `PushHeaderReady` and +/// `PushDataReadable` will be delayed until a push promise is received (they are kept in +/// `events`). +/// `Active`: there is a push steam and at least one push promise frame. +/// `Close`: the push stream has been closed or reset already. +#[derive(Debug, PartialEq, Clone)] +enum PushState { + Init, + PushPromise { + headers: Vec<Header>, + }, + OnlyPushStream { + stream_id: u64, + events: Vec<Http3ClientEvent>, + }, + Active { + stream_id: u64, + headers: Vec<Header>, + }, + Closed, +} + +/// `ActivePushStreams` holds information about push streams. +/// +/// `first_push_id` holds a `push_id` of the first element in `push_streams` if it is present. +/// `push_id` smaller than `first_push_id` have been already closed. `push_id` => `first_push_id` +/// are in `push_streams` or they are not yet opened. +#[derive(Debug)] +struct ActivePushStreams { + push_streams: VecDeque<PushState>, + first_push_id: u64, +} + +impl ActivePushStreams { + pub fn new() -> Self { + Self { + push_streams: VecDeque::new(), + first_push_id: 0, + } + } + + /// Returns None if a stream has been closed already. + pub fn get_mut( + &mut self, + push_id: u64, + ) -> Option<&mut <usize as SliceIndex<[PushState]>>::Output> { + if push_id < self.first_push_id { + return None; + } + + let inx = usize::try_from(push_id - self.first_push_id).unwrap(); + if inx >= self.push_streams.len() { + self.push_streams.resize(inx + 1, PushState::Init); + } + match self.push_streams.get_mut(inx) { + Some(PushState::Closed) => None, + e => e, + } + } + + /// Returns None if a stream has been closed already. + pub fn get(&mut self, push_id: u64) -> Option<&mut PushState> { + self.get_mut(push_id) + } + + /// Returns the State of a closed push stream or None for already closed streams. + pub fn close(&mut self, push_id: u64) -> Option<PushState> { + match self.get_mut(push_id) { + None | Some(PushState::Closed) => None, + Some(s) => { + let res = mem::replace(s, PushState::Closed); + while self.push_streams.get(0).is_some() + && *self.push_streams.get(0).unwrap() == PushState::Closed + { + self.push_streams.pop_front(); + self.first_push_id += 1; + } + Some(res) + } + } + } + + #[must_use] + pub fn number_done(&self) -> u64 { + self.first_push_id + + u64::try_from( + self.push_streams + .iter() + .filter(|&e| e == &PushState::Closed) + .count(), + ) + .unwrap() + } + + pub fn clear(&mut self) { + self.first_push_id = 0; + self.push_streams.clear(); + } +} + +/// `PushController` keeps information about push stream states. +/// +/// A `PushStream` calls `add_new_push_stream` that may change the push state from Init to `OnlyPushStream` or from +/// `PushPromise` to `Active`. If a stream has already been closed `add_new_push_stream` returns false (the `PushStream` +/// will close the transport stream). +/// A `PushStream` calls `push_stream_reset` if the transport stream has been canceled. +/// When a push stream is done it calls `close`. +/// +/// The `PushController` handles: +/// `PUSH_PROMISE` frame: frames may change the push state from Init to `PushPromise` and from `OnlyPushStream` to +/// `Active`. Frames for a closed steams are ignored. +/// `CANCEL_PUSH` frame: (`handle_cancel_push` will be called). If a push is in state `PushPromise` or `Active`, any +/// posted events will be removed and a `PushCanceled` event will be posted. If a push is in +/// state `OnlyPushStream` or `Active` the transport stream and the `PushStream` will be closed. +/// The frame will be ignored for already closed pushes. +/// Application calling cancel: the actions are similar to the `CANCEL_PUSH` frame. The difference is that +/// `PushCanceled` will not be posted and a `CANCEL_PUSH` frame may be sent. +#[derive(Debug)] +pub(crate) struct PushController { + max_concurent_push: u64, + current_max_push_id: u64, + // push_streams holds the states of push streams. + // We keep a stream until the stream has been closed. + push_streams: ActivePushStreams, + // The keeps the next consecutive push_id that should be open. + // All push_id < next_push_id_to_open are in the push_stream lists. If they are not in the list they have + // been already closed. + conn_events: Http3ClientEvents, +} + +impl PushController { + pub fn new(max_concurent_push: u64, conn_events: Http3ClientEvents) -> Self { + PushController { + max_concurent_push, + current_max_push_id: 0, + push_streams: ActivePushStreams::new(), + conn_events, + } + } +} + +impl Display for PushController { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Push controler") + } +} + +impl PushController { + /// A new `push_promise` has been received. + /// # Errors + /// `HttpId` if `push_id` greater than it is allowed has been received. + pub fn new_push_promise( + &mut self, + push_id: u64, + ref_stream_id: u64, + new_headers: Vec<Header>, + ) -> Res<()> { + qtrace!( + [self], + "New push promise push_id={} headers={:?} max_push={}", + push_id, + new_headers, + self.max_concurent_push + ); + + self.check_push_id(push_id)?; + + match self.push_streams.get_mut(push_id) { + None => { + qtrace!("Push has been closed already {}.", push_id); + Ok(()) + } + Some(push_state) => match push_state { + PushState::Init => { + self.conn_events + .push_promise(push_id, ref_stream_id, new_headers.clone()); + *push_state = PushState::PushPromise { + headers: new_headers, + }; + Ok(()) + } + PushState::PushPromise { headers } | PushState::Active { headers, .. } => { + if new_headers != *headers { + return Err(Error::HttpGeneralProtocol); + } + self.conn_events + .push_promise(push_id, ref_stream_id, new_headers); + Ok(()) + } + PushState::OnlyPushStream { stream_id, events } => { + let stream_id_tmp = *stream_id; + self.conn_events + .push_promise(push_id, ref_stream_id, new_headers.clone()); + + for e in events.drain(..) { + self.conn_events.insert(e); + } + *push_state = PushState::Active { + stream_id: stream_id_tmp, + headers: new_headers, + }; + Ok(()) + } + PushState::Closed => unreachable!("This is only internal; it is transfer to None"), + }, + } + } + + pub fn add_new_push_stream(&mut self, push_id: u64, stream_id: u64) -> Res<bool> { + qtrace!( + "A new push stream with push_id={} stream_id={}", + push_id, + stream_id + ); + + self.check_push_id(push_id)?; + + match self.push_streams.get_mut(push_id) { + None => { + qinfo!("Push has been closed already."); + Ok(false) + } + Some(push_state) => match push_state { + PushState::Init => { + *push_state = PushState::OnlyPushStream { + stream_id, + events: Vec::new(), + }; + Ok(true) + } + PushState::PushPromise { headers } => { + let tmp = mem::replace(headers, Vec::new()); + *push_state = PushState::Active { + stream_id, + headers: tmp, + }; + Ok(true) + } + // The following state have already have a push stream: + // PushState::OnlyPushStream | PushState::Active + _ => { + qerror!("Duplicate push stream."); + Err(Error::HttpId) + } + }, + } + } + + fn check_push_id(&mut self, push_id: u64) -> Res<()> { + // Check if push id is greater than what we allow. + if push_id > self.current_max_push_id { + qerror!("Push id is greater than current_max_push_id."); + Err(Error::HttpId) + } else { + Ok(()) + } + } + + pub fn handle_cancel_push( + &mut self, + push_id: u64, + conn: &mut Connection, + base_handler: &mut Http3Connection, + ) -> Res<()> { + qtrace!("CANCEL_PUSH frame has been received, push_id={}", push_id); + + self.check_push_id(push_id)?; + + match self.push_streams.close(push_id) { + None => { + qtrace!("Push has already been closed (push_id={}).", push_id); + Ok(()) + } + Some(ps) => match ps { + PushState::Init => Ok(()), + PushState::PushPromise { .. } => { + self.conn_events.remove_events_for_push_id(push_id); + self.conn_events.push_canceled(push_id); + Ok(()) + } + PushState::OnlyPushStream { stream_id, .. } + | PushState::Active { stream_id, .. } => { + let _ = base_handler.stream_reset( + conn, + stream_id, + Error::HttpRequestCancelled.code(), + ); + self.conn_events.remove_events_for_push_id(push_id); + self.conn_events.push_canceled(push_id); + Ok(()) + } + PushState::Closed => unreachable!("This is only internal; it is transfer to None"), + }, + } + } + + pub fn close(&mut self, push_id: u64) { + qtrace!("Push stream has been closed."); + if let Some(push_state) = self.push_streams.close(push_id) { + debug_assert!(matches!(push_state, PushState::Active{..})); + } else { + debug_assert!(false, "Closing non existing push stream!"); + } + } + + pub fn cancel( + &mut self, + push_id: u64, + conn: &mut Connection, + base_handler: &mut Http3Connection, + ) -> Res<()> { + qtrace!("Cancel push_id={}", push_id); + + self.check_push_id(push_id)?; + + match self.push_streams.get(push_id) { + None => { + qtrace!("Push has already been closed."); + // If we have some events for the push_id in the event queue, the caller still does not + // not know that the push has been closed. Otherwise return InvalidStreamId. + if self.conn_events.has_push(push_id) { + self.conn_events.remove_events_for_push_id(push_id); + Ok(()) + } else { + Err(Error::InvalidStreamId) + } + } + Some(PushState::PushPromise { .. }) => { + self.conn_events.remove_events_for_push_id(push_id); + base_handler.queue_control_frame(&HFrame::CancelPush { push_id }); + self.push_streams.close(push_id); + Ok(()) + } + Some(PushState::Active { stream_id, .. }) => { + self.conn_events.remove_events_for_push_id(push_id); + // Cancel the stream. the transport steam may already be done, so ignore an error. + let _ = + base_handler.stream_reset(conn, *stream_id, Error::HttpRequestCancelled.code()); + self.push_streams.close(push_id); + Ok(()) + } + Some(_) => Err(Error::InvalidStreamId), + } + } + + pub fn push_stream_reset(&mut self, push_id: u64, app_error: AppError, reset_type: ResetType) { + qtrace!("Push stream has been reset, push_id={}", push_id); + + if let Some(push_state) = self.push_streams.get(push_id) { + match push_state { + PushState::OnlyPushStream { .. } => { + self.push_streams.close(push_id); + } + PushState::Active { .. } => { + self.push_streams.close(push_id); + self.conn_events.remove_events_for_push_id(push_id); + if reset_type == ResetType::Local { + self.conn_events.push_reset(push_id, app_error); + } else { + self.conn_events.push_canceled(push_id); + } + } + _ => { + debug_assert!( + false, + "Reset cannot actually happen because we do not have a stream." + ); + } + } + } + } + + pub fn get_active_stream_id(&mut self, push_id: u64) -> Option<u64> { + match self.push_streams.get(push_id) { + Some(PushState::Active { stream_id, .. }) => Some(*stream_id), + _ => None, + } + } + + pub fn maybe_send_max_push_id_frame(&mut self, base_handler: &mut Http3Connection) { + let push_done = self.push_streams.number_done(); + if self.max_concurent_push > 0 + && (self.current_max_push_id - push_done) <= (self.max_concurent_push / 2) + { + self.current_max_push_id = push_done + self.max_concurent_push; + base_handler.queue_control_frame(&HFrame::MaxPushId { + push_id: self.current_max_push_id, + }); + } + } + + pub fn handle_zero_rtt_rejected(&mut self) { + self.current_max_push_id = 0; + } + + pub fn clear(&mut self) { + self.push_streams.clear(); + } + + pub fn can_receive_push(&self) -> bool { + self.max_concurent_push > 0 + } + + pub fn new_stream_event(&mut self, push_id: u64, event: Http3ClientEvent) { + match self.push_streams.get_mut(push_id) { + None => { + debug_assert!(false, "Push has been closed already."); + } + Some(PushState::OnlyPushStream { events, .. }) => { + events.push(event); + } + Some(PushState::Active { .. }) => { + self.conn_events.insert(event); + } + Some(_) => { + debug_assert!(false, "No record of a stream!"); + } + } + } +} + +#[derive(Debug)] +pub(crate) struct RecvPushEvents { + push_id: u64, + push_handler: Rc<RefCell<PushController>>, +} + +impl RecvPushEvents { + pub fn new(push_id: u64, push_handler: Rc<RefCell<PushController>>) -> Self { + Self { + push_id, + push_handler, + } + } +} + +impl RecvMessageEvents for RecvPushEvents { + fn header_ready(&self, _stream_id: u64, headers: Vec<Header>, interim: bool, fin: bool) { + self.push_handler.borrow_mut().new_stream_event( + self.push_id, + Http3ClientEvent::PushHeaderReady { + push_id: self.push_id, + headers, + interim, + fin, + }, + ); + } + + fn data_readable(&self, _stream_id: u64) { + self.push_handler.borrow_mut().new_stream_event( + self.push_id, + Http3ClientEvent::PushDataReadable { + push_id: self.push_id, + }, + ); + } + + fn reset(&self, _stream_id: u64, _error: AppError, _local: bool) {} +} diff --git a/third_party/rust/neqo-http3/src/push_stream.rs b/third_party/rust/neqo-http3/src/push_stream.rs new file mode 100644 index 0000000000..7bcce22a01 --- /dev/null +++ b/third_party/rust/neqo-http3/src/push_stream.rs @@ -0,0 +1,175 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::client_events::Http3ClientEvents; +use crate::push_controller::{PushController, RecvPushEvents}; +use crate::recv_message::{MessageType, RecvMessage}; +use crate::stream_type_reader::NewStreamTypeReader; +use crate::{Error, RecvStream, Res, ResetType}; +use neqo_qpack::decoder::QPackDecoder; +use neqo_transport::{AppError, Connection}; +use std::cell::RefCell; +use std::fmt::Display; +use std::rc::Rc; + +#[derive(Debug)] +enum PushStreamState { + ReadPushId(NewStreamTypeReader), + ReadResponse { push_id: u64, response: RecvMessage }, + Closed, +} + +impl PushStreamState { + pub fn push_id(&self) -> Option<u64> { + match self { + Self::ReadResponse { push_id, .. } => Some(*push_id), + _ => None, + } + } +} + +// The `PushController` keeps information about all push streams. Each push stream is responsible for contacting the +// `PushController` to consult it about the push state and to inform it when the push stream is done (this are signal +// from the peer: stream has been closed or reset). `PushController` handles CANCEL_PUSH frames and canceling +// push from applications and PUSH_PROMISE frames. +// +// `PushStream` is responsible for reading from a push stream. It is used for reading push_id as well. +// It is created when a new push stream is received. +// +// After push_id has been read, The `PushController` is informed about the stream and its push_id. This is done by +// calling `add_new_push_stream`. `add_new_push_stream` may return an error (this will be a connection error) +// or a bool. true means that the streams should continue and false means that the stream should be reset(the stream +// will be canceled if the push has been canceled already (CANCEL_PUSH frame or canceling push from the application) +// +// `PushStreams` are kept in Http3Connection::recv_streams the same as a normal request/response stream. +// Http3Connection and read_data is responsible for reading the push data. +// +// PushHeaderReady and PushDataReadable are posted through the `PushController` that may decide to postpone them if +// a push_promise has not been received for the stream. +// +// `PushStream` is responsible for removing itself from the `PushController`. +// +// `PushStream` may be reset from the peer in the same way as a request stream. The `PushStream` informs the +// `PushController` that will set the push state to closed and remove any push events. + +#[derive(Debug)] +pub(crate) struct PushStream { + state: PushStreamState, + stream_id: u64, + push_handler: Rc<RefCell<PushController>>, + events: Http3ClientEvents, +} + +impl PushStream { + pub fn new( + stream_id: u64, + push_handler: Rc<RefCell<PushController>>, + events: Http3ClientEvents, + ) -> Self { + Self { + state: PushStreamState::ReadPushId(NewStreamTypeReader::new()), + stream_id, + push_handler, + events, + } + } +} + +impl Display for PushStream { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Push stream {:?}", self.stream_id) + } +} + +impl RecvStream for PushStream { + fn receive(&mut self, conn: &mut Connection, decoder: &mut QPackDecoder) -> Res<()> { + loop { + match &mut self.state { + PushStreamState::ReadPushId(id_reader) => { + let push_id = id_reader.get_type(conn, self.stream_id); + let fin = id_reader.fin(); + if fin { + self.state = PushStreamState::Closed; + return Ok(()); + } + if let Some(p) = push_id { + if self + .push_handler + .borrow_mut() + .add_new_push_stream(p, self.stream_id)? + { + self.state = PushStreamState::ReadResponse { + push_id: p, + response: RecvMessage::new( + MessageType::Response, + self.stream_id, + Box::new(RecvPushEvents::new(p, self.push_handler.clone())), + None, + ), + }; + } else { + let _ = conn.stream_stop_sending( + self.stream_id, + Error::HttpRequestCancelled.code(), + ); + self.state = PushStreamState::Closed; + return Ok(()); + } + } + } + PushStreamState::ReadResponse { response, push_id } => { + response.receive(conn, decoder)?; + if response.done() { + self.push_handler.borrow_mut().close(*push_id); + self.state = PushStreamState::Closed; + } + return Ok(()); + } + _ => return Ok(()), + } + } + } + + fn header_unblocked(&mut self, conn: &mut Connection, decoder: &mut QPackDecoder) -> Res<()> { + self.receive(conn, decoder) + } + fn done(&self) -> bool { + matches!(self.state, PushStreamState::Closed) + } + + fn stream_reset(&self, app_error: AppError, decoder: &mut QPackDecoder, reset_type: ResetType) { + if !self.done() { + decoder.cancel_stream(self.stream_id); + } + match reset_type { + ResetType::App => {} + t => { + if let Some(push_id) = self.state.push_id() { + self.push_handler + .borrow_mut() + .push_stream_reset(push_id, app_error, t); + } + } + } + } + + fn read_data( + &mut self, + conn: &mut Connection, + decoder: &mut QPackDecoder, + buf: &mut [u8], + ) -> Res<(usize, bool)> { + if let PushStreamState::ReadResponse { response, push_id } = &mut self.state { + let res = response.read_data(conn, decoder, buf); + if response.done() { + self.push_handler.borrow_mut().close(*push_id); + } + res + } else { + Err(Error::InvalidStreamId) + } + } +} diff --git a/third_party/rust/neqo-http3/src/qlog.rs b/third_party/rust/neqo-http3/src/qlog.rs new file mode 100644 index 0000000000..d948f90557 --- /dev/null +++ b/third_party/rust/neqo-http3/src/qlog.rs @@ -0,0 +1,37 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::convert::TryFrom; + +use qlog::{self, event::Event, H3DataRecipient}; + +use neqo_common::qlog::NeqoQlog; + +pub fn h3_data_moved_up(qlog: &mut NeqoQlog, stream_id: u64, amount: usize) { + qlog.add_event(|| { + Some(Event::h3_data_moved( + stream_id.to_string(), + None, + Some(u64::try_from(amount).unwrap()), + Some(H3DataRecipient::Transport), + Some(H3DataRecipient::Application), + None, + )) + }) +} + +pub fn h3_data_moved_down(qlog: &mut NeqoQlog, stream_id: u64, amount: usize) { + qlog.add_event(|| { + Some(Event::h3_data_moved( + stream_id.to_string(), + None, + Some(u64::try_from(amount).unwrap()), + Some(H3DataRecipient::Application), + Some(H3DataRecipient::Transport), + None, + )) + }) +} diff --git a/third_party/rust/neqo-http3/src/recv_message.rs b/third_party/rust/neqo-http3/src/recv_message.rs new file mode 100644 index 0000000000..0902d71bf1 --- /dev/null +++ b/third_party/rust/neqo-http3/src/recv_message.rs @@ -0,0 +1,515 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::hframe::{HFrame, HFrameReader}; +use crate::push_controller::PushController; +use crate::qlog; +use crate::{Error, Header, Res}; +use crate::{RecvMessageEvents, RecvStream, ResetType}; + +use neqo_common::{qdebug, qinfo, qtrace}; +use neqo_qpack::decoder::QPackDecoder; +use neqo_transport::{AppError, Connection}; +use std::cell::RefCell; +use std::cmp::min; +use std::collections::VecDeque; +use std::convert::TryFrom; +use std::fmt::Debug; +use std::rc::Rc; + +const PSEUDO_HEADER_STATUS: u8 = 0x1; +const PSEUDO_HEADER_METHOD: u8 = 0x2; +const PSEUDO_HEADER_SCHEME: u8 = 0x4; +const PSEUDO_HEADER_AUTHORITY: u8 = 0x8; +const PSEUDO_HEADER_PATH: u8 = 0x10; +const REGULAR_HEADER: u8 = 0x80; + +#[derive(Debug)] +pub enum MessageType { + Request, + Response, +} + +/* + * Response stream state: + * WaitingForResponseHeaders : we wait for headers. in this state we can + * also get a PUSH_PROMISE frame. + * DecodingHeaders : In this step the headers will be decoded. The stream + * may be blocked in this state on encoder instructions. + * WaitingForData : we got HEADERS, we are waiting for one or more data + * frames. In this state we can receive one or more + * PUSH_PROMIS frames or a HEADERS frame carrying trailers. + * ReadingData : we got a DATA frame, now we letting the app read payload. + * From here we will go back to WaitingForData state to wait + * for more data frames or to CLosed state + * ClosePending : waiting for app to pick up data, after that we can delete + * the TransactionClient. + * Closed + */ +#[derive(Debug)] +enum RecvMessageState { + WaitingForResponseHeaders { frame_reader: HFrameReader }, + DecodingHeaders { header_block: Vec<u8>, fin: bool }, + WaitingForData { frame_reader: HFrameReader }, + ReadingData { remaining_data_len: usize }, + WaitingForFinAfterTrailers { frame_reader: HFrameReader }, + ClosePending, // Close must first be read by application + Closed, +} + +#[derive(Debug)] +struct PushInfo { + push_id: u64, + header_block: Vec<u8>, +} + +#[derive(Debug)] +pub(crate) struct RecvMessage { + state: RecvMessageState, + message_type: MessageType, + conn_events: Box<dyn RecvMessageEvents>, + push_handler: Option<Rc<RefCell<PushController>>>, + stream_id: u64, + blocked_push_promise: VecDeque<PushInfo>, +} + +impl ::std::fmt::Display for RecvMessage { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "RecvMessage stream_id:{}", self.stream_id) + } +} + +impl RecvMessage { + pub fn new( + message_type: MessageType, + stream_id: u64, + conn_events: Box<dyn RecvMessageEvents>, + push_handler: Option<Rc<RefCell<PushController>>>, + ) -> Self { + Self { + state: RecvMessageState::WaitingForResponseHeaders { + frame_reader: HFrameReader::new(), + }, + message_type, + conn_events, + push_handler, + stream_id, + blocked_push_promise: VecDeque::new(), + } + } + + fn handle_headers_frame(&mut self, header_block: Vec<u8>, fin: bool) -> Res<()> { + match self.state { + RecvMessageState::WaitingForResponseHeaders {..} => { + if header_block.is_empty() { + return Err(Error::HttpGeneralProtocolStream); + } else { + self.state = RecvMessageState::DecodingHeaders { header_block, fin }; + } + } + RecvMessageState::WaitingForData { ..} => { + // TODO implement trailers, for now just ignore them. + self.state = RecvMessageState::WaitingForFinAfterTrailers{frame_reader: HFrameReader::new()}; + } + RecvMessageState::WaitingForFinAfterTrailers {..} => { + return Err(Error::HttpFrameUnexpected); + } + _ => unreachable!("This functions is only called in WaitingForResponseHeaders | WaitingForData | WaitingForFinAfterTrailers state.") + } + Ok(()) + } + + fn handle_data_frame(&mut self, len: u64, fin: bool) -> Res<()> { + match self.state { + RecvMessageState::WaitingForResponseHeaders {..} | RecvMessageState::WaitingForFinAfterTrailers {..} => { + return Err(Error::HttpFrameUnexpected); + } + RecvMessageState::WaitingForData {..} => { + if len > 0 { + if fin { + return Err(Error::HttpFrame); + } + self.state = RecvMessageState::ReadingData { + remaining_data_len: usize::try_from(len).or(Err(Error::HttpFrame))?, + }; + } + } + _ => unreachable!("This functions is only called in WaitingForResponseHeaders | WaitingForData | WaitingForFinAfterTrailers state.") + } + Ok(()) + } + + fn add_headers( + &mut self, + headers: Vec<Header>, + fin: bool, + decoder: &mut QPackDecoder, + ) -> Res<()> { + let interim = self.is_interim(&headers)?; + let _valid = self.headers_valid(&headers)?; + if fin && interim { + return Err(Error::HttpGeneralProtocolStream); + } + + self.conn_events + .header_ready(self.stream_id, headers, interim, fin); + + if fin { + self.set_closed(decoder); + } else { + self.state = if interim { + RecvMessageState::WaitingForResponseHeaders { + frame_reader: HFrameReader::new(), + } + } else { + RecvMessageState::WaitingForData { + frame_reader: HFrameReader::new(), + } + }; + } + Ok(()) + } + + fn set_state_to_close_pending(&mut self, post_readable_event: bool) -> Res<()> { + // Stream has received fin. Depending on headers state set header_ready + // or data_readable event so that app can pick up the fin. + qtrace!([self], "set_state_to_close_pending: state={:?}", self.state); + + match self.state { + RecvMessageState::WaitingForResponseHeaders { .. } => { + return Err(Error::HttpGeneralProtocolStream); + } + RecvMessageState::ReadingData { .. } => {} + RecvMessageState::WaitingForData { .. } + | RecvMessageState::WaitingForFinAfterTrailers { .. } => { + if post_readable_event { + self.conn_events.data_readable(self.stream_id) + } + } + _ => unreachable!("Closing an already closed transaction."), + } + if !matches!(self.state, RecvMessageState::Closed) { + self.state = RecvMessageState::ClosePending; + } + Ok(()) + } + + fn handle_push_promise( + &mut self, + push_id: u64, + header_block: Vec<u8>, + decoder: &mut QPackDecoder, + ) -> Res<()> { + if self.push_handler.is_none() { + return Err(Error::HttpFrameUnexpected); + } + + if !self.blocked_push_promise.is_empty() { + self.blocked_push_promise.push_back(PushInfo { + push_id, + header_block, + }); + } else if let Some(headers) = decoder.decode_header_block(&header_block, self.stream_id)? { + self.push_handler + .as_ref() + .ok_or(Error::HttpFrameUnexpected)? + .borrow_mut() + .new_push_promise(push_id, self.stream_id, headers)? + } else { + self.blocked_push_promise.push_back(PushInfo { + push_id, + header_block, + }); + } + Ok(()) + } + + fn receive_internal( + &mut self, + conn: &mut Connection, + decoder: &mut QPackDecoder, + post_readable_event: bool, + ) -> Res<()> { + let label = ::neqo_common::log_subject!(::log::Level::Debug, self); + loop { + qdebug!([label], "state={:?}.", self.state); + match &mut self.state { + // In the following 3 states we need to read frames. + RecvMessageState::WaitingForResponseHeaders { frame_reader } + | RecvMessageState::WaitingForData { frame_reader } + | RecvMessageState::WaitingForFinAfterTrailers { frame_reader } => { + match frame_reader.receive(conn, self.stream_id)? { + (None, true) => { + break self.set_state_to_close_pending(post_readable_event); + } + (None, false) => break Ok(()), + (Some(frame), fin) => { + qinfo!( + [self], + "A new frame has been received: {:?}; state={:?} fin={}", + frame, + self.state, + fin, + ); + match frame { + HFrame::Headers { header_block } => { + self.handle_headers_frame(header_block, fin)? + } + HFrame::Data { len } => self.handle_data_frame(len, fin)?, + HFrame::PushPromise { + push_id, + header_block, + } => self.handle_push_promise(push_id, header_block, decoder)?, + _ => break Err(Error::HttpFrameUnexpected), + } + if matches!(self.state, RecvMessageState::Closed) { + break Ok(()); + } + if fin && !matches!(self.state, RecvMessageState::DecodingHeaders{..}) { + break self.set_state_to_close_pending(post_readable_event); + } + } + }; + } + RecvMessageState::DecodingHeaders { + ref header_block, + fin, + } => { + if decoder.refers_dynamic_table(header_block)? + && !self.blocked_push_promise.is_empty() + { + qinfo!( + [self], + "decoding header is blocked waiting for a push_promise header block." + ); + break Ok(()); + } + let done = *fin; + if let Some(headers) = + decoder.decode_header_block(header_block, self.stream_id)? + { + self.add_headers(headers, done, decoder)?; + if matches!(self.state, RecvMessageState::Closed) { + break Ok(()); + } + } else { + qinfo!([self], "decoding header is blocked."); + break Ok(()); + } + } + RecvMessageState::ReadingData { .. } => { + if post_readable_event { + self.conn_events.data_readable(self.stream_id); + } + break Ok(()); + } + RecvMessageState::ClosePending | RecvMessageState::Closed => { + panic!("Stream readable after being closed!"); + } + }; + } + } + + fn set_closed(&mut self, decoder: &mut QPackDecoder) { + if !self.blocked_push_promise.is_empty() { + decoder.cancel_stream(self.stream_id); + } + self.state = RecvMessageState::Closed; + } + + fn closing(&self) -> bool { + matches!( + self.state, + RecvMessageState::ClosePending | RecvMessageState::Closed + ) + } + + fn is_interim(&self, headers: &[Header]) -> Res<bool> { + match self.message_type { + MessageType::Response => { + let status = headers.iter().find(|(name, _value)| name == ":status"); + if let Some((_name, value)) = status { + #[allow(clippy::map_err_ignore, clippy::unknown_clippy_lints)] + let status_code = value.parse::<i32>().map_err(|_| Error::InvalidHeader)?; + Ok(status_code >= 100 && status_code < 200) + } else { + Err(Error::InvalidHeader) + } + } + MessageType::Request => Ok(false), + } + } + + fn track_pseudo(name: &str, state: &mut u8, message_type: &MessageType) -> Res<bool> { + let (pseudo, bit) = if name.starts_with(':') { + if *state & REGULAR_HEADER != 0 { + return Err(Error::InvalidHeader); + } + let bit = match message_type { + MessageType::Response => match name { + ":status" => PSEUDO_HEADER_STATUS, + _ => return Err(Error::InvalidHeader), + }, + MessageType::Request => match name { + ":method" => PSEUDO_HEADER_METHOD, + ":scheme" => PSEUDO_HEADER_SCHEME, + ":authority" => PSEUDO_HEADER_AUTHORITY, + ":path" => PSEUDO_HEADER_PATH, + _ => return Err(Error::InvalidHeader), + }, + }; + (true, bit) + } else { + (false, REGULAR_HEADER) + }; + + if *state & bit == 0 || !pseudo { + *state |= bit; + Ok(pseudo) + } else { + Err(Error::InvalidHeader) + } + } + + fn headers_valid(&self, headers: &[Header]) -> Res<bool> { + let mut method_value: Option<&String> = None; + let mut pseudo_state = 0; + for header in headers { + let is_pseudo = Self::track_pseudo(&header.0, &mut pseudo_state, &self.message_type)?; + + let mut bytes = header.0.bytes(); + if is_pseudo { + if header.0 == ":method" { + method_value = Some(&header.1); + } + let _ = bytes.next(); + } + + if bytes.any(|b| matches!(b, 0 | 0x10 | 0x13 | 0x3a | 0x41..=0x5a)) { + return Err(Error::InvalidHeader); // illegal characters. + } + + if matches!( + header.0.as_str(), + "connection" + | "host" + | "keep-alive" + | "proxy-connection" + | "te" + | "transfer-encoding" + | "upgrade" + ) { + return Err(Error::InvalidHeader); + } + } + // Clear the regular header bit, since we only check pseudo headers below. + pseudo_state &= !REGULAR_HEADER; + let pseudo_header_mask = match self.message_type { + MessageType::Response => PSEUDO_HEADER_STATUS, + MessageType::Request => { + if method_value == Some(&"CONNECT".to_string()) { + PSEUDO_HEADER_METHOD | PSEUDO_HEADER_AUTHORITY + } else { + PSEUDO_HEADER_METHOD | PSEUDO_HEADER_SCHEME | PSEUDO_HEADER_PATH + } + } + }; + if pseudo_state & pseudo_header_mask != pseudo_header_mask { + return Err(Error::InvalidHeader); + } + + Ok(true) + } +} + +impl RecvStream for RecvMessage { + fn receive(&mut self, conn: &mut Connection, decoder: &mut QPackDecoder) -> Res<()> { + self.receive_internal(conn, decoder, true) + } + + fn header_unblocked(&mut self, conn: &mut Connection, decoder: &mut QPackDecoder) -> Res<()> { + while let Some(p) = self.blocked_push_promise.front() { + if let Some(headers) = decoder.decode_header_block(&p.header_block, self.stream_id)? { + self.push_handler + .as_ref() + .ok_or(Error::HttpFrameUnexpected)? + .borrow_mut() + .new_push_promise(p.push_id, self.stream_id, headers)?; + self.blocked_push_promise.pop_front(); + } + } + + if self.blocked_push_promise.is_empty() { + return self.receive_internal(conn, decoder, true); + } + Ok(()) + } + + fn done(&self) -> bool { + matches!(self.state, RecvMessageState::Closed) + } + + fn stream_reset(&self, app_error: AppError, decoder: &mut QPackDecoder, reset_type: ResetType) { + if !self.closing() || !self.blocked_push_promise.is_empty() { + decoder.cancel_stream(self.stream_id); + } + match reset_type { + ResetType::Local => { + self.conn_events.reset(self.stream_id, app_error, true); + } + ResetType::Remote => { + self.conn_events.reset(self.stream_id, app_error, false); + } + ResetType::App => {} + } + } + + fn read_data( + &mut self, + conn: &mut Connection, + decoder: &mut QPackDecoder, + buf: &mut [u8], + ) -> Res<(usize, bool)> { + let mut written = 0; + loop { + match self.state { + RecvMessageState::ReadingData { + ref mut remaining_data_len, + } => { + let to_read = min(*remaining_data_len, buf.len() - written); + let (amount, fin) = conn + .stream_recv(self.stream_id, &mut buf[written..written + to_read]) + .map_err(|e| Error::map_stream_recv_errors(&e))?; + qlog::h3_data_moved_up(conn.qlog_mut(), self.stream_id, amount); + + debug_assert!(amount <= to_read); + *remaining_data_len -= amount; + written += amount; + + if fin { + if *remaining_data_len > 0 { + return Err(Error::HttpFrame); + } + self.set_closed(decoder); + break Ok((written, fin)); + } else if *remaining_data_len == 0 { + self.state = RecvMessageState::WaitingForData { + frame_reader: HFrameReader::new(), + }; + self.receive_internal(conn, decoder, false)?; + } else { + break Ok((written, false)); + } + } + RecvMessageState::ClosePending => { + self.set_closed(decoder); + break Ok((written, true)); + } + _ => break Ok((written, false)), + } + } + } +} diff --git a/third_party/rust/neqo-http3/src/send_message.rs b/third_party/rust/neqo-http3/src/send_message.rs new file mode 100644 index 0000000000..025f1fcf56 --- /dev/null +++ b/third_party/rust/neqo-http3/src/send_message.rs @@ -0,0 +1,300 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::hframe::HFrame; +use crate::qlog; +use crate::Header; +use crate::{Error, Res}; + +use neqo_common::{qdebug, qinfo, qtrace, Encoder}; +use neqo_qpack::encoder::QPackEncoder; +use neqo_transport::{AppError, Connection}; +use std::cmp::min; +use std::fmt::Debug; + +const MAX_DATA_HEADER_SIZE_2: usize = (1 << 6) - 1; // Maximal amount of data with DATA frame header size 2 +const MAX_DATA_HEADER_SIZE_2_LIMIT: usize = MAX_DATA_HEADER_SIZE_2 + 3; // 63 + 3 (size of the next buffer data frame header) +const MAX_DATA_HEADER_SIZE_3: usize = (1 << 14) - 1; // Maximal amount of data with DATA frame header size 3 +const MAX_DATA_HEADER_SIZE_3_LIMIT: usize = MAX_DATA_HEADER_SIZE_3 + 5; // 16383 + 5 (size of the next buffer data frame header) +const MAX_DATA_HEADER_SIZE_5: usize = (1 << 30) - 1; // Maximal amount of data with DATA frame header size 3 +const MAX_DATA_HEADER_SIZE_5_LIMIT: usize = MAX_DATA_HEADER_SIZE_5 + 9; // 1073741823 + 9 (size of the next buffer data frame header) + +pub(crate) trait SendMessageEvents: Debug { + fn data_writable(&self, stream_id: u64); + fn remove_send_side_event(&self, stream_id: u64); + fn stop_sending(&self, stream_id: u64, app_err: AppError); +} + +/* + * SendMessage states: + * Uninitialized + * Initialized : Headers are present but still not encoded. A message body may be present as well. + * The client side sends a message body using the send_body() function that directly + * writes into a transport stream. The server side sets headers and body when + * initializing a send message (TODO: make server use send_body as well) + * SendingInitialMessage : sending headers and maybe message body. From here we may switch to + * SendingData or Closed (if the app does not want to send data and + * has already closed the send stream). + * SendingData : We are sending request data until the app closes the stream. + * Closed + */ + +#[derive(PartialEq, Debug)] +enum SendMessageState { + Uninitialized, + Initialized { + headers: Vec<Header>, + data: Option<Vec<u8>>, + fin: bool, + }, + SendingInitialMessage { + buf: Vec<u8>, + fin: bool, + }, + SendingData, + Closed, +} + +impl SendMessageState { + pub fn is_sending_closed(&self) -> bool { + match self { + Self::Initialized { fin, .. } | Self::SendingInitialMessage { fin, .. } => *fin, + Self::SendingData => false, + _ => true, + } + } + + pub fn done(&self) -> bool { + matches!(self, Self::Closed) + } + + pub fn is_state_sending_data(&self) -> bool { + matches!(self, Self::SendingData) + } +} + +#[derive(Debug)] +pub(crate) struct SendMessage { + state: SendMessageState, + stream_id: u64, + conn_events: Box<dyn SendMessageEvents>, +} + +impl SendMessage { + pub fn new(stream_id: u64, conn_events: Box<dyn SendMessageEvents>) -> Self { + qinfo!("Create a request stream_id={}", stream_id); + Self { + state: SendMessageState::Uninitialized, + stream_id, + conn_events, + } + } + + pub fn new_with_headers( + stream_id: u64, + headers: Vec<Header>, + conn_events: Box<dyn SendMessageEvents>, + ) -> Self { + qinfo!("Create a request stream_id={}", stream_id); + Self { + state: SendMessageState::Initialized { + headers, + data: None, + fin: false, + }, + stream_id, + conn_events, + } + } + + pub fn set_message(&mut self, headers: &[Header], data: Option<&[u8]>) -> Res<()> { + if !matches!(self.state, SendMessageState::Uninitialized) { + return Err(Error::AlreadyInitialized); + } + + self.state = SendMessageState::Initialized { + headers: headers.to_vec(), + data: data.map(|d| d.to_vec()), + fin: true, + }; + Ok(()) + } + + pub fn send_body(&mut self, conn: &mut Connection, buf: &[u8]) -> Res<usize> { + qtrace!( + [self], + "send_body: state={:?} len={}", + self.state, + buf.len() + ); + match self.state { + SendMessageState::Uninitialized + | SendMessageState::Initialized { .. } + | SendMessageState::SendingInitialMessage { .. } => Ok(0), + SendMessageState::SendingData => { + let available = conn + .stream_avail_send_space(self.stream_id) + .map_err(|e| Error::map_stream_send_errors(&e))?; + if available <= 2 { + return Ok(0); + } + let to_send; + if available <= MAX_DATA_HEADER_SIZE_2_LIMIT { + // 63 + 3 + to_send = min(min(buf.len(), available - 2), MAX_DATA_HEADER_SIZE_2); + } else if available <= MAX_DATA_HEADER_SIZE_3_LIMIT { + // 16383 + 5 + to_send = min(min(buf.len(), available - 3), MAX_DATA_HEADER_SIZE_3); + } else if available <= MAX_DATA_HEADER_SIZE_5 { + // 1073741823 + 9 + to_send = min(min(buf.len(), available - 5), MAX_DATA_HEADER_SIZE_5_LIMIT); + } else { + to_send = min(buf.len(), available - 9); + } + + qinfo!( + [self], + "send_request_body: available={} to_send={}.", + available, + to_send + ); + + let data_frame = HFrame::Data { + len: to_send as u64, + }; + let mut enc = Encoder::default(); + data_frame.encode(&mut enc); + let sent_fh = conn + .stream_send(self.stream_id, &enc) + .map_err(|e| Error::map_stream_send_errors(&e))?; + debug_assert_eq!(sent_fh, enc.len()); + + let sent = conn + .stream_send(self.stream_id, &buf[..to_send]) + .map_err(|e| Error::map_stream_send_errors(&e))?; + qlog::h3_data_moved_down(&mut conn.qlog_mut(), self.stream_id, to_send); + Ok(sent) + } + SendMessageState::Closed => Err(Error::AlreadyClosed), + } + } + + pub fn done(&self) -> bool { + self.state.done() + } + + pub fn stream_writable(&self) { + if self.state.is_state_sending_data() { + self.conn_events.data_writable(self.stream_id); + } + } + + /// # Errors + /// `ClosedCriticalStream` if the encoder stream is closed. + /// `InternalError` if an unexpected error occurred. + fn ensure_encoded(&mut self, conn: &mut Connection, encoder: &mut QPackEncoder) -> Res<()> { + if let SendMessageState::Initialized { headers, data, fin } = &self.state { + qdebug!([self], "Encoding headers"); + let header_block = encoder.encode_header_block(conn, &headers, self.stream_id)?; + let hframe = HFrame::Headers { + header_block: header_block.to_vec(), + }; + let mut d = Encoder::default(); + hframe.encode(&mut d); + if let Some(buf) = data { + qdebug!([self], "Encoding data"); + let d_frame = HFrame::Data { + len: buf.len() as u64, + }; + d_frame.encode(&mut d); + d.encode(&buf); + } + + self.state = SendMessageState::SendingInitialMessage { + buf: d.into(), + fin: *fin, + }; + } + Ok(()) + } + + /// # Errors + /// `ClosedCriticalStream` if the encoder stream is closed. + /// `InternalError` if an unexpected error occurred. + /// `InvalidStreamId` if the stream does not exist, + /// `AlreadyClosed` if the stream has already been closed. + /// `TransportStreamDoesNotExist` if the transport stream does not exist (this may happen if `process_output` + /// has not been called when needed, and HTTP3 layer has not picked up the info that the stream has been closed.) + pub fn send(&mut self, conn: &mut Connection, encoder: &mut QPackEncoder) -> Res<()> { + self.ensure_encoded(conn, encoder)?; + + let label = if ::log::log_enabled!(::log::Level::Debug) { + format!("{}", self) + } else { + String::new() + }; + + if let SendMessageState::SendingInitialMessage { ref mut buf, fin } = self.state { + let sent = + Error::map_error(conn.stream_send(self.stream_id, &buf), Error::HttpInternal)?; + qlog::h3_data_moved_down(&mut conn.qlog_mut(), self.stream_id, sent); + + qtrace!([label], "{} bytes sent", sent); + + if sent == buf.len() { + if fin { + Error::map_error(conn.stream_close_send(self.stream_id), Error::HttpInternal)?; + self.state = SendMessageState::Closed; + qtrace!([label], "done sending request"); + } else { + self.state = SendMessageState::SendingData; + self.conn_events.data_writable(self.stream_id); + qtrace!([label], "change to state SendingData"); + } + } else { + let b = buf.split_off(sent); + *buf = b; + } + } + Ok(()) + } + + // SendMessage owns headers and sends them. It may also own data for the server side. + // This method returns if they're still being sent. Request body (if any) is sent by + // http client afterwards using `send_request_body` after receiving DataWritable event. + pub fn has_data_to_send(&self) -> bool { + matches!(self.state, SendMessageState::Initialized {..} | SendMessageState::SendingInitialMessage { .. } ) + } + + pub fn close(&mut self, conn: &mut Connection) -> Res<()> { + match self.state { + SendMessageState::SendingInitialMessage { ref mut fin, .. } + | SendMessageState::Initialized { ref mut fin, .. } => { + *fin = true; + } + _ => { + self.state = SendMessageState::Closed; + conn.stream_close_send(self.stream_id)?; + } + } + + self.conn_events.remove_send_side_event(self.stream_id); + Ok(()) + } + + pub fn stop_sending(&mut self, app_err: AppError) { + if !self.state.is_sending_closed() { + self.conn_events.remove_send_side_event(self.stream_id); + self.conn_events.stop_sending(self.stream_id, app_err); + } + } +} + +impl ::std::fmt::Display for SendMessage { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "SendMesage {}", self.stream_id) + } +} diff --git a/third_party/rust/neqo-http3/src/server.rs b/third_party/rust/neqo-http3/src/server.rs new file mode 100644 index 0000000000..f95ebaf1b6 --- /dev/null +++ b/third_party/rust/neqo-http3/src/server.rs @@ -0,0 +1,1068 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(clippy::module_name_repetitions)] + +use crate::connection::Http3State; +use crate::connection_server::Http3ServerHandler; +use crate::server_connection_events::Http3ServerConnEvent; +use crate::server_events::{ClientRequestStream, Http3ServerEvent, Http3ServerEvents}; +use crate::settings::HttpZeroRttChecker; +use crate::Res; +use neqo_common::{qtrace, Datagram}; +use neqo_crypto::{AntiReplay, Cipher}; +use neqo_qpack::QpackSettings; +use neqo_transport::server::{ActiveConnectionRef, Server, ValidateAddress}; +use neqo_transport::{ConnectionIdManager, ConnectionParameters, Output}; +use std::cell::RefCell; +use std::cell::RefMut; +use std::collections::HashMap; +use std::path::PathBuf; +use std::rc::Rc; +use std::time::Instant; + +type HandlerRef = Rc<RefCell<Http3ServerHandler>>; + +const MAX_EVENT_DATA_SIZE: usize = 1024; + +pub struct Http3Server { + server: Server, + qpack_settings: QpackSettings, + http3_handlers: HashMap<ActiveConnectionRef, HandlerRef>, + events: Http3ServerEvents, +} + +impl ::std::fmt::Display for Http3Server { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + write!(f, "Http3 server ") + } +} + +impl Http3Server { + /// # Errors + /// Making a `neqo_transport::Server` may produce an error. This can only be a crypto error if + /// the socket can't be created or configured. + pub fn new( + now: Instant, + certs: &[impl AsRef<str>], + protocols: &[impl AsRef<str>], + anti_replay: AntiReplay, + cid_manager: Rc<RefCell<dyn ConnectionIdManager>>, + qpack_settings: QpackSettings, + ) -> Res<Self> { + Ok(Self { + server: Server::new( + now, + certs, + protocols, + anti_replay, + Box::new(HttpZeroRttChecker::new(qpack_settings)), + cid_manager, + ConnectionParameters::default(), + )?, + qpack_settings, + http3_handlers: HashMap::new(), + events: Http3ServerEvents::default(), + }) + } + + pub fn set_qlog_dir(&mut self, dir: Option<PathBuf>) { + self.server.set_qlog_dir(dir) + } + + pub fn set_validation(&mut self, v: ValidateAddress) { + self.server.set_validation(v); + } + + pub fn set_ciphers(&mut self, ciphers: impl AsRef<[Cipher]>) { + self.server.set_ciphers(ciphers); + } + + pub fn process(&mut self, dgram: Option<Datagram>, now: Instant) -> Output { + qtrace!([self], "Process."); + let out = self.server.process(dgram, now); + self.process_http3(now); + // If we do not that a dgram already try again after process_http3. + match out { + Output::Datagram(d) => { + qtrace!([self], "Send packet: {:?}", d); + Output::Datagram(d) + } + _ => self.server.process(None, now), + } + } + + /// Process HTTP3 layer. + fn process_http3(&mut self, now: Instant) { + qtrace!([self], "Process http3 internal."); + let mut active_conns = self.server.active_connections(); + + // We need to find connections that needs to be process on http3 level. + let mut http3_active: Vec<ActiveConnectionRef> = self + .http3_handlers + .iter() + .filter_map(|(conn, handler)| { + if handler.borrow_mut().should_be_processed() && !active_conns.contains(&conn) { + Some(conn) + } else { + None + } + }) + .cloned() + .collect(); + // For http_active connection we need to put them in neqo-transport's server + // waiting queue. + active_conns.append(&mut http3_active); + active_conns.dedup(); + active_conns + .iter() + .for_each(|conn| self.server.add_to_waiting(conn.clone())); + let qpack_settings = self.qpack_settings; + for mut conn in active_conns { + let handler = self + .http3_handlers + .entry(conn.clone()) + .or_insert_with(|| Rc::new(RefCell::new(Http3ServerHandler::new(qpack_settings)))); + + handler + .borrow_mut() + .process_http3(&mut conn.borrow_mut(), now); + let mut remove = false; + { + let mut handler_borrowed = handler.borrow_mut(); + while let Some(e) = handler_borrowed.next_event() { + match e { + Http3ServerConnEvent::Headers { + stream_id, + headers, + fin, + } => self.events.headers( + ClientRequestStream::new(conn.clone(), handler.clone(), stream_id), + headers, + fin, + ), + Http3ServerConnEvent::DataReadable { stream_id } => { + prepare_data( + stream_id, + &mut handler_borrowed, + &mut conn, + &handler, + now, + &mut self.events, + ); + } + Http3ServerConnEvent::StateChange(state) => { + self.events + .connection_state_change(conn.clone(), state.clone()); + if let Http3State::Closed { .. } = state { + remove = true; + } + } + } + } + } + if remove { + self.http3_handlers.remove(&conn.clone()); + } + } + } + + /// Get all current events. Best used just in debug/testing code, use + /// `next_event` instead. + pub fn events(&mut self) -> impl Iterator<Item = Http3ServerEvent> { + self.events.events() + } + + /// Return true if there are outstanding events. + #[must_use] + pub fn has_events(&self) -> bool { + self.events.has_events() + } + + /// Get events that indicate state changes on the connection. This method + /// correctly handles cases where handling one event can obsolete + /// previously-queued events, or cause new events to be generated. + pub fn next_event(&mut self) -> Option<Http3ServerEvent> { + self.events.next_event() + } +} +fn prepare_data( + stream_id: u64, + handler_borrowed: &mut RefMut<Http3ServerHandler>, + conn: &mut ActiveConnectionRef, + handler: &HandlerRef, + now: Instant, + events: &mut Http3ServerEvents, +) { + loop { + let mut data = vec![0; MAX_EVENT_DATA_SIZE]; + let res = + handler_borrowed.read_request_data(&mut conn.borrow_mut(), now, stream_id, &mut data); + if let Ok((amount, fin)) = res { + if amount > 0 { + if amount < MAX_EVENT_DATA_SIZE { + data.resize(amount, 0); + } + events.data( + ClientRequestStream::new(conn.clone(), handler.clone(), stream_id), + data, + fin, + ); + } + if amount < MAX_EVENT_DATA_SIZE || fin { + break; + } + } else { + // Any error will closed the handler, just ignore this event, the next event must + // be a state change event. + break; + } + } +} + +#[cfg(test)] +mod tests { + use super::{Http3Server, Http3ServerEvent, Http3State, Rc, RefCell}; + use crate::{Error, Header}; + use neqo_common::event::Provider; + use neqo_crypto::AuthenticationStatus; + use neqo_qpack::encoder::QPackEncoder; + use neqo_qpack::QpackSettings; + use neqo_transport::{ + CloseError, Connection, ConnectionEvent, FixedConnectionIdManager, State, StreamType, + ZeroRttState, + }; + use std::ops::{Deref, DerefMut}; + use test_fixture::{ + anti_replay, default_client, fixture_init, now, DEFAULT_ALPN, DEFAULT_KEYS, + }; + + const DEFAULT_SETTINGS: QpackSettings = QpackSettings { + max_table_size_encoder: 100, + max_table_size_decoder: 100, + max_blocked_streams: 100, + }; + + pub fn create_server(settings: QpackSettings) -> Http3Server { + fixture_init(); + Http3Server::new( + now(), + DEFAULT_KEYS, + DEFAULT_ALPN, + anti_replay(), + Rc::new(RefCell::new(FixedConnectionIdManager::new(5))), + settings, + ) + .expect("create a server") + } + + /// Create a http3 server with default configuration. + pub fn default_server() -> Http3Server { + create_server(DEFAULT_SETTINGS) + } + + fn assert_closed(hconn: &mut Http3Server, expected: &Error) { + let err = CloseError::Application(expected.code()); + let closed = |e| { + matches!(e, + Http3ServerEvent::StateChange{ state: Http3State::Closing(e), .. } + | Http3ServerEvent::StateChange{ state: Http3State::Closed(e), .. } + if e == err) + }; + assert!(hconn.events().any(closed)); + } + + fn assert_connected(hconn: &mut Http3Server) { + let connected = + |e| matches!(e, Http3ServerEvent::StateChange{ state: Http3State::Connected, ..} ); + assert!(hconn.events().any(connected)); + } + + fn assert_not_closed(hconn: &mut Http3Server) { + let closed = |e| { + matches!(e, + Http3ServerEvent::StateChange{ state: Http3State::Closing(..), .. }) + }; + assert!(!hconn.events().any(closed)); + } + + const CLIENT_SIDE_CONTROL_STREAM_ID: u64 = 2; + const CLIENT_SIDE_ENCODER_STREAM_ID: u64 = 6; + const CLIENT_SIDE_DECODER_STREAM_ID: u64 = 10; + const SERVER_SIDE_CONTROL_STREAM_ID: u64 = 3; + const SERVER_SIDE_ENCODER_STREAM_ID: u64 = 7; + const SERVER_SIDE_DECODER_STREAM_ID: u64 = 11; + + fn connect_transport(server: &mut Http3Server, client: &mut Connection, resume: bool) { + let c1 = client.process(None, now()).dgram(); + let s1 = server.process(c1, now()).dgram(); + let c2 = client.process(s1, now()).dgram(); + let needs_auth = client + .events() + .any(|e| e == ConnectionEvent::AuthenticationNeeded); + let c2 = if needs_auth { + assert!(!resume); + // c2 should just be an ACK, so absorb that. + let s_ack = server.process(c2, now()).dgram(); + assert!(s_ack.is_none()); + + client.authenticated(AuthenticationStatus::Ok, now()); + client.process(None, now()).dgram() + } else { + assert!(resume); + c2 + }; + assert!(client.state().connected()); + let s2 = server.process(c2, now()).dgram(); + assert_connected(server); + let c3 = client.process(s2, now()).dgram(); + assert!(c3.is_none()); + } + + // Start a client/server and check setting frame. + fn connect_and_receive_settings() -> (Http3Server, Connection) { + // Create a server and connect it to a client. + // We will have a http3 server on one side and a neqo_transport + // connection on the other side so that we can check what the http3 + // side sends and also to simulate an incorrectly behaving http3 + // client. + + const CONTROL_STREAM_DATA: &[u8] = &[0x0, 0x4, 0x6, 0x1, 0x40, 0x64, 0x7, 0x40, 0x64]; + + let mut server = default_server(); + let mut client = default_client(); + connect_transport(&mut server, &mut client, false); + + let mut connected = false; + while let Some(e) = client.next_event() { + match e { + ConnectionEvent::NewStream { stream_id } => { + assert!( + (stream_id.as_u64() == SERVER_SIDE_CONTROL_STREAM_ID) + || (stream_id.as_u64() == SERVER_SIDE_ENCODER_STREAM_ID) + || (stream_id.as_u64() == SERVER_SIDE_DECODER_STREAM_ID) + ); + assert_eq!(stream_id.stream_type(), StreamType::UniDi); + } + ConnectionEvent::RecvStreamReadable { stream_id } => { + if stream_id == CLIENT_SIDE_CONTROL_STREAM_ID + || stream_id == SERVER_SIDE_CONTROL_STREAM_ID + { + // the control stream + let mut buf = [0_u8; 100]; + let (amount, fin) = client.stream_recv(stream_id, &mut buf).unwrap(); + assert_eq!(fin, false); + assert_eq!(amount, CONTROL_STREAM_DATA.len()); + assert_eq!(&buf[..9], CONTROL_STREAM_DATA); + } else if stream_id == CLIENT_SIDE_ENCODER_STREAM_ID + || stream_id == SERVER_SIDE_ENCODER_STREAM_ID + { + let mut buf = [0_u8; 100]; + let (amount, fin) = client.stream_recv(stream_id, &mut buf).unwrap(); + assert_eq!(fin, false); + assert_eq!(amount, 1); + assert_eq!(buf[..1], [0x2]); + } else if stream_id == CLIENT_SIDE_DECODER_STREAM_ID + || stream_id == SERVER_SIDE_DECODER_STREAM_ID + { + let mut buf = [0_u8; 100]; + let (amount, fin) = client.stream_recv(stream_id, &mut buf).unwrap(); + assert_eq!(fin, false); + assert_eq!(amount, 1); + assert_eq!(buf[..1], [0x3]); + } else { + panic!("unexpected event"); + } + } + ConnectionEvent::SendStreamWritable { stream_id } => { + assert!( + (stream_id == CLIENT_SIDE_CONTROL_STREAM_ID) + || (stream_id == CLIENT_SIDE_ENCODER_STREAM_ID) + || (stream_id == CLIENT_SIDE_DECODER_STREAM_ID) + ); + } + ConnectionEvent::StateChange(State::Connected) => connected = true, + ConnectionEvent::StateChange(_) => (), + _ => panic!("unexpected event"), + } + } + assert!(connected); + (server, client) + } + + // Test http3 connection inintialization. + // The server will open the control and qpack streams and send SETTINGS frame. + #[test] + fn test_server_connect() { + let _ = connect_and_receive_settings(); + } + + struct PeerConnection { + conn: Connection, + control_stream_id: u64, + } + + impl PeerConnection { + /// A shortcut for sending on the control stream. + fn control_send(&mut self, data: &[u8]) { + let res = self.conn.stream_send(self.control_stream_id, data); + assert_eq!(res, Ok(data.len())); + } + } + + impl Deref for PeerConnection { + type Target = Connection; + fn deref(&self) -> &Self::Target { + &self.conn + } + } + + impl DerefMut for PeerConnection { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.conn + } + } + + // Connect transport, send and receive settings. + fn connect() -> (Http3Server, PeerConnection) { + let (mut hconn, mut neqo_trans_conn) = connect_and_receive_settings(); + let control_stream = neqo_trans_conn.stream_create(StreamType::UniDi).unwrap(); + let mut sent = neqo_trans_conn.stream_send( + control_stream, + &[0x0, 0x4, 0x6, 0x1, 0x40, 0x64, 0x7, 0x40, 0x64], + ); + assert_eq!(sent, Ok(9)); + let mut encoder = QPackEncoder::new( + QpackSettings { + max_table_size_encoder: 100, + max_table_size_decoder: 0, + max_blocked_streams: 0, + }, + true, + ); + encoder.add_send_stream(neqo_trans_conn.stream_create(StreamType::UniDi).unwrap()); + encoder.send(&mut neqo_trans_conn).unwrap(); + let decoder_stream = neqo_trans_conn.stream_create(StreamType::UniDi).unwrap(); + sent = neqo_trans_conn.stream_send(decoder_stream, &[0x3]); + assert_eq!(sent, Ok(1)); + let out1 = neqo_trans_conn.process(None, now()); + let out2 = hconn.process(out1.dgram(), now()); + let _ = neqo_trans_conn.process(out2.dgram(), now()); + + // assert no error occured. + assert_not_closed(&mut hconn); + ( + hconn, + PeerConnection { + conn: neqo_trans_conn, + control_stream_id: control_stream, + }, + ) + } + + // Server: Test receiving a new control stream and a SETTINGS frame. + #[test] + fn test_server_receive_control_frame() { + let _ = connect(); + } + + // Server: Test that the connection will be closed if control stream + // has been closed. + #[test] + fn test_server_close_control_stream() { + let (mut hconn, mut peer_conn) = connect(); + let control = peer_conn.control_stream_id; + peer_conn.stream_close_send(control).unwrap(); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); + } + + // Server: test missing SETTINGS frame + // (the first frame sent is a MAX_PUSH_ID frame). + #[test] + fn test_server_missing_settings() { + let (mut hconn, mut neqo_trans_conn) = connect_and_receive_settings(); + // Create client control stream. + let control_stream = neqo_trans_conn.stream_create(StreamType::UniDi).unwrap(); + // Send a MAX_PUSH_ID frame instead. + let sent = neqo_trans_conn.stream_send(control_stream, &[0x0, 0xd, 0x1, 0xf]); + assert_eq!(sent, Ok(4)); + let out = neqo_trans_conn.process(None, now()); + hconn.process(out.dgram(), now()); + assert_closed(&mut hconn, &Error::HttpMissingSettings); + } + + // Server: receiving SETTINGS frame twice causes connection close + // with error HTTP_UNEXPECTED_FRAME. + #[test] + fn test_server_receive_settings_twice() { + let (mut hconn, mut peer_conn) = connect(); + // send the second SETTINGS frame. + peer_conn.control_send(&[0x4, 0x6, 0x1, 0x40, 0x64, 0x7, 0x40, 0x64]); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + assert_closed(&mut hconn, &Error::HttpFrameUnexpected); + } + + fn test_wrong_frame_on_control_stream(v: &[u8]) { + let (mut hconn, mut peer_conn) = connect(); + + // receive a frame that is not allowed on the control stream. + peer_conn.control_send(v); + + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + assert_closed(&mut hconn, &Error::HttpFrameUnexpected); + } + + // send DATA frame on a cortrol stream + #[test] + fn test_server_data_frame_on_control_stream() { + test_wrong_frame_on_control_stream(&[0x0, 0x2, 0x1, 0x2]); + } + + // send HEADERS frame on a cortrol stream + #[test] + fn test_server_headers_frame_on_control_stream() { + test_wrong_frame_on_control_stream(&[0x1, 0x2, 0x1, 0x2]); + } + + // send PUSH_PROMISE frame on a cortrol stream + #[test] + fn test_server_push_promise_frame_on_control_stream() { + test_wrong_frame_on_control_stream(&[0x5, 0x2, 0x1, 0x2]); + } + + // Server: receive unknown stream type + // also test getting stream id that does not fit into a single byte. + #[test] + fn test_server_received_unknown_stream() { + let (mut hconn, mut peer_conn) = connect(); + + // create a stream with unknown type. + let new_stream_id = peer_conn.stream_create(StreamType::UniDi).unwrap(); + let _ = peer_conn.stream_send(new_stream_id, &[0x41, 0x19, 0x4, 0x4, 0x6, 0x0, 0x8, 0x0]); + let out = peer_conn.process(None, now()); + let out = hconn.process(out.dgram(), now()); + let _ = peer_conn.process(out.dgram(), now()); + let out = hconn.process(None, now()); + let _ = peer_conn.process(out.dgram(), now()); + + // check for stop-sending with Error::HttpStreamCreation. + let mut stop_sending_event_found = false; + while let Some(e) = peer_conn.next_event() { + if let ConnectionEvent::SendStreamStopSending { + stream_id, + app_error, + } = e + { + stop_sending_event_found = true; + assert_eq!(stream_id, new_stream_id); + assert_eq!(app_error, Error::HttpStreamCreation.code()); + } + } + assert!(stop_sending_event_found); + assert_not_closed(&mut hconn); + } + + // Server: receiving a push stream on a server should cause WrongStreamDirection + #[test] + fn test_server_received_push_stream() { + let (mut hconn, mut peer_conn) = connect(); + + // create a push stream. + let push_stream_id = peer_conn.stream_create(StreamType::UniDi).unwrap(); + let _ = peer_conn.stream_send(push_stream_id, &[0x1]); + let out = peer_conn.process(None, now()); + let out = hconn.process(out.dgram(), now()); + let _ = peer_conn.conn.process(out.dgram(), now()); + assert_closed(&mut hconn, &Error::HttpStreamCreation); + } + + //// Test reading of a slowly streamed frame. bytes are received one by one + #[test] + fn test_server_frame_reading() { + let (mut hconn, mut peer_conn) = connect_and_receive_settings(); + + // create a control stream. + let control_stream = peer_conn.stream_create(StreamType::UniDi).unwrap(); + + // send the stream type + let mut sent = peer_conn.stream_send(control_stream, &[0x0]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + // start sending SETTINGS frame + sent = peer_conn.stream_send(control_stream, &[0x4]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + sent = peer_conn.stream_send(control_stream, &[0x4]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + sent = peer_conn.stream_send(control_stream, &[0x6]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + sent = peer_conn.stream_send(control_stream, &[0x0]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + sent = peer_conn.stream_send(control_stream, &[0x8]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + sent = peer_conn.stream_send(control_stream, &[0x0]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + assert_not_closed(&mut hconn); + + // Now test PushPromise + sent = peer_conn.stream_send(control_stream, &[0x5]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + sent = peer_conn.stream_send(control_stream, &[0x5]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + sent = peer_conn.stream_send(control_stream, &[0x4]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + sent = peer_conn.stream_send(control_stream, &[0x61]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + sent = peer_conn.stream_send(control_stream, &[0x62]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + sent = peer_conn.stream_send(control_stream, &[0x63]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + sent = peer_conn.stream_send(control_stream, &[0x64]); + assert_eq!(sent, Ok(1)); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + // PUSH_PROMISE on a control stream will cause an error + assert_closed(&mut hconn, &Error::HttpFrameUnexpected); + } + + // Test reading of a slowly streamed frame. bytes are received one by one + fn test_incomplet_frame(res: &[u8]) { + let (mut hconn, mut peer_conn) = connect_and_receive_settings(); + + // send an incomplete reequest. + let stream_id = peer_conn.stream_create(StreamType::BiDi).unwrap(); + peer_conn.stream_send(stream_id, res).unwrap(); + peer_conn.stream_close_send(stream_id).unwrap(); + + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + assert_closed(&mut hconn, &Error::HttpFrame); + } + + const REQUEST_WITH_BODY: &[u8] = &[ + // headers + 0x01, 0x10, 0x00, 0x00, 0xd1, 0xd7, 0x50, 0x89, 0x41, 0xe9, 0x2a, 0x67, 0x35, 0x53, 0x2e, + 0x43, 0xd3, 0xc1, // the first data frame. + 0x0, 0x3, 0x61, 0x62, 0x63, // the second data frame. + 0x0, 0x3, 0x64, 0x65, 0x66, + ]; + const REQUEST_BODY: &[u8] = &[0x61, 0x62, 0x63, 0x64, 0x65, 0x66]; + + const RESPONSE_BODY: &[u8] = &[0x67, 0x68, 0x69]; + + fn check_request_header(header: &[Header]) { + let expected_request_header = &[ + (String::from(":method"), String::from("GET")), + (String::from(":scheme"), String::from("https")), + (String::from(":authority"), String::from("something.com")), + (String::from(":path"), String::from("/")), + ]; + assert_eq!(header, expected_request_header); + } + + // Incomplete DATA frame + #[test] + fn test_server_incomplet_data_frame() { + test_incomplet_frame(&REQUEST_WITH_BODY[..22]); + } + + // Incomplete HEADERS frame + #[test] + fn test_server_incomplet_headers_frame() { + test_incomplet_frame(&REQUEST_WITH_BODY[..10]); + } + + #[test] + fn test_server_incomplet_unknown_frame() { + test_incomplet_frame(&[0x21]); + } + + #[test] + fn test_server_request_with_body() { + let (mut hconn, mut peer_conn) = connect(); + + let stream_id = peer_conn.stream_create(StreamType::BiDi).unwrap(); + peer_conn.stream_send(stream_id, REQUEST_WITH_BODY).unwrap(); + peer_conn.stream_close_send(stream_id).unwrap(); + + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + // Check connection event. There should be 1 Header and 2 data events. + let mut headers_frames = 0; + let mut data_received = 0; + while let Some(event) = hconn.next_event() { + match event { + Http3ServerEvent::Headers { headers, fin, .. } => { + check_request_header(&headers); + assert_eq!(fin, false); + headers_frames += 1; + } + Http3ServerEvent::Data { + mut request, + data, + fin, + } => { + assert_eq!(data, REQUEST_BODY); + assert_eq!(fin, true); + request + .set_response( + &[ + (String::from(":status"), String::from("200")), + (String::from("content-length"), String::from("3")), + ], + RESPONSE_BODY, + ) + .unwrap(); + data_received += 1; + } + _ => {} + } + } + assert_eq!(headers_frames, 1); + assert_eq!(data_received, 1); + } + + #[test] + fn test_server_request_with_body_send_stop_sending() { + let (mut hconn, mut peer_conn) = connect(); + + let stream_id = peer_conn.stream_create(StreamType::BiDi).unwrap(); + // Send only request headers for now. + peer_conn + .stream_send(stream_id, &REQUEST_WITH_BODY[..20]) + .unwrap(); + + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + // Check connection event. There should be 1 Header and no data events. + let mut headers_frames = 0; + while let Some(event) = hconn.next_event() { + match event { + Http3ServerEvent::Headers { + mut request, + headers, + fin, + } => { + check_request_header(&headers); + assert_eq!(fin, false); + headers_frames += 1; + request + .stream_stop_sending(Error::HttpNoError.code()) + .unwrap(); + request + .set_response( + &[ + (String::from(":status"), String::from("200")), + (String::from("content-length"), String::from("3")), + ], + RESPONSE_BODY, + ) + .unwrap(); + } + Http3ServerEvent::Data { .. } => { + panic!("We should not have a Data event"); + } + _ => {} + } + } + let out = hconn.process(None, now()); + + // Send data. + peer_conn + .stream_send(stream_id, &REQUEST_WITH_BODY[20..]) + .unwrap(); + peer_conn.stream_close_send(stream_id).unwrap(); + + let out = peer_conn.process(out.dgram(), now()); + hconn.process(out.dgram(), now()); + + while let Some(event) = hconn.next_event() { + match event { + Http3ServerEvent::Headers { .. } => { + panic!("We should not have a Header event"); + } + Http3ServerEvent::Data { .. } => { + panic!("We should not have a Data event"); + } + _ => {} + } + } + assert_eq!(headers_frames, 1); + } + + #[test] + fn test_server_request_with_body_server_reset() { + let (mut hconn, mut peer_conn) = connect(); + + let request_stream_id = peer_conn.stream_create(StreamType::BiDi).unwrap(); + // Send only request headers for now. + peer_conn + .stream_send(request_stream_id, &REQUEST_WITH_BODY[..20]) + .unwrap(); + + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + + // Check connection event. There should be 1 Header and no data events. + // The server will reset the stream. + let mut headers_frames = 0; + while let Some(event) = hconn.next_event() { + match event { + Http3ServerEvent::Headers { + mut request, + headers, + fin, + } => { + check_request_header(&headers); + assert_eq!(fin, false); + headers_frames += 1; + request + .stream_reset(Error::HttpRequestRejected.code()) + .unwrap(); + } + Http3ServerEvent::Data { .. } => { + panic!("We should not have a Data event"); + } + _ => {} + } + } + let out = hconn.process(None, now()); + + let out = peer_conn.process(out.dgram(), now()); + hconn.process(out.dgram(), now()); + + // Check that STOP_SENDING and REET has been received. + let mut reset = 0; + let mut stop_sending = 0; + while let Some(event) = peer_conn.next_event() { + match event { + ConnectionEvent::RecvStreamReset { stream_id, .. } => { + assert_eq!(request_stream_id, stream_id); + reset += 1; + } + ConnectionEvent::SendStreamStopSending { stream_id, .. } => { + assert_eq!(request_stream_id, stream_id); + stop_sending += 1; + } + _ => {} + } + } + assert_eq!(headers_frames, 1); + assert_eq!(reset, 1); + assert_eq!(stop_sending, 1); + } + + // Server: Test that the connection will be closed if the local control stream + // has been reset. + #[test] + fn test_server_reset_control_stream() { + let (mut hconn, mut peer_conn) = connect(); + peer_conn + .stream_reset_send(CLIENT_SIDE_CONTROL_STREAM_ID, Error::HttpNoError.code()) + .unwrap(); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); + } + + // Server: Test that the connection will be closed if the client side encoder stream + // has been reset. + #[test] + fn test_server_reset_client_side_encoder_stream() { + let (mut hconn, mut peer_conn) = connect(); + peer_conn + .stream_reset_send(CLIENT_SIDE_ENCODER_STREAM_ID, Error::HttpNoError.code()) + .unwrap(); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); + } + + // Server: Test that the connection will be closed if the client side decoder stream + // has been reset. + #[test] + fn test_server_reset_client_side_decoder_stream() { + let (mut hconn, mut peer_conn) = connect(); + peer_conn + .stream_reset_send(CLIENT_SIDE_DECODER_STREAM_ID, Error::HttpNoError.code()) + .unwrap(); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); + } + + // Server: Test that the connection will be closed if the local control stream + // has received a stop_sending. + #[test] + fn test_client_stop_sending_control_stream() { + let (mut hconn, mut peer_conn) = connect(); + + peer_conn + .stream_stop_sending(SERVER_SIDE_CONTROL_STREAM_ID, Error::HttpNoError.code()) + .unwrap(); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); + } + + // Server: Test that the connection will be closed if the server side encoder stream + // has received a stop_sending. + #[test] + fn test_server_stop_sending_encoder_stream() { + let (mut hconn, mut peer_conn) = connect(); + peer_conn + .stream_stop_sending(SERVER_SIDE_ENCODER_STREAM_ID, Error::HttpNoError.code()) + .unwrap(); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); + } + + // Server: Test that the connection will be closed if the server side decoder stream + // has received a stop_sending. + #[test] + fn test_server_stop_sending_decoder_stream() { + let (mut hconn, mut peer_conn) = connect(); + peer_conn + .stream_stop_sending(SERVER_SIDE_DECODER_STREAM_ID, Error::HttpNoError.code()) + .unwrap(); + let out = peer_conn.process(None, now()); + hconn.process(out.dgram(), now()); + assert_closed(&mut hconn, &Error::HttpClosedCriticalStream); + } + + /// Perform a handshake, then another with the token from the first. + /// The second should always resume, but it might not always accept early data. + fn zero_rtt_with_settings(settings: QpackSettings, zero_rtt: &ZeroRttState) { + let (_, mut client) = connect(); + let token = client.events().find_map(|e| { + if let ConnectionEvent::ResumptionToken(token) = e { + Some(token) + } else { + None + } + }); + assert!(token.is_some()); + + let mut server = create_server(settings); + let mut client = default_client(); + client.enable_resumption(now(), token.unwrap()).unwrap(); + + connect_transport(&mut server, &mut client, true); + assert!(client.tls_info().unwrap().resumed()); + assert_eq!(client.zero_rtt_state(), zero_rtt); + } + + #[test] + fn zero_rtt() { + zero_rtt_with_settings(DEFAULT_SETTINGS, &ZeroRttState::AcceptedClient); + } + + /// A larger QPACK decoder table size isn't an impediment to 0-RTT. + #[test] + fn zero_rtt_larger_decoder_table() { + zero_rtt_with_settings( + QpackSettings { + max_table_size_decoder: DEFAULT_SETTINGS.max_table_size_decoder + 1, + ..DEFAULT_SETTINGS + }, + &ZeroRttState::AcceptedClient, + ); + } + + /// A smaller QPACK decoder table size prevents 0-RTT. + #[test] + fn zero_rtt_smaller_decoder_table() { + zero_rtt_with_settings( + QpackSettings { + max_table_size_decoder: DEFAULT_SETTINGS.max_table_size_decoder - 1, + ..DEFAULT_SETTINGS + }, + &ZeroRttState::Rejected, + ); + } + + /// More blocked streams does not prevent 0-RTT. + #[test] + fn zero_rtt_more_blocked_streams() { + zero_rtt_with_settings( + QpackSettings { + max_blocked_streams: DEFAULT_SETTINGS.max_blocked_streams + 1, + ..DEFAULT_SETTINGS + }, + &ZeroRttState::AcceptedClient, + ); + } + + /// A lower number of blocked streams also prevents 0-RTT. + #[test] + fn zero_rtt_fewer_blocked_streams() { + zero_rtt_with_settings( + QpackSettings { + max_blocked_streams: DEFAULT_SETTINGS.max_blocked_streams - 1, + ..DEFAULT_SETTINGS + }, + &ZeroRttState::Rejected, + ); + } + + /// The size of the encoder table is local and therefore doesn't prevent 0-RTT. + #[test] + fn zero_rtt_smaller_encoder_table() { + zero_rtt_with_settings( + QpackSettings { + max_table_size_encoder: DEFAULT_SETTINGS.max_table_size_encoder - 1, + ..DEFAULT_SETTINGS + }, + &ZeroRttState::AcceptedClient, + ); + } +} diff --git a/third_party/rust/neqo-http3/src/server_connection_events.rs b/third_party/rust/neqo-http3/src/server_connection_events.rs new file mode 100644 index 0000000000..6354982510 --- /dev/null +++ b/third_party/rust/neqo-http3/src/server_connection_events.rs @@ -0,0 +1,98 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use crate::connection::Http3State; +use crate::send_message::SendMessageEvents; +use crate::Header; +use crate::RecvMessageEvents; + +use neqo_transport::AppError; + +use std::cell::RefCell; +use std::collections::VecDeque; +use std::rc::Rc; + +#[derive(Debug, PartialOrd, Ord, PartialEq, Eq, Clone)] +pub(crate) enum Http3ServerConnEvent { + /// Headers are ready. + Headers { + stream_id: u64, + headers: Vec<Header>, + fin: bool, + }, + /// Request data is ready. + DataReadable { stream_id: u64 }, + //TODO: This is never used. Do we need it? + // Peer reset the stream. + //Reset { stream_id: u64, error: AppError }, + /// Connection state change. + StateChange(Http3State), +} + +#[derive(Debug, Default, Clone)] +pub(crate) struct Http3ServerConnEvents { + events: Rc<RefCell<VecDeque<Http3ServerConnEvent>>>, +} + +impl RecvMessageEvents for Http3ServerConnEvents { + /// Add a new `HeaderReady` event. + fn header_ready(&self, stream_id: u64, headers: Vec<Header>, _interim: bool, fin: bool) { + self.insert(Http3ServerConnEvent::Headers { + stream_id, + headers, + fin, + }); + } + + /// Add a new `DataReadable` event + fn data_readable(&self, stream_id: u64) { + self.insert(Http3ServerConnEvent::DataReadable { stream_id }); + } + + fn reset(&self, _stream_id: u64, _error: AppError, _local: bool) {} +} + +impl SendMessageEvents for Http3ServerConnEvents { + fn data_writable(&self, _stream_id: u64) { + // Curently not used on the server side. + } + + fn remove_send_side_event(&self, _stream_id: u64) {} + + fn stop_sending(&self, _stream_id: u64, _app_err: AppError) {} +} + +impl Http3ServerConnEvents { + fn insert(&self, event: Http3ServerConnEvent) { + self.events.borrow_mut().push_back(event); + } + + fn remove<F>(&self, f: F) + where + F: Fn(&Http3ServerConnEvent) -> bool, + { + self.events.borrow_mut().retain(|evt| !f(evt)) + } + + pub fn has_events(&self) -> bool { + !self.events.borrow().is_empty() + } + + pub fn next_event(&self) -> Option<Http3ServerConnEvent> { + self.events.borrow_mut().pop_front() + } + + pub fn connection_state_change(&self, state: Http3State) { + self.insert(Http3ServerConnEvent::StateChange(state)); + } + + pub fn remove_events_for_stream_id(&self, stream_id: u64) { + self.remove(|evt| { + matches!(evt, + Http3ServerConnEvent::Headers { stream_id: x, .. } | Http3ServerConnEvent::DataReadable { stream_id: x, .. } if *x == stream_id) + }); + } +} diff --git a/third_party/rust/neqo-http3/src/server_events.rs b/third_party/rust/neqo-http3/src/server_events.rs new file mode 100644 index 0000000000..9005e44a7a --- /dev/null +++ b/third_party/rust/neqo-http3/src/server_events.rs @@ -0,0 +1,148 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(clippy::module_name_repetitions)] + +use crate::connection::Http3State; +use crate::connection_server::Http3ServerHandler; +use crate::{Header, Res}; +use neqo_common::{qdebug, qinfo}; +use neqo_transport::server::ActiveConnectionRef; +use neqo_transport::{AppError, Connection}; + +use std::cell::RefCell; +use std::collections::VecDeque; +use std::rc::Rc; + +#[derive(Debug, Clone)] +pub struct ClientRequestStream { + conn: ActiveConnectionRef, + handler: Rc<RefCell<Http3ServerHandler>>, + stream_id: u64, +} + +impl ::std::fmt::Display for ClientRequestStream { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + let conn: &Connection = &self.conn.borrow(); + write!( + f, + "Http3 server conn={:?} stream_id={}", + conn, self.stream_id + ) + } +} + +impl ClientRequestStream { + pub(crate) fn new( + conn: ActiveConnectionRef, + handler: Rc<RefCell<Http3ServerHandler>>, + stream_id: u64, + ) -> Self { + Self { + conn, + handler, + stream_id, + } + } + + /// Supply a response to a request. + pub fn set_response(&mut self, headers: &[Header], data: &[u8]) -> Res<()> { + qinfo!([self], "Set new response."); + self.handler + .borrow_mut() + .set_response(self.stream_id, headers, data) + } + + /// Request a peer to stop sending a request. + pub fn stream_stop_sending(&mut self, app_error: AppError) -> Res<()> { + qdebug!( + [self], + "stop sending stream_id:{} error:{}.", + self.stream_id, + app_error + ); + self.conn + .borrow_mut() + .stream_stop_sending(self.stream_id, app_error)?; + Ok(()) + } + + /// Reset a stream/request. + pub fn stream_reset(&mut self, app_error: AppError) -> Res<()> { + qdebug!([self], "reset error:{}.", app_error); + self.handler.borrow_mut().stream_reset( + &mut self.conn.borrow_mut(), + self.stream_id, + app_error, + ) + } +} + +#[derive(Debug, Clone)] +pub enum Http3ServerEvent { + /// Headers are ready. + Headers { + request: ClientRequestStream, + headers: Vec<Header>, + fin: bool, + }, + /// Request data is ready. + Data { + request: ClientRequestStream, + data: Vec<u8>, + fin: bool, + }, + /// When individual connection change state. It is only used for tests. + StateChange { + conn: ActiveConnectionRef, + state: Http3State, + }, +} + +#[derive(Debug, Default, Clone)] +pub struct Http3ServerEvents { + events: Rc<RefCell<VecDeque<Http3ServerEvent>>>, +} + +impl Http3ServerEvents { + fn insert(&self, event: Http3ServerEvent) { + self.events.borrow_mut().push_back(event); + } + + /// Take all events + pub fn events(&self) -> impl Iterator<Item = Http3ServerEvent> { + self.events.replace(VecDeque::new()).into_iter() + } + + /// Whether there is request pending. + pub fn has_events(&self) -> bool { + !self.events.borrow().is_empty() + } + + /// Take the next event if present. + pub fn next_event(&self) -> Option<Http3ServerEvent> { + self.events.borrow_mut().pop_front() + } + + /// Insert a `Headers` event. + pub(crate) fn headers(&self, request: ClientRequestStream, headers: Vec<Header>, fin: bool) { + self.insert(Http3ServerEvent::Headers { + request, + headers, + fin, + }); + } + + /// Insert a `StateChange` event. + pub(crate) fn connection_state_change(&self, conn: ActiveConnectionRef, state: Http3State) { + self.insert(Http3ServerEvent::StateChange { conn, state }); + } + + /// Insert a `Data` event. + pub(crate) fn data(&self, request: ClientRequestStream, data: Vec<u8>, fin: bool) { + self.insert(Http3ServerEvent::Data { request, data, fin }); + } +} diff --git a/third_party/rust/neqo-http3/src/settings.rs b/third_party/rust/neqo-http3/src/settings.rs new file mode 100644 index 0000000000..9eee0fe4b4 --- /dev/null +++ b/third_party/rust/neqo-http3/src/settings.rs @@ -0,0 +1,185 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(clippy::module_name_repetitions)] + +use crate::{Error, Res}; +use neqo_common::{Decoder, Encoder}; +use neqo_crypto::{ZeroRttCheckResult, ZeroRttChecker}; +use neqo_qpack::QpackSettings; +use std::ops::Deref; + +type SettingsType = u64; + +/// Increment this version number if a new setting is added and that might +/// cause 0-RTT to be accepted where shouldn't be. +const SETTINGS_ZERO_RTT_VERSION: u64 = 1; + +const SETTINGS_MAX_HEADER_LIST_SIZE: SettingsType = 0x6; +const SETTINGS_QPACK_MAX_TABLE_CAPACITY: SettingsType = 0x1; +const SETTINGS_QPACK_BLOCKED_STREAMS: SettingsType = 0x7; + +pub const H3_RESERVED_SETTINGS: &[SettingsType] = &[0x2, 0x3, 0x4, 0x5]; + +#[derive(Clone, PartialEq, Debug, Copy)] +pub enum HSettingType { + MaxHeaderListSize, + MaxTableCapacity, + BlockedStreams, +} + +fn hsetting_default(setting_type: HSettingType) -> u64 { + match setting_type { + HSettingType::MaxHeaderListSize => 1 << 62, + HSettingType::MaxTableCapacity | HSettingType::BlockedStreams => 0, + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct HSetting { + pub setting_type: HSettingType, + pub value: u64, +} + +impl HSetting { + pub fn new(setting_type: HSettingType, value: u64) -> Self { + Self { + setting_type, + value, + } + } +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct HSettings { + settings: Vec<HSetting>, +} + +impl HSettings { + pub fn new(settings: &[HSetting]) -> Self { + Self { + settings: settings.to_vec(), + } + } + + pub fn get(&self, setting: HSettingType) -> u64 { + match self.settings.iter().find(|s| s.setting_type == setting) { + Some(v) => v.value, + None => hsetting_default(setting), + } + } + + pub fn encode_frame_contents(&self, enc: &mut Encoder) { + enc.encode_vvec_with(|enc_inner| { + for iter in &self.settings { + match iter.setting_type { + HSettingType::MaxHeaderListSize => { + enc_inner.encode_varint(SETTINGS_MAX_HEADER_LIST_SIZE as u64); + enc_inner.encode_varint(iter.value); + } + HSettingType::MaxTableCapacity => { + enc_inner.encode_varint(SETTINGS_QPACK_MAX_TABLE_CAPACITY as u64); + enc_inner.encode_varint(iter.value); + } + HSettingType::BlockedStreams => { + enc_inner.encode_varint(SETTINGS_QPACK_BLOCKED_STREAMS as u64); + enc_inner.encode_varint(iter.value); + } + } + } + }); + } + + pub fn decode_frame_contents(&mut self, dec: &mut Decoder) -> Res<()> { + while dec.remaining() > 0 { + let t = dec.decode_varint(); + let v = dec.decode_varint(); + + if let Some(settings_type) = t { + if H3_RESERVED_SETTINGS.contains(&settings_type) { + return Err(Error::HttpSettings); + } + } + match (t, v) { + (Some(SETTINGS_MAX_HEADER_LIST_SIZE), Some(value)) => self + .settings + .push(HSetting::new(HSettingType::MaxHeaderListSize, value)), + (Some(SETTINGS_QPACK_MAX_TABLE_CAPACITY), Some(value)) => self + .settings + .push(HSetting::new(HSettingType::MaxTableCapacity, value)), + (Some(SETTINGS_QPACK_BLOCKED_STREAMS), Some(value)) => self + .settings + .push(HSetting::new(HSettingType::BlockedStreams, value)), + // other supported settings here + (Some(_), Some(_)) => {} // ignore unknown setting, it is fine. + _ => return Err(Error::NotEnoughData), + }; + } + Ok(()) + } +} + +impl Deref for HSettings { + type Target = [HSetting]; + fn deref(&self) -> &Self::Target { + &self.settings + } +} + +#[derive(Debug)] +pub struct HttpZeroRttChecker { + settings: QpackSettings, +} + +impl HttpZeroRttChecker { + /// Right now we only have QPACK settings, so that is all this takes. + pub fn new(settings: QpackSettings) -> Self { + Self { settings } + } + + /// Save the settings that matter for 0-RTT. + pub fn save(settings: QpackSettings) -> Vec<u8> { + let mut enc = Encoder::new(); + enc.encode_varint(SETTINGS_ZERO_RTT_VERSION) + .encode_varint(SETTINGS_QPACK_MAX_TABLE_CAPACITY) + .encode_varint(settings.max_table_size_decoder) + .encode_varint(SETTINGS_QPACK_BLOCKED_STREAMS) + .encode_varint(settings.max_blocked_streams); + enc.into() + } +} + +impl ZeroRttChecker for HttpZeroRttChecker { + fn check(&self, token: &[u8]) -> ZeroRttCheckResult { + let mut dec = Decoder::from(token); + + // Read and check the version. + if let Some(version) = dec.decode_varint() { + if version != SETTINGS_ZERO_RTT_VERSION { + return ZeroRttCheckResult::Reject; + } + } else { + return ZeroRttCheckResult::Fail; + } + + // Now treat the rest as a settings frame. + let mut settings = HSettings::new(&[]); + if settings.decode_frame_contents(&mut dec).is_err() { + return ZeroRttCheckResult::Fail; + } + if settings.iter().all(|setting| match setting.setting_type { + HSettingType::BlockedStreams => { + u64::from(self.settings.max_blocked_streams) >= setting.value + } + HSettingType::MaxTableCapacity => self.settings.max_table_size_decoder >= setting.value, + HSettingType::MaxHeaderListSize => true, + }) { + ZeroRttCheckResult::Accept + } else { + ZeroRttCheckResult::Reject + } + } +} diff --git a/third_party/rust/neqo-http3/src/stream_type_reader.rs b/third_party/rust/neqo-http3/src/stream_type_reader.rs new file mode 100644 index 0000000000..18d57bac9c --- /dev/null +++ b/third_party/rust/neqo-http3/src/stream_type_reader.rs @@ -0,0 +1,61 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(clippy::module_name_repetitions)] + +use neqo_common::{qdebug, Decoder, IncrementalDecoderUint}; +use neqo_transport::Connection; + +#[derive(Debug)] +pub(crate) struct NewStreamTypeReader { + reader: IncrementalDecoderUint, + fin: bool, +} + +impl NewStreamTypeReader { + pub fn new() -> Self { + Self { + reader: IncrementalDecoderUint::default(), + fin: false, + } + } + pub fn get_type(&mut self, conn: &mut Connection, stream_id: u64) -> Option<u64> { + // On any error we will only close this stream! + loop { + let to_read = self.reader.min_remaining(); + let mut buf = vec![0; to_read]; + match conn.stream_recv(stream_id, &mut buf[..]) { + Ok((_, true)) => { + self.fin = true; + return None; + } + Ok((0, false)) => { + return None; + } + Ok((amount, false)) => { + let res = self.reader.consume(&mut Decoder::from(&buf[..amount])); + if res.is_some() { + return res; + } + } + Err(e) => { + qdebug!( + [conn], + "Error reading stream type for stream {}: {:?}", + stream_id, + e + ); + self.fin = true; + return None; + } + } + } + } + + pub fn fin(&self) -> bool { + self.fin + } +} diff --git a/third_party/rust/neqo-http3/tests/httpconn.rs b/third_party/rust/neqo-http3/tests/httpconn.rs new file mode 100644 index 0000000000..f53c8a93ba --- /dev/null +++ b/third_party/rust/neqo-http3/tests/httpconn.rs @@ -0,0 +1,132 @@ +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(unused_assignments)] + +use neqo_common::{event::Provider, Datagram}; +use neqo_crypto::AuthenticationStatus; +use neqo_http3::{Http3Client, Http3ClientEvent, Http3Server, Http3ServerEvent, Http3State}; +use test_fixture::*; + +const RESPONSE_DATA: &[u8] = &[0x61, 0x62, 0x63]; + +fn process_server_events(server: &mut Http3Server) { + let mut request_found = false; + while let Some(event) = server.next_event() { + if let Http3ServerEvent::Headers { + mut request, + headers, + fin, + } = event + { + assert_eq!( + headers, + vec![ + (String::from(":method"), String::from("GET")), + (String::from(":scheme"), String::from("https")), + (String::from(":authority"), String::from("something.com")), + (String::from(":path"), String::from("/")) + ] + ); + assert_eq!(fin, true); + request + .set_response( + &[ + (String::from(":status"), String::from("200")), + (String::from("content-length"), String::from("3")), + ], + RESPONSE_DATA, + ) + .unwrap(); + request_found = true; + } + } + assert_eq!(request_found, true); +} + +fn process_client_events(conn: &mut Http3Client) { + let mut response_header_found = false; + let mut response_data_found = false; + while let Some(event) = conn.next_event() { + match event { + Http3ClientEvent::HeaderReady { headers, fin, .. } => { + assert_eq!( + headers, + vec![ + (String::from(":status"), String::from("200")), + (String::from("content-length"), String::from("3")), + ] + ); + assert_eq!(fin, false); + response_header_found = true; + } + Http3ClientEvent::DataReadable { stream_id } => { + let mut buf = [0u8; 100]; + let (amount, fin) = conn.read_response_data(now(), stream_id, &mut buf).unwrap(); + assert_eq!(fin, true); + assert_eq!(amount, RESPONSE_DATA.len()); + assert_eq!(&buf[..RESPONSE_DATA.len()], RESPONSE_DATA); + response_data_found = true; + } + _ => {} + } + } + assert_eq!(response_header_found, true); + assert_eq!(response_data_found, true) +} + +fn connect() -> (Http3Client, Http3Server, Option<Datagram>) { + let mut hconn_c = default_http3_client(); + let mut hconn_s = default_http3_server(); + + assert_eq!(hconn_c.state(), Http3State::Initializing); + let out = hconn_c.process(None, now()); // Initial + let out = hconn_s.process(out.dgram(), now()); // Initial + Handshake + let out = hconn_c.process(out.dgram(), now()); // ACK + let _ = hconn_s.process(out.dgram(), now()); //consume ACK + let authentication_needed = |e| matches!(e, Http3ClientEvent::AuthenticationNeeded); + assert!(hconn_c.events().any(authentication_needed)); + hconn_c.authenticated(AuthenticationStatus::Ok, now()); + let out = hconn_c.process(None, now()); // Handshake + assert_eq!(hconn_c.state(), Http3State::Connected); + let out = hconn_s.process(out.dgram(), now()); // Handshake + let out = hconn_c.process(out.dgram(), now()); + let out = hconn_s.process(out.dgram(), now()); + // assert_eq!(hconn_s.settings_received, true); + let out = hconn_c.process(out.dgram(), now()); + // assert_eq!(hconn_c.settings_received, true); + + (hconn_c, hconn_s, out.dgram()) +} + +#[test] +fn test_connect() { + let (_hconn_c, _hconn_s, _d) = connect(); +} + +#[test] +fn test_fetch() { + let (mut hconn_c, mut hconn_s, dgram) = connect(); + + eprintln!("-----client"); + let req = hconn_c + .fetch(now(), "GET", "https", "something.com", "/", &[]) + .unwrap(); + assert_eq!(req, 0); + hconn_c.stream_close_send(req).unwrap(); + let out = hconn_c.process(dgram, now()); + eprintln!("-----server"); + let out = hconn_s.process(out.dgram(), now()); + let _ = hconn_c.process(out.dgram(), now()); + process_server_events(&mut hconn_s); + let out = hconn_s.process(None, now()); + + eprintln!("-----client"); + let _ = hconn_c.process(out.dgram(), now()); + let out = hconn_s.process(None, now()); + let _ = hconn_c.process(out.dgram(), now()); + process_client_events(&mut hconn_c); +} |