// 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_collector.h" #include #include "base/logging.h" #include "net/http2/hpack/decoder/hpack_string_collector.h" #include "net/http2/hpack/http2_hpack_constants.h" #include "net/http2/platform/api/http2_string_utils.h" #include "net/http2/tools/failure.h" #include "testing/gtest/include/gtest/gtest.h" using ::testing::AssertionResult; namespace net { namespace test { namespace { const HpackEntryType kInvalidHeaderType = static_cast(99); const size_t kInvalidIndex = 99999999; } // namespace HpackEntryCollector::HpackEntryCollector() { Clear(); } HpackEntryCollector::HpackEntryCollector(const HpackEntryCollector& other) : header_type_(other.header_type_), index_(other.index_), name_(other.name_), value_(other.value_), started_(other.started_), ended_(other.ended_) {} 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 Http2String& value) : header_type_(type), index_(index), value_(value, value_huffman), started_(true), ended_(true) {} HpackEntryCollector::HpackEntryCollector(HpackEntryType type, bool name_huffman, const Http2String& name, bool value_huffman, const Http2String& value) : header_type_(type), index_(0), name_(name, name_huffman), value_(value, value_huffman), started_(true), ended_(true) {} HpackEntryCollector::~HpackEntryCollector() {} 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, Http2StringPiece 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, Http2StringPiece expected_name, bool expected_value_huffman, Http2StringPiece 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; } } Http2String HpackEntryCollector::ToString() const { Http2String 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 += ""; } 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 net