blob: 41433ff9af1508fb92b6d8a6be37cbd85ced2fca [file] [log] [blame]
// Copyright (c) 2018 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/qpack/qpack_progressive_encoder.h"
#include "base/logging.h"
#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string.h"
#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
namespace quic {
QpackProgressiveEncoder::QpackProgressiveEncoder(
QuicStreamId stream_id,
QpackHeaderTable* header_table,
QpackEncoderStreamSender* encoder_stream_sender,
const spdy::SpdyHeaderBlock* header_list)
: stream_id_(stream_id),
header_table_(header_table),
encoder_stream_sender_(encoder_stream_sender),
header_list_(header_list),
header_list_iterator_(header_list_->begin()),
prefix_encoded_(false) {
// TODO(bnc): Use |stream_id_| for dynamic table entry management, and
// remove this dummy DCHECK.
DCHECK_LE(0u, stream_id_);
DCHECK(header_table_);
DCHECK(encoder_stream_sender_);
DCHECK(header_list_);
}
bool QpackProgressiveEncoder::HasNext() const {
return header_list_iterator_ != header_list_->end() || !prefix_encoded_;
}
void QpackProgressiveEncoder::Next(size_t max_encoded_bytes,
QuicString* output) {
DCHECK_NE(0u, max_encoded_bytes);
DCHECK(HasNext());
// Since QpackInstructionEncoder::Next() does not indicate the number of bytes
// written, save the maximum new size of |*output|.
const size_t max_length = output->size() + max_encoded_bytes;
DCHECK_LT(output->size(), max_length);
if (!prefix_encoded_ && !instruction_encoder_.HasNext()) {
// TODO(bnc): Implement dynamic entries and set Required Insert Count and
// Delta Base accordingly.
instruction_encoder_.set_varint(0);
instruction_encoder_.set_varint2(0);
instruction_encoder_.set_s_bit(false);
instruction_encoder_.Encode(QpackPrefixInstruction());
DCHECK(instruction_encoder_.HasNext());
}
do {
// Call QpackInstructionEncoder::Encode for |*header_list_iterator_| if it
// has not been called yet.
if (!instruction_encoder_.HasNext()) {
DCHECK(prefix_encoded_);
// Even after |name| and |value| go out of scope, copies of these
// QuicStringPieces retained by QpackInstructionEncoder are still valid as
// long as |header_list_| is valid.
QuicStringPiece name = header_list_iterator_->first;
QuicStringPiece value = header_list_iterator_->second;
// |is_static| and |index| are saved by QpackInstructionEncoder by value,
// there are no lifetime concerns.
bool is_static;
uint64_t index;
auto match_type =
header_table_->FindHeaderField(name, value, &is_static, &index);
switch (match_type) {
case QpackHeaderTable::MatchType::kNameAndValue:
DCHECK(is_static) << "Dynamic table entries not supported yet.";
instruction_encoder_.set_s_bit(is_static);
instruction_encoder_.set_varint(index);
instruction_encoder_.Encode(QpackIndexedHeaderFieldInstruction());
break;
case QpackHeaderTable::MatchType::kName:
DCHECK(is_static) << "Dynamic table entries not supported yet.";
instruction_encoder_.set_s_bit(is_static);
instruction_encoder_.set_varint(index);
instruction_encoder_.set_value(value);
instruction_encoder_.Encode(
QpackLiteralHeaderFieldNameReferenceInstruction());
break;
case QpackHeaderTable::MatchType::kNoMatch:
instruction_encoder_.set_name(name);
instruction_encoder_.set_value(value);
instruction_encoder_.Encode(QpackLiteralHeaderFieldInstruction());
break;
}
}
DCHECK(instruction_encoder_.HasNext());
instruction_encoder_.Next(max_length - output->size(), output);
if (instruction_encoder_.HasNext()) {
// There was not enough room to completely encode current header field.
DCHECK_EQ(output->size(), max_length);
return;
}
// It is possible that the output buffer was just large enough for encoding
// the current header field, hence equality is allowed here.
DCHECK_LE(output->size(), max_length);
if (prefix_encoded_) {
// Move on to the next header field.
++header_list_iterator_;
} else {
// Mark prefix as encoded.
prefix_encoded_ = true;
}
} while (HasNext() && output->size() < max_length);
}
} // namespace quic