|  | // 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 <string> | 
|  |  | 
|  | #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" | 
|  |  | 
|  | namespace quic { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // The maximum number of buffered control frames which are waiting to be ACKed | 
|  | // or sent for the first time. | 
|  | const size_t kMaxNumControlFrames = 1000; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | QuicControlFrameManager::QuicControlFrameManager(QuicSession* session) | 
|  | : last_control_frame_id_(kInvalidControlFrameId), | 
|  | least_unacked_(1), | 
|  | least_unsent_(1), | 
|  | session_(session), | 
|  | add_upper_limit_(GetQuicReloadableFlag( | 
|  | quic_add_upper_limit_of_buffered_control_frames3)) { | 
|  | if (add_upper_limit_) { | 
|  | QUIC_RELOADABLE_FLAG_COUNT( | 
|  | quic_add_upper_limit_of_buffered_control_frames3); | 
|  | } | 
|  | } | 
|  |  | 
|  | 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 (add_upper_limit_ && control_frames_.size() > kMaxNumControlFrames) { | 
|  | session_->connection()->CloseConnection( | 
|  | QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES, | 
|  | QuicStrCat("More than ", kMaxNumControlFrames, | 
|  | "buffered control frames, least_unacked: ", least_unacked_, | 
|  | ", least_unsent_: ", least_unsent_), | 
|  | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | 
|  | return; | 
|  | } | 
|  | 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 std::string& 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::WriteOrBufferStreamsBlocked(QuicStreamCount count, | 
|  | bool unidirectional) { | 
|  | QUIC_DVLOG(1) << "Writing STREAMS_BLOCKED Frame"; | 
|  | QUIC_CODE_COUNT(quic_streams_blocked_transmits); | 
|  | WriteOrBufferQuicFrame(QuicFrame(QuicStreamsBlockedFrame( | 
|  | ++last_control_frame_id_, count, unidirectional))); | 
|  | } | 
|  |  | 
|  | void QuicControlFrameManager::WriteOrBufferMaxStreams(QuicStreamCount count, | 
|  | bool unidirectional) { | 
|  | QUIC_DVLOG(1) << "Writing MAX_STREAMS Frame"; | 
|  | QUIC_CODE_COUNT(quic_max_streams_transmits); | 
|  | WriteOrBufferQuicFrame(QuicFrame( | 
|  | QuicMaxStreamsFrame(++last_control_frame_id_, count, unidirectional))); | 
|  | } | 
|  |  | 
|  | 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_))); | 
|  | if (add_upper_limit_ && control_frames_.size() > kMaxNumControlFrames) { | 
|  | session_->connection()->CloseConnection( | 
|  | QUIC_TOO_MANY_BUFFERED_CONTROL_FRAMES, | 
|  | QuicStrCat("More than ", kMaxNumControlFrames, | 
|  | "buffered control frames, least_unacked: ", least_unacked_, | 
|  | ", least_unsent_: ", least_unsent_), | 
|  | ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET); | 
|  | return; | 
|  | } | 
|  | 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; | 
|  | QUIC_BUG_IF(pending_retransmissions_.size() > control_frames_.size()) | 
|  | << "least_unacked_: " << least_unacked_ | 
|  | << ", least_unsent_: " << least_unsent_; | 
|  | } | 
|  | } | 
|  |  | 
|  | 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()) { | 
|  | 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 |