diff options
Diffstat (limited to 'chromium/net/quic/quic_flow_controller.cc')
-rw-r--r-- | chromium/net/quic/quic_flow_controller.cc | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/chromium/net/quic/quic_flow_controller.cc b/chromium/net/quic/quic_flow_controller.cc new file mode 100644 index 00000000000..107b4d9510a --- /dev/null +++ b/chromium/net/quic/quic_flow_controller.cc @@ -0,0 +1,203 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/quic/quic_flow_controller.h" + +#include "base/basictypes.h" +#include "net/quic/quic_connection.h" +#include "net/quic/quic_flags.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +#define ENDPOINT (is_server_ ? "Server: " : " Client: ") + +QuicFlowController::QuicFlowController(QuicConnection* connection, + QuicStreamId id, + bool is_server, + uint64 send_window_offset, + uint64 receive_window_offset, + uint64 max_receive_window) + : connection_(connection), + id_(id), + is_enabled_(true), + is_server_(is_server), + bytes_consumed_(0), + highest_received_byte_offset_(0), + bytes_sent_(0), + send_window_offset_(send_window_offset), + receive_window_offset_(receive_window_offset), + max_receive_window_(max_receive_window), + last_blocked_send_window_offset_(0) { + DVLOG(1) << ENDPOINT << "Created flow controller for stream " << id_ + << ", setting initial receive window offset to: " + << receive_window_offset_ + << ", max receive window to: " + << max_receive_window_ + << ", setting send window offset to: " << send_window_offset_; + if (connection_->version() < QUIC_VERSION_17) { + DVLOG(1) << ENDPOINT << "Disabling QuicFlowController for stream " << id_ + << ", QUIC version " << connection_->version(); + Disable(); + } +} + +void QuicFlowController::AddBytesConsumed(uint64 bytes_consumed) { + if (!IsEnabled()) { + return; + } + + bytes_consumed_ += bytes_consumed; + DVLOG(1) << ENDPOINT << "Stream " << id_ << " consumed: " << bytes_consumed_; + + MaybeSendWindowUpdate(); +} + +bool QuicFlowController::UpdateHighestReceivedOffset(uint64 new_offset) { + if (!IsEnabled()) { + return false; + } + + // Only update if offset has increased. + if (new_offset <= highest_received_byte_offset_) { + return false; + } + + DVLOG(1) << ENDPOINT << "Stream " << id_ + << " highest byte offset increased from: " + << highest_received_byte_offset_ << " to " << new_offset; + highest_received_byte_offset_ = new_offset; + return true; +} + +void QuicFlowController::AddBytesSent(uint64 bytes_sent) { + if (!IsEnabled()) { + return; + } + + if (bytes_sent_ + bytes_sent > send_window_offset_) { + LOG(DFATAL) << ENDPOINT << "Stream " << id_ << " Trying to send an extra " + << bytes_sent << " bytes, when bytes_sent = " << bytes_sent_ + << ", and send_window_offset_ = " << send_window_offset_; + bytes_sent_ = send_window_offset_; + + // This is an error on our side, close the connection as soon as possible. + connection_->SendConnectionClose(QUIC_FLOW_CONTROL_SENT_TOO_MUCH_DATA); + return; + } + + bytes_sent_ += bytes_sent; + DVLOG(1) << ENDPOINT << "Stream " << id_ << " sent: " << bytes_sent_; +} + +bool QuicFlowController::FlowControlViolation() { + if (!IsEnabled()) { + return false; + } + + if (highest_received_byte_offset_ > receive_window_offset_) { + LOG(ERROR) << ENDPOINT << "Flow control violation on stream " + << id_ << ", receive window offset: " + << receive_window_offset_ + << ", highest received byte offset: " + << highest_received_byte_offset_; + return true; + } + return false; +} + +void QuicFlowController::MaybeSendWindowUpdate() { + if (!IsEnabled()) { + return; + } + + // Send WindowUpdate to increase receive window if + // (receive window offset - consumed bytes) < (max window / 2). + // This is behaviour copied from SPDY. + DCHECK_LT(bytes_consumed_, receive_window_offset_); + size_t consumed_window = receive_window_offset_ - bytes_consumed_; + size_t threshold = (max_receive_window_ / 2); + + if (consumed_window < threshold) { + // Update our receive window. + receive_window_offset_ += (max_receive_window_ - consumed_window); + + DVLOG(1) << ENDPOINT << "Sending WindowUpdate frame for stream " << id_ + << ", consumed bytes: " << bytes_consumed_ + << ", consumed window: " << consumed_window + << ", and threshold: " << threshold + << ", and max recvw: " << max_receive_window_ + << ". New receive window offset is: " << receive_window_offset_; + + // Inform the peer of our new receive window. + connection_->SendWindowUpdate(id_, receive_window_offset_); + } +} + +void QuicFlowController::MaybeSendBlocked() { + if (!IsEnabled()) { + return; + } + + if (SendWindowSize() == 0 && + last_blocked_send_window_offset_ < send_window_offset_) { + DVLOG(1) << ENDPOINT << "Stream " << id_ << " is flow control blocked. " + << "Send window: " << SendWindowSize() + << ", bytes sent: " << bytes_sent_ + << ", send limit: " << send_window_offset_; + // The entire send_window has been consumed, we are now flow control + // blocked. + connection_->SendBlocked(id_); + + // Keep track of when we last sent a BLOCKED frame so that we only send one + // at a given send offset. + last_blocked_send_window_offset_ = send_window_offset_; + } +} + +bool QuicFlowController::UpdateSendWindowOffset(uint64 new_send_window_offset) { + if (!IsEnabled()) { + return false; + } + + // Only update if send window has increased. + if (new_send_window_offset <= send_window_offset_) { + return false; + } + + DVLOG(1) << ENDPOINT << "UpdateSendWindowOffset for stream " << id_ + << " with new offset " << new_send_window_offset + << " , current offset: " << send_window_offset_; + + send_window_offset_ = new_send_window_offset; + return true; +} + +void QuicFlowController::Disable() { + is_enabled_ = false; +} + +bool QuicFlowController::IsEnabled() const { + bool connection_flow_control_enabled = + (id_ == kConnectionLevelId && + FLAGS_enable_quic_connection_flow_control_2); + bool stream_flow_control_enabled = + (id_ != kConnectionLevelId && + FLAGS_enable_quic_stream_flow_control_2); + return (connection_flow_control_enabled || stream_flow_control_enabled) && + is_enabled_; +} + +bool QuicFlowController::IsBlocked() const { + return IsEnabled() && SendWindowSize() == 0; +} + +uint64 QuicFlowController::SendWindowSize() const { + if (bytes_sent_ > send_window_offset_) { + return 0; + } + return send_window_offset_ - bytes_sent_; +} + +} // namespace net |