| // 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 <string> | |
| #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_logging.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_); | |
| } | |
| bool QpackProgressiveEncoder::HasNext() const { | |
| return header_list_iterator_ != header_list_.end() || !prefix_encoded_; | |
| } | |
| void QpackProgressiveEncoder::Next(size_t max_encoded_bytes, | |
| std::string* 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 |