• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/quic/quic_flow_controller.h"
6 
7 #include "base/basictypes.h"
8 #include "net/quic/quic_connection.h"
9 #include "net/quic/quic_flags.h"
10 #include "net/quic/quic_protocol.h"
11 
12 namespace net {
13 
14 #define ENDPOINT (is_server_ ? "Server: " : " Client: ")
15 
QuicFlowController(QuicConnection * connection,QuicStreamId id,bool is_server,uint64 send_window_offset,uint64 receive_window_offset,uint64 max_receive_window)16 QuicFlowController::QuicFlowController(QuicConnection* connection,
17                                        QuicStreamId id,
18                                        bool is_server,
19                                        uint64 send_window_offset,
20                                        uint64 receive_window_offset,
21                                        uint64 max_receive_window)
22     : connection_(connection),
23       id_(id),
24       is_enabled_(true),
25       is_server_(is_server),
26       bytes_consumed_(0),
27       highest_received_byte_offset_(0),
28       bytes_sent_(0),
29       send_window_offset_(send_window_offset),
30       receive_window_offset_(receive_window_offset),
31       max_receive_window_(max_receive_window),
32       last_blocked_send_window_offset_(0) {
33   DVLOG(1) << ENDPOINT << "Created flow controller for stream " << id_
34            << ", setting initial receive window offset to: "
35            << receive_window_offset_
36            << ", max receive window to: "
37            << max_receive_window_
38            << ", setting send window offset to: " << send_window_offset_;
39   if (connection_->version() <= QUIC_VERSION_16) {
40     DVLOG(1) << ENDPOINT << "Disabling QuicFlowController for stream " << id_
41              << ", QUIC version " << connection_->version();
42     Disable();
43   }
44 }
45 
AddBytesConsumed(uint64 bytes_consumed)46 void QuicFlowController::AddBytesConsumed(uint64 bytes_consumed) {
47   if (!IsEnabled()) {
48     return;
49   }
50 
51   bytes_consumed_ += bytes_consumed;
52   DVLOG(1) << ENDPOINT << "Stream " << id_ << " consumed: " << bytes_consumed_;
53 
54   MaybeSendWindowUpdate();
55 }
56 
UpdateHighestReceivedOffset(uint64 new_offset)57 bool QuicFlowController::UpdateHighestReceivedOffset(uint64 new_offset) {
58   if (!IsEnabled()) {
59     return false;
60   }
61 
62   // Only update if offset has increased.
63   if (new_offset <= highest_received_byte_offset_) {
64     return false;
65   }
66 
67   DVLOG(1) << ENDPOINT << "Stream " << id_
68            << " highest byte offset increased from: "
69            << highest_received_byte_offset_ << " to " << new_offset;
70   highest_received_byte_offset_ = new_offset;
71   return true;
72 }
73 
AddBytesSent(uint64 bytes_sent)74 void QuicFlowController::AddBytesSent(uint64 bytes_sent) {
75   if (!IsEnabled()) {
76     return;
77   }
78 
79   if (bytes_sent_ + bytes_sent > send_window_offset_) {
80     LOG(DFATAL) << ENDPOINT << "Stream " << id_ << " Trying to send an extra "
81                 << bytes_sent << " bytes, when bytes_sent = " << bytes_sent_
82                 << ", and send_window_offset_ = " << send_window_offset_;
83     bytes_sent_ = send_window_offset_;
84 
85     // This is an error on our side, close the connection as soon as possible.
86     connection_->SendConnectionClose(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA);
87     return;
88   }
89 
90   bytes_sent_ += bytes_sent;
91   DVLOG(1) << ENDPOINT << "Stream " << id_ << " sent: " << bytes_sent_;
92 }
93 
FlowControlViolation()94 bool QuicFlowController::FlowControlViolation() {
95   if (!IsEnabled()) {
96     return false;
97   }
98 
99   if (highest_received_byte_offset_ > receive_window_offset_) {
100     LOG(ERROR) << ENDPOINT << "Flow control violation on stream "
101                << id_ << ", receive window offset: "
102                << receive_window_offset_
103                << ", highest received byte offset: "
104                << highest_received_byte_offset_;
105     return true;
106   }
107   return false;
108 }
109 
MaybeSendWindowUpdate()110 void QuicFlowController::MaybeSendWindowUpdate() {
111   if (!IsEnabled()) {
112     return;
113   }
114 
115   // Send WindowUpdate to increase receive window if
116   // (receive window offset - consumed bytes) < (max window / 2).
117   // This is behaviour copied from SPDY.
118   DCHECK_LT(bytes_consumed_, receive_window_offset_);
119   size_t consumed_window = receive_window_offset_ - bytes_consumed_;
120   size_t threshold = (max_receive_window_ / 2);
121 
122   if (consumed_window < threshold) {
123     // Update our receive window.
124     receive_window_offset_ += (max_receive_window_ - consumed_window);
125 
126     DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_
127              << ", consumed bytes: " << bytes_consumed_
128              << ", consumed window: " << consumed_window
129              << ", and threshold: " << threshold
130              << ", and max recvw: " << max_receive_window_
131              << ". New receive window offset is: " << receive_window_offset_;
132 
133     // Inform the peer of our new receive window.
134     connection_->SendWindowUpdate(id_, receive_window_offset_);
135   }
136 }
137 
MaybeSendBlocked()138 void QuicFlowController::MaybeSendBlocked() {
139   if (!IsEnabled()) {
140     return;
141   }
142 
143   if (SendWindowSize() == 0 &&
144       last_blocked_send_window_offset_ < send_window_offset_) {
145     DVLOG(1) << ENDPOINT << "Stream " << id_ << " is flow control blocked. "
146              << "Send window: " << SendWindowSize()
147              << ", bytes sent: " << bytes_sent_
148              << ", send limit: " << send_window_offset_;
149     // The entire send_window has been consumed, we are now flow control
150     // blocked.
151     connection_->SendBlocked(id_);
152 
153     // Keep track of when we last sent a BLOCKED frame so that we only send one
154     // at a given send offset.
155     last_blocked_send_window_offset_ = send_window_offset_;
156   }
157 }
158 
UpdateSendWindowOffset(uint64 new_send_window_offset)159 bool QuicFlowController::UpdateSendWindowOffset(uint64 new_send_window_offset) {
160   if (!IsEnabled()) {
161     return false;
162   }
163 
164   // Only update if send window has increased.
165   if (new_send_window_offset <= send_window_offset_) {
166     return false;
167   }
168 
169   DVLOG(1) << ENDPOINT << "UpdateSendWindowOffset for stream " << id_
170            << " with new offset " << new_send_window_offset
171            << " current offset: " << send_window_offset_
172            << " bytes_sent: " << bytes_sent_;
173 
174   const bool blocked = IsBlocked();
175   send_window_offset_ = new_send_window_offset;
176   return blocked;
177 }
178 
Disable()179 void QuicFlowController::Disable() {
180   is_enabled_ = false;
181 }
182 
IsEnabled() const183 bool QuicFlowController::IsEnabled() const {
184   return is_enabled_;
185 }
186 
IsBlocked() const187 bool QuicFlowController::IsBlocked() const {
188   return IsEnabled() && SendWindowSize() == 0;
189 }
190 
SendWindowSize() const191 uint64 QuicFlowController::SendWindowSize() const {
192   if (bytes_sent_ > send_window_offset_) {
193     return 0;
194   }
195   return send_window_offset_ - bytes_sent_;
196 }
197 
198 }  // namespace net
199