mirror of
https://github.com/klzgrad/naiveproxy.git
synced 2024-12-05 03:36:08 +03:00
220 lines
7.8 KiB
C++
220 lines
7.8 KiB
C++
|
// 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 "base/logging.h"
|
||
|
#include "net/http2/hpack/hpack_string.h"
|
||
|
#include "net/http2/http2_constants.h"
|
||
|
|
||
|
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_tables_debug_listener(
|
||
|
HpackDecoderTablesDebugListener* debug_listener) {
|
||
|
decoder_tables_.set_debug_listener(debug_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
|
||
|
<< ", required="
|
||
|
<< (require_dynamic_table_size_update_ ? "true" : "false")
|
||
|
<< ", allowed="
|
||
|
<< (allow_dynamic_table_size_update_ ? "true" : "false");
|
||
|
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(Http2StringPiece 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(Http2StringPiece error_message) {
|
||
|
DVLOG(2) << "HpackDecoderState::ReportError is new="
|
||
|
<< (!error_detected_ ? "true" : "false")
|
||
|
<< ", error_message: " << error_message;
|
||
|
if (!error_detected_) {
|
||
|
listener_->OnHeaderErrorDetected(error_message);
|
||
|
error_detected_ = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
} // namespace net
|