@@ -69,6 +69,10 @@ pub(super) struct Counts {
6969
7070 /// connection-level budget for DATA framing overhead.
7171 data_frame_budget : Budget ,
72+
73+ /// Number of empty, non-final DATA frames received over the lifetime of
74+ /// the connection.
75+ num_recv_empty_data_frames : usize ,
7276}
7377
7478impl Counts {
@@ -87,12 +91,22 @@ impl Counts {
8791 max_local_error_reset_streams : config. local_max_error_reset_streams ,
8892 num_local_error_reset_streams : 0 ,
8993 data_frame_budget : Budget :: new ( config. data_frame_budget ) ,
94+ num_recv_empty_data_frames : 0 ,
9095 }
9196 }
9297
9398 /// Records the framing overhead of a DATA frame.
9499 pub fn record_data_frame ( & mut self , payload_len : usize ) -> Result < ( ) , BudgetExhausted > {
95- if payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
100+ if payload_len == 0 {
101+ self . num_recv_empty_data_frames = self
102+ . num_recv_empty_data_frames
103+ . checked_add ( 1 )
104+ . ok_or ( BudgetExhausted ) ?;
105+ if self . num_recv_empty_data_frames > MAX_RECV_EMPTY_DATA_FRAMES {
106+ return Err ( BudgetExhausted ) ;
107+ }
108+ Ok ( ( ) )
109+ } else if payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
96110 self . data_frame_budget
97111 . consume ( DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD - payload_len)
98112 } else {
@@ -105,7 +119,7 @@ impl Counts {
105119 /// Releases the framing overhead of a DATA frame that is no longer
106120 /// buffered internally.
107121 pub fn release_data_frame ( & mut self , payload_len : usize ) {
108- if payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
122+ if payload_len != 0 && payload_len < DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD {
109123 self . data_frame_budget
110124 . replenish ( DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD - payload_len) ;
111125 }
@@ -399,4 +413,32 @@ mod tests {
399413 counts. release_data_frame ( 1 ) ;
400414 }
401415 }
416+
417+ #[ test]
418+ fn empty_data_frames_do_not_consume_data_frame_budget ( ) {
419+ let mut counts = counts ( ) ;
420+ counts. data_frame_budget = Budget :: new ( 0 ) ;
421+
422+ for _ in 0 ..MAX_RECV_EMPTY_DATA_FRAMES {
423+ counts. record_data_frame ( 0 ) . unwrap ( ) ;
424+ }
425+
426+ // Empty frames have their own limit, while a non-empty small frame
427+ // still consumes the independently configured DATA frame budget.
428+ assert ! ( counts. record_data_frame( 0 ) . is_err( ) ) ;
429+ assert ! ( counts. record_data_frame( 1 ) . is_err( ) ) ;
430+ }
431+
432+ #[ test]
433+ fn large_data_frames_do_not_replenish_empty_data_frame_limit ( ) {
434+ let mut counts = counts ( ) ;
435+
436+ for _ in 0 ..MAX_RECV_EMPTY_DATA_FRAMES {
437+ counts. record_data_frame ( 0 ) . unwrap ( ) ;
438+ counts
439+ . record_data_frame ( DEFAULT_DATA_FRAME_OVERHEAD_THRESHOLD * 2 )
440+ . unwrap ( ) ;
441+ }
442+ assert ! ( counts. record_data_frame( 0 ) . is_err( ) ) ;
443+ }
402444}
0 commit comments