blob: fa54464ed94124b9b1b6aa75b9e0dcf3bd004850 [file] [log] [blame]
// Copyright 2017 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/spdy/core/hpack/hpack_decoder_adapter.h"
#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_estimate_memory_usage.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_flags.h"
#include "net/third_party/quiche/src/spdy/platform/api/spdy_logging.h"
using ::http2::DecodeBuffer;
using ::http2::HpackString;
namespace spdy {
namespace {
const size_t kMaxDecodeBufferSizeBytes = 32 * 1024; // 32 KB
} // namespace
HpackDecoderAdapter::HpackDecoderAdapter()
: hpack_decoder_(&listener_adapter_, kMaxDecodeBufferSizeBytes),
max_decode_buffer_size_bytes_(kMaxDecodeBufferSizeBytes),
max_header_block_bytes_(0),
header_block_started_(false),
error_(http2::HpackDecodingError::kOk) {}
HpackDecoderAdapter::~HpackDecoderAdapter() = default;
void HpackDecoderAdapter::ApplyHeaderTableSizeSetting(size_t size_setting) {
SPDY_DVLOG(2) << "HpackDecoderAdapter::ApplyHeaderTableSizeSetting";
hpack_decoder_.ApplyHeaderTableSizeSetting(size_setting);
}
void HpackDecoderAdapter::HandleControlFrameHeadersStart(
SpdyHeadersHandlerInterface* handler) {
SPDY_DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersStart";
DCHECK(!header_block_started_);
listener_adapter_.set_handler(handler);
}
bool HpackDecoderAdapter::HandleControlFrameHeadersData(
const char* headers_data,
size_t headers_data_length) {
SPDY_DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersData: len="
<< headers_data_length;
if (!header_block_started_) {
// Initialize the decoding process here rather than in
// HandleControlFrameHeadersStart because that method is not always called.
header_block_started_ = true;
if (!hpack_decoder_.StartDecodingBlock()) {
header_block_started_ = false;
error_ = hpack_decoder_.error();
detailed_error_ = hpack_decoder_.detailed_error();
return false;
}
}
// Sometimes we get a call with headers_data==nullptr and
// headers_data_length==0, in which case we need to avoid creating
// a DecodeBuffer, which would otherwise complain.
if (headers_data_length > 0) {
DCHECK_NE(headers_data, nullptr);
if (headers_data_length > max_decode_buffer_size_bytes_) {
SPDY_DVLOG(1) << "max_decode_buffer_size_bytes_ < headers_data_length: "
<< max_decode_buffer_size_bytes_ << " < "
<< headers_data_length;
error_ = http2::HpackDecodingError::kFragmentTooLong;
detailed_error_ = "";
return false;
}
listener_adapter_.AddToTotalHpackBytes(headers_data_length);
if (max_header_block_bytes_ != 0 &&
listener_adapter_.total_hpack_bytes() > max_header_block_bytes_) {
error_ = http2::HpackDecodingError::kCompressedHeaderSizeExceedsLimit;
detailed_error_ = "";
return false;
}
http2::DecodeBuffer db(headers_data, headers_data_length);
bool ok = hpack_decoder_.DecodeFragment(&db);
DCHECK(!ok || db.Empty()) << "Remaining=" << db.Remaining();
if (!ok) {
error_ = hpack_decoder_.error();
detailed_error_ = hpack_decoder_.detailed_error();
}
return ok;
}
return true;
}
bool HpackDecoderAdapter::HandleControlFrameHeadersComplete(
size_t* compressed_len) {
SPDY_DVLOG(2) << "HpackDecoderAdapter::HandleControlFrameHeadersComplete";
if (compressed_len != nullptr) {
*compressed_len = listener_adapter_.total_hpack_bytes();
}
if (!hpack_decoder_.EndDecodingBlock()) {
SPDY_DVLOG(3) << "EndDecodingBlock returned false";
error_ = hpack_decoder_.error();
detailed_error_ = hpack_decoder_.detailed_error();
return false;
}
header_block_started_ = false;
return true;
}
const SpdyHeaderBlock& HpackDecoderAdapter::decoded_block() const {
return listener_adapter_.decoded_block();
}
void HpackDecoderAdapter::SetHeaderTableDebugVisitor(
std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) {
SPDY_DVLOG(2) << "HpackDecoderAdapter::SetHeaderTableDebugVisitor";
if (visitor != nullptr) {
listener_adapter_.SetHeaderTableDebugVisitor(std::move(visitor));
hpack_decoder_.set_tables_debug_listener(&listener_adapter_);
} else {
hpack_decoder_.set_tables_debug_listener(nullptr);
listener_adapter_.SetHeaderTableDebugVisitor(nullptr);
}
}
void HpackDecoderAdapter::set_max_decode_buffer_size_bytes(
size_t max_decode_buffer_size_bytes) {
SPDY_DVLOG(2) << "HpackDecoderAdapter::set_max_decode_buffer_size_bytes";
max_decode_buffer_size_bytes_ = max_decode_buffer_size_bytes;
hpack_decoder_.set_max_string_size_bytes(max_decode_buffer_size_bytes);
}
void HpackDecoderAdapter::set_max_header_block_bytes(
size_t max_header_block_bytes) {
max_header_block_bytes_ = max_header_block_bytes;
}
size_t HpackDecoderAdapter::EstimateMemoryUsage() const {
return SpdyEstimateMemoryUsage(hpack_decoder_);
}
HpackDecoderAdapter::ListenerAdapter::ListenerAdapter() : handler_(nullptr) {}
HpackDecoderAdapter::ListenerAdapter::~ListenerAdapter() = default;
void HpackDecoderAdapter::ListenerAdapter::set_handler(
SpdyHeadersHandlerInterface* handler) {
handler_ = handler;
}
void HpackDecoderAdapter::ListenerAdapter::SetHeaderTableDebugVisitor(
std::unique_ptr<HpackHeaderTable::DebugVisitorInterface> visitor) {
visitor_ = std::move(visitor);
}
void HpackDecoderAdapter::ListenerAdapter::OnHeaderListStart() {
SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeaderListStart";
total_hpack_bytes_ = 0;
total_uncompressed_bytes_ = 0;
decoded_block_.clear();
if (handler_ != nullptr) {
handler_->OnHeaderBlockStart();
}
}
void HpackDecoderAdapter::ListenerAdapter::OnHeader(const HpackString& name,
const HpackString& value) {
SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeader:\n name: "
<< name << "\n value: " << value;
total_uncompressed_bytes_ += name.size() + value.size();
if (handler_ == nullptr) {
SPDY_DVLOG(3) << "Adding to decoded_block";
decoded_block_.AppendValueOrAddHeader(name.ToStringPiece(),
value.ToStringPiece());
} else {
SPDY_DVLOG(3) << "Passing to handler";
handler_->OnHeader(name.ToStringPiece(), value.ToStringPiece());
}
}
void HpackDecoderAdapter::ListenerAdapter::OnHeaderListEnd() {
SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnHeaderListEnd";
// We don't clear the SpdyHeaderBlock here to allow access to it until the
// next HPACK block is decoded.
if (handler_ != nullptr) {
handler_->OnHeaderBlockEnd(total_uncompressed_bytes_, total_hpack_bytes_);
handler_ = nullptr;
}
}
void HpackDecoderAdapter::ListenerAdapter::OnHeaderErrorDetected(
absl::string_view error_message) {
SPDY_VLOG(1) << error_message;
}
int64_t HpackDecoderAdapter::ListenerAdapter::OnEntryInserted(
const http2::HpackStringPair& entry,
size_t insert_count) {
SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnEntryInserted: "
<< entry << ", insert_count=" << insert_count;
if (visitor_ == nullptr) {
return 0;
}
HpackEntry hpack_entry(entry.name.ToStringPiece(),
entry.value.ToStringPiece(),
/*is_static*/ false, insert_count);
int64_t time_added = visitor_->OnNewEntry(hpack_entry);
SPDY_DVLOG(2)
<< "HpackDecoderAdapter::ListenerAdapter::OnEntryInserted: time_added="
<< time_added;
return time_added;
}
void HpackDecoderAdapter::ListenerAdapter::OnUseEntry(
const http2::HpackStringPair& entry,
size_t insert_count,
int64_t time_added) {
SPDY_DVLOG(2) << "HpackDecoderAdapter::ListenerAdapter::OnUseEntry: " << entry
<< ", insert_count=" << insert_count
<< ", time_added=" << time_added;
if (visitor_ != nullptr) {
HpackEntry hpack_entry(entry.name.ToStringPiece(),
entry.value.ToStringPiece(), /*is_static*/ false,
insert_count);
hpack_entry.set_time_added(time_added);
visitor_->OnUseEntry(hpack_entry);
}
}
} // namespace spdy