Home | History | Annotate | Download | only in quic
      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 
     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_17) {
     40     DVLOG(1) << ENDPOINT << "Disabling QuicFlowController for stream " << id_
     41              << ", QUIC version " << connection_->version();
     42     Disable();
     43   }
     44 }
     45 
     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 
     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 
     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 
     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 
    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 
    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 
    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 
    173   send_window_offset_ = new_send_window_offset;
    174   return true;
    175 }
    176 
    177 void QuicFlowController::Disable() {
    178   is_enabled_ = false;
    179 }
    180 
    181 bool QuicFlowController::IsEnabled() const {
    182   bool connection_flow_control_enabled =
    183       (id_ == kConnectionLevelId &&
    184        FLAGS_enable_quic_connection_flow_control_2);
    185   bool stream_flow_control_enabled =
    186       (id_ != kConnectionLevelId &&
    187        FLAGS_enable_quic_stream_flow_control_2);
    188   return (connection_flow_control_enabled || stream_flow_control_enabled) &&
    189          is_enabled_;
    190 }
    191 
    192 bool QuicFlowController::IsBlocked() const {
    193   return IsEnabled() && SendWindowSize() == 0;
    194 }
    195 
    196 uint64 QuicFlowController::SendWindowSize() const {
    197   if (bytes_sent_ > send_window_offset_) {
    198     return 0;
    199   }
    200   return send_window_offset_ - bytes_sent_;
    201 }
    202 
    203 }  // namespace net
    204