diff --git a/src/client.rs b/src/client.rs index ca911741..d3c43d28 100644 --- a/src/client.rs +++ b/src/client.rs @@ -143,6 +143,7 @@ use crate::{FlowControl, PingPong, RecvStream, SendStream}; use bytes::{Buf, Bytes}; use http::{uri, HeaderMap, Method, Request, Response, Version}; +use std::collections::BTreeSet; use std::fmt; use std::future::Future; use std::pin::Pin; @@ -343,6 +344,9 @@ pub struct Builder { /// /// When this gets exceeded, we issue GOAWAYs. local_max_error_reset_streams: Option, + + /// Custom settings IDs to be tracked from the remote + allowed_custom_settings: BTreeSet, } #[derive(Debug)] @@ -673,6 +677,7 @@ impl Builder { settings: Default::default(), stream_id: 1.into(), local_max_error_reset_streams: Some(proto::DEFAULT_LOCAL_RESET_COUNT_MAX), + allowed_custom_settings: BTreeSet::new(), } } @@ -879,6 +884,16 @@ impl Builder { self } + /// By default, unknown settings recieved from the remote will be ignored. + /// + /// See [Section 6.5] in the HTTP/2 spec for more details. + /// + /// [Section 6.5]: https://httpwg.org/specs/rfc7540.html#rfc.section.6.5 + pub fn allow_custom_setting(&mut self, id: u16) -> &mut Self { + self.allowed_custom_settings.insert(id); + self + } + /// Sets the initial maximum of locally initiated (send) streams. /// /// The initial settings will be overwritten by the remote peer when @@ -1355,6 +1370,7 @@ where remote_reset_stream_max: builder.pending_accept_reset_stream_max, local_error_reset_streams_max: builder.local_max_error_reset_streams, settings: builder.settings.clone(), + allowed_custom_settings: builder.allowed_custom_settings, }, ); let send_request = SendRequest { diff --git a/src/frame/settings.rs b/src/frame/settings.rs index 90fd7b10..76fbe76b 100644 --- a/src/frame/settings.rs +++ b/src/frame/settings.rs @@ -136,8 +136,8 @@ impl Settings { } } - pub fn custom_settings(&self) -> &BTreeMap { - &self.custom + pub fn custom_setting(&self, id: u16) -> Option { + self.custom.get(&id).copied() } pub fn load(head: Head, payload: &[u8]) -> Result { diff --git a/src/proto/connection.rs b/src/proto/connection.rs index b0384c9a..461b92cd 100644 --- a/src/proto/connection.rs +++ b/src/proto/connection.rs @@ -7,6 +7,7 @@ use crate::proto::*; use bytes::Bytes; use futures_core::Stream; +use std::collections::BTreeSet; use std::io; use std::marker::PhantomData; use std::pin::Pin; @@ -83,6 +84,7 @@ pub(crate) struct Config { pub remote_reset_stream_max: usize, pub local_error_reset_streams_max: Option, pub settings: frame::Settings, + pub allowed_custom_settings: BTreeSet, } #[derive(Debug)] @@ -123,6 +125,7 @@ where .max_concurrent_streams() .map(|max| max as usize), local_max_error_reset_streams: config.local_error_reset_streams_max, + allowed_custom_settings: config.allowed_custom_settings.clone(), } } let streams = Streams::new(streams_config(&config)); diff --git a/src/proto/streams/mod.rs b/src/proto/streams/mod.rs index c4a83234..5b87f76e 100644 --- a/src/proto/streams/mod.rs +++ b/src/proto/streams/mod.rs @@ -29,6 +29,7 @@ use crate::frame::{StreamId, StreamIdOverflow}; use crate::proto::*; use bytes::Bytes; +use std::collections::BTreeSet; use std::time::Duration; #[derive(Debug)] @@ -72,4 +73,7 @@ pub struct Config { /// /// When this gets exceeded, we issue GOAWAYs. pub local_max_error_reset_streams: Option, + + /// Custom settings IDs to be tracked from the remote + pub allowed_custom_settings: BTreeSet, } diff --git a/src/proto/streams/send.rs b/src/proto/streams/send.rs index 8d729e8f..81948920 100644 --- a/src/proto/streams/send.rs +++ b/src/proto/streams/send.rs @@ -10,7 +10,7 @@ use bytes::Buf; use tokio::io::AsyncWrite; use std::cmp::Ordering; -use std::collections::BTreeMap; +use std::collections::{BTreeMap, BTreeSet}; use std::io; use std::task::{Context, Poll, Waker}; @@ -42,6 +42,7 @@ pub(super) struct Send { /// Custom settings custom_settings: BTreeMap, + allowed_custom_settings: BTreeSet, } /// A value to detect which public API has called `poll_reset`. @@ -62,6 +63,7 @@ impl Send { is_push_enabled: true, is_extended_connect_protocol_enabled: false, custom_settings: BTreeMap::new(), + allowed_custom_settings: config.allowed_custom_settings.clone(), } } @@ -439,7 +441,11 @@ impl Send { if let Some(val) = settings.is_extended_connect_protocol_enabled() { self.is_extended_connect_protocol_enabled = val; } - self.custom_settings.extend(settings.custom_settings()); + for id in &self.allowed_custom_settings { + if let Some(val) = settings.custom_setting(*id) { + self.custom_settings.insert(*id, val); + } + } // Applies an update to the remote endpoint's initial window size. // @@ -590,6 +596,6 @@ impl Send { } pub(crate) fn custom_setting(&self, id: u16) -> Option { - self.custom_settings.get(&id).map(|v| *v) + self.custom_settings.get(&id).copied() } } diff --git a/src/server.rs b/src/server.rs index e65e54df..938be415 100644 --- a/src/server.rs +++ b/src/server.rs @@ -122,6 +122,7 @@ use crate::{FlowControl, PingPong, RecvStream, SendStream}; use bytes::{Buf, Bytes}; use http::{HeaderMap, Method, Request, Response}; +use std::collections::BTreeSet; use std::future::Future; use std::pin::Pin; use std::task::{Context, Poll}; @@ -258,6 +259,9 @@ pub struct Builder { /// /// When this gets exceeded, we issue GOAWAYs. local_max_error_reset_streams: Option, + + /// Custom settings IDs to be tracked from the remote + allowed_custom_settings: BTreeSet, } /// Send a response back to the client @@ -675,8 +679,8 @@ impl Builder { settings: Settings::default(), initial_target_connection_window_size: None, max_send_buffer_size: proto::DEFAULT_MAX_SEND_BUFFER_SIZE, - local_max_error_reset_streams: Some(proto::DEFAULT_LOCAL_RESET_COUNT_MAX), + allowed_custom_settings: BTreeSet::new(), } } @@ -1058,6 +1062,16 @@ impl Builder { self } + /// By default, unknown settings recieved from the remote will be ignored. + /// + /// See [Section 6.5] in the HTTP/2 spec for more details. + /// + /// [Section 6.5]: https://httpwg.org/specs/rfc7540.html#rfc.section.6.5 + pub fn allow_custom_setting(&mut self, id: u16) -> &mut Self { + self.allowed_custom_settings.insert(id); + self + } + /// Creates a new configured HTTP/2 server backed by `io`. /// /// It is expected that `io` already be in an appropriate state to commence @@ -1420,6 +1434,7 @@ where .builder .local_max_error_reset_streams, settings: self.builder.settings.clone(), + allowed_custom_settings: self.builder.allowed_custom_settings.clone(), }, );