| Index: net/http2/hpack/decoder/hpack_entry_decoder.cc
|
| diff --git a/net/http2/hpack/decoder/hpack_entry_decoder.cc b/net/http2/hpack/decoder/hpack_entry_decoder.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..c47ef0cf7134f7ea19807e892755dda4cceb375c
|
| --- /dev/null
|
| +++ b/net/http2/hpack/decoder/hpack_entry_decoder.cc
|
| @@ -0,0 +1,233 @@
|
| +// 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_entry_decoder.h"
|
| +
|
| +#include <stddef.h>
|
| +
|
| +#include "base/logging.h"
|
| +#include "base/macros.h"
|
| +
|
| +namespace net {
|
| +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
|
| +
|
| +// Only call Resume if the previous call (Start or Resume) returned
|
| +// kDecodeInProgress; Resume is also called from Start when it has succeeded
|
| +// in decoding the entry type and its varint.
|
| +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;
|
| + // FALLTHROUGH_INTENDED
|
| +
|
| + 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;
|
| + // FALLTHROUGH_INTENDED
|
| +
|
| + 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 = 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;
|
| + }
|
| +
|
| + NOTREACHED();
|
| + return true;
|
| +}
|
| +
|
| +void HpackEntryDecoder::OutputDebugString(std::ostream& out) const {
|
| + out << "HpackEntryDecoder(state=" << state_ << ", " << entry_type_decoder_
|
| + << ", " << string_decoder_ << ")";
|
| +}
|
| +
|
| +std::string 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 net
|
|
|