summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quiche/http2/hpack
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2022-05-17 17:24:03 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2022-06-22 07:51:41 +0000
commit774f54339e5db91f785733232d3950366db65d07 (patch)
tree068e1b47bd1af94d77094ed12b604a6b83d9c22a /chromium/net/third_party/quiche/src/quiche/http2/hpack
parentf7eaed5286974984ba5f9e3189d8f49d03e99f81 (diff)
downloadqtwebengine-chromium-774f54339e5db91f785733232d3950366db65d07.tar.gz
BASELINE: Update Chromium to 102.0.5005.57
Change-Id: I885f714bb40ee724c28f94ca6bd8dbdb39915158 Reviewed-by: Allan Sandfeld Jensen <allan.jensen@qt.io>
Diffstat (limited to 'chromium/net/third_party/quiche/src/quiche/http2/hpack')
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_collector.cc151
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_collector.h129
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder.cc66
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder.h69
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder_test.cc291
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder.cc125
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder.h132
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_listener.cc29
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_listener.h61
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state.cc226
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state.h138
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state_test.cc549
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer.cc239
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer.h102
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc249
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables.cc148
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables.h166
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables_test.cc259
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_test.cc1192
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoding_error.cc51
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoding_error.h51
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_collector.cc301
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_collector.h159
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.cc295
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.h91
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_listener.cc81
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_listener.h110
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_test.cc202
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder.cc362
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder.h57
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder_test.cc85
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_collector.cc124
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_collector.h63
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder.cc35
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder.h209
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_listener.cc36
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_listener.h62
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_test.cc154
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer.cc153
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer.h102
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc231
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_listener.cc34
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_listener.h83
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/hpack_static_table_entries.inc65
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants.cc31
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants.h63
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants_test.cc66
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder.cc485
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder.h134
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder_test.cc242
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder.cc129
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder.h40
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder_test.cc131
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_transcoder_test.cc183
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/huffman_spec_tables.cc572
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/huffman_spec_tables.h29
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder.cc66
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder.h98
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder_test.cc169
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_example.cc59
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_example.h32
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc143
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.h128
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder_test.cc309
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.cc47
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.h29
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder_test.cc161
-rw-r--r--chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_round_trip_test.cc416
68 files changed, 11279 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_collector.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_collector.cc
new file mode 100644
index 00000000000..180dd24e2a5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_collector.cc
@@ -0,0 +1,151 @@
+// 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 "quiche/http2/hpack/decoder/hpack_block_collector.h"
+
+#include <algorithm>
+#include <memory>
+
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+
+namespace http2 {
+namespace test {
+
+HpackBlockCollector::HpackBlockCollector() = default;
+HpackBlockCollector::HpackBlockCollector(const HpackBlockCollector& other)
+ : pending_entry_(other.pending_entry_), entries_(other.entries_) {}
+HpackBlockCollector::~HpackBlockCollector() = default;
+
+void HpackBlockCollector::OnIndexedHeader(size_t index) {
+ pending_entry_.OnIndexedHeader(index);
+ PushPendingEntry();
+}
+void HpackBlockCollector::OnDynamicTableSizeUpdate(size_t size) {
+ pending_entry_.OnDynamicTableSizeUpdate(size);
+ PushPendingEntry();
+}
+void HpackBlockCollector::OnStartLiteralHeader(HpackEntryType header_type,
+ size_t maybe_name_index) {
+ pending_entry_.OnStartLiteralHeader(header_type, maybe_name_index);
+}
+void HpackBlockCollector::OnNameStart(bool huffman_encoded, size_t len) {
+ pending_entry_.OnNameStart(huffman_encoded, len);
+}
+void HpackBlockCollector::OnNameData(const char* data, size_t len) {
+ pending_entry_.OnNameData(data, len);
+}
+void HpackBlockCollector::OnNameEnd() {
+ pending_entry_.OnNameEnd();
+}
+void HpackBlockCollector::OnValueStart(bool huffman_encoded, size_t len) {
+ pending_entry_.OnValueStart(huffman_encoded, len);
+}
+void HpackBlockCollector::OnValueData(const char* data, size_t len) {
+ pending_entry_.OnValueData(data, len);
+}
+void HpackBlockCollector::OnValueEnd() {
+ pending_entry_.OnValueEnd();
+ PushPendingEntry();
+}
+
+void HpackBlockCollector::PushPendingEntry() {
+ EXPECT_TRUE(pending_entry_.IsComplete());
+ HTTP2_DVLOG(2) << "PushPendingEntry: " << pending_entry_;
+ entries_.push_back(pending_entry_);
+ EXPECT_TRUE(entries_.back().IsComplete());
+ pending_entry_.Clear();
+}
+void HpackBlockCollector::Clear() {
+ pending_entry_.Clear();
+ entries_.clear();
+}
+
+void HpackBlockCollector::ExpectIndexedHeader(size_t index) {
+ entries_.push_back(
+ HpackEntryCollector(HpackEntryType::kIndexedHeader, index));
+}
+void HpackBlockCollector::ExpectDynamicTableSizeUpdate(size_t size) {
+ entries_.push_back(
+ HpackEntryCollector(HpackEntryType::kDynamicTableSizeUpdate, size));
+}
+void HpackBlockCollector::ExpectNameIndexAndLiteralValue(
+ HpackEntryType type,
+ size_t index,
+ bool value_huffman,
+ const std::string& value) {
+ entries_.push_back(HpackEntryCollector(type, index, value_huffman, value));
+}
+void HpackBlockCollector::ExpectLiteralNameAndValue(HpackEntryType type,
+ bool name_huffman,
+ const std::string& name,
+ bool value_huffman,
+ const std::string& value) {
+ entries_.push_back(
+ HpackEntryCollector(type, name_huffman, name, value_huffman, value));
+}
+
+void HpackBlockCollector::ShuffleEntries(Http2Random* rng) {
+ std::shuffle(entries_.begin(), entries_.end(), *rng);
+}
+
+void HpackBlockCollector::AppendToHpackBlockBuilder(
+ HpackBlockBuilder* hbb) const {
+ QUICHE_CHECK(IsNotPending());
+ for (const auto& entry : entries_) {
+ entry.AppendToHpackBlockBuilder(hbb);
+ }
+}
+
+AssertionResult HpackBlockCollector::ValidateSoleIndexedHeader(
+ size_t ndx) const {
+ VERIFY_TRUE(pending_entry_.IsClear());
+ VERIFY_EQ(1u, entries_.size());
+ VERIFY_TRUE(entries_.front().ValidateIndexedHeader(ndx));
+ return AssertionSuccess();
+}
+AssertionResult HpackBlockCollector::ValidateSoleLiteralValueHeader(
+ HpackEntryType expected_type,
+ size_t expected_index,
+ bool expected_value_huffman,
+ absl::string_view expected_value) const {
+ VERIFY_TRUE(pending_entry_.IsClear());
+ VERIFY_EQ(1u, entries_.size());
+ VERIFY_TRUE(entries_.front().ValidateLiteralValueHeader(
+ expected_type, expected_index, expected_value_huffman, expected_value));
+ return AssertionSuccess();
+}
+AssertionResult HpackBlockCollector::ValidateSoleLiteralNameValueHeader(
+ HpackEntryType expected_type,
+ bool expected_name_huffman,
+ absl::string_view expected_name,
+ bool expected_value_huffman,
+ absl::string_view expected_value) const {
+ VERIFY_TRUE(pending_entry_.IsClear());
+ VERIFY_EQ(1u, entries_.size());
+ VERIFY_TRUE(entries_.front().ValidateLiteralNameValueHeader(
+ expected_type, expected_name_huffman, expected_name,
+ expected_value_huffman, expected_value));
+ return AssertionSuccess();
+}
+AssertionResult HpackBlockCollector::ValidateSoleDynamicTableSizeUpdate(
+ size_t size) const {
+ VERIFY_TRUE(pending_entry_.IsClear());
+ VERIFY_EQ(1u, entries_.size());
+ VERIFY_TRUE(entries_.front().ValidateDynamicTableSizeUpdate(size));
+ return AssertionSuccess();
+}
+
+AssertionResult HpackBlockCollector::VerifyEq(
+ const HpackBlockCollector& that) const {
+ VERIFY_EQ(pending_entry_, that.pending_entry_);
+ VERIFY_EQ(entries_, that.entries_);
+ return AssertionSuccess();
+}
+
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_collector.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_collector.h
new file mode 100644
index 00000000000..be24110f262
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_collector.h
@@ -0,0 +1,129 @@
+// 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_BLOCK_COLLECTOR_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_BLOCK_COLLECTOR_H_
+
+// HpackBlockCollector implements HpackEntryDecoderListener in order to record
+// the calls using HpackEntryCollector instances (one per HPACK entry). This
+// supports testing of HpackBlockDecoder, which decodes entire HPACK blocks.
+//
+// In addition to implementing the callback methods, HpackBlockCollector also
+// supports comparing two HpackBlockCollector instances (i.e. an expected and
+// an actual), or a sole HPACK entry against an expected value.
+
+#include <stddef.h>
+
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/decoder/hpack_entry_collector.h"
+#include "quiche/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/http2/hpack/tools/hpack_block_builder.h"
+#include "quiche/http2/test_tools/http2_random.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+namespace test {
+
+class QUICHE_NO_EXPORT HpackBlockCollector : public HpackEntryDecoderListener {
+ public:
+ HpackBlockCollector();
+ HpackBlockCollector(const HpackBlockCollector& other);
+ ~HpackBlockCollector() override;
+
+ // Implementations of HpackEntryDecoderListener, forwarding to pending_entry_,
+ // an HpackEntryCollector for the "in-progress" HPACK entry. OnIndexedHeader
+ // and OnDynamicTableSizeUpdate are pending only for that one call, while
+ // OnStartLiteralHeader is followed by many calls, ending with OnValueEnd.
+ // Once all the calls for one HPACK entry have been received, PushPendingEntry
+ // is used to append the pending_entry_ entry to the collected entries_.
+ void OnIndexedHeader(size_t index) override;
+ void OnDynamicTableSizeUpdate(size_t size) override;
+ void OnStartLiteralHeader(HpackEntryType header_type,
+ size_t maybe_name_index) override;
+ void OnNameStart(bool huffman_encoded, size_t len) override;
+ void OnNameData(const char* data, size_t len) override;
+ void OnNameEnd() override;
+ void OnValueStart(bool huffman_encoded, size_t len) override;
+ void OnValueData(const char* data, size_t len) override;
+ void OnValueEnd() override;
+
+ // Methods for creating a set of expectations (i.e. HPACK entries to compare
+ // against those collected by another instance of HpackBlockCollector).
+
+ // Add an HPACK entry for an indexed header.
+ void ExpectIndexedHeader(size_t index);
+
+ // Add an HPACK entry for a dynamic table size update.
+ void ExpectDynamicTableSizeUpdate(size_t size);
+
+ // Add an HPACK entry for a header entry with an index for the name, and a
+ // literal value.
+ void ExpectNameIndexAndLiteralValue(HpackEntryType type,
+ size_t index,
+ bool value_huffman,
+ const std::string& value);
+
+ // Add an HPACK entry for a header entry with a literal name and value.
+ void ExpectLiteralNameAndValue(HpackEntryType type,
+ bool name_huffman,
+ const std::string& name,
+ bool value_huffman,
+ const std::string& value);
+
+ // Shuffle the entries, in support of generating an HPACK block of entries
+ // in some random order.
+ void ShuffleEntries(Http2Random* rng);
+
+ // Serialize entries_ to the HpackBlockBuilder.
+ void AppendToHpackBlockBuilder(HpackBlockBuilder* hbb) const;
+
+ // Return AssertionSuccess if there is just one entry, and it is an
+ // Indexed Header with the specified index.
+ ::testing::AssertionResult ValidateSoleIndexedHeader(size_t ndx) const;
+
+ // Return AssertionSuccess if there is just one entry, and it is a
+ // Dynamic Table Size Update with the specified size.
+ ::testing::AssertionResult ValidateSoleDynamicTableSizeUpdate(
+ size_t size) const;
+
+ // Return AssertionSuccess if there is just one entry, and it is a Header
+ // entry with an index for the name and a literal value.
+ ::testing::AssertionResult ValidateSoleLiteralValueHeader(
+ HpackEntryType expected_type,
+ size_t expected_index,
+ bool expected_value_huffman,
+ absl::string_view expected_value) const;
+
+ // Return AssertionSuccess if there is just one entry, and it is a Header
+ // with a literal name and literal value.
+ ::testing::AssertionResult ValidateSoleLiteralNameValueHeader(
+ HpackEntryType expected_type,
+ bool expected_name_huffman,
+ absl::string_view expected_name,
+ bool expected_value_huffman,
+ absl::string_view expected_value) const;
+
+ bool IsNotPending() const { return pending_entry_.IsClear(); }
+ bool IsClear() const { return IsNotPending() && entries_.empty(); }
+ void Clear();
+
+ ::testing::AssertionResult VerifyEq(const HpackBlockCollector& that) const;
+
+ private:
+ // Push the value of pending_entry_ onto entries_, and clear pending_entry_.
+ // The pending_entry_ must be complete.
+ void PushPendingEntry();
+
+ HpackEntryCollector pending_entry_;
+ std::vector<HpackEntryCollector> entries_;
+};
+
+} // namespace test
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_BLOCK_COLLECTOR_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder.cc
new file mode 100644
index 00000000000..8c8d4c064bd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder.cc
@@ -0,0 +1,66 @@
+// 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 "quiche/http2/hpack/decoder/hpack_block_decoder.h"
+
+#include <cstdint>
+
+#include "absl/strings/str_cat.h"
+#include "quiche/http2/platform/api/http2_flag_utils.h"
+#include "quiche/http2/platform/api/http2_flags.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+
+namespace http2 {
+
+DecodeStatus HpackBlockDecoder::Decode(DecodeBuffer* db) {
+ if (!before_entry_) {
+ HTTP2_DVLOG(2) << "HpackBlockDecoder::Decode resume entry, db->Remaining="
+ << db->Remaining();
+ DecodeStatus status = entry_decoder_.Resume(db, listener_);
+ switch (status) {
+ case DecodeStatus::kDecodeDone:
+ before_entry_ = true;
+ break;
+ case DecodeStatus::kDecodeInProgress:
+ QUICHE_DCHECK_EQ(0u, db->Remaining());
+ return DecodeStatus::kDecodeInProgress;
+ case DecodeStatus::kDecodeError:
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 1, 23);
+ return DecodeStatus::kDecodeError;
+ }
+ }
+ QUICHE_DCHECK(before_entry_);
+ while (db->HasData()) {
+ HTTP2_DVLOG(2) << "HpackBlockDecoder::Decode start entry, db->Remaining="
+ << db->Remaining();
+ DecodeStatus status = entry_decoder_.Start(db, listener_);
+ switch (status) {
+ case DecodeStatus::kDecodeDone:
+ continue;
+ case DecodeStatus::kDecodeInProgress:
+ QUICHE_DCHECK_EQ(0u, db->Remaining());
+ before_entry_ = false;
+ return DecodeStatus::kDecodeInProgress;
+ case DecodeStatus::kDecodeError:
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 2, 23);
+ return DecodeStatus::kDecodeError;
+ }
+ QUICHE_DCHECK(false);
+ }
+ QUICHE_DCHECK(before_entry_);
+ return DecodeStatus::kDecodeDone;
+}
+
+std::string HpackBlockDecoder::DebugString() const {
+ return absl::StrCat(
+ "HpackBlockDecoder(", entry_decoder_.DebugString(), ", listener@",
+ absl::Hex(reinterpret_cast<intptr_t>(listener_)),
+ (before_entry_ ? ", between entries)" : ", in an entry)"));
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackBlockDecoder& v) {
+ return out << v.DebugString();
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder.h
new file mode 100644
index 00000000000..85ee8ff7126
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder.h
@@ -0,0 +1,69 @@
+// 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_BLOCK_DECODER_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_BLOCK_DECODER_H_
+
+// HpackBlockDecoder decodes an entire HPACK block (or the available portion
+// thereof in the DecodeBuffer) into entries, but doesn't include HPACK static
+// or dynamic table support, so table indices remain indices at this level.
+// Reports the entries to an HpackEntryDecoderListener.
+
+#include <string>
+
+#include "quiche/http2/decoder/decode_buffer.h"
+#include "quiche/http2/decoder/decode_status.h"
+#include "quiche/http2/hpack/decoder/hpack_decoding_error.h"
+#include "quiche/http2/hpack/decoder/hpack_entry_decoder.h"
+#include "quiche/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+class QUICHE_EXPORT_PRIVATE HpackBlockDecoder {
+ public:
+ explicit HpackBlockDecoder(HpackEntryDecoderListener* listener)
+ : listener_(listener) {
+ QUICHE_DCHECK_NE(listener_, nullptr);
+ }
+ ~HpackBlockDecoder() {}
+
+ HpackBlockDecoder(const HpackBlockDecoder&) = delete;
+ HpackBlockDecoder& operator=(const HpackBlockDecoder&) = delete;
+
+ // Prepares the decoder to start decoding a new HPACK block. Expected
+ // to be called from an implementation of Http2FrameDecoderListener's
+ // OnHeadersStart or OnPushPromiseStart methods.
+ void Reset() {
+ HTTP2_DVLOG(2) << "HpackBlockDecoder::Reset";
+ before_entry_ = true;
+ }
+
+ // Decode the fragment of the HPACK block contained in the decode buffer.
+ // Expected to be called from an implementation of Http2FrameDecoderListener's
+ // OnHpackFragment method.
+ DecodeStatus Decode(DecodeBuffer* db);
+
+ // Is the decoding process between entries (i.e. would the next byte be the
+ // first byte of a new HPACK entry)?
+ bool before_entry() const { return before_entry_; }
+
+ // Return error code after decoding error occurred in HpackEntryDecoder.
+ HpackDecodingError error() const { return entry_decoder_.error(); }
+
+ std::string DebugString() const;
+
+ private:
+ HpackEntryDecoder entry_decoder_;
+ HpackEntryDecoderListener* const listener_;
+ bool before_entry_ = true;
+};
+
+QUICHE_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+ const HpackBlockDecoder& v);
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_BLOCK_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder_test.cc
new file mode 100644
index 00000000000..e8a1cb47620
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_block_decoder_test.cc
@@ -0,0 +1,291 @@
+// 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 "quiche/http2/hpack/decoder/hpack_block_decoder.h"
+
+// Tests of HpackBlockDecoder.
+
+#include <cstdint>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/decoder/decode_buffer.h"
+#include "quiche/http2/hpack/decoder/hpack_block_collector.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/http2/hpack/tools/hpack_block_builder.h"
+#include "quiche/http2/hpack/tools/hpack_example.h"
+#include "quiche/http2/test_tools/http2_random.h"
+#include "quiche/http2/tools/random_decoder_test.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+namespace http2 {
+namespace test {
+namespace {
+
+class HpackBlockDecoderTest : public RandomDecoderTest {
+ protected:
+ HpackBlockDecoderTest() : listener_(&collector_), decoder_(&listener_) {
+ stop_decode_on_done_ = false;
+ decoder_.Reset();
+ // Make sure logging doesn't crash. Not examining the result.
+ std::ostringstream strm;
+ strm << decoder_;
+ }
+
+ DecodeStatus StartDecoding(DecodeBuffer* db) override {
+ collector_.Clear();
+ decoder_.Reset();
+ return ResumeDecoding(db);
+ }
+
+ DecodeStatus ResumeDecoding(DecodeBuffer* db) override {
+ DecodeStatus status = decoder_.Decode(db);
+
+ // Make sure logging doesn't crash. Not examining the result.
+ std::ostringstream strm;
+ strm << decoder_;
+
+ return status;
+ }
+
+ AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* db,
+ const Validator& validator) {
+ bool return_non_zero_on_first = false;
+ return RandomDecoderTest::DecodeAndValidateSeveralWays(
+ db, return_non_zero_on_first, validator);
+ }
+
+ AssertionResult DecodeAndValidateSeveralWays(const HpackBlockBuilder& hbb,
+ const Validator& validator) {
+ DecodeBuffer db(hbb.buffer());
+ return DecodeAndValidateSeveralWays(&db, validator);
+ }
+
+ AssertionResult DecodeHpackExampleAndValidateSeveralWays(
+ absl::string_view hpack_example,
+ Validator validator) {
+ std::string input = HpackExampleToStringOrDie(hpack_example);
+ DecodeBuffer db(input);
+ return DecodeAndValidateSeveralWays(&db, validator);
+ }
+
+ uint8_t Rand8() { return Random().Rand8(); }
+
+ std::string Rand8String() { return Random().RandString(Rand8()); }
+
+ HpackBlockCollector collector_;
+ HpackEntryDecoderVLoggingListener listener_;
+ HpackBlockDecoder decoder_;
+};
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.1
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_1) {
+ auto do_check = [this]() {
+ return collector_.ValidateSoleLiteralNameValueHeader(
+ HpackEntryType::kIndexedLiteralHeader, false, "custom-key", false,
+ "custom-header");
+ };
+ const char hpack_example[] = R"(
+ 40 | == Literal indexed ==
+ 0a | Literal name (len = 10)
+ 6375 7374 6f6d 2d6b 6579 | custom-key
+ 0d | Literal value (len = 13)
+ 6375 7374 6f6d 2d68 6561 6465 72 | custom-header
+ | -> custom-key:
+ | custom-header
+ )";
+ EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
+ hpack_example, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.2
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_2) {
+ auto do_check = [this]() {
+ return collector_.ValidateSoleLiteralValueHeader(
+ HpackEntryType::kUnindexedLiteralHeader, 4, false, "/sample/path");
+ };
+ const char hpack_example[] = R"(
+ 04 | == Literal not indexed ==
+ | Indexed name (idx = 4)
+ | :path
+ 0c | Literal value (len = 12)
+ 2f73 616d 706c 652f 7061 7468 | /sample/path
+ | -> :path: /sample/path
+ )";
+ EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
+ hpack_example, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.3
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_3) {
+ auto do_check = [this]() {
+ return collector_.ValidateSoleLiteralNameValueHeader(
+ HpackEntryType::kNeverIndexedLiteralHeader, false, "password", false,
+ "secret");
+ };
+ const char hpack_example[] = R"(
+ 10 | == Literal never indexed ==
+ 08 | Literal name (len = 8)
+ 7061 7373 776f 7264 | password
+ 06 | Literal value (len = 6)
+ 7365 6372 6574 | secret
+ | -> password: secret
+ )";
+ EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
+ hpack_example, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.2.4
+TEST_F(HpackBlockDecoderTest, SpecExample_C_2_4) {
+ auto do_check = [this]() { return collector_.ValidateSoleIndexedHeader(2); };
+ const char hpack_example[] = R"(
+ 82 | == Indexed - Add ==
+ | idx = 2
+ | -> :method: GET
+ )";
+ EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
+ hpack_example, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+}
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.3.1
+TEST_F(HpackBlockDecoderTest, SpecExample_C_3_1) {
+ std::string example = R"(
+ 82 | == Indexed - Add ==
+ | idx = 2
+ | -> :method: GET
+ 86 | == Indexed - Add ==
+ | idx = 6
+ | -> :scheme: http
+ 84 | == Indexed - Add ==
+ | idx = 4
+ | -> :path: /
+ 41 | == Literal indexed ==
+ | Indexed name (idx = 1)
+ | :authority
+ 0f | Literal value (len = 15)
+ 7777 772e 6578 616d 706c 652e 636f 6d | www.example.com
+ | -> :authority:
+ | www.example.com
+ )";
+ HpackBlockCollector expected;
+ expected.ExpectIndexedHeader(2);
+ expected.ExpectIndexedHeader(6);
+ expected.ExpectIndexedHeader(4);
+ expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+ 1, false, "www.example.com");
+ NoArgValidator do_check = [expected, this]() {
+ return collector_.VerifyEq(expected);
+ };
+ EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
+ example, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+}
+
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.5.1
+TEST_F(HpackBlockDecoderTest, SpecExample_C_5_1) {
+ std::string example = R"(
+ 48 | == Literal indexed ==
+ | Indexed name (idx = 8)
+ | :status
+ 03 | Literal value (len = 3)
+ 3330 32 | 302
+ | -> :status: 302
+ 58 | == Literal indexed ==
+ | Indexed name (idx = 24)
+ | cache-control
+ 07 | Literal value (len = 7)
+ 7072 6976 6174 65 | private
+ | -> cache-control: private
+ 61 | == Literal indexed ==
+ | Indexed name (idx = 33)
+ | date
+ 1d | Literal value (len = 29)
+ 4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013
+ 2032 303a 3133 3a32 3120 474d 54 | 20:13:21 GMT
+ | -> date: Mon, 21 Oct 2013
+ | 20:13:21 GMT
+ 6e | == Literal indexed ==
+ | Indexed name (idx = 46)
+ | location
+ 17 | Literal value (len = 23)
+ 6874 7470 733a 2f2f 7777 772e 6578 616d | https://www.exam
+ 706c 652e 636f 6d | ple.com
+ | -> location:
+ | https://www.example.com
+ )";
+ HpackBlockCollector expected;
+ expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+ 8, false, "302");
+ expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+ 24, false, "private");
+ expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+ 33, false,
+ "Mon, 21 Oct 2013 20:13:21 GMT");
+ expected.ExpectNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader,
+ 46, false, "https://www.example.com");
+ NoArgValidator do_check = [expected, this]() {
+ return collector_.VerifyEq(expected);
+ };
+ EXPECT_TRUE(DecodeHpackExampleAndValidateSeveralWays(
+ example, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+}
+
+// Generate a bunch of HPACK block entries to expect, use those expectations
+// to generate an HPACK block, then decode it and confirm it matches those
+// expectations. Some of these are invalid (such as Indexed, with index=0),
+// but well-formed, and the decoder doesn't check for validity, just
+// well-formedness. That includes the validity of the strings not being checked,
+// such as lower-case ascii for the names, and valid Huffman encodings.
+TEST_F(HpackBlockDecoderTest, Computed) {
+ HpackBlockCollector expected;
+ expected.ExpectIndexedHeader(0);
+ expected.ExpectIndexedHeader(1);
+ expected.ExpectIndexedHeader(126);
+ expected.ExpectIndexedHeader(127);
+ expected.ExpectIndexedHeader(128);
+ expected.ExpectDynamicTableSizeUpdate(0);
+ expected.ExpectDynamicTableSizeUpdate(1);
+ expected.ExpectDynamicTableSizeUpdate(14);
+ expected.ExpectDynamicTableSizeUpdate(15);
+ expected.ExpectDynamicTableSizeUpdate(30);
+ expected.ExpectDynamicTableSizeUpdate(31);
+ expected.ExpectDynamicTableSizeUpdate(4095);
+ expected.ExpectDynamicTableSizeUpdate(4096);
+ expected.ExpectDynamicTableSizeUpdate(8192);
+ for (auto type : {HpackEntryType::kIndexedLiteralHeader,
+ HpackEntryType::kUnindexedLiteralHeader,
+ HpackEntryType::kNeverIndexedLiteralHeader}) {
+ for (bool value_huffman : {false, true}) {
+ // An entry with an index for the name. Ensure the name index
+ // is not zero by adding one to the Rand8() result.
+ expected.ExpectNameIndexAndLiteralValue(type, Rand8() + 1, value_huffman,
+ Rand8String());
+ // And two entries with literal names, one plain, one huffman encoded.
+ expected.ExpectLiteralNameAndValue(type, false, Rand8String(),
+ value_huffman, Rand8String());
+ expected.ExpectLiteralNameAndValue(type, true, Rand8String(),
+ value_huffman, Rand8String());
+ }
+ }
+ // Shuffle the entries and serialize them to produce an HPACK block.
+ expected.ShuffleEntries(RandomPtr());
+ HpackBlockBuilder hbb;
+ expected.AppendToHpackBlockBuilder(&hbb);
+
+ NoArgValidator do_check = [expected, this]() {
+ return collector_.VerifyEq(expected);
+ };
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder.cc
new file mode 100644
index 00000000000..77ad8bd543e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder.cc
@@ -0,0 +1,125 @@
+// Copyright 2017 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 "quiche/http2/hpack/decoder/hpack_decoder.h"
+
+#include "quiche/http2/decoder/decode_status.h"
+#include "quiche/http2/platform/api/http2_flag_utils.h"
+#include "quiche/http2/platform/api/http2_flags.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+
+namespace http2 {
+
+HpackDecoder::HpackDecoder(HpackDecoderListener* listener,
+ size_t max_string_size)
+ : decoder_state_(listener),
+ entry_buffer_(&decoder_state_, max_string_size),
+ block_decoder_(&entry_buffer_),
+ error_(HpackDecodingError::kOk) {}
+
+HpackDecoder::~HpackDecoder() = default;
+
+void HpackDecoder::set_max_string_size_bytes(size_t max_string_size_bytes) {
+ entry_buffer_.set_max_string_size_bytes(max_string_size_bytes);
+}
+
+void HpackDecoder::ApplyHeaderTableSizeSetting(uint32_t max_header_table_size) {
+ decoder_state_.ApplyHeaderTableSizeSetting(max_header_table_size);
+}
+
+bool HpackDecoder::StartDecodingBlock() {
+ HTTP2_DVLOG(3) << "HpackDecoder::StartDecodingBlock, error_detected="
+ << (DetectError() ? "true" : "false");
+ if (DetectError()) {
+ return false;
+ }
+ // TODO(jamessynge): Eliminate Reset(), which shouldn't be necessary
+ // if there are no errors, and shouldn't be necessary with errors if
+ // we never resume decoding after an error has been detected.
+ block_decoder_.Reset();
+ decoder_state_.OnHeaderBlockStart();
+ return true;
+}
+
+bool HpackDecoder::DecodeFragment(DecodeBuffer* db) {
+ HTTP2_DVLOG(3) << "HpackDecoder::DecodeFragment, error_detected="
+ << (DetectError() ? "true" : "false")
+ << ", size=" << db->Remaining();
+ if (DetectError()) {
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 3, 23);
+ return false;
+ }
+ // Decode contents of db as an HPACK block fragment, forwards the decoded
+ // entries to entry_buffer_, which in turn forwards them to decode_state_,
+ // which finally forwards them to the HpackDecoderListener.
+ DecodeStatus status = block_decoder_.Decode(db);
+ if (status == DecodeStatus::kDecodeError) {
+ ReportError(block_decoder_.error(), "");
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 4, 23);
+ return false;
+ } else if (DetectError()) {
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 5, 23);
+ return false;
+ }
+ // Should be positioned between entries iff decoding is complete.
+ QUICHE_DCHECK_EQ(block_decoder_.before_entry(),
+ status == DecodeStatus::kDecodeDone)
+ << status;
+ if (!block_decoder_.before_entry()) {
+ entry_buffer_.BufferStringsIfUnbuffered();
+ }
+ return true;
+}
+
+bool HpackDecoder::EndDecodingBlock() {
+ HTTP2_DVLOG(3) << "HpackDecoder::EndDecodingBlock, error_detected="
+ << (DetectError() ? "true" : "false");
+ if (DetectError()) {
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 6, 23);
+ return false;
+ }
+ if (!block_decoder_.before_entry()) {
+ // The HPACK block ended in the middle of an entry.
+ ReportError(HpackDecodingError::kTruncatedBlock, "");
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 7, 23);
+ return false;
+ }
+ decoder_state_.OnHeaderBlockEnd();
+ if (DetectError()) {
+ // HpackDecoderState will have reported the error.
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 8, 23);
+ return false;
+ }
+ return true;
+}
+
+bool HpackDecoder::DetectError() {
+ if (error_ != HpackDecodingError::kOk) {
+ return true;
+ }
+
+ if (decoder_state_.error() != HpackDecodingError::kOk) {
+ HTTP2_DVLOG(2) << "Error detected in decoder_state_";
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 10, 23);
+ error_ = decoder_state_.error();
+ detailed_error_ = decoder_state_.detailed_error();
+ }
+
+ return error_ != HpackDecodingError::kOk;
+}
+
+void HpackDecoder::ReportError(HpackDecodingError error,
+ std::string detailed_error) {
+ HTTP2_DVLOG(3) << "HpackDecoder::ReportError is new="
+ << (error_ == HpackDecodingError::kOk ? "true" : "false")
+ << ", error: " << HpackDecodingErrorToString(error);
+ if (error_ == HpackDecodingError::kOk) {
+ error_ = error;
+ detailed_error_ = detailed_error;
+ decoder_state_.listener()->OnHeaderErrorDetected(
+ HpackDecodingErrorToString(error));
+ }
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder.h
new file mode 100644
index 00000000000..393a6ca0c40
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder.h
@@ -0,0 +1,132 @@
+// Copyright 2017 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_DECODER_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_H_
+
+// Decodes HPACK blocks, calls an HpackDecoderListener with the decoded header
+// entries. Also notifies the listener of errors and of the boundaries of the
+// HPACK blocks.
+
+// TODO(jamessynge): Add feature allowing an HpackEntryDecoderListener
+// sub-class (and possibly others) to be passed in for counting events,
+// so that deciding whether to count is not done by having lots of if
+// statements, but instead by inserting an indirection only when needed.
+
+// TODO(jamessynge): Consider whether to return false from methods below
+// when an error has been previously detected. It protects calling code
+// from its failure to pay attention to previous errors, but should we
+// spend time to do that?
+
+#include <stddef.h>
+
+#include <cstdint>
+
+#include "quiche/http2/decoder/decode_buffer.h"
+#include "quiche/http2/hpack/decoder/hpack_block_decoder.h"
+#include "quiche/http2/hpack/decoder/hpack_decoder_listener.h"
+#include "quiche/http2/hpack/decoder/hpack_decoder_state.h"
+#include "quiche/http2/hpack/decoder/hpack_decoder_tables.h"
+#include "quiche/http2/hpack/decoder/hpack_decoding_error.h"
+#include "quiche/http2/hpack/decoder/hpack_whole_entry_buffer.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+namespace test {
+class HpackDecoderPeer;
+} // namespace test
+
+class QUICHE_EXPORT_PRIVATE HpackDecoder {
+ public:
+ HpackDecoder(HpackDecoderListener* listener, size_t max_string_size);
+ virtual ~HpackDecoder();
+
+ HpackDecoder(const HpackDecoder&) = delete;
+ HpackDecoder& operator=(const HpackDecoder&) = delete;
+
+ // max_string_size specifies the maximum size of an on-the-wire string (name
+ // or value, plain or Huffman encoded) that will be accepted. See sections
+ // 5.1 and 5.2 of RFC 7541. This is a defense against OOM attacks; HTTP/2
+ // allows a decoder to enforce any limit of the size of the header lists
+ // that it is willing to decode, including less than the MAX_HEADER_LIST_SIZE
+ // setting, a setting that is initially unlimited. For example, we might
+ // choose to send a MAX_HEADER_LIST_SIZE of 64KB, and to use that same value
+ // as the upper bound for individual strings.
+ void set_max_string_size_bytes(size_t max_string_size_bytes);
+
+ // ApplyHeaderTableSizeSetting notifies this object that this endpoint has
+ // received a SETTINGS ACK frame acknowledging an earlier SETTINGS frame from
+ // this endpoint specifying a new value for SETTINGS_HEADER_TABLE_SIZE (the
+ // maximum size of the dynamic table that this endpoint will use to decode
+ // HPACK blocks).
+ // Because a SETTINGS frame can contain SETTINGS_HEADER_TABLE_SIZE values,
+ // the caller must keep track of those multiple changes, and make
+ // corresponding calls to this method. In particular, a call must be made
+ // with the lowest value acknowledged by the peer, and a call must be made
+ // with the final value acknowledged, in that order; additional calls may
+ // be made if additional values were sent. These calls must be made between
+ // decoding the SETTINGS ACK, and before the next HPACK block is decoded.
+ void ApplyHeaderTableSizeSetting(uint32_t max_header_table_size);
+
+ // Returns the most recently applied value of SETTINGS_HEADER_TABLE_SIZE.
+ size_t GetCurrentHeaderTableSizeSetting() const {
+ return decoder_state_.GetCurrentHeaderTableSizeSetting();
+ }
+
+ // Prepares the decoder for decoding a new HPACK block, and announces this to
+ // its listener. Returns true if OK to continue with decoding, false if an
+ // error has been detected, which for StartDecodingBlock means the error was
+ // detected while decoding a previous HPACK block.
+ bool StartDecodingBlock();
+
+ // Decodes a fragment (some or all of the remainder) of an HPACK block,
+ // reporting header entries (name & value pairs) that it completely decodes
+ // in the process to the listener. Returns true successfully decoded, false if
+ // an error has been detected, either during decoding of the fragment, or
+ // prior to this call.
+ bool DecodeFragment(DecodeBuffer* db);
+
+ // Completes the process of decoding an HPACK block: if the HPACK block was
+ // properly terminated, announces the end of the header list to the listener
+ // and returns true; else returns false.
+ bool EndDecodingBlock();
+
+ // If no error has been detected so far, query |decoder_state_| for errors and
+ // set |error_| if necessary. Returns true if an error has ever been
+ // detected.
+ bool DetectError();
+
+ size_t GetDynamicTableSize() const {
+ return decoder_state_.GetDynamicTableSize();
+ }
+
+ // Error code if an error has occurred, HpackDecodingError::kOk otherwise.
+ HpackDecodingError error() const { return error_; }
+
+ std::string detailed_error() const { return detailed_error_; }
+
+ private:
+ friend class test::HpackDecoderPeer;
+
+ // Reports an error to the listener IF this is the first error detected.
+ void ReportError(HpackDecodingError error, std::string detailed_error);
+
+ // The decompressor state, as defined by HPACK (i.e. the static and dynamic
+ // tables).
+ HpackDecoderState decoder_state_;
+
+ // Assembles the various parts of a header entry into whole entries.
+ HpackWholeEntryBuffer entry_buffer_;
+
+ // The decoder of HPACK blocks into entry parts, passed to entry_buffer_.
+ HpackBlockDecoder block_decoder_;
+
+ // Error code if an error has occurred, HpackDecodingError::kOk otherwise.
+ HpackDecodingError error_;
+ std::string detailed_error_;
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_listener.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_listener.cc
new file mode 100644
index 00000000000..75a59695c27
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_listener.cc
@@ -0,0 +1,29 @@
+// 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 "quiche/http2/hpack/decoder/hpack_decoder_listener.h"
+
+namespace http2 {
+
+HpackDecoderListener::HpackDecoderListener() = default;
+HpackDecoderListener::~HpackDecoderListener() = default;
+
+HpackDecoderNoOpListener::HpackDecoderNoOpListener() = default;
+HpackDecoderNoOpListener::~HpackDecoderNoOpListener() = default;
+
+void HpackDecoderNoOpListener::OnHeaderListStart() {}
+void HpackDecoderNoOpListener::OnHeader(const std::string& /*name*/,
+ const std::string& /*value*/) {}
+void HpackDecoderNoOpListener::OnHeaderListEnd() {}
+void HpackDecoderNoOpListener::OnHeaderErrorDetected(
+ absl::string_view /*error_message*/) {}
+
+// static
+HpackDecoderNoOpListener* HpackDecoderNoOpListener::NoOpListener() {
+ static HpackDecoderNoOpListener* static_instance =
+ new HpackDecoderNoOpListener();
+ return static_instance;
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_listener.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_listener.h
new file mode 100644
index 00000000000..bd87c629340
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_listener.h
@@ -0,0 +1,61 @@
+// 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.
+
+// Defines HpackDecoderListener, the base class of listeners for HTTP header
+// lists decoded from an HPACK block.
+
+#ifndef QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_LISTENER_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_LISTENER_H_
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+class QUICHE_EXPORT_PRIVATE HpackDecoderListener {
+ public:
+ HpackDecoderListener();
+ virtual ~HpackDecoderListener();
+
+ // OnHeaderListStart is called at the start of decoding an HPACK block into
+ // an HTTP/2 header list. Will only be called once per block, even if it
+ // extends into CONTINUATION frames.
+ virtual void OnHeaderListStart() = 0;
+
+ // Called for each header name-value pair that is decoded, in the order they
+ // appear in the HPACK block. Multiple values for a given key will be emitted
+ // as multiple calls to OnHeader.
+ virtual void OnHeader(const std::string& name, const std::string& value) = 0;
+
+ // OnHeaderListEnd is called after successfully decoding an HPACK block into
+ // an HTTP/2 header list. Will only be called once per block, even if it
+ // extends into CONTINUATION frames.
+ virtual void OnHeaderListEnd() = 0;
+
+ // OnHeaderErrorDetected is called if an error is detected while decoding.
+ // error_message may be used in a GOAWAY frame as the Opaque Data.
+ virtual void OnHeaderErrorDetected(absl::string_view error_message) = 0;
+};
+
+// A no-op implementation of HpackDecoderListener, useful for ignoring
+// callbacks once an error is detected.
+class QUICHE_EXPORT_PRIVATE HpackDecoderNoOpListener
+ : public HpackDecoderListener {
+ public:
+ HpackDecoderNoOpListener();
+ ~HpackDecoderNoOpListener() override;
+
+ void OnHeaderListStart() override;
+ void OnHeader(const std::string& name, const std::string& value) override;
+ void OnHeaderListEnd() override;
+ void OnHeaderErrorDetected(absl::string_view error_message) override;
+
+ // Returns a listener that ignores all the calls.
+ static HpackDecoderNoOpListener* NoOpListener();
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_LISTENER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state.cc
new file mode 100644
index 00000000000..c8672bb291f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state.cc
@@ -0,0 +1,226 @@
+// 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 "quiche/http2/hpack/decoder/hpack_decoder_state.h"
+
+#include <utility>
+
+#include "quiche/http2/http2_constants.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_logging.h"
+
+namespace http2 {
+namespace {
+
+std::string ExtractString(HpackDecoderStringBuffer* string_buffer) {
+ if (string_buffer->IsBuffered()) {
+ return string_buffer->ReleaseString();
+ } else {
+ auto result = std::string(string_buffer->str());
+ string_buffer->Reset();
+ return result;
+ }
+}
+
+} // namespace
+
+HpackDecoderState::HpackDecoderState(HpackDecoderListener* listener)
+ : listener_(listener),
+ final_header_table_size_(Http2SettingsInfo::DefaultHeaderTableSize()),
+ lowest_header_table_size_(final_header_table_size_),
+ require_dynamic_table_size_update_(false),
+ allow_dynamic_table_size_update_(true),
+ saw_dynamic_table_size_update_(false),
+ error_(HpackDecodingError::kOk) {
+ QUICHE_CHECK(listener_);
+}
+
+HpackDecoderState::~HpackDecoderState() = default;
+
+void HpackDecoderState::ApplyHeaderTableSizeSetting(
+ uint32_t header_table_size) {
+ HTTP2_DVLOG(2) << "HpackDecoderState::ApplyHeaderTableSizeSetting("
+ << header_table_size << ")";
+ QUICHE_DCHECK_LE(lowest_header_table_size_, final_header_table_size_);
+ if (header_table_size < lowest_header_table_size_) {
+ lowest_header_table_size_ = header_table_size;
+ }
+ final_header_table_size_ = header_table_size;
+ HTTP2_DVLOG(2) << "low water mark: " << lowest_header_table_size_;
+ HTTP2_DVLOG(2) << "final limit: " << final_header_table_size_;
+}
+
+// Called to notify this object that we're starting to decode an HPACK block
+// (e.g. a HEADERS or PUSH_PROMISE frame's header has been decoded).
+void HpackDecoderState::OnHeaderBlockStart() {
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnHeaderBlockStart";
+ // This instance can't be reused after an error has been detected, as we must
+ // assume that the encoder and decoder compression states are no longer
+ // synchronized.
+ QUICHE_DCHECK(error_ == HpackDecodingError::kOk)
+ << HpackDecodingErrorToString(error_);
+ QUICHE_DCHECK_LE(lowest_header_table_size_, final_header_table_size_);
+ allow_dynamic_table_size_update_ = true;
+ saw_dynamic_table_size_update_ = false;
+ // If the peer has acknowledged a HEADER_TABLE_SIZE smaller than that which
+ // its HPACK encoder has been using, then the next HPACK block it sends MUST
+ // start with a Dynamic Table Size Update entry that is at least as low as
+ // lowest_header_table_size_. That may be followed by another as great as
+ // final_header_table_size_, if those are different.
+ require_dynamic_table_size_update_ =
+ (lowest_header_table_size_ <
+ decoder_tables_.current_header_table_size() ||
+ final_header_table_size_ < decoder_tables_.header_table_size_limit());
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnHeaderListStart "
+ << "require_dynamic_table_size_update_="
+ << require_dynamic_table_size_update_;
+ listener_->OnHeaderListStart();
+}
+
+void HpackDecoderState::OnIndexedHeader(size_t index) {
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnIndexedHeader: " << index;
+ if (error_ != HpackDecodingError::kOk) {
+ return;
+ }
+ if (require_dynamic_table_size_update_) {
+ ReportError(HpackDecodingError::kMissingDynamicTableSizeUpdate, "");
+ return;
+ }
+ allow_dynamic_table_size_update_ = false;
+ const HpackStringPair* entry = decoder_tables_.Lookup(index);
+ if (entry != nullptr) {
+ listener_->OnHeader(entry->name, entry->value);
+ } else {
+ ReportError(HpackDecodingError::kInvalidIndex, "");
+ }
+}
+
+void HpackDecoderState::OnNameIndexAndLiteralValue(
+ HpackEntryType entry_type,
+ size_t name_index,
+ HpackDecoderStringBuffer* value_buffer) {
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnNameIndexAndLiteralValue "
+ << entry_type << ", " << name_index << ", "
+ << value_buffer->str();
+ if (error_ != HpackDecodingError::kOk) {
+ return;
+ }
+ if (require_dynamic_table_size_update_) {
+ ReportError(HpackDecodingError::kMissingDynamicTableSizeUpdate, "");
+ return;
+ }
+ allow_dynamic_table_size_update_ = false;
+ const HpackStringPair* entry = decoder_tables_.Lookup(name_index);
+ if (entry != nullptr) {
+ std::string value(ExtractString(value_buffer));
+ listener_->OnHeader(entry->name, value);
+ if (entry_type == HpackEntryType::kIndexedLiteralHeader) {
+ decoder_tables_.Insert(entry->name, std::move(value));
+ }
+ } else {
+ ReportError(HpackDecodingError::kInvalidNameIndex, "");
+ }
+}
+
+void HpackDecoderState::OnLiteralNameAndValue(
+ HpackEntryType entry_type,
+ HpackDecoderStringBuffer* name_buffer,
+ HpackDecoderStringBuffer* value_buffer) {
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnLiteralNameAndValue " << entry_type
+ << ", " << name_buffer->str() << ", " << value_buffer->str();
+ if (error_ != HpackDecodingError::kOk) {
+ return;
+ }
+ if (require_dynamic_table_size_update_) {
+ ReportError(HpackDecodingError::kMissingDynamicTableSizeUpdate, "");
+ return;
+ }
+ allow_dynamic_table_size_update_ = false;
+ std::string name(ExtractString(name_buffer));
+ std::string value(ExtractString(value_buffer));
+ listener_->OnHeader(name, value);
+ if (entry_type == HpackEntryType::kIndexedLiteralHeader) {
+ decoder_tables_.Insert(std::move(name), std::move(value));
+ }
+}
+
+void HpackDecoderState::OnDynamicTableSizeUpdate(size_t size_limit) {
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnDynamicTableSizeUpdate " << size_limit
+ << ", required="
+ << (require_dynamic_table_size_update_ ? "true" : "false")
+ << ", allowed="
+ << (allow_dynamic_table_size_update_ ? "true" : "false");
+ if (error_ != HpackDecodingError::kOk) {
+ return;
+ }
+ QUICHE_DCHECK_LE(lowest_header_table_size_, final_header_table_size_);
+ if (!allow_dynamic_table_size_update_) {
+ // At most two dynamic table size updates allowed at the start, and not
+ // after a header.
+ ReportError(HpackDecodingError::kDynamicTableSizeUpdateNotAllowed, "");
+ return;
+ }
+ if (require_dynamic_table_size_update_) {
+ // The new size must not be greater than the low water mark.
+ if (size_limit > lowest_header_table_size_) {
+ ReportError(
+ HpackDecodingError::kInitialDynamicTableSizeUpdateIsAboveLowWaterMark,
+ "");
+ return;
+ }
+ require_dynamic_table_size_update_ = false;
+ } else if (size_limit > final_header_table_size_) {
+ // The new size must not be greater than the final max header table size
+ // that the peer acknowledged.
+ ReportError(
+ HpackDecodingError::kDynamicTableSizeUpdateIsAboveAcknowledgedSetting,
+ "");
+ return;
+ }
+ decoder_tables_.DynamicTableSizeUpdate(size_limit);
+ if (saw_dynamic_table_size_update_) {
+ allow_dynamic_table_size_update_ = false;
+ } else {
+ saw_dynamic_table_size_update_ = true;
+ }
+ // We no longer need to keep an eye out for a lower header table size.
+ lowest_header_table_size_ = final_header_table_size_;
+}
+
+void HpackDecoderState::OnHpackDecodeError(HpackDecodingError error,
+ std::string detailed_error) {
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnHpackDecodeError "
+ << HpackDecodingErrorToString(error);
+ if (error_ == HpackDecodingError::kOk) {
+ ReportError(error, detailed_error);
+ }
+}
+
+void HpackDecoderState::OnHeaderBlockEnd() {
+ HTTP2_DVLOG(2) << "HpackDecoderState::OnHeaderBlockEnd";
+ if (error_ != HpackDecodingError::kOk) {
+ return;
+ }
+ if (require_dynamic_table_size_update_) {
+ // Apparently the HPACK block was empty, but we needed it to contain at
+ // least 1 dynamic table size update.
+ ReportError(HpackDecodingError::kMissingDynamicTableSizeUpdate, "");
+ } else {
+ listener_->OnHeaderListEnd();
+ }
+}
+
+void HpackDecoderState::ReportError(HpackDecodingError error,
+ std::string detailed_error) {
+ HTTP2_DVLOG(2) << "HpackDecoderState::ReportError is new="
+ << (error_ == HpackDecodingError::kOk ? "true" : "false")
+ << ", error: " << HpackDecodingErrorToString(error);
+ if (error_ == HpackDecodingError::kOk) {
+ listener_->OnHeaderErrorDetected(HpackDecodingErrorToString(error));
+ error_ = error;
+ detailed_error_ = detailed_error;
+ }
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state.h
new file mode 100644
index 00000000000..272805f79c6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state.h
@@ -0,0 +1,138 @@
+// 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.
+
+// HpackDecoderState maintains the HPACK decompressor state; i.e. updates the
+// HPACK dynamic table according to RFC 7541 as the entries in an HPACK block
+// are decoded, and reads from the static and dynamic tables in order to build
+// complete header entries. Calls an HpackDecoderListener with the completely
+// decoded headers (i.e. after resolving table indices into names or values),
+// thus translating the decoded HPACK entries into HTTP/2 headers.
+
+#ifndef QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_STATE_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_STATE_H_
+
+#include <stddef.h>
+
+#include <cstdint>
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/decoder/hpack_decoder_listener.h"
+#include "quiche/http2/hpack/decoder/hpack_decoder_string_buffer.h"
+#include "quiche/http2/hpack/decoder/hpack_decoder_tables.h"
+#include "quiche/http2/hpack/decoder/hpack_decoding_error.h"
+#include "quiche/http2/hpack/decoder/hpack_whole_entry_listener.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+namespace test {
+class HpackDecoderStatePeer;
+} // namespace test
+
+class QUICHE_EXPORT_PRIVATE HpackDecoderState : public HpackWholeEntryListener {
+ public:
+ explicit HpackDecoderState(HpackDecoderListener* listener);
+ ~HpackDecoderState() override;
+
+ HpackDecoderState(const HpackDecoderState&) = delete;
+ HpackDecoderState& operator=(const HpackDecoderState&) = delete;
+
+ // Set the listener to be notified when a whole entry has been decoded,
+ // including resolving name or name and value references.
+ // The listener may be changed at any time.
+ HpackDecoderListener* listener() const { return listener_; }
+
+ // ApplyHeaderTableSizeSetting notifies this object that this endpoint has
+ // received a SETTINGS ACK frame acknowledging an earlier SETTINGS frame from
+ // this endpoint specifying a new value for SETTINGS_HEADER_TABLE_SIZE (the
+ // maximum size of the dynamic table that this endpoint will use to decode
+ // HPACK blocks).
+ // Because a SETTINGS frame can contain SETTINGS_HEADER_TABLE_SIZE values,
+ // the caller must keep track of those multiple changes, and make
+ // corresponding calls to this method. In particular, a call must be made
+ // with the lowest value acknowledged by the peer, and a call must be made
+ // with the final value acknowledged, in that order; additional calls may
+ // be made if additional values were sent. These calls must be made between
+ // decoding the SETTINGS ACK, and before the next HPACK block is decoded.
+ void ApplyHeaderTableSizeSetting(uint32_t max_header_table_size);
+
+ // Returns the most recently applied value of SETTINGS_HEADER_TABLE_SIZE.
+ size_t GetCurrentHeaderTableSizeSetting() const {
+ return final_header_table_size_;
+ }
+
+ // OnHeaderBlockStart notifies this object that we're starting to decode the
+ // HPACK payload of a HEADERS or PUSH_PROMISE frame.
+ void OnHeaderBlockStart();
+
+ // Implement the HpackWholeEntryListener methods, each of which notifies this
+ // object when an entire entry has been decoded.
+ void OnIndexedHeader(size_t index) override;
+ void OnNameIndexAndLiteralValue(
+ HpackEntryType entry_type,
+ size_t name_index,
+ HpackDecoderStringBuffer* value_buffer) override;
+ void OnLiteralNameAndValue(HpackEntryType entry_type,
+ HpackDecoderStringBuffer* name_buffer,
+ HpackDecoderStringBuffer* value_buffer) override;
+ void OnDynamicTableSizeUpdate(size_t size) override;
+ void OnHpackDecodeError(HpackDecodingError error,
+ std::string detailed_error) override;
+
+ // OnHeaderBlockEnd notifies this object that an entire HPACK block has been
+ // decoded, which might have extended into CONTINUATION blocks.
+ void OnHeaderBlockEnd();
+
+ // Returns error code after an error has been detected and reported.
+ // No further callbacks will be made to the listener.
+ HpackDecodingError error() const { return error_; }
+
+ size_t GetDynamicTableSize() const {
+ return decoder_tables_.current_header_table_size();
+ }
+
+ const HpackDecoderTables& decoder_tables_for_test() const {
+ return decoder_tables_;
+ }
+
+ std::string detailed_error() const { return detailed_error_; }
+
+ private:
+ friend class test::HpackDecoderStatePeer;
+
+ // Reports an error to the listener IF this is the first error detected.
+ void ReportError(HpackDecodingError error, std::string detailed_error);
+
+ // The static and dynamic HPACK tables.
+ HpackDecoderTables decoder_tables_;
+
+ // The listener to be notified of headers, the start and end of header
+ // lists, and of errors.
+ HpackDecoderListener* listener_;
+
+ // The most recent HEADER_TABLE_SIZE setting acknowledged by the peer.
+ uint32_t final_header_table_size_;
+
+ // The lowest HEADER_TABLE_SIZE setting acknowledged by the peer; valid until
+ // the next HPACK block is decoded.
+ // TODO(jamessynge): Test raising the HEADER_TABLE_SIZE.
+ uint32_t lowest_header_table_size_;
+
+ // Must the next (first) HPACK entry be a dynamic table size update?
+ bool require_dynamic_table_size_update_;
+
+ // May the next (first or second) HPACK entry be a dynamic table size update?
+ bool allow_dynamic_table_size_update_;
+
+ // Have we already seen a dynamic table size update in this HPACK block?
+ bool saw_dynamic_table_size_update_;
+
+ // Has an error already been detected and reported to the listener?
+ HpackDecodingError error_;
+ std::string detailed_error_;
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_STATE_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state_test.cc
new file mode 100644
index 00000000000..03a47519ce1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_state_test.cc
@@ -0,0 +1,549 @@
+// 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 "quiche/http2/hpack/decoder/hpack_decoder_state.h"
+
+// Tests of HpackDecoderState.
+
+#include <utility>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/http2/http2_constants.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::Eq;
+using ::testing::Mock;
+using ::testing::StrictMock;
+
+namespace http2 {
+namespace test {
+class HpackDecoderStatePeer {
+ public:
+ static HpackDecoderTables* GetDecoderTables(HpackDecoderState* state) {
+ return &state->decoder_tables_;
+ }
+};
+
+namespace {
+
+class MockHpackDecoderListener : public HpackDecoderListener {
+ public:
+ MOCK_METHOD(void, OnHeaderListStart, (), (override));
+ MOCK_METHOD(void,
+ OnHeader,
+ (const std::string& name, const std::string& value),
+ (override));
+ MOCK_METHOD(void, OnHeaderListEnd, (), (override));
+ MOCK_METHOD(void,
+ OnHeaderErrorDetected,
+ (absl::string_view error_message),
+ (override));
+};
+
+enum StringBacking { STATIC, UNBUFFERED, BUFFERED };
+
+class HpackDecoderStateTest : public QuicheTest {
+ protected:
+ HpackDecoderStateTest() : decoder_state_(&listener_) {}
+
+ HpackDecoderTables* GetDecoderTables() {
+ return HpackDecoderStatePeer::GetDecoderTables(&decoder_state_);
+ }
+
+ const HpackStringPair* Lookup(size_t index) {
+ return GetDecoderTables()->Lookup(index);
+ }
+
+ size_t current_header_table_size() {
+ return GetDecoderTables()->current_header_table_size();
+ }
+
+ size_t header_table_size_limit() {
+ return GetDecoderTables()->header_table_size_limit();
+ }
+
+ void set_header_table_size_limit(size_t size) {
+ GetDecoderTables()->DynamicTableSizeUpdate(size);
+ }
+
+ void SetStringBuffer(const char* s,
+ StringBacking backing,
+ HpackDecoderStringBuffer* string_buffer) {
+ switch (backing) {
+ case STATIC:
+ string_buffer->Set(s, true);
+ break;
+ case UNBUFFERED:
+ string_buffer->Set(s, false);
+ break;
+ case BUFFERED:
+ string_buffer->Set(s, false);
+ string_buffer->BufferStringIfUnbuffered();
+ break;
+ }
+ }
+
+ void SetName(const char* s, StringBacking backing) {
+ SetStringBuffer(s, backing, &name_buffer_);
+ }
+
+ void SetValue(const char* s, StringBacking backing) {
+ SetStringBuffer(s, backing, &value_buffer_);
+ }
+
+ void SendStartAndVerifyCallback() {
+ EXPECT_CALL(listener_, OnHeaderListStart());
+ decoder_state_.OnHeaderBlockStart();
+ Mock::VerifyAndClearExpectations(&listener_);
+ }
+
+ void SendSizeUpdate(size_t size) {
+ decoder_state_.OnDynamicTableSizeUpdate(size);
+ Mock::VerifyAndClearExpectations(&listener_);
+ }
+
+ void SendIndexAndVerifyCallback(size_t index,
+ HpackEntryType /*expected_type*/,
+ const char* expected_name,
+ const char* expected_value) {
+ EXPECT_CALL(listener_, OnHeader(Eq(expected_name), Eq(expected_value)));
+ decoder_state_.OnIndexedHeader(index);
+ Mock::VerifyAndClearExpectations(&listener_);
+ }
+
+ void SendValueAndVerifyCallback(size_t name_index,
+ HpackEntryType entry_type,
+ const char* name,
+ const char* value,
+ StringBacking value_backing) {
+ SetValue(value, value_backing);
+ EXPECT_CALL(listener_, OnHeader(Eq(name), Eq(value)));
+ decoder_state_.OnNameIndexAndLiteralValue(entry_type, name_index,
+ &value_buffer_);
+ Mock::VerifyAndClearExpectations(&listener_);
+ }
+
+ void SendNameAndValueAndVerifyCallback(HpackEntryType entry_type,
+ const char* name,
+ StringBacking name_backing,
+ const char* value,
+ StringBacking value_backing) {
+ SetName(name, name_backing);
+ SetValue(value, value_backing);
+ EXPECT_CALL(listener_, OnHeader(Eq(name), Eq(value)));
+ decoder_state_.OnLiteralNameAndValue(entry_type, &name_buffer_,
+ &value_buffer_);
+ Mock::VerifyAndClearExpectations(&listener_);
+ }
+
+ void SendEndAndVerifyCallback() {
+ EXPECT_CALL(listener_, OnHeaderListEnd());
+ decoder_state_.OnHeaderBlockEnd();
+ Mock::VerifyAndClearExpectations(&listener_);
+ }
+
+ // dynamic_index is one-based, because that is the way RFC 7541 shows it.
+ AssertionResult VerifyEntry(size_t dynamic_index,
+ const char* name,
+ const char* value) {
+ const HpackStringPair* entry =
+ Lookup(dynamic_index + kFirstDynamicTableIndex - 1);
+ VERIFY_NE(entry, nullptr);
+ VERIFY_EQ(entry->name, name);
+ VERIFY_EQ(entry->value, value);
+ return AssertionSuccess();
+ }
+ AssertionResult VerifyNoEntry(size_t dynamic_index) {
+ const HpackStringPair* entry =
+ Lookup(dynamic_index + kFirstDynamicTableIndex - 1);
+ VERIFY_EQ(entry, nullptr);
+ return AssertionSuccess();
+ }
+ AssertionResult VerifyDynamicTableContents(
+ const std::vector<std::pair<const char*, const char*>>& entries) {
+ size_t index = 1;
+ for (const auto& entry : entries) {
+ VERIFY_SUCCESS(VerifyEntry(index, entry.first, entry.second));
+ ++index;
+ }
+ VERIFY_SUCCESS(VerifyNoEntry(index));
+ return AssertionSuccess();
+ }
+
+ StrictMock<MockHpackDecoderListener> listener_;
+ HpackDecoderState decoder_state_;
+ HpackDecoderStringBuffer name_buffer_, value_buffer_;
+};
+
+// Test based on RFC 7541, section C.3: Request Examples without Huffman Coding.
+// This section shows several consecutive header lists, corresponding to HTTP
+// requests, on the same connection.
+TEST_F(HpackDecoderStateTest, C3_RequestExamples) {
+ // C.3.1 First Request
+ //
+ // Header list to encode:
+ //
+ // :method: GET
+ // :scheme: http
+ // :path: /
+ // :authority: www.example.com
+
+ SendStartAndVerifyCallback();
+ SendIndexAndVerifyCallback(2, HpackEntryType::kIndexedHeader, ":method",
+ "GET");
+ SendIndexAndVerifyCallback(6, HpackEntryType::kIndexedHeader, ":scheme",
+ "http");
+ SendIndexAndVerifyCallback(4, HpackEntryType::kIndexedHeader, ":path", "/");
+ SendValueAndVerifyCallback(1, HpackEntryType::kIndexedLiteralHeader,
+ ":authority", "www.example.com", UNBUFFERED);
+ SendEndAndVerifyCallback();
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 57) :authority: www.example.com
+ // Table size: 57
+
+ ASSERT_TRUE(VerifyDynamicTableContents({{":authority", "www.example.com"}}));
+ ASSERT_EQ(57u, current_header_table_size());
+
+ // C.3.2 Second Request
+ //
+ // Header list to encode:
+ //
+ // :method: GET
+ // :scheme: http
+ // :path: /
+ // :authority: www.example.com
+ // cache-control: no-cache
+
+ SendStartAndVerifyCallback();
+ SendIndexAndVerifyCallback(2, HpackEntryType::kIndexedHeader, ":method",
+ "GET");
+ SendIndexAndVerifyCallback(6, HpackEntryType::kIndexedHeader, ":scheme",
+ "http");
+ SendIndexAndVerifyCallback(4, HpackEntryType::kIndexedHeader, ":path", "/");
+ SendIndexAndVerifyCallback(62, HpackEntryType::kIndexedHeader, ":authority",
+ "www.example.com");
+ SendValueAndVerifyCallback(24, HpackEntryType::kIndexedLiteralHeader,
+ "cache-control", "no-cache", UNBUFFERED);
+ SendEndAndVerifyCallback();
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 53) cache-control: no-cache
+ // [ 2] (s = 57) :authority: www.example.com
+ // Table size: 110
+
+ ASSERT_TRUE(VerifyDynamicTableContents(
+ {{"cache-control", "no-cache"}, {":authority", "www.example.com"}}));
+ ASSERT_EQ(110u, current_header_table_size());
+
+ // C.3.3 Third Request
+ //
+ // Header list to encode:
+ //
+ // :method: GET
+ // :scheme: https
+ // :path: /index.html
+ // :authority: www.example.com
+ // custom-key: custom-value
+
+ SendStartAndVerifyCallback();
+ SendIndexAndVerifyCallback(2, HpackEntryType::kIndexedHeader, ":method",
+ "GET");
+ SendIndexAndVerifyCallback(7, HpackEntryType::kIndexedHeader, ":scheme",
+ "https");
+ SendIndexAndVerifyCallback(5, HpackEntryType::kIndexedHeader, ":path",
+ "/index.html");
+ SendIndexAndVerifyCallback(63, HpackEntryType::kIndexedHeader, ":authority",
+ "www.example.com");
+ SendNameAndValueAndVerifyCallback(HpackEntryType::kIndexedLiteralHeader,
+ "custom-key", UNBUFFERED, "custom-value",
+ UNBUFFERED);
+ SendEndAndVerifyCallback();
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 54) custom-key: custom-value
+ // [ 2] (s = 53) cache-control: no-cache
+ // [ 3] (s = 57) :authority: www.example.com
+ // Table size: 164
+
+ ASSERT_TRUE(VerifyDynamicTableContents({{"custom-key", "custom-value"},
+ {"cache-control", "no-cache"},
+ {":authority", "www.example.com"}}));
+ ASSERT_EQ(164u, current_header_table_size());
+}
+
+// Test based on RFC 7541, section C.5: Response Examples without Huffman
+// Coding. This section shows several consecutive header lists, corresponding
+// to HTTP responses, on the same connection. The HTTP/2 setting parameter
+// SETTINGS_HEADER_TABLE_SIZE is set to the value of 256 octets, causing
+// some evictions to occur.
+TEST_F(HpackDecoderStateTest, C5_ResponseExamples) {
+ set_header_table_size_limit(256);
+
+ // C.5.1 First Response
+ //
+ // Header list to encode:
+ //
+ // :status: 302
+ // cache-control: private
+ // date: Mon, 21 Oct 2013 20:13:21 GMT
+ // location: https://www.example.com
+
+ SendStartAndVerifyCallback();
+ SendValueAndVerifyCallback(8, HpackEntryType::kIndexedLiteralHeader,
+ ":status", "302", BUFFERED);
+ SendValueAndVerifyCallback(24, HpackEntryType::kIndexedLiteralHeader,
+ "cache-control", "private", UNBUFFERED);
+ SendValueAndVerifyCallback(33, HpackEntryType::kIndexedLiteralHeader, "date",
+ "Mon, 21 Oct 2013 20:13:21 GMT", UNBUFFERED);
+ SendValueAndVerifyCallback(46, HpackEntryType::kIndexedLiteralHeader,
+ "location", "https://www.example.com", UNBUFFERED);
+ SendEndAndVerifyCallback();
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 63) location: https://www.example.com
+ // [ 2] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT
+ // [ 3] (s = 52) cache-control: private
+ // [ 4] (s = 42) :status: 302
+ // Table size: 222
+
+ ASSERT_TRUE(
+ VerifyDynamicTableContents({{"location", "https://www.example.com"},
+ {"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ {"cache-control", "private"},
+ {":status", "302"}}));
+ ASSERT_EQ(222u, current_header_table_size());
+
+ // C.5.2 Second Response
+ //
+ // The (":status", "302") header field is evicted from the dynamic table to
+ // free space to allow adding the (":status", "307") header field.
+ //
+ // Header list to encode:
+ //
+ // :status: 307
+ // cache-control: private
+ // date: Mon, 21 Oct 2013 20:13:21 GMT
+ // location: https://www.example.com
+
+ SendStartAndVerifyCallback();
+ SendValueAndVerifyCallback(8, HpackEntryType::kIndexedLiteralHeader,
+ ":status", "307", BUFFERED);
+ SendIndexAndVerifyCallback(65, HpackEntryType::kIndexedHeader,
+ "cache-control", "private");
+ SendIndexAndVerifyCallback(64, HpackEntryType::kIndexedHeader, "date",
+ "Mon, 21 Oct 2013 20:13:21 GMT");
+ SendIndexAndVerifyCallback(63, HpackEntryType::kIndexedHeader, "location",
+ "https://www.example.com");
+ SendEndAndVerifyCallback();
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 42) :status: 307
+ // [ 2] (s = 63) location: https://www.example.com
+ // [ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT
+ // [ 4] (s = 52) cache-control: private
+ // Table size: 222
+
+ ASSERT_TRUE(
+ VerifyDynamicTableContents({{":status", "307"},
+ {"location", "https://www.example.com"},
+ {"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ {"cache-control", "private"}}));
+ ASSERT_EQ(222u, current_header_table_size());
+
+ // C.5.3 Third Response
+ //
+ // Several header fields are evicted from the dynamic table during the
+ // processing of this header list.
+ //
+ // Header list to encode:
+ //
+ // :status: 200
+ // cache-control: private
+ // date: Mon, 21 Oct 2013 20:13:22 GMT
+ // location: https://www.example.com
+ // content-encoding: gzip
+ // set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1
+
+ SendStartAndVerifyCallback();
+ SendIndexAndVerifyCallback(8, HpackEntryType::kIndexedHeader, ":status",
+ "200");
+ SendIndexAndVerifyCallback(65, HpackEntryType::kIndexedHeader,
+ "cache-control", "private");
+ SendValueAndVerifyCallback(33, HpackEntryType::kIndexedLiteralHeader, "date",
+ "Mon, 21 Oct 2013 20:13:22 GMT", BUFFERED);
+ SendIndexAndVerifyCallback(64, HpackEntryType::kIndexedHeader, "location",
+ "https://www.example.com");
+ SendValueAndVerifyCallback(26, HpackEntryType::kIndexedLiteralHeader,
+ "content-encoding", "gzip", UNBUFFERED);
+ SendValueAndVerifyCallback(
+ 55, HpackEntryType::kIndexedLiteralHeader, "set-cookie",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", BUFFERED);
+ SendEndAndVerifyCallback();
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;
+ // max-age=3600; version=1
+ // [ 2] (s = 52) content-encoding: gzip
+ // [ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:22 GMT
+ // Table size: 215
+
+ ASSERT_TRUE(VerifyDynamicTableContents(
+ {{"set-cookie",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"},
+ {"content-encoding", "gzip"},
+ {"date", "Mon, 21 Oct 2013 20:13:22 GMT"}}));
+ ASSERT_EQ(215u, current_header_table_size());
+}
+
+// Confirm that the table size can be changed, but at most twice.
+TEST_F(HpackDecoderStateTest, OptionalTableSizeChanges) {
+ SendStartAndVerifyCallback();
+ EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(),
+ header_table_size_limit());
+ SendSizeUpdate(1024);
+ EXPECT_EQ(1024u, header_table_size_limit());
+ SendSizeUpdate(0);
+ EXPECT_EQ(0u, header_table_size_limit());
+
+ // Three updates aren't allowed.
+ EXPECT_CALL(listener_, OnHeaderErrorDetected(
+ Eq("Dynamic table size update not allowed")));
+ SendSizeUpdate(0);
+}
+
+// Confirm that required size updates are indeed required before headers.
+TEST_F(HpackDecoderStateTest, RequiredTableSizeChangeBeforeHeader) {
+ EXPECT_EQ(4096u, decoder_state_.GetCurrentHeaderTableSizeSetting());
+ decoder_state_.ApplyHeaderTableSizeSetting(1024);
+ decoder_state_.ApplyHeaderTableSizeSetting(2048);
+ EXPECT_EQ(2048u, decoder_state_.GetCurrentHeaderTableSizeSetting());
+
+ // First provide the required update, and an allowed second update.
+ SendStartAndVerifyCallback();
+ EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(),
+ header_table_size_limit());
+ SendSizeUpdate(1024);
+ EXPECT_EQ(1024u, header_table_size_limit());
+ SendSizeUpdate(1500);
+ EXPECT_EQ(1500u, header_table_size_limit());
+ SendEndAndVerifyCallback();
+
+ // Another HPACK block, but this time missing the required size update.
+ decoder_state_.ApplyHeaderTableSizeSetting(1024);
+ EXPECT_EQ(1024u, decoder_state_.GetCurrentHeaderTableSizeSetting());
+ SendStartAndVerifyCallback();
+ EXPECT_CALL(listener_,
+ OnHeaderErrorDetected(Eq("Missing dynamic table size update")));
+ decoder_state_.OnIndexedHeader(1);
+
+ // Further decoded entries are ignored.
+ decoder_state_.OnIndexedHeader(1);
+ decoder_state_.OnDynamicTableSizeUpdate(1);
+ SetValue("value", UNBUFFERED);
+ decoder_state_.OnNameIndexAndLiteralValue(
+ HpackEntryType::kIndexedLiteralHeader, 4, &value_buffer_);
+ SetName("name", UNBUFFERED);
+ decoder_state_.OnLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader,
+ &name_buffer_, &value_buffer_);
+ decoder_state_.OnHeaderBlockEnd();
+ decoder_state_.OnHpackDecodeError(HpackDecodingError::kIndexVarintError, "");
+}
+
+// Confirm that required size updates are validated.
+TEST_F(HpackDecoderStateTest, InvalidRequiredSizeUpdate) {
+ // Require a size update, but provide one that isn't small enough.
+ decoder_state_.ApplyHeaderTableSizeSetting(1024);
+ SendStartAndVerifyCallback();
+ EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(),
+ header_table_size_limit());
+ EXPECT_CALL(
+ listener_,
+ OnHeaderErrorDetected(
+ Eq("Initial dynamic table size update is above low water mark")));
+ SendSizeUpdate(2048);
+}
+
+// Confirm that required size updates are indeed required before the end.
+TEST_F(HpackDecoderStateTest, RequiredTableSizeChangeBeforeEnd) {
+ decoder_state_.ApplyHeaderTableSizeSetting(1024);
+ SendStartAndVerifyCallback();
+ EXPECT_CALL(listener_,
+ OnHeaderErrorDetected(Eq("Missing dynamic table size update")));
+ decoder_state_.OnHeaderBlockEnd();
+}
+
+// Confirm that optional size updates are validated.
+TEST_F(HpackDecoderStateTest, InvalidOptionalSizeUpdate) {
+ // Require a size update, but provide one that isn't small enough.
+ SendStartAndVerifyCallback();
+ EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(),
+ header_table_size_limit());
+ EXPECT_CALL(listener_,
+ OnHeaderErrorDetected(Eq(
+ "Dynamic table size update is above acknowledged setting")));
+ SendSizeUpdate(Http2SettingsInfo::DefaultHeaderTableSize() + 1);
+}
+
+TEST_F(HpackDecoderStateTest, InvalidStaticIndex) {
+ SendStartAndVerifyCallback();
+ EXPECT_CALL(listener_,
+ OnHeaderErrorDetected(
+ Eq("Invalid index in indexed header field representation")));
+ decoder_state_.OnIndexedHeader(0);
+}
+
+TEST_F(HpackDecoderStateTest, InvalidDynamicIndex) {
+ SendStartAndVerifyCallback();
+ EXPECT_CALL(listener_,
+ OnHeaderErrorDetected(
+ Eq("Invalid index in indexed header field representation")));
+ decoder_state_.OnIndexedHeader(kFirstDynamicTableIndex);
+}
+
+TEST_F(HpackDecoderStateTest, InvalidNameIndex) {
+ SendStartAndVerifyCallback();
+ EXPECT_CALL(listener_,
+ OnHeaderErrorDetected(Eq("Invalid index in literal header field "
+ "with indexed name representation")));
+ SetValue("value", UNBUFFERED);
+ decoder_state_.OnNameIndexAndLiteralValue(
+ HpackEntryType::kIndexedLiteralHeader, kFirstDynamicTableIndex,
+ &value_buffer_);
+}
+
+TEST_F(HpackDecoderStateTest, ErrorsSuppressCallbacks) {
+ SendStartAndVerifyCallback();
+ EXPECT_CALL(listener_,
+ OnHeaderErrorDetected(Eq("Name Huffman encoding error")));
+ decoder_state_.OnHpackDecodeError(HpackDecodingError::kNameHuffmanError, "");
+
+ // Further decoded entries are ignored.
+ decoder_state_.OnIndexedHeader(1);
+ decoder_state_.OnDynamicTableSizeUpdate(1);
+ SetValue("value", UNBUFFERED);
+ decoder_state_.OnNameIndexAndLiteralValue(
+ HpackEntryType::kIndexedLiteralHeader, 4, &value_buffer_);
+ SetName("name", UNBUFFERED);
+ decoder_state_.OnLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader,
+ &name_buffer_, &value_buffer_);
+ decoder_state_.OnHeaderBlockEnd();
+ decoder_state_.OnHpackDecodeError(HpackDecodingError::kIndexVarintError, "");
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer.cc
new file mode 100644
index 00000000000..e928686ceb9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer.cc
@@ -0,0 +1,239 @@
+// 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 "quiche/http2/hpack/decoder/hpack_decoder_string_buffer.h"
+
+#include <utility>
+
+#include "quiche/http2/platform/api/http2_bug_tracker.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+
+namespace http2 {
+
+std::ostream& operator<<(std::ostream& out,
+ const HpackDecoderStringBuffer::State v) {
+ switch (v) {
+ case HpackDecoderStringBuffer::State::RESET:
+ return out << "RESET";
+ case HpackDecoderStringBuffer::State::COLLECTING:
+ return out << "COLLECTING";
+ case HpackDecoderStringBuffer::State::COMPLETE:
+ return out << "COMPLETE";
+ }
+ // Since the value doesn't come over the wire, only a programming bug should
+ // result in reaching this point.
+ int unknown = static_cast<int>(v);
+ HTTP2_BUG(http2_bug_50_1)
+ << "Invalid HpackDecoderStringBuffer::State: " << unknown;
+ return out << "HpackDecoderStringBuffer::State(" << unknown << ")";
+}
+
+std::ostream& operator<<(std::ostream& out,
+ const HpackDecoderStringBuffer::Backing v) {
+ switch (v) {
+ case HpackDecoderStringBuffer::Backing::RESET:
+ return out << "RESET";
+ case HpackDecoderStringBuffer::Backing::UNBUFFERED:
+ return out << "UNBUFFERED";
+ case HpackDecoderStringBuffer::Backing::BUFFERED:
+ return out << "BUFFERED";
+ case HpackDecoderStringBuffer::Backing::STATIC:
+ return out << "STATIC";
+ }
+ // Since the value doesn't come over the wire, only a programming bug should
+ // result in reaching this point.
+ auto v2 = static_cast<int>(v);
+ HTTP2_BUG(http2_bug_50_2)
+ << "Invalid HpackDecoderStringBuffer::Backing: " << v2;
+ return out << "HpackDecoderStringBuffer::Backing(" << v2 << ")";
+}
+
+HpackDecoderStringBuffer::HpackDecoderStringBuffer()
+ : remaining_len_(0),
+ is_huffman_encoded_(false),
+ state_(State::RESET),
+ backing_(Backing::RESET) {}
+HpackDecoderStringBuffer::~HpackDecoderStringBuffer() = default;
+
+void HpackDecoderStringBuffer::Reset() {
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::Reset";
+ state_ = State::RESET;
+}
+
+void HpackDecoderStringBuffer::Set(absl::string_view value, bool is_static) {
+ HTTP2_DVLOG(2) << "HpackDecoderStringBuffer::Set";
+ QUICHE_DCHECK_EQ(state_, State::RESET);
+ value_ = value;
+ state_ = State::COMPLETE;
+ backing_ = is_static ? Backing::STATIC : Backing::UNBUFFERED;
+ // TODO(jamessynge): Determine which of these two fields must be set.
+ remaining_len_ = 0;
+ is_huffman_encoded_ = false;
+}
+
+void HpackDecoderStringBuffer::OnStart(bool huffman_encoded, size_t len) {
+ HTTP2_DVLOG(2) << "HpackDecoderStringBuffer::OnStart";
+ QUICHE_DCHECK_EQ(state_, State::RESET);
+
+ remaining_len_ = len;
+ is_huffman_encoded_ = huffman_encoded;
+ state_ = State::COLLECTING;
+
+ if (huffman_encoded) {
+ // We don't set, clear or use value_ for buffered strings until OnEnd.
+ decoder_.Reset();
+ buffer_.clear();
+ backing_ = Backing::BUFFERED;
+
+ // Reserve space in buffer_ for the uncompressed string, assuming the
+ // maximum expansion. The shortest Huffman codes in the RFC are 5 bits long,
+ // which then expand to 8 bits during decoding (i.e. each code is for one
+ // plain text octet, aka byte), so the maximum size is 60% longer than the
+ // encoded size.
+ len = len * 8 / 5;
+ if (buffer_.capacity() < len) {
+ buffer_.reserve(len);
+ }
+ } else {
+ // Assume for now that we won't need to use buffer_, so don't reserve space
+ // in it.
+ backing_ = Backing::RESET;
+ // OnData is not called for empty (zero length) strings, so make sure that
+ // value_ is cleared.
+ value_ = absl::string_view();
+ }
+}
+
+bool HpackDecoderStringBuffer::OnData(const char* data, size_t len) {
+ HTTP2_DVLOG(2) << "HpackDecoderStringBuffer::OnData state=" << state_
+ << ", backing=" << backing_;
+ QUICHE_DCHECK_EQ(state_, State::COLLECTING);
+ QUICHE_DCHECK_LE(len, remaining_len_);
+ remaining_len_ -= len;
+
+ if (is_huffman_encoded_) {
+ QUICHE_DCHECK_EQ(backing_, Backing::BUFFERED);
+ return decoder_.Decode(absl::string_view(data, len), &buffer_);
+ }
+
+ if (backing_ == Backing::RESET) {
+ // This is the first call to OnData. If data contains the entire string,
+ // don't copy the string. If we later find that the HPACK entry is split
+ // across input buffers, then we'll copy the string into buffer_.
+ if (remaining_len_ == 0) {
+ value_ = absl::string_view(data, len);
+ backing_ = Backing::UNBUFFERED;
+ return true;
+ }
+
+ // We need to buffer the string because it is split across input buffers.
+ // Reserve space in buffer_ for the entire string.
+ backing_ = Backing::BUFFERED;
+ buffer_.reserve(remaining_len_ + len);
+ buffer_.assign(data, len);
+ return true;
+ }
+
+ // This is not the first call to OnData for this string, so it should be
+ // buffered.
+ QUICHE_DCHECK_EQ(backing_, Backing::BUFFERED);
+
+ // Append to the current contents of the buffer.
+ buffer_.append(data, len);
+ return true;
+}
+
+bool HpackDecoderStringBuffer::OnEnd() {
+ HTTP2_DVLOG(2) << "HpackDecoderStringBuffer::OnEnd";
+ QUICHE_DCHECK_EQ(state_, State::COLLECTING);
+ QUICHE_DCHECK_EQ(0u, remaining_len_);
+
+ if (is_huffman_encoded_) {
+ QUICHE_DCHECK_EQ(backing_, Backing::BUFFERED);
+ // Did the Huffman encoding of the string end properly?
+ if (!decoder_.InputProperlyTerminated()) {
+ return false; // No, it didn't.
+ }
+ value_ = buffer_;
+ } else if (backing_ == Backing::BUFFERED) {
+ value_ = buffer_;
+ }
+ state_ = State::COMPLETE;
+ return true;
+}
+
+void HpackDecoderStringBuffer::BufferStringIfUnbuffered() {
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::BufferStringIfUnbuffered state="
+ << state_ << ", backing=" << backing_;
+ if (state_ != State::RESET && backing_ == Backing::UNBUFFERED) {
+ HTTP2_DVLOG(2)
+ << "HpackDecoderStringBuffer buffering std::string of length "
+ << value_.size();
+ buffer_.assign(value_.data(), value_.size());
+ if (state_ == State::COMPLETE) {
+ value_ = buffer_;
+ }
+ backing_ = Backing::BUFFERED;
+ }
+}
+
+bool HpackDecoderStringBuffer::IsBuffered() const {
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::IsBuffered";
+ return state_ != State::RESET && backing_ == Backing::BUFFERED;
+}
+
+size_t HpackDecoderStringBuffer::BufferedLength() const {
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::BufferedLength";
+ return IsBuffered() ? buffer_.size() : 0;
+}
+
+absl::string_view HpackDecoderStringBuffer::str() const {
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::str";
+ QUICHE_DCHECK_EQ(state_, State::COMPLETE);
+ return value_;
+}
+
+absl::string_view HpackDecoderStringBuffer::GetStringIfComplete() const {
+ if (state_ != State::COMPLETE) {
+ return {};
+ }
+ return str();
+}
+
+std::string HpackDecoderStringBuffer::ReleaseString() {
+ HTTP2_DVLOG(3) << "HpackDecoderStringBuffer::ReleaseString";
+ QUICHE_DCHECK_EQ(state_, State::COMPLETE);
+ QUICHE_DCHECK_EQ(backing_, Backing::BUFFERED);
+ if (state_ == State::COMPLETE) {
+ state_ = State::RESET;
+ if (backing_ == Backing::BUFFERED) {
+ return std::move(buffer_);
+ } else {
+ return std::string(value_);
+ }
+ }
+ return "";
+}
+
+void HpackDecoderStringBuffer::OutputDebugStringTo(std::ostream& out) const {
+ out << "{state=" << state_;
+ if (state_ != State::RESET) {
+ out << ", backing=" << backing_;
+ out << ", remaining_len=" << remaining_len_;
+ out << ", is_huffman_encoded=" << is_huffman_encoded_;
+ if (backing_ == Backing::BUFFERED) {
+ out << ", buffer: " << buffer_;
+ } else {
+ out << ", value: " << value_;
+ }
+ }
+ out << "}";
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackDecoderStringBuffer& v) {
+ v.OutputDebugStringTo(out);
+ return out;
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer.h
new file mode 100644
index 00000000000..a1877c88496
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer.h
@@ -0,0 +1,102 @@
+// 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_DECODER_STRING_BUFFER_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_STRING_BUFFER_H_
+
+// HpackDecoderStringBuffer helps an HPACK decoder to avoid copies of a string
+// literal (name or value) except when necessary (e.g. when split across two
+// or more HPACK block fragments).
+
+#include <stddef.h>
+
+#include <ostream>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/huffman/hpack_huffman_decoder.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+class QUICHE_EXPORT_PRIVATE HpackDecoderStringBuffer {
+ public:
+ enum class State : uint8_t { RESET, COLLECTING, COMPLETE };
+ enum class Backing : uint8_t { RESET, UNBUFFERED, BUFFERED, STATIC };
+
+ HpackDecoderStringBuffer();
+ ~HpackDecoderStringBuffer();
+
+ HpackDecoderStringBuffer(const HpackDecoderStringBuffer&) = delete;
+ HpackDecoderStringBuffer& operator=(const HpackDecoderStringBuffer&) = delete;
+
+ void Reset();
+ void Set(absl::string_view value, bool is_static);
+
+ // Note that for Huffman encoded strings the length of the string after
+ // decoding may be larger (expected), the same or even smaller; the latter
+ // are unlikely, but possible if the encoder makes odd choices.
+ void OnStart(bool huffman_encoded, size_t len);
+ bool OnData(const char* data, size_t len);
+ bool OnEnd();
+ void BufferStringIfUnbuffered();
+ bool IsBuffered() const;
+ size_t BufferedLength() const;
+
+ // Accessors for the completely collected string (i.e. Set or OnEnd has just
+ // been called, and no reset of the state has occurred).
+
+ // Returns a string_view pointing to the backing store for the string,
+ // either the internal buffer or the original transport buffer (e.g. for a
+ // literal value that wasn't Huffman encoded, and that wasn't split across
+ // transport buffers).
+ absl::string_view str() const;
+
+ // Same as str() if state_ is COMPLETE. Otherwise, returns empty string piece.
+ absl::string_view GetStringIfComplete() const;
+
+ // Returns the completely collected string by value, using std::move in an
+ // effort to avoid unnecessary copies. ReleaseString() must not be called
+ // unless the string has been buffered (to avoid forcing a potentially
+ // unnecessary copy). ReleaseString() also resets the instance so that it can
+ // be used to collect another string.
+ std::string ReleaseString();
+
+ State state_for_testing() const { return state_; }
+ Backing backing_for_testing() const { return backing_; }
+ void OutputDebugStringTo(std::ostream& out) const;
+
+ private:
+ // Storage for the string being buffered, if buffering is necessary
+ // (e.g. if Huffman encoded, buffer_ is storage for the decoded string).
+ std::string buffer_;
+
+ // The string_view to be returned by HpackDecoderStringBuffer::str(). If
+ // a string has been collected, but not buffered, value_ points to that
+ // string.
+ absl::string_view value_;
+
+ // The decoder to use if the string is Huffman encoded.
+ HpackHuffmanDecoder decoder_;
+
+ // Count of bytes not yet passed to OnData.
+ size_t remaining_len_;
+
+ // Is the HPACK string Huffman encoded?
+ bool is_huffman_encoded_;
+
+ // State of the string decoding process.
+ State state_;
+
+ // Where is the string stored?
+ Backing backing_;
+};
+
+QUICHE_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& out,
+ const HpackDecoderStringBuffer& v);
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_STRING_BUFFER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc
new file mode 100644
index 00000000000..63785388ffc
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_string_buffer_test.cc
@@ -0,0 +1,249 @@
+// 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 "quiche/http2/hpack/decoder/hpack_decoder_string_buffer.h"
+
+// Tests of HpackDecoderStringBuffer.
+
+#include <initializer_list>
+
+#include "absl/strings/escaping.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::HasSubstr;
+
+namespace http2 {
+namespace test {
+namespace {
+
+class HpackDecoderStringBufferTest : public QuicheTest {
+ protected:
+ typedef HpackDecoderStringBuffer::State State;
+ typedef HpackDecoderStringBuffer::Backing Backing;
+
+ State state() const { return buf_.state_for_testing(); }
+ Backing backing() const { return buf_.backing_for_testing(); }
+
+ // We want to know that HTTP2_LOG(x) << buf_ will work in production should
+ // that be needed, so we test that it outputs the expected values.
+ AssertionResult VerifyLogHasSubstrs(std::initializer_list<std::string> strs) {
+ HTTP2_VLOG(1) << buf_;
+ std::ostringstream ss;
+ buf_.OutputDebugStringTo(ss);
+ std::string dbg_str(ss.str());
+ for (const auto& expected : strs) {
+ VERIFY_THAT(dbg_str, HasSubstr(expected));
+ }
+ return AssertionSuccess();
+ }
+
+ HpackDecoderStringBuffer buf_;
+};
+
+TEST_F(HpackDecoderStringBufferTest, SetStatic) {
+ absl::string_view data("static string");
+
+ EXPECT_EQ(state(), State::RESET);
+ EXPECT_TRUE(VerifyLogHasSubstrs({"state=RESET"}));
+
+ buf_.Set(data, /*is_static*/ true);
+ HTTP2_LOG(INFO) << buf_;
+ EXPECT_EQ(state(), State::COMPLETE);
+ EXPECT_EQ(backing(), Backing::STATIC);
+ EXPECT_EQ(data, buf_.str());
+ EXPECT_EQ(data.data(), buf_.str().data());
+ EXPECT_TRUE(VerifyLogHasSubstrs(
+ {"state=COMPLETE", "backing=STATIC", "value: static string"}));
+
+ // The string is static, so BufferStringIfUnbuffered won't change anything.
+ buf_.BufferStringIfUnbuffered();
+ EXPECT_EQ(state(), State::COMPLETE);
+ EXPECT_EQ(backing(), Backing::STATIC);
+ EXPECT_EQ(data, buf_.str());
+ EXPECT_EQ(data.data(), buf_.str().data());
+ EXPECT_TRUE(VerifyLogHasSubstrs(
+ {"state=COMPLETE", "backing=STATIC", "value: static string"}));
+}
+
+TEST_F(HpackDecoderStringBufferTest, PlainWhole) {
+ absl::string_view data("some text.");
+
+ HTTP2_LOG(INFO) << buf_;
+ EXPECT_EQ(state(), State::RESET);
+
+ buf_.OnStart(/*huffman_encoded*/ false, data.size());
+ EXPECT_EQ(state(), State::COLLECTING);
+ EXPECT_EQ(backing(), Backing::RESET);
+ HTTP2_LOG(INFO) << buf_;
+
+ EXPECT_TRUE(buf_.OnData(data.data(), data.size()));
+ EXPECT_EQ(state(), State::COLLECTING);
+ EXPECT_EQ(backing(), Backing::UNBUFFERED);
+
+ EXPECT_TRUE(buf_.OnEnd());
+ EXPECT_EQ(state(), State::COMPLETE);
+ EXPECT_EQ(backing(), Backing::UNBUFFERED);
+ EXPECT_EQ(0u, buf_.BufferedLength());
+ EXPECT_TRUE(VerifyLogHasSubstrs(
+ {"state=COMPLETE", "backing=UNBUFFERED", "value: some text."}));
+
+ // We expect that the string buffer points to the passed in
+ // string_view's backing store.
+ EXPECT_EQ(data.data(), buf_.str().data());
+
+ // Now force it to buffer the string, after which it will still have the same
+ // string value, but the backing store will be different.
+ buf_.BufferStringIfUnbuffered();
+ HTTP2_LOG(INFO) << buf_;
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+ EXPECT_EQ(buf_.BufferedLength(), data.size());
+ EXPECT_EQ(data, buf_.str());
+ EXPECT_NE(data.data(), buf_.str().data());
+ EXPECT_TRUE(VerifyLogHasSubstrs(
+ {"state=COMPLETE", "backing=BUFFERED", "buffer: some text."}));
+}
+
+TEST_F(HpackDecoderStringBufferTest, PlainSplit) {
+ absl::string_view data("some text.");
+ absl::string_view part1 = data.substr(0, 1);
+ absl::string_view part2 = data.substr(1);
+
+ EXPECT_EQ(state(), State::RESET);
+ buf_.OnStart(/*huffman_encoded*/ false, data.size());
+ EXPECT_EQ(state(), State::COLLECTING);
+ EXPECT_EQ(backing(), Backing::RESET);
+
+ // OnData with only a part of the data, not the whole, so buf_ will buffer
+ // the data.
+ EXPECT_TRUE(buf_.OnData(part1.data(), part1.size()));
+ EXPECT_EQ(state(), State::COLLECTING);
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+ EXPECT_EQ(buf_.BufferedLength(), part1.size());
+ HTTP2_LOG(INFO) << buf_;
+
+ EXPECT_TRUE(buf_.OnData(part2.data(), part2.size()));
+ EXPECT_EQ(state(), State::COLLECTING);
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+ EXPECT_EQ(buf_.BufferedLength(), data.size());
+
+ EXPECT_TRUE(buf_.OnEnd());
+ EXPECT_EQ(state(), State::COMPLETE);
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+ EXPECT_EQ(buf_.BufferedLength(), data.size());
+ HTTP2_LOG(INFO) << buf_;
+
+ absl::string_view buffered = buf_.str();
+ EXPECT_EQ(data, buffered);
+ EXPECT_NE(data.data(), buffered.data());
+
+ // The string is already buffered, so BufferStringIfUnbuffered should not make
+ // any change.
+ buf_.BufferStringIfUnbuffered();
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+ EXPECT_EQ(buf_.BufferedLength(), data.size());
+ EXPECT_EQ(buffered, buf_.str());
+ EXPECT_EQ(buffered.data(), buf_.str().data());
+}
+
+TEST_F(HpackDecoderStringBufferTest, HuffmanWhole) {
+ std::string encoded = absl::HexStringToBytes("f1e3c2e5f23a6ba0ab90f4ff");
+ absl::string_view decoded("www.example.com");
+
+ EXPECT_EQ(state(), State::RESET);
+ buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+ EXPECT_EQ(state(), State::COLLECTING);
+
+ EXPECT_TRUE(buf_.OnData(encoded.data(), encoded.size()));
+ EXPECT_EQ(state(), State::COLLECTING);
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+
+ EXPECT_TRUE(buf_.OnEnd());
+ EXPECT_EQ(state(), State::COMPLETE);
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+ EXPECT_EQ(buf_.BufferedLength(), decoded.size());
+ EXPECT_EQ(decoded, buf_.str());
+ EXPECT_TRUE(VerifyLogHasSubstrs(
+ {"{state=COMPLETE", "backing=BUFFERED", "buffer: www.example.com}"}));
+
+ std::string s = buf_.ReleaseString();
+ EXPECT_EQ(s, decoded);
+ EXPECT_EQ(state(), State::RESET);
+}
+
+TEST_F(HpackDecoderStringBufferTest, HuffmanSplit) {
+ std::string encoded = absl::HexStringToBytes("f1e3c2e5f23a6ba0ab90f4ff");
+ std::string part1 = encoded.substr(0, 5);
+ std::string part2 = encoded.substr(5);
+ absl::string_view decoded("www.example.com");
+
+ EXPECT_EQ(state(), State::RESET);
+ buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+ EXPECT_EQ(state(), State::COLLECTING);
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+ EXPECT_EQ(0u, buf_.BufferedLength());
+ HTTP2_LOG(INFO) << buf_;
+
+ EXPECT_TRUE(buf_.OnData(part1.data(), part1.size()));
+ EXPECT_EQ(state(), State::COLLECTING);
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+ EXPECT_GT(buf_.BufferedLength(), 0u);
+ EXPECT_LT(buf_.BufferedLength(), decoded.size());
+ HTTP2_LOG(INFO) << buf_;
+
+ EXPECT_TRUE(buf_.OnData(part2.data(), part2.size()));
+ EXPECT_EQ(state(), State::COLLECTING);
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+ EXPECT_EQ(buf_.BufferedLength(), decoded.size());
+ HTTP2_LOG(INFO) << buf_;
+
+ EXPECT_TRUE(buf_.OnEnd());
+ EXPECT_EQ(state(), State::COMPLETE);
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+ EXPECT_EQ(buf_.BufferedLength(), decoded.size());
+ EXPECT_EQ(decoded, buf_.str());
+ HTTP2_LOG(INFO) << buf_;
+
+ buf_.Reset();
+ EXPECT_EQ(state(), State::RESET);
+ HTTP2_LOG(INFO) << buf_;
+}
+
+TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnData) {
+ // Explicitly encode the End-of-String symbol, a no-no.
+ std::string encoded = absl::HexStringToBytes("ffffffff");
+
+ buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+ EXPECT_EQ(state(), State::COLLECTING);
+
+ EXPECT_FALSE(buf_.OnData(encoded.data(), encoded.size()));
+ EXPECT_EQ(state(), State::COLLECTING);
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+
+ HTTP2_LOG(INFO) << buf_;
+}
+
+TEST_F(HpackDecoderStringBufferTest, InvalidHuffmanOnEnd) {
+ // Last byte of string doesn't end with prefix of End-of-String symbol.
+ std::string encoded = absl::HexStringToBytes("00");
+
+ buf_.OnStart(/*huffman_encoded*/ true, encoded.size());
+ EXPECT_EQ(state(), State::COLLECTING);
+
+ EXPECT_TRUE(buf_.OnData(encoded.data(), encoded.size()));
+ EXPECT_EQ(state(), State::COLLECTING);
+ EXPECT_EQ(backing(), Backing::BUFFERED);
+
+ EXPECT_FALSE(buf_.OnEnd());
+ HTTP2_LOG(INFO) << buf_;
+}
+
+// TODO(jamessynge): Add tests for ReleaseString().
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables.cc
new file mode 100644
index 00000000000..233e4b68358
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables.cc
@@ -0,0 +1,148 @@
+// 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 "quiche/http2/hpack/decoder/hpack_decoder_tables.h"
+
+#include "absl/strings/str_cat.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+
+namespace http2 {
+namespace {
+
+std::vector<HpackStringPair>* MakeStaticTable() {
+ auto* ptr = new std::vector<HpackStringPair>();
+ ptr->reserve(kFirstDynamicTableIndex);
+ ptr->emplace_back("", "");
+
+#define STATIC_TABLE_ENTRY(name, value, index) \
+ QUICHE_DCHECK_EQ(ptr->size(), static_cast<size_t>(index)); \
+ ptr->emplace_back(name, value)
+
+#include "quiche/http2/hpack/hpack_static_table_entries.inc"
+
+#undef STATIC_TABLE_ENTRY
+
+ return ptr;
+}
+
+const std::vector<HpackStringPair>* GetStaticTable() {
+ static const std::vector<HpackStringPair>* const g_static_table =
+ MakeStaticTable();
+ return g_static_table;
+}
+
+} // namespace
+
+HpackStringPair::HpackStringPair(std::string name, std::string value)
+ : name(std::move(name)), value(std::move(value)) {
+ HTTP2_DVLOG(3) << DebugString() << " ctor";
+}
+
+HpackStringPair::~HpackStringPair() {
+ HTTP2_DVLOG(3) << DebugString() << " dtor";
+}
+
+std::string HpackStringPair::DebugString() const {
+ return absl::StrCat("HpackStringPair(name=", name, ", value=", value, ")");
+}
+
+std::ostream& operator<<(std::ostream& os, const HpackStringPair& p) {
+ os << p.DebugString();
+ return os;
+}
+
+HpackDecoderStaticTable::HpackDecoderStaticTable(
+ const std::vector<HpackStringPair>* table)
+ : table_(table) {}
+
+HpackDecoderStaticTable::HpackDecoderStaticTable() : table_(GetStaticTable()) {}
+
+const HpackStringPair* HpackDecoderStaticTable::Lookup(size_t index) const {
+ if (0 < index && index < kFirstDynamicTableIndex) {
+ return &((*table_)[index]);
+ }
+ return nullptr;
+}
+
+HpackDecoderDynamicTable::HpackDecoderDynamicTable()
+ : insert_count_(kFirstDynamicTableIndex - 1) {}
+HpackDecoderDynamicTable::~HpackDecoderDynamicTable() = default;
+
+void HpackDecoderDynamicTable::DynamicTableSizeUpdate(size_t size_limit) {
+ HTTP2_DVLOG(3) << "HpackDecoderDynamicTable::DynamicTableSizeUpdate "
+ << size_limit;
+ EnsureSizeNoMoreThan(size_limit);
+ QUICHE_DCHECK_LE(current_size_, size_limit);
+ size_limit_ = size_limit;
+}
+
+// TODO(jamessynge): Check somewhere before here that names received from the
+// peer are valid (e.g. are lower-case, no whitespace, etc.).
+void HpackDecoderDynamicTable::Insert(std::string name, std::string value) {
+ HpackStringPair entry(std::move(name), std::move(value));
+ size_t entry_size = entry.size();
+ HTTP2_DVLOG(2) << "InsertEntry of size=" << entry_size
+ << "\n name: " << entry.name
+ << "\n value: " << entry.value;
+ if (entry_size > size_limit_) {
+ HTTP2_DVLOG(2) << "InsertEntry: entry larger than table, removing "
+ << table_.size() << " entries, of total size "
+ << current_size_ << " bytes.";
+ table_.clear();
+ current_size_ = 0;
+ return;
+ }
+ ++insert_count_;
+ size_t insert_limit = size_limit_ - entry_size;
+ EnsureSizeNoMoreThan(insert_limit);
+ table_.push_front(entry);
+ current_size_ += entry_size;
+ HTTP2_DVLOG(2) << "InsertEntry: current_size_=" << current_size_;
+ QUICHE_DCHECK_GE(current_size_, entry_size);
+ QUICHE_DCHECK_LE(current_size_, size_limit_);
+}
+
+const HpackStringPair* HpackDecoderDynamicTable::Lookup(size_t index) const {
+ if (index < table_.size()) {
+ return &table_[index];
+ }
+ return nullptr;
+}
+
+void HpackDecoderDynamicTable::EnsureSizeNoMoreThan(size_t limit) {
+ HTTP2_DVLOG(2) << "EnsureSizeNoMoreThan limit=" << limit
+ << ", current_size_=" << current_size_;
+ // Not the most efficient choice, but any easy way to start.
+ while (current_size_ > limit) {
+ RemoveLastEntry();
+ }
+ QUICHE_DCHECK_LE(current_size_, limit);
+}
+
+void HpackDecoderDynamicTable::RemoveLastEntry() {
+ QUICHE_DCHECK(!table_.empty());
+ if (!table_.empty()) {
+ HTTP2_DVLOG(2) << "RemoveLastEntry current_size_=" << current_size_
+ << ", last entry size=" << table_.back().size();
+ QUICHE_DCHECK_GE(current_size_, table_.back().size());
+ current_size_ -= table_.back().size();
+ table_.pop_back();
+ // Empty IFF current_size_ == 0.
+ QUICHE_DCHECK_EQ(table_.empty(), current_size_ == 0);
+ }
+}
+
+HpackDecoderTables::HpackDecoderTables() = default;
+HpackDecoderTables::~HpackDecoderTables() = default;
+
+const HpackStringPair* HpackDecoderTables::Lookup(size_t index) const {
+ if (index < kFirstDynamicTableIndex) {
+ return static_table_.Lookup(index);
+ } else {
+ return dynamic_table_.Lookup(index - kFirstDynamicTableIndex);
+ }
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables.h
new file mode 100644
index 00000000000..e78b91d62b5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables.h
@@ -0,0 +1,166 @@
+// 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_DECODER_TABLES_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_TABLES_H_
+
+// Static and dynamic tables for the HPACK decoder. See:
+// http://httpwg.org/specs/rfc7541.html#indexing.tables
+
+// Note that the Lookup methods return nullptr if the requested index was not
+// found. This should be treated as a COMPRESSION error according to the HTTP/2
+// spec, which is a connection level protocol error (i.e. the connection must
+// be terminated). See these sections in the two RFCs:
+// http://httpwg.org/specs/rfc7541.html#indexed.header.representation
+// http://httpwg.org/specs/rfc7541.html#index.address.space
+// http://httpwg.org/specs/rfc7540.html#HeaderBlock
+
+#include <stddef.h>
+
+#include <cstdint>
+#include <iosfwd>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "quiche/http2/http2_constants.h"
+#include "quiche/common/platform/api/quiche_export.h"
+#include "quiche/common/quiche_circular_deque.h"
+
+namespace http2 {
+namespace test {
+class HpackDecoderTablesPeer;
+} // namespace test
+
+struct QUICHE_EXPORT_PRIVATE HpackStringPair {
+ HpackStringPair(std::string name, std::string value);
+ ~HpackStringPair();
+
+ // Returns the size of a header entry with this name and value, per the RFC:
+ // http://httpwg.org/specs/rfc7541.html#calculating.table.size
+ size_t size() const { return 32 + name.size() + value.size(); }
+
+ std::string DebugString() const;
+
+ const std::string name;
+ const std::string value;
+};
+
+QUICHE_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os,
+ const HpackStringPair& p);
+
+// See http://httpwg.org/specs/rfc7541.html#static.table.definition for the
+// contents, and http://httpwg.org/specs/rfc7541.html#index.address.space for
+// info about accessing the static table.
+class QUICHE_EXPORT_PRIVATE HpackDecoderStaticTable {
+ public:
+ explicit HpackDecoderStaticTable(const std::vector<HpackStringPair>* table);
+ // Uses a global table shared by all threads.
+ HpackDecoderStaticTable();
+
+ // If index is valid, returns a pointer to the entry, otherwise returns
+ // nullptr.
+ const HpackStringPair* Lookup(size_t index) const;
+
+ private:
+ friend class test::HpackDecoderTablesPeer;
+ const std::vector<HpackStringPair>* const table_;
+};
+
+// HpackDecoderDynamicTable implements HPACK compression feature "indexed
+// headers"; previously sent headers may be referenced later by their index
+// in the dynamic table. See these sections of the RFC:
+// http://httpwg.org/specs/rfc7541.html#dynamic.table
+// http://httpwg.org/specs/rfc7541.html#dynamic.table.management
+class QUICHE_EXPORT_PRIVATE HpackDecoderDynamicTable {
+ public:
+ HpackDecoderDynamicTable();
+ ~HpackDecoderDynamicTable();
+
+ HpackDecoderDynamicTable(const HpackDecoderDynamicTable&) = delete;
+ HpackDecoderDynamicTable& operator=(const HpackDecoderDynamicTable&) = delete;
+
+ // Sets a new size limit, received from the peer; performs evictions if
+ // necessary to ensure that the current size does not exceed the new limit.
+ // The caller needs to have validated that size_limit does not
+ // exceed the acknowledged value of SETTINGS_HEADER_TABLE_SIZE.
+ void DynamicTableSizeUpdate(size_t size_limit);
+
+ // Insert entry if possible.
+ // If entry is too large to insert, then dynamic table will be empty.
+ void Insert(std::string name, std::string value);
+
+ // If index is valid, returns a pointer to the entry, otherwise returns
+ // nullptr.
+ const HpackStringPair* Lookup(size_t index) const;
+
+ size_t size_limit() const { return size_limit_; }
+ size_t current_size() const { return current_size_; }
+
+ private:
+ friend class test::HpackDecoderTablesPeer;
+
+ // Drop older entries to ensure the size is not greater than limit.
+ void EnsureSizeNoMoreThan(size_t limit);
+
+ // Removes the oldest dynamic table entry.
+ void RemoveLastEntry();
+
+ quiche::QuicheCircularDeque<HpackStringPair> table_;
+
+ // The last received DynamicTableSizeUpdate value, initialized to
+ // SETTINGS_HEADER_TABLE_SIZE.
+ size_t size_limit_ = Http2SettingsInfo::DefaultHeaderTableSize();
+
+ size_t current_size_ = 0;
+
+ // insert_count_ and debug_listener_ are used by a QUIC experiment; remove
+ // when the experiment is done.
+ size_t insert_count_;
+};
+
+class QUICHE_EXPORT_PRIVATE HpackDecoderTables {
+ public:
+ HpackDecoderTables();
+ ~HpackDecoderTables();
+
+ HpackDecoderTables(const HpackDecoderTables&) = delete;
+ HpackDecoderTables& operator=(const HpackDecoderTables&) = delete;
+
+ // Sets a new size limit, received from the peer; performs evictions if
+ // necessary to ensure that the current size does not exceed the new limit.
+ // The caller needs to have validated that size_limit does not
+ // exceed the acknowledged value of SETTINGS_HEADER_TABLE_SIZE.
+ void DynamicTableSizeUpdate(size_t size_limit) {
+ dynamic_table_.DynamicTableSizeUpdate(size_limit);
+ }
+
+ // Insert entry if possible.
+ // If entry is too large to insert, then dynamic table will be empty.
+ void Insert(std::string name, std::string value) {
+ dynamic_table_.Insert(std::move(name), std::move(value));
+ }
+
+ // If index is valid, returns a pointer to the entry, otherwise returns
+ // nullptr.
+ const HpackStringPair* Lookup(size_t index) const;
+
+ // The size limit that the peer (the HPACK encoder) has told the decoder it is
+ // currently operating with. Defaults to SETTINGS_HEADER_TABLE_SIZE, 4096.
+ size_t header_table_size_limit() const { return dynamic_table_.size_limit(); }
+
+ // Sum of the sizes of the dynamic table entries.
+ size_t current_header_table_size() const {
+ return dynamic_table_.current_size();
+ }
+
+ private:
+ friend class test::HpackDecoderTablesPeer;
+ HpackDecoderStaticTable static_table_;
+ HpackDecoderDynamicTable dynamic_table_;
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODER_TABLES_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables_test.cc
new file mode 100644
index 00000000000..cd4655ba0fa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_tables_test.cc
@@ -0,0 +1,259 @@
+// 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 "quiche/http2/hpack/decoder/hpack_decoder_tables.h"
+
+#include <algorithm>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/http2/test_tools/http2_random.h"
+#include "quiche/http2/tools/random_util.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+
+namespace http2 {
+namespace test {
+class HpackDecoderTablesPeer {
+ public:
+ static size_t num_dynamic_entries(const HpackDecoderTables& tables) {
+ return tables.dynamic_table_.table_.size();
+ }
+};
+
+namespace {
+struct StaticEntry {
+ const char* name;
+ const char* value;
+ size_t index;
+};
+
+std::vector<StaticEntry> MakeSpecStaticEntries() {
+ std::vector<StaticEntry> static_entries;
+
+#define STATIC_TABLE_ENTRY(name, value, index) \
+ QUICHE_DCHECK_EQ(static_entries.size() + 1, static_cast<size_t>(index)); \
+ static_entries.push_back({name, value, index});
+
+#include "quiche/http2/hpack/hpack_static_table_entries.inc"
+
+#undef STATIC_TABLE_ENTRY
+
+ return static_entries;
+}
+
+template <class C>
+void ShuffleCollection(C* collection, Http2Random* r) {
+ std::shuffle(collection->begin(), collection->end(), *r);
+}
+
+class HpackDecoderStaticTableTest : public QuicheTest {
+ protected:
+ HpackDecoderStaticTableTest() = default;
+
+ std::vector<StaticEntry> shuffled_static_entries() {
+ std::vector<StaticEntry> entries = MakeSpecStaticEntries();
+ ShuffleCollection(&entries, &random_);
+ return entries;
+ }
+
+ // This test is in a function so that it can be applied to both the static
+ // table and the combined static+dynamic tables.
+ AssertionResult VerifyStaticTableContents() {
+ for (const auto& expected : shuffled_static_entries()) {
+ const HpackStringPair* found = Lookup(expected.index);
+ VERIFY_NE(found, nullptr);
+ VERIFY_EQ(expected.name, found->name) << expected.index;
+ VERIFY_EQ(expected.value, found->value) << expected.index;
+ }
+
+ // There should be no entry with index 0.
+ VERIFY_EQ(nullptr, Lookup(0));
+ return AssertionSuccess();
+ }
+
+ virtual const HpackStringPair* Lookup(size_t index) {
+ return static_table_.Lookup(index);
+ }
+
+ Http2Random* RandomPtr() { return &random_; }
+
+ Http2Random random_;
+
+ private:
+ HpackDecoderStaticTable static_table_;
+};
+
+TEST_F(HpackDecoderStaticTableTest, StaticTableContents) {
+ EXPECT_TRUE(VerifyStaticTableContents());
+}
+
+size_t Size(const std::string& name, const std::string& value) {
+ return name.size() + value.size() + 32;
+}
+
+// To support tests with more than a few of hand crafted changes to the dynamic
+// table, we have another, exceedingly simple, implementation of the HPACK
+// dynamic table containing FakeHpackEntry instances. We can thus compare the
+// contents of the actual table with those in fake_dynamic_table_.
+
+typedef std::tuple<std::string, std::string, size_t> FakeHpackEntry;
+const std::string& Name(const FakeHpackEntry& entry) {
+ return std::get<0>(entry);
+}
+const std::string& Value(const FakeHpackEntry& entry) {
+ return std::get<1>(entry);
+}
+size_t Size(const FakeHpackEntry& entry) {
+ return std::get<2>(entry);
+}
+
+class HpackDecoderTablesTest : public HpackDecoderStaticTableTest {
+ protected:
+ const HpackStringPair* Lookup(size_t index) override {
+ return tables_.Lookup(index);
+ }
+
+ size_t dynamic_size_limit() const {
+ return tables_.header_table_size_limit();
+ }
+ size_t current_dynamic_size() const {
+ return tables_.current_header_table_size();
+ }
+ size_t num_dynamic_entries() const {
+ return HpackDecoderTablesPeer::num_dynamic_entries(tables_);
+ }
+
+ // Insert the name and value into fake_dynamic_table_.
+ void FakeInsert(const std::string& name, const std::string& value) {
+ FakeHpackEntry entry(name, value, Size(name, value));
+ fake_dynamic_table_.insert(fake_dynamic_table_.begin(), entry);
+ }
+
+ // Add up the size of all entries in fake_dynamic_table_.
+ size_t FakeSize() {
+ size_t sz = 0;
+ for (const auto& entry : fake_dynamic_table_) {
+ sz += Size(entry);
+ }
+ return sz;
+ }
+
+ // If the total size of the fake_dynamic_table_ is greater than limit,
+ // keep the first N entries such that those N entries have a size not
+ // greater than limit, and such that keeping entry N+1 would have a size
+ // greater than limit. Returns the count of removed bytes.
+ size_t FakeTrim(size_t limit) {
+ size_t original_size = FakeSize();
+ size_t total_size = 0;
+ for (size_t ndx = 0; ndx < fake_dynamic_table_.size(); ++ndx) {
+ total_size += Size(fake_dynamic_table_[ndx]);
+ if (total_size > limit) {
+ // Need to get rid of ndx and all following entries.
+ fake_dynamic_table_.erase(fake_dynamic_table_.begin() + ndx,
+ fake_dynamic_table_.end());
+ return original_size - FakeSize();
+ }
+ }
+ return 0;
+ }
+
+ // Verify that the contents of the actual dynamic table match those in
+ // fake_dynamic_table_.
+ AssertionResult VerifyDynamicTableContents() {
+ VERIFY_EQ(current_dynamic_size(), FakeSize());
+ VERIFY_EQ(num_dynamic_entries(), fake_dynamic_table_.size());
+
+ for (size_t ndx = 0; ndx < fake_dynamic_table_.size(); ++ndx) {
+ const HpackStringPair* found = Lookup(ndx + kFirstDynamicTableIndex);
+ VERIFY_NE(found, nullptr);
+
+ const auto& expected = fake_dynamic_table_[ndx];
+ VERIFY_EQ(Name(expected), found->name);
+ VERIFY_EQ(Value(expected), found->value);
+ }
+
+ // Make sure there are no more entries.
+ VERIFY_EQ(nullptr,
+ Lookup(fake_dynamic_table_.size() + kFirstDynamicTableIndex));
+ return AssertionSuccess();
+ }
+
+ // Apply an update to the limit on the maximum size of the dynamic table.
+ AssertionResult DynamicTableSizeUpdate(size_t size_limit) {
+ VERIFY_EQ(current_dynamic_size(), FakeSize());
+ if (size_limit < current_dynamic_size()) {
+ // Will need to trim the dynamic table's oldest entries.
+ tables_.DynamicTableSizeUpdate(size_limit);
+ FakeTrim(size_limit);
+ return VerifyDynamicTableContents();
+ }
+ // Shouldn't change the size.
+ tables_.DynamicTableSizeUpdate(size_limit);
+ return VerifyDynamicTableContents();
+ }
+
+ // Insert an entry into the dynamic table, confirming that trimming of entries
+ // occurs if the total size is greater than the limit, and that older entries
+ // move up by 1 index.
+ AssertionResult Insert(const std::string& name, const std::string& value) {
+ size_t old_count = num_dynamic_entries();
+ tables_.Insert(name, value);
+ FakeInsert(name, value);
+ VERIFY_EQ(old_count + 1, fake_dynamic_table_.size());
+ FakeTrim(dynamic_size_limit());
+ VERIFY_EQ(current_dynamic_size(), FakeSize());
+ VERIFY_EQ(num_dynamic_entries(), fake_dynamic_table_.size());
+ return VerifyDynamicTableContents();
+ }
+
+ private:
+ HpackDecoderTables tables_;
+
+ std::vector<FakeHpackEntry> fake_dynamic_table_;
+};
+
+TEST_F(HpackDecoderTablesTest, StaticTableContents) {
+ EXPECT_TRUE(VerifyStaticTableContents());
+}
+
+// Generate a bunch of random header entries, insert them, and confirm they
+// present, as required by the RFC, using VerifyDynamicTableContents above on
+// each Insert. Also apply various resizings of the dynamic table.
+TEST_F(HpackDecoderTablesTest, RandomDynamicTable) {
+ EXPECT_EQ(0u, current_dynamic_size());
+ EXPECT_TRUE(VerifyStaticTableContents());
+ EXPECT_TRUE(VerifyDynamicTableContents());
+
+ std::vector<size_t> table_sizes;
+ table_sizes.push_back(dynamic_size_limit());
+ table_sizes.push_back(0);
+ table_sizes.push_back(dynamic_size_limit() / 2);
+ table_sizes.push_back(dynamic_size_limit());
+ table_sizes.push_back(dynamic_size_limit() / 2);
+ table_sizes.push_back(0);
+ table_sizes.push_back(dynamic_size_limit());
+
+ for (size_t limit : table_sizes) {
+ ASSERT_TRUE(DynamicTableSizeUpdate(limit));
+ for (int insert_count = 0; insert_count < 100; ++insert_count) {
+ std::string name =
+ GenerateHttp2HeaderName(random_.UniformInRange(2, 40), RandomPtr());
+ std::string value =
+ GenerateWebSafeString(random_.UniformInRange(2, 600), RandomPtr());
+ ASSERT_TRUE(Insert(name, value));
+ }
+ EXPECT_TRUE(VerifyStaticTableContents());
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_test.cc
new file mode 100644
index 00000000000..18b7b64746a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoder_test.cc
@@ -0,0 +1,1192 @@
+// Copyright 2017 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 "quiche/http2/hpack/decoder/hpack_decoder.h"
+
+// Tests of HpackDecoder.
+
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+#include "quiche/http2/decoder/decode_buffer.h"
+#include "quiche/http2/hpack/decoder/hpack_decoder_listener.h"
+#include "quiche/http2/hpack/decoder/hpack_decoder_state.h"
+#include "quiche/http2/hpack/decoder/hpack_decoder_tables.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/http2/hpack/tools/hpack_block_builder.h"
+#include "quiche/http2/hpack/tools/hpack_example.h"
+#include "quiche/http2/http2_constants.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/http2/test_tools/http2_random.h"
+#include "quiche/http2/tools/random_util.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+using ::testing::AssertionResult;
+using ::testing::AssertionSuccess;
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+
+namespace http2 {
+namespace test {
+class HpackDecoderStatePeer {
+ public:
+ static HpackDecoderTables* GetDecoderTables(HpackDecoderState* state) {
+ return &state->decoder_tables_;
+ }
+ static void set_listener(HpackDecoderState* state,
+ HpackDecoderListener* listener) {
+ state->listener_ = listener;
+ }
+};
+class HpackDecoderPeer {
+ public:
+ static HpackDecoderState* GetDecoderState(HpackDecoder* decoder) {
+ return &decoder->decoder_state_;
+ }
+ static HpackDecoderTables* GetDecoderTables(HpackDecoder* decoder) {
+ return HpackDecoderStatePeer::GetDecoderTables(GetDecoderState(decoder));
+ }
+};
+
+namespace {
+
+typedef std::pair<std::string, std::string> HpackHeaderEntry;
+typedef std::vector<HpackHeaderEntry> HpackHeaderEntries;
+
+// TODO(jamessynge): Create a ...test_utils.h file with the mock listener
+// and with VerifyDynamicTableContents.
+class MockHpackDecoderListener : public HpackDecoderListener {
+ public:
+ MOCK_METHOD(void, OnHeaderListStart, (), (override));
+ MOCK_METHOD(void,
+ OnHeader,
+ (const std::string& name, const std::string& value),
+ (override));
+ MOCK_METHOD(void, OnHeaderListEnd, (), (override));
+ MOCK_METHOD(void,
+ OnHeaderErrorDetected,
+ (absl::string_view error_message),
+ (override));
+};
+
+class HpackDecoderTest : public QuicheTestWithParam<bool>,
+ public HpackDecoderListener {
+ protected:
+ // Note that we initialize the random number generator with the same seed
+ // for each individual test, therefore the order in which the tests are
+ // executed does not effect the sequence produced by the RNG within any
+ // one test.
+ HpackDecoderTest() : decoder_(this, 4096) {
+ fragment_the_hpack_block_ = GetParam();
+ }
+ ~HpackDecoderTest() override = default;
+
+ void OnHeaderListStart() override {
+ ASSERT_FALSE(saw_start_);
+ ASSERT_FALSE(saw_end_);
+ saw_start_ = true;
+ header_entries_.clear();
+ }
+
+ // Called for each header name-value pair that is decoded, in the order they
+ // appear in the HPACK block. Multiple values for a given key will be emitted
+ // as multiple calls to OnHeader.
+ void OnHeader(const std::string& name, const std::string& value) override {
+ ASSERT_TRUE(saw_start_);
+ ASSERT_FALSE(saw_end_);
+ header_entries_.emplace_back(name, value);
+ }
+
+ // OnHeaderBlockEnd is called after successfully decoding an HPACK block. Will
+ // only be called once per block, even if it extends into CONTINUATION frames.
+ // A callback method which notifies when the parser finishes handling a
+ // header block (i.e. the containing frame has the END_STREAM flag set).
+ // Also indicates the total number of bytes in this block.
+ void OnHeaderListEnd() override {
+ ASSERT_TRUE(saw_start_);
+ ASSERT_FALSE(saw_end_);
+ ASSERT_TRUE(error_messages_.empty());
+ saw_end_ = true;
+ }
+
+ // OnHeaderErrorDetected is called if an error is detected while decoding.
+ // error_message may be used in a GOAWAY frame as the Opaque Data.
+ void OnHeaderErrorDetected(absl::string_view error_message) override {
+ ASSERT_TRUE(saw_start_);
+ error_messages_.push_back(std::string(error_message));
+ // No further callbacks should be made at this point, so replace 'this' as
+ // the listener with mock_listener_, which is a strict mock, so will
+ // generate an error for any calls.
+ HpackDecoderStatePeer::set_listener(
+ HpackDecoderPeer::GetDecoderState(&decoder_), &mock_listener_);
+ }
+
+ AssertionResult DecodeBlock(absl::string_view block) {
+ HTTP2_VLOG(1) << "HpackDecoderTest::DecodeBlock";
+
+ VERIFY_FALSE(decoder_.DetectError());
+ VERIFY_TRUE(error_messages_.empty());
+ VERIFY_FALSE(saw_start_);
+ VERIFY_FALSE(saw_end_);
+ header_entries_.clear();
+
+ VERIFY_FALSE(decoder_.DetectError());
+ VERIFY_TRUE(decoder_.StartDecodingBlock());
+ VERIFY_FALSE(decoder_.DetectError());
+
+ if (fragment_the_hpack_block_) {
+ // See note in ctor regarding RNG.
+ while (!block.empty()) {
+ size_t fragment_size = random_.RandomSizeSkewedLow(block.size());
+ DecodeBuffer db(block.substr(0, fragment_size));
+ VERIFY_TRUE(decoder_.DecodeFragment(&db));
+ VERIFY_EQ(0u, db.Remaining());
+ block.remove_prefix(fragment_size);
+ }
+ } else {
+ DecodeBuffer db(block);
+ VERIFY_TRUE(decoder_.DecodeFragment(&db));
+ VERIFY_EQ(0u, db.Remaining());
+ }
+ VERIFY_FALSE(decoder_.DetectError());
+
+ VERIFY_TRUE(decoder_.EndDecodingBlock());
+ if (saw_end_) {
+ VERIFY_FALSE(decoder_.DetectError());
+ VERIFY_TRUE(error_messages_.empty());
+ } else {
+ VERIFY_TRUE(decoder_.DetectError());
+ VERIFY_FALSE(error_messages_.empty());
+ }
+
+ saw_start_ = saw_end_ = false;
+ return AssertionSuccess();
+ }
+
+ const HpackDecoderTables& GetDecoderTables() {
+ return *HpackDecoderPeer::GetDecoderTables(&decoder_);
+ }
+ const HpackStringPair* Lookup(size_t index) {
+ return GetDecoderTables().Lookup(index);
+ }
+ size_t current_header_table_size() {
+ return GetDecoderTables().current_header_table_size();
+ }
+ size_t header_table_size_limit() {
+ return GetDecoderTables().header_table_size_limit();
+ }
+ void set_header_table_size_limit(size_t size) {
+ HpackDecoderPeer::GetDecoderTables(&decoder_)->DynamicTableSizeUpdate(size);
+ }
+
+ // dynamic_index is one-based, because that is the way RFC 7541 shows it.
+ AssertionResult VerifyEntry(size_t dynamic_index,
+ const char* name,
+ const char* value) {
+ const HpackStringPair* entry =
+ Lookup(dynamic_index + kFirstDynamicTableIndex - 1);
+ VERIFY_NE(entry, nullptr);
+ VERIFY_EQ(entry->name, name);
+ VERIFY_EQ(entry->value, value);
+ return AssertionSuccess();
+ }
+ AssertionResult VerifyNoEntry(size_t dynamic_index) {
+ const HpackStringPair* entry =
+ Lookup(dynamic_index + kFirstDynamicTableIndex - 1);
+ VERIFY_EQ(entry, nullptr);
+ return AssertionSuccess();
+ }
+ AssertionResult VerifyDynamicTableContents(
+ const std::vector<std::pair<const char*, const char*>>& entries) {
+ size_t index = 1;
+ for (const auto& entry : entries) {
+ VERIFY_SUCCESS(VerifyEntry(index, entry.first, entry.second));
+ ++index;
+ }
+ VERIFY_SUCCESS(VerifyNoEntry(index));
+ return AssertionSuccess();
+ }
+
+ Http2Random random_;
+ HpackDecoder decoder_;
+ testing::StrictMock<MockHpackDecoderListener> mock_listener_;
+ HpackHeaderEntries header_entries_;
+ std::vector<std::string> error_messages_;
+ bool fragment_the_hpack_block_;
+ bool saw_start_ = false;
+ bool saw_end_ = false;
+};
+INSTANTIATE_TEST_SUITE_P(AllWays, HpackDecoderTest, ::testing::Bool());
+
+// Test based on RFC 7541, section C.3: Request Examples without Huffman Coding.
+// This section shows several consecutive header lists, corresponding to HTTP
+// requests, on the same connection.
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.3
+TEST_P(HpackDecoderTest, C3_RequestExamples) {
+ // C.3.1 First Request
+ std::string hpack_block = HpackExampleToStringOrDie(R"(
+ 82 | == Indexed - Add ==
+ | idx = 2
+ | -> :method: GET
+ 86 | == Indexed - Add ==
+ | idx = 6
+ | -> :scheme: http
+ 84 | == Indexed - Add ==
+ | idx = 4
+ | -> :path: /
+ 41 | == Literal indexed ==
+ | Indexed name (idx = 1)
+ | :authority
+ 0f | Literal value (len = 15)
+ 7777 772e 6578 616d 706c 652e 636f 6d | www.example.com
+ | -> :authority:
+ | www.example.com
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":method", "GET"},
+ HpackHeaderEntry{":scheme", "http"},
+ HpackHeaderEntry{":path", "/"},
+ HpackHeaderEntry{":authority", "www.example.com"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 57) :authority: www.example.com
+ // Table size: 57
+ ASSERT_TRUE(VerifyDynamicTableContents({{":authority", "www.example.com"}}));
+ ASSERT_EQ(57u, current_header_table_size());
+
+ // C.3.2 Second Request
+ hpack_block = HpackExampleToStringOrDie(R"(
+ 82 | == Indexed - Add ==
+ | idx = 2
+ | -> :method: GET
+ 86 | == Indexed - Add ==
+ | idx = 6
+ | -> :scheme: http
+ 84 | == Indexed - Add ==
+ | idx = 4
+ | -> :path: /
+ be | == Indexed - Add ==
+ | idx = 62
+ | -> :authority:
+ | www.example.com
+ 58 | == Literal indexed ==
+ | Indexed name (idx = 24)
+ | cache-control
+ 08 | Literal value (len = 8)
+ 6e6f 2d63 6163 6865 | no-cache
+ | -> cache-control: no-cache
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":method", "GET"},
+ HpackHeaderEntry{":scheme", "http"},
+ HpackHeaderEntry{":path", "/"},
+ HpackHeaderEntry{":authority", "www.example.com"},
+ HpackHeaderEntry{"cache-control", "no-cache"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 53) cache-control: no-cache
+ // [ 2] (s = 57) :authority: www.example.com
+ // Table size: 110
+ ASSERT_TRUE(VerifyDynamicTableContents(
+ {{"cache-control", "no-cache"}, {":authority", "www.example.com"}}));
+ ASSERT_EQ(110u, current_header_table_size());
+
+ // C.3.2 Third Request
+ hpack_block = HpackExampleToStringOrDie(R"(
+ 82 | == Indexed - Add ==
+ | idx = 2
+ | -> :method: GET
+ 87 | == Indexed - Add ==
+ | idx = 7
+ | -> :scheme: https
+ 85 | == Indexed - Add ==
+ | idx = 5
+ | -> :path: /index.html
+ bf | == Indexed - Add ==
+ | idx = 63
+ | -> :authority:
+ | www.example.com
+ 40 | == Literal indexed ==
+ 0a | Literal name (len = 10)
+ 6375 7374 6f6d 2d6b 6579 | custom-key
+ 0c | Literal value (len = 12)
+ 6375 7374 6f6d 2d76 616c 7565 | custom-value
+ | -> custom-key:
+ | custom-value
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":method", "GET"},
+ HpackHeaderEntry{":scheme", "https"},
+ HpackHeaderEntry{":path", "/index.html"},
+ HpackHeaderEntry{":authority", "www.example.com"},
+ HpackHeaderEntry{"custom-key", "custom-value"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 54) custom-key: custom-value
+ // [ 2] (s = 53) cache-control: no-cache
+ // [ 3] (s = 57) :authority: www.example.com
+ // Table size: 164
+ ASSERT_TRUE(VerifyDynamicTableContents({{"custom-key", "custom-value"},
+ {"cache-control", "no-cache"},
+ {":authority", "www.example.com"}}));
+ ASSERT_EQ(164u, current_header_table_size());
+}
+
+// Test based on RFC 7541, section C.4 Request Examples with Huffman Coding.
+// This section shows the same examples as the previous section but uses
+// Huffman encoding for the literal values.
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.4
+TEST_P(HpackDecoderTest, C4_RequestExamplesWithHuffmanEncoding) {
+ // C.4.1 First Request
+ std::string hpack_block = HpackExampleToStringOrDie(R"(
+ 82 | == Indexed - Add ==
+ | idx = 2
+ | -> :method: GET
+ 86 | == Indexed - Add ==
+ | idx = 6
+ | -> :scheme: http
+ 84 | == Indexed - Add ==
+ | idx = 4
+ | -> :path: /
+ 41 | == Literal indexed ==
+ | Indexed name (idx = 1)
+ | :authority
+ 8c | Literal value (len = 12)
+ | Huffman encoded:
+ f1e3 c2e5 f23a 6ba0 ab90 f4ff | .....:k.....
+ | Decoded:
+ | www.example.com
+ | -> :authority:
+ | www.example.com
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":method", "GET"},
+ HpackHeaderEntry{":scheme", "http"},
+ HpackHeaderEntry{":path", "/"},
+ HpackHeaderEntry{":authority", "www.example.com"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 57) :authority: www.example.com
+ // Table size: 57
+ ASSERT_TRUE(VerifyDynamicTableContents({{":authority", "www.example.com"}}));
+ ASSERT_EQ(57u, current_header_table_size());
+
+ // C.4.2 Second Request
+ hpack_block = HpackExampleToStringOrDie(R"(
+ 82 | == Indexed - Add ==
+ | idx = 2
+ | -> :method: GET
+ 86 | == Indexed - Add ==
+ | idx = 6
+ | -> :scheme: http
+ 84 | == Indexed - Add ==
+ | idx = 4
+ | -> :path: /
+ be | == Indexed - Add ==
+ | idx = 62
+ | -> :authority:
+ | www.example.com
+ 58 | == Literal indexed ==
+ | Indexed name (idx = 24)
+ | cache-control
+ 86 | Literal value (len = 6)
+ | Huffman encoded:
+ a8eb 1064 9cbf | ...d..
+ | Decoded:
+ | no-cache
+ | -> cache-control: no-cache
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":method", "GET"},
+ HpackHeaderEntry{":scheme", "http"},
+ HpackHeaderEntry{":path", "/"},
+ HpackHeaderEntry{":authority", "www.example.com"},
+ HpackHeaderEntry{"cache-control", "no-cache"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 53) cache-control: no-cache
+ // [ 2] (s = 57) :authority: www.example.com
+ // Table size: 110
+ ASSERT_TRUE(VerifyDynamicTableContents(
+ {{"cache-control", "no-cache"}, {":authority", "www.example.com"}}));
+ ASSERT_EQ(110u, current_header_table_size());
+
+ // C.4.2 Third Request
+ hpack_block = HpackExampleToStringOrDie(R"(
+ 82 | == Indexed - Add ==
+ | idx = 2
+ | -> :method: GET
+ 87 | == Indexed - Add ==
+ | idx = 7
+ | -> :scheme: https
+ 85 | == Indexed - Add ==
+ | idx = 5
+ | -> :path: /index.html
+ bf | == Indexed - Add ==
+ | idx = 63
+ | -> :authority:
+ | www.example.com
+ 40 | == Literal indexed ==
+ 88 | Literal name (len = 8)
+ | Huffman encoded:
+ 25a8 49e9 5ba9 7d7f | %.I.[.}.
+ | Decoded:
+ | custom-key
+ 89 | Literal value (len = 9)
+ | Huffman encoded:
+ 25a8 49e9 5bb8 e8b4 bf | %.I.[....
+ | Decoded:
+ | custom-value
+ | -> custom-key:
+ | custom-value
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":method", "GET"},
+ HpackHeaderEntry{":scheme", "https"},
+ HpackHeaderEntry{":path", "/index.html"},
+ HpackHeaderEntry{":authority", "www.example.com"},
+ HpackHeaderEntry{"custom-key", "custom-value"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 54) custom-key: custom-value
+ // [ 2] (s = 53) cache-control: no-cache
+ // [ 3] (s = 57) :authority: www.example.com
+ // Table size: 164
+ ASSERT_TRUE(VerifyDynamicTableContents({{"custom-key", "custom-value"},
+ {"cache-control", "no-cache"},
+ {":authority", "www.example.com"}}));
+ ASSERT_EQ(164u, current_header_table_size());
+}
+
+// Test based on RFC 7541, section C.5: Response Examples without Huffman
+// Coding. This section shows several consecutive header lists, corresponding
+// to HTTP responses, on the same connection. The HTTP/2 setting parameter
+// SETTINGS_HEADER_TABLE_SIZE is set to the value of 256 octets, causing
+// some evictions to occur.
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.5
+TEST_P(HpackDecoderTest, C5_ResponseExamples) {
+ set_header_table_size_limit(256);
+
+ // C.5.1 First Response
+ //
+ // Header list to encode:
+ //
+ // :status: 302
+ // cache-control: private
+ // date: Mon, 21 Oct 2013 20:13:21 GMT
+ // location: https://www.example.com
+
+ std::string hpack_block = HpackExampleToStringOrDie(R"(
+ 48 | == Literal indexed ==
+ | Indexed name (idx = 8)
+ | :status
+ 03 | Literal value (len = 3)
+ 3330 32 | 302
+ | -> :status: 302
+ 58 | == Literal indexed ==
+ | Indexed name (idx = 24)
+ | cache-control
+ 07 | Literal value (len = 7)
+ 7072 6976 6174 65 | private
+ | -> cache-control: private
+ 61 | == Literal indexed ==
+ | Indexed name (idx = 33)
+ | date
+ 1d | Literal value (len = 29)
+ 4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013
+ 2032 303a 3133 3a32 3120 474d 54 | 20:13:21 GMT
+ | -> date: Mon, 21 Oct 2013
+ | 20:13:21 GMT
+ 6e | == Literal indexed ==
+ | Indexed name (idx = 46)
+ | location
+ 17 | Literal value (len = 23)
+ 6874 7470 733a 2f2f 7777 772e 6578 616d | https://www.exam
+ 706c 652e 636f 6d | ple.com
+ | -> location:
+ | https://www.example.com
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":status", "302"},
+ HpackHeaderEntry{"cache-control", "private"},
+ HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ HpackHeaderEntry{"location", "https://www.example.com"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 63) location: https://www.example.com
+ // [ 2] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT
+ // [ 3] (s = 52) cache-control: private
+ // [ 4] (s = 42) :status: 302
+ // Table size: 222
+ ASSERT_TRUE(
+ VerifyDynamicTableContents({{"location", "https://www.example.com"},
+ {"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ {"cache-control", "private"},
+ {":status", "302"}}));
+ ASSERT_EQ(222u, current_header_table_size());
+
+ // C.5.2 Second Response
+ //
+ // The (":status", "302") header field is evicted from the dynamic table to
+ // free space to allow adding the (":status", "307") header field.
+ //
+ // Header list to encode:
+ //
+ // :status: 307
+ // cache-control: private
+ // date: Mon, 21 Oct 2013 20:13:21 GMT
+ // location: https://www.example.com
+
+ hpack_block = HpackExampleToStringOrDie(R"(
+ 48 | == Literal indexed ==
+ | Indexed name (idx = 8)
+ | :status
+ 03 | Literal value (len = 3)
+ 3330 37 | 307
+ | - evict: :status: 302
+ | -> :status: 307
+ c1 | == Indexed - Add ==
+ | idx = 65
+ | -> cache-control: private
+ c0 | == Indexed - Add ==
+ | idx = 64
+ | -> date: Mon, 21 Oct 2013
+ | 20:13:21 GMT
+ bf | == Indexed - Add ==
+ | idx = 63
+ | -> location:
+ | https://www.example.com
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":status", "307"},
+ HpackHeaderEntry{"cache-control", "private"},
+ HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ HpackHeaderEntry{"location", "https://www.example.com"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 42) :status: 307
+ // [ 2] (s = 63) location: https://www.example.com
+ // [ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT
+ // [ 4] (s = 52) cache-control: private
+ // Table size: 222
+
+ ASSERT_TRUE(
+ VerifyDynamicTableContents({{":status", "307"},
+ {"location", "https://www.example.com"},
+ {"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ {"cache-control", "private"}}));
+ ASSERT_EQ(222u, current_header_table_size());
+
+ // C.5.3 Third Response
+ //
+ // Several header fields are evicted from the dynamic table during the
+ // processing of this header list.
+ //
+ // Header list to encode:
+ //
+ // :status: 200
+ // cache-control: private
+ // date: Mon, 21 Oct 2013 20:13:22 GMT
+ // location: https://www.example.com
+ // content-encoding: gzip
+ // set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1
+ hpack_block = HpackExampleToStringOrDie(R"(
+ 88 | == Indexed - Add ==
+ | idx = 8
+ | -> :status: 200
+ c1 | == Indexed - Add ==
+ | idx = 65
+ | -> cache-control: private
+ 61 | == Literal indexed ==
+ | Indexed name (idx = 33)
+ | date
+ 1d | Literal value (len = 29)
+ 4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013
+ 2032 303a 3133 3a32 3220 474d 54 | 20:13:22 GMT
+ | - evict: cache-control:
+ | private
+ | -> date: Mon, 21 Oct 2013
+ | 20:13:22 GMT
+ c0 | == Indexed - Add ==
+ | idx = 64
+ | -> location:
+ | https://www.example.com
+ 5a | == Literal indexed ==
+ | Indexed name (idx = 26)
+ | content-encoding
+ 04 | Literal value (len = 4)
+ 677a 6970 | gzip
+ | - evict: date: Mon, 21 Oct
+ | 2013 20:13:21 GMT
+ | -> content-encoding: gzip
+ 77 | == Literal indexed ==
+ | Indexed name (idx = 55)
+ | set-cookie
+ 38 | Literal value (len = 56)
+ 666f 6f3d 4153 444a 4b48 514b 425a 584f | foo=ASDJKHQKBZXO
+ 5157 454f 5049 5541 5851 5745 4f49 553b | QWEOPIUAXQWEOIU;
+ 206d 6178 2d61 6765 3d33 3630 303b 2076 | max-age=3600; v
+ 6572 7369 6f6e 3d31 | ersion=1
+ | - evict: location:
+ | https://www.example.com
+ | - evict: :status: 307
+ | -> set-cookie: foo=ASDJKHQ
+ | KBZXOQWEOPIUAXQWEOIU; ma
+ | x-age=3600; version=1
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(
+ header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":status", "200"},
+ HpackHeaderEntry{"cache-control", "private"},
+ HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:22 GMT"},
+ HpackHeaderEntry{"location", "https://www.example.com"},
+ HpackHeaderEntry{"content-encoding", "gzip"},
+ HpackHeaderEntry{
+ "set-cookie",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;
+ // max-age=3600; version=1
+ // [ 2] (s = 52) content-encoding: gzip
+ // [ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:22 GMT
+ // Table size: 215
+ ASSERT_TRUE(VerifyDynamicTableContents(
+ {{"set-cookie",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"},
+ {"content-encoding", "gzip"},
+ {"date", "Mon, 21 Oct 2013 20:13:22 GMT"}}));
+ ASSERT_EQ(215u, current_header_table_size());
+}
+
+// Test based on RFC 7541, section C.6: Response Examples with Huffman Coding.
+// This section shows the same examples as the previous section but uses Huffman
+// encoding for the literal values. The HTTP/2 setting parameter
+// SETTINGS_HEADER_TABLE_SIZE is set to the value of 256 octets, causing some
+// evictions to occur. The eviction mechanism uses the length of the decoded
+// literal values, so the same evictions occur as in the previous section.
+// http://httpwg.org/specs/rfc7541.html#rfc.section.C.6
+TEST_P(HpackDecoderTest, C6_ResponseExamplesWithHuffmanEncoding) {
+ set_header_table_size_limit(256);
+
+ // C.5.1 First Response
+ //
+ // Header list to encode:
+ //
+ // :status: 302
+ // cache-control: private
+ // date: Mon, 21 Oct 2013 20:13:21 GMT
+ // location: https://www.example.com
+ std::string hpack_block = HpackExampleToStringOrDie(R"(
+ 48 | == Literal indexed ==
+ | Indexed name (idx = 8)
+ | :status
+ 03 | Literal value (len = 3)
+ 3330 32 | 302
+ | -> :status: 302
+ 58 | == Literal indexed ==
+ | Indexed name (idx = 24)
+ | cache-control
+ 07 | Literal value (len = 7)
+ 7072 6976 6174 65 | private
+ | -> cache-control: private
+ 61 | == Literal indexed ==
+ | Indexed name (idx = 33)
+ | date
+ 1d | Literal value (len = 29)
+ 4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013
+ 2032 303a 3133 3a32 3120 474d 54 | 20:13:21 GMT
+ | -> date: Mon, 21 Oct 2013
+ | 20:13:21 GMT
+ 6e | == Literal indexed ==
+ | Indexed name (idx = 46)
+ | location
+ 17 | Literal value (len = 23)
+ 6874 7470 733a 2f2f 7777 772e 6578 616d | https://www.exam
+ 706c 652e 636f 6d | ple.com
+ | -> location:
+ | https://www.example.com
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":status", "302"},
+ HpackHeaderEntry{"cache-control", "private"},
+ HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ HpackHeaderEntry{"location", "https://www.example.com"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 63) location: https://www.example.com
+ // [ 2] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT
+ // [ 3] (s = 52) cache-control: private
+ // [ 4] (s = 42) :status: 302
+ // Table size: 222
+ ASSERT_TRUE(
+ VerifyDynamicTableContents({{"location", "https://www.example.com"},
+ {"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ {"cache-control", "private"},
+ {":status", "302"}}));
+ ASSERT_EQ(222u, current_header_table_size());
+
+ // C.5.2 Second Response
+ //
+ // The (":status", "302") header field is evicted from the dynamic table to
+ // free space to allow adding the (":status", "307") header field.
+ //
+ // Header list to encode:
+ //
+ // :status: 307
+ // cache-control: private
+ // date: Mon, 21 Oct 2013 20:13:21 GMT
+ // location: https://www.example.com
+ hpack_block = HpackExampleToStringOrDie(R"(
+ 48 | == Literal indexed ==
+ | Indexed name (idx = 8)
+ | :status
+ 03 | Literal value (len = 3)
+ 3330 37 | 307
+ | - evict: :status: 302
+ | -> :status: 307
+ c1 | == Indexed - Add ==
+ | idx = 65
+ | -> cache-control: private
+ c0 | == Indexed - Add ==
+ | idx = 64
+ | -> date: Mon, 21 Oct 2013
+ | 20:13:21 GMT
+ bf | == Indexed - Add ==
+ | idx = 63
+ | -> location:
+ | https://www.example.com
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":status", "307"},
+ HpackHeaderEntry{"cache-control", "private"},
+ HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ HpackHeaderEntry{"location", "https://www.example.com"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 42) :status: 307
+ // [ 2] (s = 63) location: https://www.example.com
+ // [ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:21 GMT
+ // [ 4] (s = 52) cache-control: private
+ // Table size: 222
+ ASSERT_TRUE(
+ VerifyDynamicTableContents({{":status", "307"},
+ {"location", "https://www.example.com"},
+ {"date", "Mon, 21 Oct 2013 20:13:21 GMT"},
+ {"cache-control", "private"}}));
+ ASSERT_EQ(222u, current_header_table_size());
+
+ // C.5.3 Third Response
+ //
+ // Several header fields are evicted from the dynamic table during the
+ // processing of this header list.
+ //
+ // Header list to encode:
+ //
+ // :status: 200
+ // cache-control: private
+ // date: Mon, 21 Oct 2013 20:13:22 GMT
+ // location: https://www.example.com
+ // content-encoding: gzip
+ // set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1
+ hpack_block = HpackExampleToStringOrDie(R"(
+ 88 | == Indexed - Add ==
+ | idx = 8
+ | -> :status: 200
+ c1 | == Indexed - Add ==
+ | idx = 65
+ | -> cache-control: private
+ 61 | == Literal indexed ==
+ | Indexed name (idx = 33)
+ | date
+ 1d | Literal value (len = 29)
+ 4d6f 6e2c 2032 3120 4f63 7420 3230 3133 | Mon, 21 Oct 2013
+ 2032 303a 3133 3a32 3220 474d 54 | 20:13:22 GMT
+ | - evict: cache-control:
+ | private
+ | -> date: Mon, 21 Oct 2013
+ | 20:13:22 GMT
+ c0 | == Indexed - Add ==
+ | idx = 64
+ | -> location:
+ | https://www.example.com
+ 5a | == Literal indexed ==
+ | Indexed name (idx = 26)
+ | content-encoding
+ 04 | Literal value (len = 4)
+ 677a 6970 | gzip
+ | - evict: date: Mon, 21 Oct
+ | 2013 20:13:21 GMT
+ | -> content-encoding: gzip
+ 77 | == Literal indexed ==
+ | Indexed name (idx = 55)
+ | set-cookie
+ 38 | Literal value (len = 56)
+ 666f 6f3d 4153 444a 4b48 514b 425a 584f | foo=ASDJKHQKBZXO
+ 5157 454f 5049 5541 5851 5745 4f49 553b | QWEOPIUAXQWEOIU;
+ 206d 6178 2d61 6765 3d33 3630 303b 2076 | max-age=3600; v
+ 6572 7369 6f6e 3d31 | ersion=1
+ | - evict: location:
+ | https://www.example.com
+ | - evict: :status: 307
+ | -> set-cookie: foo=ASDJKHQ
+ | KBZXOQWEOPIUAXQWEOIU; ma
+ | x-age=3600; version=1
+ )");
+ EXPECT_TRUE(DecodeBlock(hpack_block));
+ ASSERT_THAT(
+ header_entries_,
+ ElementsAreArray({
+ HpackHeaderEntry{":status", "200"},
+ HpackHeaderEntry{"cache-control", "private"},
+ HpackHeaderEntry{"date", "Mon, 21 Oct 2013 20:13:22 GMT"},
+ HpackHeaderEntry{"location", "https://www.example.com"},
+ HpackHeaderEntry{"content-encoding", "gzip"},
+ HpackHeaderEntry{
+ "set-cookie",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"},
+ }));
+
+ // Dynamic Table (after decoding):
+ //
+ // [ 1] (s = 98) set-cookie: foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU;
+ // max-age=3600; version=1
+ // [ 2] (s = 52) content-encoding: gzip
+ // [ 3] (s = 65) date: Mon, 21 Oct 2013 20:13:22 GMT
+ // Table size: 215
+ ASSERT_TRUE(VerifyDynamicTableContents(
+ {{"set-cookie",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1"},
+ {"content-encoding", "gzip"},
+ {"date", "Mon, 21 Oct 2013 20:13:22 GMT"}}));
+ ASSERT_EQ(215u, current_header_table_size());
+}
+
+// Confirm that the table size can be changed, but at most twice.
+TEST_P(HpackDecoderTest, ProcessesOptionalTableSizeUpdates) {
+ EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(),
+ header_table_size_limit());
+ // One update allowed.
+ {
+ HpackBlockBuilder hbb;
+ hbb.AppendDynamicTableSizeUpdate(3000);
+ EXPECT_TRUE(DecodeBlock(hbb.buffer()));
+ EXPECT_EQ(3000u, header_table_size_limit());
+ EXPECT_EQ(0u, current_header_table_size());
+ EXPECT_TRUE(header_entries_.empty());
+ }
+ // Two updates allowed.
+ {
+ HpackBlockBuilder hbb;
+ hbb.AppendDynamicTableSizeUpdate(2000);
+ hbb.AppendDynamicTableSizeUpdate(2500);
+ EXPECT_TRUE(DecodeBlock(hbb.buffer()));
+ EXPECT_EQ(2500u, header_table_size_limit());
+ EXPECT_EQ(0u, current_header_table_size());
+ EXPECT_TRUE(header_entries_.empty());
+ }
+ // A third update in the same HPACK block is rejected, so the final
+ // size is 1000, not 500.
+ {
+ HpackBlockBuilder hbb;
+ hbb.AppendDynamicTableSizeUpdate(1500);
+ hbb.AppendDynamicTableSizeUpdate(1000);
+ hbb.AppendDynamicTableSizeUpdate(500);
+ EXPECT_FALSE(DecodeBlock(hbb.buffer()));
+ EXPECT_EQ(HpackDecodingError::kDynamicTableSizeUpdateNotAllowed,
+ decoder_.error());
+ EXPECT_EQ(1u, error_messages_.size());
+ EXPECT_THAT(error_messages_[0],
+ Eq("Dynamic table size update not allowed"));
+ EXPECT_EQ(1000u, header_table_size_limit());
+ EXPECT_EQ(0u, current_header_table_size());
+ EXPECT_TRUE(header_entries_.empty());
+ }
+ // An error has been detected, so calls to HpackDecoder::DecodeFragment
+ // should return immediately.
+ DecodeBuffer db("\x80");
+ EXPECT_FALSE(decoder_.DecodeFragment(&db));
+ EXPECT_EQ(0u, db.Offset());
+ EXPECT_EQ(1u, error_messages_.size());
+}
+
+// Confirm that the table size can be changed when required, but at most twice.
+TEST_P(HpackDecoderTest, ProcessesRequiredTableSizeUpdate) {
+ EXPECT_EQ(4096u, decoder_.GetCurrentHeaderTableSizeSetting());
+ // One update required, two allowed, one provided, followed by a header.
+ decoder_.ApplyHeaderTableSizeSetting(1024);
+ decoder_.ApplyHeaderTableSizeSetting(2048);
+ EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(),
+ header_table_size_limit());
+ EXPECT_EQ(2048u, decoder_.GetCurrentHeaderTableSizeSetting());
+ {
+ HpackBlockBuilder hbb;
+ hbb.AppendDynamicTableSizeUpdate(1024);
+ hbb.AppendIndexedHeader(4); // :path: /
+ EXPECT_TRUE(DecodeBlock(hbb.buffer()));
+ EXPECT_THAT(header_entries_,
+ ElementsAreArray({HpackHeaderEntry{":path", "/"}}));
+ EXPECT_EQ(1024u, header_table_size_limit());
+ EXPECT_EQ(0u, current_header_table_size());
+ }
+ // One update required, two allowed, two provided, followed by a header.
+ decoder_.ApplyHeaderTableSizeSetting(1000);
+ decoder_.ApplyHeaderTableSizeSetting(1500);
+ EXPECT_EQ(1500u, decoder_.GetCurrentHeaderTableSizeSetting());
+ {
+ HpackBlockBuilder hbb;
+ hbb.AppendDynamicTableSizeUpdate(500);
+ hbb.AppendDynamicTableSizeUpdate(1250);
+ hbb.AppendIndexedHeader(5); // :path: /index.html
+ EXPECT_TRUE(DecodeBlock(hbb.buffer()));
+ EXPECT_THAT(header_entries_,
+ ElementsAreArray({HpackHeaderEntry{":path", "/index.html"}}));
+ EXPECT_EQ(1250u, header_table_size_limit());
+ EXPECT_EQ(0u, current_header_table_size());
+ }
+ // One update required, two allowed, three provided, followed by a header.
+ // The third update is rejected, so the final size is 1000, not 500.
+ decoder_.ApplyHeaderTableSizeSetting(500);
+ decoder_.ApplyHeaderTableSizeSetting(1000);
+ EXPECT_EQ(1000u, decoder_.GetCurrentHeaderTableSizeSetting());
+ {
+ HpackBlockBuilder hbb;
+ hbb.AppendDynamicTableSizeUpdate(200);
+ hbb.AppendDynamicTableSizeUpdate(700);
+ hbb.AppendDynamicTableSizeUpdate(900);
+ hbb.AppendIndexedHeader(5); // Not decoded.
+ EXPECT_FALSE(DecodeBlock(hbb.buffer()));
+ EXPECT_FALSE(saw_end_);
+ EXPECT_EQ(HpackDecodingError::kDynamicTableSizeUpdateNotAllowed,
+ decoder_.error());
+ EXPECT_EQ(1u, error_messages_.size());
+ EXPECT_THAT(error_messages_[0],
+ Eq("Dynamic table size update not allowed"));
+ EXPECT_EQ(700u, header_table_size_limit());
+ EXPECT_EQ(0u, current_header_table_size());
+ EXPECT_TRUE(header_entries_.empty());
+ }
+ EXPECT_EQ(1000u, decoder_.GetCurrentHeaderTableSizeSetting());
+ // Now that an error has been detected, StartDecodingBlock should return
+ // false.
+ EXPECT_FALSE(decoder_.StartDecodingBlock());
+}
+
+// Confirm that required size updates are validated.
+TEST_P(HpackDecoderTest, InvalidRequiredSizeUpdate) {
+ // Require a size update, but provide one that isn't small enough (must be
+ // zero or one, in this case).
+ decoder_.ApplyHeaderTableSizeSetting(1);
+ decoder_.ApplyHeaderTableSizeSetting(1024);
+ HpackBlockBuilder hbb;
+ hbb.AppendDynamicTableSizeUpdate(2);
+ EXPECT_TRUE(decoder_.StartDecodingBlock());
+ DecodeBuffer db(hbb.buffer());
+ EXPECT_FALSE(decoder_.DecodeFragment(&db));
+ EXPECT_FALSE(saw_end_);
+ EXPECT_EQ(
+ HpackDecodingError::kInitialDynamicTableSizeUpdateIsAboveLowWaterMark,
+ decoder_.error());
+ EXPECT_EQ(1u, error_messages_.size());
+ EXPECT_THAT(error_messages_[0],
+ Eq("Initial dynamic table size update is above low water mark"));
+ EXPECT_EQ(Http2SettingsInfo::DefaultHeaderTableSize(),
+ header_table_size_limit());
+}
+
+// Confirm that required size updates are indeed required before the end.
+TEST_P(HpackDecoderTest, RequiredTableSizeChangeBeforeEnd) {
+ decoder_.ApplyHeaderTableSizeSetting(1024);
+ EXPECT_FALSE(DecodeBlock(""));
+ EXPECT_EQ(HpackDecodingError::kMissingDynamicTableSizeUpdate,
+ decoder_.error());
+ EXPECT_EQ(1u, error_messages_.size());
+ EXPECT_THAT(error_messages_[0], Eq("Missing dynamic table size update"));
+ EXPECT_FALSE(saw_end_);
+}
+
+// Confirm that required size updates are indeed required before an
+// indexed header.
+TEST_P(HpackDecoderTest, RequiredTableSizeChangeBeforeIndexedHeader) {
+ decoder_.ApplyHeaderTableSizeSetting(1024);
+ HpackBlockBuilder hbb;
+ hbb.AppendIndexedHeader(1);
+ EXPECT_FALSE(DecodeBlock(hbb.buffer()));
+ EXPECT_EQ(HpackDecodingError::kMissingDynamicTableSizeUpdate,
+ decoder_.error());
+ EXPECT_EQ(1u, error_messages_.size());
+ EXPECT_THAT(error_messages_[0], Eq("Missing dynamic table size update"));
+ EXPECT_FALSE(saw_end_);
+ EXPECT_TRUE(header_entries_.empty());
+}
+
+// Confirm that required size updates are indeed required before an indexed
+// header name.
+// TODO(jamessynge): Move some of these to hpack_decoder_state_test.cc.
+TEST_P(HpackDecoderTest, RequiredTableSizeChangeBeforeIndexedHeaderName) {
+ decoder_.ApplyHeaderTableSizeSetting(1024);
+ HpackBlockBuilder hbb;
+ hbb.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 2,
+ false, "PUT");
+ EXPECT_FALSE(DecodeBlock(hbb.buffer()));
+ EXPECT_EQ(HpackDecodingError::kMissingDynamicTableSizeUpdate,
+ decoder_.error());
+ EXPECT_EQ(1u, error_messages_.size());
+ EXPECT_THAT(error_messages_[0], Eq("Missing dynamic table size update"));
+ EXPECT_FALSE(saw_end_);
+ EXPECT_TRUE(header_entries_.empty());
+}
+
+// Confirm that required size updates are indeed required before a literal
+// header name.
+TEST_P(HpackDecoderTest, RequiredTableSizeChangeBeforeLiteralName) {
+ decoder_.ApplyHeaderTableSizeSetting(1024);
+ HpackBlockBuilder hbb;
+ hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
+ false, "name", false, "some data.");
+ EXPECT_FALSE(DecodeBlock(hbb.buffer()));
+ EXPECT_EQ(HpackDecodingError::kMissingDynamicTableSizeUpdate,
+ decoder_.error());
+ EXPECT_EQ(1u, error_messages_.size());
+ EXPECT_THAT(error_messages_[0], Eq("Missing dynamic table size update"));
+ EXPECT_FALSE(saw_end_);
+ EXPECT_TRUE(header_entries_.empty());
+}
+
+// Confirm that an excessively long varint is detected, in this case an
+// index of 127, but with lots of additional high-order 0 bits provided,
+// too many to be allowed.
+TEST_P(HpackDecoderTest, InvalidIndexedHeaderVarint) {
+ EXPECT_TRUE(decoder_.StartDecodingBlock());
+ DecodeBuffer db("\xff\x80\x80\x80\x80\x80\x80\x80\x80\x80\x80\x00");
+ EXPECT_FALSE(decoder_.DecodeFragment(&db));
+ EXPECT_TRUE(decoder_.DetectError());
+ EXPECT_FALSE(saw_end_);
+ EXPECT_EQ(HpackDecodingError::kIndexVarintError, decoder_.error());
+ EXPECT_EQ(1u, error_messages_.size());
+ EXPECT_THAT(error_messages_[0],
+ Eq("Index varint beyond implementation limit"));
+ EXPECT_TRUE(header_entries_.empty());
+ // Now that an error has been detected, EndDecodingBlock should not succeed.
+ EXPECT_FALSE(decoder_.EndDecodingBlock());
+}
+
+// Confirm that an invalid index into the tables is detected, in this case an
+// index of 0.
+TEST_P(HpackDecoderTest, InvalidIndex) {
+ EXPECT_TRUE(decoder_.StartDecodingBlock());
+ DecodeBuffer db("\x80");
+ EXPECT_FALSE(decoder_.DecodeFragment(&db));
+ EXPECT_TRUE(decoder_.DetectError());
+ EXPECT_FALSE(saw_end_);
+ EXPECT_EQ(HpackDecodingError::kInvalidIndex, decoder_.error());
+ EXPECT_EQ(1u, error_messages_.size());
+ EXPECT_THAT(error_messages_[0],
+ Eq("Invalid index in indexed header field representation"));
+ EXPECT_TRUE(header_entries_.empty());
+ // Now that an error has been detected, EndDecodingBlock should not succeed.
+ EXPECT_FALSE(decoder_.EndDecodingBlock());
+}
+
+// Confirm that EndDecodingBlock detects a truncated HPACK block.
+TEST_P(HpackDecoderTest, TruncatedBlock) {
+ HpackBlockBuilder hbb;
+ hbb.AppendDynamicTableSizeUpdate(3000);
+ EXPECT_EQ(3u, hbb.size());
+ hbb.AppendDynamicTableSizeUpdate(4000);
+ EXPECT_EQ(6u, hbb.size());
+ // Decodes this block if the whole thing is provided.
+ EXPECT_TRUE(DecodeBlock(hbb.buffer()));
+ EXPECT_EQ(4000u, header_table_size_limit());
+ // Multiple times even.
+ EXPECT_TRUE(DecodeBlock(hbb.buffer()));
+ EXPECT_EQ(4000u, header_table_size_limit());
+ // But not if the block is truncated.
+ EXPECT_FALSE(DecodeBlock(hbb.buffer().substr(0, hbb.size() - 1)));
+ EXPECT_FALSE(saw_end_);
+ EXPECT_EQ(HpackDecodingError::kTruncatedBlock, decoder_.error());
+ EXPECT_EQ(1u, error_messages_.size());
+ EXPECT_THAT(error_messages_[0],
+ Eq("Block ends in the middle of an instruction"));
+ // The first update was decoded.
+ EXPECT_EQ(3000u, header_table_size_limit());
+ EXPECT_EQ(0u, current_header_table_size());
+ EXPECT_TRUE(header_entries_.empty());
+}
+
+// Confirm that an oversized string is detected, ending decoding.
+TEST_P(HpackDecoderTest, OversizeStringDetected) {
+ HpackBlockBuilder hbb;
+ hbb.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
+ false, "name", false, "some data.");
+ hbb.AppendLiteralNameAndValue(HpackEntryType::kUnindexedLiteralHeader, false,
+ "name2", false, "longer data");
+
+ // Normally able to decode this block.
+ EXPECT_TRUE(DecodeBlock(hbb.buffer()));
+ EXPECT_THAT(header_entries_,
+ ElementsAreArray({HpackHeaderEntry{"name", "some data."},
+ HpackHeaderEntry{"name2", "longer data"}}));
+
+ // But not if the maximum size of strings is less than the longest string.
+ decoder_.set_max_string_size_bytes(10);
+ EXPECT_FALSE(DecodeBlock(hbb.buffer()));
+ EXPECT_THAT(header_entries_,
+ ElementsAreArray({HpackHeaderEntry{"name", "some data."}}));
+ EXPECT_FALSE(saw_end_);
+ EXPECT_EQ(HpackDecodingError::kValueTooLong, decoder_.error());
+ EXPECT_EQ(1u, error_messages_.size());
+ EXPECT_THAT(error_messages_[0], Eq("Value length exceeds buffer limit"));
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoding_error.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoding_error.cc
new file mode 100644
index 00000000000..9adb2e29b9b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoding_error.cc
@@ -0,0 +1,51 @@
+// Copyright 2020 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 "quiche/http2/hpack/decoder/hpack_decoding_error.h"
+
+namespace http2 {
+
+// static
+absl::string_view HpackDecodingErrorToString(HpackDecodingError error) {
+ switch (error) {
+ case HpackDecodingError::kOk:
+ return "No error detected";
+ case HpackDecodingError::kIndexVarintError:
+ return "Index varint beyond implementation limit";
+ case HpackDecodingError::kNameLengthVarintError:
+ return "Name length varint beyond implementation limit";
+ case HpackDecodingError::kValueLengthVarintError:
+ return "Value length varint beyond implementation limit";
+ case HpackDecodingError::kNameTooLong:
+ return "Name length exceeds buffer limit";
+ case HpackDecodingError::kValueTooLong:
+ return "Value length exceeds buffer limit";
+ case HpackDecodingError::kNameHuffmanError:
+ return "Name Huffman encoding error";
+ case HpackDecodingError::kValueHuffmanError:
+ return "Value Huffman encoding error";
+ case HpackDecodingError::kMissingDynamicTableSizeUpdate:
+ return "Missing dynamic table size update";
+ case HpackDecodingError::kInvalidIndex:
+ return "Invalid index in indexed header field representation";
+ case HpackDecodingError::kInvalidNameIndex:
+ return "Invalid index in literal header field with indexed name "
+ "representation";
+ case HpackDecodingError::kDynamicTableSizeUpdateNotAllowed:
+ return "Dynamic table size update not allowed";
+ case HpackDecodingError::kInitialDynamicTableSizeUpdateIsAboveLowWaterMark:
+ return "Initial dynamic table size update is above low water mark";
+ case HpackDecodingError::kDynamicTableSizeUpdateIsAboveAcknowledgedSetting:
+ return "Dynamic table size update is above acknowledged setting";
+ case HpackDecodingError::kTruncatedBlock:
+ return "Block ends in the middle of an instruction";
+ case HpackDecodingError::kFragmentTooLong:
+ return "Incoming data fragment exceeds buffer limit";
+ case HpackDecodingError::kCompressedHeaderSizeExceedsLimit:
+ return "Total compressed HPACK data size exceeds limit";
+ }
+ return "invalid HpackDecodingError value";
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoding_error.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoding_error.h
new file mode 100644
index 00000000000..e24eaf151c4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_decoding_error.h
@@ -0,0 +1,51 @@
+// Copyright 2020 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_DECODING_ERROR_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODING_ERROR_H_
+
+#include "absl/strings/string_view.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+enum class HpackDecodingError {
+ // No error detected so far.
+ kOk,
+ // Varint beyond implementation limit.
+ kIndexVarintError,
+ kNameLengthVarintError,
+ kValueLengthVarintError,
+ // String literal length exceeds buffer limit.
+ kNameTooLong,
+ kValueTooLong,
+ // Error in Huffman encoding.
+ kNameHuffmanError,
+ kValueHuffmanError,
+ // Next instruction should have been a dynamic table size update.
+ kMissingDynamicTableSizeUpdate,
+ // Invalid index in indexed header field representation.
+ kInvalidIndex,
+ // Invalid index in literal header field with indexed name representation.
+ kInvalidNameIndex,
+ // Dynamic table size update not allowed.
+ kDynamicTableSizeUpdateNotAllowed,
+ // Initial dynamic table size update is above low water mark.
+ kInitialDynamicTableSizeUpdateIsAboveLowWaterMark,
+ // Dynamic table size update is above acknowledged setting.
+ kDynamicTableSizeUpdateIsAboveAcknowledgedSetting,
+ // HPACK block ends in the middle of an instruction.
+ kTruncatedBlock,
+ // Incoming data fragment exceeds buffer limit.
+ kFragmentTooLong,
+ // Total compressed HPACK data size exceeds limit.
+ kCompressedHeaderSizeExceedsLimit,
+};
+
+QUICHE_EXPORT_PRIVATE absl::string_view HpackDecodingErrorToString(
+ HpackDecodingError error);
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_DECODING_ERROR_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_collector.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_collector.cc
new file mode 100644
index 00000000000..d2e7e2eccd4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_collector.cc
@@ -0,0 +1,301 @@
+// 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 "quiche/http2/hpack/decoder/hpack_entry_collector.h"
+
+#include "absl/strings/str_cat.h"
+#include "quiche/http2/hpack/decoder/hpack_string_collector.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_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,
+ absl::string_view 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,
+ absl::string_view expected_name,
+ bool expected_value_huffman,
+ absl::string_view 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) {
+ QUICHE_CHECK(name_.IsClear());
+ hbb->AppendNameIndexAndLiteralValue(header_type_, index_,
+ value_.huffman_encoded, value_.s);
+ } else {
+ QUICHE_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 {
+ absl::StrAppend(&result, header_type_);
+ }
+ }
+ if (index_ != 0) {
+ absl::StrAppend(&result, " Index=", index_);
+ }
+ if (!name_.IsClear()) {
+ absl::StrAppend(&result, " Name", name_.ToString());
+ }
+ if (!value_.IsClear()) {
+ absl::StrAppend(&result, " Value", value_.ToString());
+ }
+ if (!started_) {
+ EXPECT_FALSE(ended_);
+ absl::StrAppend(&result, " !started");
+ } else if (!ended_) {
+ absl::StrAppend(&result, " !ended");
+ } else {
+ absl::StrAppend(&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
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_collector.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_collector.h
new file mode 100644
index 00000000000..aa3919c8fca
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_collector.h
@@ -0,0 +1,159 @@
+// 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_ENTRY_COLLECTOR_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_ENTRY_COLLECTOR_H_
+
+// HpackEntryCollector records calls to HpackEntryDecoderListener in support
+// of tests of HpackEntryDecoder, or which use it. Can only record the callbacks
+// for the decoding of a single entry; call Clear() between decoding successive
+// entries or use a distinct HpackEntryCollector for each entry.
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "quiche/http2/hpack/decoder/hpack_string_collector.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/http2/hpack/tools/hpack_block_builder.h"
+#include "quiche/common/platform/api/quiche_export.h"
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace test {
+
+class QUICHE_NO_EXPORT HpackEntryCollector : public HpackEntryDecoderListener {
+ public:
+ HpackEntryCollector();
+ HpackEntryCollector(const HpackEntryCollector& other);
+
+ // These next three constructors are intended for use in tests that create
+ // an HpackEntryCollector "manually", and then compare it against another
+ // that is populated via calls to the HpackEntryDecoderListener methods.
+ HpackEntryCollector(HpackEntryType type, size_t index_or_size);
+ HpackEntryCollector(HpackEntryType type,
+ size_t index,
+ bool value_huffman,
+ const std::string& value);
+ HpackEntryCollector(HpackEntryType type,
+ bool name_huffman,
+ const std::string& name,
+ bool value_huffman,
+ const std::string& value);
+
+ ~HpackEntryCollector() override;
+
+ // Methods defined by HpackEntryDecoderListener.
+ void OnIndexedHeader(size_t index) override;
+ void OnStartLiteralHeader(HpackEntryType header_type,
+ size_t maybe_name_index) override;
+ void OnNameStart(bool huffman_encoded, size_t len) override;
+ void OnNameData(const char* data, size_t len) override;
+ void OnNameEnd() override;
+ void OnValueStart(bool huffman_encoded, size_t len) override;
+ void OnValueData(const char* data, size_t len) override;
+ void OnValueEnd() override;
+ void OnDynamicTableSizeUpdate(size_t size) override;
+
+ // Clears the fields of the collector so that it is ready to start collecting
+ // another HPACK block entry.
+ void Clear();
+
+ // Is the collector ready to start collecting another HPACK block entry.
+ bool IsClear() const;
+
+ // Has a complete entry been collected?
+ bool IsComplete() const;
+
+ // Based on the HpackEntryType, is a literal name expected?
+ bool LiteralNameExpected() const;
+
+ // Based on the HpackEntryType, is a literal value expected?
+ bool LiteralValueExpected() const;
+
+ // Returns success if collected an Indexed Header (i.e. OnIndexedHeader was
+ // called).
+ ::testing::AssertionResult ValidateIndexedHeader(size_t expected_index) const;
+
+ // Returns success if collected a Header with an indexed name and literal
+ // value (i.e. OnStartLiteralHeader was called with a non-zero index for
+ // the name, which must match expected_index).
+ ::testing::AssertionResult ValidateLiteralValueHeader(
+ HpackEntryType expected_type,
+ size_t expected_index,
+ bool expected_value_huffman,
+ absl::string_view expected_value) const;
+
+ // Returns success if collected a Header with an literal name and literal
+ // value.
+ ::testing::AssertionResult ValidateLiteralNameValueHeader(
+ HpackEntryType expected_type,
+ bool expected_name_huffman,
+ absl::string_view expected_name,
+ bool expected_value_huffman,
+ absl::string_view expected_value) const;
+
+ // Returns success if collected a Dynamic Table Size Update,
+ // with the specified size.
+ ::testing::AssertionResult ValidateDynamicTableSizeUpdate(
+ size_t expected_size) const;
+
+ void set_header_type(HpackEntryType v) { header_type_ = v; }
+ HpackEntryType header_type() const { return header_type_; }
+
+ void set_index(size_t v) { index_ = v; }
+ size_t index() const { return index_; }
+
+ void set_name(const HpackStringCollector& v) { name_ = v; }
+ const HpackStringCollector& name() const { return name_; }
+
+ void set_value(const HpackStringCollector& v) { value_ = v; }
+ const HpackStringCollector& value() const { return value_; }
+
+ void set_started(bool v) { started_ = v; }
+ bool started() const { return started_; }
+
+ void set_ended(bool v) { ended_ = v; }
+ bool ended() const { return ended_; }
+
+ void AppendToHpackBlockBuilder(HpackBlockBuilder* hbb) const;
+
+ // Returns a debug string.
+ std::string ToString() const;
+
+ private:
+ void Init(HpackEntryType type, size_t maybe_index);
+
+ HpackEntryType header_type_;
+ size_t index_;
+
+ HpackStringCollector name_;
+ HpackStringCollector value_;
+
+ // True if has received a call to an HpackEntryDecoderListener method
+ // indicating the start of decoding an HPACK entry; for example,
+ // OnIndexedHeader set it true, but OnNameStart does not change it.
+ bool started_ = false;
+
+ // True if has received a call to an HpackEntryDecoderListener method
+ // indicating the end of decoding an HPACK entry; for example,
+ // OnIndexedHeader and OnValueEnd both set it true, but OnNameEnd does
+ // not change it.
+ bool ended_ = false;
+};
+
+QUICHE_NO_EXPORT bool operator==(const HpackEntryCollector& a,
+ const HpackEntryCollector& b);
+QUICHE_NO_EXPORT bool operator!=(const HpackEntryCollector& a,
+ const HpackEntryCollector& b);
+QUICHE_NO_EXPORT std::ostream& operator<<(std::ostream& out,
+ const HpackEntryCollector& v);
+
+} // namespace test
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_ENTRY_COLLECTOR_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.cc
new file mode 100644
index 00000000000..c81e2984be5
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.cc
@@ -0,0 +1,295 @@
+// 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 "quiche/http2/hpack/decoder/hpack_entry_decoder.h"
+
+#include <stddef.h>
+
+#include <cstdint>
+
+#include "absl/base/macros.h"
+#include "quiche/http2/platform/api/http2_bug_tracker.h"
+#include "quiche/http2/platform/api/http2_flag_utils.h"
+#include "quiche/http2/platform/api/http2_flags.h"
+#include "quiche/http2/platform/api/http2_logging.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) {
+ QUICHE_DCHECK(db != nullptr);
+ QUICHE_DCHECK(listener != nullptr);
+ QUICHE_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.
+ QUICHE_DCHECK_EQ(0u, db->Remaining());
+ state_ = EntryDecoderState::kResumeDecodingType;
+ return status;
+ case DecodeStatus::kDecodeError:
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 11, 23);
+ error_ = HpackDecodingError::kIndexVarintError;
+ // The varint must have been invalid (too long).
+ return status;
+ }
+
+ HTTP2_BUG(http2_bug_63_1) << "Unreachable";
+ return DecodeStatus::kDecodeError;
+}
+
+DecodeStatus HpackEntryDecoder::Resume(DecodeBuffer* db,
+ HpackEntryDecoderListener* listener) {
+ QUICHE_DCHECK(db != nullptr);
+ QUICHE_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::kDecodeError) {
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 12, 23);
+ error_ = HpackDecodingError::kIndexVarintError;
+ }
+ if (status != DecodeStatus::kDecodeDone) {
+ return status;
+ }
+ state_ = EntryDecoderState::kDecodedType;
+ ABSL_FALLTHROUGH_INTENDED;
+
+ 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;
+ if (status == DecodeStatus::kDecodeError) {
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 13, 23);
+ error_ = HpackDecodingError::kNameLengthVarintError;
+ }
+ return status;
+ }
+ state_ = EntryDecoderState::kStartDecodingValue;
+ ABSL_FALLTHROUGH_INTENDED;
+
+ case EntryDecoderState::kStartDecodingValue:
+ HTTP2_DVLOG(1) << "kStartDecodingValue: db->Remaining="
+ << db->Remaining();
+ {
+ ValueDecoderListener vcb(listener);
+ status = string_decoder_.Start(db, &vcb);
+ }
+ if (status == DecodeStatus::kDecodeError) {
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 14, 23);
+ error_ = HpackDecodingError::kValueLengthVarintError;
+ }
+ 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;
+ if (status == DecodeStatus::kDecodeError) {
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 15, 23);
+ error_ = HpackDecodingError::kNameLengthVarintError;
+ }
+ 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::kDecodeError) {
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 16, 23);
+ error_ = HpackDecodingError::kValueLengthVarintError;
+ }
+ 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(http2_bug_63_2) << "Unreachable, entry_type=" << entry_type;
+ return true;
+}
+
+void HpackEntryDecoder::OutputDebugString(std::ostream& out) const {
+ out << "HpackEntryDecoder(state=" << state_ << ", " << entry_type_decoder_
+ << ", " << string_decoder_ << ")";
+}
+
+std::string 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
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.h
new file mode 100644
index 00000000000..50ddd98039c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder.h
@@ -0,0 +1,91 @@
+// 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_ENTRY_DECODER_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_H_
+
+// HpackEntryDecoder decodes a single HPACK entry (i.e. one header or one
+// dynamic table size update), in a resumable fashion. The first call, Start(),
+// must provide a non-empty decode buffer. Continue with calls to Resume() if
+// Start, and any subsequent calls to Resume, returns kDecodeInProgress.
+
+#include <string>
+
+#include "quiche/http2/decoder/decode_buffer.h"
+#include "quiche/http2/decoder/decode_status.h"
+#include "quiche/http2/hpack/decoder/hpack_decoding_error.h"
+#include "quiche/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "quiche/http2/hpack/decoder/hpack_entry_type_decoder.h"
+#include "quiche/http2/hpack/decoder/hpack_string_decoder.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+class QUICHE_EXPORT_PRIVATE HpackEntryDecoder {
+ public:
+ enum class EntryDecoderState {
+ // Have started decoding the type/varint, but didn't finish on the previous
+ // attempt. Next state is kResumeDecodingType or kDecodedType.
+ kResumeDecodingType,
+
+ // Have just finished decoding the type/varint. Final state if the type is
+ // kIndexedHeader or kDynamicTableSizeUpdate. Otherwise, the next state is
+ // kStartDecodingName (if the varint is 0), else kStartDecodingValue.
+ kDecodedType,
+
+ // Ready to start decoding the literal name of a header entry. Next state
+ // is kResumeDecodingName (if the name is split across decode buffers),
+ // else kStartDecodingValue.
+ kStartDecodingName,
+
+ // Resume decoding the literal name of a header that is split across decode
+ // buffers.
+ kResumeDecodingName,
+
+ // Ready to start decoding the literal value of a header entry. Final state
+ // if the value string is entirely in the decode buffer, else the next state
+ // is kResumeDecodingValue.
+ kStartDecodingValue,
+
+ // Resume decoding the literal value of a header that is split across decode
+ // buffers.
+ kResumeDecodingValue,
+ };
+
+ // Only call when the decode buffer has data (i.e. HpackBlockDecoder must
+ // not call until there is data).
+ DecodeStatus Start(DecodeBuffer* db, HpackEntryDecoderListener* listener);
+
+ // Only call Resume if the previous call (Start or Resume) returned
+ // kDecodeInProgress; Resume is also called from Start when it has succeeded
+ // in decoding the entry type and its varint.
+ DecodeStatus Resume(DecodeBuffer* db, HpackEntryDecoderListener* listener);
+
+ // Return error code after decoding error occurred.
+ HpackDecodingError error() const { return error_; }
+
+ std::string DebugString() const;
+ void OutputDebugString(std::ostream& out) const;
+
+ private:
+ // Implements handling state kDecodedType.
+ bool DispatchOnType(HpackEntryDecoderListener* listener);
+
+ HpackEntryTypeDecoder entry_type_decoder_;
+ HpackStringDecoder string_decoder_;
+ EntryDecoderState state_ = EntryDecoderState();
+ HpackDecodingError error_ = HpackDecodingError::kOk;
+};
+
+QUICHE_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+ const HpackEntryDecoder& v);
+QUICHE_EXPORT_PRIVATE std::ostream& operator<<(
+ std::ostream& out,
+ HpackEntryDecoder::EntryDecoderState state);
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_listener.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_listener.cc
new file mode 100644
index 00000000000..c7fe4b42a64
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_listener.cc
@@ -0,0 +1,81 @@
+// 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 "quiche/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+
+#include "quiche/http2/platform/api/http2_logging.h"
+
+namespace http2 {
+
+void HpackEntryDecoderVLoggingListener::OnIndexedHeader(size_t index) {
+ HTTP2_VLOG(1) << "OnIndexedHeader, index=" << index;
+ if (wrapped_) {
+ wrapped_->OnIndexedHeader(index);
+ }
+}
+
+void HpackEntryDecoderVLoggingListener::OnStartLiteralHeader(
+ HpackEntryType entry_type,
+ size_t maybe_name_index) {
+ HTTP2_VLOG(1) << "OnStartLiteralHeader: entry_type=" << entry_type
+ << ", maybe_name_index=" << maybe_name_index;
+ if (wrapped_) {
+ wrapped_->OnStartLiteralHeader(entry_type, maybe_name_index);
+ }
+}
+
+void HpackEntryDecoderVLoggingListener::OnNameStart(bool huffman_encoded,
+ size_t len) {
+ HTTP2_VLOG(1) << "OnNameStart: H=" << huffman_encoded << ", len=" << len;
+ if (wrapped_) {
+ wrapped_->OnNameStart(huffman_encoded, len);
+ }
+}
+
+void HpackEntryDecoderVLoggingListener::OnNameData(const char* data,
+ size_t len) {
+ HTTP2_VLOG(1) << "OnNameData: len=" << len;
+ if (wrapped_) {
+ wrapped_->OnNameData(data, len);
+ }
+}
+
+void HpackEntryDecoderVLoggingListener::OnNameEnd() {
+ HTTP2_VLOG(1) << "OnNameEnd";
+ if (wrapped_) {
+ wrapped_->OnNameEnd();
+ }
+}
+
+void HpackEntryDecoderVLoggingListener::OnValueStart(bool huffman_encoded,
+ size_t len) {
+ HTTP2_VLOG(1) << "OnValueStart: H=" << huffman_encoded << ", len=" << len;
+ if (wrapped_) {
+ wrapped_->OnValueStart(huffman_encoded, len);
+ }
+}
+
+void HpackEntryDecoderVLoggingListener::OnValueData(const char* data,
+ size_t len) {
+ HTTP2_VLOG(1) << "OnValueData: len=" << len;
+ if (wrapped_) {
+ wrapped_->OnValueData(data, len);
+ }
+}
+
+void HpackEntryDecoderVLoggingListener::OnValueEnd() {
+ HTTP2_VLOG(1) << "OnValueEnd";
+ if (wrapped_) {
+ wrapped_->OnValueEnd();
+ }
+}
+
+void HpackEntryDecoderVLoggingListener::OnDynamicTableSizeUpdate(size_t size) {
+ HTTP2_VLOG(1) << "OnDynamicTableSizeUpdate: size=" << size;
+ if (wrapped_) {
+ wrapped_->OnDynamicTableSizeUpdate(size);
+ }
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_listener.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_listener.h
new file mode 100644
index 00000000000..1ba93391d67
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_listener.h
@@ -0,0 +1,110 @@
+// 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_ENTRY_DECODER_LISTENER_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_LISTENER_H_
+
+// Defines HpackEntryDecoderListener, the base class of listeners that
+// HpackEntryDecoder calls. Also defines HpackEntryDecoderVLoggingListener
+// which logs before calling another HpackEntryDecoderListener implementation.
+
+#include <stddef.h>
+
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+class QUICHE_EXPORT_PRIVATE HpackEntryDecoderListener {
+ public:
+ virtual ~HpackEntryDecoderListener() {}
+
+ // Called when an indexed header (i.e. one in the static or dynamic table) has
+ // been decoded from an HPACK block. index is supposed to be non-zero, but
+ // that has not been checked by the caller.
+ virtual void OnIndexedHeader(size_t index) = 0;
+
+ // Called when the start of a header with a literal value, and maybe a literal
+ // name, has been decoded. maybe_name_index is zero if the header has a
+ // literal name, else it is a reference into the static or dynamic table, from
+ // which the name should be determined. When the name is literal, the next
+ // call will be to OnNameStart; else it will be to OnValueStart. entry_type
+ // indicates whether the peer has added the entry to its dynamic table, and
+ // whether a proxy is permitted to do so when forwarding the entry.
+ virtual void OnStartLiteralHeader(HpackEntryType entry_type,
+ size_t maybe_name_index) = 0;
+
+ // Called when the encoding (Huffman compressed or plain text) and the encoded
+ // length of a literal name has been decoded. OnNameData will be called next,
+ // and repeatedly until the sum of lengths passed to OnNameData is len.
+ virtual void OnNameStart(bool huffman_encoded, size_t len) = 0;
+
+ // Called when len bytes of an encoded header name have been decoded.
+ virtual void OnNameData(const char* data, size_t len) = 0;
+
+ // Called after the entire name has been passed to OnNameData.
+ // OnValueStart will be called next.
+ virtual void OnNameEnd() = 0;
+
+ // Called when the encoding (Huffman compressed or plain text) and the encoded
+ // length of a literal value has been decoded. OnValueData will be called
+ // next, and repeatedly until the sum of lengths passed to OnValueData is len.
+ virtual void OnValueStart(bool huffman_encoded, size_t len) = 0;
+
+ // Called when len bytes of an encoded header value have been decoded.
+ virtual void OnValueData(const char* data, size_t len) = 0;
+
+ // Called after the entire value has been passed to OnValueData, marking the
+ // end of a header entry with a literal value, and maybe a literal name.
+ virtual void OnValueEnd() = 0;
+
+ // Called when an update to the size of the peer's dynamic table has been
+ // decoded.
+ virtual void OnDynamicTableSizeUpdate(size_t size) = 0;
+};
+
+class QUICHE_EXPORT_PRIVATE HpackEntryDecoderVLoggingListener
+ : public HpackEntryDecoderListener {
+ public:
+ HpackEntryDecoderVLoggingListener() : wrapped_(nullptr) {}
+ explicit HpackEntryDecoderVLoggingListener(HpackEntryDecoderListener* wrapped)
+ : wrapped_(wrapped) {}
+ ~HpackEntryDecoderVLoggingListener() override {}
+
+ void OnIndexedHeader(size_t index) override;
+ void OnStartLiteralHeader(HpackEntryType entry_type,
+ size_t maybe_name_index) override;
+ void OnNameStart(bool huffman_encoded, size_t len) override;
+ void OnNameData(const char* data, size_t len) override;
+ void OnNameEnd() override;
+ void OnValueStart(bool huffman_encoded, size_t len) override;
+ void OnValueData(const char* data, size_t len) override;
+ void OnValueEnd() override;
+ void OnDynamicTableSizeUpdate(size_t size) override;
+
+ private:
+ HpackEntryDecoderListener* const wrapped_;
+};
+
+// A no-op implementation of HpackEntryDecoderListener.
+class QUICHE_EXPORT_PRIVATE HpackEntryDecoderNoOpListener
+ : public HpackEntryDecoderListener {
+ public:
+ ~HpackEntryDecoderNoOpListener() override {}
+
+ void OnIndexedHeader(size_t /*index*/) override {}
+ void OnStartLiteralHeader(HpackEntryType /*entry_type*/,
+ size_t /*maybe_name_index*/) override {}
+ void OnNameStart(bool /*huffman_encoded*/, size_t /*len*/) override {}
+ void OnNameData(const char* /*data*/, size_t /*len*/) override {}
+ void OnNameEnd() override {}
+ void OnValueStart(bool /*huffman_encoded*/, size_t /*len*/) override {}
+ void OnValueData(const char* /*data*/, size_t /*len*/) override {}
+ void OnValueEnd() override {}
+ void OnDynamicTableSizeUpdate(size_t /*size*/) override {}
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_ENTRY_DECODER_LISTENER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_test.cc
new file mode 100644
index 00000000000..275495d6ee3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_decoder_test.cc
@@ -0,0 +1,202 @@
+// 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 "quiche/http2/hpack/decoder/hpack_entry_decoder.h"
+
+// Tests of HpackEntryDecoder.
+
+#include <cstdint>
+
+#include "quiche/http2/hpack/decoder/hpack_entry_collector.h"
+#include "quiche/http2/hpack/tools/hpack_block_builder.h"
+#include "quiche/http2/test_tools/http2_random.h"
+#include "quiche/http2/tools/random_decoder_test.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+namespace http2 {
+namespace test {
+namespace {
+
+class HpackEntryDecoderTest : public RandomDecoderTest {
+ protected:
+ HpackEntryDecoderTest() : listener_(&collector_) {}
+
+ DecodeStatus StartDecoding(DecodeBuffer* b) override {
+ collector_.Clear();
+ return decoder_.Start(b, &listener_);
+ }
+
+ DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+ return decoder_.Resume(b, &listener_);
+ }
+
+ AssertionResult DecodeAndValidateSeveralWays(DecodeBuffer* db,
+ const Validator& validator) {
+ // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
+ // can call Start with the prefix byte.
+ bool return_non_zero_on_first = true;
+ return RandomDecoderTest::DecodeAndValidateSeveralWays(
+ db, return_non_zero_on_first, validator);
+ }
+
+ AssertionResult DecodeAndValidateSeveralWays(const HpackBlockBuilder& hbb,
+ const Validator& validator) {
+ DecodeBuffer db(hbb.buffer());
+ return DecodeAndValidateSeveralWays(&db, validator);
+ }
+
+ HpackEntryDecoder decoder_;
+ HpackEntryCollector collector_;
+ HpackEntryDecoderVLoggingListener listener_;
+};
+
+TEST_F(HpackEntryDecoderTest, IndexedHeader_Literals) {
+ {
+ const char input[] = {'\x82'}; // == Index 2 ==
+ DecodeBuffer b(input);
+ auto do_check = [this]() { return collector_.ValidateIndexedHeader(2); };
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+ }
+ collector_.Clear();
+ {
+ const char input[] = {'\xfe'}; // == Index 126 ==
+ DecodeBuffer b(input);
+ auto do_check = [this]() { return collector_.ValidateIndexedHeader(126); };
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+ }
+ collector_.Clear();
+ {
+ const char input[] = {'\xff', '\x00'}; // == Index 127 ==
+ DecodeBuffer b(input);
+ auto do_check = [this]() { return collector_.ValidateIndexedHeader(127); };
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+ }
+}
+
+TEST_F(HpackEntryDecoderTest, IndexedHeader_Various) {
+ // Indices chosen to hit encoding and table boundaries.
+ for (const uint32_t ndx : {1, 2, 61, 62, 63, 126, 127, 254, 255, 256}) {
+ HpackBlockBuilder hbb;
+ hbb.AppendIndexedHeader(ndx);
+
+ auto do_check = [this, ndx]() {
+ return collector_.ValidateIndexedHeader(ndx);
+ };
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+ }
+}
+
+TEST_F(HpackEntryDecoderTest, IndexedLiteralValue_Literal) {
+ const char input[] =
+ "\x7f" // == Literal indexed, name index 0x40 ==
+ "\x01" // 2nd byte of name index (0x01 + 0x3f == 0x40)
+ "\x0d" // Value length (13)
+ "custom-header"; // Value
+ DecodeBuffer b(input, sizeof input - 1);
+ auto do_check = [this]() {
+ return collector_.ValidateLiteralValueHeader(
+ HpackEntryType::kIndexedLiteralHeader, 0x40, false, "custom-header");
+ };
+ EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+}
+
+TEST_F(HpackEntryDecoderTest, IndexedLiteralNameValue_Literal) {
+ const char input[] =
+ "\x40" // == Literal indexed ==
+ "\x0a" // Name length (10)
+ "custom-key" // Name
+ "\x0d" // Value length (13)
+ "custom-header"; // Value
+
+ DecodeBuffer b(input, sizeof input - 1);
+ auto do_check = [this]() {
+ return collector_.ValidateLiteralNameValueHeader(
+ HpackEntryType::kIndexedLiteralHeader, false, "custom-key", false,
+ "custom-header");
+ };
+ EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+}
+
+TEST_F(HpackEntryDecoderTest, DynamicTableSizeUpdate_Literal) {
+ // Size update, length 31.
+ const char input[] = "\x3f\x00";
+ DecodeBuffer b(input, 2);
+ auto do_check = [this]() {
+ return collector_.ValidateDynamicTableSizeUpdate(31);
+ };
+ EXPECT_TRUE(DecodeAndValidateSeveralWays(&b, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+}
+
+class HpackLiteralEntryDecoderTest
+ : public HpackEntryDecoderTest,
+ public ::testing::WithParamInterface<HpackEntryType> {
+ protected:
+ HpackLiteralEntryDecoderTest() : entry_type_(GetParam()) {}
+
+ const HpackEntryType entry_type_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ AllLiteralTypes, HpackLiteralEntryDecoderTest,
+ testing::Values(HpackEntryType::kIndexedLiteralHeader,
+ HpackEntryType::kUnindexedLiteralHeader,
+ HpackEntryType::kNeverIndexedLiteralHeader));
+
+TEST_P(HpackLiteralEntryDecoderTest, RandNameIndexAndLiteralValue) {
+ for (int n = 0; n < 10; n++) {
+ const uint32_t ndx = 1 + Random().Rand8();
+ const bool value_is_huffman_encoded = (n % 2) == 0;
+ const std::string value = Random().RandString(Random().Rand8());
+ HpackBlockBuilder hbb;
+ hbb.AppendNameIndexAndLiteralValue(entry_type_, ndx,
+ value_is_huffman_encoded, value);
+ auto do_check = [this, ndx, value_is_huffman_encoded,
+ value]() -> AssertionResult {
+ return collector_.ValidateLiteralValueHeader(
+ entry_type_, ndx, value_is_huffman_encoded, value);
+ };
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+ }
+}
+
+TEST_P(HpackLiteralEntryDecoderTest, RandLiteralNameAndValue) {
+ for (int n = 0; n < 10; n++) {
+ const bool name_is_huffman_encoded = (n & 1) == 0;
+ const int name_len = 1 + Random().Rand8();
+ const std::string name = Random().RandString(name_len);
+ const bool value_is_huffman_encoded = (n & 2) == 0;
+ const int value_len = Random().Skewed(10);
+ const std::string value = Random().RandString(value_len);
+ HpackBlockBuilder hbb;
+ hbb.AppendLiteralNameAndValue(entry_type_, name_is_huffman_encoded, name,
+ value_is_huffman_encoded, value);
+ auto do_check = [this, name_is_huffman_encoded, name,
+ value_is_huffman_encoded, value]() -> AssertionResult {
+ return collector_.ValidateLiteralNameValueHeader(
+ entry_type_, name_is_huffman_encoded, name, value_is_huffman_encoded,
+ value);
+ };
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(hbb, ValidateDoneAndEmpty(do_check)));
+ EXPECT_TRUE(do_check());
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder.cc
new file mode 100644
index 00000000000..2bf6d8dd36c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder.cc
@@ -0,0 +1,362 @@
+// 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 "quiche/http2/hpack/decoder/hpack_entry_type_decoder.h"
+
+#include "absl/strings/str_cat.h"
+#include "quiche/http2/platform/api/http2_bug_tracker.h"
+#include "quiche/http2/platform/api/http2_flag_utils.h"
+#include "quiche/http2/platform/api/http2_flags.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+
+namespace http2 {
+
+std::string HpackEntryTypeDecoder::DebugString() const {
+ return absl::StrCat(
+ "HpackEntryTypeDecoder(varint_decoder=", varint_decoder_.DebugString(),
+ ", entry_type=", entry_type_, ")");
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackEntryTypeDecoder& v) {
+ return out << v.DebugString();
+}
+
+// This ridiculous looking function turned out to be the winner in benchmarking
+// of several very different alternative implementations. It would be even
+// faster (~7%) if inlined in the header file, but I'm not sure if that is
+// worth doing... yet.
+// TODO(jamessynge): Benchmark again at a higher level (e.g. at least at the
+// full HTTP/2 decoder level, but preferably still higher) to determine if the
+// alternatives that take less code/data space are preferable in that situation.
+DecodeStatus HpackEntryTypeDecoder::Start(DecodeBuffer* db) {
+ QUICHE_DCHECK(db != nullptr);
+ QUICHE_DCHECK(db->HasData());
+
+ // The high four bits (nibble) of first byte of the entry determine the type
+ // of the entry, and may also be the initial bits of the varint that
+ // represents an index or table size. Note the use of the word 'initial'
+ // rather than 'high'; the HPACK encoding of varints is not in network
+ // order (i.e. not big-endian, the high-order byte isn't first), nor in
+ // little-endian order. See:
+ // http://httpwg.org/specs/rfc7541.html#integer.representation
+ uint8_t byte = db->DecodeUInt8();
+ switch (byte) {
+ case 0b00000000:
+ case 0b00000001:
+ case 0b00000010:
+ case 0b00000011:
+ case 0b00000100:
+ case 0b00000101:
+ case 0b00000110:
+ case 0b00000111:
+ case 0b00001000:
+ case 0b00001001:
+ case 0b00001010:
+ case 0b00001011:
+ case 0b00001100:
+ case 0b00001101:
+ case 0b00001110:
+ // The low 4 bits of |byte| are the initial bits of the varint.
+ // One of those bits is 0, so the varint is only one byte long.
+ entry_type_ = HpackEntryType::kUnindexedLiteralHeader;
+ varint_decoder_.set_value(byte);
+ return DecodeStatus::kDecodeDone;
+
+ case 0b00001111:
+ // The low 4 bits of |byte| are the initial bits of the varint. All 4
+ // are 1, so the varint extends into another byte.
+ entry_type_ = HpackEntryType::kUnindexedLiteralHeader;
+ return varint_decoder_.StartExtended(4, db);
+
+ case 0b00010000:
+ case 0b00010001:
+ case 0b00010010:
+ case 0b00010011:
+ case 0b00010100:
+ case 0b00010101:
+ case 0b00010110:
+ case 0b00010111:
+ case 0b00011000:
+ case 0b00011001:
+ case 0b00011010:
+ case 0b00011011:
+ case 0b00011100:
+ case 0b00011101:
+ case 0b00011110:
+ // The low 4 bits of |byte| are the initial bits of the varint.
+ // One of those bits is 0, so the varint is only one byte long.
+ entry_type_ = HpackEntryType::kNeverIndexedLiteralHeader;
+ varint_decoder_.set_value(byte & 0x0f);
+ return DecodeStatus::kDecodeDone;
+
+ case 0b00011111:
+ // The low 4 bits of |byte| are the initial bits of the varint.
+ // All of those bits are 1, so the varint extends into another byte.
+ entry_type_ = HpackEntryType::kNeverIndexedLiteralHeader;
+ return varint_decoder_.StartExtended(4, db);
+
+ case 0b00100000:
+ case 0b00100001:
+ case 0b00100010:
+ case 0b00100011:
+ case 0b00100100:
+ case 0b00100101:
+ case 0b00100110:
+ case 0b00100111:
+ case 0b00101000:
+ case 0b00101001:
+ case 0b00101010:
+ case 0b00101011:
+ case 0b00101100:
+ case 0b00101101:
+ case 0b00101110:
+ case 0b00101111:
+ case 0b00110000:
+ case 0b00110001:
+ case 0b00110010:
+ case 0b00110011:
+ case 0b00110100:
+ case 0b00110101:
+ case 0b00110110:
+ case 0b00110111:
+ case 0b00111000:
+ case 0b00111001:
+ case 0b00111010:
+ case 0b00111011:
+ case 0b00111100:
+ case 0b00111101:
+ case 0b00111110:
+ entry_type_ = HpackEntryType::kDynamicTableSizeUpdate;
+ // The low 5 bits of |byte| are the initial bits of the varint.
+ // One of those bits is 0, so the varint is only one byte long.
+ varint_decoder_.set_value(byte & 0x01f);
+ return DecodeStatus::kDecodeDone;
+
+ case 0b00111111:
+ entry_type_ = HpackEntryType::kDynamicTableSizeUpdate;
+ // The low 5 bits of |byte| are the initial bits of the varint.
+ // All of those bits are 1, so the varint extends into another byte.
+ return varint_decoder_.StartExtended(5, db);
+
+ case 0b01000000:
+ case 0b01000001:
+ case 0b01000010:
+ case 0b01000011:
+ case 0b01000100:
+ case 0b01000101:
+ case 0b01000110:
+ case 0b01000111:
+ case 0b01001000:
+ case 0b01001001:
+ case 0b01001010:
+ case 0b01001011:
+ case 0b01001100:
+ case 0b01001101:
+ case 0b01001110:
+ case 0b01001111:
+ case 0b01010000:
+ case 0b01010001:
+ case 0b01010010:
+ case 0b01010011:
+ case 0b01010100:
+ case 0b01010101:
+ case 0b01010110:
+ case 0b01010111:
+ case 0b01011000:
+ case 0b01011001:
+ case 0b01011010:
+ case 0b01011011:
+ case 0b01011100:
+ case 0b01011101:
+ case 0b01011110:
+ case 0b01011111:
+ case 0b01100000:
+ case 0b01100001:
+ case 0b01100010:
+ case 0b01100011:
+ case 0b01100100:
+ case 0b01100101:
+ case 0b01100110:
+ case 0b01100111:
+ case 0b01101000:
+ case 0b01101001:
+ case 0b01101010:
+ case 0b01101011:
+ case 0b01101100:
+ case 0b01101101:
+ case 0b01101110:
+ case 0b01101111:
+ case 0b01110000:
+ case 0b01110001:
+ case 0b01110010:
+ case 0b01110011:
+ case 0b01110100:
+ case 0b01110101:
+ case 0b01110110:
+ case 0b01110111:
+ case 0b01111000:
+ case 0b01111001:
+ case 0b01111010:
+ case 0b01111011:
+ case 0b01111100:
+ case 0b01111101:
+ case 0b01111110:
+ entry_type_ = HpackEntryType::kIndexedLiteralHeader;
+ // The low 6 bits of |byte| are the initial bits of the varint.
+ // One of those bits is 0, so the varint is only one byte long.
+ varint_decoder_.set_value(byte & 0x03f);
+ return DecodeStatus::kDecodeDone;
+
+ case 0b01111111:
+ entry_type_ = HpackEntryType::kIndexedLiteralHeader;
+ // The low 6 bits of |byte| are the initial bits of the varint.
+ // All of those bits are 1, so the varint extends into another byte.
+ return varint_decoder_.StartExtended(6, db);
+
+ case 0b10000000:
+ case 0b10000001:
+ case 0b10000010:
+ case 0b10000011:
+ case 0b10000100:
+ case 0b10000101:
+ case 0b10000110:
+ case 0b10000111:
+ case 0b10001000:
+ case 0b10001001:
+ case 0b10001010:
+ case 0b10001011:
+ case 0b10001100:
+ case 0b10001101:
+ case 0b10001110:
+ case 0b10001111:
+ case 0b10010000:
+ case 0b10010001:
+ case 0b10010010:
+ case 0b10010011:
+ case 0b10010100:
+ case 0b10010101:
+ case 0b10010110:
+ case 0b10010111:
+ case 0b10011000:
+ case 0b10011001:
+ case 0b10011010:
+ case 0b10011011:
+ case 0b10011100:
+ case 0b10011101:
+ case 0b10011110:
+ case 0b10011111:
+ case 0b10100000:
+ case 0b10100001:
+ case 0b10100010:
+ case 0b10100011:
+ case 0b10100100:
+ case 0b10100101:
+ case 0b10100110:
+ case 0b10100111:
+ case 0b10101000:
+ case 0b10101001:
+ case 0b10101010:
+ case 0b10101011:
+ case 0b10101100:
+ case 0b10101101:
+ case 0b10101110:
+ case 0b10101111:
+ case 0b10110000:
+ case 0b10110001:
+ case 0b10110010:
+ case 0b10110011:
+ case 0b10110100:
+ case 0b10110101:
+ case 0b10110110:
+ case 0b10110111:
+ case 0b10111000:
+ case 0b10111001:
+ case 0b10111010:
+ case 0b10111011:
+ case 0b10111100:
+ case 0b10111101:
+ case 0b10111110:
+ case 0b10111111:
+ case 0b11000000:
+ case 0b11000001:
+ case 0b11000010:
+ case 0b11000011:
+ case 0b11000100:
+ case 0b11000101:
+ case 0b11000110:
+ case 0b11000111:
+ case 0b11001000:
+ case 0b11001001:
+ case 0b11001010:
+ case 0b11001011:
+ case 0b11001100:
+ case 0b11001101:
+ case 0b11001110:
+ case 0b11001111:
+ case 0b11010000:
+ case 0b11010001:
+ case 0b11010010:
+ case 0b11010011:
+ case 0b11010100:
+ case 0b11010101:
+ case 0b11010110:
+ case 0b11010111:
+ case 0b11011000:
+ case 0b11011001:
+ case 0b11011010:
+ case 0b11011011:
+ case 0b11011100:
+ case 0b11011101:
+ case 0b11011110:
+ case 0b11011111:
+ case 0b11100000:
+ case 0b11100001:
+ case 0b11100010:
+ case 0b11100011:
+ case 0b11100100:
+ case 0b11100101:
+ case 0b11100110:
+ case 0b11100111:
+ case 0b11101000:
+ case 0b11101001:
+ case 0b11101010:
+ case 0b11101011:
+ case 0b11101100:
+ case 0b11101101:
+ case 0b11101110:
+ case 0b11101111:
+ case 0b11110000:
+ case 0b11110001:
+ case 0b11110010:
+ case 0b11110011:
+ case 0b11110100:
+ case 0b11110101:
+ case 0b11110110:
+ case 0b11110111:
+ case 0b11111000:
+ case 0b11111001:
+ case 0b11111010:
+ case 0b11111011:
+ case 0b11111100:
+ case 0b11111101:
+ case 0b11111110:
+ entry_type_ = HpackEntryType::kIndexedHeader;
+ // The low 7 bits of |byte| are the initial bits of the varint.
+ // One of those bits is 0, so the varint is only one byte long.
+ varint_decoder_.set_value(byte & 0x07f);
+ return DecodeStatus::kDecodeDone;
+
+ case 0b11111111:
+ entry_type_ = HpackEntryType::kIndexedHeader;
+ // The low 7 bits of |byte| are the initial bits of the varint.
+ // All of those bits are 1, so the varint extends into another byte.
+ return varint_decoder_.StartExtended(7, db);
+ }
+ HTTP2_BUG(http2_bug_66_1)
+ << "Unreachable, byte=" << std::hex << static_cast<uint32_t>(byte);
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 17, 23);
+ return DecodeStatus::kDecodeError;
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder.h
new file mode 100644
index 00000000000..94554d4a4ba
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder.h
@@ -0,0 +1,57 @@
+// 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_ENTRY_TYPE_DECODER_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_ENTRY_TYPE_DECODER_H_
+
+// Decodes the type of an HPACK entry, and the variable length integer whose
+// prefix is in the low-order bits of the same byte, "below" the type bits.
+// The integer represents an index into static or dynamic table, which may be
+// zero, or is the new size limit of the dynamic table.
+
+#include <cstdint>
+#include <string>
+
+#include "quiche/http2/decoder/decode_buffer.h"
+#include "quiche/http2/decoder/decode_status.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/http2/hpack/varint/hpack_varint_decoder.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+class QUICHE_EXPORT_PRIVATE HpackEntryTypeDecoder {
+ public:
+ // Only call when the decode buffer has data (i.e. HpackEntryDecoder must
+ // not call until there is data).
+ DecodeStatus Start(DecodeBuffer* db);
+
+ // Only call Resume if the previous call (Start or Resume) returned
+ // DecodeStatus::kDecodeInProgress.
+ DecodeStatus Resume(DecodeBuffer* db) { return varint_decoder_.Resume(db); }
+
+ // Returns the decoded entry type. Only call if the preceding call to Start
+ // or Resume returned kDecodeDone.
+ HpackEntryType entry_type() const { return entry_type_; }
+
+ // Returns the decoded variable length integer. Only call if the
+ // preceding call to Start or Resume returned kDecodeDone.
+ uint64_t varint() const { return varint_decoder_.value(); }
+
+ std::string DebugString() const;
+
+ private:
+ HpackVarintDecoder varint_decoder_;
+
+ // This field is initialized just to keep ASAN happy about reading it
+ // from DebugString().
+ HpackEntryType entry_type_ = HpackEntryType::kIndexedHeader;
+};
+
+QUICHE_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+ const HpackEntryTypeDecoder& v);
+
+} // namespace http2
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_ENTRY_TYPE_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder_test.cc
new file mode 100644
index 00000000000..be0d5acfad6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_entry_type_decoder_test.cc
@@ -0,0 +1,85 @@
+// 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 "quiche/http2/hpack/decoder/hpack_entry_type_decoder.h"
+
+#include <vector>
+
+#include "quiche/http2/hpack/tools/hpack_block_builder.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/http2/tools/random_decoder_test.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+using ::testing::AssertionSuccess;
+
+namespace http2 {
+namespace test {
+namespace {
+const bool kReturnNonZeroOnFirst = true;
+
+class HpackEntryTypeDecoderTest : public RandomDecoderTest {
+ protected:
+ DecodeStatus StartDecoding(DecodeBuffer* b) override {
+ QUICHE_CHECK_LT(0u, b->Remaining());
+ return decoder_.Start(b);
+ }
+
+ DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+ return decoder_.Resume(b);
+ }
+
+ HpackEntryTypeDecoder decoder_;
+};
+
+TEST_F(HpackEntryTypeDecoderTest, DynamicTableSizeUpdate) {
+ for (uint32_t size = 0; size < 1000 * 1000; size += 256) {
+ HpackBlockBuilder bb;
+ bb.AppendDynamicTableSizeUpdate(size);
+ DecodeBuffer db(bb.buffer());
+ auto validator = [size, this]() -> AssertionResult {
+ VERIFY_EQ(HpackEntryType::kDynamicTableSizeUpdate, decoder_.entry_type());
+ VERIFY_EQ(size, decoder_.varint());
+ return AssertionSuccess();
+ };
+ EXPECT_TRUE(DecodeAndValidateSeveralWays(&db, kReturnNonZeroOnFirst,
+ ValidateDoneAndEmpty(validator)))
+ << "\nentry_type=kDynamicTableSizeUpdate, size=" << size;
+ // Run the validator again to make sure that DecodeAndValidateSeveralWays
+ // did the right thing.
+ EXPECT_TRUE(validator());
+ }
+}
+
+TEST_F(HpackEntryTypeDecoderTest, HeaderWithIndex) {
+ std::vector<HpackEntryType> entry_types = {
+ HpackEntryType::kIndexedHeader,
+ HpackEntryType::kIndexedLiteralHeader,
+ HpackEntryType::kUnindexedLiteralHeader,
+ HpackEntryType::kNeverIndexedLiteralHeader,
+ };
+ for (const HpackEntryType entry_type : entry_types) {
+ const uint32_t first = entry_type == HpackEntryType::kIndexedHeader ? 1 : 0;
+ for (uint32_t index = first; index < 1000; ++index) {
+ HpackBlockBuilder bb;
+ bb.AppendEntryTypeAndVarint(entry_type, index);
+ DecodeBuffer db(bb.buffer());
+ auto validator = [entry_type, index, this]() -> AssertionResult {
+ VERIFY_EQ(entry_type, decoder_.entry_type());
+ VERIFY_EQ(index, decoder_.varint());
+ return AssertionSuccess();
+ };
+ EXPECT_TRUE(DecodeAndValidateSeveralWays(&db, kReturnNonZeroOnFirst,
+ ValidateDoneAndEmpty(validator)))
+ << "\nentry_type=" << entry_type << ", index=" << index;
+ // Run the validator again to make sure that DecodeAndValidateSeveralWays
+ // did the right thing.
+ EXPECT_TRUE(validator());
+ }
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_collector.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_collector.cc
new file mode 100644
index 00000000000..5709f2d6451
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_collector.cc
@@ -0,0 +1,124 @@
+// 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 "quiche/http2/hpack/decoder/hpack_string_collector.h"
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <ostream>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/str_cat.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+namespace http2 {
+namespace test {
+namespace {
+
+std::ostream& operator<<(std::ostream& out,
+ HpackStringCollector::CollectorState v) {
+ switch (v) {
+ case HpackStringCollector::CollectorState::kGenesis:
+ return out << "kGenesis";
+ case HpackStringCollector::CollectorState::kStarted:
+ return out << "kStarted";
+ case HpackStringCollector::CollectorState::kEnded:
+ return out << "kEnded";
+ }
+ return out << "UnknownCollectorState";
+}
+
+} // namespace
+
+HpackStringCollector::HpackStringCollector() {
+ Clear();
+}
+
+HpackStringCollector::HpackStringCollector(const std::string& str, bool huffman)
+ : s(str), len(str.size()), huffman_encoded(huffman), state(kEnded) {}
+
+void HpackStringCollector::Clear() {
+ s = "";
+ len = 0;
+ huffman_encoded = false;
+ state = kGenesis;
+}
+
+bool HpackStringCollector::IsClear() const {
+ return s.empty() && len == 0 && huffman_encoded == false && state == kGenesis;
+}
+
+bool HpackStringCollector::IsInProgress() const {
+ return state == kStarted;
+}
+
+bool HpackStringCollector::HasEnded() const {
+ return state == kEnded;
+}
+
+void HpackStringCollector::OnStringStart(bool huffman, size_t length) {
+ EXPECT_TRUE(IsClear()) << ToString();
+ state = kStarted;
+ huffman_encoded = huffman;
+ len = length;
+}
+
+void HpackStringCollector::OnStringData(const char* data, size_t length) {
+ absl::string_view sp(data, length);
+ EXPECT_TRUE(IsInProgress()) << ToString();
+ EXPECT_LE(sp.size(), len) << ToString();
+ absl::StrAppend(&s, sp);
+ EXPECT_LE(s.size(), len) << ToString();
+}
+
+void HpackStringCollector::OnStringEnd() {
+ EXPECT_TRUE(IsInProgress()) << ToString();
+ EXPECT_EQ(s.size(), len) << ToString();
+ state = kEnded;
+}
+
+::testing::AssertionResult HpackStringCollector::Collected(
+ absl::string_view str,
+ bool is_huffman_encoded) const {
+ VERIFY_TRUE(HasEnded());
+ VERIFY_EQ(str.size(), len);
+ VERIFY_EQ(is_huffman_encoded, huffman_encoded);
+ VERIFY_EQ(str, s);
+ return ::testing::AssertionSuccess();
+}
+
+std::string HpackStringCollector::ToString() const {
+ std::stringstream ss;
+ ss << *this;
+ return ss.str();
+}
+
+bool operator==(const HpackStringCollector& a, const HpackStringCollector& b) {
+ return a.s == b.s && a.len == b.len &&
+ a.huffman_encoded == b.huffman_encoded && a.state == b.state;
+}
+
+bool operator!=(const HpackStringCollector& a, const HpackStringCollector& b) {
+ return !(a == b);
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackStringCollector& v) {
+ out << "HpackStringCollector(state=" << v.state;
+ if (v.state == HpackStringCollector::kGenesis) {
+ return out << ")";
+ }
+ if (v.huffman_encoded) {
+ out << ", Huffman Encoded";
+ }
+ out << ", Length=" << v.len;
+ if (!v.s.empty() && v.len != v.s.size()) {
+ out << " (" << v.s.size() << ")";
+ }
+ return out << ", String=\"" << absl::CHexEscape(v.s) << "\")";
+}
+
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_collector.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_collector.h
new file mode 100644
index 00000000000..270c8a5906f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_collector.h
@@ -0,0 +1,63 @@
+// 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_COLLECTOR_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_STRING_COLLECTOR_H_
+
+// Supports tests of decoding HPACK strings.
+
+#include <stddef.h>
+
+#include <iosfwd>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/decoder/hpack_string_decoder_listener.h"
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace test {
+
+// Records the callbacks associated with a decoding a string; must
+// call Clear() between decoding successive strings.
+struct HpackStringCollector : public HpackStringDecoderListener {
+ enum CollectorState {
+ kGenesis,
+ kStarted,
+ kEnded,
+ };
+
+ HpackStringCollector();
+ HpackStringCollector(const std::string& str, bool huffman);
+
+ void Clear();
+ bool IsClear() const;
+ bool IsInProgress() const;
+ bool HasEnded() const;
+
+ void OnStringStart(bool huffman, size_t length) override;
+ void OnStringData(const char* data, size_t length) override;
+ void OnStringEnd() override;
+
+ ::testing::AssertionResult Collected(absl::string_view str,
+ bool is_huffman_encoded) const;
+
+ std::string ToString() const;
+
+ std::string s;
+ size_t len;
+ bool huffman_encoded;
+ CollectorState state;
+};
+
+bool operator==(const HpackStringCollector& a, const HpackStringCollector& b);
+
+bool operator!=(const HpackStringCollector& a, const HpackStringCollector& b);
+
+std::ostream& operator<<(std::ostream& out, const HpackStringCollector& v);
+
+} // namespace test
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_STRING_COLLECTOR_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder.cc
new file mode 100644
index 00000000000..f2a4bf80664
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder.cc
@@ -0,0 +1,35 @@
+// 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 "quiche/http2/hpack/decoder/hpack_string_decoder.h"
+
+#include "absl/strings/str_cat.h"
+
+namespace http2 {
+
+std::string HpackStringDecoder::DebugString() const {
+ return absl::StrCat("HpackStringDecoder(state=", StateToString(state_),
+ ", length=", length_decoder_.DebugString(),
+ ", remaining=", remaining_,
+ ", huffman=", huffman_encoded_ ? "true)" : "false)");
+}
+
+// static
+std::string HpackStringDecoder::StateToString(StringDecoderState v) {
+ switch (v) {
+ case kStartDecodingLength:
+ return "kStartDecodingLength";
+ case kDecodingString:
+ return "kDecodingString";
+ case kResumeDecodingLength:
+ return "kResumeDecodingLength";
+ }
+ return absl::StrCat("UNKNOWN_STATE(", static_cast<uint32_t>(v), ")");
+}
+
+std::ostream& operator<<(std::ostream& out, const HpackStringDecoder& v) {
+ return out << v.DebugString();
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder.h
new file mode 100644
index 00000000000..a8566858aba
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder.h
@@ -0,0 +1,209 @@
+// 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 <string>
+
+#include "absl/base/macros.h"
+#include "quiche/http2/decoder/decode_buffer.h"
+#include "quiche/http2/decoder/decode_status.h"
+#include "quiche/http2/hpack/varint/hpack_varint_decoder.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_export.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 QUICHE_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.
+ ABSL_FALLTHROUGH_INTENDED;
+
+ 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;
+ }
+ }
+ }
+ }
+
+ std::string DebugString() const;
+
+ private:
+ static std::string 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) {
+ QUICHE_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;
+};
+
+QUICHE_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+ const HpackStringDecoder& v);
+
+} // namespace http2
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_listener.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_listener.cc
new file mode 100644
index 00000000000..fef70c28e9d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_listener.cc
@@ -0,0 +1,36 @@
+// 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 "quiche/http2/hpack/decoder/hpack_string_decoder_listener.h"
+
+#include "quiche/http2/platform/api/http2_logging.h"
+
+namespace http2 {
+namespace test {
+
+void HpackStringDecoderVLoggingListener::OnStringStart(bool huffman_encoded,
+ size_t len) {
+ HTTP2_VLOG(1) << "OnStringStart: H=" << huffman_encoded << ", len=" << len;
+ if (wrapped_) {
+ wrapped_->OnStringStart(huffman_encoded, len);
+ }
+}
+
+void HpackStringDecoderVLoggingListener::OnStringData(const char* data,
+ size_t len) {
+ HTTP2_VLOG(1) << "OnStringData: len=" << len;
+ if (wrapped_) {
+ return wrapped_->OnStringData(data, len);
+ }
+}
+
+void HpackStringDecoderVLoggingListener::OnStringEnd() {
+ HTTP2_VLOG(1) << "OnStringEnd";
+ if (wrapped_) {
+ return wrapped_->OnStringEnd();
+ }
+}
+
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_listener.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_listener.h
new file mode 100644
index 00000000000..c343d407e7c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_listener.h
@@ -0,0 +1,62 @@
+// 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_LISTENER_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_LISTENER_H_
+
+// Defines HpackStringDecoderListener which defines the methods required by an
+// HpackStringDecoder. Also defines HpackStringDecoderVLoggingListener which
+// logs before calling another HpackStringDecoderListener implementation.
+// For now these are only used by tests, so placed in the test namespace.
+
+#include <stddef.h>
+
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+namespace test {
+
+// HpackStringDecoder methods require a listener that implements the methods
+// below, but it is NOT necessary to extend this class because the methods
+// are templates.
+class QUICHE_EXPORT_PRIVATE HpackStringDecoderListener {
+ public:
+ virtual ~HpackStringDecoderListener() {}
+
+ // Called at the start of decoding an HPACK string. The encoded length of the
+ // string is |len| bytes, which may be zero. The string is Huffman encoded
+ // if huffman_encoded is true, else it is plain text (i.e. the encoded length
+ // is then the plain text length).
+ virtual void OnStringStart(bool huffman_encoded, size_t len) = 0;
+
+ // Called when some data is available, or once when the string length is zero
+ // (to simplify the decoder, it doesn't have a special case for len==0).
+ virtual void OnStringData(const char* data, size_t len) = 0;
+
+ // Called after OnStringData has provided all of the encoded bytes of the
+ // string.
+ virtual void OnStringEnd() = 0;
+};
+
+class QUICHE_EXPORT_PRIVATE HpackStringDecoderVLoggingListener
+ : public HpackStringDecoderListener {
+ public:
+ HpackStringDecoderVLoggingListener() : wrapped_(nullptr) {}
+ explicit HpackStringDecoderVLoggingListener(
+ HpackStringDecoderListener* wrapped)
+ : wrapped_(wrapped) {}
+ ~HpackStringDecoderVLoggingListener() override {}
+
+ void OnStringStart(bool huffman_encoded, size_t len) override;
+ void OnStringData(const char* data, size_t len) override;
+ void OnStringEnd() override;
+
+ private:
+ HpackStringDecoderListener* const wrapped_;
+};
+
+} // namespace test
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_STRING_DECODER_LISTENER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_test.cc
new file mode 100644
index 00000000000..cfdb20ab147
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_string_decoder_test.cc
@@ -0,0 +1,154 @@
+// 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 "quiche/http2/hpack/decoder/hpack_string_decoder.h"
+
+// Tests of HpackStringDecoder.
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/decoder/hpack_string_collector.h"
+#include "quiche/http2/hpack/decoder/hpack_string_decoder_listener.h"
+#include "quiche/http2/hpack/tools/hpack_block_builder.h"
+#include "quiche/http2/test_tools/http2_random.h"
+#include "quiche/http2/tools/random_decoder_test.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+namespace http2 {
+namespace test {
+namespace {
+
+const bool kMayReturnZeroOnFirst = false;
+const bool kCompressed = true;
+const bool kUncompressed = false;
+
+class HpackStringDecoderTest : public RandomDecoderTest {
+ protected:
+ HpackStringDecoderTest() : listener_(&collector_) {}
+
+ DecodeStatus StartDecoding(DecodeBuffer* b) override {
+ ++start_decoding_calls_;
+ collector_.Clear();
+ return decoder_.Start(b, &listener_);
+ }
+
+ DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+ // Provides coverage of DebugString and StateToString.
+ // Not validating output.
+ HTTP2_VLOG(1) << decoder_.DebugString();
+ HTTP2_VLOG(2) << collector_;
+ return decoder_.Resume(b, &listener_);
+ }
+
+ AssertionResult Collected(absl::string_view s, bool huffman_encoded) {
+ HTTP2_VLOG(1) << collector_;
+ return collector_.Collected(s, huffman_encoded);
+ }
+
+ // expected_str is a std::string rather than a const std::string& or
+ // absl::string_view so that the lambda makes a copy of the string, and thus
+ // the string to be passed to Collected outlives the call to MakeValidator.
+ Validator MakeValidator(const std::string& expected_str,
+ bool expected_huffman) {
+ return
+ [expected_str, expected_huffman, this](
+ const DecodeBuffer& /*input*/,
+ DecodeStatus /*status*/) -> AssertionResult {
+ AssertionResult result = Collected(expected_str, expected_huffman);
+ if (result) {
+ VERIFY_EQ(collector_,
+ HpackStringCollector(expected_str, expected_huffman));
+ } else {
+ VERIFY_NE(collector_,
+ HpackStringCollector(expected_str, expected_huffman));
+ }
+ HTTP2_VLOG(2) << collector_.ToString();
+ collector_.Clear();
+ HTTP2_VLOG(2) << collector_;
+ return result;
+ };
+ }
+
+ HpackStringDecoder decoder_;
+ HpackStringCollector collector_;
+ HpackStringDecoderVLoggingListener listener_;
+ size_t start_decoding_calls_ = 0;
+};
+
+TEST_F(HpackStringDecoderTest, DecodeEmptyString) {
+ {
+ Validator validator = ValidateDoneAndEmpty(MakeValidator("", kCompressed));
+ const char kData[] = {'\x80'};
+ DecodeBuffer b(kData);
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+ }
+ {
+ // Make sure it stops after decoding the empty string.
+ Validator validator =
+ ValidateDoneAndOffset(1, MakeValidator("", kUncompressed));
+ const char kData[] = {'\x00', '\xff'};
+ DecodeBuffer b(kData);
+ EXPECT_EQ(2u, b.Remaining());
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+ EXPECT_EQ(1u, b.Remaining());
+ }
+}
+
+TEST_F(HpackStringDecoderTest, DecodeShortString) {
+ {
+ // Make sure it stops after decoding the non-empty string.
+ Validator validator =
+ ValidateDoneAndOffset(11, MakeValidator("start end.", kCompressed));
+ const char kData[] = "\x8astart end.Don't peek at this.";
+ DecodeBuffer b(kData);
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+ }
+ {
+ Validator validator =
+ ValidateDoneAndOffset(11, MakeValidator("start end.", kUncompressed));
+ absl::string_view data("\x0astart end.");
+ DecodeBuffer b(data);
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(&b, kMayReturnZeroOnFirst, validator));
+ }
+}
+
+TEST_F(HpackStringDecoderTest, DecodeLongStrings) {
+ std::string name = Random().RandString(1024);
+ std::string value = Random().RandString(65536);
+ HpackBlockBuilder hbb;
+
+ hbb.AppendString(false, name);
+ uint32_t offset_after_name = hbb.size();
+ EXPECT_EQ(3 + name.size(), offset_after_name);
+
+ hbb.AppendString(true, value);
+ uint32_t offset_after_value = hbb.size();
+ EXPECT_EQ(3 + name.size() + 4 + value.size(), offset_after_value);
+
+ DecodeBuffer b(hbb.buffer());
+
+ // Decode the name...
+ EXPECT_TRUE(DecodeAndValidateSeveralWays(
+ &b, kMayReturnZeroOnFirst,
+ ValidateDoneAndOffset(offset_after_name,
+ MakeValidator(name, kUncompressed))));
+ EXPECT_EQ(offset_after_name, b.Offset());
+ EXPECT_EQ(offset_after_value - offset_after_name, b.Remaining());
+
+ // Decode the value...
+ EXPECT_TRUE(DecodeAndValidateSeveralWays(
+ &b, kMayReturnZeroOnFirst,
+ ValidateDoneAndOffset(offset_after_value - offset_after_name,
+ MakeValidator(value, kCompressed))));
+ EXPECT_EQ(offset_after_value, b.Offset());
+ EXPECT_EQ(0u, b.Remaining());
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer.cc
new file mode 100644
index 00000000000..3bb9652a1c9
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer.cc
@@ -0,0 +1,153 @@
+// 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 "quiche/http2/hpack/decoder/hpack_whole_entry_buffer.h"
+
+#include "absl/strings/str_cat.h"
+#include "quiche/http2/platform/api/http2_flag_utils.h"
+#include "quiche/http2/platform/api/http2_flags.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_logging.h"
+#include "quiche/common/quiche_text_utils.h"
+
+namespace http2 {
+
+HpackWholeEntryBuffer::HpackWholeEntryBuffer(HpackWholeEntryListener* listener,
+ size_t max_string_size_bytes)
+ : max_string_size_bytes_(max_string_size_bytes) {
+ set_listener(listener);
+}
+HpackWholeEntryBuffer::~HpackWholeEntryBuffer() = default;
+
+void HpackWholeEntryBuffer::set_listener(HpackWholeEntryListener* listener) {
+ QUICHE_CHECK(listener);
+ listener_ = listener;
+}
+
+void HpackWholeEntryBuffer::set_max_string_size_bytes(
+ size_t max_string_size_bytes) {
+ max_string_size_bytes_ = max_string_size_bytes;
+}
+
+void HpackWholeEntryBuffer::BufferStringsIfUnbuffered() {
+ name_.BufferStringIfUnbuffered();
+ value_.BufferStringIfUnbuffered();
+}
+
+void HpackWholeEntryBuffer::OnIndexedHeader(size_t index) {
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnIndexedHeader: index=" << index;
+ listener_->OnIndexedHeader(index);
+}
+
+void HpackWholeEntryBuffer::OnStartLiteralHeader(HpackEntryType entry_type,
+ size_t maybe_name_index) {
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnStartLiteralHeader: entry_type="
+ << entry_type << ", maybe_name_index=" << maybe_name_index;
+ entry_type_ = entry_type;
+ maybe_name_index_ = maybe_name_index;
+}
+
+void HpackWholeEntryBuffer::OnNameStart(bool huffman_encoded, size_t len) {
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnNameStart: huffman_encoded="
+ << (huffman_encoded ? "true" : "false") << ", len=" << len;
+ QUICHE_DCHECK_EQ(maybe_name_index_, 0u);
+ if (!error_detected_) {
+ if (len > max_string_size_bytes_) {
+ HTTP2_DVLOG(1) << "Name length (" << len << ") is longer than permitted ("
+ << max_string_size_bytes_ << ")";
+ ReportError(HpackDecodingError::kNameTooLong, "");
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 18, 23);
+ return;
+ }
+ name_.OnStart(huffman_encoded, len);
+ }
+}
+
+void HpackWholeEntryBuffer::OnNameData(const char* data, size_t len) {
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnNameData: len=" << len
+ << " data:\n"
+ << quiche::QuicheTextUtils::HexDump(
+ absl::string_view(data, len));
+ QUICHE_DCHECK_EQ(maybe_name_index_, 0u);
+ if (!error_detected_ && !name_.OnData(data, len)) {
+ ReportError(HpackDecodingError::kNameHuffmanError, "");
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 19, 23);
+ }
+}
+
+void HpackWholeEntryBuffer::OnNameEnd() {
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnNameEnd";
+ QUICHE_DCHECK_EQ(maybe_name_index_, 0u);
+ if (!error_detected_ && !name_.OnEnd()) {
+ ReportError(HpackDecodingError::kNameHuffmanError, "");
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 20, 23);
+ }
+}
+
+void HpackWholeEntryBuffer::OnValueStart(bool huffman_encoded, size_t len) {
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnValueStart: huffman_encoded="
+ << (huffman_encoded ? "true" : "false") << ", len=" << len;
+ if (!error_detected_) {
+ if (len > max_string_size_bytes_) {
+ std::string detailed_error = absl::StrCat(
+ "Value length (", len, ") of [", name_.GetStringIfComplete(),
+ "] is longer than permitted (", max_string_size_bytes_, ")");
+ HTTP2_DVLOG(1) << detailed_error;
+ ReportError(HpackDecodingError::kValueTooLong, detailed_error);
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 21, 23);
+ return;
+ }
+ value_.OnStart(huffman_encoded, len);
+ }
+}
+
+void HpackWholeEntryBuffer::OnValueData(const char* data, size_t len) {
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnValueData: len=" << len
+ << " data:\n"
+ << quiche::QuicheTextUtils::HexDump(
+ absl::string_view(data, len));
+ if (!error_detected_ && !value_.OnData(data, len)) {
+ ReportError(HpackDecodingError::kValueHuffmanError, "");
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 22, 23);
+ }
+}
+
+void HpackWholeEntryBuffer::OnValueEnd() {
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnValueEnd";
+ if (error_detected_) {
+ return;
+ }
+ if (!value_.OnEnd()) {
+ ReportError(HpackDecodingError::kValueHuffmanError, "");
+ HTTP2_CODE_COUNT_N(decompress_failure_3, 23, 23);
+ return;
+ }
+ if (maybe_name_index_ == 0) {
+ listener_->OnLiteralNameAndValue(entry_type_, &name_, &value_);
+ name_.Reset();
+ } else {
+ listener_->OnNameIndexAndLiteralValue(entry_type_, maybe_name_index_,
+ &value_);
+ }
+ value_.Reset();
+}
+
+void HpackWholeEntryBuffer::OnDynamicTableSizeUpdate(size_t size) {
+ HTTP2_DVLOG(2) << "HpackWholeEntryBuffer::OnDynamicTableSizeUpdate: size="
+ << size;
+ listener_->OnDynamicTableSizeUpdate(size);
+}
+
+void HpackWholeEntryBuffer::ReportError(HpackDecodingError error,
+ std::string detailed_error) {
+ if (!error_detected_) {
+ HTTP2_DVLOG(1) << "HpackWholeEntryBuffer::ReportError: "
+ << HpackDecodingErrorToString(error);
+ error_detected_ = true;
+ listener_->OnHpackDecodeError(error, detailed_error);
+ listener_ = HpackWholeEntryNoOpListener::NoOpListener();
+ }
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer.h
new file mode 100644
index 00000000000..0cdde65e108
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer.h
@@ -0,0 +1,102 @@
+// 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_WHOLE_ENTRY_BUFFER_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_WHOLE_ENTRY_BUFFER_H_
+
+// HpackWholeEntryBuffer isolates a listener from the fact that an entry may
+// be split across multiple input buffers, providing one callback per entry.
+// HpackWholeEntryBuffer requires that the HpackEntryDecoderListener be made in
+// the correct order, which is tested by hpack_entry_decoder_test.cc.
+
+#include <stddef.h>
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/decoder/hpack_decoder_string_buffer.h"
+#include "quiche/http2/hpack/decoder/hpack_decoding_error.h"
+#include "quiche/http2/hpack/decoder/hpack_entry_decoder_listener.h"
+#include "quiche/http2/hpack/decoder/hpack_whole_entry_listener.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+// TODO(jamessynge): Consider renaming HpackEntryDecoderListener to
+// HpackEntryPartsListener or HpackEntryFragmentsListener.
+class QUICHE_EXPORT_PRIVATE HpackWholeEntryBuffer
+ : public HpackEntryDecoderListener {
+ public:
+ // max_string_size specifies the maximum size of an on-the-wire string (name
+ // or value, plain or Huffman encoded) that will be accepted. See sections
+ // 5.1 and 5.2 of RFC 7541. This is a defense against OOM attacks; HTTP/2
+ // allows a decoder to enforce any limit of the size of the header lists
+ // that it is willing decode, including less than the MAX_HEADER_LIST_SIZE
+ // setting, a setting that is initially unlimited. For example, we might
+ // choose to send a MAX_HEADER_LIST_SIZE of 64KB, and to use that same value
+ // as the upper bound for individual strings.
+ HpackWholeEntryBuffer(HpackWholeEntryListener* listener,
+ size_t max_string_size);
+ ~HpackWholeEntryBuffer() override;
+
+ HpackWholeEntryBuffer(const HpackWholeEntryBuffer&) = delete;
+ HpackWholeEntryBuffer& operator=(const HpackWholeEntryBuffer&) = delete;
+
+ // Set the listener to be notified when a whole entry has been decoded.
+ // The listener may be changed at any time.
+ void set_listener(HpackWholeEntryListener* listener);
+
+ // Set how much encoded data this decoder is willing to buffer.
+ // TODO(jamessynge): Come up with consistent semantics for this protection
+ // across the various decoders; e.g. should it be for a single string or
+ // a single header entry?
+ void set_max_string_size_bytes(size_t max_string_size_bytes);
+
+ // Ensure that decoded strings pointed to by the HpackDecoderStringBuffer
+ // instances name_ and value_ are buffered, which allows any underlying
+ // transport buffer to be freed or reused without overwriting the decoded
+ // strings. This is needed only when an HPACK entry is split across transport
+ // buffers. See HpackDecoder::DecodeFragment.
+ void BufferStringsIfUnbuffered();
+
+ // Was an error detected? After an error has been detected and reported,
+ // no further callbacks will be made to the listener.
+ bool error_detected() const { return error_detected_; }
+
+ // Implement the HpackEntryDecoderListener methods.
+
+ void OnIndexedHeader(size_t index) override;
+ void OnStartLiteralHeader(HpackEntryType entry_type,
+ size_t maybe_name_index) override;
+ void OnNameStart(bool huffman_encoded, size_t len) override;
+ void OnNameData(const char* data, size_t len) override;
+ void OnNameEnd() override;
+ void OnValueStart(bool huffman_encoded, size_t len) override;
+ void OnValueData(const char* data, size_t len) override;
+ void OnValueEnd() override;
+ void OnDynamicTableSizeUpdate(size_t size) override;
+
+ private:
+ void ReportError(HpackDecodingError error, std::string detailed_error);
+
+ HpackWholeEntryListener* listener_;
+ HpackDecoderStringBuffer name_, value_;
+
+ // max_string_size_bytes_ specifies the maximum allowed size of an on-the-wire
+ // string. Larger strings will be reported as errors to the listener; the
+ // endpoint should treat these as COMPRESSION errors, which are CONNECTION
+ // level errors.
+ size_t max_string_size_bytes_;
+
+ // The name index (or zero) of the current header entry with a literal value.
+ size_t maybe_name_index_;
+
+ // The type of the current header entry (with literals) that is being decoded.
+ HpackEntryType entry_type_;
+
+ bool error_detected_ = false;
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_WHOLE_ENTRY_BUFFER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc
new file mode 100644
index 00000000000..5c8ba808ac1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_buffer_test.cc
@@ -0,0 +1,231 @@
+// 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 "quiche/http2/hpack/decoder/hpack_whole_entry_buffer.h"
+
+// Tests of HpackWholeEntryBuffer: does it buffer correctly, and does it
+// detect Huffman decoding errors and oversize string errors?
+
+#include "quiche/common/platform/api/quiche_test.h"
+
+using ::testing::_;
+using ::testing::AllOf;
+using ::testing::InSequence;
+using ::testing::Property;
+using ::testing::StrictMock;
+
+namespace http2 {
+namespace test {
+namespace {
+
+constexpr size_t kMaxStringSize = 20;
+
+class MockHpackWholeEntryListener : public HpackWholeEntryListener {
+ public:
+ ~MockHpackWholeEntryListener() override = default;
+
+ MOCK_METHOD(void, OnIndexedHeader, (size_t index), (override));
+ MOCK_METHOD(void,
+ OnNameIndexAndLiteralValue,
+ (HpackEntryType entry_type,
+ size_t name_index,
+ HpackDecoderStringBuffer* value_buffer),
+ (override));
+ MOCK_METHOD(void,
+ OnLiteralNameAndValue,
+ (HpackEntryType entry_type,
+ HpackDecoderStringBuffer* name_buffer,
+ HpackDecoderStringBuffer* value_buffer),
+ (override));
+ MOCK_METHOD(void, OnDynamicTableSizeUpdate, (size_t size), (override));
+ MOCK_METHOD(void,
+ OnHpackDecodeError,
+ (HpackDecodingError error, std::string detailed_error),
+ (override));
+};
+
+class HpackWholeEntryBufferTest : public QuicheTest {
+ protected:
+ HpackWholeEntryBufferTest() : entry_buffer_(&listener_, kMaxStringSize) {}
+ ~HpackWholeEntryBufferTest() override = default;
+
+ StrictMock<MockHpackWholeEntryListener> listener_;
+ HpackWholeEntryBuffer entry_buffer_;
+};
+
+// OnIndexedHeader is an immediate pass through.
+TEST_F(HpackWholeEntryBufferTest, OnIndexedHeader) {
+ {
+ InSequence seq;
+ EXPECT_CALL(listener_, OnIndexedHeader(17));
+ entry_buffer_.OnIndexedHeader(17);
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(listener_, OnIndexedHeader(62));
+ entry_buffer_.OnIndexedHeader(62);
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(listener_, OnIndexedHeader(62));
+ entry_buffer_.OnIndexedHeader(62);
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(listener_, OnIndexedHeader(128));
+ entry_buffer_.OnIndexedHeader(128);
+ }
+ StrictMock<MockHpackWholeEntryListener> listener2;
+ entry_buffer_.set_listener(&listener2);
+ {
+ InSequence seq;
+ EXPECT_CALL(listener2, OnIndexedHeader(100));
+ entry_buffer_.OnIndexedHeader(100);
+ }
+}
+
+// OnDynamicTableSizeUpdate is an immediate pass through.
+TEST_F(HpackWholeEntryBufferTest, OnDynamicTableSizeUpdate) {
+ {
+ InSequence seq;
+ EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(4096));
+ entry_buffer_.OnDynamicTableSizeUpdate(4096);
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(0));
+ entry_buffer_.OnDynamicTableSizeUpdate(0);
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(1024));
+ entry_buffer_.OnDynamicTableSizeUpdate(1024);
+ }
+ {
+ InSequence seq;
+ EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(1024));
+ entry_buffer_.OnDynamicTableSizeUpdate(1024);
+ }
+ StrictMock<MockHpackWholeEntryListener> listener2;
+ entry_buffer_.set_listener(&listener2);
+ {
+ InSequence seq;
+ EXPECT_CALL(listener2, OnDynamicTableSizeUpdate(0));
+ entry_buffer_.OnDynamicTableSizeUpdate(0);
+ }
+}
+
+TEST_F(HpackWholeEntryBufferTest, OnNameIndexAndLiteralValue) {
+ entry_buffer_.OnStartLiteralHeader(HpackEntryType::kNeverIndexedLiteralHeader,
+ 123);
+ entry_buffer_.OnValueStart(false, 10);
+ entry_buffer_.OnValueData("some data.", 10);
+
+ // Force the value to be buffered.
+ entry_buffer_.BufferStringsIfUnbuffered();
+
+ EXPECT_CALL(
+ listener_,
+ OnNameIndexAndLiteralValue(
+ HpackEntryType::kNeverIndexedLiteralHeader, 123,
+ AllOf(Property(&HpackDecoderStringBuffer::str, "some data."),
+ Property(&HpackDecoderStringBuffer::BufferedLength, 10))));
+
+ entry_buffer_.OnValueEnd();
+}
+
+TEST_F(HpackWholeEntryBufferTest, OnLiteralNameAndValue) {
+ entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 0);
+ // Force the name to be buffered by delivering it in two pieces.
+ entry_buffer_.OnNameStart(false, 9);
+ entry_buffer_.OnNameData("some-", 5);
+ entry_buffer_.OnNameData("name", 4);
+ entry_buffer_.OnNameEnd();
+ entry_buffer_.OnValueStart(false, 12);
+ entry_buffer_.OnValueData("Header Value", 12);
+
+ EXPECT_CALL(
+ listener_,
+ OnLiteralNameAndValue(
+ HpackEntryType::kIndexedLiteralHeader,
+ AllOf(Property(&HpackDecoderStringBuffer::str, "some-name"),
+ Property(&HpackDecoderStringBuffer::BufferedLength, 9)),
+ AllOf(Property(&HpackDecoderStringBuffer::str, "Header Value"),
+ Property(&HpackDecoderStringBuffer::BufferedLength, 0))));
+
+ entry_buffer_.OnValueEnd();
+}
+
+// Verify that a name longer than the allowed size generates an error.
+TEST_F(HpackWholeEntryBufferTest, NameTooLong) {
+ entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 0);
+ EXPECT_CALL(listener_,
+ OnHpackDecodeError(HpackDecodingError::kNameTooLong, _));
+ entry_buffer_.OnNameStart(false, kMaxStringSize + 1);
+}
+
+// Verify that a value longer than the allowed size generates an error.
+TEST_F(HpackWholeEntryBufferTest, ValueTooLong) {
+ entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 0);
+ EXPECT_CALL(listener_,
+ OnHpackDecodeError(
+ HpackDecodingError::kValueTooLong,
+ "Value length (21) of [path] is longer than permitted (20)"));
+ entry_buffer_.OnNameStart(false, 4);
+ entry_buffer_.OnNameData("path", 4);
+ entry_buffer_.OnNameEnd();
+ entry_buffer_.OnValueStart(false, kMaxStringSize + 1);
+}
+
+// Regression test for b/162141899.
+TEST_F(HpackWholeEntryBufferTest, ValueTooLongWithoutName) {
+ entry_buffer_.OnStartLiteralHeader(HpackEntryType::kIndexedLiteralHeader, 1);
+ EXPECT_CALL(listener_,
+ OnHpackDecodeError(
+ HpackDecodingError::kValueTooLong,
+ "Value length (21) of [] is longer than permitted (20)"));
+ entry_buffer_.OnValueStart(false, kMaxStringSize + 1);
+}
+
+// Verify that a Huffman encoded name with an explicit EOS generates an error
+// for an explicit EOS.
+TEST_F(HpackWholeEntryBufferTest, NameHuffmanError) {
+ const char data[] = "\xff\xff\xff";
+ entry_buffer_.OnStartLiteralHeader(HpackEntryType::kUnindexedLiteralHeader,
+ 0);
+ entry_buffer_.OnNameStart(true, 4);
+ entry_buffer_.OnNameData(data, 3);
+
+ EXPECT_CALL(listener_,
+ OnHpackDecodeError(HpackDecodingError::kNameHuffmanError, _));
+
+ entry_buffer_.OnNameData(data, 1);
+
+ // After an error is reported, the listener is not called again.
+ EXPECT_CALL(listener_, OnDynamicTableSizeUpdate(8096)).Times(0);
+ entry_buffer_.OnDynamicTableSizeUpdate(8096);
+}
+
+// Verify that a Huffman encoded value that isn't properly terminated with
+// a partial EOS symbol generates an error.
+TEST_F(HpackWholeEntryBufferTest, ValueHuffmanError) {
+ const char data[] = "\x00\x00\x00";
+ entry_buffer_.OnStartLiteralHeader(HpackEntryType::kNeverIndexedLiteralHeader,
+ 61);
+ entry_buffer_.OnValueStart(true, 3);
+ entry_buffer_.OnValueData(data, 3);
+
+ EXPECT_CALL(listener_,
+ OnHpackDecodeError(HpackDecodingError::kValueHuffmanError, _));
+
+ entry_buffer_.OnValueEnd();
+
+ // After an error is reported, the listener is not called again.
+ EXPECT_CALL(listener_, OnIndexedHeader(17)).Times(0);
+ entry_buffer_.OnIndexedHeader(17);
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_listener.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_listener.cc
new file mode 100644
index 00000000000..cafd6ecbe84
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_listener.cc
@@ -0,0 +1,34 @@
+// 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 "quiche/http2/hpack/decoder/hpack_whole_entry_listener.h"
+
+namespace http2 {
+
+HpackWholeEntryListener::~HpackWholeEntryListener() = default;
+
+HpackWholeEntryNoOpListener::~HpackWholeEntryNoOpListener() = default;
+
+void HpackWholeEntryNoOpListener::OnIndexedHeader(size_t /*index*/) {}
+void HpackWholeEntryNoOpListener::OnNameIndexAndLiteralValue(
+ HpackEntryType /*entry_type*/,
+ size_t /*name_index*/,
+ HpackDecoderStringBuffer* /*value_buffer*/) {}
+void HpackWholeEntryNoOpListener::OnLiteralNameAndValue(
+ HpackEntryType /*entry_type*/,
+ HpackDecoderStringBuffer* /*name_buffer*/,
+ HpackDecoderStringBuffer* /*value_buffer*/) {}
+void HpackWholeEntryNoOpListener::OnDynamicTableSizeUpdate(size_t /*size*/) {}
+void HpackWholeEntryNoOpListener::OnHpackDecodeError(
+ HpackDecodingError /*error*/,
+ std::string /*detailed_error*/) {}
+
+// static
+HpackWholeEntryNoOpListener* HpackWholeEntryNoOpListener::NoOpListener() {
+ static HpackWholeEntryNoOpListener* static_instance =
+ new HpackWholeEntryNoOpListener();
+ return static_instance;
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_listener.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_listener.h
new file mode 100644
index 00000000000..90c6f9d6da4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/decoder/hpack_whole_entry_listener.h
@@ -0,0 +1,83 @@
+// 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.
+
+// Defines HpackWholeEntryListener, the base class of listeners for decoded
+// complete HPACK entries, as opposed to HpackEntryDecoderListener which
+// receives multiple callbacks for some single entries.
+
+#ifndef QUICHE_HTTP2_HPACK_DECODER_HPACK_WHOLE_ENTRY_LISTENER_H_
+#define QUICHE_HTTP2_HPACK_DECODER_HPACK_WHOLE_ENTRY_LISTENER_H_
+
+#include <stddef.h>
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/decoder/hpack_decoder_string_buffer.h"
+#include "quiche/http2/hpack/decoder/hpack_decoding_error.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+class QUICHE_EXPORT_PRIVATE HpackWholeEntryListener {
+ public:
+ virtual ~HpackWholeEntryListener();
+
+ // Called when an indexed header (i.e. one in the static or dynamic table) has
+ // been decoded from an HPACK block. index is supposed to be non-zero, but
+ // that has not been checked by the caller.
+ virtual void OnIndexedHeader(size_t index) = 0;
+
+ // Called when a header entry with a name index and literal value has
+ // been fully decoded from an HPACK block. name_index is NOT zero.
+ // entry_type will be kIndexedLiteralHeader, kUnindexedLiteralHeader, or
+ // kNeverIndexedLiteralHeader.
+ virtual void OnNameIndexAndLiteralValue(
+ HpackEntryType entry_type,
+ size_t name_index,
+ HpackDecoderStringBuffer* value_buffer) = 0;
+
+ // Called when a header entry with a literal name and literal value
+ // has been fully decoded from an HPACK block. entry_type will be
+ // kIndexedLiteralHeader, kUnindexedLiteralHeader, or
+ // kNeverIndexedLiteralHeader.
+ virtual void OnLiteralNameAndValue(
+ HpackEntryType entry_type,
+ HpackDecoderStringBuffer* name_buffer,
+ HpackDecoderStringBuffer* value_buffer) = 0;
+
+ // Called when an update to the size of the peer's dynamic table has been
+ // decoded.
+ virtual void OnDynamicTableSizeUpdate(size_t size) = 0;
+
+ // OnHpackDecodeError is called if an error is detected while decoding.
+ virtual void OnHpackDecodeError(HpackDecodingError error,
+ std::string detailed_error) = 0;
+};
+
+// A no-op implementation of HpackWholeEntryDecoderListener, useful for ignoring
+// callbacks once an error is detected.
+class QUICHE_EXPORT_PRIVATE HpackWholeEntryNoOpListener
+ : public HpackWholeEntryListener {
+ public:
+ ~HpackWholeEntryNoOpListener() override;
+
+ void OnIndexedHeader(size_t index) override;
+ void OnNameIndexAndLiteralValue(
+ HpackEntryType entry_type,
+ size_t name_index,
+ HpackDecoderStringBuffer* value_buffer) override;
+ void OnLiteralNameAndValue(HpackEntryType entry_type,
+ HpackDecoderStringBuffer* name_buffer,
+ HpackDecoderStringBuffer* value_buffer) override;
+ void OnDynamicTableSizeUpdate(size_t size) override;
+ void OnHpackDecodeError(HpackDecodingError error,
+ std::string detailed_error) override;
+
+ // Returns a listener that ignores all the calls.
+ static HpackWholeEntryNoOpListener* NoOpListener();
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_DECODER_HPACK_WHOLE_ENTRY_LISTENER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/hpack_static_table_entries.inc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/hpack_static_table_entries.inc
new file mode 100644
index 00000000000..c6ae125f3b2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/hpack_static_table_entries.inc
@@ -0,0 +1,65 @@
+// This file is designed to be included by C/C++ files which need the contents
+// of the HPACK static table. It may be included more than once if necessary.
+// See http://httpwg.org/specs/rfc7541.html#static.table.definition
+
+STATIC_TABLE_ENTRY(":authority", "", 1);
+STATIC_TABLE_ENTRY(":method", "GET", 2);
+STATIC_TABLE_ENTRY(":method", "POST", 3);
+STATIC_TABLE_ENTRY(":path", "/", 4);
+STATIC_TABLE_ENTRY(":path", "/index.html", 5);
+STATIC_TABLE_ENTRY(":scheme", "http", 6);
+STATIC_TABLE_ENTRY(":scheme", "https", 7);
+STATIC_TABLE_ENTRY(":status", "200", 8);
+STATIC_TABLE_ENTRY(":status", "204", 9);
+STATIC_TABLE_ENTRY(":status", "206", 10);
+STATIC_TABLE_ENTRY(":status", "304", 11);
+STATIC_TABLE_ENTRY(":status", "400", 12);
+STATIC_TABLE_ENTRY(":status", "404", 13);
+STATIC_TABLE_ENTRY(":status", "500", 14);
+STATIC_TABLE_ENTRY("accept-charset", "", 15);
+STATIC_TABLE_ENTRY("accept-encoding", "gzip, deflate", 16);
+STATIC_TABLE_ENTRY("accept-language", "", 17);
+STATIC_TABLE_ENTRY("accept-ranges", "", 18);
+STATIC_TABLE_ENTRY("accept", "", 19);
+STATIC_TABLE_ENTRY("access-control-allow-origin", "", 20);
+STATIC_TABLE_ENTRY("age", "", 21);
+STATIC_TABLE_ENTRY("allow", "", 22);
+STATIC_TABLE_ENTRY("authorization", "", 23);
+STATIC_TABLE_ENTRY("cache-control", "", 24);
+STATIC_TABLE_ENTRY("content-disposition", "", 25);
+STATIC_TABLE_ENTRY("content-encoding", "", 26);
+STATIC_TABLE_ENTRY("content-language", "", 27);
+STATIC_TABLE_ENTRY("content-length", "", 28);
+STATIC_TABLE_ENTRY("content-location", "", 29);
+STATIC_TABLE_ENTRY("content-range", "", 30);
+STATIC_TABLE_ENTRY("content-type", "", 31);
+STATIC_TABLE_ENTRY("cookie", "", 32);
+STATIC_TABLE_ENTRY("date", "", 33);
+STATIC_TABLE_ENTRY("etag", "", 34);
+STATIC_TABLE_ENTRY("expect", "", 35);
+STATIC_TABLE_ENTRY("expires", "", 36);
+STATIC_TABLE_ENTRY("from", "", 37);
+STATIC_TABLE_ENTRY("host", "", 38);
+STATIC_TABLE_ENTRY("if-match", "", 39);
+STATIC_TABLE_ENTRY("if-modified-since", "", 40);
+STATIC_TABLE_ENTRY("if-none-match", "", 41);
+STATIC_TABLE_ENTRY("if-range", "", 42);
+STATIC_TABLE_ENTRY("if-unmodified-since", "", 43);
+STATIC_TABLE_ENTRY("last-modified", "", 44);
+STATIC_TABLE_ENTRY("link", "", 45);
+STATIC_TABLE_ENTRY("location", "", 46);
+STATIC_TABLE_ENTRY("max-forwards", "", 47);
+STATIC_TABLE_ENTRY("proxy-authenticate", "", 48);
+STATIC_TABLE_ENTRY("proxy-authorization", "", 49);
+STATIC_TABLE_ENTRY("range", "", 50);
+STATIC_TABLE_ENTRY("referer", "", 51);
+STATIC_TABLE_ENTRY("refresh", "", 52);
+STATIC_TABLE_ENTRY("retry-after", "", 53);
+STATIC_TABLE_ENTRY("server", "", 54);
+STATIC_TABLE_ENTRY("set-cookie", "", 55);
+STATIC_TABLE_ENTRY("strict-transport-security", "", 56);
+STATIC_TABLE_ENTRY("transfer-encoding", "", 57);
+STATIC_TABLE_ENTRY("user-agent", "", 58);
+STATIC_TABLE_ENTRY("vary", "", 59);
+STATIC_TABLE_ENTRY("via", "", 60);
+STATIC_TABLE_ENTRY("www-authenticate", "", 61);
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants.cc
new file mode 100644
index 00000000000..e4a71b8fe9f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants.cc
@@ -0,0 +1,31 @@
+// 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 "quiche/http2/hpack/http2_hpack_constants.h"
+
+#include "absl/strings/str_cat.h"
+
+namespace http2 {
+
+std::string HpackEntryTypeToString(HpackEntryType v) {
+ switch (v) {
+ case HpackEntryType::kIndexedHeader:
+ return "kIndexedHeader";
+ case HpackEntryType::kDynamicTableSizeUpdate:
+ return "kDynamicTableSizeUpdate";
+ case HpackEntryType::kIndexedLiteralHeader:
+ return "kIndexedLiteralHeader";
+ case HpackEntryType::kUnindexedLiteralHeader:
+ return "kUnindexedLiteralHeader";
+ case HpackEntryType::kNeverIndexedLiteralHeader:
+ return "kNeverIndexedLiteralHeader";
+ }
+ return absl::StrCat("UnknownHpackEntryType(", static_cast<int>(v), ")");
+}
+
+std::ostream& operator<<(std::ostream& out, HpackEntryType v) {
+ return out << HpackEntryTypeToString(v);
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants.h
new file mode 100644
index 00000000000..a10b140bb70
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants.h
@@ -0,0 +1,63 @@
+// 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_HTTP2_HPACK_CONSTANTS_H_
+#define QUICHE_HTTP2_HPACK_HTTP2_HPACK_CONSTANTS_H_
+
+// Enum HpackEntryType identifies the 5 basic types of HPACK Block Entries.
+//
+// See the spec for details:
+// https://http2.github.io/http2-spec/compression.html#rfc.section.6
+
+#include <ostream>
+#include <string>
+
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+const size_t kFirstDynamicTableIndex = 62;
+
+enum class HpackEntryType {
+ // Entry is an index into the static or dynamic table. Decoding it has no
+ // effect on the dynamic table.
+ kIndexedHeader,
+
+ // The entry contains a literal value. The name may be either a literal or a
+ // reference to an entry in the static or dynamic table.
+ // The entry is added to the dynamic table after decoding.
+ kIndexedLiteralHeader,
+
+ // The entry contains a literal value. The name may be either a literal or a
+ // reference to an entry in the static or dynamic table.
+ // The entry is not added to the dynamic table after decoding, but a proxy
+ // may choose to insert the entry into its dynamic table when forwarding
+ // to another endpoint.
+ kUnindexedLiteralHeader,
+
+ // The entry contains a literal value. The name may be either a literal or a
+ // reference to an entry in the static or dynamic table.
+ // The entry is not added to the dynamic table after decoding, and a proxy
+ // must NOT insert the entry into its dynamic table when forwarding to another
+ // endpoint.
+ kNeverIndexedLiteralHeader,
+
+ // Entry conveys the size limit of the dynamic table of the encoder to
+ // the decoder. May be used to flush the table by sending a zero and then
+ // resetting the size back up to the maximum that the encoder will use
+ // (within the limits of SETTINGS_HEADER_TABLE_SIZE sent by the
+ // decoder to the encoder, with the default of 4096 assumed).
+ kDynamicTableSizeUpdate,
+};
+
+// Returns the name of the enum member.
+QUICHE_EXPORT_PRIVATE std::string HpackEntryTypeToString(HpackEntryType v);
+
+// Inserts the name of the enum member into |out|.
+QUICHE_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& out,
+ HpackEntryType v);
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_HTTP2_HPACK_CONSTANTS_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants_test.cc
new file mode 100644
index 00000000000..61b465f2fe4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/http2_hpack_constants_test.cc
@@ -0,0 +1,66 @@
+// 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 "quiche/http2/hpack/http2_hpack_constants.h"
+
+#include <sstream>
+
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace test {
+namespace {
+
+TEST(HpackEntryTypeTest, HpackEntryTypeToString) {
+ EXPECT_EQ("kIndexedHeader",
+ HpackEntryTypeToString(HpackEntryType::kIndexedHeader));
+ EXPECT_EQ("kDynamicTableSizeUpdate",
+ HpackEntryTypeToString(HpackEntryType::kDynamicTableSizeUpdate));
+ EXPECT_EQ("kIndexedLiteralHeader",
+ HpackEntryTypeToString(HpackEntryType::kIndexedLiteralHeader));
+ EXPECT_EQ("kUnindexedLiteralHeader",
+ HpackEntryTypeToString(HpackEntryType::kUnindexedLiteralHeader));
+ EXPECT_EQ("kNeverIndexedLiteralHeader",
+ HpackEntryTypeToString(HpackEntryType::kNeverIndexedLiteralHeader));
+ EXPECT_EQ("UnknownHpackEntryType(12321)",
+ HpackEntryTypeToString(static_cast<HpackEntryType>(12321)));
+}
+
+TEST(HpackEntryTypeTest, OutputHpackEntryType) {
+ {
+ std::stringstream log;
+ log << HpackEntryType::kIndexedHeader;
+ EXPECT_EQ("kIndexedHeader", log.str());
+ }
+ {
+ std::stringstream log;
+ log << HpackEntryType::kDynamicTableSizeUpdate;
+ EXPECT_EQ("kDynamicTableSizeUpdate", log.str());
+ }
+ {
+ std::stringstream log;
+ log << HpackEntryType::kIndexedLiteralHeader;
+ EXPECT_EQ("kIndexedLiteralHeader", log.str());
+ }
+ {
+ std::stringstream log;
+ log << HpackEntryType::kUnindexedLiteralHeader;
+ EXPECT_EQ("kUnindexedLiteralHeader", log.str());
+ }
+ {
+ std::stringstream log;
+ log << HpackEntryType::kNeverIndexedLiteralHeader;
+ EXPECT_EQ("kNeverIndexedLiteralHeader", log.str());
+ }
+ {
+ std::stringstream log;
+ log << static_cast<HpackEntryType>(1234321);
+ EXPECT_EQ("UnknownHpackEntryType(1234321)", log.str());
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder.cc
new file mode 100644
index 00000000000..6a026a4b08a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder.cc
@@ -0,0 +1,485 @@
+// 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 "quiche/http2/hpack/huffman/hpack_huffman_decoder.h"
+
+#include <bitset>
+#include <limits>
+
+#include "quiche/http2/platform/api/http2_logging.h"
+
+// Terminology:
+//
+// Symbol - a plain text (unencoded) character (uint8), or the End-of-String
+// (EOS) symbol, 256.
+//
+// Code - the sequence of bits used to encode a symbol, varying in length from
+// 5 bits for the most common symbols (e.g. '0', '1', and 'a'), to
+// 30 bits for the least common (e.g. the EOS symbol).
+// For those symbols whose codes have the same length, their code values
+// are sorted such that the lower symbol value has a lower code value.
+//
+// Canonical - a symbol's cardinal value when sorted first by code length, and
+// then by symbol value. For example, canonical 0 is for ASCII '0'
+// (uint8 value 0x30), which is the first of the symbols whose code
+// is 5 bits long, and the last canonical is EOS, which is the last
+// of the symbols whose code is 30 bits long.
+
+namespace http2 {
+namespace {
+
+// HuffmanCode is used to store the codes associated with symbols (a pattern of
+// from 5 to 30 bits).
+typedef uint32_t HuffmanCode;
+
+// HuffmanCodeBitCount is used to store a count of bits in a code.
+typedef uint16_t HuffmanCodeBitCount;
+
+// HuffmanCodeBitSet is used for producing a string version of a code because
+// std::bitset logs nicely.
+typedef std::bitset<32> HuffmanCodeBitSet;
+typedef std::bitset<64> HuffmanAccumulatorBitSet;
+
+static constexpr HuffmanCodeBitCount kMinCodeBitCount = 5;
+static constexpr HuffmanCodeBitCount kMaxCodeBitCount = 30;
+static constexpr HuffmanCodeBitCount kHuffmanCodeBitCount =
+ std::numeric_limits<HuffmanCode>::digits;
+
+static_assert(std::numeric_limits<HuffmanCode>::digits >= kMaxCodeBitCount,
+ "HuffmanCode isn't big enough.");
+
+static_assert(std::numeric_limits<HuffmanAccumulator>::digits >=
+ kMaxCodeBitCount,
+ "HuffmanAccumulator isn't big enough.");
+
+static constexpr HuffmanAccumulatorBitCount kHuffmanAccumulatorBitCount =
+ std::numeric_limits<HuffmanAccumulator>::digits;
+static constexpr HuffmanAccumulatorBitCount kExtraAccumulatorBitCount =
+ kHuffmanAccumulatorBitCount - kHuffmanCodeBitCount;
+
+// PrefixInfo holds info about a group of codes that are all of the same length.
+struct PrefixInfo {
+ // Given the leading bits (32 in this case) of the encoded string, and that
+ // they start with a code of length |code_length|, return the corresponding
+ // canonical for that leading code.
+ uint32_t DecodeToCanonical(HuffmanCode bits) const {
+ // What is the position of the canonical symbol being decoded within
+ // the canonical symbols of |length|?
+ HuffmanCode ordinal_in_length =
+ ((bits - first_code) >> (kHuffmanCodeBitCount - code_length));
+
+ // Combined with |canonical| to produce the position of the canonical symbol
+ // being decoded within all of the canonical symbols.
+ return first_canonical + ordinal_in_length;
+ }
+
+ const HuffmanCode first_code; // First code of this length, left justified in
+ // the field (i.e. the first bit of the code is
+ // the high-order bit).
+ const uint16_t code_length; // Length of the prefix code |base|.
+ const uint16_t first_canonical; // First canonical symbol of this length.
+};
+
+inline std::ostream& operator<<(std::ostream& out, const PrefixInfo& v) {
+ return out << "{first_code: " << HuffmanCodeBitSet(v.first_code)
+ << ", code_length: " << v.code_length
+ << ", first_canonical: " << v.first_canonical << "}";
+}
+
+// Given |value|, a sequence of the leading bits remaining to be decoded,
+// figure out which group of canonicals (by code length) that value starts
+// with. This function was generated.
+PrefixInfo PrefixToInfo(HuffmanCode value) {
+ if (value < 0b10111000000000000000000000000000) {
+ if (value < 0b01010000000000000000000000000000) {
+ return {0b00000000000000000000000000000000, 5, 0};
+ } else {
+ return {0b01010000000000000000000000000000, 6, 10};
+ }
+ } else {
+ if (value < 0b11111110000000000000000000000000) {
+ if (value < 0b11111000000000000000000000000000) {
+ return {0b10111000000000000000000000000000, 7, 36};
+ } else {
+ return {0b11111000000000000000000000000000, 8, 68};
+ }
+ } else {
+ if (value < 0b11111111110000000000000000000000) {
+ if (value < 0b11111111101000000000000000000000) {
+ if (value < 0b11111111010000000000000000000000) {
+ return {0b11111110000000000000000000000000, 10, 74};
+ } else {
+ return {0b11111111010000000000000000000000, 11, 79};
+ }
+ } else {
+ return {0b11111111101000000000000000000000, 12, 82};
+ }
+ } else {
+ if (value < 0b11111111111111100000000000000000) {
+ if (value < 0b11111111111110000000000000000000) {
+ if (value < 0b11111111111100000000000000000000) {
+ return {0b11111111110000000000000000000000, 13, 84};
+ } else {
+ return {0b11111111111100000000000000000000, 14, 90};
+ }
+ } else {
+ return {0b11111111111110000000000000000000, 15, 92};
+ }
+ } else {
+ if (value < 0b11111111111111110100100000000000) {
+ if (value < 0b11111111111111101110000000000000) {
+ if (value < 0b11111111111111100110000000000000) {
+ return {0b11111111111111100000000000000000, 19, 95};
+ } else {
+ return {0b11111111111111100110000000000000, 20, 98};
+ }
+ } else {
+ return {0b11111111111111101110000000000000, 21, 106};
+ }
+ } else {
+ if (value < 0b11111111111111111110101000000000) {
+ if (value < 0b11111111111111111011000000000000) {
+ return {0b11111111111111110100100000000000, 22, 119};
+ } else {
+ return {0b11111111111111111011000000000000, 23, 145};
+ }
+ } else {
+ if (value < 0b11111111111111111111101111000000) {
+ if (value < 0b11111111111111111111100000000000) {
+ if (value < 0b11111111111111111111011000000000) {
+ return {0b11111111111111111110101000000000, 24, 174};
+ } else {
+ return {0b11111111111111111111011000000000, 25, 186};
+ }
+ } else {
+ return {0b11111111111111111111100000000000, 26, 190};
+ }
+ } else {
+ if (value < 0b11111111111111111111111111110000) {
+ if (value < 0b11111111111111111111111000100000) {
+ return {0b11111111111111111111101111000000, 27, 205};
+ } else {
+ return {0b11111111111111111111111000100000, 28, 224};
+ }
+ } else {
+ return {0b11111111111111111111111111110000, 30, 253};
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+// Mapping from canonical symbol (0 to 255) to actual symbol.
+// clang-format off
+constexpr unsigned char kCanonicalToSymbol[] = {
+ '0', '1', '2', 'a', 'c', 'e', 'i', 'o',
+ 's', 't', 0x20, '%', '-', '.', '/', '3',
+ '4', '5', '6', '7', '8', '9', '=', 'A',
+ '_', 'b', 'd', 'f', 'g', 'h', 'l', 'm',
+ 'n', 'p', 'r', 'u', ':', 'B', 'C', 'D',
+ 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L',
+ 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
+ 'U', 'V', 'W', 'Y', 'j', 'k', 'q', 'v',
+ 'w', 'x', 'y', 'z', '&', '*', ',', ';',
+ 'X', 'Z', '!', '\"', '(', ')', '?', '\'',
+ '+', '|', '#', '>', 0x00, '$', '@', '[',
+ ']', '~', '^', '}', '<', '`', '{', '\\',
+ 0xc3, 0xd0, 0x80, 0x82, 0x83, 0xa2, 0xb8, 0xc2,
+ 0xe0, 0xe2, 0x99, 0xa1, 0xa7, 0xac, 0xb0, 0xb1,
+ 0xb3, 0xd1, 0xd8, 0xd9, 0xe3, 0xe5, 0xe6, 0x81,
+ 0x84, 0x85, 0x86, 0x88, 0x92, 0x9a, 0x9c, 0xa0,
+ 0xa3, 0xa4, 0xa9, 0xaa, 0xad, 0xb2, 0xb5, 0xb9,
+ 0xba, 0xbb, 0xbd, 0xbe, 0xc4, 0xc6, 0xe4, 0xe8,
+ 0xe9, 0x01, 0x87, 0x89, 0x8a, 0x8b, 0x8c, 0x8d,
+ 0x8f, 0x93, 0x95, 0x96, 0x97, 0x98, 0x9b, 0x9d,
+ 0x9e, 0xa5, 0xa6, 0xa8, 0xae, 0xaf, 0xb4, 0xb6,
+ 0xb7, 0xbc, 0xbf, 0xc5, 0xe7, 0xef, 0x09, 0x8e,
+ 0x90, 0x91, 0x94, 0x9f, 0xab, 0xce, 0xd7, 0xe1,
+ 0xec, 0xed, 0xc7, 0xcf, 0xea, 0xeb, 0xc0, 0xc1,
+ 0xc8, 0xc9, 0xca, 0xcd, 0xd2, 0xd5, 0xda, 0xdb,
+ 0xee, 0xf0, 0xf2, 0xf3, 0xff, 0xcb, 0xcc, 0xd3,
+ 0xd4, 0xd6, 0xdd, 0xde, 0xdf, 0xf1, 0xf4, 0xf5,
+ 0xf6, 0xf7, 0xf8, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe,
+ 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x0b,
+ 0x0c, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d,
+ 0x1e, 0x1f, 0x7f, 0xdc, 0xf9, 0x0a, 0x0d, 0x16,
+};
+// clang-format on
+
+constexpr size_t kShortCodeTableSize = 124;
+struct ShortCodeInfo {
+ uint8_t symbol;
+ uint8_t length;
+} kShortCodeTable[kShortCodeTableSize] = {
+ {0x30, 5}, // Match: 0b0000000, Symbol: 0
+ {0x30, 5}, // Match: 0b0000001, Symbol: 0
+ {0x30, 5}, // Match: 0b0000010, Symbol: 0
+ {0x30, 5}, // Match: 0b0000011, Symbol: 0
+ {0x31, 5}, // Match: 0b0000100, Symbol: 1
+ {0x31, 5}, // Match: 0b0000101, Symbol: 1
+ {0x31, 5}, // Match: 0b0000110, Symbol: 1
+ {0x31, 5}, // Match: 0b0000111, Symbol: 1
+ {0x32, 5}, // Match: 0b0001000, Symbol: 2
+ {0x32, 5}, // Match: 0b0001001, Symbol: 2
+ {0x32, 5}, // Match: 0b0001010, Symbol: 2
+ {0x32, 5}, // Match: 0b0001011, Symbol: 2
+ {0x61, 5}, // Match: 0b0001100, Symbol: a
+ {0x61, 5}, // Match: 0b0001101, Symbol: a
+ {0x61, 5}, // Match: 0b0001110, Symbol: a
+ {0x61, 5}, // Match: 0b0001111, Symbol: a
+ {0x63, 5}, // Match: 0b0010000, Symbol: c
+ {0x63, 5}, // Match: 0b0010001, Symbol: c
+ {0x63, 5}, // Match: 0b0010010, Symbol: c
+ {0x63, 5}, // Match: 0b0010011, Symbol: c
+ {0x65, 5}, // Match: 0b0010100, Symbol: e
+ {0x65, 5}, // Match: 0b0010101, Symbol: e
+ {0x65, 5}, // Match: 0b0010110, Symbol: e
+ {0x65, 5}, // Match: 0b0010111, Symbol: e
+ {0x69, 5}, // Match: 0b0011000, Symbol: i
+ {0x69, 5}, // Match: 0b0011001, Symbol: i
+ {0x69, 5}, // Match: 0b0011010, Symbol: i
+ {0x69, 5}, // Match: 0b0011011, Symbol: i
+ {0x6f, 5}, // Match: 0b0011100, Symbol: o
+ {0x6f, 5}, // Match: 0b0011101, Symbol: o
+ {0x6f, 5}, // Match: 0b0011110, Symbol: o
+ {0x6f, 5}, // Match: 0b0011111, Symbol: o
+ {0x73, 5}, // Match: 0b0100000, Symbol: s
+ {0x73, 5}, // Match: 0b0100001, Symbol: s
+ {0x73, 5}, // Match: 0b0100010, Symbol: s
+ {0x73, 5}, // Match: 0b0100011, Symbol: s
+ {0x74, 5}, // Match: 0b0100100, Symbol: t
+ {0x74, 5}, // Match: 0b0100101, Symbol: t
+ {0x74, 5}, // Match: 0b0100110, Symbol: t
+ {0x74, 5}, // Match: 0b0100111, Symbol: t
+ {0x20, 6}, // Match: 0b0101000, Symbol: (space)
+ {0x20, 6}, // Match: 0b0101001, Symbol: (space)
+ {0x25, 6}, // Match: 0b0101010, Symbol: %
+ {0x25, 6}, // Match: 0b0101011, Symbol: %
+ {0x2d, 6}, // Match: 0b0101100, Symbol: -
+ {0x2d, 6}, // Match: 0b0101101, Symbol: -
+ {0x2e, 6}, // Match: 0b0101110, Symbol: .
+ {0x2e, 6}, // Match: 0b0101111, Symbol: .
+ {0x2f, 6}, // Match: 0b0110000, Symbol: /
+ {0x2f, 6}, // Match: 0b0110001, Symbol: /
+ {0x33, 6}, // Match: 0b0110010, Symbol: 3
+ {0x33, 6}, // Match: 0b0110011, Symbol: 3
+ {0x34, 6}, // Match: 0b0110100, Symbol: 4
+ {0x34, 6}, // Match: 0b0110101, Symbol: 4
+ {0x35, 6}, // Match: 0b0110110, Symbol: 5
+ {0x35, 6}, // Match: 0b0110111, Symbol: 5
+ {0x36, 6}, // Match: 0b0111000, Symbol: 6
+ {0x36, 6}, // Match: 0b0111001, Symbol: 6
+ {0x37, 6}, // Match: 0b0111010, Symbol: 7
+ {0x37, 6}, // Match: 0b0111011, Symbol: 7
+ {0x38, 6}, // Match: 0b0111100, Symbol: 8
+ {0x38, 6}, // Match: 0b0111101, Symbol: 8
+ {0x39, 6}, // Match: 0b0111110, Symbol: 9
+ {0x39, 6}, // Match: 0b0111111, Symbol: 9
+ {0x3d, 6}, // Match: 0b1000000, Symbol: =
+ {0x3d, 6}, // Match: 0b1000001, Symbol: =
+ {0x41, 6}, // Match: 0b1000010, Symbol: A
+ {0x41, 6}, // Match: 0b1000011, Symbol: A
+ {0x5f, 6}, // Match: 0b1000100, Symbol: _
+ {0x5f, 6}, // Match: 0b1000101, Symbol: _
+ {0x62, 6}, // Match: 0b1000110, Symbol: b
+ {0x62, 6}, // Match: 0b1000111, Symbol: b
+ {0x64, 6}, // Match: 0b1001000, Symbol: d
+ {0x64, 6}, // Match: 0b1001001, Symbol: d
+ {0x66, 6}, // Match: 0b1001010, Symbol: f
+ {0x66, 6}, // Match: 0b1001011, Symbol: f
+ {0x67, 6}, // Match: 0b1001100, Symbol: g
+ {0x67, 6}, // Match: 0b1001101, Symbol: g
+ {0x68, 6}, // Match: 0b1001110, Symbol: h
+ {0x68, 6}, // Match: 0b1001111, Symbol: h
+ {0x6c, 6}, // Match: 0b1010000, Symbol: l
+ {0x6c, 6}, // Match: 0b1010001, Symbol: l
+ {0x6d, 6}, // Match: 0b1010010, Symbol: m
+ {0x6d, 6}, // Match: 0b1010011, Symbol: m
+ {0x6e, 6}, // Match: 0b1010100, Symbol: n
+ {0x6e, 6}, // Match: 0b1010101, Symbol: n
+ {0x70, 6}, // Match: 0b1010110, Symbol: p
+ {0x70, 6}, // Match: 0b1010111, Symbol: p
+ {0x72, 6}, // Match: 0b1011000, Symbol: r
+ {0x72, 6}, // Match: 0b1011001, Symbol: r
+ {0x75, 6}, // Match: 0b1011010, Symbol: u
+ {0x75, 6}, // Match: 0b1011011, Symbol: u
+ {0x3a, 7}, // Match: 0b1011100, Symbol: :
+ {0x42, 7}, // Match: 0b1011101, Symbol: B
+ {0x43, 7}, // Match: 0b1011110, Symbol: C
+ {0x44, 7}, // Match: 0b1011111, Symbol: D
+ {0x45, 7}, // Match: 0b1100000, Symbol: E
+ {0x46, 7}, // Match: 0b1100001, Symbol: F
+ {0x47, 7}, // Match: 0b1100010, Symbol: G
+ {0x48, 7}, // Match: 0b1100011, Symbol: H
+ {0x49, 7}, // Match: 0b1100100, Symbol: I
+ {0x4a, 7}, // Match: 0b1100101, Symbol: J
+ {0x4b, 7}, // Match: 0b1100110, Symbol: K
+ {0x4c, 7}, // Match: 0b1100111, Symbol: L
+ {0x4d, 7}, // Match: 0b1101000, Symbol: M
+ {0x4e, 7}, // Match: 0b1101001, Symbol: N
+ {0x4f, 7}, // Match: 0b1101010, Symbol: O
+ {0x50, 7}, // Match: 0b1101011, Symbol: P
+ {0x51, 7}, // Match: 0b1101100, Symbol: Q
+ {0x52, 7}, // Match: 0b1101101, Symbol: R
+ {0x53, 7}, // Match: 0b1101110, Symbol: S
+ {0x54, 7}, // Match: 0b1101111, Symbol: T
+ {0x55, 7}, // Match: 0b1110000, Symbol: U
+ {0x56, 7}, // Match: 0b1110001, Symbol: V
+ {0x57, 7}, // Match: 0b1110010, Symbol: W
+ {0x59, 7}, // Match: 0b1110011, Symbol: Y
+ {0x6a, 7}, // Match: 0b1110100, Symbol: j
+ {0x6b, 7}, // Match: 0b1110101, Symbol: k
+ {0x71, 7}, // Match: 0b1110110, Symbol: q
+ {0x76, 7}, // Match: 0b1110111, Symbol: v
+ {0x77, 7}, // Match: 0b1111000, Symbol: w
+ {0x78, 7}, // Match: 0b1111001, Symbol: x
+ {0x79, 7}, // Match: 0b1111010, Symbol: y
+ {0x7a, 7}, // Match: 0b1111011, Symbol: z
+};
+
+} // namespace
+
+HuffmanBitBuffer::HuffmanBitBuffer() {
+ Reset();
+}
+
+void HuffmanBitBuffer::Reset() {
+ accumulator_ = 0;
+ count_ = 0;
+}
+
+size_t HuffmanBitBuffer::AppendBytes(absl::string_view input) {
+ HuffmanAccumulatorBitCount free_cnt = free_count();
+ size_t bytes_available = input.size();
+ if (free_cnt < 8 || bytes_available == 0) {
+ return 0;
+ }
+
+ // Top up |accumulator_| until there isn't room for a whole byte.
+ size_t bytes_used = 0;
+ auto* ptr = reinterpret_cast<const uint8_t*>(input.data());
+ do {
+ auto b = static_cast<HuffmanAccumulator>(*ptr++);
+ free_cnt -= 8;
+ accumulator_ |= (b << free_cnt);
+ ++bytes_used;
+ } while (free_cnt >= 8 && bytes_used < bytes_available);
+ count_ += (bytes_used * 8);
+ return bytes_used;
+}
+
+HuffmanAccumulatorBitCount HuffmanBitBuffer::free_count() const {
+ return kHuffmanAccumulatorBitCount - count_;
+}
+
+void HuffmanBitBuffer::ConsumeBits(HuffmanAccumulatorBitCount code_length) {
+ QUICHE_DCHECK_LE(code_length, count_);
+ accumulator_ <<= code_length;
+ count_ -= code_length;
+}
+
+bool HuffmanBitBuffer::InputProperlyTerminated() const {
+ auto cnt = count();
+ if (cnt < 8) {
+ if (cnt == 0) {
+ return true;
+ }
+ HuffmanAccumulator expected = ~(~HuffmanAccumulator() >> cnt);
+ // We expect all the bits below the high order |cnt| bits of accumulator_
+ // to be cleared as we perform left shift operations while decoding.
+ QUICHE_DCHECK_EQ(accumulator_ & ~expected, 0u)
+ << "\n expected: " << HuffmanAccumulatorBitSet(expected) << "\n "
+ << *this;
+ return accumulator_ == expected;
+ }
+ return false;
+}
+
+std::string HuffmanBitBuffer::DebugString() const {
+ std::stringstream ss;
+ ss << "{accumulator: " << HuffmanAccumulatorBitSet(accumulator_)
+ << "; count: " << count_ << "}";
+ return ss.str();
+}
+
+HpackHuffmanDecoder::HpackHuffmanDecoder() = default;
+
+HpackHuffmanDecoder::~HpackHuffmanDecoder() = default;
+
+bool HpackHuffmanDecoder::Decode(absl::string_view input, std::string* output) {
+ HTTP2_DVLOG(1) << "HpackHuffmanDecoder::Decode";
+
+ // Fill bit_buffer_ from input.
+ input.remove_prefix(bit_buffer_.AppendBytes(input));
+
+ while (true) {
+ HTTP2_DVLOG(3) << "Enter Decode Loop, bit_buffer_: " << bit_buffer_;
+ if (bit_buffer_.count() >= 7) {
+ // Get high 7 bits of the bit buffer, see if that contains a complete
+ // code of 5, 6 or 7 bits.
+ uint8_t short_code =
+ bit_buffer_.value() >> (kHuffmanAccumulatorBitCount - 7);
+ QUICHE_DCHECK_LT(short_code, 128);
+ if (short_code < kShortCodeTableSize) {
+ ShortCodeInfo info = kShortCodeTable[short_code];
+ bit_buffer_.ConsumeBits(info.length);
+ output->push_back(static_cast<char>(info.symbol));
+ continue;
+ }
+ // The code is more than 7 bits long. Use PrefixToInfo, etc. to decode
+ // longer codes.
+ } else {
+ // We may have (mostly) drained bit_buffer_. If we can top it up, try
+ // using the table decoder above.
+ size_t byte_count = bit_buffer_.AppendBytes(input);
+ if (byte_count > 0) {
+ input.remove_prefix(byte_count);
+ continue;
+ }
+ }
+
+ HuffmanCode code_prefix = bit_buffer_.value() >> kExtraAccumulatorBitCount;
+ HTTP2_DVLOG(3) << "code_prefix: " << HuffmanCodeBitSet(code_prefix);
+
+ PrefixInfo prefix_info = PrefixToInfo(code_prefix);
+ HTTP2_DVLOG(3) << "prefix_info: " << prefix_info;
+ QUICHE_DCHECK_LE(kMinCodeBitCount, prefix_info.code_length);
+ QUICHE_DCHECK_LE(prefix_info.code_length, kMaxCodeBitCount);
+
+ if (prefix_info.code_length <= bit_buffer_.count()) {
+ // We have enough bits for one code.
+ uint32_t canonical = prefix_info.DecodeToCanonical(code_prefix);
+ if (canonical < 256) {
+ // Valid code.
+ char c = kCanonicalToSymbol[canonical];
+ output->push_back(c);
+ bit_buffer_.ConsumeBits(prefix_info.code_length);
+ continue;
+ }
+ // Encoder is not supposed to explicity encode the EOS symbol.
+ HTTP2_DLOG(ERROR) << "EOS explicitly encoded!\n " << bit_buffer_ << "\n "
+ << prefix_info;
+ return false;
+ }
+ // bit_buffer_ doesn't have enough bits in it to decode the next symbol.
+ // Append to it as many bytes as are available AND fit.
+ size_t byte_count = bit_buffer_.AppendBytes(input);
+ if (byte_count == 0) {
+ QUICHE_DCHECK_EQ(input.size(), 0u);
+ return true;
+ }
+ input.remove_prefix(byte_count);
+ }
+}
+
+std::string HpackHuffmanDecoder::DebugString() const {
+ return bit_buffer_.DebugString();
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder.h
new file mode 100644
index 00000000000..9befd6c628d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder.h
@@ -0,0 +1,134 @@
+// 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_HUFFMAN_HPACK_HUFFMAN_DECODER_H_
+#define QUICHE_HTTP2_HPACK_HUFFMAN_HPACK_HUFFMAN_DECODER_H_
+
+// HpackHuffmanDecoder is an incremental decoder of strings that have been
+// encoded using the Huffman table defined in the HPACK spec.
+// By incremental, we mean that the HpackHuffmanDecoder::Decode method does
+// not require the entire string to be provided, and can instead decode the
+// string as fragments of it become available (e.g. as HPACK block fragments
+// are received for decoding by HpackEntryDecoder).
+
+#include <stddef.h>
+
+#include <cstdint>
+#include <iosfwd>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+// HuffmanAccumulator is used to store bits during decoding, e.g. next N bits
+// that have not yet been decoded, but have been extracted from the encoded
+// string). An advantage of using a uint64 for the accumulator
+// is that it has room for the bits of the longest code plus the bits of a full
+// byte; that means that when adding more bits to the accumulator, it can always
+// be done in whole bytes. For example, if we currently have 26 bits in the
+// accumulator, and need more to decode the current symbol, we can add a whole
+// byte to the accumulator, and not have to do juggling with adding 6 bits (to
+// reach 30), and then keep track of the last two bits we've not been able to
+// add to the accumulator.
+typedef uint64_t HuffmanAccumulator;
+typedef size_t HuffmanAccumulatorBitCount;
+
+// HuffmanBitBuffer stores the leading edge of bits to be decoded. The high
+// order bit of accumulator_ is the next bit to be decoded.
+class QUICHE_EXPORT_PRIVATE HuffmanBitBuffer {
+ public:
+ HuffmanBitBuffer();
+
+ // Prepare for decoding a new Huffman encoded string.
+ void Reset();
+
+ // Add as many whole bytes to the accumulator (accumulator_) as possible,
+ // returning the number of bytes added.
+ size_t AppendBytes(absl::string_view input);
+
+ // Get the bits of the accumulator.
+ HuffmanAccumulator value() const { return accumulator_; }
+
+ // Number of bits of the encoded string that are in the accumulator
+ // (accumulator_).
+ HuffmanAccumulatorBitCount count() const { return count_; }
+
+ // Are there no bits in the accumulator?
+ bool IsEmpty() const { return count_ == 0; }
+
+ // Number of additional bits that can be added to the accumulator.
+ HuffmanAccumulatorBitCount free_count() const;
+
+ // Consume the leading |code_length| bits of the accumulator.
+ void ConsumeBits(HuffmanAccumulatorBitCount code_length);
+
+ // Are the contents valid for the end of a Huffman encoded string? The RFC
+ // states that EOS (end-of-string) symbol must not be explicitly encoded in
+ // the bit stream, but any unused bits in the final byte must be set to the
+ // prefix of the EOS symbol, which is all 1 bits. So there can be at most 7
+ // such bits.
+ // Returns true if the bit buffer is empty, or contains at most 7 bits, all
+ // of them 1. Otherwise returns false.
+ bool InputProperlyTerminated() const;
+
+ std::string DebugString() const;
+
+ private:
+ HuffmanAccumulator accumulator_;
+ HuffmanAccumulatorBitCount count_;
+};
+
+inline std::ostream& operator<<(std::ostream& out, const HuffmanBitBuffer& v) {
+ return out << v.DebugString();
+}
+
+class QUICHE_EXPORT_PRIVATE HpackHuffmanDecoder {
+ public:
+ HpackHuffmanDecoder();
+ ~HpackHuffmanDecoder();
+
+ // Prepare for decoding a new Huffman encoded string.
+ void Reset() { bit_buffer_.Reset(); }
+
+ // Decode the portion of a HPACK Huffman encoded string that is in |input|,
+ // appending the decoded symbols into |*output|, stopping when more bits are
+ // needed to determine the next symbol, which/ means that the input has been
+ // drained, and also that the bit_buffer_ is empty or that the bits that are
+ // in it are not a whole symbol.
+ // If |input| is the start of a string, the caller must first call Reset.
+ // If |input| includes the end of the encoded string, the caller must call
+ // InputProperlyTerminated after Decode has returned true in order to
+ // determine if the encoded string was properly terminated.
+ // Returns false if something went wrong (e.g. the encoding contains the code
+ // EOS symbol). Otherwise returns true, in which case input has been fully
+ // decoded or buffered; in particular, if the low-order bit of the final byte
+ // of the input is not the last bit of an encoded symbol, then bit_buffer_
+ // will contain the leading bits of the code for that symbol, but not the
+ // final bits of that code.
+ // Note that output should be empty, but that it is not cleared by Decode().
+ bool Decode(absl::string_view input, std::string* output);
+
+ // Is what remains in the bit_buffer_ valid at the end of an encoded string?
+ // Call after passing the the final portion of a Huffman string to Decode,
+ // and getting true as the result.
+ bool InputProperlyTerminated() const {
+ return bit_buffer_.InputProperlyTerminated();
+ }
+
+ std::string DebugString() const;
+
+ private:
+ HuffmanBitBuffer bit_buffer_;
+};
+
+inline std::ostream& operator<<(std::ostream& out,
+ const HpackHuffmanDecoder& v) {
+ return out << v.DebugString();
+}
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_HUFFMAN_HPACK_HUFFMAN_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder_test.cc
new file mode 100644
index 00000000000..728d3d47b21
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_decoder_test.cc
@@ -0,0 +1,242 @@
+// 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 "quiche/http2/hpack/huffman/hpack_huffman_decoder.h"
+
+// Tests of HpackHuffmanDecoder and HuffmanBitBuffer.
+
+#include <iostream>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "quiche/http2/decoder/decode_buffer.h"
+#include "quiche/http2/decoder/decode_status.h"
+#include "quiche/http2/tools/random_decoder_test.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+namespace http2 {
+namespace test {
+namespace {
+
+TEST(HuffmanBitBufferTest, Reset) {
+ HuffmanBitBuffer bb;
+ EXPECT_TRUE(bb.IsEmpty());
+ EXPECT_TRUE(bb.InputProperlyTerminated());
+ EXPECT_EQ(bb.count(), 0u);
+ EXPECT_EQ(bb.free_count(), 64u);
+ EXPECT_EQ(bb.value(), 0u);
+}
+
+TEST(HuffmanBitBufferTest, AppendBytesAligned) {
+ std::string s;
+ s.push_back('\x11');
+ s.push_back('\x22');
+ s.push_back('\x33');
+ absl::string_view sp(s);
+
+ HuffmanBitBuffer bb;
+ sp.remove_prefix(bb.AppendBytes(sp));
+ EXPECT_TRUE(sp.empty());
+ EXPECT_FALSE(bb.IsEmpty()) << bb;
+ EXPECT_FALSE(bb.InputProperlyTerminated());
+ EXPECT_EQ(bb.count(), 24u) << bb;
+ EXPECT_EQ(bb.free_count(), 40u) << bb;
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 40) << bb;
+
+ s.clear();
+ s.push_back('\x44');
+ sp = s;
+
+ sp.remove_prefix(bb.AppendBytes(sp));
+ EXPECT_TRUE(sp.empty());
+ EXPECT_EQ(bb.count(), 32u) << bb;
+ EXPECT_EQ(bb.free_count(), 32u) << bb;
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x11223344) << 32) << bb;
+
+ s.clear();
+ s.push_back('\x55');
+ s.push_back('\x66');
+ s.push_back('\x77');
+ s.push_back('\x88');
+ s.push_back('\x99');
+ sp = s;
+
+ sp.remove_prefix(bb.AppendBytes(sp));
+ EXPECT_EQ(sp.size(), 1u);
+ EXPECT_EQ('\x99', sp[0]);
+ EXPECT_EQ(bb.count(), 64u) << bb;
+ EXPECT_EQ(bb.free_count(), 0u) << bb;
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb;
+
+ sp.remove_prefix(bb.AppendBytes(sp));
+ EXPECT_EQ(sp.size(), 1u);
+ EXPECT_EQ('\x99', sp[0]);
+ EXPECT_EQ(bb.count(), 64u) << bb;
+ EXPECT_EQ(bb.free_count(), 0u) << bb;
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x1122334455667788LL)) << bb;
+}
+
+TEST(HuffmanBitBufferTest, ConsumeBits) {
+ std::string s;
+ s.push_back('\x11');
+ s.push_back('\x22');
+ s.push_back('\x33');
+ absl::string_view sp(s);
+
+ HuffmanBitBuffer bb;
+ sp.remove_prefix(bb.AppendBytes(sp));
+ EXPECT_TRUE(sp.empty());
+
+ bb.ConsumeBits(1);
+ EXPECT_EQ(bb.count(), 23u) << bb;
+ EXPECT_EQ(bb.free_count(), 41u) << bb;
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x112233) << 41) << bb;
+
+ bb.ConsumeBits(20);
+ EXPECT_EQ(bb.count(), 3u) << bb;
+ EXPECT_EQ(bb.free_count(), 61u) << bb;
+ EXPECT_EQ(bb.value(), HuffmanAccumulator(0x3) << 61) << bb;
+}
+
+TEST(HuffmanBitBufferTest, AppendBytesUnaligned) {
+ std::string s;
+ s.push_back('\x11');
+ s.push_back('\x22');
+ s.push_back('\x33');
+ s.push_back('\x44');
+ s.push_back('\x55');
+ s.push_back('\x66');
+ s.push_back('\x77');
+ s.push_back('\x88');
+ s.push_back('\x99');
+ s.push_back('\xaa');
+ s.push_back('\xbb');
+ s.push_back('\xcc');
+ s.push_back('\xdd');
+ absl::string_view sp(s);
+
+ HuffmanBitBuffer bb;
+ sp.remove_prefix(bb.AppendBytes(sp));
+ EXPECT_EQ(sp.size(), 5u);
+ EXPECT_FALSE(bb.InputProperlyTerminated());
+
+ bb.ConsumeBits(15);
+ EXPECT_EQ(bb.count(), 49u) << bb;
+ EXPECT_EQ(bb.free_count(), 15u) << bb;
+
+ HuffmanAccumulator expected(0x1122334455667788);
+ expected <<= 15;
+ EXPECT_EQ(bb.value(), expected);
+
+ sp.remove_prefix(bb.AppendBytes(sp));
+ EXPECT_EQ(sp.size(), 4u);
+ EXPECT_EQ(bb.count(), 57u) << bb;
+ EXPECT_EQ(bb.free_count(), 7u) << bb;
+
+ expected |= (HuffmanAccumulator(0x99) << 7);
+ EXPECT_EQ(bb.value(), expected)
+ << bb << std::hex << "\n actual: " << bb.value()
+ << "\n expected: " << expected;
+}
+
+class HpackHuffmanDecoderTest : public RandomDecoderTest {
+ protected:
+ HpackHuffmanDecoderTest() {
+ // The decoder may return true, and its accumulator may be empty, at
+ // many boundaries while decoding, and yet the whole string hasn't
+ // been decoded.
+ stop_decode_on_done_ = false;
+ }
+
+ DecodeStatus StartDecoding(DecodeBuffer* b) override {
+ input_bytes_seen_ = 0;
+ output_buffer_.clear();
+ decoder_.Reset();
+ return ResumeDecoding(b);
+ }
+
+ DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+ input_bytes_seen_ += b->Remaining();
+ absl::string_view sp(b->cursor(), b->Remaining());
+ if (decoder_.Decode(sp, &output_buffer_)) {
+ b->AdvanceCursor(b->Remaining());
+ // Successfully decoded (or buffered) the bytes in absl::string_view.
+ EXPECT_LE(input_bytes_seen_, input_bytes_expected_);
+ // Have we reached the end of the encoded string?
+ if (input_bytes_expected_ == input_bytes_seen_) {
+ if (decoder_.InputProperlyTerminated()) {
+ return DecodeStatus::kDecodeDone;
+ } else {
+ return DecodeStatus::kDecodeError;
+ }
+ }
+ return DecodeStatus::kDecodeInProgress;
+ }
+ return DecodeStatus::kDecodeError;
+ }
+
+ HpackHuffmanDecoder decoder_;
+ std::string output_buffer_;
+ size_t input_bytes_seen_;
+ size_t input_bytes_expected_;
+};
+
+TEST_F(HpackHuffmanDecoderTest, SpecRequestExamples) {
+ HpackHuffmanDecoder decoder;
+ std::string test_table[] = {
+ absl::HexStringToBytes("f1e3c2e5f23a6ba0ab90f4ff"),
+ "www.example.com",
+ absl::HexStringToBytes("a8eb10649cbf"),
+ "no-cache",
+ absl::HexStringToBytes("25a849e95ba97d7f"),
+ "custom-key",
+ absl::HexStringToBytes("25a849e95bb8e8b4bf"),
+ "custom-value",
+ };
+ for (size_t i = 0; i != ABSL_ARRAYSIZE(test_table); i += 2) {
+ const std::string& huffman_encoded(test_table[i]);
+ const std::string& plain_string(test_table[i + 1]);
+ std::string buffer;
+ decoder.Reset();
+ EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder;
+ EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder;
+ EXPECT_EQ(buffer, plain_string);
+ }
+}
+
+TEST_F(HpackHuffmanDecoderTest, SpecResponseExamples) {
+ HpackHuffmanDecoder decoder;
+ // clang-format off
+ std::string test_table[] = {
+ absl::HexStringToBytes("6402"),
+ "302",
+ absl::HexStringToBytes("aec3771a4b"),
+ "private",
+ absl::HexStringToBytes("d07abe941054d444a8200595040b8166"
+ "e082a62d1bff"),
+ "Mon, 21 Oct 2013 20:13:21 GMT",
+ absl::HexStringToBytes("9d29ad171863c78f0b97c8e9ae82ae43"
+ "d3"),
+ "https://www.example.com",
+ absl::HexStringToBytes("94e7821dd7f2e6c7b335dfdfcd5b3960"
+ "d5af27087f3672c1ab270fb5291f9587"
+ "316065c003ed4ee5b1063d5007"),
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+ };
+ // clang-format on
+ for (size_t i = 0; i != ABSL_ARRAYSIZE(test_table); i += 2) {
+ const std::string& huffman_encoded(test_table[i]);
+ const std::string& plain_string(test_table[i + 1]);
+ std::string buffer;
+ decoder.Reset();
+ EXPECT_TRUE(decoder.Decode(huffman_encoded, &buffer)) << decoder;
+ EXPECT_TRUE(decoder.InputProperlyTerminated()) << decoder;
+ EXPECT_EQ(buffer, plain_string);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder.cc
new file mode 100644
index 00000000000..b5e3404c668
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder.cc
@@ -0,0 +1,129 @@
+// Copyright (c) 2018 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 "quiche/http2/hpack/huffman/hpack_huffman_encoder.h"
+
+#include "quiche/http2/hpack/huffman/huffman_spec_tables.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+
+namespace http2 {
+
+size_t HuffmanSize(absl::string_view plain) {
+ size_t bits = 0;
+ for (const uint8_t c : plain) {
+ bits += HuffmanSpecTables::kCodeLengths[c];
+ }
+ return (bits + 7) / 8;
+}
+
+void HuffmanEncode(absl::string_view plain,
+ size_t encoded_size,
+ std::string* huffman) {
+ QUICHE_DCHECK(huffman != nullptr);
+ huffman->reserve(huffman->size() + encoded_size);
+ uint64_t bit_buffer = 0; // High-bit is next bit to output. Not clear if that
+ // is more performant than having the low-bit be the
+ // last to be output.
+ size_t bits_unused = 64; // Number of bits available for the next code.
+ for (uint8_t c : plain) {
+ size_t code_length = HuffmanSpecTables::kCodeLengths[c];
+ if (bits_unused < code_length) {
+ // There isn't enough room in bit_buffer for the code of c.
+ // Flush until bits_unused > 56 (i.e. 64 - 8).
+ do {
+ char h = static_cast<char>(bit_buffer >> 56);
+ bit_buffer <<= 8;
+ bits_unused += 8;
+ // Perhaps would be more efficient if we populated an array of chars,
+ // so we don't have to call push_back each time. Reconsider if used
+ // for production.
+ huffman->push_back(h);
+ } while (bits_unused <= 56);
+ }
+ uint64_t code = HuffmanSpecTables::kRightCodes[c];
+ size_t shift_by = bits_unused - code_length;
+ bit_buffer |= (code << shift_by);
+ bits_unused -= code_length;
+ }
+ // bit_buffer contains (64-bits_unused) bits that still need to be flushed.
+ // Output whole bytes until we don't have any whole bytes left.
+ size_t bits_used = 64 - bits_unused;
+ while (bits_used >= 8) {
+ char h = static_cast<char>(bit_buffer >> 56);
+ bit_buffer <<= 8;
+ bits_used -= 8;
+ huffman->push_back(h);
+ }
+ if (bits_used > 0) {
+ // We have less than a byte left to output. The spec calls for padding out
+ // the final byte with the leading bits of the EOS symbol (30 1-bits).
+ constexpr uint64_t leading_eos_bits = 0b11111111;
+ bit_buffer |= (leading_eos_bits << (56 - bits_used));
+ char h = static_cast<char>(bit_buffer >> 56);
+ huffman->push_back(h);
+ }
+}
+
+void HuffmanEncodeFast(absl::string_view input,
+ size_t encoded_size,
+ std::string* output) {
+ const size_t original_size = output->size();
+ const size_t final_size = original_size + encoded_size;
+ // Reserve an extra four bytes to avoid accessing unallocated memory (even
+ // though it would only be OR'd with zeros and thus not modified).
+ output->resize(final_size + 4, 0);
+
+ // Pointer to first appended byte.
+ char* const first = &*output->begin() + original_size;
+ size_t bit_counter = 0;
+ for (uint8_t c : input) {
+ // Align the Huffman code to byte boundaries as it needs to be written.
+ // The longest Huffman code is 30 bits long, and it can be shifted by up to
+ // 7 bits, requiring 37 bits in total. The most significant 25 bits and
+ // least significant 2 bits of |code| are always zero.
+ uint64_t code = static_cast<uint64_t>(HuffmanSpecTables::kLeftCodes[c])
+ << (8 - (bit_counter % 8));
+ // The byte where the first bit of |code| needs to be written.
+ char* const current = first + (bit_counter / 8);
+
+ bit_counter += HuffmanSpecTables::kCodeLengths[c];
+
+ *current |= code >> 32;
+
+ // Do not check if this write is zero before executing it, because with
+ // uniformly random shifts and an ideal random input distribution
+ // corresponding to the Huffman tree it would only be zero in 29% of the
+ // cases.
+ *(current + 1) |= (code >> 24) & 0xff;
+
+ // Continue to next input character if there is nothing else to write.
+ // (If next byte is zero, then rest must also be zero.)
+ if ((code & 0xff0000) == 0) {
+ continue;
+ }
+ *(current + 2) |= (code >> 16) & 0xff;
+
+ // Continue to next input character if there is nothing else to write.
+ // (If next byte is zero, then rest must also be zero.)
+ if ((code & 0xff00) == 0) {
+ continue;
+ }
+ *(current + 3) |= (code >> 8) & 0xff;
+
+ // Do not check if this write is zero, because the check would probably be
+ // as expensive as the write.
+ *(current + 4) |= code & 0xff;
+ }
+
+ QUICHE_DCHECK_EQ(encoded_size, (bit_counter + 7) / 8);
+
+ // EOF
+ if (bit_counter % 8 != 0) {
+ *(first + encoded_size - 1) |= 0xff >> (bit_counter & 7);
+ }
+
+ output->resize(final_size);
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder.h
new file mode 100644
index 00000000000..7e731870705
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder.h
@@ -0,0 +1,40 @@
+// 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_HUFFMAN_HPACK_HUFFMAN_ENCODER_H_
+#define QUICHE_HTTP2_HPACK_HUFFMAN_HPACK_HUFFMAN_ENCODER_H_
+
+// Functions supporting the encoding of strings using the HPACK-defined Huffman
+// table.
+
+#include <cstddef> // For size_t
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+// Returns the size of the Huffman encoding of |plain|, which may be greater
+// than plain.size().
+QUICHE_EXPORT_PRIVATE size_t HuffmanSize(absl::string_view plain);
+
+// Encode the plain text string |plain| with the Huffman encoding defined in the
+// HPACK RFC, 7541. |encoded_size| is used to pre-allocate storage and it
+// should be the value returned by HuffmanSize(). Appends the result to
+// |*huffman|.
+QUICHE_EXPORT_PRIVATE void HuffmanEncode(absl::string_view plain,
+ size_t encoded_size,
+ std::string* huffman);
+
+// Encode |input| with the Huffman encoding defined RFC7541, used in HPACK and
+// QPACK. |encoded_size| must be the value returned by HuffmanSize().
+// Appends the result to the end of |*output|.
+QUICHE_EXPORT_PRIVATE void HuffmanEncodeFast(absl::string_view input,
+ size_t encoded_size,
+ std::string* output);
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_HUFFMAN_HPACK_HUFFMAN_ENCODER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder_test.cc
new file mode 100644
index 00000000000..a670b038785
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_encoder_test.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2018 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 "quiche/http2/hpack/huffman/hpack_huffman_encoder.h"
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace {
+
+class HuffmanEncoderTest : public ::testing::TestWithParam<bool> {
+ protected:
+ HuffmanEncoderTest() : use_fast_encoder_(GetParam()) {}
+ virtual ~HuffmanEncoderTest() = default;
+
+ void Encode(absl::string_view input,
+ size_t encoded_size,
+ std::string* output) {
+ use_fast_encoder_ ? HuffmanEncodeFast(input, encoded_size, output)
+ : HuffmanEncode(input, encoded_size, output);
+ }
+
+ const bool use_fast_encoder_;
+};
+
+INSTANTIATE_TEST_SUITE_P(TwoEncoders, HuffmanEncoderTest, ::testing::Bool());
+
+TEST_P(HuffmanEncoderTest, Empty) {
+ std::string empty("");
+ size_t encoded_size = HuffmanSize(empty);
+ EXPECT_EQ(0u, encoded_size);
+
+ std::string buffer;
+ Encode(empty, encoded_size, &buffer);
+ EXPECT_EQ("", buffer);
+}
+
+TEST_P(HuffmanEncoderTest, SpecRequestExamples) {
+ std::string test_table[] = {
+ absl::HexStringToBytes("f1e3c2e5f23a6ba0ab90f4ff"),
+ "www.example.com",
+ absl::HexStringToBytes("a8eb10649cbf"),
+ "no-cache",
+ absl::HexStringToBytes("25a849e95ba97d7f"),
+ "custom-key",
+ absl::HexStringToBytes("25a849e95bb8e8b4bf"),
+ "custom-value",
+ };
+ for (size_t i = 0; i != ABSL_ARRAYSIZE(test_table); i += 2) {
+ const std::string& huffman_encoded(test_table[i]);
+ const std::string& plain_string(test_table[i + 1]);
+ size_t encoded_size = HuffmanSize(plain_string);
+ EXPECT_EQ(huffman_encoded.size(), encoded_size);
+ std::string buffer;
+ buffer.reserve();
+ Encode(plain_string, encoded_size, &buffer);
+ EXPECT_EQ(buffer, huffman_encoded) << "Error encoding " << plain_string;
+ }
+}
+
+TEST_P(HuffmanEncoderTest, SpecResponseExamples) {
+ // clang-format off
+ std::string test_table[] = {
+ absl::HexStringToBytes("6402"),
+ "302",
+ absl::HexStringToBytes("aec3771a4b"),
+ "private",
+ absl::HexStringToBytes("d07abe941054d444a8200595040b8166"
+ "e082a62d1bff"),
+ "Mon, 21 Oct 2013 20:13:21 GMT",
+ absl::HexStringToBytes("9d29ad171863c78f0b97c8e9ae82ae43"
+ "d3"),
+ "https://www.example.com",
+ absl::HexStringToBytes("94e7821dd7f2e6c7b335dfdfcd5b3960"
+ "d5af27087f3672c1ab270fb5291f9587"
+ "316065c003ed4ee5b1063d5007"),
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+ };
+ // clang-format on
+ for (size_t i = 0; i != ABSL_ARRAYSIZE(test_table); i += 2) {
+ const std::string& huffman_encoded(test_table[i]);
+ const std::string& plain_string(test_table[i + 1]);
+ size_t encoded_size = HuffmanSize(plain_string);
+ EXPECT_EQ(huffman_encoded.size(), encoded_size);
+ std::string buffer;
+ Encode(plain_string, encoded_size, &buffer);
+ EXPECT_EQ(buffer, huffman_encoded) << "Error encoding " << plain_string;
+ }
+}
+
+TEST_P(HuffmanEncoderTest, EncodedSizeAgreesWithEncodeString) {
+ std::string test_table[] = {
+ "",
+ "Mon, 21 Oct 2013 20:13:21 GMT",
+ "https://www.example.com",
+ "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
+ std::string(1, '\0'),
+ std::string("foo\0bar", 7),
+ std::string(256, '\0'),
+ };
+ // Modify last |test_table| entry to cover all codes.
+ for (size_t i = 0; i != 256; ++i) {
+ test_table[ABSL_ARRAYSIZE(test_table) - 1][i] = static_cast<char>(i);
+ }
+
+ for (size_t i = 0; i != ABSL_ARRAYSIZE(test_table); ++i) {
+ const std::string& plain_string = test_table[i];
+ size_t encoded_size = HuffmanSize(plain_string);
+ std::string huffman_encoded;
+ Encode(plain_string, encoded_size, &huffman_encoded);
+ EXPECT_EQ(encoded_size, huffman_encoded.size());
+ }
+}
+
+// Test that encoding appends to output without overwriting it.
+TEST_P(HuffmanEncoderTest, AppendToOutput) {
+ size_t encoded_size = HuffmanSize("foo");
+ std::string buffer;
+ Encode("foo", encoded_size, &buffer);
+ EXPECT_EQ(absl::HexStringToBytes("94e7"), buffer);
+
+ encoded_size = HuffmanSize("bar");
+ Encode("bar", encoded_size, &buffer);
+ EXPECT_EQ(absl::HexStringToBytes("94e78c767f"), buffer);
+}
+
+} // namespace
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_transcoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_transcoder_test.cc
new file mode 100644
index 00000000000..8d57ec96dcd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/hpack_huffman_transcoder_test.cc
@@ -0,0 +1,183 @@
+// Copyright (c) 2018 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.
+
+// A test of roundtrips through the encoder and decoder.
+
+#include <stddef.h>
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/decoder/decode_buffer.h"
+#include "quiche/http2/decoder/decode_status.h"
+#include "quiche/http2/hpack/huffman/hpack_huffman_decoder.h"
+#include "quiche/http2/hpack/huffman/hpack_huffman_encoder.h"
+#include "quiche/http2/tools/random_decoder_test.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/quiche_text_utils.h"
+
+using ::testing::AssertionSuccess;
+using ::testing::Combine;
+using ::testing::Range;
+using ::testing::Values;
+
+namespace http2 {
+namespace test {
+namespace {
+
+std::string GenAsciiNonControlSet() {
+ std::string s;
+ const char space = ' '; // First character after the control characters: 0x20
+ const char del = 127; // First character after the non-control characters.
+ for (char c = space; c < del; ++c) {
+ s.push_back(c);
+ }
+ return s;
+}
+
+class HpackHuffmanTranscoderTest : public RandomDecoderTest {
+ protected:
+ HpackHuffmanTranscoderTest()
+ : ascii_non_control_set_(GenAsciiNonControlSet()) {
+ // The decoder may return true, and its accumulator may be empty, at
+ // many boundaries while decoding, and yet the whole string hasn't
+ // been decoded.
+ stop_decode_on_done_ = false;
+ }
+
+ DecodeStatus StartDecoding(DecodeBuffer* b) override {
+ input_bytes_seen_ = 0;
+ output_buffer_.clear();
+ decoder_.Reset();
+ return ResumeDecoding(b);
+ }
+
+ DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+ input_bytes_seen_ += b->Remaining();
+ absl::string_view sp(b->cursor(), b->Remaining());
+ if (decoder_.Decode(sp, &output_buffer_)) {
+ b->AdvanceCursor(b->Remaining());
+ // Successfully decoded (or buffered) the bytes in absl::string_view.
+ EXPECT_LE(input_bytes_seen_, input_bytes_expected_);
+ // Have we reached the end of the encoded string?
+ if (input_bytes_expected_ == input_bytes_seen_) {
+ if (decoder_.InputProperlyTerminated()) {
+ return DecodeStatus::kDecodeDone;
+ } else {
+ return DecodeStatus::kDecodeError;
+ }
+ }
+ return DecodeStatus::kDecodeInProgress;
+ }
+ return DecodeStatus::kDecodeError;
+ }
+
+ AssertionResult TranscodeAndValidateSeveralWays(
+ absl::string_view plain,
+ absl::string_view expected_huffman) {
+ size_t encoded_size = HuffmanSize(plain);
+ std::string encoded;
+ HuffmanEncode(plain, encoded_size, &encoded);
+ VERIFY_EQ(encoded_size, encoded.size());
+ if (!expected_huffman.empty() || plain.empty()) {
+ VERIFY_EQ(encoded, expected_huffman);
+ }
+ input_bytes_expected_ = encoded.size();
+ auto validator = [plain, this]() -> AssertionResult {
+ VERIFY_EQ(output_buffer_.size(), plain.size());
+ VERIFY_EQ(output_buffer_, plain);
+ return AssertionSuccess();
+ };
+ DecodeBuffer db(encoded);
+ bool return_non_zero_on_first = false;
+ return DecodeAndValidateSeveralWays(&db, return_non_zero_on_first,
+ ValidateDoneAndEmpty(validator));
+ }
+
+ AssertionResult TranscodeAndValidateSeveralWays(absl::string_view plain) {
+ return TranscodeAndValidateSeveralWays(plain, "");
+ }
+
+ std::string RandomAsciiNonControlString(int length) {
+ return Random().RandStringWithAlphabet(length, ascii_non_control_set_);
+ }
+
+ std::string RandomBytes(int length) { return Random().RandString(length); }
+
+ const std::string ascii_non_control_set_;
+ HpackHuffmanDecoder decoder_;
+ std::string output_buffer_;
+ size_t input_bytes_seen_;
+ size_t input_bytes_expected_;
+};
+
+TEST_F(HpackHuffmanTranscoderTest, RoundTripRandomAsciiNonControlString) {
+ for (size_t length = 0; length != 20; length++) {
+ const std::string s = RandomAsciiNonControlString(length);
+ ASSERT_TRUE(TranscodeAndValidateSeveralWays(s))
+ << "Unable to decode:\n\n"
+ << quiche::QuicheTextUtils::HexDump(s) << "\n\noutput_buffer_:\n"
+ << quiche::QuicheTextUtils::HexDump(output_buffer_);
+ }
+}
+
+TEST_F(HpackHuffmanTranscoderTest, RoundTripRandomBytes) {
+ for (size_t length = 0; length != 20; length++) {
+ const std::string s = RandomBytes(length);
+ ASSERT_TRUE(TranscodeAndValidateSeveralWays(s))
+ << "Unable to decode:\n\n"
+ << quiche::QuicheTextUtils::HexDump(s) << "\n\noutput_buffer_:\n"
+ << quiche::QuicheTextUtils::HexDump(output_buffer_);
+ }
+}
+
+// Two parameters: decoder choice, and the character to round-trip.
+class HpackHuffmanTranscoderAdjacentCharTest
+ : public HpackHuffmanTranscoderTest,
+ public testing::WithParamInterface<int> {
+ protected:
+ HpackHuffmanTranscoderAdjacentCharTest()
+ : c_(static_cast<char>(GetParam())) {}
+
+ const char c_;
+};
+
+INSTANTIATE_TEST_SUITE_P(HpackHuffmanTranscoderAdjacentCharTest,
+ HpackHuffmanTranscoderAdjacentCharTest, Range(0, 256));
+
+// Test c_ adjacent to every other character, both before and after.
+TEST_P(HpackHuffmanTranscoderAdjacentCharTest, RoundTripAdjacentChar) {
+ std::string s;
+ for (int a = 0; a < 256; ++a) {
+ s.push_back(static_cast<char>(a));
+ s.push_back(c_);
+ s.push_back(static_cast<char>(a));
+ }
+ ASSERT_TRUE(TranscodeAndValidateSeveralWays(s));
+}
+
+// Two parameters: character to repeat, number of repeats.
+class HpackHuffmanTranscoderRepeatedCharTest
+ : public HpackHuffmanTranscoderTest,
+ public testing::WithParamInterface<std::tuple<int, int>> {
+ protected:
+ HpackHuffmanTranscoderRepeatedCharTest()
+ : c_(static_cast<char>(std::get<0>(GetParam()))),
+ length_(std::get<1>(GetParam())) {}
+ std::string MakeString() { return std::string(length_, c_); }
+
+ private:
+ const char c_;
+ const size_t length_;
+};
+
+INSTANTIATE_TEST_SUITE_P(HpackHuffmanTranscoderRepeatedCharTest,
+ HpackHuffmanTranscoderRepeatedCharTest,
+ Combine(Range(0, 256), Values(1, 2, 3, 4, 8, 16, 32)));
+
+TEST_P(HpackHuffmanTranscoderRepeatedCharTest, RoundTripRepeatedChar) {
+ ASSERT_TRUE(TranscodeAndValidateSeveralWays(MakeString()));
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/huffman_spec_tables.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/huffman_spec_tables.cc
new file mode 100644
index 00000000000..f4b103b93f6
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/huffman_spec_tables.cc
@@ -0,0 +1,572 @@
+// Copyright (c) 2018 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 "quiche/http2/hpack/huffman/huffman_spec_tables.h"
+
+namespace http2 {
+
+// clang-format off
+// static
+const uint8_t HuffmanSpecTables::kCodeLengths[] = {
+ 13, 23, 28, 28, 28, 28, 28, 28, // 0 - 7
+ 28, 24, 30, 28, 28, 30, 28, 28, // 8 - 15
+ 28, 28, 28, 28, 28, 28, 30, 28, // 16 - 23
+ 28, 28, 28, 28, 28, 28, 28, 28, // 24 - 31
+ 6, 10, 10, 12, 13, 6, 8, 11, // 32 - 39
+ 10, 10, 8, 11, 8, 6, 6, 6, // 40 - 47
+ 5, 5, 5, 6, 6, 6, 6, 6, // 48 - 55
+ 6, 6, 7, 8, 15, 6, 12, 10, // 56 - 63
+ 13, 6, 7, 7, 7, 7, 7, 7, // 64 - 71
+ 7, 7, 7, 7, 7, 7, 7, 7, // 72 - 79
+ 7, 7, 7, 7, 7, 7, 7, 7, // 80 - 87
+ 8, 7, 8, 13, 19, 13, 14, 6, // 88 - 95
+ 15, 5, 6, 5, 6, 5, 6, 6, // 96 - 103
+ 6, 5, 7, 7, 6, 6, 6, 5, // 104 - 111
+ 6, 7, 6, 5, 5, 6, 7, 7, // 112 - 119
+ 7, 7, 7, 15, 11, 14, 13, 28, // 120 - 127
+ 20, 22, 20, 20, 22, 22, 22, 23, // 128 - 135
+ 22, 23, 23, 23, 23, 23, 24, 23, // 136 - 143
+ 24, 24, 22, 23, 24, 23, 23, 23, // 144 - 151
+ 23, 21, 22, 23, 22, 23, 23, 24, // 152 - 159
+ 22, 21, 20, 22, 22, 23, 23, 21, // 160 - 167
+ 23, 22, 22, 24, 21, 22, 23, 23, // 168 - 175
+ 21, 21, 22, 21, 23, 22, 23, 23, // 176 - 183
+ 20, 22, 22, 22, 23, 22, 22, 23, // 184 - 191
+ 26, 26, 20, 19, 22, 23, 22, 25, // 192 - 199
+ 26, 26, 26, 27, 27, 26, 24, 25, // 200 - 207
+ 19, 21, 26, 27, 27, 26, 27, 24, // 208 - 215
+ 21, 21, 26, 26, 28, 27, 27, 27, // 216 - 223
+ 20, 24, 20, 21, 22, 21, 21, 23, // 224 - 231
+ 22, 22, 25, 25, 24, 24, 26, 23, // 232 - 239
+ 26, 27, 26, 26, 27, 27, 27, 27, // 240 - 247
+ 27, 28, 27, 27, 27, 27, 27, 26, // 248 - 255
+ 30, // 256
+};
+
+// The encoding of each symbol, left justified (as printed), which means that
+// the first bit of the encoding is the high-order bit of the uint32.
+// static
+const uint32_t HuffmanSpecTables::kLeftCodes[] = {
+ 0b11111111110000000000000000000000, // 0x00
+ 0b11111111111111111011000000000000, // 0x01
+ 0b11111111111111111111111000100000, // 0x02
+ 0b11111111111111111111111000110000, // 0x03
+ 0b11111111111111111111111001000000, // 0x04
+ 0b11111111111111111111111001010000, // 0x05
+ 0b11111111111111111111111001100000, // 0x06
+ 0b11111111111111111111111001110000, // 0x07
+ 0b11111111111111111111111010000000, // 0x08
+ 0b11111111111111111110101000000000, // 0x09
+ 0b11111111111111111111111111110000, // 0x0a
+ 0b11111111111111111111111010010000, // 0x0b
+ 0b11111111111111111111111010100000, // 0x0c
+ 0b11111111111111111111111111110100, // 0x0d
+ 0b11111111111111111111111010110000, // 0x0e
+ 0b11111111111111111111111011000000, // 0x0f
+ 0b11111111111111111111111011010000, // 0x10
+ 0b11111111111111111111111011100000, // 0x11
+ 0b11111111111111111111111011110000, // 0x12
+ 0b11111111111111111111111100000000, // 0x13
+ 0b11111111111111111111111100010000, // 0x14
+ 0b11111111111111111111111100100000, // 0x15
+ 0b11111111111111111111111111111000, // 0x16
+ 0b11111111111111111111111100110000, // 0x17
+ 0b11111111111111111111111101000000, // 0x18
+ 0b11111111111111111111111101010000, // 0x19
+ 0b11111111111111111111111101100000, // 0x1a
+ 0b11111111111111111111111101110000, // 0x1b
+ 0b11111111111111111111111110000000, // 0x1c
+ 0b11111111111111111111111110010000, // 0x1d
+ 0b11111111111111111111111110100000, // 0x1e
+ 0b11111111111111111111111110110000, // 0x1f
+ 0b01010000000000000000000000000000, // 0x20
+ 0b11111110000000000000000000000000, // '!'
+ 0b11111110010000000000000000000000, // '\"'
+ 0b11111111101000000000000000000000, // '#'
+ 0b11111111110010000000000000000000, // '$'
+ 0b01010100000000000000000000000000, // '%'
+ 0b11111000000000000000000000000000, // '&'
+ 0b11111111010000000000000000000000, // '\''
+ 0b11111110100000000000000000000000, // '('
+ 0b11111110110000000000000000000000, // ')'
+ 0b11111001000000000000000000000000, // '*'
+ 0b11111111011000000000000000000000, // '+'
+ 0b11111010000000000000000000000000, // ','
+ 0b01011000000000000000000000000000, // '-'
+ 0b01011100000000000000000000000000, // '.'
+ 0b01100000000000000000000000000000, // '/'
+ 0b00000000000000000000000000000000, // '0'
+ 0b00001000000000000000000000000000, // '1'
+ 0b00010000000000000000000000000000, // '2'
+ 0b01100100000000000000000000000000, // '3'
+ 0b01101000000000000000000000000000, // '4'
+ 0b01101100000000000000000000000000, // '5'
+ 0b01110000000000000000000000000000, // '6'
+ 0b01110100000000000000000000000000, // '7'
+ 0b01111000000000000000000000000000, // '8'
+ 0b01111100000000000000000000000000, // '9'
+ 0b10111000000000000000000000000000, // ':'
+ 0b11111011000000000000000000000000, // ';'
+ 0b11111111111110000000000000000000, // '<'
+ 0b10000000000000000000000000000000, // '='
+ 0b11111111101100000000000000000000, // '>'
+ 0b11111111000000000000000000000000, // '?'
+ 0b11111111110100000000000000000000, // '@'
+ 0b10000100000000000000000000000000, // 'A'
+ 0b10111010000000000000000000000000, // 'B'
+ 0b10111100000000000000000000000000, // 'C'
+ 0b10111110000000000000000000000000, // 'D'
+ 0b11000000000000000000000000000000, // 'E'
+ 0b11000010000000000000000000000000, // 'F'
+ 0b11000100000000000000000000000000, // 'G'
+ 0b11000110000000000000000000000000, // 'H'
+ 0b11001000000000000000000000000000, // 'I'
+ 0b11001010000000000000000000000000, // 'J'
+ 0b11001100000000000000000000000000, // 'K'
+ 0b11001110000000000000000000000000, // 'L'
+ 0b11010000000000000000000000000000, // 'M'
+ 0b11010010000000000000000000000000, // 'N'
+ 0b11010100000000000000000000000000, // 'O'
+ 0b11010110000000000000000000000000, // 'P'
+ 0b11011000000000000000000000000000, // 'Q'
+ 0b11011010000000000000000000000000, // 'R'
+ 0b11011100000000000000000000000000, // 'S'
+ 0b11011110000000000000000000000000, // 'T'
+ 0b11100000000000000000000000000000, // 'U'
+ 0b11100010000000000000000000000000, // 'V'
+ 0b11100100000000000000000000000000, // 'W'
+ 0b11111100000000000000000000000000, // 'X'
+ 0b11100110000000000000000000000000, // 'Y'
+ 0b11111101000000000000000000000000, // 'Z'
+ 0b11111111110110000000000000000000, // '['
+ 0b11111111111111100000000000000000, // '\\'
+ 0b11111111111000000000000000000000, // ']'
+ 0b11111111111100000000000000000000, // '^'
+ 0b10001000000000000000000000000000, // '_'
+ 0b11111111111110100000000000000000, // '`'
+ 0b00011000000000000000000000000000, // 'a'
+ 0b10001100000000000000000000000000, // 'b'
+ 0b00100000000000000000000000000000, // 'c'
+ 0b10010000000000000000000000000000, // 'd'
+ 0b00101000000000000000000000000000, // 'e'
+ 0b10010100000000000000000000000000, // 'f'
+ 0b10011000000000000000000000000000, // 'g'
+ 0b10011100000000000000000000000000, // 'h'
+ 0b00110000000000000000000000000000, // 'i'
+ 0b11101000000000000000000000000000, // 'j'
+ 0b11101010000000000000000000000000, // 'k'
+ 0b10100000000000000000000000000000, // 'l'
+ 0b10100100000000000000000000000000, // 'm'
+ 0b10101000000000000000000000000000, // 'n'
+ 0b00111000000000000000000000000000, // 'o'
+ 0b10101100000000000000000000000000, // 'p'
+ 0b11101100000000000000000000000000, // 'q'
+ 0b10110000000000000000000000000000, // 'r'
+ 0b01000000000000000000000000000000, // 's'
+ 0b01001000000000000000000000000000, // 't'
+ 0b10110100000000000000000000000000, // 'u'
+ 0b11101110000000000000000000000000, // 'v'
+ 0b11110000000000000000000000000000, // 'w'
+ 0b11110010000000000000000000000000, // 'x'
+ 0b11110100000000000000000000000000, // 'y'
+ 0b11110110000000000000000000000000, // 'z'
+ 0b11111111111111000000000000000000, // '{'
+ 0b11111111100000000000000000000000, // '|'
+ 0b11111111111101000000000000000000, // '}'
+ 0b11111111111010000000000000000000, // '~'
+ 0b11111111111111111111111111000000, // 0x7f
+ 0b11111111111111100110000000000000, // 0x80
+ 0b11111111111111110100100000000000, // 0x81
+ 0b11111111111111100111000000000000, // 0x82
+ 0b11111111111111101000000000000000, // 0x83
+ 0b11111111111111110100110000000000, // 0x84
+ 0b11111111111111110101000000000000, // 0x85
+ 0b11111111111111110101010000000000, // 0x86
+ 0b11111111111111111011001000000000, // 0x87
+ 0b11111111111111110101100000000000, // 0x88
+ 0b11111111111111111011010000000000, // 0x89
+ 0b11111111111111111011011000000000, // 0x8a
+ 0b11111111111111111011100000000000, // 0x8b
+ 0b11111111111111111011101000000000, // 0x8c
+ 0b11111111111111111011110000000000, // 0x8d
+ 0b11111111111111111110101100000000, // 0x8e
+ 0b11111111111111111011111000000000, // 0x8f
+ 0b11111111111111111110110000000000, // 0x90
+ 0b11111111111111111110110100000000, // 0x91
+ 0b11111111111111110101110000000000, // 0x92
+ 0b11111111111111111100000000000000, // 0x93
+ 0b11111111111111111110111000000000, // 0x94
+ 0b11111111111111111100001000000000, // 0x95
+ 0b11111111111111111100010000000000, // 0x96
+ 0b11111111111111111100011000000000, // 0x97
+ 0b11111111111111111100100000000000, // 0x98
+ 0b11111111111111101110000000000000, // 0x99
+ 0b11111111111111110110000000000000, // 0x9a
+ 0b11111111111111111100101000000000, // 0x9b
+ 0b11111111111111110110010000000000, // 0x9c
+ 0b11111111111111111100110000000000, // 0x9d
+ 0b11111111111111111100111000000000, // 0x9e
+ 0b11111111111111111110111100000000, // 0x9f
+ 0b11111111111111110110100000000000, // 0xa0
+ 0b11111111111111101110100000000000, // 0xa1
+ 0b11111111111111101001000000000000, // 0xa2
+ 0b11111111111111110110110000000000, // 0xa3
+ 0b11111111111111110111000000000000, // 0xa4
+ 0b11111111111111111101000000000000, // 0xa5
+ 0b11111111111111111101001000000000, // 0xa6
+ 0b11111111111111101111000000000000, // 0xa7
+ 0b11111111111111111101010000000000, // 0xa8
+ 0b11111111111111110111010000000000, // 0xa9
+ 0b11111111111111110111100000000000, // 0xaa
+ 0b11111111111111111111000000000000, // 0xab
+ 0b11111111111111101111100000000000, // 0xac
+ 0b11111111111111110111110000000000, // 0xad
+ 0b11111111111111111101011000000000, // 0xae
+ 0b11111111111111111101100000000000, // 0xaf
+ 0b11111111111111110000000000000000, // 0xb0
+ 0b11111111111111110000100000000000, // 0xb1
+ 0b11111111111111111000000000000000, // 0xb2
+ 0b11111111111111110001000000000000, // 0xb3
+ 0b11111111111111111101101000000000, // 0xb4
+ 0b11111111111111111000010000000000, // 0xb5
+ 0b11111111111111111101110000000000, // 0xb6
+ 0b11111111111111111101111000000000, // 0xb7
+ 0b11111111111111101010000000000000, // 0xb8
+ 0b11111111111111111000100000000000, // 0xb9
+ 0b11111111111111111000110000000000, // 0xba
+ 0b11111111111111111001000000000000, // 0xbb
+ 0b11111111111111111110000000000000, // 0xbc
+ 0b11111111111111111001010000000000, // 0xbd
+ 0b11111111111111111001100000000000, // 0xbe
+ 0b11111111111111111110001000000000, // 0xbf
+ 0b11111111111111111111100000000000, // 0xc0
+ 0b11111111111111111111100001000000, // 0xc1
+ 0b11111111111111101011000000000000, // 0xc2
+ 0b11111111111111100010000000000000, // 0xc3
+ 0b11111111111111111001110000000000, // 0xc4
+ 0b11111111111111111110010000000000, // 0xc5
+ 0b11111111111111111010000000000000, // 0xc6
+ 0b11111111111111111111011000000000, // 0xc7
+ 0b11111111111111111111100010000000, // 0xc8
+ 0b11111111111111111111100011000000, // 0xc9
+ 0b11111111111111111111100100000000, // 0xca
+ 0b11111111111111111111101111000000, // 0xcb
+ 0b11111111111111111111101111100000, // 0xcc
+ 0b11111111111111111111100101000000, // 0xcd
+ 0b11111111111111111111000100000000, // 0xce
+ 0b11111111111111111111011010000000, // 0xcf
+ 0b11111111111111100100000000000000, // 0xd0
+ 0b11111111111111110001100000000000, // 0xd1
+ 0b11111111111111111111100110000000, // 0xd2
+ 0b11111111111111111111110000000000, // 0xd3
+ 0b11111111111111111111110000100000, // 0xd4
+ 0b11111111111111111111100111000000, // 0xd5
+ 0b11111111111111111111110001000000, // 0xd6
+ 0b11111111111111111111001000000000, // 0xd7
+ 0b11111111111111110010000000000000, // 0xd8
+ 0b11111111111111110010100000000000, // 0xd9
+ 0b11111111111111111111101000000000, // 0xda
+ 0b11111111111111111111101001000000, // 0xdb
+ 0b11111111111111111111111111010000, // 0xdc
+ 0b11111111111111111111110001100000, // 0xdd
+ 0b11111111111111111111110010000000, // 0xde
+ 0b11111111111111111111110010100000, // 0xdf
+ 0b11111111111111101100000000000000, // 0xe0
+ 0b11111111111111111111001100000000, // 0xe1
+ 0b11111111111111101101000000000000, // 0xe2
+ 0b11111111111111110011000000000000, // 0xe3
+ 0b11111111111111111010010000000000, // 0xe4
+ 0b11111111111111110011100000000000, // 0xe5
+ 0b11111111111111110100000000000000, // 0xe6
+ 0b11111111111111111110011000000000, // 0xe7
+ 0b11111111111111111010100000000000, // 0xe8
+ 0b11111111111111111010110000000000, // 0xe9
+ 0b11111111111111111111011100000000, // 0xea
+ 0b11111111111111111111011110000000, // 0xeb
+ 0b11111111111111111111010000000000, // 0xec
+ 0b11111111111111111111010100000000, // 0xed
+ 0b11111111111111111111101010000000, // 0xee
+ 0b11111111111111111110100000000000, // 0xef
+ 0b11111111111111111111101011000000, // 0xf0
+ 0b11111111111111111111110011000000, // 0xf1
+ 0b11111111111111111111101100000000, // 0xf2
+ 0b11111111111111111111101101000000, // 0xf3
+ 0b11111111111111111111110011100000, // 0xf4
+ 0b11111111111111111111110100000000, // 0xf5
+ 0b11111111111111111111110100100000, // 0xf6
+ 0b11111111111111111111110101000000, // 0xf7
+ 0b11111111111111111111110101100000, // 0xf8
+ 0b11111111111111111111111111100000, // 0xf9
+ 0b11111111111111111111110110000000, // 0xfa
+ 0b11111111111111111111110110100000, // 0xfb
+ 0b11111111111111111111110111000000, // 0xfc
+ 0b11111111111111111111110111100000, // 0xfd
+ 0b11111111111111111111111000000000, // 0xfe
+ 0b11111111111111111111101110000000, // 0xff
+ 0b11111111111111111111111111111100, // 0x100
+};
+
+// static
+const uint32_t HuffmanSpecTables::kRightCodes[] = {
+ 0b00000000000000000001111111111000, // 0x00
+ 0b00000000011111111111111111011000, // 0x01
+ 0b00001111111111111111111111100010, // 0x02
+ 0b00001111111111111111111111100011, // 0x03
+ 0b00001111111111111111111111100100, // 0x04
+ 0b00001111111111111111111111100101, // 0x05
+ 0b00001111111111111111111111100110, // 0x06
+ 0b00001111111111111111111111100111, // 0x07
+ 0b00001111111111111111111111101000, // 0x08
+ 0b00000000111111111111111111101010, // 0x09
+ 0b00111111111111111111111111111100, // 0x0a
+ 0b00001111111111111111111111101001, // 0x0b
+ 0b00001111111111111111111111101010, // 0x0c
+ 0b00111111111111111111111111111101, // 0x0d
+ 0b00001111111111111111111111101011, // 0x0e
+ 0b00001111111111111111111111101100, // 0x0f
+ 0b00001111111111111111111111101101, // 0x10
+ 0b00001111111111111111111111101110, // 0x11
+ 0b00001111111111111111111111101111, // 0x12
+ 0b00001111111111111111111111110000, // 0x13
+ 0b00001111111111111111111111110001, // 0x14
+ 0b00001111111111111111111111110010, // 0x15
+ 0b00111111111111111111111111111110, // 0x16
+ 0b00001111111111111111111111110011, // 0x17
+ 0b00001111111111111111111111110100, // 0x18
+ 0b00001111111111111111111111110101, // 0x19
+ 0b00001111111111111111111111110110, // 0x1a
+ 0b00001111111111111111111111110111, // 0x1b
+ 0b00001111111111111111111111111000, // 0x1c
+ 0b00001111111111111111111111111001, // 0x1d
+ 0b00001111111111111111111111111010, // 0x1e
+ 0b00001111111111111111111111111011, // 0x1f
+ 0b00000000000000000000000000010100, // 0x20
+ 0b00000000000000000000001111111000, // '!'
+ 0b00000000000000000000001111111001, // '\"'
+ 0b00000000000000000000111111111010, // '#'
+ 0b00000000000000000001111111111001, // '$'
+ 0b00000000000000000000000000010101, // '%'
+ 0b00000000000000000000000011111000, // '&'
+ 0b00000000000000000000011111111010, // '\''
+ 0b00000000000000000000001111111010, // '('
+ 0b00000000000000000000001111111011, // ')'
+ 0b00000000000000000000000011111001, // '*'
+ 0b00000000000000000000011111111011, // '+'
+ 0b00000000000000000000000011111010, // ','
+ 0b00000000000000000000000000010110, // '-'
+ 0b00000000000000000000000000010111, // '.'
+ 0b00000000000000000000000000011000, // '/'
+ 0b00000000000000000000000000000000, // '0'
+ 0b00000000000000000000000000000001, // '1'
+ 0b00000000000000000000000000000010, // '2'
+ 0b00000000000000000000000000011001, // '3'
+ 0b00000000000000000000000000011010, // '4'
+ 0b00000000000000000000000000011011, // '5'
+ 0b00000000000000000000000000011100, // '6'
+ 0b00000000000000000000000000011101, // '7'
+ 0b00000000000000000000000000011110, // '8'
+ 0b00000000000000000000000000011111, // '9'
+ 0b00000000000000000000000001011100, // ':'
+ 0b00000000000000000000000011111011, // ';'
+ 0b00000000000000000111111111111100, // '<'
+ 0b00000000000000000000000000100000, // '='
+ 0b00000000000000000000111111111011, // '>'
+ 0b00000000000000000000001111111100, // '?'
+ 0b00000000000000000001111111111010, // '@'
+ 0b00000000000000000000000000100001, // 'A'
+ 0b00000000000000000000000001011101, // 'B'
+ 0b00000000000000000000000001011110, // 'C'
+ 0b00000000000000000000000001011111, // 'D'
+ 0b00000000000000000000000001100000, // 'E'
+ 0b00000000000000000000000001100001, // 'F'
+ 0b00000000000000000000000001100010, // 'G'
+ 0b00000000000000000000000001100011, // 'H'
+ 0b00000000000000000000000001100100, // 'I'
+ 0b00000000000000000000000001100101, // 'J'
+ 0b00000000000000000000000001100110, // 'K'
+ 0b00000000000000000000000001100111, // 'L'
+ 0b00000000000000000000000001101000, // 'M'
+ 0b00000000000000000000000001101001, // 'N'
+ 0b00000000000000000000000001101010, // 'O'
+ 0b00000000000000000000000001101011, // 'P'
+ 0b00000000000000000000000001101100, // 'Q'
+ 0b00000000000000000000000001101101, // 'R'
+ 0b00000000000000000000000001101110, // 'S'
+ 0b00000000000000000000000001101111, // 'T'
+ 0b00000000000000000000000001110000, // 'U'
+ 0b00000000000000000000000001110001, // 'V'
+ 0b00000000000000000000000001110010, // 'W'
+ 0b00000000000000000000000011111100, // 'X'
+ 0b00000000000000000000000001110011, // 'Y'
+ 0b00000000000000000000000011111101, // 'Z'
+ 0b00000000000000000001111111111011, // '['
+ 0b00000000000001111111111111110000, // '\\'
+ 0b00000000000000000001111111111100, // ']'
+ 0b00000000000000000011111111111100, // '^'
+ 0b00000000000000000000000000100010, // '_'
+ 0b00000000000000000111111111111101, // '`'
+ 0b00000000000000000000000000000011, // 'a'
+ 0b00000000000000000000000000100011, // 'b'
+ 0b00000000000000000000000000000100, // 'c'
+ 0b00000000000000000000000000100100, // 'd'
+ 0b00000000000000000000000000000101, // 'e'
+ 0b00000000000000000000000000100101, // 'f'
+ 0b00000000000000000000000000100110, // 'g'
+ 0b00000000000000000000000000100111, // 'h'
+ 0b00000000000000000000000000000110, // 'i'
+ 0b00000000000000000000000001110100, // 'j'
+ 0b00000000000000000000000001110101, // 'k'
+ 0b00000000000000000000000000101000, // 'l'
+ 0b00000000000000000000000000101001, // 'm'
+ 0b00000000000000000000000000101010, // 'n'
+ 0b00000000000000000000000000000111, // 'o'
+ 0b00000000000000000000000000101011, // 'p'
+ 0b00000000000000000000000001110110, // 'q'
+ 0b00000000000000000000000000101100, // 'r'
+ 0b00000000000000000000000000001000, // 's'
+ 0b00000000000000000000000000001001, // 't'
+ 0b00000000000000000000000000101101, // 'u'
+ 0b00000000000000000000000001110111, // 'v'
+ 0b00000000000000000000000001111000, // 'w'
+ 0b00000000000000000000000001111001, // 'x'
+ 0b00000000000000000000000001111010, // 'y'
+ 0b00000000000000000000000001111011, // 'z'
+ 0b00000000000000000111111111111110, // '{'
+ 0b00000000000000000000011111111100, // '|'
+ 0b00000000000000000011111111111101, // '}'
+ 0b00000000000000000001111111111101, // '~'
+ 0b00001111111111111111111111111100, // 0x7f
+ 0b00000000000011111111111111100110, // 0x80
+ 0b00000000001111111111111111010010, // 0x81
+ 0b00000000000011111111111111100111, // 0x82
+ 0b00000000000011111111111111101000, // 0x83
+ 0b00000000001111111111111111010011, // 0x84
+ 0b00000000001111111111111111010100, // 0x85
+ 0b00000000001111111111111111010101, // 0x86
+ 0b00000000011111111111111111011001, // 0x87
+ 0b00000000001111111111111111010110, // 0x88
+ 0b00000000011111111111111111011010, // 0x89
+ 0b00000000011111111111111111011011, // 0x8a
+ 0b00000000011111111111111111011100, // 0x8b
+ 0b00000000011111111111111111011101, // 0x8c
+ 0b00000000011111111111111111011110, // 0x8d
+ 0b00000000111111111111111111101011, // 0x8e
+ 0b00000000011111111111111111011111, // 0x8f
+ 0b00000000111111111111111111101100, // 0x90
+ 0b00000000111111111111111111101101, // 0x91
+ 0b00000000001111111111111111010111, // 0x92
+ 0b00000000011111111111111111100000, // 0x93
+ 0b00000000111111111111111111101110, // 0x94
+ 0b00000000011111111111111111100001, // 0x95
+ 0b00000000011111111111111111100010, // 0x96
+ 0b00000000011111111111111111100011, // 0x97
+ 0b00000000011111111111111111100100, // 0x98
+ 0b00000000000111111111111111011100, // 0x99
+ 0b00000000001111111111111111011000, // 0x9a
+ 0b00000000011111111111111111100101, // 0x9b
+ 0b00000000001111111111111111011001, // 0x9c
+ 0b00000000011111111111111111100110, // 0x9d
+ 0b00000000011111111111111111100111, // 0x9e
+ 0b00000000111111111111111111101111, // 0x9f
+ 0b00000000001111111111111111011010, // 0xa0
+ 0b00000000000111111111111111011101, // 0xa1
+ 0b00000000000011111111111111101001, // 0xa2
+ 0b00000000001111111111111111011011, // 0xa3
+ 0b00000000001111111111111111011100, // 0xa4
+ 0b00000000011111111111111111101000, // 0xa5
+ 0b00000000011111111111111111101001, // 0xa6
+ 0b00000000000111111111111111011110, // 0xa7
+ 0b00000000011111111111111111101010, // 0xa8
+ 0b00000000001111111111111111011101, // 0xa9
+ 0b00000000001111111111111111011110, // 0xaa
+ 0b00000000111111111111111111110000, // 0xab
+ 0b00000000000111111111111111011111, // 0xac
+ 0b00000000001111111111111111011111, // 0xad
+ 0b00000000011111111111111111101011, // 0xae
+ 0b00000000011111111111111111101100, // 0xaf
+ 0b00000000000111111111111111100000, // 0xb0
+ 0b00000000000111111111111111100001, // 0xb1
+ 0b00000000001111111111111111100000, // 0xb2
+ 0b00000000000111111111111111100010, // 0xb3
+ 0b00000000011111111111111111101101, // 0xb4
+ 0b00000000001111111111111111100001, // 0xb5
+ 0b00000000011111111111111111101110, // 0xb6
+ 0b00000000011111111111111111101111, // 0xb7
+ 0b00000000000011111111111111101010, // 0xb8
+ 0b00000000001111111111111111100010, // 0xb9
+ 0b00000000001111111111111111100011, // 0xba
+ 0b00000000001111111111111111100100, // 0xbb
+ 0b00000000011111111111111111110000, // 0xbc
+ 0b00000000001111111111111111100101, // 0xbd
+ 0b00000000001111111111111111100110, // 0xbe
+ 0b00000000011111111111111111110001, // 0xbf
+ 0b00000011111111111111111111100000, // 0xc0
+ 0b00000011111111111111111111100001, // 0xc1
+ 0b00000000000011111111111111101011, // 0xc2
+ 0b00000000000001111111111111110001, // 0xc3
+ 0b00000000001111111111111111100111, // 0xc4
+ 0b00000000011111111111111111110010, // 0xc5
+ 0b00000000001111111111111111101000, // 0xc6
+ 0b00000001111111111111111111101100, // 0xc7
+ 0b00000011111111111111111111100010, // 0xc8
+ 0b00000011111111111111111111100011, // 0xc9
+ 0b00000011111111111111111111100100, // 0xca
+ 0b00000111111111111111111111011110, // 0xcb
+ 0b00000111111111111111111111011111, // 0xcc
+ 0b00000011111111111111111111100101, // 0xcd
+ 0b00000000111111111111111111110001, // 0xce
+ 0b00000001111111111111111111101101, // 0xcf
+ 0b00000000000001111111111111110010, // 0xd0
+ 0b00000000000111111111111111100011, // 0xd1
+ 0b00000011111111111111111111100110, // 0xd2
+ 0b00000111111111111111111111100000, // 0xd3
+ 0b00000111111111111111111111100001, // 0xd4
+ 0b00000011111111111111111111100111, // 0xd5
+ 0b00000111111111111111111111100010, // 0xd6
+ 0b00000000111111111111111111110010, // 0xd7
+ 0b00000000000111111111111111100100, // 0xd8
+ 0b00000000000111111111111111100101, // 0xd9
+ 0b00000011111111111111111111101000, // 0xda
+ 0b00000011111111111111111111101001, // 0xdb
+ 0b00001111111111111111111111111101, // 0xdc
+ 0b00000111111111111111111111100011, // 0xdd
+ 0b00000111111111111111111111100100, // 0xde
+ 0b00000111111111111111111111100101, // 0xdf
+ 0b00000000000011111111111111101100, // 0xe0
+ 0b00000000111111111111111111110011, // 0xe1
+ 0b00000000000011111111111111101101, // 0xe2
+ 0b00000000000111111111111111100110, // 0xe3
+ 0b00000000001111111111111111101001, // 0xe4
+ 0b00000000000111111111111111100111, // 0xe5
+ 0b00000000000111111111111111101000, // 0xe6
+ 0b00000000011111111111111111110011, // 0xe7
+ 0b00000000001111111111111111101010, // 0xe8
+ 0b00000000001111111111111111101011, // 0xe9
+ 0b00000001111111111111111111101110, // 0xea
+ 0b00000001111111111111111111101111, // 0xeb
+ 0b00000000111111111111111111110100, // 0xec
+ 0b00000000111111111111111111110101, // 0xed
+ 0b00000011111111111111111111101010, // 0xee
+ 0b00000000011111111111111111110100, // 0xef
+ 0b00000011111111111111111111101011, // 0xf0
+ 0b00000111111111111111111111100110, // 0xf1
+ 0b00000011111111111111111111101100, // 0xf2
+ 0b00000011111111111111111111101101, // 0xf3
+ 0b00000111111111111111111111100111, // 0xf4
+ 0b00000111111111111111111111101000, // 0xf5
+ 0b00000111111111111111111111101001, // 0xf6
+ 0b00000111111111111111111111101010, // 0xf7
+ 0b00000111111111111111111111101011, // 0xf8
+ 0b00001111111111111111111111111110, // 0xf9
+ 0b00000111111111111111111111101100, // 0xfa
+ 0b00000111111111111111111111101101, // 0xfb
+ 0b00000111111111111111111111101110, // 0xfc
+ 0b00000111111111111111111111101111, // 0xfd
+ 0b00000111111111111111111111110000, // 0xfe
+ 0b00000011111111111111111111101110, // 0xff
+ 0b00111111111111111111111111111111, // 0x100
+};
+// clang-format off
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/huffman_spec_tables.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/huffman_spec_tables.h
new file mode 100644
index 00000000000..d1b144b1358
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/huffman/huffman_spec_tables.h
@@ -0,0 +1,29 @@
+// Copyright (c) 2018 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_HUFFMAN_HUFFMAN_SPEC_TABLES_H_
+#define QUICHE_HTTP2_HPACK_HUFFMAN_HUFFMAN_SPEC_TABLES_H_
+
+// Tables describing the Huffman encoding of bytes as specified by RFC7541.
+
+#include <cstdint>
+
+namespace http2 {
+
+struct HuffmanSpecTables {
+ // Number of bits in the encoding of each symbol (byte).
+ static const uint8_t kCodeLengths[257];
+
+ // The encoding of each symbol, right justified (as printed), which means that
+ // the last bit of the encoding is the low-order bit of the uint32.
+ static const uint32_t kRightCodes[257];
+
+ // The encoding of each symbol, left justified (as printed), which means that
+ // the first bit of the encoding is the high-order bit of the uint32.
+ static const uint32_t kLeftCodes[257];
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_HUFFMAN_HUFFMAN_SPEC_TABLES_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder.cc
new file mode 100644
index 00000000000..d958b7c7832
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder.cc
@@ -0,0 +1,66 @@
+// 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 "quiche/http2/hpack/tools/hpack_block_builder.h"
+
+#include "quiche/http2/hpack/varint/hpack_varint_encoder.h"
+#include "quiche/http2/platform/api/http2_bug_tracker.h"
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace test {
+
+void HpackBlockBuilder::AppendHighBitsAndVarint(uint8_t high_bits,
+ uint8_t prefix_length,
+ uint64_t varint) {
+ EXPECT_LE(3, prefix_length);
+ EXPECT_LE(prefix_length, 8);
+
+ HpackVarintEncoder::Encode(high_bits, prefix_length, varint, &buffer_);
+}
+
+void HpackBlockBuilder::AppendEntryTypeAndVarint(HpackEntryType entry_type,
+ uint64_t varint) {
+ uint8_t high_bits;
+ uint8_t prefix_length; // Bits of the varint prefix in the first byte.
+ switch (entry_type) {
+ case HpackEntryType::kIndexedHeader:
+ high_bits = 0x80;
+ prefix_length = 7;
+ break;
+ case HpackEntryType::kDynamicTableSizeUpdate:
+ high_bits = 0x20;
+ prefix_length = 5;
+ break;
+ case HpackEntryType::kIndexedLiteralHeader:
+ high_bits = 0x40;
+ prefix_length = 6;
+ break;
+ case HpackEntryType::kUnindexedLiteralHeader:
+ high_bits = 0x00;
+ prefix_length = 4;
+ break;
+ case HpackEntryType::kNeverIndexedLiteralHeader:
+ high_bits = 0x10;
+ prefix_length = 4;
+ break;
+ default:
+ HTTP2_BUG(http2_bug_110_1) << "Unreached, entry_type=" << entry_type;
+ high_bits = 0;
+ prefix_length = 0;
+ break;
+ }
+ AppendHighBitsAndVarint(high_bits, prefix_length, varint);
+}
+
+void HpackBlockBuilder::AppendString(bool is_huffman_encoded,
+ absl::string_view str) {
+ uint8_t high_bits = is_huffman_encoded ? 0x80 : 0;
+ uint8_t prefix_length = 7;
+ AppendHighBitsAndVarint(high_bits, prefix_length, str.size());
+ buffer_.append(str.data(), str.size());
+}
+
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder.h
new file mode 100644
index 00000000000..c385a7d9a13
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder.h
@@ -0,0 +1,98 @@
+// 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_TOOLS_HPACK_BLOCK_BUILDER_H_
+#define QUICHE_HTTP2_HPACK_TOOLS_HPACK_BLOCK_BUILDER_H_
+
+// HpackBlockBuilder builds wire-format HPACK blocks (or fragments thereof)
+// from components.
+
+// Supports very large varints to enable tests to create HPACK blocks with
+// values that the decoder should reject. For now, this is only intended for
+// use in tests, and thus has EXPECT* in the code. If desired to use it in an
+// encoder, it will need optimization work, especially w.r.t memory mgmt, and
+// the EXPECT* will need to be removed or replaced with QUICHE_DCHECKs. And of
+// course the support for very large varints will not be needed in production
+// code.
+
+#include <stddef.h>
+
+#include <cstdint>
+#include <string>
+
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/http2_hpack_constants.h"
+#include "quiche/common/platform/api/quiche_export.h"
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace test {
+
+class QUICHE_NO_EXPORT HpackBlockBuilder {
+ public:
+ explicit HpackBlockBuilder(absl::string_view initial_contents)
+ : buffer_(initial_contents.data(), initial_contents.size()) {}
+ HpackBlockBuilder() {}
+ ~HpackBlockBuilder() {}
+
+ size_t size() const { return buffer_.size(); }
+ const std::string& buffer() const { return buffer_; }
+
+ //----------------------------------------------------------------------------
+ // Methods for appending a valid HPACK entry.
+
+ void AppendIndexedHeader(uint64_t index) {
+ AppendEntryTypeAndVarint(HpackEntryType::kIndexedHeader, index);
+ }
+
+ void AppendDynamicTableSizeUpdate(uint64_t size) {
+ AppendEntryTypeAndVarint(HpackEntryType::kDynamicTableSizeUpdate, size);
+ }
+
+ void AppendNameIndexAndLiteralValue(HpackEntryType entry_type,
+ uint64_t name_index,
+ bool value_is_huffman_encoded,
+ absl::string_view value) {
+ // name_index==0 would indicate that the entry includes a literal name.
+ // Call AppendLiteralNameAndValue in that case.
+ EXPECT_NE(0u, name_index);
+ AppendEntryTypeAndVarint(entry_type, name_index);
+ AppendString(value_is_huffman_encoded, value);
+ }
+
+ void AppendLiteralNameAndValue(HpackEntryType entry_type,
+ bool name_is_huffman_encoded,
+ absl::string_view name,
+ bool value_is_huffman_encoded,
+ absl::string_view value) {
+ AppendEntryTypeAndVarint(entry_type, 0);
+ AppendString(name_is_huffman_encoded, name);
+ AppendString(value_is_huffman_encoded, value);
+ }
+
+ //----------------------------------------------------------------------------
+ // Primitive methods that are not guaranteed to write a valid HPACK entry.
+
+ // Appends a varint, with the specified high_bits above the prefix of the
+ // varint.
+ void AppendHighBitsAndVarint(uint8_t high_bits,
+ uint8_t prefix_length,
+ uint64_t varint);
+
+ // Append the start of an HPACK entry for the specified type, with the
+ // specified varint.
+ void AppendEntryTypeAndVarint(HpackEntryType entry_type, uint64_t varint);
+
+ // Append a header string (i.e. a header name or value) in HPACK format.
+ // Does NOT perform Huffman encoding.
+ void AppendString(bool is_huffman_encoded, absl::string_view str);
+
+ private:
+ std::string buffer_;
+};
+
+} // namespace test
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_TOOLS_HPACK_BLOCK_BUILDER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder_test.cc
new file mode 100644
index 00000000000..26e42ff844e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_block_builder_test.cc
@@ -0,0 +1,169 @@
+// 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 "quiche/http2/hpack/tools/hpack_block_builder.h"
+
+#include "absl/strings/escaping.h"
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace test {
+namespace {
+const bool kUncompressed = false;
+const bool kCompressed = true;
+
+// TODO(jamessynge): Once static table code is checked in, switch to using
+// constants from there.
+const uint32_t kStaticTableMethodGET = 2;
+const uint32_t kStaticTablePathSlash = 4;
+const uint32_t kStaticTableSchemeHttp = 6;
+
+// Tests of encoding per the RFC. See:
+// http://httpwg.org/specs/rfc7541.html#header.field.representation.examples
+// The expected values have been copied from the RFC.
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC2) {
+ {
+ HpackBlockBuilder b;
+ b.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader,
+ kUncompressed, "custom-key", kUncompressed,
+ "custom-header");
+ EXPECT_EQ(26u, b.size());
+
+ const char kExpected[] =
+ "\x40" // == Literal indexed ==
+ "\x0a" // Name length (10)
+ "custom-key" // Name
+ "\x0d" // Value length (13)
+ "custom-header"; // Value
+ EXPECT_EQ(kExpected, b.buffer());
+ }
+ {
+ HpackBlockBuilder b;
+ b.AppendNameIndexAndLiteralValue(HpackEntryType::kUnindexedLiteralHeader, 4,
+ kUncompressed, "/sample/path");
+ EXPECT_EQ(14u, b.size());
+
+ const char kExpected[] =
+ "\x04" // == Literal unindexed, name index 0x04 ==
+ "\x0c" // Value length (12)
+ "/sample/path"; // Value
+ EXPECT_EQ(kExpected, b.buffer());
+ }
+ {
+ HpackBlockBuilder b;
+ b.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
+ kUncompressed, "password", kUncompressed,
+ "secret");
+ EXPECT_EQ(17u, b.size());
+
+ const char kExpected[] =
+ "\x10" // == Literal never indexed ==
+ "\x08" // Name length (8)
+ "password" // Name
+ "\x06" // Value length (6)
+ "secret"; // Value
+ EXPECT_EQ(kExpected, b.buffer());
+ }
+ {
+ HpackBlockBuilder b;
+ b.AppendIndexedHeader(2);
+ EXPECT_EQ(1u, b.size());
+
+ const char kExpected[] = "\x82"; // == Indexed (2) ==
+ EXPECT_EQ(kExpected, b.buffer());
+ }
+}
+
+// Tests of encoding per the RFC. See:
+// http://httpwg.org/specs/rfc7541.html#request.examples.without.huffman.coding
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC3) {
+ {
+ // Header block to encode:
+ // :method: GET
+ // :scheme: http
+ // :path: /
+ // :authority: www.example.com
+ HpackBlockBuilder b;
+ b.AppendIndexedHeader(2); // :method: GET
+ b.AppendIndexedHeader(6); // :scheme: http
+ b.AppendIndexedHeader(4); // :path: /
+ b.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 1,
+ kUncompressed, "www.example.com");
+ EXPECT_EQ(20u, b.size());
+
+ // Hex dump of encoded data (copied from RFC):
+ // 0x0000: 8286 8441 0f77 7777 2e65 7861 6d70 6c65 ...A.www.example
+ // 0x0010: 2e63 6f6d .com
+
+ const std::string expected =
+ absl::HexStringToBytes("828684410f7777772e6578616d706c652e636f6d");
+ EXPECT_EQ(expected, b.buffer());
+ }
+}
+
+// Tests of encoding per the RFC. See:
+// http://httpwg.org/specs/rfc7541.html#request.examples.with.huffman.coding
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC4) {
+ {
+ // Header block to encode:
+ // :method: GET
+ // :scheme: http
+ // :path: /
+ // :authority: www.example.com (Huffman encoded)
+ HpackBlockBuilder b;
+ b.AppendIndexedHeader(kStaticTableMethodGET);
+ b.AppendIndexedHeader(kStaticTableSchemeHttp);
+ b.AppendIndexedHeader(kStaticTablePathSlash);
+ const char kHuffmanWwwExampleCom[] = {'\xf1', '\xe3', '\xc2', '\xe5',
+ '\xf2', '\x3a', '\x6b', '\xa0',
+ '\xab', '\x90', '\xf4', '\xff'};
+ b.AppendNameIndexAndLiteralValue(
+ HpackEntryType::kIndexedLiteralHeader, 1, kCompressed,
+ absl::string_view(kHuffmanWwwExampleCom, sizeof kHuffmanWwwExampleCom));
+ EXPECT_EQ(17u, b.size());
+
+ // Hex dump of encoded data (copied from RFC):
+ // 0x0000: 8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ...A......:k....
+ // 0x0010: ff .
+
+ const std::string expected =
+ absl::HexStringToBytes("828684418cf1e3c2e5f23a6ba0ab90f4ff");
+ EXPECT_EQ(expected, b.buffer());
+ }
+}
+
+TEST(HpackBlockBuilderTest, DynamicTableSizeUpdate) {
+ {
+ HpackBlockBuilder b;
+ b.AppendDynamicTableSizeUpdate(0);
+ EXPECT_EQ(1u, b.size());
+
+ const char kData[] = {'\x20'};
+ absl::string_view expected(kData, sizeof kData);
+ EXPECT_EQ(expected, b.buffer());
+ }
+ {
+ HpackBlockBuilder b;
+ b.AppendDynamicTableSizeUpdate(4096); // The default size.
+ EXPECT_EQ(3u, b.size());
+
+ const char kData[] = {'\x3f', '\xe1', '\x1f'};
+ absl::string_view expected(kData, sizeof kData);
+ EXPECT_EQ(expected, b.buffer());
+ }
+ {
+ HpackBlockBuilder b;
+ b.AppendDynamicTableSizeUpdate(1000000000000); // A very large value.
+ EXPECT_EQ(7u, b.size());
+
+ const char kData[] = {'\x3f', '\xe1', '\x9f', '\x94',
+ '\xa5', '\x8d', '\x1d'};
+ absl::string_view expected(kData, sizeof kData);
+ EXPECT_EQ(expected, b.buffer());
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_example.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_example.cc
new file mode 100644
index 00000000000..c6e6d2d66c1
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_example.cc
@@ -0,0 +1,59 @@
+// 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 "quiche/http2/hpack/tools/hpack_example.h"
+
+#include <ctype.h>
+
+#include "absl/strings/escaping.h"
+#include "absl/strings/str_cat.h"
+#include "quiche/http2/platform/api/http2_bug_tracker.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+
+namespace http2 {
+namespace test {
+namespace {
+
+void HpackExampleToStringOrDie(absl::string_view example, std::string* output) {
+ while (!example.empty()) {
+ const char c0 = example[0];
+ if (isxdigit(c0)) {
+ QUICHE_CHECK_GT(example.size(), 1u) << "Truncated hex byte?";
+ const char c1 = example[1];
+ QUICHE_CHECK(isxdigit(c1)) << "Found half a byte?";
+ *output += absl::HexStringToBytes(example.substr(0, 2));
+ example.remove_prefix(2);
+ continue;
+ }
+ if (isspace(c0)) {
+ example.remove_prefix(1);
+ continue;
+ }
+ if (!example.empty() && example[0] == '|') {
+ // Start of a comment. Skip to end of line or of input.
+ auto pos = example.find('\n');
+ if (pos == absl::string_view::npos) {
+ // End of input.
+ break;
+ }
+ example.remove_prefix(pos + 1);
+ continue;
+ }
+ HTTP2_BUG(http2_bug_107_1)
+ << "Can't parse byte " << static_cast<int>(c0)
+ << absl::StrCat(" (0x", absl::Hex(c0), ")") << "\nExample: " << example;
+ }
+ QUICHE_CHECK_LT(0u, output->size()) << "Example is empty.";
+}
+
+} // namespace
+
+std::string HpackExampleToStringOrDie(absl::string_view example) {
+ std::string output;
+ HpackExampleToStringOrDie(example, &output);
+ return output;
+}
+
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_example.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_example.h
new file mode 100644
index 00000000000..de203cccbde
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/tools/hpack_example.h
@@ -0,0 +1,32 @@
+// 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_TOOLS_HPACK_EXAMPLE_H_
+#define QUICHE_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_
+
+#include <string>
+
+#include "absl/strings/string_view.h"
+
+// Parses HPACK examples in the format seen in the HPACK specification,
+// RFC 7541. For example:
+//
+// 10 | == Literal never indexed ==
+// 08 | Literal name (len = 8)
+// 7061 7373 776f 7264 | password
+// 06 | Literal value (len = 6)
+// 7365 6372 6574 | secret
+// | -> password: secret
+//
+// (excluding the leading "//").
+
+namespace http2 {
+namespace test {
+
+std::string HpackExampleToStringOrDie(absl::string_view example);
+
+} // namespace test
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc
new file mode 100644
index 00000000000..33348073f44
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.cc
@@ -0,0 +1,143 @@
+// 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 "quiche/http2/hpack/varint/hpack_varint_decoder.h"
+
+#include "absl/strings/str_cat.h"
+
+namespace http2 {
+
+DecodeStatus HpackVarintDecoder::Start(uint8_t prefix_value,
+ uint8_t prefix_length,
+ DecodeBuffer* db) {
+ QUICHE_DCHECK_LE(3u, prefix_length);
+ QUICHE_DCHECK_LE(prefix_length, 8u);
+
+ // |prefix_mask| defines the sequence of low-order bits of the first byte
+ // that encode the prefix of the value. It is also the marker in those bits
+ // of the first byte indicating that at least one extension byte is needed.
+ const uint8_t prefix_mask = (1 << prefix_length) - 1;
+
+ // Ignore the bits that aren't a part of the prefix of the varint.
+ value_ = prefix_value & prefix_mask;
+
+ if (value_ < prefix_mask) {
+ MarkDone();
+ return DecodeStatus::kDecodeDone;
+ }
+
+ offset_ = 0;
+ return Resume(db);
+}
+
+DecodeStatus HpackVarintDecoder::StartExtended(uint8_t prefix_length,
+ DecodeBuffer* db) {
+ QUICHE_DCHECK_LE(3u, prefix_length);
+ QUICHE_DCHECK_LE(prefix_length, 8u);
+
+ value_ = (1 << prefix_length) - 1;
+ offset_ = 0;
+ return Resume(db);
+}
+
+DecodeStatus HpackVarintDecoder::Resume(DecodeBuffer* db) {
+ // There can be at most 10 continuation bytes. Offset is zero for the
+ // first one and increases by 7 for each subsequent one.
+ const uint8_t kMaxOffset = 63;
+ CheckNotDone();
+
+ // Process most extension bytes without the need for overflow checking.
+ while (offset_ < kMaxOffset) {
+ if (db->Empty()) {
+ return DecodeStatus::kDecodeInProgress;
+ }
+
+ uint8_t byte = db->DecodeUInt8();
+ uint64_t summand = byte & 0x7f;
+
+ // Shifting a 7 bit value to the left by at most 56 places can never
+ // overflow on uint64_t.
+ QUICHE_DCHECK_LE(offset_, 56);
+ QUICHE_DCHECK_LE(summand, std::numeric_limits<uint64_t>::max() >> offset_);
+
+ summand <<= offset_;
+
+ // At this point,
+ // |value_| is at most (2^prefix_length - 1) + (2^49 - 1), and
+ // |summand| is at most 255 << 56 (which is smaller than 2^63),
+ // so adding them can never overflow on uint64_t.
+ QUICHE_DCHECK_LE(value_, std::numeric_limits<uint64_t>::max() - summand);
+
+ value_ += summand;
+
+ // Decoding ends if continuation flag is not set.
+ if ((byte & 0x80) == 0) {
+ MarkDone();
+ return DecodeStatus::kDecodeDone;
+ }
+
+ offset_ += 7;
+ }
+
+ if (db->Empty()) {
+ return DecodeStatus::kDecodeInProgress;
+ }
+
+ QUICHE_DCHECK_EQ(kMaxOffset, offset_);
+
+ uint8_t byte = db->DecodeUInt8();
+ // No more extension bytes are allowed after this.
+ if ((byte & 0x80) == 0) {
+ uint64_t summand = byte & 0x7f;
+ // Check for overflow in left shift.
+ if (summand <= std::numeric_limits<uint64_t>::max() >> offset_) {
+ summand <<= offset_;
+ // Check for overflow in addition.
+ if (value_ <= std::numeric_limits<uint64_t>::max() - summand) {
+ value_ += summand;
+ MarkDone();
+ return DecodeStatus::kDecodeDone;
+ }
+ }
+ }
+
+ // Signal error if value is too large or there are too many extension bytes.
+ HTTP2_DLOG(WARNING)
+ << "Variable length int encoding is too large or too long. "
+ << DebugString();
+ MarkDone();
+ return DecodeStatus::kDecodeError;
+}
+
+uint64_t HpackVarintDecoder::value() const {
+ CheckDone();
+ return value_;
+}
+
+void HpackVarintDecoder::set_value(uint64_t v) {
+ MarkDone();
+ value_ = v;
+}
+
+std::string HpackVarintDecoder::DebugString() const {
+ return absl::StrCat("HpackVarintDecoder(value=", value_, ", offset=", offset_,
+ ")");
+}
+
+DecodeStatus HpackVarintDecoder::StartForTest(uint8_t prefix_value,
+ uint8_t prefix_length,
+ DecodeBuffer* db) {
+ return Start(prefix_value, prefix_length, db);
+}
+
+DecodeStatus HpackVarintDecoder::StartExtendedForTest(uint8_t prefix_length,
+ DecodeBuffer* db) {
+ return StartExtended(prefix_length, db);
+}
+
+DecodeStatus HpackVarintDecoder::ResumeForTest(DecodeBuffer* db) {
+ return Resume(db);
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.h
new file mode 100644
index 00000000000..d2c8feb3abb
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder.h
@@ -0,0 +1,128 @@
+// 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.
+
+// HpackVarintDecoder decodes HPACK variable length unsigned integers. In HPACK,
+// these integers are used to identify static or dynamic table index entries, to
+// specify string lengths, and to update the size limit of the dynamic table.
+// In QPACK, in addition to these uses, these integers also identify streams.
+//
+// The caller will need to validate that the decoded value is in an acceptable
+// range.
+//
+// For details of the encoding, see:
+// http://httpwg.org/specs/rfc7541.html#integer.representation
+//
+// HpackVarintDecoder supports decoding any integer that can be represented on
+// uint64_t, thereby exceeding the requirements for QPACK: "QPACK
+// implementations MUST be able to decode integers up to 62 bits long." See
+// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.1.1
+//
+// This decoder supports at most 10 extension bytes (bytes following the prefix,
+// also called continuation bytes). An encoder is allowed to zero pad the
+// encoded integer on the left, thereby increasing the number of extension
+// bytes. If an encoder uses so much padding that the number of extension bytes
+// exceeds the limit, then this decoder signals an error.
+
+#ifndef QUICHE_HTTP2_HPACK_VARINT_HPACK_VARINT_DECODER_H_
+#define QUICHE_HTTP2_HPACK_VARINT_HPACK_VARINT_DECODER_H_
+
+#include <cstdint>
+#include <limits>
+#include <string>
+
+#include "quiche/http2/decoder/decode_buffer.h"
+#include "quiche/http2/decoder/decode_status.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+// Sentinel value for |HpackVarintDecoder::offset_| to signify that decoding is
+// completed. Only used in debug builds.
+#ifndef NDEBUG
+const uint8_t kHpackVarintDecoderOffsetDone =
+ std::numeric_limits<uint8_t>::max();
+#endif
+
+// Decodes an HPACK variable length unsigned integer, in a resumable fashion
+// so it can handle running out of input in the DecodeBuffer. Call Start or
+// StartExtended the first time (when decoding the byte that contains the
+// prefix), then call Resume later if it is necessary to resume. When done,
+// call value() to retrieve the decoded value.
+//
+// No constructor or destructor. Holds no resources, so destruction isn't
+// needed. Start and StartExtended handles the initialization of member
+// variables. This is necessary in order for HpackVarintDecoder to be part
+// of a union.
+class QUICHE_EXPORT_PRIVATE HpackVarintDecoder {
+ public:
+ // |prefix_value| is the first byte of the encoded varint.
+ // |prefix_length| is number of bits in the first byte that are used for
+ // encoding the integer. |db| is the rest of the buffer, that is, not
+ // including the first byte.
+ DecodeStatus Start(uint8_t prefix_value, uint8_t prefix_length,
+ DecodeBuffer* db);
+
+ // The caller has already determined that the encoding requires multiple
+ // bytes, i.e. that the 3 to 8 low-order bits (the number determined by
+ // |prefix_length|) of the first byte are are all 1. |db| is the rest of the
+ // buffer, that is, not including the first byte.
+ DecodeStatus StartExtended(uint8_t prefix_length, DecodeBuffer* db);
+
+ // Resume decoding a variable length integer after an earlier
+ // call to Start or StartExtended returned kDecodeInProgress.
+ DecodeStatus Resume(DecodeBuffer* db);
+
+ uint64_t value() const;
+
+ // This supports optimizations for the case of a varint with zero extension
+ // bytes, where the handling of the prefix is done by the caller.
+ void set_value(uint64_t v);
+
+ // All the public methods below are for supporting assertions and tests.
+
+ std::string DebugString() const;
+
+ // For benchmarking, these methods ensure the decoder
+ // is NOT inlined into the caller.
+ DecodeStatus StartForTest(uint8_t prefix_value, uint8_t prefix_length,
+ DecodeBuffer* db);
+ DecodeStatus StartExtendedForTest(uint8_t prefix_length, DecodeBuffer* db);
+ DecodeStatus ResumeForTest(DecodeBuffer* db);
+
+ private:
+ // Protection in case Resume is called when it shouldn't be.
+ void MarkDone() {
+#ifndef NDEBUG
+ offset_ = kHpackVarintDecoderOffsetDone;
+#endif
+ }
+ void CheckNotDone() const {
+#ifndef NDEBUG
+ QUICHE_DCHECK_NE(kHpackVarintDecoderOffsetDone, offset_);
+#endif
+ }
+ void CheckDone() const {
+#ifndef NDEBUG
+ QUICHE_DCHECK_EQ(kHpackVarintDecoderOffsetDone, offset_);
+#endif
+ }
+
+ // These fields are initialized just to keep ASAN happy about reading
+ // them from DebugString().
+
+ // The encoded integer is being accumulated in |value_|. When decoding is
+ // complete, |value_| holds the result.
+ uint64_t value_ = 0;
+
+ // Each extension byte encodes in its lowest 7 bits a segment of the integer.
+ // |offset_| is the number of places this segment has to be shifted to the
+ // left for decoding. It is zero for the first extension byte, and increases
+ // by 7 for each subsequent extension byte.
+ uint8_t offset_ = 0;
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_VARINT_HPACK_VARINT_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder_test.cc
new file mode 100644
index 00000000000..020ca0f7a6a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_decoder_test.cc
@@ -0,0 +1,309 @@
+// Copyright 2018 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 "quiche/http2/hpack/varint/hpack_varint_decoder.h"
+
+// Test HpackVarintDecoder against hardcoded data.
+
+#include <stddef.h>
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "absl/strings/string_view.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/http2/tools/random_decoder_test.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/platform/api/quiche_test_helpers.h"
+
+using ::testing::AssertionSuccess;
+
+namespace http2 {
+namespace test {
+namespace {
+
+class HpackVarintDecoderTest
+ : public RandomDecoderTest,
+ public ::testing::WithParamInterface<std::tuple<uint8_t, const char*>> {
+ protected:
+ HpackVarintDecoderTest()
+ : high_bits_(std::get<0>(GetParam())),
+ suffix_(absl::HexStringToBytes(std::get<1>(GetParam()))),
+ prefix_length_(0) {}
+
+ void DecodeExpectSuccess(absl::string_view data, uint32_t prefix_length,
+ uint64_t expected_value) {
+ Validator validator = [expected_value, this](
+ const DecodeBuffer& /*db*/,
+ DecodeStatus /*status*/) -> AssertionResult {
+ VERIFY_EQ(expected_value, decoder_.value())
+ << "Value doesn't match expected: " << decoder_.value()
+ << " != " << expected_value;
+ return AssertionSuccess();
+ };
+
+ // First validate that decoding is done and that we've advanced the cursor
+ // the expected amount.
+ validator = ValidateDoneAndOffset(/* offset = */ data.size(), validator);
+
+ EXPECT_TRUE(Decode(data, prefix_length, validator));
+
+ EXPECT_EQ(expected_value, decoder_.value());
+ }
+
+ void DecodeExpectError(absl::string_view data, uint32_t prefix_length) {
+ Validator validator = [](const DecodeBuffer& /*db*/,
+ DecodeStatus status) -> AssertionResult {
+ VERIFY_EQ(DecodeStatus::kDecodeError, status);
+ return AssertionSuccess();
+ };
+
+ EXPECT_TRUE(Decode(data, prefix_length, validator));
+ }
+
+ private:
+ AssertionResult Decode(absl::string_view data, uint32_t prefix_length,
+ const Validator validator) {
+ prefix_length_ = prefix_length;
+
+ // Copy |data| so that it can be modified.
+ std::string data_copy(data);
+
+ // Bits of the first byte not part of the prefix should be ignored.
+ uint8_t high_bits_mask = 0b11111111 << prefix_length_;
+ data_copy[0] |= (high_bits_mask & high_bits_);
+
+ // Extra bytes appended to the input should be ignored.
+ data_copy.append(suffix_);
+
+ DecodeBuffer b(data_copy);
+
+ // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
+ // can call Start with the prefix byte.
+ bool return_non_zero_on_first = true;
+
+ return DecodeAndValidateSeveralWays(&b, return_non_zero_on_first,
+ validator);
+ }
+
+ DecodeStatus StartDecoding(DecodeBuffer* b) override {
+ QUICHE_CHECK_LT(0u, b->Remaining());
+ uint8_t prefix = b->DecodeUInt8();
+ return decoder_.Start(prefix, prefix_length_, b);
+ }
+
+ DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+ return decoder_.Resume(b);
+ }
+
+ // Bits of the first byte not part of the prefix.
+ const uint8_t high_bits_;
+ // Extra bytes appended to the input.
+ const std::string suffix_;
+
+ HpackVarintDecoder decoder_;
+ uint8_t prefix_length_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ HpackVarintDecoderTest, HpackVarintDecoderTest,
+ ::testing::Combine(
+ // Bits of the first byte not part of the prefix should be ignored.
+ ::testing::Values(0b00000000, 0b11111111, 0b10101010),
+ // Extra bytes appended to the input should be ignored.
+ ::testing::Values("", "00", "666f6f")));
+
+struct {
+ const char* data;
+ uint32_t prefix_length;
+ uint64_t expected_value;
+} kSuccessTestData[] = {
+ // Zero value with different prefix lengths.
+ {"00", 3, 0},
+ {"00", 4, 0},
+ {"00", 5, 0},
+ {"00", 6, 0},
+ {"00", 7, 0},
+ {"00", 8, 0},
+ // Small values that fit in the prefix.
+ {"06", 3, 6},
+ {"0d", 4, 13},
+ {"10", 5, 16},
+ {"29", 6, 41},
+ {"56", 7, 86},
+ {"bf", 8, 191},
+ // Values of 2^n-1, which have an all-zero extension byte.
+ {"0700", 3, 7},
+ {"0f00", 4, 15},
+ {"1f00", 5, 31},
+ {"3f00", 6, 63},
+ {"7f00", 7, 127},
+ {"ff00", 8, 255},
+ // Values of 2^n-1, plus one extra byte of padding.
+ {"078000", 3, 7},
+ {"0f8000", 4, 15},
+ {"1f8000", 5, 31},
+ {"3f8000", 6, 63},
+ {"7f8000", 7, 127},
+ {"ff8000", 8, 255},
+ // Values requiring one extension byte.
+ {"0760", 3, 103},
+ {"0f2a", 4, 57},
+ {"1f7f", 5, 158},
+ {"3f02", 6, 65},
+ {"7f49", 7, 200},
+ {"ff6f", 8, 366},
+ // Values requiring one extension byte, plus one byte of padding.
+ {"07e000", 3, 103},
+ {"0faa00", 4, 57},
+ {"1fff00", 5, 158},
+ {"3f8200", 6, 65},
+ {"7fc900", 7, 200},
+ {"ffef00", 8, 366},
+ // Values requiring one extension byte, plus two bytes of padding.
+ {"07e08000", 3, 103},
+ {"0faa8000", 4, 57},
+ {"1fff8000", 5, 158},
+ {"3f828000", 6, 65},
+ {"7fc98000", 7, 200},
+ {"ffef8000", 8, 366},
+ // Values requiring one extension byte, plus the maximum amount of padding.
+ {"07e0808080808080808000", 3, 103},
+ {"0faa808080808080808000", 4, 57},
+ {"1fff808080808080808000", 5, 158},
+ {"3f82808080808080808000", 6, 65},
+ {"7fc9808080808080808000", 7, 200},
+ {"ffef808080808080808000", 8, 366},
+ // Values requiring two extension bytes.
+ {"07b260", 3, 12345},
+ {"0f8a2a", 4, 5401},
+ {"1fa87f", 5, 16327},
+ {"3fd002", 6, 399},
+ {"7fff49", 7, 9598},
+ {"ffe32f", 8, 6370},
+ // Values requiring two extension bytes, plus one byte of padding.
+ {"07b2e000", 3, 12345},
+ {"0f8aaa00", 4, 5401},
+ {"1fa8ff00", 5, 16327},
+ {"3fd08200", 6, 399},
+ {"7fffc900", 7, 9598},
+ {"ffe3af00", 8, 6370},
+ // Values requiring two extension bytes, plus the maximum amount of padding.
+ {"07b2e080808080808000", 3, 12345},
+ {"0f8aaa80808080808000", 4, 5401},
+ {"1fa8ff80808080808000", 5, 16327},
+ {"3fd08280808080808000", 6, 399},
+ {"7fffc980808080808000", 7, 9598},
+ {"ffe3af80808080808000", 8, 6370},
+ // Values requiring three extension bytes.
+ {"078ab260", 3, 1579281},
+ {"0fc18a2a", 4, 689488},
+ {"1fada87f", 5, 2085964},
+ {"3fa0d002", 6, 43103},
+ {"7ffeff49", 7, 1212541},
+ {"ff93de23", 8, 585746},
+ // Values requiring three extension bytes, plus one byte of padding.
+ {"078ab2e000", 3, 1579281},
+ {"0fc18aaa00", 4, 689488},
+ {"1fada8ff00", 5, 2085964},
+ {"3fa0d08200", 6, 43103},
+ {"7ffeffc900", 7, 1212541},
+ {"ff93dea300", 8, 585746},
+ // Values requiring four extension bytes.
+ {"079f8ab260", 3, 202147110},
+ {"0fa2c18a2a", 4, 88252593},
+ {"1fd0ada87f", 5, 266999535},
+ {"3ff9a0d002", 6, 5509304},
+ {"7f9efeff49", 7, 155189149},
+ {"ffaa82f404", 8, 10289705},
+ // Values requiring four extension bytes, plus one byte of padding.
+ {"079f8ab2e000", 3, 202147110},
+ {"0fa2c18aaa00", 4, 88252593},
+ {"1fd0ada8ff00", 5, 266999535},
+ {"3ff9a0d08200", 6, 5509304},
+ {"7f9efeffc900", 7, 155189149},
+ {"ffaa82f48400", 8, 10289705},
+ // Values requiring six extension bytes.
+ {"0783aa9f8ab260", 3, 3311978140938},
+ {"0ff0b0a2c18a2a", 4, 1445930244223},
+ {"1fda84d0ada87f", 5, 4374519874169},
+ {"3fb5fbf9a0d002", 6, 90263420404},
+ {"7fcff19efeff49", 7, 2542616951118},
+ {"ff9fa486bbc327", 8, 1358138807070},
+ // Values requiring eight extension bytes.
+ {"07f19883aa9f8ab260", 3, 54263449861016696},
+ {"0f84fdf0b0a2c18a2a", 4, 23690121121119891},
+ {"1fa0dfda84d0ada87f", 5, 71672133617889215},
+ {"3f9ff0b5fbf9a0d002", 6, 1478875878881374},
+ {"7ffbc1cff19efeff49", 7, 41658236125045114},
+ {"ff91b6fb85af99c342", 8, 37450237664484368},
+ // Values requiring ten extension bytes.
+ {"0794f1f19883aa9f8ab201", 3, 12832019021693745307u},
+ {"0fa08f84fdf0b0a2c18a01", 4, 9980690937382242223u},
+ {"1fbfdda0dfda84d0ada801", 5, 12131360551794650846u},
+ {"3f9dc79ff0b5fbf9a0d001", 6, 15006530362736632796u},
+ {"7f8790fbc1cff19efeff01", 7, 18445754019193211014u},
+ {"fffba8c5b8d3fe9f8c8401", 8, 9518498503615141242u},
+ // Maximum value: 2^64-1.
+ {"07f8ffffffffffffffff01", 3, 18446744073709551615u},
+ {"0ff0ffffffffffffffff01", 4, 18446744073709551615u},
+ {"1fe0ffffffffffffffff01", 5, 18446744073709551615u},
+ {"3fc0ffffffffffffffff01", 6, 18446744073709551615u},
+ {"7f80ffffffffffffffff01", 7, 18446744073709551615u},
+ {"ff80feffffffffffffff01", 8, 18446744073709551615u},
+ // Examples from RFC7541 C.1.
+ {"0a", 5, 10},
+ {"1f9a0a", 5, 1337},
+};
+
+TEST_P(HpackVarintDecoderTest, Success) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kSuccessTestData); ++i) {
+ DecodeExpectSuccess(absl::HexStringToBytes(kSuccessTestData[i].data),
+ kSuccessTestData[i].prefix_length,
+ kSuccessTestData[i].expected_value);
+ }
+}
+
+struct {
+ const char* data;
+ uint32_t prefix_length;
+} kErrorTestData[] = {
+ // Too many extension bytes, all 0s (except for extension bit in each byte).
+ {"0780808080808080808080", 3},
+ {"0f80808080808080808080", 4},
+ {"1f80808080808080808080", 5},
+ {"3f80808080808080808080", 6},
+ {"7f80808080808080808080", 7},
+ {"ff80808080808080808080", 8},
+ // Too many extension bytes, all 1s.
+ {"07ffffffffffffffffffff", 3},
+ {"0fffffffffffffffffffff", 4},
+ {"1fffffffffffffffffffff", 5},
+ {"3fffffffffffffffffffff", 6},
+ {"7fffffffffffffffffffff", 7},
+ {"ffffffffffffffffffffff", 8},
+ // Value of 2^64, one higher than maximum of 2^64-1.
+ {"07f9ffffffffffffffff01", 3},
+ {"0ff1ffffffffffffffff01", 4},
+ {"1fe1ffffffffffffffff01", 5},
+ {"3fc1ffffffffffffffff01", 6},
+ {"7f81ffffffffffffffff01", 7},
+ {"ff81feffffffffffffff01", 8},
+ // Maximum value: 2^64-1, with one byte of padding.
+ {"07f8ffffffffffffffff8100", 3},
+ {"0ff0ffffffffffffffff8100", 4},
+ {"1fe0ffffffffffffffff8100", 5},
+ {"3fc0ffffffffffffffff8100", 6},
+ {"7f80ffffffffffffffff8100", 7},
+ {"ff80feffffffffffffff8100", 8}};
+
+TEST_P(HpackVarintDecoderTest, Error) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kErrorTestData); ++i) {
+ DecodeExpectError(absl::HexStringToBytes(kErrorTestData[i].data),
+ kErrorTestData[i].prefix_length);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.cc
new file mode 100644
index 00000000000..89beb4ae00b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.cc
@@ -0,0 +1,47 @@
+// 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 "quiche/http2/hpack/varint/hpack_varint_encoder.h"
+
+#include <limits>
+
+#include "quiche/http2/platform/api/http2_logging.h"
+
+namespace http2 {
+
+// static
+void HpackVarintEncoder::Encode(uint8_t high_bits, uint8_t prefix_length,
+ uint64_t varint, std::string* output) {
+ QUICHE_DCHECK_LE(1u, prefix_length);
+ QUICHE_DCHECK_LE(prefix_length, 8u);
+
+ // prefix_mask defines the sequence of low-order bits of the first byte
+ // that encode the prefix of the value. It is also the marker in those bits
+ // of the first byte indicating that at least one extension byte is needed.
+ const uint8_t prefix_mask = (1 << prefix_length) - 1;
+ QUICHE_DCHECK_EQ(0, high_bits & prefix_mask);
+
+ if (varint < prefix_mask) {
+ // The integer fits into the prefix in its entirety.
+ unsigned char first_byte = high_bits | static_cast<unsigned char>(varint);
+ output->push_back(first_byte);
+ return;
+ }
+
+ // Extension bytes are needed.
+ unsigned char first_byte = high_bits | prefix_mask;
+ output->push_back(first_byte);
+
+ varint -= prefix_mask;
+ while (varint >= 128) {
+ // Encode the next seven bits, with continuation bit set to one.
+ output->push_back(0b10000000 | (varint % 128));
+ varint >>= 7;
+ }
+
+ // Encode final seven bits, with continuation bit set to zero.
+ output->push_back(varint);
+}
+
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.h b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.h
new file mode 100644
index 00000000000..69acc161a67
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder.h
@@ -0,0 +1,29 @@
+// 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_VARINT_HPACK_VARINT_ENCODER_H_
+#define QUICHE_HTTP2_HPACK_VARINT_HPACK_VARINT_ENCODER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "quiche/common/platform/api/quiche_export.h"
+
+namespace http2 {
+
+// HPACK integer encoder class with single static method implementing variable
+// length integer representation defined in RFC7541, Section 5.1:
+// https://httpwg.org/specs/rfc7541.html#integer.representation
+class QUICHE_EXPORT_PRIVATE HpackVarintEncoder {
+ public:
+ // Encode |varint|, appending encoded data to |*output|.
+ // Appends between 1 and 11 bytes in total.
+ static void Encode(uint8_t high_bits, uint8_t prefix_length, uint64_t varint,
+ std::string* output);
+};
+
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_VARINT_HPACK_VARINT_ENCODER_H_
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder_test.cc
new file mode 100644
index 00000000000..bd51606b079
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_encoder_test.cc
@@ -0,0 +1,161 @@
+// 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 "quiche/http2/hpack/varint/hpack_varint_encoder.h"
+
+#include "absl/base/macros.h"
+#include "absl/strings/escaping.h"
+#include "quiche/common/platform/api/quiche_test.h"
+
+namespace http2 {
+namespace test {
+namespace {
+
+struct {
+ uint8_t high_bits;
+ uint8_t prefix_length;
+ uint64_t value;
+ uint8_t expected_encoding;
+} kShortTestData[] = {{0b10110010, 1, 0, 0b10110010},
+ {0b10101100, 2, 2, 0b10101110},
+ {0b10100000, 3, 6, 0b10100110},
+ {0b10110000, 4, 13, 0b10111101},
+ {0b10100000, 5, 8, 0b10101000},
+ {0b11000000, 6, 48, 0b11110000},
+ {0b10000000, 7, 99, 0b11100011},
+ // Example from RFC7541 C.1.
+ {0b00000000, 5, 10, 0b00001010}};
+
+// Encode integers that fit in the prefix.
+TEST(HpackVarintEncoderTest, Short) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kShortTestData); ++i) {
+ std::string output;
+ HpackVarintEncoder::Encode(kShortTestData[i].high_bits,
+ kShortTestData[i].prefix_length,
+ kShortTestData[i].value, &output);
+ ASSERT_EQ(1u, output.size());
+ EXPECT_EQ(kShortTestData[i].expected_encoding,
+ static_cast<uint8_t>(output[0]));
+ }
+}
+
+struct {
+ uint8_t high_bits;
+ uint8_t prefix_length;
+ uint64_t value;
+ const char* expected_encoding;
+} kLongTestData[] = {
+ // One extension byte.
+ {0b10011000, 3, 103, "9f60"},
+ {0b10010000, 4, 57, "9f2a"},
+ {0b11000000, 5, 158, "df7f"},
+ {0b01000000, 6, 65, "7f02"},
+ {0b00000000, 7, 200, "7f49"},
+ // Two extension bytes.
+ {0b10011000, 3, 12345, "9fb260"},
+ {0b10010000, 4, 5401, "9f8a2a"},
+ {0b11000000, 5, 16327, "dfa87f"},
+ {0b01000000, 6, 399, "7fd002"},
+ {0b00000000, 7, 9598, "7fff49"},
+ // Three extension bytes.
+ {0b10011000, 3, 1579281, "9f8ab260"},
+ {0b10010000, 4, 689488, "9fc18a2a"},
+ {0b11000000, 5, 2085964, "dfada87f"},
+ {0b01000000, 6, 43103, "7fa0d002"},
+ {0b00000000, 7, 1212541, "7ffeff49"},
+ // Four extension bytes.
+ {0b10011000, 3, 202147110, "9f9f8ab260"},
+ {0b10010000, 4, 88252593, "9fa2c18a2a"},
+ {0b11000000, 5, 266999535, "dfd0ada87f"},
+ {0b01000000, 6, 5509304, "7ff9a0d002"},
+ {0b00000000, 7, 155189149, "7f9efeff49"},
+ // Six extension bytes.
+ {0b10011000, 3, 3311978140938, "9f83aa9f8ab260"},
+ {0b10010000, 4, 1445930244223, "9ff0b0a2c18a2a"},
+ {0b11000000, 5, 4374519874169, "dfda84d0ada87f"},
+ {0b01000000, 6, 90263420404, "7fb5fbf9a0d002"},
+ {0b00000000, 7, 2542616951118, "7fcff19efeff49"},
+ // Eight extension bytes.
+ {0b10011000, 3, 54263449861016696, "9ff19883aa9f8ab260"},
+ {0b10010000, 4, 23690121121119891, "9f84fdf0b0a2c18a2a"},
+ {0b11000000, 5, 71672133617889215, "dfa0dfda84d0ada87f"},
+ {0b01000000, 6, 1478875878881374, "7f9ff0b5fbf9a0d002"},
+ {0b00000000, 7, 41658236125045114, "7ffbc1cff19efeff49"},
+ // Ten extension bytes.
+ {0b10011000, 3, 12832019021693745307u, "9f94f1f19883aa9f8ab201"},
+ {0b10010000, 4, 9980690937382242223u, "9fa08f84fdf0b0a2c18a01"},
+ {0b11000000, 5, 12131360551794650846u, "dfbfdda0dfda84d0ada801"},
+ {0b01000000, 6, 15006530362736632796u, "7f9dc79ff0b5fbf9a0d001"},
+ {0b00000000, 7, 18445754019193211014u, "7f8790fbc1cff19efeff01"},
+ // Maximum value: 2^64-1.
+ {0b10011000, 3, 18446744073709551615u, "9ff8ffffffffffffffff01"},
+ {0b10010000, 4, 18446744073709551615u, "9ff0ffffffffffffffff01"},
+ {0b11000000, 5, 18446744073709551615u, "dfe0ffffffffffffffff01"},
+ {0b01000000, 6, 18446744073709551615u, "7fc0ffffffffffffffff01"},
+ {0b00000000, 7, 18446744073709551615u, "7f80ffffffffffffffff01"},
+ // Example from RFC7541 C.1.
+ {0b00000000, 5, 1337, "1f9a0a"},
+};
+
+// Encode integers that do not fit in the prefix.
+TEST(HpackVarintEncoderTest, Long) {
+ // Test encoding byte by byte, also test encoding in
+ // a single ResumeEncoding() call.
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kLongTestData); ++i) {
+ std::string expected_encoding =
+ absl::HexStringToBytes(kLongTestData[i].expected_encoding);
+
+ std::string output;
+ HpackVarintEncoder::Encode(kLongTestData[i].high_bits,
+ kLongTestData[i].prefix_length,
+ kLongTestData[i].value, &output);
+
+ EXPECT_EQ(expected_encoding, output);
+ }
+}
+
+struct {
+ uint8_t high_bits;
+ uint8_t prefix_length;
+ uint64_t value;
+ uint8_t expected_encoding_first_byte;
+} kLastByteIsZeroTestData[] = {
+ {0b10110010, 1, 1, 0b10110011}, {0b10101100, 2, 3, 0b10101111},
+ {0b10101000, 3, 7, 0b10101111}, {0b10110000, 4, 15, 0b10111111},
+ {0b10100000, 5, 31, 0b10111111}, {0b11000000, 6, 63, 0b11111111},
+ {0b10000000, 7, 127, 0b11111111}, {0b00000000, 8, 255, 0b11111111}};
+
+// Make sure that the encoder outputs the last byte even when it is zero. This
+// happens exactly when encoding the value 2^prefix_length - 1.
+TEST(HpackVarintEncoderTest, LastByteIsZero) {
+ for (size_t i = 0; i < ABSL_ARRAYSIZE(kLastByteIsZeroTestData); ++i) {
+ std::string output;
+ HpackVarintEncoder::Encode(kLastByteIsZeroTestData[i].high_bits,
+ kLastByteIsZeroTestData[i].prefix_length,
+ kLastByteIsZeroTestData[i].value, &output);
+ ASSERT_EQ(2u, output.size());
+ EXPECT_EQ(kLastByteIsZeroTestData[i].expected_encoding_first_byte,
+ static_cast<uint8_t>(output[0]));
+ EXPECT_EQ(0b00000000, output[1]);
+ }
+}
+
+// Test that encoder appends correctly to non-empty string.
+TEST(HpackVarintEncoderTest, Append) {
+ std::string output("foo");
+ EXPECT_EQ(absl::HexStringToBytes("666f6f"), output);
+
+ HpackVarintEncoder::Encode(0b10011000, 3, 103, &output);
+ EXPECT_EQ(absl::HexStringToBytes("666f6f9f60"), output);
+
+ HpackVarintEncoder::Encode(0b10100000, 5, 8, &output);
+ EXPECT_EQ(absl::HexStringToBytes("666f6f9f60a8"), output);
+
+ HpackVarintEncoder::Encode(0b10011000, 3, 202147110, &output);
+ EXPECT_EQ(absl::HexStringToBytes("666f6f9f60a89f9f8ab260"), output);
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_round_trip_test.cc b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_round_trip_test.cc
new file mode 100644
index 00000000000..ba5164961aa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quiche/http2/hpack/varint/hpack_varint_round_trip_test.cc
@@ -0,0 +1,416 @@
+// 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 "quiche/http2/hpack/varint/hpack_varint_decoder.h"
+
+// Test HpackVarintDecoder against data encoded via HpackBlockBuilder,
+// which uses HpackVarintEncoder under the hood.
+
+#include <stddef.h>
+
+#include <iterator>
+#include <set>
+#include <vector>
+
+#include "absl/strings/str_cat.h"
+#include "absl/strings/str_format.h"
+#include "absl/strings/string_view.h"
+#include "quiche/http2/hpack/tools/hpack_block_builder.h"
+#include "quiche/http2/platform/api/http2_logging.h"
+#include "quiche/http2/tools/random_decoder_test.h"
+#include "quiche/common/platform/api/quiche_test.h"
+#include "quiche/common/quiche_text_utils.h"
+
+using ::testing::AssertionFailure;
+using ::testing::AssertionSuccess;
+
+namespace http2 {
+namespace test {
+namespace {
+
+// Returns the highest value with the specified number of extension bytes
+// and the specified prefix length (bits).
+uint64_t HiValueOfExtensionBytes(uint32_t extension_bytes,
+ uint32_t prefix_length) {
+ return (1 << prefix_length) - 2 +
+ (extension_bytes == 0 ? 0 : (1LLU << (extension_bytes * 7)));
+}
+
+class HpackVarintRoundTripTest : public RandomDecoderTest {
+ protected:
+ HpackVarintRoundTripTest() : prefix_length_(0) {}
+
+ DecodeStatus StartDecoding(DecodeBuffer* b) override {
+ QUICHE_CHECK_LT(0u, b->Remaining());
+ uint8_t prefix = b->DecodeUInt8();
+ return decoder_.Start(prefix, prefix_length_, b);
+ }
+
+ DecodeStatus ResumeDecoding(DecodeBuffer* b) override {
+ return decoder_.Resume(b);
+ }
+
+ void DecodeSeveralWays(uint64_t expected_value, uint32_t expected_offset) {
+ // The validator is called after each of the several times that the input
+ // DecodeBuffer is decoded, each with a different segmentation of the input.
+ // Validate that decoder_.value() matches the expected value.
+ Validator validator = [expected_value, this](
+ const DecodeBuffer& /*db*/,
+ DecodeStatus /*status*/) -> AssertionResult {
+ if (decoder_.value() != expected_value) {
+ return AssertionFailure()
+ << "Value doesn't match expected: " << decoder_.value()
+ << " != " << expected_value;
+ }
+ return AssertionSuccess();
+ };
+
+ // First validate that decoding is done and that we've advanced the cursor
+ // the expected amount.
+ validator = ValidateDoneAndOffset(expected_offset, validator);
+
+ // StartDecoding, above, requires the DecodeBuffer be non-empty so that it
+ // can call Start with the prefix byte.
+ bool return_non_zero_on_first = true;
+
+ DecodeBuffer b(buffer_);
+ EXPECT_TRUE(
+ DecodeAndValidateSeveralWays(&b, return_non_zero_on_first, validator));
+
+ EXPECT_EQ(expected_value, decoder_.value());
+ EXPECT_EQ(expected_offset, b.Offset());
+ }
+
+ void EncodeNoRandom(uint64_t value, uint8_t prefix_length) {
+ QUICHE_DCHECK_LE(3, prefix_length);
+ QUICHE_DCHECK_LE(prefix_length, 8);
+ prefix_length_ = prefix_length;
+
+ HpackBlockBuilder bb;
+ bb.AppendHighBitsAndVarint(0, prefix_length_, value);
+ buffer_ = bb.buffer();
+ ASSERT_LT(0u, buffer_.size());
+
+ const uint8_t prefix_mask = (1 << prefix_length_) - 1;
+ ASSERT_EQ(static_cast<uint8_t>(buffer_[0]),
+ static_cast<uint8_t>(buffer_[0]) & prefix_mask);
+ }
+
+ void Encode(uint64_t value, uint8_t prefix_length) {
+ EncodeNoRandom(value, prefix_length);
+ // Add some random bits to the prefix (the first byte) above the mask.
+ uint8_t prefix = buffer_[0];
+ buffer_[0] = prefix | (Random().Rand8() << prefix_length);
+ const uint8_t prefix_mask = (1 << prefix_length_) - 1;
+ ASSERT_EQ(prefix, buffer_[0] & prefix_mask);
+ }
+
+ // This is really a test of HpackBlockBuilder, making sure that the input to
+ // HpackVarintDecoder is as expected, which also acts as confirmation that
+ // my thinking about the encodings being used by the tests, i.e. cover the
+ // range desired.
+ void ValidateEncoding(uint64_t value, uint64_t minimum, uint64_t maximum,
+ size_t expected_bytes) {
+ ASSERT_EQ(expected_bytes, buffer_.size());
+ if (expected_bytes > 1) {
+ const uint8_t prefix_mask = (1 << prefix_length_) - 1;
+ EXPECT_EQ(prefix_mask, buffer_[0] & prefix_mask);
+ size_t last = expected_bytes - 1;
+ for (size_t ndx = 1; ndx < last; ++ndx) {
+ // Before the last extension byte, we expect the high-bit set.
+ uint8_t byte = buffer_[ndx];
+ if (value == minimum) {
+ EXPECT_EQ(0x80, byte) << "ndx=" << ndx;
+ } else if (value == maximum) {
+ if (expected_bytes < 11) {
+ EXPECT_EQ(0xff, byte) << "ndx=" << ndx;
+ }
+ } else {
+ EXPECT_EQ(0x80, byte & 0x80) << "ndx=" << ndx;
+ }
+ }
+ // The last extension byte should not have the high-bit set.
+ uint8_t byte = buffer_[last];
+ if (value == minimum) {
+ if (expected_bytes == 2) {
+ EXPECT_EQ(0x00, byte);
+ } else {
+ EXPECT_EQ(0x01, byte);
+ }
+ } else if (value == maximum) {
+ if (expected_bytes < 11) {
+ EXPECT_EQ(0x7f, byte);
+ }
+ } else {
+ EXPECT_EQ(0x00, byte & 0x80);
+ }
+ } else {
+ const uint8_t prefix_mask = (1 << prefix_length_) - 1;
+ EXPECT_EQ(value, static_cast<uint32_t>(buffer_[0] & prefix_mask));
+ EXPECT_LT(value, prefix_mask);
+ }
+ }
+
+ void EncodeAndDecodeValues(const std::set<uint64_t>& values,
+ uint8_t prefix_length, size_t expected_bytes) {
+ QUICHE_CHECK(!values.empty());
+ const uint64_t minimum = *values.begin();
+ const uint64_t maximum = *values.rbegin();
+ for (const uint64_t value : values) {
+ Encode(value, prefix_length); // Sets buffer_.
+
+ std::string msg = absl::StrCat("value=", value, " (0x", absl::Hex(value),
+ "), prefix_length=", prefix_length,
+ ", expected_bytes=", expected_bytes, "\n",
+ quiche::QuicheTextUtils::HexDump(buffer_));
+
+ if (value == minimum) {
+ HTTP2_LOG(INFO) << "Checking minimum; " << msg;
+ } else if (value == maximum) {
+ HTTP2_LOG(INFO) << "Checking maximum; " << msg;
+ }
+
+ SCOPED_TRACE(msg);
+ ValidateEncoding(value, minimum, maximum, expected_bytes);
+ DecodeSeveralWays(value, expected_bytes);
+
+ // Append some random data to the end of buffer_ and repeat. That random
+ // data should be ignored.
+ buffer_.append(Random().RandString(1 + Random().Uniform(10)));
+ DecodeSeveralWays(value, expected_bytes);
+
+ // If possible, add extension bytes that don't change the value.
+ if (1 < expected_bytes) {
+ buffer_.resize(expected_bytes);
+ for (uint8_t total_bytes = expected_bytes + 1; total_bytes <= 6;
+ ++total_bytes) {
+ // Mark the current last byte as not being the last one.
+ EXPECT_EQ(0x00, 0x80 & buffer_.back());
+ buffer_.back() |= 0x80;
+ buffer_.push_back('\0');
+ DecodeSeveralWays(value, total_bytes);
+ }
+ }
+ }
+ }
+
+ // Encode values (all or some of it) in [start, start+range). Check
+ // that |start| is the smallest value and |start+range-1| is the largest value
+ // corresponding to |expected_bytes|, except if |expected_bytes| is maximal.
+ void EncodeAndDecodeValuesInRange(uint64_t start, uint64_t range,
+ uint8_t prefix_length,
+ size_t expected_bytes) {
+ const uint8_t prefix_mask = (1 << prefix_length) - 1;
+ const uint64_t beyond = start + range;
+
+ HTTP2_LOG(INFO)
+ << "############################################################";
+ HTTP2_LOG(INFO) << "prefix_length=" << static_cast<int>(prefix_length);
+ HTTP2_LOG(INFO) << "prefix_mask=" << std::hex
+ << static_cast<int>(prefix_mask);
+ HTTP2_LOG(INFO) << "start=" << start << " (" << std::hex << start << ")";
+ HTTP2_LOG(INFO) << "range=" << range << " (" << std::hex << range << ")";
+ HTTP2_LOG(INFO) << "beyond=" << beyond << " (" << std::hex << beyond << ")";
+ HTTP2_LOG(INFO) << "expected_bytes=" << expected_bytes;
+
+ if (expected_bytes < 11) {
+ // Confirm the claim that beyond requires more bytes.
+ Encode(beyond, prefix_length);
+ EXPECT_EQ(expected_bytes + 1, buffer_.size())
+ << quiche::QuicheTextUtils::HexDump(buffer_);
+ }
+
+ std::set<uint64_t> values;
+ if (range < 200) {
+ // Select all values in the range.
+ for (uint64_t offset = 0; offset < range; ++offset) {
+ values.insert(start + offset);
+ }
+ } else {
+ // Select some values in this range, including the minimum and maximum
+ // values that require exactly |expected_bytes| extension bytes.
+ values.insert({start, start + 1, beyond - 2, beyond - 1});
+ while (values.size() < 100) {
+ values.insert(Random().UniformInRange(start, beyond - 1));
+ }
+ }
+
+ EncodeAndDecodeValues(values, prefix_length, expected_bytes);
+ }
+
+ HpackVarintDecoder decoder_;
+ std::string buffer_;
+ uint8_t prefix_length_;
+};
+
+// To help me and future debuggers of varint encodings, this HTTP2_LOGs out the
+// transition points where a new extension byte is added.
+TEST_F(HpackVarintRoundTripTest, Encode) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t a = HiValueOfExtensionBytes(0, prefix_length);
+ const uint64_t b = HiValueOfExtensionBytes(1, prefix_length);
+ const uint64_t c = HiValueOfExtensionBytes(2, prefix_length);
+ const uint64_t d = HiValueOfExtensionBytes(3, prefix_length);
+ const uint64_t e = HiValueOfExtensionBytes(4, prefix_length);
+ const uint64_t f = HiValueOfExtensionBytes(5, prefix_length);
+ const uint64_t g = HiValueOfExtensionBytes(6, prefix_length);
+ const uint64_t h = HiValueOfExtensionBytes(7, prefix_length);
+ const uint64_t i = HiValueOfExtensionBytes(8, prefix_length);
+ const uint64_t j = HiValueOfExtensionBytes(9, prefix_length);
+
+ HTTP2_LOG(INFO)
+ << "############################################################";
+ HTTP2_LOG(INFO) << "prefix_length=" << prefix_length << " a=" << a
+ << " b=" << b << " c=" << c << " d=" << d
+ << " e=" << e << " f=" << f << " g=" << g
+ << " h=" << h << " i=" << i << " j=" << j;
+
+ std::vector<uint64_t> values = {
+ 0, 1, // Force line break.
+ a - 1, a, a + 1, a + 2, a + 3, // Force line break.
+ b - 1, b, b + 1, b + 2, b + 3, // Force line break.
+ c - 1, c, c + 1, c + 2, c + 3, // Force line break.
+ d - 1, d, d + 1, d + 2, d + 3, // Force line break.
+ e - 1, e, e + 1, e + 2, e + 3, // Force line break.
+ f - 1, f, f + 1, f + 2, f + 3, // Force line break.
+ g - 1, g, g + 1, g + 2, g + 3, // Force line break.
+ h - 1, h, h + 1, h + 2, h + 3, // Force line break.
+ i - 1, i, i + 1, i + 2, i + 3, // Force line break.
+ j - 1, j, j + 1, j + 2, j + 3, // Force line break.
+ };
+
+ for (uint64_t value : values) {
+ EncodeNoRandom(value, prefix_length);
+ std::string dump = quiche::QuicheTextUtils::HexDump(buffer_);
+ HTTP2_LOG(INFO) << absl::StrFormat("%10llu %0#18x ", value, value)
+ << quiche::QuicheTextUtils::HexDump(buffer_).substr(7);
+ }
+ }
+}
+
+TEST_F(HpackVarintRoundTripTest, FromSpec1337) {
+ DecodeBuffer b(absl::string_view("\x1f\x9a\x0a"));
+ uint32_t prefix_length = 5;
+ uint8_t p = b.DecodeUInt8();
+ EXPECT_EQ(1u, b.Offset());
+ EXPECT_EQ(DecodeStatus::kDecodeDone, decoder_.Start(p, prefix_length, &b));
+ EXPECT_EQ(3u, b.Offset());
+ EXPECT_EQ(1337u, decoder_.value());
+
+ EncodeNoRandom(1337, prefix_length);
+ EXPECT_EQ(3u, buffer_.size());
+ EXPECT_EQ('\x1f', buffer_[0]);
+ EXPECT_EQ('\x9a', buffer_[1]);
+ EXPECT_EQ('\x0a', buffer_[2]);
+}
+
+// Test all the values that fit into the prefix (one less than the mask).
+TEST_F(HpackVarintRoundTripTest, ValidatePrefixOnly) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint8_t prefix_mask = (1 << prefix_length) - 1;
+ EncodeAndDecodeValuesInRange(0, prefix_mask, prefix_length, 1);
+ }
+}
+
+// Test all values that require exactly 1 extension byte.
+TEST_F(HpackVarintRoundTripTest, ValidateOneExtensionByte) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(0, prefix_length) + 1;
+ EncodeAndDecodeValuesInRange(start, 128, prefix_length, 2);
+ }
+}
+
+// Test *some* values that require exactly 2 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateTwoExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(1, prefix_length) + 1;
+ const uint64_t range = 127 << 7;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 3);
+ }
+}
+
+// Test *some* values that require 3 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateThreeExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(2, prefix_length) + 1;
+ const uint64_t range = 127 << 14;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 4);
+ }
+}
+
+// Test *some* values that require 4 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateFourExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(3, prefix_length) + 1;
+ const uint64_t range = 127 << 21;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 5);
+ }
+}
+
+// Test *some* values that require 5 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateFiveExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(4, prefix_length) + 1;
+ const uint64_t range = 127llu << 28;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 6);
+ }
+}
+
+// Test *some* values that require 6 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateSixExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(5, prefix_length) + 1;
+ const uint64_t range = 127llu << 35;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 7);
+ }
+}
+
+// Test *some* values that require 7 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateSevenExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(6, prefix_length) + 1;
+ const uint64_t range = 127llu << 42;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 8);
+ }
+}
+
+// Test *some* values that require 8 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateEightExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(7, prefix_length) + 1;
+ const uint64_t range = 127llu << 49;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 9);
+ }
+}
+
+// Test *some* values that require 9 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateNineExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(8, prefix_length) + 1;
+ const uint64_t range = 127llu << 56;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 10);
+ }
+}
+
+// Test *some* values that require 10 extension bytes.
+TEST_F(HpackVarintRoundTripTest, ValidateTenExtensionBytes) {
+ for (int prefix_length = 3; prefix_length <= 8; ++prefix_length) {
+ const uint64_t start = HiValueOfExtensionBytes(9, prefix_length) + 1;
+ const uint64_t range = std::numeric_limits<uint64_t>::max() - start;
+
+ EncodeAndDecodeValuesInRange(start, range, prefix_length, 11);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace http2