// 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 "quic/core/qpack/qpack_progressive_decoder.h" #include #include #include #include "absl/strings/string_view.h" #include "quic/core/qpack/qpack_index_conversions.h" #include "quic/core/qpack/qpack_instructions.h" #include "quic/core/qpack/qpack_required_insert_count.h" #include "quic/platform/api/quic_logging.h" namespace quic { QpackProgressiveDecoder::QpackProgressiveDecoder( QuicStreamId stream_id, BlockedStreamLimitEnforcer* enforcer, DecodingCompletedVisitor* visitor, QpackDecoderHeaderTable* header_table, HeadersHandlerInterface* handler) : stream_id_(stream_id), prefix_decoder_( std::make_unique(QpackPrefixLanguage(), this)), instruction_decoder_(QpackRequestStreamLanguage(), this), enforcer_(enforcer), visitor_(visitor), header_table_(header_table), handler_(handler), required_insert_count_(0), base_(0), required_insert_count_so_far_(0), prefix_decoded_(false), blocked_(false), decoding_(true), error_detected_(false), cancelled_(false) {} QpackProgressiveDecoder::~QpackProgressiveDecoder() { if (blocked_ && !cancelled_) { header_table_->UnregisterObserver(required_insert_count_, this); } } void QpackProgressiveDecoder::Decode(absl::string_view data) { QUICHE_DCHECK(decoding_); if (data.empty() || error_detected_) { return; } // Decode prefix byte by byte until the first (and only) instruction is // decoded. while (!prefix_decoded_) { QUICHE_DCHECK(!blocked_); if (!prefix_decoder_->Decode(data.substr(0, 1))) { return; } // |prefix_decoder_->Decode()| must return false if an error is detected. QUICHE_DCHECK(!error_detected_); data = data.substr(1); if (data.empty()) { return; } } if (blocked_) { buffer_.append(data.data(), data.size()); } else { QUICHE_DCHECK(buffer_.empty()); instruction_decoder_.Decode(data); } } void QpackProgressiveDecoder::EndHeaderBlock() { QUICHE_DCHECK(decoding_); decoding_ = false; if (!blocked_) { FinishDecoding(); } } void QpackProgressiveDecoder::OnError(absl::string_view error_message) { QUICHE_DCHECK(!error_detected_); error_detected_ = true; // Might destroy |this|. handler_->OnDecodingErrorDetected(error_message); } bool QpackProgressiveDecoder::OnInstructionDecoded( const QpackInstruction* instruction) { if (instruction == QpackPrefixInstruction()) { return DoPrefixInstruction(); } QUICHE_DCHECK(prefix_decoded_); QUICHE_DCHECK_LE(required_insert_count_, header_table_->inserted_entry_count()); if (instruction == QpackIndexedHeaderFieldInstruction()) { return DoIndexedHeaderFieldInstruction(); } if (instruction == QpackIndexedHeaderFieldPostBaseInstruction()) { return DoIndexedHeaderFieldPostBaseInstruction(); } if (instruction == QpackLiteralHeaderFieldNameReferenceInstruction()) { return DoLiteralHeaderFieldNameReferenceInstruction(); } if (instruction == QpackLiteralHeaderFieldPostBaseInstruction()) { return DoLiteralHeaderFieldPostBaseInstruction(); } QUICHE_DCHECK_EQ(instruction, QpackLiteralHeaderFieldInstruction()); return DoLiteralHeaderFieldInstruction(); } void QpackProgressiveDecoder::OnInstructionDecodingError( QpackInstructionDecoder::ErrorCode /* error_code */, absl::string_view error_message) { // Ignore |error_code|, because header block decoding errors trigger a // RESET_STREAM frame which cannot carry an error code more granular than // QPACK_DECOMPRESSION_FAILED. OnError(error_message); } void QpackProgressiveDecoder::OnInsertCountReachedThreshold() { QUICHE_DCHECK(blocked_); // Clear |blocked_| before calling instruction_decoder_.Decode() below, // because that might destroy |this| and ~QpackProgressiveDecoder() needs to // know not to call UnregisterObserver(). blocked_ = false; enforcer_->OnStreamUnblocked(stream_id_); if (!buffer_.empty()) { std::string buffer(std::move(buffer_)); buffer_.clear(); if (!instruction_decoder_.Decode(buffer)) { // |this| might be destroyed. return; } } if (!decoding_) { FinishDecoding(); } } void QpackProgressiveDecoder::Cancel() { cancelled_ = true; } bool QpackProgressiveDecoder::DoIndexedHeaderFieldInstruction() { if (!instruction_decoder_.s_bit()) { uint64_t absolute_index; if (!QpackRequestStreamRelativeIndexToAbsoluteIndex( instruction_decoder_.varint(), base_, &absolute_index)) { OnError("Invalid relative index."); return false; } if (absolute_index >= required_insert_count_) { OnError("Absolute Index must be smaller than Required Insert Count."); return false; } QUICHE_DCHECK_LT(absolute_index, std::numeric_limits::max()); required_insert_count_so_far_ = std::max(required_insert_count_so_far_, absolute_index + 1); auto entry = header_table_->LookupEntry(/* is_static = */ false, absolute_index); if (!entry) { OnError("Dynamic table entry already evicted."); return false; } header_table_->set_dynamic_table_entry_referenced(); handler_->OnHeaderDecoded(entry->name(), entry->value()); return true; } auto entry = header_table_->LookupEntry(/* is_static = */ true, instruction_decoder_.varint()); if (!entry) { OnError("Static table entry not found."); return false; } handler_->OnHeaderDecoded(entry->name(), entry->value()); return true; } bool QpackProgressiveDecoder::DoIndexedHeaderFieldPostBaseInstruction() { uint64_t absolute_index; if (!QpackPostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), base_, &absolute_index)) { OnError("Invalid post-base index."); return false; } if (absolute_index >= required_insert_count_) { OnError("Absolute Index must be smaller than Required Insert Count."); return false; } QUICHE_DCHECK_LT(absolute_index, std::numeric_limits::max()); required_insert_count_so_far_ = std::max(required_insert_count_so_far_, absolute_index + 1); auto entry = header_table_->LookupEntry(/* is_static = */ false, absolute_index); if (!entry) { OnError("Dynamic table entry already evicted."); return false; } header_table_->set_dynamic_table_entry_referenced(); handler_->OnHeaderDecoded(entry->name(), entry->value()); return true; } bool QpackProgressiveDecoder::DoLiteralHeaderFieldNameReferenceInstruction() { if (!instruction_decoder_.s_bit()) { uint64_t absolute_index; if (!QpackRequestStreamRelativeIndexToAbsoluteIndex( instruction_decoder_.varint(), base_, &absolute_index)) { OnError("Invalid relative index."); return false; } if (absolute_index >= required_insert_count_) { OnError("Absolute Index must be smaller than Required Insert Count."); return false; } QUICHE_DCHECK_LT(absolute_index, std::numeric_limits::max()); required_insert_count_so_far_ = std::max(required_insert_count_so_far_, absolute_index + 1); auto entry = header_table_->LookupEntry(/* is_static = */ false, absolute_index); if (!entry) { OnError("Dynamic table entry already evicted."); return false; } header_table_->set_dynamic_table_entry_referenced(); handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); return true; } auto entry = header_table_->LookupEntry(/* is_static = */ true, instruction_decoder_.varint()); if (!entry) { OnError("Static table entry not found."); return false; } handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); return true; } bool QpackProgressiveDecoder::DoLiteralHeaderFieldPostBaseInstruction() { uint64_t absolute_index; if (!QpackPostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(), base_, &absolute_index)) { OnError("Invalid post-base index."); return false; } if (absolute_index >= required_insert_count_) { OnError("Absolute Index must be smaller than Required Insert Count."); return false; } QUICHE_DCHECK_LT(absolute_index, std::numeric_limits::max()); required_insert_count_so_far_ = std::max(required_insert_count_so_far_, absolute_index + 1); auto entry = header_table_->LookupEntry(/* is_static = */ false, absolute_index); if (!entry) { OnError("Dynamic table entry already evicted."); return false; } header_table_->set_dynamic_table_entry_referenced(); handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value()); return true; } bool QpackProgressiveDecoder::DoLiteralHeaderFieldInstruction() { handler_->OnHeaderDecoded(instruction_decoder_.name(), instruction_decoder_.value()); return true; } bool QpackProgressiveDecoder::DoPrefixInstruction() { QUICHE_DCHECK(!prefix_decoded_); if (!QpackDecodeRequiredInsertCount( prefix_decoder_->varint(), header_table_->max_entries(), header_table_->inserted_entry_count(), &required_insert_count_)) { OnError("Error decoding Required Insert Count."); return false; } const bool sign = prefix_decoder_->s_bit(); const uint64_t delta_base = prefix_decoder_->varint2(); if (!DeltaBaseToBase(sign, delta_base, &base_)) { OnError("Error calculating Base."); return false; } prefix_decoded_ = true; if (required_insert_count_ > header_table_->inserted_entry_count()) { if (!enforcer_->OnStreamBlocked(stream_id_)) { OnError("Limit on number of blocked streams exceeded."); return false; } blocked_ = true; header_table_->RegisterObserver(required_insert_count_, this); } return true; } void QpackProgressiveDecoder::FinishDecoding() { QUICHE_DCHECK(buffer_.empty()); QUICHE_DCHECK(!blocked_); QUICHE_DCHECK(!decoding_); if (error_detected_) { return; } if (!instruction_decoder_.AtInstructionBoundary()) { OnError("Incomplete header block."); return; } if (!prefix_decoded_) { OnError("Incomplete header data prefix."); return; } if (required_insert_count_ != required_insert_count_so_far_) { OnError("Required Insert Count too large."); return; } visitor_->OnDecodingCompleted(stream_id_, required_insert_count_); handler_->OnDecodingCompleted(); } bool QpackProgressiveDecoder::DeltaBaseToBase(bool sign, uint64_t delta_base, uint64_t* base) { if (sign) { if (delta_base == std::numeric_limits::max() || required_insert_count_ < delta_base + 1) { return false; } *base = required_insert_count_ - delta_base - 1; return true; } if (delta_base > std::numeric_limits::max() - required_insert_count_) { return false; } *base = required_insert_count_ + delta_base; return true; } } // namespace quic