Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,8 @@ impl Builder {
///
/// Small DATA frames consume this budget. The budget is restored when
/// buffered frames are consumed by the application, while sufficiently
/// large frames may also restore budget.
/// large frames may also restore budget. Empty DATA frames are limited
/// separately and do not consume this budget.
///
/// When this budget is exhausted, the connection is closed with
/// `ENHANCE_YOUR_CALM`.
Expand Down
1 change: 1 addition & 0 deletions src/proto/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ pub const DEFAULT_LOCAL_RESET_COUNT_MAX: usize = 1024;
// smaller than this consume more internal bookkeeping than useful data.
pub const DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD: usize = 256;
pub const DEFAULT_DATA_FRAME_BUDGET: usize = DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD * 100;
pub const MAX_RECV_EMPTY_DATA_FRAMES: usize = 100;
// RFC 9113 suggests allowing at minimum 100 streams, it seems reasonable to
// by default allow a portion of that to be remembered as reset for some time.
pub const DEFAULT_RESET_STREAM_MAX: usize = 50;
Expand Down
46 changes: 44 additions & 2 deletions src/proto/streams/counts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ pub(super) struct Counts {

/// connection-level budget for DATA framing overhead.
data_frame_budget: Budget,

/// Number of empty, non-final DATA frames received over the lifetime of
/// the connection.
num_recv_empty_data_frames: usize,
}

impl Counts {
Expand All @@ -87,12 +91,22 @@ impl Counts {
max_local_error_reset_streams: config.local_max_error_reset_streams,
num_local_error_reset_streams: 0,
data_frame_budget: Budget::new(config.data_frame_budget),
num_recv_empty_data_frames: 0,
}
}

/// Records the framing overhead of a DATA frame.
pub fn record_data_frame(&mut self, payload_len: usize) -> Result<(), BudgetExhausted> {
if payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
if payload_len == 0 {
self.num_recv_empty_data_frames = self
.num_recv_empty_data_frames
.checked_add(1)
.ok_or(BudgetExhausted)?;
if self.num_recv_empty_data_frames > MAX_RECV_EMPTY_DATA_FRAMES {
return Err(BudgetExhausted);
}
Ok(())
} else if payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
self.data_frame_budget
.consume(DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD - payload_len)
} else {
Expand All @@ -105,7 +119,7 @@ impl Counts {
/// Releases the framing overhead of a DATA frame that is no longer
/// buffered internally.
pub fn release_data_frame(&mut self, payload_len: usize) {
if payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
if payload_len != 0 && payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
self.data_frame_budget
.replenish(DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD - payload_len);
}
Expand Down Expand Up @@ -399,4 +413,32 @@ mod tests {
counts.release_data_frame(1);
}
}

#[test]
fn empty_data_frames_do_not_consume_data_frame_budget() {
let mut counts = counts();
counts.data_frame_budget = Budget::new(0);

for _ in 0..MAX_RECV_EMPTY_DATA_FRAMES {
counts.record_data_frame(0).unwrap();
}

// Empty frames have their own limit, while a non-empty small frame
// still consumes the independently configured DATA frame budget.
assert!(counts.record_data_frame(0).is_err());
assert!(counts.record_data_frame(1).is_err());
}

#[test]
fn large_data_frames_do_not_replenish_empty_data_frame_limit() {
let mut counts = counts();

for _ in 0..MAX_RECV_EMPTY_DATA_FRAMES {
counts.record_data_frame(0).unwrap();
counts
.record_data_frame(DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD * 2)
.unwrap();
}
assert!(counts.record_data_frame(0).is_err());
}
}
3 changes: 2 additions & 1 deletion src/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1054,7 +1054,8 @@ impl Builder {
///
/// Small DATA frames consume this budget. The budget is restored when
/// buffered frames are consumed by the application, while sufficiently
/// large frames may also restore budget.
/// large frames may also restore budget. Empty DATA frames are limited
/// separately and do not consume this budget.
///
/// When this budget is exhausted, the connection is closed with
/// `ENHANCE_YOUR_CALM`.
Expand Down