Project import generated by Copybara.
PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/core/quic_control_frame_manager.cc b/quic/core/quic_control_frame_manager.cc
new file mode 100644
index 0000000..8d4ba53
--- /dev/null
+++ b/quic/core/quic_control_frame_manager.cc
@@ -0,0 +1,311 @@
+// Copyright (c) 2017 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/third_party/quiche/src/quic/core/quic_control_frame_manager.h"
+
+#include "net/third_party/quiche/src/quic/core/quic_constants.h"
+#include "net/third_party/quiche/src/quic/core/quic_session.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_bug_tracker.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_map_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
+
+namespace quic {
+
+QuicControlFrameManager::QuicControlFrameManager(QuicSession* session)
+ : last_control_frame_id_(kInvalidControlFrameId),
+ least_unacked_(1),
+ least_unsent_(1),
+ session_(session) {}
+
+QuicControlFrameManager::~QuicControlFrameManager() {
+ while (!control_frames_.empty()) {
+ DeleteFrame(&control_frames_.front());
+ control_frames_.pop_front();
+ }
+}
+
+void QuicControlFrameManager::WriteOrBufferQuicFrame(QuicFrame frame) {
+ const bool had_buffered_frames = HasBufferedFrames();
+ control_frames_.emplace_back(frame);
+ if (had_buffered_frames) {
+ return;
+ }
+ WriteBufferedFrames();
+}
+
+void QuicControlFrameManager::WriteOrBufferRstStream(
+ QuicStreamId id,
+ QuicRstStreamErrorCode error,
+ QuicStreamOffset bytes_written) {
+ QUIC_DVLOG(1) << "Writing RST_STREAM_FRAME";
+ WriteOrBufferQuicFrame((QuicFrame(new QuicRstStreamFrame(
+ ++last_control_frame_id_, id, error, bytes_written))));
+}
+
+void QuicControlFrameManager::WriteOrBufferGoAway(
+ QuicErrorCode error,
+ QuicStreamId last_good_stream_id,
+ const QuicString& reason) {
+ QUIC_DVLOG(1) << "Writing GOAWAY_FRAME";
+ WriteOrBufferQuicFrame(QuicFrame(new QuicGoAwayFrame(
+ ++last_control_frame_id_, error, last_good_stream_id, reason)));
+}
+
+void QuicControlFrameManager::WriteOrBufferWindowUpdate(
+ QuicStreamId id,
+ QuicStreamOffset byte_offset) {
+ QUIC_DVLOG(1) << "Writing WINDOW_UPDATE_FRAME";
+ WriteOrBufferQuicFrame(QuicFrame(
+ new QuicWindowUpdateFrame(++last_control_frame_id_, id, byte_offset)));
+}
+
+void QuicControlFrameManager::WriteOrBufferBlocked(QuicStreamId id) {
+ QUIC_DVLOG(1) << "Writing BLOCKED_FRAME";
+ WriteOrBufferQuicFrame(
+ QuicFrame(new QuicBlockedFrame(++last_control_frame_id_, id)));
+}
+
+void QuicControlFrameManager::WriteOrBufferStreamIdBlocked(QuicStreamId id) {
+ QUIC_DVLOG(1) << "Writing STREAM_ID_BLOCKED Frame";
+ QUIC_CODE_COUNT(stream_id_blocked_transmits);
+ WriteOrBufferQuicFrame(
+ QuicFrame(QuicStreamIdBlockedFrame(++last_control_frame_id_, id)));
+}
+
+void QuicControlFrameManager::WriteOrBufferMaxStreamId(QuicStreamId id) {
+ QUIC_DVLOG(1) << "Writing MAX_STREAM_ID Frame";
+ QUIC_CODE_COUNT(max_stream_id_transmits);
+ WriteOrBufferQuicFrame(
+ QuicFrame(QuicMaxStreamIdFrame(++last_control_frame_id_, id)));
+}
+
+void QuicControlFrameManager::WriteOrBufferStopSending(uint16_t code,
+ QuicStreamId stream_id) {
+ QUIC_DVLOG(1) << "Writing STOP_SENDING_FRAME";
+ WriteOrBufferQuicFrame(QuicFrame(
+ new QuicStopSendingFrame(++last_control_frame_id_, stream_id, code)));
+}
+
+void QuicControlFrameManager::WritePing() {
+ QUIC_DVLOG(1) << "Writing PING_FRAME";
+ if (HasBufferedFrames()) {
+ // Do not send ping if there is buffered frames.
+ QUIC_LOG(WARNING)
+ << "Try to send PING when there is buffered control frames.";
+ return;
+ }
+ control_frames_.emplace_back(
+ QuicFrame(QuicPingFrame(++last_control_frame_id_)));
+ WriteBufferedFrames();
+}
+
+void QuicControlFrameManager::OnControlFrameSent(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ QUIC_BUG
+ << "Send or retransmit a control frame with invalid control frame id";
+ return;
+ }
+ if (frame.type == WINDOW_UPDATE_FRAME) {
+ QuicStreamId stream_id = frame.window_update_frame->stream_id;
+ if (QuicContainsKey(window_update_frames_, stream_id) &&
+ id > window_update_frames_[stream_id]) {
+ // Consider the older window update of the same stream as acked.
+ OnControlFrameIdAcked(window_update_frames_[stream_id]);
+ }
+ window_update_frames_[stream_id] = id;
+ }
+ if (QuicContainsKey(pending_retransmissions_, id)) {
+ // This is retransmitted control frame.
+ pending_retransmissions_.erase(id);
+ return;
+ }
+ if (id > least_unsent_) {
+ QUIC_BUG << "Try to send control frames out of order, id: " << id
+ << " least_unsent: " << least_unsent_;
+ session_->connection()->CloseConnection(
+ QUIC_INTERNAL_ERROR, "Try to send control frames out of order",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ ++least_unsent_;
+}
+
+bool QuicControlFrameManager::OnControlFrameAcked(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (!OnControlFrameIdAcked(id)) {
+ return false;
+ }
+ if (frame.type == WINDOW_UPDATE_FRAME) {
+ QuicStreamId stream_id = frame.window_update_frame->stream_id;
+ if (QuicContainsKey(window_update_frames_, stream_id) &&
+ window_update_frames_[stream_id] == id) {
+ window_update_frames_.erase(stream_id);
+ }
+ }
+ return true;
+}
+
+void QuicControlFrameManager::OnControlFrameLost(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ // Frame does not have a valid control frame ID, ignore it.
+ return;
+ }
+ if (id >= least_unsent_) {
+ QUIC_BUG << "Try to mark unsent control frame as lost";
+ session_->connection()->CloseConnection(
+ QUIC_INTERNAL_ERROR, "Try to mark unsent control frame as lost",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return;
+ }
+ if (id < least_unacked_ ||
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) ==
+ kInvalidControlFrameId) {
+ // This frame has already been acked.
+ return;
+ }
+ if (!QuicContainsKey(pending_retransmissions_, id)) {
+ pending_retransmissions_[id] = true;
+ }
+}
+
+bool QuicControlFrameManager::IsControlFrameOutstanding(
+ const QuicFrame& frame) const {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ // Frame without a control frame ID should not be retransmitted.
+ return false;
+ }
+ // Consider this frame is outstanding if it does not get acked.
+ return id < least_unacked_ + control_frames_.size() && id >= least_unacked_ &&
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) !=
+ kInvalidControlFrameId;
+}
+
+bool QuicControlFrameManager::HasPendingRetransmission() const {
+ return !pending_retransmissions_.empty();
+}
+
+bool QuicControlFrameManager::WillingToWrite() const {
+ return HasPendingRetransmission() || HasBufferedFrames();
+}
+
+QuicFrame QuicControlFrameManager::NextPendingRetransmission() const {
+ QUIC_BUG_IF(pending_retransmissions_.empty())
+ << "Unexpected call to NextPendingRetransmission() with empty pending "
+ << "retransmission list.";
+ QuicControlFrameId id = pending_retransmissions_.begin()->first;
+ return control_frames_.at(id - least_unacked_);
+}
+
+void QuicControlFrameManager::OnCanWrite() {
+ if (HasPendingRetransmission()) {
+ // Exit early to allow streams to write pending retransmissions if any.
+ WritePendingRetransmission();
+ return;
+ }
+ WriteBufferedFrames();
+}
+
+bool QuicControlFrameManager::RetransmitControlFrame(const QuicFrame& frame) {
+ QuicControlFrameId id = GetControlFrameId(frame);
+ if (id == kInvalidControlFrameId) {
+ // Frame does not have a valid control frame ID, ignore it. Returns true
+ // to allow writing following frames.
+ return true;
+ }
+ if (id >= least_unsent_) {
+ QUIC_BUG << "Try to retransmit unsent control frame";
+ session_->connection()->CloseConnection(
+ QUIC_INTERNAL_ERROR, "Try to retransmit unsent control frame",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ if (id < least_unacked_ ||
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) ==
+ kInvalidControlFrameId) {
+ // This frame has already been acked.
+ return true;
+ }
+ QuicFrame copy = CopyRetransmittableControlFrame(frame);
+ QUIC_DVLOG(1) << "control frame manager is forced to retransmit frame: "
+ << frame;
+ if (session_->WriteControlFrame(copy)) {
+ return true;
+ }
+ DeleteFrame(©);
+ return false;
+}
+
+void QuicControlFrameManager::WriteBufferedFrames() {
+ while (HasBufferedFrames()) {
+ if (session_->session_decides_what_to_write()) {
+ session_->SetTransmissionType(NOT_RETRANSMISSION);
+ }
+ QuicFrame frame_to_send =
+ control_frames_.at(least_unsent_ - least_unacked_);
+ QuicFrame copy = CopyRetransmittableControlFrame(frame_to_send);
+ if (!session_->WriteControlFrame(copy)) {
+ // Connection is write blocked.
+ DeleteFrame(©);
+ break;
+ }
+ OnControlFrameSent(frame_to_send);
+ }
+}
+
+void QuicControlFrameManager::WritePendingRetransmission() {
+ while (HasPendingRetransmission()) {
+ QuicFrame pending = NextPendingRetransmission();
+ QuicFrame copy = CopyRetransmittableControlFrame(pending);
+ if (!session_->WriteControlFrame(copy)) {
+ // Connection is write blocked.
+ DeleteFrame(©);
+ break;
+ }
+ OnControlFrameSent(pending);
+ }
+}
+
+bool QuicControlFrameManager::OnControlFrameIdAcked(QuicControlFrameId id) {
+ if (id == kInvalidControlFrameId) {
+ // Frame does not have a valid control frame ID, ignore it.
+ return false;
+ }
+ if (id >= least_unsent_) {
+ QUIC_BUG << "Try to ack unsent control frame";
+ session_->connection()->CloseConnection(
+ QUIC_INTERNAL_ERROR, "Try to ack unsent control frame",
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
+ return false;
+ }
+ if (id < least_unacked_ ||
+ GetControlFrameId(control_frames_.at(id - least_unacked_)) ==
+ kInvalidControlFrameId) {
+ // This frame has already been acked.
+ return false;
+ }
+
+ // Set control frame ID of acked frames to 0.
+ SetControlFrameId(kInvalidControlFrameId,
+ &control_frames_.at(id - least_unacked_));
+ // Remove acked control frames from pending retransmissions.
+ pending_retransmissions_.erase(id);
+ // Clean up control frames queue and increment least_unacked_.
+ while (!control_frames_.empty() &&
+ GetControlFrameId(control_frames_.front()) == kInvalidControlFrameId) {
+ DeleteFrame(&control_frames_.front());
+ control_frames_.pop_front();
+ ++least_unacked_;
+ }
+ return true;
+}
+
+bool QuicControlFrameManager::HasBufferedFrames() const {
+ return least_unsent_ < least_unacked_ + control_frames_.size();
+}
+
+} // namespace quic