Index: net/quic/quic_flow_controller.cc |
diff --git a/net/quic/quic_flow_controller.cc b/net/quic/quic_flow_controller.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..b9a9e448546bc7db7cbfdce54adb33d1d6f64a01 |
--- /dev/null |
+++ b/net/quic/quic_flow_controller.cc |
@@ -0,0 +1,183 @@ |
+// Copyright (c) 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(QuicStreamId id, |
+ bool is_server, |
+ uint64 send_window_offset, |
+ uint64 receive_window_offset, |
+ uint64 max_receive_window) |
+ : id_(id), |
+ is_enabled_(true), |
+ is_server_(is_server), |
+ bytes_consumed_(0), |
+ bytes_buffered_(0), |
+ bytes_sent_(0), |
+ send_window_offset_(send_window_offset), |
+ receive_window_offset_(receive_window_offset), |
+ max_receive_window_(max_receive_window) { |
+ 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_; |
+} |
+ |
+void QuicFlowController::AddBytesConsumed(uint64 bytes_consumed) { |
+ if (!IsEnabled()) { |
+ return; |
+ } |
+ |
+ bytes_consumed_ += bytes_consumed; |
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " consumed: " << bytes_consumed_; |
+} |
+ |
+void QuicFlowController::AddBytesBuffered(uint64 bytes_buffered) { |
+ if (!IsEnabled()) { |
+ return; |
+ } |
+ |
+ bytes_buffered_ += bytes_buffered; |
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " buffered: " << bytes_buffered_; |
+} |
+ |
+void QuicFlowController::RemoveBytesBuffered(uint64 bytes_buffered) { |
+ if (!IsEnabled()) { |
+ return; |
+ } |
+ |
+ if (bytes_buffered_ < bytes_buffered) { |
+ LOG(DFATAL) << "Trying to remove " << bytes_buffered << " bytes, when only " |
+ << bytes_buffered_ << " bytes are buffered"; |
+ bytes_buffered_ = 0; |
+ return; |
+ } |
+ |
+ bytes_buffered_ -= bytes_buffered; |
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " buffered: " << bytes_buffered_; |
+} |
+ |
+void QuicFlowController::AddBytesSent(uint64 bytes_sent) { |
+ if (!IsEnabled()) { |
+ return; |
+ } |
+ |
+ if (bytes_sent_ + bytes_sent > send_window_offset_) { |
+ LOG(DFATAL) << "Trying to send an extra " << bytes_sent |
+ << " bytes, when bytes_sent = " << bytes_sent_ |
+ << ", and send_window = " << send_window_offset_; |
+ bytes_sent_ = send_window_offset_; |
+ return; |
+ } |
+ |
+ bytes_sent_ += bytes_sent; |
+ DVLOG(1) << ENDPOINT << "Stream " << id_ << " sent: " << bytes_sent_; |
+} |
+ |
+bool QuicFlowController::FlowControlViolation() { |
+ if (receive_window_offset_ < TotalReceivedBytes()) { |
+ // TODO(rjshade): Lower severity from DFATAL once we have established that |
+ // flow control is working correctly. |
+ LOG(DFATAL) |
+ << ENDPOINT << "Flow control violation on stream " << id_ |
+ << ", receive window: " << receive_window_offset_ |
+ << ", bytes received: " << TotalReceivedBytes(); |
+ |
+ return true; |
+ } |
+ return false; |
+} |
+ |
+void QuicFlowController::MaybeSendWindowUpdate(QuicConnection* connection) { |
+ if (!IsEnabled()) { |
+ return; |
+ } |
+ |
+ // Send WindowUpdate to increase receive window if |
+ // (receive window offset - consumed bytes) < (max window / 2). |
+ // This is behaviour copied from SPDY. |
+ 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(QuicConnection* connection) { |
+ if (!IsEnabled()) { |
+ return; |
+ } |
+ |
+ if (SendWindowSize() == 0) { |
+ 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_); |
+ } |
+} |
+ |
+bool QuicFlowController::UpdateSendWindowOffset(uint64 new_send_window_offset) { |
+ // 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 { |
+ return FLAGS_enable_quic_stream_flow_control && 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_; |
+} |
+ |
+uint64 QuicFlowController::TotalReceivedBytes() const { |
+ return bytes_consumed_ + bytes_buffered_; |
+} |
+ |
+} // namespace net |