| Index: net/http2/hpack/decoder/hpack_decoder_state.cc
|
| diff --git a/net/http2/hpack/decoder/hpack_decoder_state.cc b/net/http2/hpack/decoder/hpack_decoder_state.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..9fd1bb1f48177b1d97d0f8320ca2e0952d41c82c
|
| --- /dev/null
|
| +++ b/net/http2/hpack/decoder/hpack_decoder_state.cc
|
| @@ -0,0 +1,215 @@
|
| +// Copyright 2016 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/http2/hpack/decoder/hpack_decoder_state.h"
|
| +
|
| +#include <algorithm>
|
| +#include <utility>
|
| +
|
| +#include "base/logging.h"
|
| +
|
| +using base::StringPiece;
|
| +
|
| +namespace net {
|
| +namespace {
|
| +
|
| +HpackString ExtractHpackString(HpackDecoderStringBuffer* string_buffer) {
|
| + if (string_buffer->IsBuffered()) {
|
| + return HpackString(string_buffer->ReleaseString());
|
| + } else {
|
| + auto result = HpackString(string_buffer->str());
|
| + string_buffer->Reset();
|
| + return result;
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +HpackDecoderState::HpackDecoderState(HpackDecoderListener* listener)
|
| + : listener_(listener),
|
| + final_header_table_size_(Http2SettingsInfo::DefaultHeaderTableSize()),
|
| + lowest_header_table_size_(final_header_table_size_),
|
| + require_dynamic_table_size_update_(false),
|
| + allow_dynamic_table_size_update_(true),
|
| + saw_dynamic_table_size_update_(false),
|
| + error_detected_(false) {
|
| + CHECK(listener);
|
| +}
|
| +HpackDecoderState::~HpackDecoderState() {}
|
| +
|
| +void HpackDecoderState::set_listener(HpackDecoderListener* listener) {
|
| + CHECK(listener);
|
| + listener_ = listener;
|
| +}
|
| +
|
| +void HpackDecoderState::ApplyHeaderTableSizeSetting(
|
| + uint32_t header_table_size) {
|
| + DVLOG(2) << "HpackDecoderState::ApplyHeaderTableSizeSetting("
|
| + << header_table_size << ")";
|
| + DCHECK_LE(lowest_header_table_size_, final_header_table_size_);
|
| + if (header_table_size < lowest_header_table_size_) {
|
| + lowest_header_table_size_ = header_table_size;
|
| + }
|
| + final_header_table_size_ = header_table_size;
|
| + DVLOG(2) << "low water mark: " << lowest_header_table_size_;
|
| + DVLOG(2) << "final limit: " << final_header_table_size_;
|
| +}
|
| +
|
| +// Called to notify this object that we're starting to decode an HPACK block
|
| +// (e.g. a HEADERS or PUSH_PROMISE frame's header has been decoded).
|
| +void HpackDecoderState::OnHeaderBlockStart() {
|
| + DVLOG(2) << "HpackDecoderState::OnHeaderBlockStart";
|
| + // This instance can't be reused after an error has been detected, as we must
|
| + // assume that the encoder and decoder compression states are no longer
|
| + // synchronized.
|
| + DCHECK(!error_detected_);
|
| + DCHECK_LE(lowest_header_table_size_, final_header_table_size_);
|
| + allow_dynamic_table_size_update_ = true;
|
| + saw_dynamic_table_size_update_ = false;
|
| + // If the peer has acknowledged a HEADER_TABLE_SIZE smaller than that which
|
| + // its HPACK encoder has been using, then the next HPACK block it sends MUST
|
| + // start with a Dynamic Table Size Update entry that is at least as low as
|
| + // lowest_header_table_size_. That may be followed by another as great as
|
| + // final_header_table_size_, if those are different.
|
| + require_dynamic_table_size_update_ =
|
| + (lowest_header_table_size_ <
|
| + decoder_tables_.current_header_table_size() ||
|
| + final_header_table_size_ < decoder_tables_.header_table_size_limit());
|
| + DVLOG(2) << "HpackDecoderState::OnHeaderListStart "
|
| + << "require_dynamic_table_size_update_="
|
| + << require_dynamic_table_size_update_;
|
| + listener_->OnHeaderListStart();
|
| +}
|
| +
|
| +void HpackDecoderState::OnIndexedHeader(size_t index) {
|
| + DVLOG(2) << "HpackDecoderState::OnIndexedHeader: " << index;
|
| + if (error_detected_) {
|
| + return;
|
| + }
|
| + if (require_dynamic_table_size_update_) {
|
| + ReportError("Missing dynamic table size update.");
|
| + return;
|
| + }
|
| + allow_dynamic_table_size_update_ = false;
|
| + const HpackStringPair* entry = decoder_tables_.Lookup(index);
|
| + if (entry != nullptr) {
|
| + listener_->OnHeader(HpackEntryType::kIndexedHeader, entry->name,
|
| + entry->value);
|
| + } else {
|
| + ReportError("Invalid index.");
|
| + }
|
| +}
|
| +
|
| +void HpackDecoderState::OnNameIndexAndLiteralValue(
|
| + HpackEntryType entry_type,
|
| + size_t name_index,
|
| + HpackDecoderStringBuffer* value_buffer) {
|
| + DVLOG(2) << "HpackDecoderState::OnNameIndexAndLiteralValue " << entry_type
|
| + << ", " << name_index << ", " << value_buffer->str();
|
| + if (error_detected_) {
|
| + return;
|
| + }
|
| + if (require_dynamic_table_size_update_) {
|
| + ReportError("Missing dynamic table size update.");
|
| + return;
|
| + }
|
| + allow_dynamic_table_size_update_ = false;
|
| + const HpackStringPair* entry = decoder_tables_.Lookup(name_index);
|
| + if (entry != nullptr) {
|
| + HpackString value(ExtractHpackString(value_buffer));
|
| + listener_->OnHeader(entry_type, entry->name, value);
|
| + if (entry_type == HpackEntryType::kIndexedLiteralHeader) {
|
| + decoder_tables_.Insert(entry->name, value);
|
| + }
|
| + } else {
|
| + ReportError("Invalid name index.");
|
| + }
|
| +}
|
| +
|
| +void HpackDecoderState::OnLiteralNameAndValue(
|
| + HpackEntryType entry_type,
|
| + HpackDecoderStringBuffer* name_buffer,
|
| + HpackDecoderStringBuffer* value_buffer) {
|
| + DVLOG(2) << "HpackDecoderState::OnLiteralNameAndValue " << entry_type << ", "
|
| + << name_buffer->str() << ", " << value_buffer->str();
|
| + if (error_detected_) {
|
| + return;
|
| + }
|
| + if (require_dynamic_table_size_update_) {
|
| + ReportError("Missing dynamic table size update.");
|
| + return;
|
| + }
|
| + allow_dynamic_table_size_update_ = false;
|
| + HpackString name(ExtractHpackString(name_buffer));
|
| + HpackString value(ExtractHpackString(value_buffer));
|
| + listener_->OnHeader(entry_type, name, value);
|
| + if (entry_type == HpackEntryType::kIndexedLiteralHeader) {
|
| + decoder_tables_.Insert(name, value);
|
| + }
|
| +}
|
| +
|
| +void HpackDecoderState::OnDynamicTableSizeUpdate(size_t size_limit) {
|
| + DVLOG(2) << "HpackDecoderState::OnDynamicTableSizeUpdate " << size_limit;
|
| + if (error_detected_) {
|
| + return;
|
| + }
|
| + DCHECK_LE(lowest_header_table_size_, final_header_table_size_);
|
| + if (!allow_dynamic_table_size_update_) {
|
| + // At most two dynamic table size updates allowed at the start, and not
|
| + // after a header.
|
| + ReportError("Dynamic table size update not allowed.");
|
| + return;
|
| + }
|
| + if (require_dynamic_table_size_update_) {
|
| + // The new size must not be greater than the low water mark.
|
| + if (size_limit > lowest_header_table_size_) {
|
| + ReportError("Initial dynamic table size update is above low water mark.");
|
| + return;
|
| + }
|
| + require_dynamic_table_size_update_ = false;
|
| + } else if (size_limit > final_header_table_size_) {
|
| + // The new size must not be greater than the final max header table size
|
| + // that the peer acknowledged.
|
| + ReportError("Dynamic table size update is above acknowledged setting.");
|
| + return;
|
| + }
|
| + decoder_tables_.DynamicTableSizeUpdate(size_limit);
|
| + if (saw_dynamic_table_size_update_) {
|
| + allow_dynamic_table_size_update_ = false;
|
| + } else {
|
| + saw_dynamic_table_size_update_ = true;
|
| + }
|
| + // We no longer need to keep an eye out for a lower header table size.
|
| + lowest_header_table_size_ = final_header_table_size_;
|
| +}
|
| +
|
| +void HpackDecoderState::OnHpackDecodeError(StringPiece error_message) {
|
| + DVLOG(2) << "HpackDecoderState::OnHpackDecodeError " << error_message;
|
| + if (!error_detected_) {
|
| + ReportError(error_message);
|
| + }
|
| +}
|
| +
|
| +void HpackDecoderState::OnHeaderBlockEnd() {
|
| + DVLOG(2) << "HpackDecoderState::OnHeaderBlockEnd";
|
| + if (error_detected_) {
|
| + return;
|
| + }
|
| + if (require_dynamic_table_size_update_) {
|
| + // Apparently the HPACK block was empty, but we needed it to contain at
|
| + // least 1 dynamic table size update.
|
| + ReportError("Missing dynamic table size update.");
|
| + } else {
|
| + listener_->OnHeaderListEnd();
|
| + }
|
| +}
|
| +
|
| +void HpackDecoderState::ReportError(StringPiece error_message) {
|
| + if (!error_detected_) {
|
| + listener_->OnHeaderErrorDetected(error_message);
|
| + error_detected_ = true;
|
| + }
|
| +}
|
| +
|
| +} // namespace net
|
|
|