blob: 9c99ab7e41e569ec5affd1965303c12fe0336fae [file] [log] [blame] [edit]
// 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