diff options
Diffstat (limited to 'chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc')
-rw-r--r-- | chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc new file mode 100644 index 00000000000..96b9f066030 --- /dev/null +++ b/chromium/net/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.cc @@ -0,0 +1,267 @@ +// 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/third_party/quiche/src/http2/hpack/decoder/hpack_entry_decoder.h" + +#include <stddef.h> + +#include <cstdint> + +#include "base/logging.h" +#include "base/macros.h" +#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h" +#include "net/third_party/quiche/src/http2/platform/api/http2_macros.h" + +namespace http2 { +namespace { +// Converts calls from HpackStringDecoder when decoding a header name into the +// appropriate HpackEntryDecoderListener::OnName* calls. +class NameDecoderListener { + public: + explicit NameDecoderListener(HpackEntryDecoderListener* listener) + : listener_(listener) {} + bool OnStringStart(bool huffman_encoded, size_t len) { + listener_->OnNameStart(huffman_encoded, len); + return true; + } + void OnStringData(const char* data, size_t len) { + listener_->OnNameData(data, len); + } + void OnStringEnd() { listener_->OnNameEnd(); } + + private: + HpackEntryDecoderListener* listener_; +}; + +// Converts calls from HpackStringDecoder when decoding a header value into +// the appropriate HpackEntryDecoderListener::OnValue* calls. +class ValueDecoderListener { + public: + explicit ValueDecoderListener(HpackEntryDecoderListener* listener) + : listener_(listener) {} + bool OnStringStart(bool huffman_encoded, size_t len) { + listener_->OnValueStart(huffman_encoded, len); + return true; + } + void OnStringData(const char* data, size_t len) { + listener_->OnValueData(data, len); + } + void OnStringEnd() { listener_->OnValueEnd(); } + + private: + HpackEntryDecoderListener* listener_; +}; +} // namespace + +DecodeStatus HpackEntryDecoder::Start(DecodeBuffer* db, + HpackEntryDecoderListener* listener) { + DCHECK(db != nullptr); + DCHECK(listener != nullptr); + DCHECK(db->HasData()); + DecodeStatus status = entry_type_decoder_.Start(db); + switch (status) { + case DecodeStatus::kDecodeDone: + // The type of the entry and its varint fit into the current decode + // buffer. + if (entry_type_decoder_.entry_type() == HpackEntryType::kIndexedHeader) { + // The entry consists solely of the entry type and varint. + // This is by far the most common case in practice. + listener->OnIndexedHeader(entry_type_decoder_.varint()); + return DecodeStatus::kDecodeDone; + } + state_ = EntryDecoderState::kDecodedType; + return Resume(db, listener); + case DecodeStatus::kDecodeInProgress: + // Hit the end of the decode buffer before fully decoding + // the entry type and varint. + DCHECK_EQ(0u, db->Remaining()); + state_ = EntryDecoderState::kResumeDecodingType; + return status; + case DecodeStatus::kDecodeError: + // The varint must have been invalid (too long). + return status; + } + + HTTP2_BUG << "Unreachable"; + return DecodeStatus::kDecodeError; +} + +DecodeStatus HpackEntryDecoder::Resume(DecodeBuffer* db, + HpackEntryDecoderListener* listener) { + DCHECK(db != nullptr); + DCHECK(listener != nullptr); + + DecodeStatus status; + + do { + switch (state_) { + case EntryDecoderState::kResumeDecodingType: + // entry_type_decoder_ returned kDecodeInProgress when last called. + DVLOG(1) << "kResumeDecodingType: db->Remaining=" << db->Remaining(); + status = entry_type_decoder_.Resume(db); + if (status != DecodeStatus::kDecodeDone) { + return status; + } + state_ = EntryDecoderState::kDecodedType; + HTTP2_FALLTHROUGH; + + case EntryDecoderState::kDecodedType: + // entry_type_decoder_ returned kDecodeDone, now need to decide how + // to proceed. + DVLOG(1) << "kDecodedType: db->Remaining=" << db->Remaining(); + if (DispatchOnType(listener)) { + // All done. + return DecodeStatus::kDecodeDone; + } + continue; + + case EntryDecoderState::kStartDecodingName: + DVLOG(1) << "kStartDecodingName: db->Remaining=" << db->Remaining(); + { + NameDecoderListener ncb(listener); + status = string_decoder_.Start(db, &ncb); + } + if (status != DecodeStatus::kDecodeDone) { + // On the assumption that the status is kDecodeInProgress, set + // state_ accordingly; unnecessary if status is kDecodeError, but + // that will only happen if the varint encoding the name's length + // is too long. + state_ = EntryDecoderState::kResumeDecodingName; + return status; + } + state_ = EntryDecoderState::kStartDecodingValue; + HTTP2_FALLTHROUGH; + + case EntryDecoderState::kStartDecodingValue: + DVLOG(1) << "kStartDecodingValue: db->Remaining=" << db->Remaining(); + { + ValueDecoderListener vcb(listener); + status = string_decoder_.Start(db, &vcb); + } + if (status == DecodeStatus::kDecodeDone) { + // Done with decoding the literal value, so we've reached the + // end of the header entry. + return status; + } + // On the assumption that the status is kDecodeInProgress, set + // state_ accordingly; unnecessary if status is kDecodeError, but + // that will only happen if the varint encoding the value's length + // is too long. + state_ = EntryDecoderState::kResumeDecodingValue; + return status; + + case EntryDecoderState::kResumeDecodingName: + // The literal name was split across decode buffers. + DVLOG(1) << "kResumeDecodingName: db->Remaining=" << db->Remaining(); + { + NameDecoderListener ncb(listener); + status = string_decoder_.Resume(db, &ncb); + } + if (status != DecodeStatus::kDecodeDone) { + // On the assumption that the status is kDecodeInProgress, set + // state_ accordingly; unnecessary if status is kDecodeError, but + // that will only happen if the varint encoding the name's length + // is too long. + state_ = EntryDecoderState::kResumeDecodingName; + return status; + } + state_ = EntryDecoderState::kStartDecodingValue; + break; + + case EntryDecoderState::kResumeDecodingValue: + // The literal value was split across decode buffers. + DVLOG(1) << "kResumeDecodingValue: db->Remaining=" << db->Remaining(); + { + ValueDecoderListener vcb(listener); + status = string_decoder_.Resume(db, &vcb); + } + if (status == DecodeStatus::kDecodeDone) { + // Done with decoding the value, therefore the entry as a whole. + return status; + } + // On the assumption that the status is kDecodeInProgress, set + // state_ accordingly; unnecessary if status is kDecodeError, but + // that will only happen if the varint encoding the value's length + // is too long. + state_ = EntryDecoderState::kResumeDecodingValue; + return status; + } + } while (true); +} + +bool HpackEntryDecoder::DispatchOnType(HpackEntryDecoderListener* listener) { + const HpackEntryType entry_type = entry_type_decoder_.entry_type(); + const uint32_t varint = static_cast<uint32_t>(entry_type_decoder_.varint()); + switch (entry_type) { + case HpackEntryType::kIndexedHeader: + // The entry consists solely of the entry type and varint. See: + // http://httpwg.org/specs/rfc7541.html#indexed.header.representation + listener->OnIndexedHeader(varint); + return true; + + case HpackEntryType::kIndexedLiteralHeader: + case HpackEntryType::kUnindexedLiteralHeader: + case HpackEntryType::kNeverIndexedLiteralHeader: + // The entry has a literal value, and if the varint is zero also has a + // literal name preceding the value. See: + // http://httpwg.org/specs/rfc7541.html#literal.header.representation + listener->OnStartLiteralHeader(entry_type, varint); + if (varint == 0) { + state_ = EntryDecoderState::kStartDecodingName; + } else { + state_ = EntryDecoderState::kStartDecodingValue; + } + return false; + + case HpackEntryType::kDynamicTableSizeUpdate: + // The entry consists solely of the entry type and varint. FWIW, I've + // never seen this type of entry in production (primarily browser + // traffic) so if you're designing an HPACK successor someday, consider + // dropping it or giving it a much longer prefix. See: + // http://httpwg.org/specs/rfc7541.html#encoding.context.update + listener->OnDynamicTableSizeUpdate(varint); + return true; + } + + HTTP2_BUG << "Unreachable, entry_type=" << entry_type; + return true; +} + +void HpackEntryDecoder::OutputDebugString(std::ostream& out) const { + out << "HpackEntryDecoder(state=" << state_ << ", " << entry_type_decoder_ + << ", " << string_decoder_ << ")"; +} + +Http2String HpackEntryDecoder::DebugString() const { + std::stringstream s; + s << *this; + return s.str(); +} + +std::ostream& operator<<(std::ostream& out, const HpackEntryDecoder& v) { + v.OutputDebugString(out); + return out; +} + +std::ostream& operator<<(std::ostream& out, + HpackEntryDecoder::EntryDecoderState state) { + typedef HpackEntryDecoder::EntryDecoderState EntryDecoderState; + switch (state) { + case EntryDecoderState::kResumeDecodingType: + return out << "kResumeDecodingType"; + case EntryDecoderState::kDecodedType: + return out << "kDecodedType"; + case EntryDecoderState::kStartDecodingName: + return out << "kStartDecodingName"; + case EntryDecoderState::kResumeDecodingName: + return out << "kResumeDecodingName"; + case EntryDecoderState::kStartDecodingValue: + return out << "kStartDecodingValue"; + case EntryDecoderState::kResumeDecodingValue: + return out << "kResumeDecodingValue"; + } + return out << static_cast<int>(state); +} + +} // namespace http2 |