|  | // 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_collector.h" | 
|  |  | 
|  | #include "testing/gtest/include/gtest/gtest.h" | 
|  | #include "net/third_party/quiche/src/http2/hpack/decoder/hpack_string_collector.h" | 
|  | #include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h" | 
|  | #include "net/third_party/quiche/src/http2/platform/api/http2_logging.h" | 
|  | #include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h" | 
|  | #include "net/third_party/quiche/src/http2/platform/api/http2_test_helpers.h" | 
|  |  | 
|  | using ::testing::AssertionResult; | 
|  |  | 
|  | namespace http2 { | 
|  | namespace test { | 
|  | namespace { | 
|  |  | 
|  | const HpackEntryType kInvalidHeaderType = static_cast<HpackEntryType>(99); | 
|  | const size_t kInvalidIndex = 99999999; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | HpackEntryCollector::HpackEntryCollector() { | 
|  | Clear(); | 
|  | } | 
|  |  | 
|  | HpackEntryCollector::HpackEntryCollector(const HpackEntryCollector& other) = | 
|  | default; | 
|  |  | 
|  | HpackEntryCollector::HpackEntryCollector(HpackEntryType type, | 
|  | size_t index_or_size) | 
|  | : header_type_(type), index_(index_or_size), started_(true), ended_(true) {} | 
|  | HpackEntryCollector::HpackEntryCollector(HpackEntryType type, | 
|  | size_t index, | 
|  | bool value_huffman, | 
|  | const std::string& value) | 
|  | : header_type_(type), | 
|  | index_(index), | 
|  | value_(value, value_huffman), | 
|  | started_(true), | 
|  | ended_(true) {} | 
|  | HpackEntryCollector::HpackEntryCollector(HpackEntryType type, | 
|  | bool name_huffman, | 
|  | const std::string& name, | 
|  | bool value_huffman, | 
|  | const std::string& value) | 
|  | : header_type_(type), | 
|  | index_(0), | 
|  | name_(name, name_huffman), | 
|  | value_(value, value_huffman), | 
|  | started_(true), | 
|  | ended_(true) {} | 
|  |  | 
|  | HpackEntryCollector::~HpackEntryCollector() = default; | 
|  |  | 
|  | void HpackEntryCollector::OnIndexedHeader(size_t index) { | 
|  | ASSERT_FALSE(started_); | 
|  | ASSERT_TRUE(IsClear()) << ToString(); | 
|  | Init(HpackEntryType::kIndexedHeader, index); | 
|  | ended_ = true; | 
|  | } | 
|  | void HpackEntryCollector::OnStartLiteralHeader(HpackEntryType header_type, | 
|  | size_t maybe_name_index) { | 
|  | ASSERT_FALSE(started_); | 
|  | ASSERT_TRUE(IsClear()) << ToString(); | 
|  | Init(header_type, maybe_name_index); | 
|  | } | 
|  | void HpackEntryCollector::OnNameStart(bool huffman_encoded, size_t len) { | 
|  | ASSERT_TRUE(started_); | 
|  | ASSERT_FALSE(ended_); | 
|  | ASSERT_FALSE(IsClear()); | 
|  | ASSERT_TRUE(LiteralNameExpected()) << ToString(); | 
|  | name_.OnStringStart(huffman_encoded, len); | 
|  | } | 
|  | void HpackEntryCollector::OnNameData(const char* data, size_t len) { | 
|  | ASSERT_TRUE(started_); | 
|  | ASSERT_FALSE(ended_); | 
|  | ASSERT_TRUE(LiteralNameExpected()) << ToString(); | 
|  | ASSERT_TRUE(name_.IsInProgress()); | 
|  | name_.OnStringData(data, len); | 
|  | } | 
|  | void HpackEntryCollector::OnNameEnd() { | 
|  | ASSERT_TRUE(started_); | 
|  | ASSERT_FALSE(ended_); | 
|  | ASSERT_TRUE(LiteralNameExpected()) << ToString(); | 
|  | ASSERT_TRUE(name_.IsInProgress()); | 
|  | name_.OnStringEnd(); | 
|  | } | 
|  | void HpackEntryCollector::OnValueStart(bool huffman_encoded, size_t len) { | 
|  | ASSERT_TRUE(started_); | 
|  | ASSERT_FALSE(ended_); | 
|  | if (LiteralNameExpected()) { | 
|  | ASSERT_TRUE(name_.HasEnded()); | 
|  | } | 
|  | ASSERT_TRUE(LiteralValueExpected()) << ToString(); | 
|  | ASSERT_TRUE(value_.IsClear()) << value_.ToString(); | 
|  | value_.OnStringStart(huffman_encoded, len); | 
|  | } | 
|  | void HpackEntryCollector::OnValueData(const char* data, size_t len) { | 
|  | ASSERT_TRUE(started_); | 
|  | ASSERT_FALSE(ended_); | 
|  | ASSERT_TRUE(LiteralValueExpected()) << ToString(); | 
|  | ASSERT_TRUE(value_.IsInProgress()); | 
|  | value_.OnStringData(data, len); | 
|  | } | 
|  | void HpackEntryCollector::OnValueEnd() { | 
|  | ASSERT_TRUE(started_); | 
|  | ASSERT_FALSE(ended_); | 
|  | ASSERT_TRUE(LiteralValueExpected()) << ToString(); | 
|  | ASSERT_TRUE(value_.IsInProgress()); | 
|  | value_.OnStringEnd(); | 
|  | ended_ = true; | 
|  | } | 
|  | void HpackEntryCollector::OnDynamicTableSizeUpdate(size_t size) { | 
|  | ASSERT_FALSE(started_); | 
|  | ASSERT_TRUE(IsClear()) << ToString(); | 
|  | Init(HpackEntryType::kDynamicTableSizeUpdate, size); | 
|  | ended_ = true; | 
|  | } | 
|  |  | 
|  | void HpackEntryCollector::Clear() { | 
|  | header_type_ = kInvalidHeaderType; | 
|  | index_ = kInvalidIndex; | 
|  | name_.Clear(); | 
|  | value_.Clear(); | 
|  | started_ = ended_ = false; | 
|  | } | 
|  | bool HpackEntryCollector::IsClear() const { | 
|  | return header_type_ == kInvalidHeaderType && index_ == kInvalidIndex && | 
|  | name_.IsClear() && value_.IsClear() && !started_ && !ended_; | 
|  | } | 
|  | bool HpackEntryCollector::IsComplete() const { | 
|  | return started_ && ended_; | 
|  | } | 
|  | bool HpackEntryCollector::LiteralNameExpected() const { | 
|  | switch (header_type_) { | 
|  | case HpackEntryType::kIndexedLiteralHeader: | 
|  | case HpackEntryType::kUnindexedLiteralHeader: | 
|  | case HpackEntryType::kNeverIndexedLiteralHeader: | 
|  | return index_ == 0; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  | bool HpackEntryCollector::LiteralValueExpected() const { | 
|  | switch (header_type_) { | 
|  | case HpackEntryType::kIndexedLiteralHeader: | 
|  | case HpackEntryType::kUnindexedLiteralHeader: | 
|  | case HpackEntryType::kNeverIndexedLiteralHeader: | 
|  | return true; | 
|  | default: | 
|  | return false; | 
|  | } | 
|  | } | 
|  | AssertionResult HpackEntryCollector::ValidateIndexedHeader( | 
|  | size_t expected_index) const { | 
|  | VERIFY_TRUE(started_); | 
|  | VERIFY_TRUE(ended_); | 
|  | VERIFY_EQ(HpackEntryType::kIndexedHeader, header_type_); | 
|  | VERIFY_EQ(expected_index, index_); | 
|  | return ::testing::AssertionSuccess(); | 
|  | } | 
|  | AssertionResult HpackEntryCollector::ValidateLiteralValueHeader( | 
|  | HpackEntryType expected_type, | 
|  | size_t expected_index, | 
|  | bool expected_value_huffman, | 
|  | quiche::QuicheStringPiece expected_value) const { | 
|  | VERIFY_TRUE(started_); | 
|  | VERIFY_TRUE(ended_); | 
|  | VERIFY_EQ(expected_type, header_type_); | 
|  | VERIFY_NE(0u, expected_index); | 
|  | VERIFY_EQ(expected_index, index_); | 
|  | VERIFY_TRUE(name_.IsClear()); | 
|  | VERIFY_SUCCESS(value_.Collected(expected_value, expected_value_huffman)); | 
|  | return ::testing::AssertionSuccess(); | 
|  | } | 
|  | AssertionResult HpackEntryCollector::ValidateLiteralNameValueHeader( | 
|  | HpackEntryType expected_type, | 
|  | bool expected_name_huffman, | 
|  | quiche::QuicheStringPiece expected_name, | 
|  | bool expected_value_huffman, | 
|  | quiche::QuicheStringPiece expected_value) const { | 
|  | VERIFY_TRUE(started_); | 
|  | VERIFY_TRUE(ended_); | 
|  | VERIFY_EQ(expected_type, header_type_); | 
|  | VERIFY_EQ(0u, index_); | 
|  | VERIFY_SUCCESS(name_.Collected(expected_name, expected_name_huffman)); | 
|  | VERIFY_SUCCESS(value_.Collected(expected_value, expected_value_huffman)); | 
|  | return ::testing::AssertionSuccess(); | 
|  | } | 
|  | AssertionResult HpackEntryCollector::ValidateDynamicTableSizeUpdate( | 
|  | size_t size) const { | 
|  | VERIFY_TRUE(started_); | 
|  | VERIFY_TRUE(ended_); | 
|  | VERIFY_EQ(HpackEntryType::kDynamicTableSizeUpdate, header_type_); | 
|  | VERIFY_EQ(index_, size); | 
|  | return ::testing::AssertionSuccess(); | 
|  | } | 
|  |  | 
|  | void HpackEntryCollector::AppendToHpackBlockBuilder( | 
|  | HpackBlockBuilder* hbb) const { | 
|  | ASSERT_TRUE(started_ && ended_) << *this; | 
|  | switch (header_type_) { | 
|  | case HpackEntryType::kIndexedHeader: | 
|  | hbb->AppendIndexedHeader(index_); | 
|  | return; | 
|  |  | 
|  | case HpackEntryType::kDynamicTableSizeUpdate: | 
|  | hbb->AppendDynamicTableSizeUpdate(index_); | 
|  | return; | 
|  |  | 
|  | case HpackEntryType::kIndexedLiteralHeader: | 
|  | case HpackEntryType::kUnindexedLiteralHeader: | 
|  | case HpackEntryType::kNeverIndexedLiteralHeader: | 
|  | ASSERT_TRUE(value_.HasEnded()) << *this; | 
|  | if (index_ != 0) { | 
|  | CHECK(name_.IsClear()); | 
|  | hbb->AppendNameIndexAndLiteralValue(header_type_, index_, | 
|  | value_.huffman_encoded, value_.s); | 
|  | } else { | 
|  | CHECK(name_.HasEnded()) << *this; | 
|  | hbb->AppendLiteralNameAndValue(header_type_, name_.huffman_encoded, | 
|  | name_.s, value_.huffman_encoded, | 
|  | value_.s); | 
|  | } | 
|  | return; | 
|  |  | 
|  | default: | 
|  | ADD_FAILURE() << *this; | 
|  | } | 
|  | } | 
|  |  | 
|  | std::string HpackEntryCollector::ToString() const { | 
|  | std::string result("Type="); | 
|  | switch (header_type_) { | 
|  | case HpackEntryType::kIndexedHeader: | 
|  | result += "IndexedHeader"; | 
|  | break; | 
|  | case HpackEntryType::kDynamicTableSizeUpdate: | 
|  | result += "DynamicTableSizeUpdate"; | 
|  | break; | 
|  | case HpackEntryType::kIndexedLiteralHeader: | 
|  | result += "IndexedLiteralHeader"; | 
|  | break; | 
|  | case HpackEntryType::kUnindexedLiteralHeader: | 
|  | result += "UnindexedLiteralHeader"; | 
|  | break; | 
|  | case HpackEntryType::kNeverIndexedLiteralHeader: | 
|  | result += "NeverIndexedLiteralHeader"; | 
|  | break; | 
|  | default: | 
|  | if (header_type_ == kInvalidHeaderType) { | 
|  | result += "<unset>"; | 
|  | } else { | 
|  | Http2StrAppend(&result, header_type_); | 
|  | } | 
|  | } | 
|  | if (index_ != 0) { | 
|  | Http2StrAppend(&result, " Index=", index_); | 
|  | } | 
|  | if (!name_.IsClear()) { | 
|  | Http2StrAppend(&result, " Name", name_.ToString()); | 
|  | } | 
|  | if (!value_.IsClear()) { | 
|  | Http2StrAppend(&result, " Value", value_.ToString()); | 
|  | } | 
|  | if (!started_) { | 
|  | EXPECT_FALSE(ended_); | 
|  | Http2StrAppend(&result, " !started"); | 
|  | } else if (!ended_) { | 
|  | Http2StrAppend(&result, " !ended"); | 
|  | } else { | 
|  | Http2StrAppend(&result, " Complete"); | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void HpackEntryCollector::Init(HpackEntryType type, size_t maybe_index) { | 
|  | ASSERT_TRUE(IsClear()) << ToString(); | 
|  | header_type_ = type; | 
|  | index_ = maybe_index; | 
|  | started_ = true; | 
|  | } | 
|  |  | 
|  | bool operator==(const HpackEntryCollector& a, const HpackEntryCollector& b) { | 
|  | return a.name() == b.name() && a.value() == b.value() && | 
|  | a.index() == b.index() && a.header_type() == b.header_type() && | 
|  | a.started() == b.started() && a.ended() == b.ended(); | 
|  | } | 
|  | bool operator!=(const HpackEntryCollector& a, const HpackEntryCollector& b) { | 
|  | return !(a == b); | 
|  | } | 
|  |  | 
|  | std::ostream& operator<<(std::ostream& out, const HpackEntryCollector& v) { | 
|  | return out << v.ToString(); | 
|  | } | 
|  |  | 
|  | }  // namespace test | 
|  | }  // namespace http2 |