1 use crate::frame::Reason;
2 use crate::proto::{WindowSize, MAX_WINDOW_SIZE};
3 
4 use std::fmt;
5 
6 // We don't want to send WINDOW_UPDATE frames for tiny changes, but instead
7 // aggregate them when the changes are significant. Many implementations do
8 // this by keeping a "ratio" of the update version the allowed window size.
9 //
10 // While some may wish to represent this ratio as percentage, using a f32,
11 // we skip having to deal with float math and stick to integers. To do so,
12 // the "ratio" is represented by 2 i32s, split into the numerator and
13 // denominator. For example, a 50% ratio is simply represented as 1/2.
14 //
15 // An example applying this ratio: If a stream has an allowed window size of
16 // 100 bytes, WINDOW_UPDATE frames are scheduled when the unclaimed change
17 // becomes greater than 1/2, or 50 bytes.
18 const UNCLAIMED_NUMERATOR: i32 = 1;
19 const UNCLAIMED_DENOMINATOR: i32 = 2;
20 
21 #[test]
sanity_unclaimed_ratio()22 fn sanity_unclaimed_ratio() {
23     assert!(UNCLAIMED_NUMERATOR < UNCLAIMED_DENOMINATOR);
24     assert!(UNCLAIMED_NUMERATOR >= 0);
25     assert!(UNCLAIMED_DENOMINATOR > 0);
26 }
27 
28 #[derive(Copy, Clone, Debug)]
29 pub struct FlowControl {
30     /// Window the peer knows about.
31     ///
32     /// This can go negative if a SETTINGS_INITIAL_WINDOW_SIZE is received.
33     ///
34     /// For example, say the peer sends a request and uses 32kb of the window.
35     /// We send a SETTINGS_INITIAL_WINDOW_SIZE of 16kb. The peer has to adjust
36     /// its understanding of the capacity of the window, and that would be:
37     ///
38     /// ```notrust
39     /// default (64kb) - used (32kb) - settings_diff (64kb - 16kb): -16kb
40     /// ```
41     window_size: Window,
42 
43     /// Window that we know about.
44     ///
45     /// This can go negative if a user declares a smaller target window than
46     /// the peer knows about.
47     available: Window,
48 }
49 
50 impl FlowControl {
new() -> FlowControl51     pub fn new() -> FlowControl {
52         FlowControl {
53             window_size: Window(0),
54             available: Window(0),
55         }
56     }
57 
58     /// Returns the window size as known by the peer
window_size(&self) -> WindowSize59     pub fn window_size(&self) -> WindowSize {
60         self.window_size.as_size()
61     }
62 
63     /// Returns the window size available to the consumer
available(&self) -> Window64     pub fn available(&self) -> Window {
65         self.available
66     }
67 
68     /// Returns true if there is unavailable window capacity
has_unavailable(&self) -> bool69     pub fn has_unavailable(&self) -> bool {
70         if self.window_size < 0 {
71             return false;
72         }
73 
74         self.window_size > self.available
75     }
76 
claim_capacity(&mut self, capacity: WindowSize)77     pub fn claim_capacity(&mut self, capacity: WindowSize) {
78         self.available -= capacity;
79     }
80 
assign_capacity(&mut self, capacity: WindowSize)81     pub fn assign_capacity(&mut self, capacity: WindowSize) {
82         self.available += capacity;
83     }
84 
85     /// If a WINDOW_UPDATE frame should be sent, returns a positive number
86     /// representing the increment to be used.
87     ///
88     /// If there is no available bytes to be reclaimed, or the number of
89     /// available bytes does not reach the threshold, this returns `None`.
90     ///
91     /// This represents pending outbound WINDOW_UPDATE frames.
unclaimed_capacity(&self) -> Option<WindowSize>92     pub fn unclaimed_capacity(&self) -> Option<WindowSize> {
93         let available = self.available;
94 
95         if self.window_size >= available {
96             return None;
97         }
98 
99         let unclaimed = available.0 - self.window_size.0;
100         let threshold = self.window_size.0 / UNCLAIMED_DENOMINATOR * UNCLAIMED_NUMERATOR;
101 
102         if unclaimed < threshold {
103             None
104         } else {
105             Some(unclaimed as WindowSize)
106         }
107     }
108 
109     /// Increase the window size.
110     ///
111     /// This is called after receiving a WINDOW_UPDATE frame
inc_window(&mut self, sz: WindowSize) -> Result<(), Reason>112     pub fn inc_window(&mut self, sz: WindowSize) -> Result<(), Reason> {
113         let (val, overflow) = self.window_size.0.overflowing_add(sz as i32);
114 
115         if overflow {
116             return Err(Reason::FLOW_CONTROL_ERROR);
117         }
118 
119         if val > MAX_WINDOW_SIZE as i32 {
120             return Err(Reason::FLOW_CONTROL_ERROR);
121         }
122 
123         log::trace!(
124             "inc_window; sz={}; old={}; new={}",
125             sz,
126             self.window_size,
127             val
128         );
129 
130         self.window_size = Window(val);
131         Ok(())
132     }
133 
134     /// Decrement the send-side window size.
135     ///
136     /// This is called after receiving a SETTINGS frame with a lower
137     /// INITIAL_WINDOW_SIZE value.
dec_send_window(&mut self, sz: WindowSize)138     pub fn dec_send_window(&mut self, sz: WindowSize) {
139         log::trace!(
140             "dec_window; sz={}; window={}, available={}",
141             sz,
142             self.window_size,
143             self.available
144         );
145         // This should not be able to overflow `window_size` from the bottom.
146         self.window_size -= sz;
147     }
148 
149     /// Decrement the recv-side window size.
150     ///
151     /// This is called after receiving a SETTINGS ACK frame with a lower
152     /// INITIAL_WINDOW_SIZE value.
dec_recv_window(&mut self, sz: WindowSize)153     pub fn dec_recv_window(&mut self, sz: WindowSize) {
154         log::trace!(
155             "dec_recv_window; sz={}; window={}, available={}",
156             sz,
157             self.window_size,
158             self.available
159         );
160         // This should not be able to overflow `window_size` from the bottom.
161         self.window_size -= sz;
162         self.available -= sz;
163     }
164 
165     /// Decrements the window reflecting data has actually been sent. The caller
166     /// must ensure that the window has capacity.
send_data(&mut self, sz: WindowSize)167     pub fn send_data(&mut self, sz: WindowSize) {
168         log::trace!(
169             "send_data; sz={}; window={}; available={}",
170             sz,
171             self.window_size,
172             self.available
173         );
174 
175         // Ensure that the argument is correct
176         assert!(sz <= self.window_size);
177 
178         // Update values
179         self.window_size -= sz;
180         self.available -= sz;
181     }
182 }
183 
184 /// The current capacity of a flow-controlled Window.
185 ///
186 /// This number can go negative when either side has used a certain amount
187 /// of capacity when the other side advertises a reduction in size.
188 ///
189 /// This type tries to centralize the knowledge of addition and subtraction
190 /// to this capacity, instead of having integer casts throughout the source.
191 #[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
192 pub struct Window(i32);
193 
194 impl Window {
as_size(&self) -> WindowSize195     pub fn as_size(&self) -> WindowSize {
196         if self.0 < 0 {
197             0
198         } else {
199             self.0 as WindowSize
200         }
201     }
202 
checked_size(&self) -> WindowSize203     pub fn checked_size(&self) -> WindowSize {
204         assert!(self.0 >= 0, "negative Window");
205         self.0 as WindowSize
206     }
207 }
208 
209 impl PartialEq<WindowSize> for Window {
eq(&self, other: &WindowSize) -> bool210     fn eq(&self, other: &WindowSize) -> bool {
211         if self.0 < 0 {
212             false
213         } else {
214             (self.0 as WindowSize).eq(other)
215         }
216     }
217 }
218 
219 impl PartialEq<Window> for WindowSize {
eq(&self, other: &Window) -> bool220     fn eq(&self, other: &Window) -> bool {
221         other.eq(self)
222     }
223 }
224 
225 impl PartialOrd<WindowSize> for Window {
partial_cmp(&self, other: &WindowSize) -> Option<::std::cmp::Ordering>226     fn partial_cmp(&self, other: &WindowSize) -> Option<::std::cmp::Ordering> {
227         if self.0 < 0 {
228             Some(::std::cmp::Ordering::Less)
229         } else {
230             (self.0 as WindowSize).partial_cmp(other)
231         }
232     }
233 }
234 
235 impl PartialOrd<Window> for WindowSize {
partial_cmp(&self, other: &Window) -> Option<::std::cmp::Ordering>236     fn partial_cmp(&self, other: &Window) -> Option<::std::cmp::Ordering> {
237         if other.0 < 0 {
238             Some(::std::cmp::Ordering::Greater)
239         } else {
240             self.partial_cmp(&(other.0 as WindowSize))
241         }
242     }
243 }
244 
245 impl ::std::ops::SubAssign<WindowSize> for Window {
sub_assign(&mut self, other: WindowSize)246     fn sub_assign(&mut self, other: WindowSize) {
247         self.0 -= other as i32;
248     }
249 }
250 
251 impl ::std::ops::Add<WindowSize> for Window {
252     type Output = Self;
add(self, other: WindowSize) -> Self::Output253     fn add(self, other: WindowSize) -> Self::Output {
254         Window(self.0 + other as i32)
255     }
256 }
257 
258 impl ::std::ops::AddAssign<WindowSize> for Window {
add_assign(&mut self, other: WindowSize)259     fn add_assign(&mut self, other: WindowSize) {
260         self.0 += other as i32;
261     }
262 }
263 
264 impl fmt::Display for Window {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result265     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
266         fmt::Display::fmt(&self.0, f)
267     }
268 }
269 
270 impl From<Window> for isize {
from(w: Window) -> isize271     fn from(w: Window) -> isize {
272         w.0 as isize
273     }
274 }
275