diff --git a/CHANGELOG.md b/CHANGELOG.md index 91f571f..06f37be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,25 +14,9 @@ * Limit number of CONTINUATION frames for misbehaving connections. -# 0.4.3 (March 15, 2024) +# 0.3.25 (March 15, 2024) -* Fix flow control limits to not apply until receiving SETTINGS ack. -* Fix not returning an error if IO ended without `close_notify`. -* Improve performance of decoding many headers. - -# 0.4.2 (January 17th, 2024) - -* Limit error resets for misbehaving connections. -* Fix selecting MAX_CONCURRENT_STREAMS value if no value is advertised initially. - -# 0.4.1 (January 8, 2024) - -* Fix assigning connection capacity which could starve streams in some instances. - -# 0.4.0 (November 15, 2023) - -* Update to `http` 1.0. -* Remove deprecated `Server::poll_close()`. +* Improve performance decoding many headers. # 0.3.24 (January 17, 2024) diff --git a/README.md b/README.md index cd2831d..310110b 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,14 @@ # H2 -This project is forked from [h2](https://github.com/hyperium/h2) - A Tokio aware, HTTP/2 client & server implementation for Rust. [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) -[![Crates.io](https://img.shields.io/crates/v/h2.svg)](https://crates.io/crates/h2) -[![Documentation](https://docs.rs/h2/badge.svg)][dox] +[![Crates.io](https://img.shields.io/crates/v/rh2.svg)](https://crates.io/crates/rh2) +[![Documentation](https://docs.rs/rh2/badge.svg)][dox] More information about this crate can be found in the [crate documentation][dox]. -[dox]: https://docs.rs/h2 +[dox]: https://docs.rs/rh2 ## Features @@ -34,19 +32,19 @@ This crate is now used by [hyper](https://github.com/hyperium/hyper), which will ## Usage -To use `h2`, first add this to your `Cargo.toml`: +To use `rh2`, first add this to your `Cargo.toml`: ```toml [dependencies] -h2 = "0.4" +rh2 = "0.4" ``` Next, add this to your crate: ```rust -extern crate h2; +extern crate rh2; -use h2::server::Connection; +use rh2::server::Connection; fn main() { // ... @@ -71,3 +69,7 @@ actively maintained. [solicit]: https://github.com/mlalic/solicit [rust-http2]: https://github.com/stepancheg/rust-http2 [h2spec]: https://github.com/summerwind/h2spec + +## Accolades + +The project is based on a fork of [h2](https://github.com/hyperium/h2). diff --git a/src/client.rs b/src/client.rs index a8286e8..a0cf24b 100644 --- a/src/client.rs +++ b/src/client.rs @@ -138,7 +138,8 @@ use crate::codec::{Codec, SendError, UserError}; use crate::ext::Protocol; use crate::frame::{ - Headers, Pseudo, PseudoOrder, Reason, Settings, SettingsOrder, StreamDependency, StreamId, + Headers, Pseudo, PseudoOrder, PseudoOrders, Reason, Settings, SettingsOrder, StreamDependency, + StreamId }; use crate::proto::{self, Error}; use crate::{FlowControl, PingPong, RecvStream, SendStream}; @@ -347,7 +348,7 @@ pub struct Builder { local_max_error_reset_streams: Option, /// The headers frame pseudo order - headers_pseudo_order: Option<[PseudoOrder; 4]>, + headers_pseudo_order: Option, /// The headers frame priority headers_priority: Option, @@ -677,20 +678,20 @@ impl Builder { } /// Set http2 header pseudo order - pub fn headers_psuedo(&mut self, headers_psuedo: Option<[PseudoOrder; 4]>) -> &mut Self { - self.headers_pseudo_order = headers_psuedo; + pub fn headers_psuedo(&mut self, order: [PseudoOrder; 4]) -> &mut Self { + self.headers_pseudo_order = Some(order.into()); self } /// Set http2 header priority - pub fn headers_priority(&mut self, headers_priority: Option) -> &mut Self { - self.headers_priority = headers_priority; + pub fn headers_priority(&mut self, headers_priority: StreamDependency) -> &mut Self { + self.headers_priority = Some(headers_priority); self } /// Settings frame order - pub fn settings_order(&mut self, order: Option<[SettingsOrder; 2]>) -> &mut Self { - self.settings.set_settings_order(order); + pub fn settings_order(&mut self, order: [SettingsOrder; 8]) -> &mut Self { + self.settings.set_settings_order(Some(order)); self } @@ -1173,6 +1174,18 @@ impl Builder { self } + /// unknown_setting8 + pub fn unknown_setting8(&mut self, enabled: bool) -> &mut Self { + self.settings.set_unknown_setting_8(enabled); + self + } + + /// unknown_setting8 + pub fn unknown_setting9(&mut self, enabled: bool) -> &mut Self { + self.settings.set_unknown_setting_9(enabled); + self + } + /// Sets the first stream ID to something other than 1. #[cfg(feature = "unstable")] pub fn initial_stream_id(&mut self, stream_id: u32) -> &mut Self { @@ -1614,7 +1627,7 @@ impl Peer { request: Request<()>, protocol: Option, end_of_stream: bool, - pseudo_order: Option<[PseudoOrder; 4]>, + pseudo_order: Option, headers_priority: Option, ) -> Result { use http::request::Parts; diff --git a/src/codec/framed_write.rs b/src/codec/framed_write.rs index c88af02..a94647b 100644 --- a/src/codec/framed_write.rs +++ b/src/codec/framed_write.rs @@ -133,7 +133,7 @@ where loop { while !self.encoder.is_empty() { - match self.encoder.next { + let n = match self.encoder.next { Some(Next::Data(ref mut frame)) => { tracing::trace!(queued_data_frame = true); let mut buf = (&mut self.encoder.buf).chain(frame.payload_mut()); @@ -148,6 +148,12 @@ where ))? } }; + if n == 0 { + return Poll::Ready(Err(io::Error::new( + io::ErrorKind::WriteZero, + "failed to write frame to socket", + ))); + } } match self.encoder.unset_frame() { diff --git a/src/frame/headers.rs b/src/frame/headers.rs index cf2ac51..af50c62 100644 --- a/src/frame/headers.rs +++ b/src/frame/headers.rs @@ -74,7 +74,7 @@ pub struct Pseudo { pub status: Option, // order of pseudo headers - pub order: Option<[PseudoOrder; 4]>, + pub order: PseudoOrders, } #[derive(Clone, Copy, Debug, Eq, PartialEq)] @@ -85,6 +85,26 @@ pub enum PseudoOrder { Path, } +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PseudoOrders([PseudoOrder; 4]); + +impl From<[PseudoOrder; 4]> for PseudoOrders { + fn from(src: [PseudoOrder; 4]) -> Self { + PseudoOrders(src) + } +} + +impl Default for PseudoOrders { + fn default() -> Self { + PseudoOrders([ + PseudoOrder::Method, + PseudoOrder::Scheme, + PseudoOrder::Authority, + PseudoOrder::Path, + ]) + } +} + #[derive(Debug)] pub struct Iter { /// Pseudo headers @@ -131,6 +151,13 @@ impl Headers { fields: HeaderMap, stream_dep: Option, ) -> Self { + // If the stream dependency is set, the PRIORITY flag must be set + let flags = if stream_dep.is_some() { + HeadersFlag(END_HEADERS | PRIORITY) + } else { + HeadersFlag::default() + }; + Headers { stream_id, stream_dep, @@ -140,7 +167,7 @@ impl Headers { is_over_size: false, pseudo, }, - flags: HeadersFlag::default(), + flags, } } @@ -580,7 +607,7 @@ impl Pseudo { method: Method, uri: Uri, protocol: Option, - pseudo_order: Option<[PseudoOrder; 4]>, + order: Option, ) -> Self { let parts = uri::Parts::from(uri); @@ -610,7 +637,7 @@ impl Pseudo { path, protocol, status: None, - order: pseudo_order, + order: order.unwrap_or_default(), }; // If the URI includes a scheme component, add it to the pseudo headers @@ -635,7 +662,7 @@ impl Pseudo { path: None, protocol: None, status: Some(status), - order: None, + order: Default::default(), } } @@ -730,28 +757,26 @@ impl Iterator for Iter { use crate::hpack::Header::*; if let Some(ref mut pseudo) = self.pseudo { - if let Some(orders) = pseudo.order.as_ref() { - for pseudo_type in orders { - match pseudo_type { - PseudoOrder::Method => { - if let Some(method) = pseudo.method.take() { - return Some(Method(method)); - } + for pseudo_type in pseudo.order.0.iter() { + match pseudo_type { + PseudoOrder::Method => { + if let Some(method) = pseudo.method.take() { + return Some(Method(method)); } - PseudoOrder::Scheme => { - if let Some(scheme) = pseudo.scheme.take() { - return Some(Scheme(scheme)); - } + } + PseudoOrder::Scheme => { + if let Some(scheme) = pseudo.scheme.take() { + return Some(Scheme(scheme)); } - PseudoOrder::Authority => { - if let Some(authority) = pseudo.authority.take() { - return Some(Authority(authority)); - } + } + PseudoOrder::Authority => { + if let Some(authority) = pseudo.authority.take() { + return Some(Authority(authority)); } - PseudoOrder::Path => { - if let Some(path) = pseudo.path.take() { - return Some(Path(path)); - } + } + PseudoOrder::Path => { + if let Some(path) = pseudo.path.take() { + return Some(Path(path)); } } } @@ -827,9 +852,9 @@ impl HeadersFlag { } impl Default for HeadersFlag { - /// Returns a `HeadersFlag` value with `END_HEADERS` and `PRIORITY` set. + /// Returns a `HeadersFlag` value with `END_HEADERS` set. fn default() -> Self { - HeadersFlag(END_HEADERS | PRIORITY) + HeadersFlag(END_HEADERS) } } @@ -1048,6 +1073,8 @@ fn decoded_header_size(name: usize, value: usize) -> usize { #[cfg(test)] mod test { + use std::iter::FromIterator; + use super::*; use crate::frame; use crate::hpack::{huffman, Encoder}; diff --git a/src/frame/mod.rs b/src/frame/mod.rs index b080541..b455927 100644 --- a/src/frame/mod.rs +++ b/src/frame/mod.rs @@ -53,7 +53,8 @@ pub use self::go_away::GoAway; pub use self::head::{Head, Kind}; pub(crate) use self::headers::PseudoOrder; pub use self::headers::{ - parse_u64, Continuation, Headers, Pseudo, PushPromise, PushPromiseHeaderError, + parse_u64, Continuation, Headers, Pseudo, PseudoOrder, PseudoOrders, PushPromise, + PushPromiseHeaderError, }; pub use self::ping::Ping; pub use self::priority::{Priority, StreamDependency}; diff --git a/src/frame/settings.rs b/src/frame/settings.rs index ff617d9..ec4ba3c 100644 --- a/src/frame/settings.rs +++ b/src/frame/settings.rs @@ -5,8 +5,38 @@ use bytes::{BufMut, BytesMut}; #[derive(Debug, Clone, Copy, Eq, PartialEq)] pub enum SettingsOrder { + HeaderTableSize, + EnablePush, InitialWindowSize, MaxConcurrentStreams, + MaxFrameSize, + MaxHeaderListSize, + UnknownSetting8, + UnknownSetting9, +} + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct SettingsOrders([SettingsOrder; 8]); + +impl From<[SettingsOrder; 8]> for SettingsOrders { + fn from(order: [SettingsOrder; 8]) -> Self { + SettingsOrders(order) + } +} + +impl Default for SettingsOrders { + fn default() -> Self { + SettingsOrders([ + SettingsOrder::HeaderTableSize, + SettingsOrder::EnablePush, + SettingsOrder::InitialWindowSize, + SettingsOrder::MaxConcurrentStreams, + SettingsOrder::MaxFrameSize, + SettingsOrder::MaxHeaderListSize, + SettingsOrder::UnknownSetting8, + SettingsOrder::UnknownSetting9, + ]) + } } #[derive(Clone, Default, Eq, PartialEq)] @@ -20,8 +50,10 @@ pub struct Settings { max_frame_size: Option, max_header_list_size: Option, enable_connect_protocol: Option, + unknown_setting_8: Option, + unknown_setting_9: Option, // Fields for the settings frame order - settings_order: Option<[SettingsOrder; 2]>, + settings_orders: SettingsOrders, } /// An enum that lists all valid settings that can be sent in a SETTINGS @@ -37,6 +69,8 @@ pub enum Setting { MaxFrameSize(u32), MaxHeaderListSize(u32), EnableConnectProtocol(u32), + UnknownSetting8(u32), + UnknownSetting9(u32), } #[derive(Copy, Clone, Eq, PartialEq, Default)] @@ -133,8 +167,16 @@ impl Settings { self.header_table_size = size; } - pub fn set_settings_order(&mut self, order: Option<[SettingsOrder; 2]>) { - self.settings_order = order; + pub fn set_unknown_setting_8(&mut self, enable: bool) { + self.unknown_setting_8 = Some(enable as u32); + } + + pub fn set_unknown_setting_9(&mut self, enable: bool) { + self.unknown_setting_9 = Some(enable as u32); + } + + pub fn set_settings_order(&mut self, order: Option<[SettingsOrder; 8]>) { + self.settings_orders = order.map_or(SettingsOrders::default(), SettingsOrders::from); } pub fn load(head: Head, payload: &[u8]) -> Result { @@ -209,6 +251,22 @@ impl Settings { return Err(Error::InvalidSettingValue); } }, + Some(UnknownSetting8(val)) => match val { + 0 | 1 => { + settings.unknown_setting_8 = Some(val); + } + _ => { + return Err(Error::InvalidSettingValue); + } + }, + Some(UnknownSetting9(val)) => match val { + 0 | 1 => { + settings.unknown_setting_9 = Some(val); + } + _ => { + return Err(Error::InvalidSettingValue); + } + }, None => {} } } @@ -241,47 +299,51 @@ impl Settings { fn for_each(&self, mut f: F) { use self::Setting::*; - if let Some(v) = self.header_table_size { - f(HeaderTableSize(v)); - } - - if let Some(v) = self.enable_push { - f(EnablePush(v)); - } - - if let Some(settings_frame_order) = self.settings_order { - for order in settings_frame_order { - match order { - SettingsOrder::InitialWindowSize => { - if let Some(v) = self.initial_window_size { - f(InitialWindowSize(v)); - } + for order in self.settings_orders.0.iter() { + match order { + SettingsOrder::HeaderTableSize => { + if let Some(v) = self.header_table_size { + f(HeaderTableSize(v)); } - SettingsOrder::MaxConcurrentStreams => { - if let Some(v) = self.max_concurrent_streams { - f(MaxConcurrentStreams(v)); - } + } + SettingsOrder::EnablePush => { + if let Some(v) = self.enable_push { + f(EnablePush(v)); + } + } + SettingsOrder::InitialWindowSize => { + if let Some(v) = self.initial_window_size { + f(InitialWindowSize(v)); + } + } + SettingsOrder::MaxConcurrentStreams => { + if let Some(v) = self.max_concurrent_streams { + f(MaxConcurrentStreams(v)); + } + } + SettingsOrder::UnknownSetting8 => { + if let Some(v) = self.unknown_setting_8 { + f(UnknownSetting8(v)); + } + } + SettingsOrder::UnknownSetting9 => { + if let Some(v) = self.unknown_setting_9 { + f(UnknownSetting9(v)); + } + } + SettingsOrder::MaxFrameSize => { + if let Some(v) = self.max_frame_size { + f(MaxFrameSize(v)); + } + } + SettingsOrder::MaxHeaderListSize => { + if let Some(v) = self.max_header_list_size { + f(MaxHeaderListSize(v)); } } - } - } else { - if let Some(v) = self.initial_window_size { - f(InitialWindowSize(v)); - } - - if let Some(v) = self.max_concurrent_streams { - f(MaxConcurrentStreams(v)); } } - if let Some(v) = self.max_frame_size { - f(MaxFrameSize(v)); - } - - if let Some(v) = self.max_header_list_size { - f(MaxHeaderListSize(v)); - } - if let Some(v) = self.enable_connect_protocol { f(EnableConnectProtocol(v)); } @@ -321,6 +383,12 @@ impl fmt::Debug for Settings { Setting::EnableConnectProtocol(v) => { builder.field("enable_connect_protocol", &v); } + Setting::UnknownSetting8(v) => { + builder.field("unknown_setting8", &v); + } + Setting::UnknownSetting9(v) => { + builder.field("unknown_setting9", &v); + } }); builder.finish() @@ -376,6 +444,8 @@ impl Setting { MaxFrameSize(v) => (5, v), MaxHeaderListSize(v) => (6, v), EnableConnectProtocol(v) => (8, v), + UnknownSetting8(v) => (8, v), + UnknownSetting9(v) => (9, v), }; dst.put_u16(kind); diff --git a/src/proto/connection.rs b/src/proto/connection.rs index 668de49..cc97ca4 100644 --- a/src/proto/connection.rs +++ b/src/proto/connection.rs @@ -6,7 +6,7 @@ use crate::frame::DEFAULT_INITIAL_WINDOW_SIZE; use crate::proto::*; use bytes::Bytes; -use frame::{PseudoOrder, StreamDependency}; +use frame::{PseudoOrders, StreamDependency}; use futures_core::Stream; use std::io; use std::marker::PhantomData; @@ -84,7 +84,7 @@ pub(crate) struct Config { pub remote_reset_stream_max: usize, pub local_error_reset_streams_max: Option, pub settings: frame::Settings, - pub headers_pseudo_order: Option<[PseudoOrder; 4]>, + pub headers_pseudo_order: Option, pub headers_priority: Option, } diff --git a/src/proto/streams/streams.rs b/src/proto/streams/streams.rs index e5d9fbb..e632b77 100644 --- a/src/proto/streams/streams.rs +++ b/src/proto/streams/streams.rs @@ -1,4 +1,4 @@ -use super::frame::{PseudoOrder, StreamDependency}; +use super::frame::{PseudoOrders, StreamDependency}; use super::recv::RecvHeaderBlockError; use super::store::{self, Entry, Resolve, Store}; use super::{Buffer, Config, Counts, Prioritized, Recv, Send, Stream, StreamId}; @@ -36,7 +36,7 @@ where send_buffer: Arc>, /// Headers frame pseudo order - headers_pseudo_order: Option<[PseudoOrder; 4]>, + headers_pseudo_order: Option, /// Headers frame priority headers_priority: Option, @@ -118,7 +118,7 @@ where pub fn new( config: Config, headers_priority: Option, - headers_pseudo_order: Option<[PseudoOrder; 4]>, + headers_pseudo_order: Option, ) -> Self { let peer = P::r#dyn(); @@ -1062,8 +1062,8 @@ where Streams { inner: self.inner.clone(), send_buffer: self.send_buffer.clone(), - headers_priority: self.headers_priority.clone(), - headers_pseudo_order: self.headers_pseudo_order.clone(), + headers_priority: self.headers_priority, + headers_pseudo_order: self.headers_pseudo_order, _p: ::std::marker::PhantomData, } } diff --git a/tests/h2-tests/tests/prioritization.rs b/tests/h2-tests/tests/prioritization.rs index dd4ed9f..11d2c2c 100644 --- a/tests/h2-tests/tests/prioritization.rs +++ b/tests/h2-tests/tests/prioritization.rs @@ -1,3 +1,4 @@ +use futures::future::{join, select}; use futures::{pin_mut, FutureExt, StreamExt}; use h2_support::prelude::*; diff --git a/tests/h2-tests/tests/server.rs b/tests/h2-tests/tests/server.rs index 91c8d40..9191640 100644 --- a/tests/h2-tests/tests/server.rs +++ b/tests/h2-tests/tests/server.rs @@ -1,5 +1,6 @@ #![deny(warnings)] +use futures::future::join; use futures::StreamExt; use h2_support::prelude::*; use tokio::io::AsyncWriteExt; diff --git a/tests/h2-tests/tests/stream_states.rs b/tests/h2-tests/tests/stream_states.rs index facd367..d511f92 100644 --- a/tests/h2-tests/tests/stream_states.rs +++ b/tests/h2-tests/tests/stream_states.rs @@ -1,6 +1,6 @@ #![deny(warnings)] -use futures::future::lazy; +use futures::future::{join, join3, lazy, try_join}; use futures::{FutureExt, StreamExt, TryStreamExt}; use h2_support::prelude::*; use h2_support::util::yield_once;