| // 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 "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h" | 
 | #include "net/third_party/quiche/src/http2/platform/api/http2_logging.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. | 
 |         HTTP2_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. | 
 |         HTTP2_DVLOG(1) << "kDecodedType: db->Remaining=" << db->Remaining(); | 
 |         if (DispatchOnType(listener)) { | 
 |           // All done. | 
 |           return DecodeStatus::kDecodeDone; | 
 |         } | 
 |         continue; | 
 |  | 
 |       case EntryDecoderState::kStartDecodingName: | 
 |         HTTP2_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: | 
 |         HTTP2_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. | 
 |         HTTP2_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. | 
 |         HTTP2_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 |