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