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
12namespace net {
13
14#define ENDPOINT (is_server_ ? "Server: " : " Client: ")
15
16QuicFlowController::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
46void 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
57bool 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
74void 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
94bool 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
110void 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
138void 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
159bool 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
179void QuicFlowController::Disable() {
180  is_enabled_ = false;
181}
182
183bool QuicFlowController::IsEnabled() const {
184  return is_enabled_;
185}
186
187bool QuicFlowController::IsBlocked() const {
188  return IsEnabled() && SendWindowSize() == 0;
189}
190
191uint64 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