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 |