Skip to content

Commit d2f09fb

Browse files
magurotunaseanmonstar
authored andcommitted
fix: set MAX_CONCURRENT_STREAMS to usize::MAX if no value is advertised initially
1 parent 66a1ed8 commit d2f09fb

File tree

4 files changed

+188
-7
lines changed

4 files changed

+188
-7
lines changed

src/proto/settings.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ pub(crate) struct Settings {
1212
/// the socket first then the settings applied **before** receiving any
1313
/// further frames.
1414
remote: Option<frame::Settings>,
15+
/// Whether the connection has received the initial SETTINGS frame from the
16+
/// remote peer.
17+
has_received_remote_initial_settings: bool,
1518
}
1619

1720
#[derive(Debug)]
@@ -32,6 +35,7 @@ impl Settings {
3235
// the handshake process.
3336
local: Local::WaitingAck(local),
3437
remote: None,
38+
has_received_remote_initial_settings: false,
3539
}
3640
}
3741

@@ -96,6 +100,15 @@ impl Settings {
96100
}
97101
}
98102

103+
/// Sets `true` to `self.has_received_remote_initial_settings`.
104+
/// Returns `true` if this method is called for the first time.
105+
/// (i.e. it is the initial SETTINGS frame from the remote peer)
106+
fn mark_remote_initial_settings_as_received(&mut self) -> bool {
107+
let has_received = self.has_received_remote_initial_settings;
108+
self.has_received_remote_initial_settings = true;
109+
!has_received
110+
}
111+
99112
pub(crate) fn poll_send<T, B, C, P>(
100113
&mut self,
101114
cx: &mut Context,
@@ -108,7 +121,7 @@ impl Settings {
108121
C: Buf,
109122
P: Peer,
110123
{
111-
if let Some(settings) = &self.remote {
124+
if let Some(settings) = self.remote.clone() {
112125
if !dst.poll_ready(cx)?.is_ready() {
113126
return Poll::Pending;
114127
}
@@ -121,7 +134,8 @@ impl Settings {
121134

122135
tracing::trace!("ACK sent; applying settings");
123136

124-
streams.apply_remote_settings(settings)?;
137+
let is_initial = self.mark_remote_initial_settings_as_received();
138+
streams.apply_remote_settings(&settings, is_initial)?;
125139

126140
if let Some(val) = settings.header_table_size() {
127141
dst.set_send_header_table_size(val as usize);

src/proto/streams/counts.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,11 @@ impl Counts {
147147
self.num_remote_reset_streams -= 1;
148148
}
149149

150-
pub fn apply_remote_settings(&mut self, settings: &frame::Settings) {
151-
if let Some(val) = settings.max_concurrent_streams() {
152-
self.max_send_streams = val as usize;
150+
pub fn apply_remote_settings(&mut self, settings: &frame::Settings, is_initial: bool) {
151+
match settings.max_concurrent_streams() {
152+
Some(val) => self.max_send_streams = val as usize,
153+
None if is_initial => self.max_send_streams = usize::MAX,
154+
None => {}
153155
}
154156
}
155157

src/proto/streams/streams.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -186,14 +186,18 @@ where
186186
me.poll_complete(&self.send_buffer, cx, dst)
187187
}
188188

189-
pub fn apply_remote_settings(&mut self, frame: &frame::Settings) -> Result<(), Error> {
189+
pub fn apply_remote_settings(
190+
&mut self,
191+
frame: &frame::Settings,
192+
is_initial: bool,
193+
) -> Result<(), Error> {
190194
let mut me = self.inner.lock().unwrap();
191195
let me = &mut *me;
192196

193197
let mut send_buffer = self.send_buffer.inner.lock().unwrap();
194198
let send_buffer = &mut *send_buffer;
195199

196-
me.counts.apply_remote_settings(frame);
200+
me.counts.apply_remote_settings(frame, is_initial);
197201

198202
me.actions.send.apply_remote_settings(
199203
frame,

tests/h2-tests/tests/client_request.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1661,6 +1661,167 @@ async fn client_builder_header_table_size() {
16611661
join(srv, h2).await;
16621662
}
16631663

1664+
#[tokio::test]
1665+
async fn configured_max_concurrent_send_streams_and_update_it_based_on_empty_settings_frame() {
1666+
h2_support::trace_init!();
1667+
let (io, mut srv) = mock::new();
1668+
1669+
let srv = async move {
1670+
// Send empty SETTINGS frame (no MAX_CONCURRENT_STREAMS is provided)
1671+
srv.send_frame(frames::settings()).await;
1672+
};
1673+
1674+
let h2 = async move {
1675+
let (_client, h2) = client::Builder::new()
1676+
// Configure the initial value to 2024
1677+
.initial_max_send_streams(2024)
1678+
.handshake::<_, bytes::Bytes>(io)
1679+
.await
1680+
.unwrap();
1681+
let mut h2 = std::pin::pin!(h2);
1682+
// It should be pre-configured value before it receives the initial
1683+
// SETTINGS frame from the server
1684+
assert_eq!(h2.max_concurrent_send_streams(), 2024);
1685+
h2.as_mut().await.unwrap();
1686+
// If the server's initial SETTINGS frame does not include
1687+
// MAX_CONCURRENT_STREAMS, this should be updated to usize::MAX.
1688+
assert_eq!(h2.max_concurrent_send_streams(), usize::MAX);
1689+
};
1690+
1691+
join(srv, h2).await;
1692+
}
1693+
1694+
#[tokio::test]
1695+
async fn configured_max_concurrent_send_streams_and_update_it_based_on_non_empty_settings_frame() {
1696+
h2_support::trace_init!();
1697+
let (io, mut srv) = mock::new();
1698+
1699+
let srv = async move {
1700+
// Send SETTINGS frame with MAX_CONCURRENT_STREAMS set to 42
1701+
srv.send_frame(frames::settings().max_concurrent_streams(42))
1702+
.await;
1703+
};
1704+
1705+
let h2 = async move {
1706+
let (_client, h2) = client::Builder::new()
1707+
// Configure the initial value to 2024
1708+
.initial_max_send_streams(2024)
1709+
.handshake::<_, bytes::Bytes>(io)
1710+
.await
1711+
.unwrap();
1712+
let mut h2 = std::pin::pin!(h2);
1713+
// It should be pre-configured value before it receives the initial
1714+
// SETTINGS frame from the server
1715+
assert_eq!(h2.max_concurrent_send_streams(), 2024);
1716+
h2.as_mut().await.unwrap();
1717+
// Now the client has received the initial SETTINGS frame from the
1718+
// server, which should update the value accordingly
1719+
assert_eq!(h2.max_concurrent_send_streams(), 42);
1720+
};
1721+
1722+
join(srv, h2).await;
1723+
}
1724+
1725+
#[tokio::test]
1726+
async fn receive_settings_frame_twice_with_second_one_empty() {
1727+
h2_support::trace_init!();
1728+
let (io, mut srv) = mock::new();
1729+
1730+
let srv = async move {
1731+
// Send the initial SETTINGS frame with MAX_CONCURRENT_STREAMS set to 42
1732+
srv.send_frame(frames::settings().max_concurrent_streams(42))
1733+
.await;
1734+
1735+
// Handle the client's connection preface
1736+
srv.read_preface().await.unwrap();
1737+
match srv.next().await {
1738+
Some(frame) => match frame.unwrap() {
1739+
h2::frame::Frame::Settings(_) => {
1740+
let ack = frame::Settings::ack();
1741+
srv.send(ack.into()).await.unwrap();
1742+
}
1743+
frame => {
1744+
panic!("unexpected frame: {:?}", frame);
1745+
}
1746+
},
1747+
None => {
1748+
panic!("unexpected EOF");
1749+
}
1750+
}
1751+
1752+
// Should receive the ack for the server's initial SETTINGS frame
1753+
let frame = assert_settings!(srv.next().await.unwrap().unwrap());
1754+
assert!(frame.is_ack());
1755+
1756+
// Send another SETTINGS frame with no MAX_CONCURRENT_STREAMS
1757+
// This should not update the max_concurrent_send_streams value that
1758+
// the client manages.
1759+
srv.send_frame(frames::settings()).await;
1760+
};
1761+
1762+
let h2 = async move {
1763+
let (_client, h2) = client::handshake(io).await.unwrap();
1764+
let mut h2 = std::pin::pin!(h2);
1765+
assert_eq!(h2.max_concurrent_send_streams(), usize::MAX);
1766+
h2.as_mut().await.unwrap();
1767+
// Even though the second SETTINGS frame contained no value for
1768+
// MAX_CONCURRENT_STREAMS, update to usize::MAX should not happen
1769+
assert_eq!(h2.max_concurrent_send_streams(), 42);
1770+
};
1771+
1772+
join(srv, h2).await;
1773+
}
1774+
1775+
#[tokio::test]
1776+
async fn receive_settings_frame_twice_with_second_one_non_empty() {
1777+
h2_support::trace_init!();
1778+
let (io, mut srv) = mock::new();
1779+
1780+
let srv = async move {
1781+
// Send the initial SETTINGS frame with MAX_CONCURRENT_STREAMS set to 42
1782+
srv.send_frame(frames::settings().max_concurrent_streams(42))
1783+
.await;
1784+
1785+
// Handle the client's connection preface
1786+
srv.read_preface().await.unwrap();
1787+
match srv.next().await {
1788+
Some(frame) => match frame.unwrap() {
1789+
h2::frame::Frame::Settings(_) => {
1790+
let ack = frame::Settings::ack();
1791+
srv.send(ack.into()).await.unwrap();
1792+
}
1793+
frame => {
1794+
panic!("unexpected frame: {:?}", frame);
1795+
}
1796+
},
1797+
None => {
1798+
panic!("unexpected EOF");
1799+
}
1800+
}
1801+
1802+
// Should receive the ack for the server's initial SETTINGS frame
1803+
let frame = assert_settings!(srv.next().await.unwrap().unwrap());
1804+
assert!(frame.is_ack());
1805+
1806+
// Send another SETTINGS frame with no MAX_CONCURRENT_STREAMS
1807+
// This should not update the max_concurrent_send_streams value that
1808+
// the client manages.
1809+
srv.send_frame(frames::settings().max_concurrent_streams(2024))
1810+
.await;
1811+
};
1812+
1813+
let h2 = async move {
1814+
let (_client, h2) = client::handshake(io).await.unwrap();
1815+
let mut h2 = std::pin::pin!(h2);
1816+
assert_eq!(h2.max_concurrent_send_streams(), usize::MAX);
1817+
h2.as_mut().await.unwrap();
1818+
// The most-recently advertised value should be used
1819+
assert_eq!(h2.max_concurrent_send_streams(), 2024);
1820+
};
1821+
1822+
join(srv, h2).await;
1823+
}
1824+
16641825
const SETTINGS: &[u8] = &[0, 0, 0, 4, 0, 0, 0, 0, 0];
16651826
const SETTINGS_ACK: &[u8] = &[0, 0, 0, 4, 1, 0, 0, 0, 0];
16661827

0 commit comments

Comments
 (0)