Skip to content

Commit 2b19acf

Browse files
authored
Handle client-disabled server push (#486)
1 parent a193237 commit 2b19acf

File tree

6 files changed

+70
-3
lines changed

6 files changed

+70
-3
lines changed

src/codec/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ pub enum UserError {
6363

6464
/// Tries to update local SETTINGS while ACK has not been received.
6565
SendSettingsWhilePending,
66+
67+
/// Tries to send push promise to peer who has disabled server push
68+
PeerDisabledServerPush,
6669
}
6770

6871
// ===== impl RecvError =====
@@ -136,6 +139,7 @@ impl fmt::Display for UserError {
136139
PollResetAfterSendResponse => "poll_reset after send_response is illegal",
137140
SendPingWhilePending => "send_ping before received previous pong",
138141
SendSettingsWhilePending => "sending SETTINGS before received previous ACK",
142+
PeerDisabledServerPush => "sending PUSH_PROMISE to peer who disabled server push",
139143
})
140144
}
141145
}

src/frame/settings.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ impl Settings {
9999
self.max_header_list_size = size;
100100
}
101101

102-
pub fn is_push_enabled(&self) -> bool {
103-
self.enable_push.unwrap_or(1) != 0
102+
pub fn is_push_enabled(&self) -> Option<bool> {
103+
self.enable_push.map(|val| val != 0)
104104
}
105105

106106
pub fn set_enable_push(&mut self, enable: bool) {

src/proto/connection.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ where
8686
.unwrap_or(DEFAULT_INITIAL_WINDOW_SIZE),
8787
initial_max_send_streams: config.initial_max_send_streams,
8888
local_next_stream_id: config.next_stream_id,
89-
local_push_enabled: config.settings.is_push_enabled(),
89+
local_push_enabled: config.settings.is_push_enabled().unwrap_or(true),
9090
local_reset_duration: config.reset_stream_duration,
9191
local_reset_max: config.reset_stream_max,
9292
remote_init_window_sz: DEFAULT_INITIAL_WINDOW_SIZE,

src/proto/streams/send.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub(super) struct Send {
3232

3333
/// Prioritization layer
3434
prioritize: Prioritize,
35+
36+
is_push_enabled: bool,
3537
}
3638

3739
/// A value to detect which public API has called `poll_reset`.
@@ -49,6 +51,7 @@ impl Send {
4951
max_stream_id: StreamId::MAX,
5052
next_stream_id: Ok(config.local_next_stream_id),
5153
prioritize: Prioritize::new(config),
54+
is_push_enabled: true,
5255
}
5356
}
5457

@@ -95,6 +98,10 @@ impl Send {
9598
stream: &mut store::Ptr,
9699
task: &mut Option<Waker>,
97100
) -> Result<(), UserError> {
101+
if !self.is_push_enabled {
102+
return Err(UserError::PeerDisabledServerPush);
103+
}
104+
98105
tracing::trace!(
99106
"send_push_promise; frame={:?}; init_window={:?}",
100107
frame,
@@ -496,6 +503,10 @@ impl Send {
496503
}
497504
}
498505

506+
if let Some(val) = settings.is_push_enabled() {
507+
self.is_push_enabled = val
508+
}
509+
499510
Ok(())
500511
}
501512

tests/h2-support/src/frames.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,11 @@ impl Mock<frame::Settings> {
339339
self.0.set_max_header_list_size(Some(val));
340340
self
341341
}
342+
343+
pub fn disable_push(mut self) -> Self {
344+
self.0.set_enable_push(false);
345+
self
346+
}
342347
}
343348

344349
impl From<Mock<frame::Settings>> for frame::Settings {

tests/h2-tests/tests/server.rs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,53 @@ async fn push_request() {
220220
join(client, srv).await;
221221
}
222222

223+
#[tokio::test]
224+
async fn push_request_disabled() {
225+
h2_support::trace_init!();
226+
let (io, mut client) = mock::new();
227+
228+
let client = async move {
229+
client
230+
.assert_server_handshake_with_settings(frames::settings().disable_push())
231+
.await;
232+
client
233+
.send_frame(
234+
frames::headers(1)
235+
.request("GET", "https://example.com/")
236+
.eos(),
237+
)
238+
.await;
239+
client
240+
.recv_frame(frames::headers(1).response(200).eos())
241+
.await;
242+
};
243+
244+
let srv = async move {
245+
let mut srv = server::handshake(io).await.expect("handshake");
246+
let (req, mut stream) = srv.next().await.unwrap().unwrap();
247+
248+
assert_eq!(req.method(), &http::Method::GET);
249+
250+
// attempt to push - expect failure
251+
let req = http::Request::builder()
252+
.method("GET")
253+
.uri("https://http2.akamai.com/style.css")
254+
.body(())
255+
.unwrap();
256+
stream
257+
.push_request(req)
258+
.expect_err("push_request should error");
259+
260+
// send normal response
261+
let rsp = http::Response::builder().status(200).body(()).unwrap();
262+
stream.send_response(rsp, true).unwrap();
263+
264+
assert!(srv.next().await.is_none());
265+
};
266+
267+
join(client, srv).await;
268+
}
269+
223270
#[tokio::test]
224271
async fn push_request_against_concurrency() {
225272
h2_support::trace_init!();

0 commit comments

Comments
 (0)