Project import generated by Copybara.
PiperOrigin-RevId: 237361882
Change-Id: I109a68f44db867b20f8c6a7732b0ce657133e52a
diff --git a/quic/core/http/quic_headers_stream.cc b/quic/core/http/quic_headers_stream.cc
new file mode 100644
index 0000000..9c99ab7
--- /dev/null
+++ b/quic/core/http/quic_headers_stream.cc
@@ -0,0 +1,157 @@
+// 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->connection()->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