| Index: net/http2/hpack/decoder/hpack_entry_collector.cc
 | 
| diff --git a/net/http2/hpack/decoder/hpack_entry_collector.cc b/net/http2/hpack/decoder/hpack_entry_collector.cc
 | 
| new file mode 100644
 | 
| index 0000000000000000000000000000000000000000..ea4feb6f8475a2307e6142b5c2d0771ea61afd46
 | 
| --- /dev/null
 | 
| +++ b/net/http2/hpack/decoder/hpack_entry_collector.cc
 | 
| @@ -0,0 +1,317 @@
 | 
| +// 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 <sstream>
 | 
| +#include <string>
 | 
| +
 | 
| +#include "base/logging.h"
 | 
| +#include "net/http2/hpack/decoder/hpack_string_collector.h"
 | 
| +#include "net/http2/hpack/http2_hpack_constants.h"
 | 
| +#include "net/http2/tools/failure.h"
 | 
| +#include "testing/gtest/include/gtest/gtest.h"
 | 
| +
 | 
| +using ::testing::AssertionResult;
 | 
| +using std::string;
 | 
| +using base::StringPiece;
 | 
| +
 | 
| +namespace net {
 | 
| +namespace test {
 | 
| +namespace {
 | 
| +
 | 
| +const HpackEntryType kInvalidHeaderType = static_cast<HpackEntryType>(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 string& value)
 | 
| +    : header_type_(type),
 | 
| +      index_(index),
 | 
| +      value_(value, value_huffman),
 | 
| +      started_(true),
 | 
| +      ended_(true) {}
 | 
| +HpackEntryCollector::HpackEntryCollector(HpackEntryType type,
 | 
| +                                         bool name_huffman,
 | 
| +                                         const string& name,
 | 
| +                                         bool value_huffman,
 | 
| +                                         const string& 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,
 | 
| +    StringPiece 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,
 | 
| +    StringPiece expected_name,
 | 
| +    bool expected_value_huffman,
 | 
| +    StringPiece 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;
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +string HpackEntryCollector::ToString() const {
 | 
| +  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 {
 | 
| +        std::stringstream ss;
 | 
| +        ss << header_type_;
 | 
| +        result.append(ss.str());
 | 
| +      }
 | 
| +  }
 | 
| +  if (index_ != 0) {
 | 
| +    result.append(" Index=");
 | 
| +    std::stringstream ss;
 | 
| +    ss << index_;
 | 
| +    result.append(ss.str());
 | 
| +  }
 | 
| +  if (!name_.IsClear()) {
 | 
| +    result.append(" Name");
 | 
| +    result.append(name_.ToString());
 | 
| +  }
 | 
| +  if (!value_.IsClear()) {
 | 
| +    result.append(" Value");
 | 
| +    result.append(value_.ToString());
 | 
| +  }
 | 
| +  if (!started_) {
 | 
| +    EXPECT_FALSE(ended_);
 | 
| +    result.append(" !started");
 | 
| +  } else if (!ended_) {
 | 
| +    result.append(" !ended");
 | 
| +  } else {
 | 
| +    result.append(" 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
 | 
| 
 |