| // Copyright 2013 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/http/quic_headers_stream.h" | 
 |  | 
 | #include "net/third_party/quiche/src/quic/core/http/quic_spdy_session.h" | 
 | #include "net/third_party/quiche/src/quic/core/quic_utils.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_flag_utils.h" | 
 | #include "net/third_party/quiche/src/quic/platform/api/quic_flags.h" | 
 |  | 
 | namespace quic { | 
 |  | 
 | QuicHeadersStream::CompressedHeaderInfo::CompressedHeaderInfo( | 
 |     QuicStreamOffset headers_stream_offset, | 
 |     QuicStreamOffset full_length, | 
 |     QuicReferenceCountedPointer<QuicAckListenerInterface> ack_listener) | 
 |     : headers_stream_offset(headers_stream_offset), | 
 |       full_length(full_length), | 
 |       unacked_length(full_length), | 
 |       ack_listener(std::move(ack_listener)) {} | 
 |  | 
 | QuicHeadersStream::CompressedHeaderInfo::CompressedHeaderInfo( | 
 |     const CompressedHeaderInfo& other) = default; | 
 |  | 
 | QuicHeadersStream::CompressedHeaderInfo::~CompressedHeaderInfo() {} | 
 |  | 
 | QuicHeadersStream::QuicHeadersStream(QuicSpdySession* session) | 
 |     : QuicStream(QuicUtils::GetHeadersStreamId(session->transport_version()), | 
 |                  session, | 
 |                  /*is_static=*/true, | 
 |                  BIDIRECTIONAL), | 
 |       spdy_session_(session) { | 
 |   // The headers stream is exempt from connection level flow control. | 
 |   DisableConnectionFlowControlForThisStream(); | 
 | } | 
 |  | 
 | QuicHeadersStream::~QuicHeadersStream() {} | 
 |  | 
 | void QuicHeadersStream::OnDataAvailable() { | 
 |   struct iovec iov; | 
 |   while (sequencer()->GetReadableRegion(&iov)) { | 
 |     if (spdy_session_->ProcessHeaderData(iov) != iov.iov_len) { | 
 |       // Error processing data. | 
 |       return; | 
 |     } | 
 |     sequencer()->MarkConsumed(iov.iov_len); | 
 |     MaybeReleaseSequencerBuffer(); | 
 |   } | 
 | } | 
 |  | 
 | void QuicHeadersStream::MaybeReleaseSequencerBuffer() { | 
 |   if (spdy_session_->ShouldReleaseHeadersStreamSequencerBuffer()) { | 
 |     sequencer()->ReleaseBufferIfEmpty(); | 
 |   } | 
 | } | 
 |  | 
 | bool QuicHeadersStream::OnStreamFrameAcked(QuicStreamOffset offset, | 
 |                                            QuicByteCount data_length, | 
 |                                            bool fin_acked, | 
 |                                            QuicTime::Delta ack_delay_time, | 
 |                                            QuicByteCount* newly_acked_length) { | 
 |   QuicIntervalSet<QuicStreamOffset> newly_acked(offset, offset + data_length); | 
 |   newly_acked.Difference(bytes_acked()); | 
 |   for (const auto& acked : newly_acked) { | 
 |     QuicStreamOffset acked_offset = acked.min(); | 
 |     QuicByteCount acked_length = acked.max() - acked.min(); | 
 |     for (CompressedHeaderInfo& header : unacked_headers_) { | 
 |       if (acked_offset < header.headers_stream_offset) { | 
 |         // This header frame offset belongs to headers with smaller offset, stop | 
 |         // processing. | 
 |         break; | 
 |       } | 
 |  | 
 |       if (acked_offset >= header.headers_stream_offset + header.full_length) { | 
 |         // This header frame belongs to headers with larger offset. | 
 |         continue; | 
 |       } | 
 |  | 
 |       QuicByteCount header_offset = acked_offset - header.headers_stream_offset; | 
 |       QuicByteCount header_length = | 
 |           std::min(acked_length, header.full_length - header_offset); | 
 |  | 
 |       if (header.unacked_length < header_length) { | 
 |         QUIC_BUG << "Unsent stream data is acked. unacked_length: " | 
 |                  << header.unacked_length << " acked_length: " << header_length; | 
 |         CloseConnectionWithDetails(QUIC_INTERNAL_ERROR, | 
 |                                    "Unsent stream data is acked"); | 
 |         return false; | 
 |       } | 
 |       if (header.ack_listener != nullptr && header_length > 0) { | 
 |         header.ack_listener->OnPacketAcked(header_length, ack_delay_time); | 
 |       } | 
 |       header.unacked_length -= header_length; | 
 |       acked_offset += header_length; | 
 |       acked_length -= header_length; | 
 |     } | 
 |   } | 
 |   // Remove headers which are fully acked. Please note, header frames can be | 
 |   // acked out of order, but unacked_headers_ is cleaned up in order. | 
 |   while (!unacked_headers_.empty() && | 
 |          unacked_headers_.front().unacked_length == 0) { | 
 |     unacked_headers_.pop_front(); | 
 |   } | 
 |   return QuicStream::OnStreamFrameAcked(offset, data_length, fin_acked, | 
 |                                         ack_delay_time, newly_acked_length); | 
 | } | 
 |  | 
 | void QuicHeadersStream::OnStreamFrameRetransmitted(QuicStreamOffset offset, | 
 |                                                    QuicByteCount data_length, | 
 |                                                    bool /*fin_retransmitted*/) { | 
 |   QuicStream::OnStreamFrameRetransmitted(offset, data_length, false); | 
 |   for (CompressedHeaderInfo& header : unacked_headers_) { | 
 |     if (offset < header.headers_stream_offset) { | 
 |       // This header frame offset belongs to headers with smaller offset, stop | 
 |       // processing. | 
 |       break; | 
 |     } | 
 |  | 
 |     if (offset >= header.headers_stream_offset + header.full_length) { | 
 |       // This header frame belongs to headers with larger offset. | 
 |       continue; | 
 |     } | 
 |  | 
 |     QuicByteCount header_offset = offset - header.headers_stream_offset; | 
 |     QuicByteCount retransmitted_length = | 
 |         std::min(data_length, header.full_length - header_offset); | 
 |     if (header.ack_listener != nullptr && retransmitted_length > 0) { | 
 |       header.ack_listener->OnPacketRetransmitted(retransmitted_length); | 
 |     } | 
 |     offset += retransmitted_length; | 
 |     data_length -= retransmitted_length; | 
 |   } | 
 | } | 
 |  | 
 | void QuicHeadersStream::OnDataBuffered( | 
 |     QuicStreamOffset offset, | 
 |     QuicByteCount data_length, | 
 |     const QuicReferenceCountedPointer<QuicAckListenerInterface>& ack_listener) { | 
 |   // Populate unacked_headers_. | 
 |   if (!unacked_headers_.empty() && | 
 |       (offset == unacked_headers_.back().headers_stream_offset + | 
 |                      unacked_headers_.back().full_length) && | 
 |       ack_listener == unacked_headers_.back().ack_listener) { | 
 |     // Try to combine with latest inserted entry if they belong to the same | 
 |     // header (i.e., having contiguous offset and the same ack listener). | 
 |     unacked_headers_.back().full_length += data_length; | 
 |     unacked_headers_.back().unacked_length += data_length; | 
 |   } else { | 
 |     unacked_headers_.push_back( | 
 |         CompressedHeaderInfo(offset, data_length, ack_listener)); | 
 |   } | 
 | } | 
 |  | 
 | }  // namespace quic |