| // 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. | 
 |  | 
 | #ifndef QUICHE_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_ | 
 | #define QUICHE_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_ | 
 |  | 
 | // HpackStringDecoder decodes strings encoded per the HPACK spec; this does | 
 | // not mean decompressing Huffman encoded strings, just identifying the length, | 
 | // encoding and contents for a listener. | 
 |  | 
 | #include <stddef.h> | 
 |  | 
 | #include <algorithm> | 
 | #include <cstdint> | 
 |  | 
 | #include "net/third_party/quiche/src/http2/decoder/decode_buffer.h" | 
 | #include "net/third_party/quiche/src/http2/decoder/decode_status.h" | 
 | #include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h" | 
 | #include "net/third_party/quiche/src/http2/platform/api/http2_export.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" | 
 | #include "net/third_party/quiche/src/http2/platform/api/http2_string.h" | 
 |  | 
 | namespace http2 { | 
 |  | 
 | // Decodes a single string in an HPACK header entry. The high order bit of | 
 | // the first byte of the length is the H (Huffman) bit indicating whether | 
 | // the value is Huffman encoded, and the remainder of the byte is the first | 
 | // 7 bits of an HPACK varint. | 
 | // | 
 | // Call Start() to begin decoding; if it returns kDecodeInProgress, then call | 
 | // Resume() when more input is available, repeating until kDecodeInProgress is | 
 | // not returned. If kDecodeDone or kDecodeError is returned, then Resume() must | 
 | // not be called until Start() has been called to start decoding a new string. | 
 | class HTTP2_EXPORT_PRIVATE HpackStringDecoder { | 
 |  public: | 
 |   enum StringDecoderState { | 
 |     kStartDecodingLength, | 
 |     kDecodingString, | 
 |     kResumeDecodingLength, | 
 |   }; | 
 |  | 
 |   template <class Listener> | 
 |   DecodeStatus Start(DecodeBuffer* db, Listener* cb) { | 
 |     // Fast decode path is used if the string is under 127 bytes and the | 
 |     // entire length of the string is in the decode buffer. More than 83% of | 
 |     // string lengths are encoded in just one byte. | 
 |     if (db->HasData() && (*db->cursor() & 0x7f) != 0x7f) { | 
 |       // The string is short. | 
 |       uint8_t h_and_prefix = db->DecodeUInt8(); | 
 |       uint8_t length = h_and_prefix & 0x7f; | 
 |       bool huffman_encoded = (h_and_prefix & 0x80) == 0x80; | 
 |       cb->OnStringStart(huffman_encoded, length); | 
 |       if (length <= db->Remaining()) { | 
 |         // Yeah, we've got the whole thing in the decode buffer. | 
 |         // Ideally this will be the common case. Note that we don't | 
 |         // update any of the member variables in this path. | 
 |         cb->OnStringData(db->cursor(), length); | 
 |         db->AdvanceCursor(length); | 
 |         cb->OnStringEnd(); | 
 |         return DecodeStatus::kDecodeDone; | 
 |       } | 
 |       // Not all in the buffer. | 
 |       huffman_encoded_ = huffman_encoded; | 
 |       remaining_ = length; | 
 |       // Call Resume to decode the string body, which is only partially | 
 |       // in the decode buffer (or not at all). | 
 |       state_ = kDecodingString; | 
 |       return Resume(db, cb); | 
 |     } | 
 |     // Call Resume to decode the string length, which is either not in | 
 |     // the decode buffer, or spans multiple bytes. | 
 |     state_ = kStartDecodingLength; | 
 |     return Resume(db, cb); | 
 |   } | 
 |  | 
 |   template <class Listener> | 
 |   DecodeStatus Resume(DecodeBuffer* db, Listener* cb) { | 
 |     DecodeStatus status; | 
 |     while (true) { | 
 |       switch (state_) { | 
 |         case kStartDecodingLength: | 
 |           HTTP2_DVLOG(2) << "kStartDecodingLength: db->Remaining=" | 
 |                          << db->Remaining(); | 
 |           if (!StartDecodingLength(db, cb, &status)) { | 
 |             // The length is split across decode buffers. | 
 |             return status; | 
 |           } | 
 |           // We've finished decoding the length, which spanned one or more | 
 |           // bytes. Approximately 17% of strings have a length that is greater | 
 |           // than 126 bytes, and thus the length is encoded in more than one | 
 |           // byte, and so doesn't get the benefit of the optimization in | 
 |           // Start() for single byte lengths. But, we still expect that most | 
 |           // of such strings will be contained entirely in a single decode | 
 |           // buffer, and hence this fall through skips another trip through the | 
 |           // switch above and more importantly skips setting the state_ variable | 
 |           // again in those cases where we don't need it. | 
 |           HTTP2_FALLTHROUGH; | 
 |  | 
 |         case kDecodingString: | 
 |           HTTP2_DVLOG(2) << "kDecodingString: db->Remaining=" << db->Remaining() | 
 |                          << "    remaining_=" << remaining_; | 
 |           return DecodeString(db, cb); | 
 |  | 
 |         case kResumeDecodingLength: | 
 |           HTTP2_DVLOG(2) << "kResumeDecodingLength: db->Remaining=" | 
 |                          << db->Remaining(); | 
 |           if (!ResumeDecodingLength(db, cb, &status)) { | 
 |             return status; | 
 |           } | 
 |       } | 
 |     } | 
 |   } | 
 |  | 
 |   Http2String DebugString() const; | 
 |  | 
 |  private: | 
 |   static Http2String StateToString(StringDecoderState v); | 
 |  | 
 |   // Returns true if the length is fully decoded and the listener wants the | 
 |   // decoding to continue, false otherwise; status is set to the status from | 
 |   // the varint decoder. | 
 |   // If the length is not fully decoded, case state_ is set appropriately | 
 |   // for the next call to Resume. | 
 |   template <class Listener> | 
 |   bool StartDecodingLength(DecodeBuffer* db, | 
 |                            Listener* cb, | 
 |                            DecodeStatus* status) { | 
 |     if (db->Empty()) { | 
 |       *status = DecodeStatus::kDecodeInProgress; | 
 |       state_ = kStartDecodingLength; | 
 |       return false; | 
 |     } | 
 |     uint8_t h_and_prefix = db->DecodeUInt8(); | 
 |     huffman_encoded_ = (h_and_prefix & 0x80) == 0x80; | 
 |     *status = length_decoder_.Start(h_and_prefix, 7, db); | 
 |     if (*status == DecodeStatus::kDecodeDone) { | 
 |       OnStringStart(cb, status); | 
 |       return true; | 
 |     } | 
 |     // Set the state to cover the DecodeStatus::kDecodeInProgress case. | 
 |     // Won't be needed if the status is kDecodeError. | 
 |     state_ = kResumeDecodingLength; | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Returns true if the length is fully decoded and the listener wants the | 
 |   // decoding to continue, false otherwise; status is set to the status from | 
 |   // the varint decoder; state_ is updated when fully decoded. | 
 |   // If the length is not fully decoded, case state_ is set appropriately | 
 |   // for the next call to Resume. | 
 |   template <class Listener> | 
 |   bool ResumeDecodingLength(DecodeBuffer* db, | 
 |                             Listener* cb, | 
 |                             DecodeStatus* status) { | 
 |     DCHECK_EQ(state_, kResumeDecodingLength); | 
 |     *status = length_decoder_.Resume(db); | 
 |     if (*status == DecodeStatus::kDecodeDone) { | 
 |       state_ = kDecodingString; | 
 |       OnStringStart(cb, status); | 
 |       return true; | 
 |     } | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Returns true if the listener wants the decoding to continue, and | 
 |   // false otherwise, in which case status set. | 
 |   template <class Listener> | 
 |   void OnStringStart(Listener* cb, DecodeStatus* status) { | 
 |     // TODO(vasilvv): fail explicitly in case of truncation. | 
 |     remaining_ = static_cast<size_t>(length_decoder_.value()); | 
 |     // Make callback so consumer knows what is coming. | 
 |     cb->OnStringStart(huffman_encoded_, remaining_); | 
 |   } | 
 |  | 
 |   // Passes the available portion of the string to the listener, and signals | 
 |   // the end of the string when it is reached. Returns kDecodeDone or | 
 |   // kDecodeInProgress as appropriate. | 
 |   template <class Listener> | 
 |   DecodeStatus DecodeString(DecodeBuffer* db, Listener* cb) { | 
 |     size_t len = std::min(remaining_, db->Remaining()); | 
 |     if (len > 0) { | 
 |       cb->OnStringData(db->cursor(), len); | 
 |       db->AdvanceCursor(len); | 
 |       remaining_ -= len; | 
 |     } | 
 |     if (remaining_ == 0) { | 
 |       cb->OnStringEnd(); | 
 |       return DecodeStatus::kDecodeDone; | 
 |     } | 
 |     state_ = kDecodingString; | 
 |     return DecodeStatus::kDecodeInProgress; | 
 |   } | 
 |  | 
 |   HpackVarintDecoder length_decoder_; | 
 |  | 
 |   // These fields are initialized just to keep ASAN happy about reading | 
 |   // them from DebugString(). | 
 |   size_t remaining_ = 0; | 
 |   StringDecoderState state_ = kStartDecodingLength; | 
 |   bool huffman_encoded_ = false; | 
 | }; | 
 |  | 
 | HTTP2_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out, | 
 |                                               const HpackStringDecoder& v); | 
 |  | 
 | }  // namespace http2 | 
 | #endif  // QUICHE_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_ |