summaryrefslogtreecommitdiff
path: root/chromium/net/third_party/quiche/src/quic/core/qpack
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-24 11:40:17 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2019-05-24 12:42:11 +0000
commit5d87695f37678f96492b258bbab36486c59866b4 (patch)
treebe9783bbaf04fb930c4d74ca9c00b5e7954c8bc6 /chromium/net/third_party/quiche/src/quic/core/qpack
parent6c11fb357ec39bf087b8b632e2b1e375aef1b38b (diff)
downloadqtwebengine-chromium-5d87695f37678f96492b258bbab36486c59866b4.tar.gz
BASELINE: Update Chromium to 75.0.3770.56
Change-Id: I86d2007fd27a45d5797eee06f4c9369b8b50ac4f Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/net/third_party/quiche/src/quic/core/qpack')
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc273
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h71
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder_bin.cc44
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.cc200
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.h147
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc72
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h66
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc119
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc141
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h102
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc52
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h63
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc91
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc61
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h55
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc91
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc703
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc82
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h114
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc55
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h72
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc60
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h68
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc169
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc81
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h58
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc113
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc168
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.cc37
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h64
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc204
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h144
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc356
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc310
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h146
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc171
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc217
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h118
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc152
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc369
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h140
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder_test.cc124
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.cc139
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h57
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc137
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.cc140
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h31
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc53
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.cc23
-rw-r--r--chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h29
50 files changed, 6552 insertions, 0 deletions
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc
new file mode 100644
index 00000000000..42f572b3eac
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.cc
@@ -0,0 +1,273 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h"
+
+#include <cstdint>
+#include <string>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_endian.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_file_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+namespace quic {
+
+QpackOfflineDecoder::QpackOfflineDecoder()
+ : encoder_stream_error_detected_(false),
+ decoder_(this, &decoder_stream_sender_delegate_) {}
+
+bool QpackOfflineDecoder::DecodeAndVerifyOfflineData(
+ QuicStringPiece input_filename,
+ QuicStringPiece expected_headers_filename) {
+ if (!ParseInputFilename(input_filename)) {
+ QUIC_LOG(ERROR) << "Error parsing input filename " << input_filename;
+ return false;
+ }
+
+ if (!DecodeHeaderBlocksFromFile(input_filename)) {
+ QUIC_LOG(ERROR) << "Error decoding header blocks in " << input_filename;
+ return false;
+ }
+
+ if (!VerifyDecodedHeaderLists(expected_headers_filename)) {
+ QUIC_LOG(ERROR) << "Header lists decoded from " << input_filename
+ << " to not match expected headers parsed from "
+ << expected_headers_filename;
+ return false;
+ }
+
+ return true;
+}
+
+void QpackOfflineDecoder::OnEncoderStreamError(QuicStringPiece error_message) {
+ QUIC_LOG(ERROR) << "Encoder stream error: " << error_message;
+ encoder_stream_error_detected_ = true;
+}
+
+bool QpackOfflineDecoder::ParseInputFilename(QuicStringPiece input_filename) {
+ auto pieces = QuicTextUtils::Split(input_filename, '.');
+
+ if (pieces.size() < 3) {
+ QUIC_LOG(ERROR) << "Not enough fields in input filename " << input_filename;
+ return false;
+ }
+
+ auto piece_it = pieces.rbegin();
+
+ // Acknowledgement mode: 1 for immediate, 0 for none.
+ bool immediate_acknowledgement = false;
+ if (*piece_it == "0") {
+ immediate_acknowledgement = false;
+ } else if (*piece_it == "1") {
+ immediate_acknowledgement = true;
+ } else {
+ QUIC_LOG(ERROR)
+ << "Header acknowledgement field must be 0 or 1 in input filename "
+ << input_filename;
+ return false;
+ }
+
+ ++piece_it;
+
+ // Maximum allowed number of blocked streams.
+ uint64_t max_blocked_streams = 0;
+ if (!QuicTextUtils::StringToUint64(*piece_it, &max_blocked_streams)) {
+ QUIC_LOG(ERROR) << "Error parsing part of input filename \"" << *piece_it
+ << "\" as an integer.";
+ return false;
+ }
+
+ if (max_blocked_streams > 0) {
+ // TODO(bnc): Implement blocked streams.
+ QUIC_LOG(ERROR) << "Blocked streams not implemented.";
+ return false;
+ }
+
+ ++piece_it;
+
+ // Dynamic Table Size in bytes
+ uint64_t dynamic_table_size = 0;
+ if (!QuicTextUtils::StringToUint64(*piece_it, &dynamic_table_size)) {
+ QUIC_LOG(ERROR) << "Error parsing part of input filename \"" << *piece_it
+ << "\" as an integer.";
+ return false;
+ }
+
+ decoder_.SetMaximumDynamicTableCapacity(dynamic_table_size);
+
+ return true;
+}
+
+bool QpackOfflineDecoder::DecodeHeaderBlocksFromFile(
+ QuicStringPiece input_filename) {
+ // Store data in |input_data_storage|; use a QuicStringPiece to efficiently
+ // keep track of remaining portion yet to be decoded.
+ std::string input_data_storage;
+ ReadFileContents(input_filename, &input_data_storage);
+ QuicStringPiece input_data(input_data_storage);
+
+ while (!input_data.empty()) {
+ if (input_data.size() < sizeof(uint64_t) + sizeof(uint32_t)) {
+ QUIC_LOG(ERROR) << "Unexpected end of input file.";
+ return false;
+ }
+
+ uint64_t stream_id = QuicEndian::NetToHost64(
+ *reinterpret_cast<const uint64_t*>(input_data.data()));
+ input_data = input_data.substr(sizeof(uint64_t));
+
+ uint32_t length = QuicEndian::NetToHost32(
+ *reinterpret_cast<const uint32_t*>(input_data.data()));
+ input_data = input_data.substr(sizeof(uint32_t));
+
+ if (input_data.size() < length) {
+ QUIC_LOG(ERROR) << "Unexpected end of input file.";
+ return false;
+ }
+
+ QuicStringPiece data = input_data.substr(0, length);
+ input_data = input_data.substr(length);
+
+ if (stream_id == 0) {
+ decoder_.DecodeEncoderStreamData(data);
+
+ if (encoder_stream_error_detected_) {
+ QUIC_LOG(ERROR) << "Error detected on encoder stream.";
+ return false;
+ }
+
+ continue;
+ }
+
+ test::TestHeadersHandler headers_handler;
+
+ auto progressive_decoder =
+ decoder_.DecodeHeaderBlock(stream_id, &headers_handler);
+ progressive_decoder->Decode(data);
+ progressive_decoder->EndHeaderBlock();
+
+ if (headers_handler.decoding_error_detected()) {
+ QUIC_LOG(ERROR) << "Decoding error on stream " << stream_id;
+ return false;
+ }
+
+ decoded_header_lists_.push_back(headers_handler.ReleaseHeaderList());
+ }
+
+ return true;
+}
+
+bool QpackOfflineDecoder::VerifyDecodedHeaderLists(
+ QuicStringPiece expected_headers_filename) {
+ // Store data in |expected_headers_data_storage|; use a QuicStringPiece to
+ // efficiently keep track of remaining portion yet to be decoded.
+ std::string expected_headers_data_storage;
+ ReadFileContents(expected_headers_filename, &expected_headers_data_storage);
+ QuicStringPiece expected_headers_data(expected_headers_data_storage);
+
+ while (!decoded_header_lists_.empty()) {
+ spdy::SpdyHeaderBlock decoded_header_list =
+ std::move(decoded_header_lists_.front());
+ decoded_header_lists_.pop_front();
+
+ spdy::SpdyHeaderBlock expected_header_list;
+ if (!ReadNextExpectedHeaderList(&expected_headers_data,
+ &expected_header_list)) {
+ QUIC_LOG(ERROR)
+ << "Error parsing expected header list to match next decoded "
+ "header list.";
+ return false;
+ }
+
+ if (!CompareHeaderBlocks(std::move(decoded_header_list),
+ std::move(expected_header_list))) {
+ QUIC_LOG(ERROR) << "Decoded header does not match expected header.";
+ return false;
+ }
+ }
+
+ if (!expected_headers_data.empty()) {
+ QUIC_LOG(ERROR)
+ << "Not enough encoded header lists to match expected ones.";
+ return false;
+ }
+
+ return true;
+}
+
+bool QpackOfflineDecoder::ReadNextExpectedHeaderList(
+ QuicStringPiece* expected_headers_data,
+ spdy::SpdyHeaderBlock* expected_header_list) {
+ while (true) {
+ QuicStringPiece::size_type endline = expected_headers_data->find('\n');
+
+ // Even last header list must be followed by an empty line.
+ if (endline == QuicStringPiece::npos) {
+ QUIC_LOG(ERROR) << "Unexpected end of expected header list file.";
+ return false;
+ }
+
+ if (endline == 0) {
+ // Empty line indicates end of header list.
+ *expected_headers_data = expected_headers_data->substr(1);
+ return true;
+ }
+
+ QuicStringPiece header_field = expected_headers_data->substr(0, endline);
+ auto pieces = QuicTextUtils::Split(header_field, '\t');
+
+ if (pieces.size() != 2) {
+ QUIC_LOG(ERROR) << "Header key and value must be separated by TAB.";
+ return false;
+ }
+
+ expected_header_list->AppendValueOrAddHeader(pieces[0], pieces[1]);
+
+ *expected_headers_data = expected_headers_data->substr(endline + 1);
+ }
+}
+
+bool QpackOfflineDecoder::CompareHeaderBlocks(
+ spdy::SpdyHeaderBlock decoded_header_list,
+ spdy::SpdyHeaderBlock expected_header_list) {
+ if (decoded_header_list == expected_header_list) {
+ return true;
+ }
+
+ // The h2o decoder reshuffles the "content-length" header and pseudo-headers,
+ // see
+ // https://github.com/qpackers/qifs/blob/master/encoded/qpack-03/h2o/README.md.
+ // Remove such headers one by one if they match.
+ const char* kContentLength = "content-length";
+ const char* kPseudoHeaderPrefix = ":";
+ for (spdy::SpdyHeaderBlock::iterator decoded_it = decoded_header_list.begin();
+ decoded_it != decoded_header_list.end();) {
+ const QuicStringPiece key = decoded_it->first;
+ if (key != kContentLength &&
+ !QuicTextUtils::StartsWith(key, kPseudoHeaderPrefix)) {
+ ++decoded_it;
+ continue;
+ }
+ spdy::SpdyHeaderBlock::iterator expected_it =
+ expected_header_list.find(key);
+ if (expected_it == expected_header_list.end() ||
+ decoded_it->second != expected_it->second) {
+ ++decoded_it;
+ continue;
+ }
+ // SpdyHeaderBlock does not support erasing by iterator, only by key.
+ ++decoded_it;
+ expected_header_list.erase(key);
+ // This will invalidate |key|.
+ decoded_header_list.erase(key);
+ }
+
+ return decoded_header_list == expected_header_list;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h
new file mode 100644
index 00000000000..922fd64835b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h
@@ -0,0 +1,71 @@
+// 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_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_
+#define QUICHE_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_
+
+#include <list>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+namespace quic {
+
+// A decoder to read encoded data from a file, decode it, and compare to
+// a list of expected header lists read from another file. File format is
+// described at
+// https://github.com/quicwg/base-drafts/wiki/QPACK-Offline-Interop.
+class QpackOfflineDecoder : public QpackDecoder::EncoderStreamErrorDelegate {
+ public:
+ QpackOfflineDecoder();
+ ~QpackOfflineDecoder() override = default;
+
+ // Read encoded header blocks and encoder stream data from |input_filename|
+ // and decode them, read expected header lists from
+ // |expected_headers_filename|, and compare decoded header lists to expected
+ // ones. Returns true if there is an equal number of them and the
+ // corresponding ones match, false otherwise.
+ bool DecodeAndVerifyOfflineData(QuicStringPiece input_filename,
+ QuicStringPiece expected_headers_filename);
+
+ // QpackDecoder::EncoderStreamErrorDelegate implementation:
+ void OnEncoderStreamError(QuicStringPiece error_message) override;
+
+ private:
+ // Parse decoder parameters from |input_filename| and set up |decoder_|
+ // accordingly.
+ bool ParseInputFilename(QuicStringPiece input_filename);
+
+ // Read encoded header blocks and encoder stream data from |input_filename|,
+ // pass them to |decoder_| for decoding, and add decoded header lists to
+ // |decoded_header_lists_|.
+ bool DecodeHeaderBlocksFromFile(QuicStringPiece input_filename);
+
+ // Read expected header lists from |expected_headers_filename| and verify
+ // decoded header lists in |decoded_header_lists_| against them.
+ bool VerifyDecodedHeaderLists(QuicStringPiece expected_headers_filename);
+
+ // Parse next header list from |*expected_headers_data| into
+ // |*expected_header_list|, removing consumed data from the beginning of
+ // |*expected_headers_data|. Returns true on success, false if parsing fails.
+ bool ReadNextExpectedHeaderList(QuicStringPiece* expected_headers_data,
+ spdy::SpdyHeaderBlock* expected_header_list);
+
+ // Compare two header lists. Allow for different orders of certain headers as
+ // described at
+ // https://github.com/qpackers/qifs/blob/master/encoded/qpack-03/h2o/README.md.
+ bool CompareHeaderBlocks(spdy::SpdyHeaderBlock decoded_header_list,
+ spdy::SpdyHeaderBlock expected_header_list);
+
+ bool encoder_stream_error_detected_;
+ test::NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate_;
+ QpackDecoder decoder_;
+ std::list<spdy::SpdyHeaderBlock> decoded_header_lists_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_OFFLINE_QPACK_OFFLINE_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder_bin.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder_bin.cc
new file mode 100644
index 00000000000..a4f5373911c
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder_bin.cc
@@ -0,0 +1,44 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/offline/qpack_offline_decoder.h"
+
+#include <cstddef>
+#include <iostream>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_flags.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+int main(int argc, char* argv[]) {
+ const char* usage =
+ "Usage: qpack_offline_decoder input_filename expected_headers_filename "
+ "....";
+ std::vector<std::string> args =
+ quic::QuicParseCommandLineFlags(usage, argc, argv);
+
+ if (args.size() < 2 || args.size() % 2 != 0) {
+ quic::QuicPrintCommandLineFlagHelp(usage);
+ return 1;
+ }
+
+ size_t i;
+ for (i = 0; 2 * i < args.size(); ++i) {
+ const quic::QuicStringPiece input_filename(args[2 * i]);
+ const quic::QuicStringPiece expected_headers_filename(args[2 * i + 1]);
+
+ // Every file represents a different connection,
+ // therefore every file needs a fresh decoding context.
+ quic::QpackOfflineDecoder decoder;
+ if (!decoder.DecodeAndVerifyOfflineData(input_filename,
+ expected_headers_filename)) {
+ return 1;
+ }
+ }
+
+ std::cout << "Successfully verified " << i << " pairs of input files."
+ << std::endl;
+
+ return 0;
+}
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.cc
new file mode 100644
index 00000000000..dd4487d7060
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.cc
@@ -0,0 +1,200 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Validate that
+// * in each instruction, the bits of |value| that are zero in |mask| are zero;
+// * every byte matches exactly one opcode.
+void ValidateLangague(const QpackLanguage* language) {
+#ifndef NDEBUG
+ for (const auto* instruction : *language) {
+ DCHECK_EQ(0, instruction->opcode.value & ~instruction->opcode.mask);
+ }
+
+ for (uint8_t byte = 0; byte < std::numeric_limits<uint8_t>::max(); ++byte) {
+ size_t match_count = 0;
+ for (const auto* instruction : *language) {
+ if ((byte & instruction->opcode.mask) == instruction->opcode.value) {
+ ++match_count;
+ }
+ }
+ DCHECK_EQ(1u, match_count) << static_cast<int>(byte);
+ }
+#endif
+}
+
+} // namespace
+
+bool operator==(const QpackInstructionOpcode& a,
+ const QpackInstructionOpcode& b) {
+ return std::tie(a.value, a.mask) == std::tie(b.value, b.mask);
+}
+
+const QpackInstruction* InsertWithNameReferenceInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b10000000, 0b10000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kSbit, 0b01000000},
+ {QpackInstructionFieldType::kVarint, 6},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackInstruction* InsertWithoutNameReferenceInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b01000000, 0b11000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kName, 5},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackInstruction* DuplicateInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00000000, 0b11100000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 5}}};
+ return instruction;
+}
+
+const QpackInstruction* SetDynamicTableCapacityInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00100000, 0b11100000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 5}}};
+ return instruction;
+}
+
+const QpackLanguage* QpackEncoderStreamLanguage() {
+ static const QpackLanguage* const language = new QpackLanguage{
+ InsertWithNameReferenceInstruction(),
+ InsertWithoutNameReferenceInstruction(), DuplicateInstruction(),
+ SetDynamicTableCapacityInstruction()};
+ ValidateLangague(language);
+ return language;
+}
+
+const QpackInstruction* InsertCountIncrementInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00000000, 0b11000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 6}}};
+ return instruction;
+}
+
+const QpackInstruction* HeaderAcknowledgementInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b10000000, 0b10000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 7}}};
+ return instruction;
+}
+
+const QpackInstruction* StreamCancellationInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b01000000, 0b11000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 6}}};
+ return instruction;
+}
+
+const QpackLanguage* QpackDecoderStreamLanguage() {
+ static const QpackLanguage* const language = new QpackLanguage{
+ InsertCountIncrementInstruction(), HeaderAcknowledgementInstruction(),
+ StreamCancellationInstruction()};
+ ValidateLangague(language);
+ return language;
+}
+
+const QpackInstruction* QpackPrefixInstruction() {
+ // This opcode matches every input.
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00000000, 0b00000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kVarint, 8},
+ {QpackInstructionFieldType::kSbit, 0b10000000},
+ {QpackInstructionFieldType::kVarint2, 7}}};
+ return instruction;
+}
+
+const QpackLanguage* QpackPrefixLanguage() {
+ static const QpackLanguage* const language =
+ new QpackLanguage{QpackPrefixInstruction()};
+ ValidateLangague(language);
+ return language;
+}
+
+const QpackInstruction* QpackIndexedHeaderFieldInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b10000000, 0b10000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kSbit, 0b01000000},
+ {QpackInstructionFieldType::kVarint, 6}}};
+ return instruction;
+}
+
+const QpackInstruction* QpackIndexedHeaderFieldPostBaseInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00010000, 0b11110000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode, {{QpackInstructionFieldType::kVarint, 4}}};
+ return instruction;
+}
+
+const QpackInstruction* QpackLiteralHeaderFieldNameReferenceInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b01000000, 0b11000000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kSbit, 0b00010000},
+ {QpackInstructionFieldType::kVarint, 4},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackInstruction* QpackLiteralHeaderFieldPostBaseInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00000000, 0b11110000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kVarint, 3},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackInstruction* QpackLiteralHeaderFieldInstruction() {
+ static const QpackInstructionOpcode* const opcode =
+ new QpackInstructionOpcode{0b00100000, 0b11100000};
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{*opcode,
+ {{QpackInstructionFieldType::kName, 3},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackLanguage* QpackRequestStreamLanguage() {
+ static const QpackLanguage* const language =
+ new QpackLanguage{QpackIndexedHeaderFieldInstruction(),
+ QpackIndexedHeaderFieldPostBaseInstruction(),
+ QpackLiteralHeaderFieldNameReferenceInstruction(),
+ QpackLiteralHeaderFieldPostBaseInstruction(),
+ QpackLiteralHeaderFieldInstruction()};
+ ValidateLangague(language);
+ return language;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.h
new file mode 100644
index 00000000000..2812e63302d
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_constants.h
@@ -0,0 +1,147 @@
+// 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_QUIC_CORE_QPACK_QPACK_CONSTANTS_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_CONSTANTS_H_
+
+#include <cstdint>
+#include <string>
+#include <tuple>
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+
+namespace quic {
+
+// Each instruction is identified with an opcode in the first byte.
+// |mask| determines which bits are part of the opcode.
+// |value| is the value of these bits. (Other bits in value must be zero.)
+struct QUIC_EXPORT_PRIVATE QpackInstructionOpcode {
+ uint8_t value;
+ uint8_t mask;
+};
+
+bool operator==(const QpackInstructionOpcode& a,
+ const QpackInstructionOpcode& b);
+
+// Possible types of an instruction field. Decoding a static bit does not
+// consume the current byte. Decoding an integer or a length-prefixed string
+// literal consumes all bytes containing the field value.
+enum class QpackInstructionFieldType {
+ // A single bit indicating whether the index refers to the static table, or
+ // indicating the sign of Delta Base. Called "S" bit because both "static"
+ // and "sign" start with the letter "S".
+ kSbit,
+ // An integer encoded with variable length encoding. This could be an index,
+ // stream ID, maximum size, or Encoded Required Insert Count.
+ kVarint,
+ // A second integer encoded with variable length encoding. This could be
+ // Delta Base.
+ kVarint2,
+ // A header name or header value encoded as:
+ // a bit indicating whether it is Huffman encoded;
+ // the encoded length of the string;
+ // the header name or value optionally Huffman encoded.
+ kName,
+ kValue
+};
+
+// Each instruction field has a type and a parameter.
+// The meaning of the parameter depends on the field type.
+struct QUIC_EXPORT_PRIVATE QpackInstructionField {
+ QpackInstructionFieldType type;
+ // For a kSbit field, |param| is a mask with exactly one bit set.
+ // For kVarint fields, |param| is the prefix length of the integer encoding.
+ // For kName and kValue fields, |param| is the prefix length of the length of
+ // the string, and the bit immediately preceding the prefix is interpreted as
+ // the Huffman bit.
+ uint8_t param;
+};
+
+using QpackInstructionFields = std::vector<QpackInstructionField>;
+
+// A QPACK instruction consists of an opcode identifying the instruction,
+// followed by a non-empty list of fields. The last field must be integer or
+// string literal type to guarantee that all bytes of the instruction are
+// consumed.
+struct QUIC_EXPORT_PRIVATE QpackInstruction {
+ QpackInstruction(const QpackInstruction&) = delete;
+ const QpackInstruction& operator=(const QpackInstruction&) = delete;
+
+ QpackInstructionOpcode opcode;
+ QpackInstructionFields fields;
+};
+
+// A language is a collection of instructions. The order does not matter.
+// Every possible input must match exactly one instruction.
+using QpackLanguage = std::vector<const QpackInstruction*>;
+
+// TODO(bnc): Move this into HpackVarintEncoder.
+// The integer encoder can encode up to 2^64-1, which can take up to 10 bytes
+// (each carrying 7 bits) after the prefix.
+const uint8_t kMaxExtensionBytesForVarintEncoding = 10;
+
+// Wire format defined in
+// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5
+
+// 5.2 Encoder stream instructions
+
+// 5.2.1 Insert With Name Reference
+const QpackInstruction* InsertWithNameReferenceInstruction();
+
+// 5.2.2 Insert Without Name Reference
+const QpackInstruction* InsertWithoutNameReferenceInstruction();
+
+// 5.2.3 Duplicate
+const QpackInstruction* DuplicateInstruction();
+
+// 5.2.4 Dynamic Table Size Update
+const QpackInstruction* SetDynamicTableCapacityInstruction();
+
+// Encoder stream language.
+const QpackLanguage* QpackEncoderStreamLanguage();
+
+// 5.3 Decoder stream instructions
+
+// 5.3.1 Insert Count Increment
+const QpackInstruction* InsertCountIncrementInstruction();
+
+// 5.3.2 Header Acknowledgement
+const QpackInstruction* HeaderAcknowledgementInstruction();
+
+// 5.3.3 Stream Cancellation
+const QpackInstruction* StreamCancellationInstruction();
+
+// Decoder stream language.
+const QpackLanguage* QpackDecoderStreamLanguage();
+
+// 5.4.1. Header data prefix instructions
+
+const QpackInstruction* QpackPrefixInstruction();
+
+const QpackLanguage* QpackPrefixLanguage();
+
+// 5.4.2. Request and push stream instructions
+
+// 5.4.2.1. Indexed Header Field
+const QpackInstruction* QpackIndexedHeaderFieldInstruction();
+
+// 5.4.2.2. Indexed Header Field With Post-Base Index
+const QpackInstruction* QpackIndexedHeaderFieldPostBaseInstruction();
+
+// 5.4.2.3. Literal Header Field With Name Reference
+const QpackInstruction* QpackLiteralHeaderFieldNameReferenceInstruction();
+
+// 5.4.2.4. Literal Header Field With Post-Base Name Reference
+const QpackInstruction* QpackLiteralHeaderFieldPostBaseInstruction();
+
+// 5.4.2.5. Literal Header Field Without Name Reference
+const QpackInstruction* QpackLiteralHeaderFieldInstruction();
+
+// Request and push stream language.
+const QpackLanguage* QpackRequestStreamLanguage();
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_CONSTANTS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc
new file mode 100644
index 00000000000..bcfe0e4e188
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.cc
@@ -0,0 +1,72 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h"
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+
+namespace quic {
+
+QpackDecodedHeadersAccumulator::QpackDecodedHeadersAccumulator(
+ QuicStreamId id,
+ QpackDecoder* qpack_decoder,
+ size_t max_header_list_size)
+ : decoder_(qpack_decoder->DecodeHeaderBlock(id, this)),
+ uncompressed_header_bytes_(0),
+ compressed_header_bytes_(0),
+ error_detected_(false) {
+ quic_header_list_.set_max_header_list_size(max_header_list_size);
+ quic_header_list_.OnHeaderBlockStart();
+}
+
+void QpackDecodedHeadersAccumulator::OnHeaderDecoded(QuicStringPiece name,
+ QuicStringPiece value) {
+ DCHECK(!error_detected_);
+
+ uncompressed_header_bytes_ += name.size() + value.size();
+ quic_header_list_.OnHeader(name, value);
+}
+
+void QpackDecodedHeadersAccumulator::OnDecodingCompleted() {}
+
+void QpackDecodedHeadersAccumulator::OnDecodingErrorDetected(
+ QuicStringPiece error_message) {
+ DCHECK(!error_detected_);
+
+ error_detected_ = true;
+ // Copy error message to ensure it remains valid for the lifetime of |this|.
+ error_message_.assign(error_message.data(), error_message.size());
+}
+
+bool QpackDecodedHeadersAccumulator::Decode(QuicStringPiece data) {
+ DCHECK(!error_detected_);
+
+ compressed_header_bytes_ += data.size();
+ decoder_->Decode(data);
+
+ return !error_detected_;
+}
+
+bool QpackDecodedHeadersAccumulator::EndHeaderBlock() {
+ DCHECK(!error_detected_);
+
+ decoder_->EndHeaderBlock();
+
+ quic_header_list_.OnHeaderBlockEnd(uncompressed_header_bytes_,
+ compressed_header_bytes_);
+
+ return !error_detected_;
+}
+
+const QuicHeaderList& QpackDecodedHeadersAccumulator::quic_header_list() const {
+ DCHECK(!error_detected_);
+ return quic_header_list_;
+}
+
+QuicStringPiece QpackDecodedHeadersAccumulator::error_message() const {
+ DCHECK(error_detected_);
+ return error_message_;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h
new file mode 100644
index 00000000000..5db88d71b33
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h
@@ -0,0 +1,66 @@
+// Copyright (c) 2019 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_QUIC_CORE_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_
+
+#include <cstddef>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/http/quic_header_list.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QpackDecoder;
+
+// A class that creates and owns a QpackProgressiveDecoder instance, accumulates
+// decoded headers in a QuicHeaderList, and keeps track of uncompressed and
+// compressed size so that it can be passed to QuicHeaderList::EndHeaderBlock().
+class QUIC_EXPORT_PRIVATE QpackDecodedHeadersAccumulator
+ : public QpackProgressiveDecoder::HeadersHandlerInterface {
+ public:
+ QpackDecodedHeadersAccumulator(QuicStreamId id,
+ QpackDecoder* qpack_decoder,
+ size_t max_header_list_size);
+ virtual ~QpackDecodedHeadersAccumulator() = default;
+
+ // QpackProgressiveDecoder::HeadersHandlerInterface implementation.
+ // These methods should only be called by |decoder_|.
+ void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override;
+ void OnDecodingCompleted() override;
+ void OnDecodingErrorDetected(QuicStringPiece error_message) override;
+
+ // Decode payload data. Returns true on success, false on error.
+ // Must not be called if an error has been detected.
+ // Must not be called after EndHeaderBlock().
+ bool Decode(QuicStringPiece data);
+
+ // Signal end of HEADERS frame. Returns true on success, false on error.
+ // Must not be called if an error has been detected.
+ // Must not be called more that once.
+ bool EndHeaderBlock();
+
+ // Returns accumulated header list.
+ const QuicHeaderList& quic_header_list() const;
+
+ // Returns error message.
+ // Must not be called unless an error has been detected.
+ QuicStringPiece error_message() const;
+
+ private:
+ std::unique_ptr<QpackProgressiveDecoder> decoder_;
+ QuicHeaderList quic_header_list_;
+ size_t uncompressed_header_bytes_;
+ size_t compressed_header_bytes_;
+ bool error_detected_;
+ std::string error_message_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODED_HEADERS_ACCUMULATOR_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
new file mode 100644
index 00000000000..c3c0b8101b0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator_test.cc
@@ -0,0 +1,119 @@
+// Copyright (c) 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoded_headers_accumulator.h"
+
+#include <cstring>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Pair;
+using ::testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+// Arbitrary stream ID used for testing.
+QuicStreamId kTestStreamId = 1;
+
+// Limit on header list size.
+const size_t kMaxHeaderListSize = 100;
+
+// Header Acknowledgement decoder stream instruction with stream_id = 1.
+const char* const kHeaderAcknowledgement = "\x81";
+
+} // anonymous namespace
+
+class QpackDecodedHeadersAccumulatorTest : public QuicTest {
+ protected:
+ QpackDecodedHeadersAccumulatorTest()
+ : qpack_decoder_(&encoder_stream_error_delegate_,
+ &decoder_stream_sender_delegate_),
+ accumulator_(kTestStreamId, &qpack_decoder_, kMaxHeaderListSize) {}
+
+ NoopEncoderStreamErrorDelegate encoder_stream_error_delegate_;
+ StrictMock<MockDecoderStreamSenderDelegate> decoder_stream_sender_delegate_;
+ QpackDecoder qpack_decoder_;
+ QpackDecodedHeadersAccumulator accumulator_;
+};
+
+// HEADERS frame payload must have a complete Header Block Prefix.
+TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyPayload) {
+ EXPECT_FALSE(accumulator_.EndHeaderBlock());
+ EXPECT_EQ("Incomplete header data prefix.", accumulator_.error_message());
+}
+
+// HEADERS frame payload must have a complete Header Block Prefix.
+TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedHeaderBlockPrefix) {
+ EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("00")));
+ EXPECT_FALSE(accumulator_.EndHeaderBlock());
+ EXPECT_EQ("Incomplete header data prefix.", accumulator_.error_message());
+}
+
+TEST_F(QpackDecodedHeadersAccumulatorTest, EmptyHeaderList) {
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("0000")));
+ EXPECT_TRUE(accumulator_.EndHeaderBlock());
+
+ EXPECT_TRUE(accumulator_.quic_header_list().empty());
+}
+
+// This payload is the prefix of a valid payload, but EndHeaderBlock() is called
+// before it can be completely decoded.
+TEST_F(QpackDecodedHeadersAccumulatorTest, TruncatedPayload) {
+ EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode("00002366")));
+ EXPECT_FALSE(accumulator_.EndHeaderBlock());
+ EXPECT_EQ("Incomplete header block.", accumulator_.error_message());
+}
+
+// This payload is invalid because it refers to a non-existing static entry.
+TEST_F(QpackDecodedHeadersAccumulatorTest, InvalidPayload) {
+ EXPECT_FALSE(accumulator_.Decode(QuicTextUtils::HexDecode("0000ff23ff24")));
+ EXPECT_EQ("Static table entry not found.", accumulator_.error_message());
+}
+
+TEST_F(QpackDecodedHeadersAccumulatorTest, Success) {
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ std::string encoded_data(QuicTextUtils::HexDecode("000023666f6f03626172"));
+ EXPECT_TRUE(accumulator_.Decode(encoded_data));
+ EXPECT_TRUE(accumulator_.EndHeaderBlock());
+
+ const QuicHeaderList& header_list = accumulator_.quic_header_list();
+ EXPECT_THAT(header_list, ElementsAre(Pair("foo", "bar")));
+
+ EXPECT_EQ(strlen("foo") + strlen("bar"),
+ header_list.uncompressed_header_bytes());
+ EXPECT_EQ(encoded_data.size(), header_list.compressed_header_bytes());
+}
+
+TEST_F(QpackDecodedHeadersAccumulatorTest, ExceedingLimit) {
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ // Total length of header list exceeds kMaxHeaderListSize.
+ EXPECT_TRUE(accumulator_.Decode(QuicTextUtils::HexDecode(
+ "0000" // header block prefix
+ "26666f6f626172" // header key: "foobar"
+ "7d61616161616161616161616161616161616161" // header value: 'a' 125 times
+ "616161616161616161616161616161616161616161616161616161616161616161616161"
+ "616161616161616161616161616161616161616161616161616161616161616161616161"
+ "61616161616161616161616161616161616161616161616161616161616161616161")));
+ EXPECT_TRUE(accumulator_.EndHeaderBlock());
+
+ // QuicHeaderList signals header list over limit by clearing it.
+ EXPECT_TRUE(accumulator_.quic_header_list().empty());
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc
new file mode 100644
index 00000000000..9efccb6c029
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.cc
@@ -0,0 +1,141 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+QpackDecoder::QpackDecoder(
+ EncoderStreamErrorDelegate* encoder_stream_error_delegate,
+ QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate)
+ : encoder_stream_error_delegate_(encoder_stream_error_delegate),
+ encoder_stream_receiver_(this),
+ decoder_stream_sender_(decoder_stream_sender_delegate) {
+ DCHECK(encoder_stream_error_delegate_);
+ DCHECK(decoder_stream_sender_delegate);
+}
+
+QpackDecoder::~QpackDecoder() {}
+
+void QpackDecoder::SetMaximumDynamicTableCapacity(
+ uint64_t maximum_dynamic_table_capacity) {
+ header_table_.SetMaximumDynamicTableCapacity(maximum_dynamic_table_capacity);
+}
+
+void QpackDecoder::OnStreamReset(QuicStreamId stream_id) {
+ decoder_stream_sender_.SendStreamCancellation(stream_id);
+}
+
+void QpackDecoder::DecodeEncoderStreamData(QuicStringPiece data) {
+ encoder_stream_receiver_.Decode(data);
+}
+
+void QpackDecoder::OnInsertWithNameReference(bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value) {
+ if (is_static) {
+ auto entry = header_table_.LookupEntry(/* is_static = */ true, name_index);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Invalid static table entry.");
+ return;
+ }
+
+ entry = header_table_.InsertEntry(entry->name(), value);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Error inserting entry with name reference.");
+ }
+ return;
+ }
+
+ uint64_t absolute_index;
+ if (!EncoderStreamRelativeIndexToAbsoluteIndex(name_index, &absolute_index)) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Invalid relative index.");
+ return;
+ }
+
+ const QpackEntry* entry =
+ header_table_.LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Dynamic table entry not found.");
+ return;
+ }
+ entry = header_table_.InsertEntry(entry->name(), value);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Error inserting entry with name reference.");
+ }
+}
+
+void QpackDecoder::OnInsertWithoutNameReference(QuicStringPiece name,
+ QuicStringPiece value) {
+ const QpackEntry* entry = header_table_.InsertEntry(name, value);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Error inserting literal entry.");
+ }
+}
+
+void QpackDecoder::OnDuplicate(uint64_t index) {
+ uint64_t absolute_index;
+ if (!EncoderStreamRelativeIndexToAbsoluteIndex(index, &absolute_index)) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Invalid relative index.");
+ return;
+ }
+
+ const QpackEntry* entry =
+ header_table_.LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Dynamic table entry not found.");
+ return;
+ }
+ entry = header_table_.InsertEntry(entry->name(), entry->value());
+ if (!entry) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Error inserting duplicate entry.");
+ }
+}
+
+void QpackDecoder::OnSetDynamicTableCapacity(uint64_t capacity) {
+ if (!header_table_.SetDynamicTableCapacity(capacity)) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(
+ "Error updating dynamic table capacity.");
+ }
+}
+
+void QpackDecoder::OnErrorDetected(QuicStringPiece error_message) {
+ encoder_stream_error_delegate_->OnEncoderStreamError(error_message);
+}
+
+bool QpackDecoder::EncoderStreamRelativeIndexToAbsoluteIndex(
+ uint64_t relative_index,
+ uint64_t* absolute_index) const {
+ if (relative_index == std::numeric_limits<uint64_t>::max() ||
+ relative_index + 1 > std::numeric_limits<uint64_t>::max() -
+ header_table_.inserted_entry_count()) {
+ return false;
+ }
+
+ *absolute_index = header_table_.inserted_entry_count() - relative_index - 1;
+ return true;
+}
+
+std::unique_ptr<QpackProgressiveDecoder> QpackDecoder::DecodeHeaderBlock(
+ QuicStreamId stream_id,
+ QpackProgressiveDecoder::HeadersHandlerInterface* handler) {
+ return QuicMakeUnique<QpackProgressiveDecoder>(
+ stream_id, &header_table_, &decoder_stream_sender_, handler);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h
new file mode 100644
index 00000000000..c3b28a3e10b
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h
@@ -0,0 +1,102 @@
+// 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_QUIC_CORE_QPACK_QPACK_DECODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// QPACK decoder class. Exactly one instance should exist per QUIC connection.
+// This class vends a new QpackProgressiveDecoder instance for each new header
+// list to be encoded.
+class QUIC_EXPORT_PRIVATE QpackDecoder
+ : public QpackEncoderStreamReceiver::Delegate {
+ public:
+ // Interface for receiving notification that an error has occurred on the
+ // encoder stream. This MUST be treated as a connection error of type
+ // HTTP_QPACK_ENCODER_STREAM_ERROR.
+ class QUIC_EXPORT_PRIVATE EncoderStreamErrorDelegate {
+ public:
+ virtual ~EncoderStreamErrorDelegate() {}
+
+ virtual void OnEncoderStreamError(QuicStringPiece error_message) = 0;
+ };
+
+ QpackDecoder(
+ EncoderStreamErrorDelegate* encoder_stream_error_delegate,
+ QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate);
+ ~QpackDecoder() override;
+
+ // Set maximum capacity of dynamic table.
+ // This method must only be called at most once.
+ void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity);
+
+ // Signal to the peer's encoder that a stream is reset. This lets the peer's
+ // encoder know that no more header blocks will be processed on this stream,
+ // therefore references to dynamic table entries shall not prevent their
+ // eviction.
+ // This method should be called regardless of whether a header block is being
+ // decoded on that stream, because a header block might be in flight from the
+ // peer.
+ // This method should be called every time a request or push stream is reset
+ // for any reason: for example, client cancels request, or a decoding error
+ // occurs and HeadersHandlerInterface::OnDecodingErrorDetected() is called.
+ // This method should also be called if the stream is reset by the peer,
+ // because the peer's encoder can only evict entries referenced by header
+ // blocks once it receives acknowledgement from this endpoint that the stream
+ // is reset.
+ // However, this method should not be called if the stream is closed normally
+ // using the FIN bit.
+ void OnStreamReset(QuicStreamId stream_id);
+
+ // Factory method to create a QpackProgressiveDecoder for decoding a header
+ // block. |handler| must remain valid until the returned
+ // QpackProgressiveDecoder instance is destroyed or the decoder calls
+ // |handler->OnHeaderBlockEnd()|.
+ std::unique_ptr<QpackProgressiveDecoder> DecodeHeaderBlock(
+ QuicStreamId stream_id,
+ QpackProgressiveDecoder::HeadersHandlerInterface* handler);
+
+ // Decode data received on the encoder stream.
+ void DecodeEncoderStreamData(QuicStringPiece data);
+
+ // QpackEncoderStreamReceiver::Delegate implementation
+ void OnInsertWithNameReference(bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value) override;
+ void OnInsertWithoutNameReference(QuicStringPiece name,
+ QuicStringPiece value) override;
+ void OnDuplicate(uint64_t index) override;
+ void OnSetDynamicTableCapacity(uint64_t capacity) override;
+ void OnErrorDetected(QuicStringPiece error_message) override;
+
+ private:
+ // The encoder stream uses relative index (but different from the kind of
+ // relative index used on a request stream). This method converts relative
+ // index to absolute index (zero based). It returns true on success, or false
+ // if conversion fails due to overflow/underflow.
+ bool EncoderStreamRelativeIndexToAbsoluteIndex(
+ uint64_t relative_index,
+ uint64_t* absolute_index) const;
+
+ EncoderStreamErrorDelegate* const encoder_stream_error_delegate_;
+ QpackEncoderStreamReceiver encoder_stream_receiver_;
+ QpackDecoderStreamSender decoder_stream_sender_;
+ QpackHeaderTable header_table_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc
new file mode 100644
index 00000000000..559ce433376
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.cc
@@ -0,0 +1,52 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h"
+
+#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
+#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+
+namespace quic {
+
+QpackDecoderStreamReceiver::QpackDecoderStreamReceiver(Delegate* delegate)
+ : instruction_decoder_(QpackDecoderStreamLanguage(), this),
+ delegate_(delegate),
+ error_detected_(false) {
+ DCHECK(delegate_);
+}
+
+void QpackDecoderStreamReceiver::Decode(QuicStringPiece data) {
+ if (data.empty() || error_detected_) {
+ return;
+ }
+
+ instruction_decoder_.Decode(data);
+}
+
+bool QpackDecoderStreamReceiver::OnInstructionDecoded(
+ const QpackInstruction* instruction) {
+ if (instruction == InsertCountIncrementInstruction()) {
+ delegate_->OnInsertCountIncrement(instruction_decoder_.varint());
+ return true;
+ }
+
+ if (instruction == HeaderAcknowledgementInstruction()) {
+ delegate_->OnHeaderAcknowledgement(instruction_decoder_.varint());
+ return true;
+ }
+
+ DCHECK_EQ(instruction, StreamCancellationInstruction());
+ delegate_->OnStreamCancellation(instruction_decoder_.varint());
+ return true;
+}
+
+void QpackDecoderStreamReceiver::OnError(QuicStringPiece error_message) {
+ DCHECK(!error_detected_);
+
+ error_detected_ = true;
+ delegate_->OnErrorDetected(error_message);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h
new file mode 100644
index 00000000000..61c2773a62e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h
@@ -0,0 +1,63 @@
+// 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_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_RECEIVER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_RECEIVER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// This class decodes data received on the decoder stream,
+// and passes it along to its Delegate.
+class QUIC_EXPORT_PRIVATE QpackDecoderStreamReceiver
+ : public QpackInstructionDecoder::Delegate {
+ public:
+ // An interface for handling instructions decoded from the decoder stream, see
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.3
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // 5.3.1 Insert Count Increment
+ virtual void OnInsertCountIncrement(uint64_t increment) = 0;
+ // 5.3.2 Header Acknowledgement
+ virtual void OnHeaderAcknowledgement(QuicStreamId stream_id) = 0;
+ // 5.3.3 Stream Cancellation
+ virtual void OnStreamCancellation(QuicStreamId stream_id) = 0;
+ // Decoding error
+ virtual void OnErrorDetected(QuicStringPiece error_message) = 0;
+ };
+
+ explicit QpackDecoderStreamReceiver(Delegate* delegate);
+ QpackDecoderStreamReceiver() = delete;
+ QpackDecoderStreamReceiver(const QpackDecoderStreamReceiver&) = delete;
+ QpackDecoderStreamReceiver& operator=(const QpackDecoderStreamReceiver&) =
+ delete;
+
+ // Decode data and call appropriate Delegate method after each decoded
+ // instruction. Once an error occurs, Delegate::OnErrorDetected() is called,
+ // and all further data is ignored.
+ void Decode(QuicStringPiece data);
+
+ // QpackInstructionDecoder::Delegate implementation.
+ bool OnInstructionDecoded(const QpackInstruction* instruction) override;
+ void OnError(QuicStringPiece error_message) override;
+
+ private:
+ QpackInstructionDecoder instruction_decoder_;
+ Delegate* const delegate_;
+
+ // True if a decoding error has been detected.
+ bool error_detected_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_RECEIVER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
new file mode 100644
index 00000000000..fc7225f8139
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver_test.cc
@@ -0,0 +1,91 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using testing::Eq;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockDelegate : public QpackDecoderStreamReceiver::Delegate {
+ public:
+ ~MockDelegate() override = default;
+
+ MOCK_METHOD1(OnInsertCountIncrement, void(uint64_t increment));
+ MOCK_METHOD1(OnHeaderAcknowledgement, void(QuicStreamId stream_id));
+ MOCK_METHOD1(OnStreamCancellation, void(QuicStreamId stream_id));
+ MOCK_METHOD1(OnErrorDetected, void(QuicStringPiece error_message));
+};
+
+class QpackDecoderStreamReceiverTest : public QuicTest {
+ protected:
+ QpackDecoderStreamReceiverTest() : stream_(&delegate_) {}
+ ~QpackDecoderStreamReceiverTest() override = default;
+
+ QpackDecoderStreamReceiver stream_;
+ StrictMock<MockDelegate> delegate_;
+};
+
+TEST_F(QpackDecoderStreamReceiverTest, InsertCountIncrement) {
+ EXPECT_CALL(delegate_, OnInsertCountIncrement(0));
+ stream_.Decode(QuicTextUtils::HexDecode("00"));
+
+ EXPECT_CALL(delegate_, OnInsertCountIncrement(10));
+ stream_.Decode(QuicTextUtils::HexDecode("0a"));
+
+ EXPECT_CALL(delegate_, OnInsertCountIncrement(63));
+ stream_.Decode(QuicTextUtils::HexDecode("3f00"));
+
+ EXPECT_CALL(delegate_, OnInsertCountIncrement(200));
+ stream_.Decode(QuicTextUtils::HexDecode("3f8901"));
+
+ EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large.")));
+ stream_.Decode(QuicTextUtils::HexDecode("3fffffffffffffffffffff"));
+}
+
+TEST_F(QpackDecoderStreamReceiverTest, HeaderAcknowledgement) {
+ EXPECT_CALL(delegate_, OnHeaderAcknowledgement(0));
+ stream_.Decode(QuicTextUtils::HexDecode("80"));
+
+ EXPECT_CALL(delegate_, OnHeaderAcknowledgement(37));
+ stream_.Decode(QuicTextUtils::HexDecode("a5"));
+
+ EXPECT_CALL(delegate_, OnHeaderAcknowledgement(127));
+ stream_.Decode(QuicTextUtils::HexDecode("ff00"));
+
+ EXPECT_CALL(delegate_, OnHeaderAcknowledgement(503));
+ stream_.Decode(QuicTextUtils::HexDecode("fff802"));
+
+ EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large.")));
+ stream_.Decode(QuicTextUtils::HexDecode("ffffffffffffffffffffff"));
+}
+
+TEST_F(QpackDecoderStreamReceiverTest, StreamCancellation) {
+ EXPECT_CALL(delegate_, OnStreamCancellation(0));
+ stream_.Decode(QuicTextUtils::HexDecode("40"));
+
+ EXPECT_CALL(delegate_, OnStreamCancellation(19));
+ stream_.Decode(QuicTextUtils::HexDecode("53"));
+
+ EXPECT_CALL(delegate_, OnStreamCancellation(63));
+ stream_.Decode(QuicTextUtils::HexDecode("7f00"));
+
+ EXPECT_CALL(delegate_, OnStreamCancellation(110));
+ stream_.Decode(QuicTextUtils::HexDecode("7f2f"));
+
+ EXPECT_CALL(delegate_, OnErrorDetected(Eq("Encoded integer too large.")));
+ stream_.Decode(QuicTextUtils::HexDecode("7fffffffffffffffffffff"));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc
new file mode 100644
index 00000000000..9474d6cdf93
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.cc
@@ -0,0 +1,61 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h"
+
+#include <cstddef>
+#include <limits>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QpackDecoderStreamSender::QpackDecoderStreamSender(Delegate* delegate)
+ : delegate_(delegate) {
+ DCHECK(delegate_);
+}
+
+void QpackDecoderStreamSender::SendInsertCountIncrement(uint64_t increment) {
+ instruction_encoder_.set_varint(increment);
+
+ instruction_encoder_.Encode(InsertCountIncrementInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteDecoderStreamData(output);
+}
+
+void QpackDecoderStreamSender::SendHeaderAcknowledgement(
+ QuicStreamId stream_id) {
+ instruction_encoder_.set_varint(stream_id);
+
+ instruction_encoder_.Encode(HeaderAcknowledgementInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteDecoderStreamData(output);
+}
+
+void QpackDecoderStreamSender::SendStreamCancellation(QuicStreamId stream_id) {
+ instruction_encoder_.set_varint(stream_id);
+
+ instruction_encoder_.Encode(StreamCancellationInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteDecoderStreamData(output);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h
new file mode 100644
index 00000000000..a791173f801
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h
@@ -0,0 +1,55 @@
+// 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_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_SENDER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_SENDER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// This class serializes (encodes) instructions for transmission on the decoder
+// stream.
+class QUIC_EXPORT_PRIVATE QpackDecoderStreamSender {
+ public:
+ // An interface for handling encoded data.
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // Encoded |data| is ready to be written on the decoder stream.
+ // WriteDecoderStreamData() is called exactly once for each instruction.
+ // |data| contains the entire encoded instruction and it is guaranteed to be
+ // not empty.
+ virtual void WriteDecoderStreamData(QuicStringPiece data) = 0;
+ };
+
+ explicit QpackDecoderStreamSender(Delegate* delegate);
+ QpackDecoderStreamSender() = delete;
+ QpackDecoderStreamSender(const QpackDecoderStreamSender&) = delete;
+ QpackDecoderStreamSender& operator=(const QpackDecoderStreamSender&) = delete;
+
+ // Methods for sending instructions, see
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.3
+
+ // 5.3.1 Insert Count Increment
+ void SendInsertCountIncrement(uint64_t increment);
+ // 5.3.2 Header Acknowledgement
+ void SendHeaderAcknowledgement(QuicStreamId stream_id);
+ // 5.3.3 Stream Cancellation
+ void SendStreamCancellation(QuicStreamId stream_id);
+
+ private:
+ Delegate* const delegate_;
+ QpackInstructionEncoder instruction_encoder_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_STREAM_SENDER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc
new file mode 100644
index 00000000000..cc957591083
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender_test.cc
@@ -0,0 +1,91 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::Eq;
+using ::testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockSenderDelegate : public QpackDecoderStreamSender::Delegate {
+ public:
+ ~MockSenderDelegate() override = default;
+
+ MOCK_METHOD1(WriteDecoderStreamData, void(QuicStringPiece data));
+};
+
+class QpackDecoderStreamSenderTest : public QuicTest {
+ protected:
+ QpackDecoderStreamSenderTest() : stream_(&delegate_) {}
+ ~QpackDecoderStreamSenderTest() override = default;
+
+ StrictMock<MockSenderDelegate> delegate_;
+ QpackDecoderStreamSender stream_;
+};
+
+TEST_F(QpackDecoderStreamSenderTest, InsertCountIncrement) {
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("00"))));
+ stream_.SendInsertCountIncrement(0);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("0a"))));
+ stream_.SendInsertCountIncrement(10);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("3f00"))));
+ stream_.SendInsertCountIncrement(63);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("3f8901"))));
+ stream_.SendInsertCountIncrement(200);
+}
+
+TEST_F(QpackDecoderStreamSenderTest, HeaderAcknowledgement) {
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("80"))));
+ stream_.SendHeaderAcknowledgement(0);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("a5"))));
+ stream_.SendHeaderAcknowledgement(37);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("ff00"))));
+ stream_.SendHeaderAcknowledgement(127);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("fff802"))));
+ stream_.SendHeaderAcknowledgement(503);
+}
+
+TEST_F(QpackDecoderStreamSenderTest, StreamCancellation) {
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("40"))));
+ stream_.SendStreamCancellation(0);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("53"))));
+ stream_.SendStreamCancellation(19);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("7f00"))));
+ stream_.SendStreamCancellation(63);
+
+ EXPECT_CALL(delegate_,
+ WriteDecoderStreamData(Eq(QuicTextUtils::HexDecode("7f2f"))));
+ stream_.SendStreamCancellation(110);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc
new file mode 100644
index 00000000000..8d83f0a8bc0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test.cc
@@ -0,0 +1,703 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+
+#include <algorithm>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+using ::testing::Eq;
+using ::testing::Sequence;
+using ::testing::StrictMock;
+using ::testing::Values;
+
+namespace quic {
+namespace test {
+namespace {
+
+// Header Acknowledgement decoder stream instruction with stream_id = 1.
+const char* const kHeaderAcknowledgement = "\x81";
+
+class QpackDecoderTest : public QuicTestWithParam<FragmentMode> {
+ protected:
+ QpackDecoderTest()
+ : qpack_decoder_(&encoder_stream_error_delegate_,
+ &decoder_stream_sender_delegate_),
+ fragment_mode_(GetParam()) {
+ qpack_decoder_.SetMaximumDynamicTableCapacity(1024);
+ }
+
+ ~QpackDecoderTest() override = default;
+
+ void DecodeEncoderStreamData(QuicStringPiece data) {
+ qpack_decoder_.DecodeEncoderStreamData(data);
+ }
+
+ void DecodeHeaderBlock(QuicStringPiece data) {
+ auto fragment_size_generator =
+ FragmentModeToFragmentSizeGenerator(fragment_mode_);
+ auto progressive_decoder =
+ qpack_decoder_.DecodeHeaderBlock(/* stream_id = */ 1, &handler_);
+ while (!data.empty()) {
+ size_t fragment_size = std::min(fragment_size_generator(), data.size());
+ progressive_decoder->Decode(data.substr(0, fragment_size));
+ data = data.substr(fragment_size);
+ }
+ progressive_decoder->EndHeaderBlock();
+ }
+
+ StrictMock<MockEncoderStreamErrorDelegate> encoder_stream_error_delegate_;
+ StrictMock<MockDecoderStreamSenderDelegate> decoder_stream_sender_delegate_;
+ StrictMock<MockHeadersHandler> handler_;
+
+ private:
+ QpackDecoder qpack_decoder_;
+ const FragmentMode fragment_mode_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+ QpackDecoderTest,
+ Values(FragmentMode::kSingleChunk,
+ FragmentMode::kOctetByOctet));
+
+TEST_P(QpackDecoderTest, NoPrefix) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Incomplete header data prefix.")));
+
+ // Header Data Prefix is at least two bytes long.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00"));
+}
+
+TEST_P(QpackDecoderTest, EmptyHeaderBlock) {
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("0000"));
+}
+
+TEST_P(QpackDecoderTest, LiteralEntryEmptyName) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq("foo")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00002003666f6f"));
+}
+
+TEST_P(QpackDecoderTest, LiteralEntryEmptyValue) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f00"));
+}
+
+TEST_P(QpackDecoderTest, LiteralEntryEmptyNameAndValue) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(""), Eq("")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00002000"));
+}
+
+TEST_P(QpackDecoderTest, SimpleLiteralEntry) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f03626172"));
+}
+
+TEST_P(QpackDecoderTest, MultipleLiteralEntries) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ std::string str(127, 'a');
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foobaar"), QuicStringPiece(str)));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0000" // prefix
+ "23666f6f03626172" // foo: bar
+ "2700666f6f62616172" // 7 octet long header name, the smallest number
+ // that does not fit on a 3-bit prefix.
+ "7f0061616161616161" // 127 octet long header value, the smallest number
+ "616161616161616161" // that does not fit on a 7-bit prefix.
+ "6161616161616161616161616161616161616161616161616161616161616161616161"
+ "6161616161616161616161616161616161616161616161616161616161616161616161"
+ "6161616161616161616161616161616161616161616161616161616161616161616161"
+ "616161616161"));
+}
+
+// Name Length value is too large for varint decoder to decode.
+TEST_P(QpackDecoderTest, NameLenTooLargeForVarintDecoder) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Encoded integer too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffffffffffffffffffff"));
+}
+
+// Name Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_P(QpackDecoderTest, NameLenExceedsLimit) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("String literal too long.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000027ffff7f"));
+}
+
+// Value Length value is too large for varint decoder to decode.
+TEST_P(QpackDecoderTest, ValueLenTooLargeForVarintDecoder) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Encoded integer too large.")));
+
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("000023666f6f7fffffffffffffffffffff"));
+}
+
+// Value Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_P(QpackDecoderTest, ValueLenExceedsLimit) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("String literal too long.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("000023666f6f7fffff7f"));
+}
+
+TEST_P(QpackDecoderTest, IncompleteHeaderBlock) {
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Incomplete header block.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("00002366"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanSimple) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4bf"));
+}
+
+TEST_P(QpackDecoderTest, AlternatingHuffmanNonHuffman) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("custom-key"), Eq("custom-value")))
+ .Times(4);
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0000" // Prefix.
+ "2f0125a849e95ba97d7f" // Huffman-encoded name.
+ "8925a849e95bb8e8b4bf" // Huffman-encoded value.
+ "2703637573746f6d2d6b6579" // Non-Huffman encoded name.
+ "0c637573746f6d2d76616c7565" // Non-Huffman encoded value.
+ "2f0125a849e95ba97d7f" // Huffman-encoded name.
+ "0c637573746f6d2d76616c7565" // Non-Huffman encoded value.
+ "2703637573746f6d2d6b6579" // Non-Huffman encoded name.
+ "8925a849e95bb8e8b4bf")); // Huffman-encoded value.
+}
+
+TEST_P(QpackDecoderTest, HuffmanNameDoesNotHaveEOSPrefix) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // 'y' ends in 0b0 on the most significant bit of the last byte.
+ // The remaining 7 bits must be a prefix of EOS, which is all 1s.
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("00002f0125a849e95ba97d7e8925a849e95bb8e8b4bf"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanValueDoesNotHaveEOSPrefix) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // 'e' ends in 0b101, taking up the 3 most significant bits of the last byte.
+ // The remaining 5 bits must be a prefix of EOS, which is all 1s.
+ DecodeHeaderBlock(
+ QuicTextUtils::HexDecode("00002f0125a849e95ba97d7f8925a849e95bb8e8b4be"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanNameEOSPrefixTooLong) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // The trailing EOS prefix must be at most 7 bits long. Appending one octet
+ // with value 0xff is invalid, even though 0b111111111111111 (15 bits) is a
+ // prefix of EOS.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "00002f0225a849e95ba97d7fff8925a849e95bb8e8b4bf"));
+}
+
+TEST_P(QpackDecoderTest, HuffmanValueEOSPrefixTooLong) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(QuicStringPiece(
+ "Error in Huffman-encoded string.")));
+
+ // The trailing EOS prefix must be at most 7 bits long. Appending one octet
+ // with value 0xff is invalid, even though 0b1111111111111 (13 bits) is a
+ // prefix of EOS.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "00002f0125a849e95ba97d7f8a25a849e95bb8e8b4bfff"));
+}
+
+TEST_P(QpackDecoderTest, StaticTable) {
+ // A header name that has multiple entries with different values.
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("POST")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("TRACE")));
+
+ // A header name that has a single entry with non-empty value.
+ EXPECT_CALL(handler_,
+ OnHeaderDecoded(Eq("accept-encoding"), Eq("gzip, deflate, br")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq("compress")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("accept-encoding"), Eq("")));
+
+ // A header name that has a single entry with empty value.
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq("")));
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("location"), Eq("foo")));
+
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0000d1dfccd45f108621e9aec2a11f5c8294e75f000554524143455f1000"));
+}
+
+TEST_P(QpackDecoderTest, TooHighStaticTableIndex) {
+ // This is the last entry in the static table with index 98.
+ EXPECT_CALL(handler_,
+ OnHeaderDecoded(Eq("x-frame-options"), Eq("sameorigin")));
+
+ // Addressing entry 99 should trigger an error.
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Static table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("0000ff23ff24"));
+}
+
+TEST_P(QpackDecoderTest, DynamicTable) {
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+ "6294e703626172" // Add literal entry with name "foo" and value "bar".
+ "80035a5a5a" // Add entry with name of dynamic table entry index 0
+ // (relative index) and value "ZZZ".
+ "cf8294e7" // Add entry with name of static table entry index 15
+ // and value "foo".
+ "01")); // Duplicate entry with relative index 1.
+
+ // Now there are four entries in the dynamic table.
+ // Entry 0: "foo", "bar"
+ // Entry 1: "foo", "ZZZ"
+ // Entry 2: ":method", "foo"
+ // Entry 3: "foo", "ZZZ"
+
+ // Use a Sequence to test that mock methods are called in order.
+ Sequence s;
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo")))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s);
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "83" // Dynamic table entry with relative index 3, absolute index 0.
+ "82" // Dynamic table entry with relative index 2, absolute index 1.
+ "81" // Dynamic table entry with relative index 1, absolute index 2.
+ "80" // Dynamic table entry with relative index 0, absolute index 3.
+ "41025a5a")); // Name of entry 1 (relative index) from dynamic table,
+ // with value "ZZ".
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo")))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s);
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0502" // Required Insert Count 4 and Delta Base 2.
+ // Base is 4 + 2 = 6.
+ "85" // Dynamic table entry with relative index 5, absolute index 0.
+ "84" // Dynamic table entry with relative index 4, absolute index 1.
+ "83" // Dynamic table entry with relative index 3, absolute index 2.
+ "82" // Dynamic table entry with relative index 2, absolute index 3.
+ "43025a5a")); // Name of entry 3 (relative index) from dynamic table,
+ // with value "ZZ".
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("foo")))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("ZZZ"))).InSequence(s);
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("ZZ"))).InSequence(s);
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)))
+ .InSequence(s);
+ EXPECT_CALL(handler_, OnDecodingCompleted()).InSequence(s);
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0582" // Required Insert Count 4 and Delta Base 2 with sign bit set.
+ // Base is 4 - 2 - 1 = 1.
+ "80" // Dynamic table entry with relative index 0, absolute index 0.
+ "10" // Dynamic table entry with post-base index 0, absolute index 1.
+ "11" // Dynamic table entry with post-base index 1, absolute index 2.
+ "12" // Dynamic table entry with post-base index 2, absolute index 3.
+ "01025a5a")); // Name of entry 1 (post-base index) from dynamic table,
+ // with value "ZZ".
+}
+
+TEST_P(QpackDecoderTest, DecreasingDynamicTableCapacityEvictsEntries) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "80")); // Dynamic table entry with relative index 0, absolute index 0.
+
+ // Change dynamic table capacity to 32 bytes, smaller than the entry.
+ // This must cause the entry to be evicted.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f01"));
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "80")); // Dynamic table entry with relative index 0, absolute index 0.
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorEntryTooLarge) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Error inserting literal entry.")));
+
+ // Set dynamic table capacity to 34.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f03"));
+ // Add literal entry with name "foo" and value "bar", size is 32 + 3 + 3 = 38.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidStaticTableEntry) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Invalid static table entry.")));
+
+ // Address invalid static table entry index 99.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("ff2400"));
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorInvalidDynamicTableEntry) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Dynamic table entry not found.")));
+
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+ "6294e703626172" // Add literal entry with name "foo" and value "bar".
+ "8100")); // Address dynamic table entry with relative index 1. Such
+ // entry does not exist. The most recently added and only
+ // dynamic table entry has relative index 0.
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorDuplicateInvalidEntry) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Dynamic table entry not found.")));
+
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode(
+ "6294e703626172" // Add literal entry with name "foo" and value "bar".
+ "01")); // Duplicate dynamic table entry with relative index 1. Such
+ // entry does not exist. The most recently added and only
+ // dynamic table entry has relative index 0.
+}
+
+TEST_P(QpackDecoderTest, EncoderStreamErrorTooLargeInteger) {
+ EXPECT_CALL(encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Encoded integer too large.")));
+
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fffffffffffffffffffff"));
+}
+
+TEST_P(QpackDecoderTest, InvalidDynamicEntryWhenBaseIsZero) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0280" // Required Insert Count is 1. Base 1 - 1 - 0 = 0 is explicitly
+ // permitted by the spec.
+ "80")); // However, addressing entry with relative index 0 would point to
+ // absolute index -1, which is invalid.
+}
+
+TEST_P(QpackDecoderTest, InvalidNegativeBase) {
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Error calculating Base.")));
+
+ // Required Insert Count 1, Delta Base 1 with sign bit set, Base would
+ // be 1 - 1 - 1 = -1, but it is not allowed to be negative.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("0281"));
+}
+
+TEST_P(QpackDecoderTest, InvalidDynamicEntryByRelativeIndex) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "82")); // Indexed Header Field instruction addressing relative index 2.
+ // This is absolute index 1. Such entry does not exist.
+
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "84")); // Indexed Header Field instruction addressing relative index 4.
+ // This is absolute index -1, which is invalid.
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "4200")); // Literal Header Field with Name Reference instruction
+ // addressing relative index 2. This is absolute index 1. Such
+ // entry does not exist.
+
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(Eq("Invalid relative index.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0500" // Required Insert Count 4 and Delta Base 0.
+ // Base is 4 + 0 = 4.
+ "4400")); // Literal Header Field with Name Reference instruction
+ // addressing relative index 4. This is absolute index -1,
+ // which is invalid.
+}
+
+TEST_P(QpackDecoderTest, InvalidDynamicEntryByPostBaseIndex) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0380" // Required Insert Count 2 and Delta Base 0 with sign bit set.
+ // Base is 2 - 0 - 1 = 1
+ "10")); // Indexed Header Field instruction addressing dynamic table
+ // entry with post-base index 0, absolute index 1. Such entry
+ // does not exist.
+
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Dynamic table entry not found.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0380" // Required Insert Count 2 and Delta Base 0 with sign bit set.
+ // Base is 2 - 0 - 1 = 1
+ "0000")); // Literal Header Field With Name Reference instruction
+ // addressing dynamic table entry with post-base index 0,
+ // absolute index 1. Such entry does not exist.
+}
+
+TEST_P(QpackDecoderTest, TableCapacityMustNotExceedMaximum) {
+ EXPECT_CALL(
+ encoder_stream_error_delegate_,
+ OnEncoderStreamError(Eq("Error updating dynamic table capacity.")));
+
+ // Try to update dynamic table capacity to 2048, which exceeds the maximum.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3fe10f"));
+}
+
+TEST_P(QpackDecoderTest, SetMaximumDynamicTableCapacity) {
+ // Update dynamic table capacity to 128, which does not exceed the maximum.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("3f61"));
+}
+
+TEST_P(QpackDecoderTest, InvalidEncodedRequiredInsertCount) {
+ // Maximum dynamic table capacity is 1024.
+ // MaxEntries is 1024 / 32 = 32.
+ // Required Insert Count is decoded modulo 2 * MaxEntries, that is, modulo 64.
+ // A value of 1 cannot be encoded as 65 even though it has the same remainder.
+ EXPECT_CALL(handler_, OnDecodingErrorDetected(
+ Eq("Error decoding Required Insert Count.")));
+ DecodeHeaderBlock(QuicTextUtils::HexDecode("4100"));
+}
+
+TEST_P(QpackDecoderTest, WrappedRequiredInsertCount) {
+ // Maximum dynamic table capacity is 1024.
+ // MaxEntries is 1024 / 32 = 32.
+
+ // Add literal entry with name "foo" and a 600 byte long value. This will fit
+ // in the dynamic table once but not twice.
+ DecodeEncoderStreamData(
+ QuicTextUtils::HexDecode("6294e7" // Name "foo".
+ "7fd903")); // Value length 600.
+ std::string header_value(600, 'Z');
+ DecodeEncoderStreamData(header_value);
+
+ // Duplicate most recent entry 200 times.
+ DecodeEncoderStreamData(std::string(200, '\x00'));
+
+ // Now there is only one entry in the dynamic table, with absolute index 200.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq(header_value)));
+ EXPECT_CALL(handler_, OnDecodingCompleted());
+ EXPECT_CALL(decoder_stream_sender_delegate_,
+ WriteDecoderStreamData(Eq(kHeaderAcknowledgement)));
+
+ // Send header block with Required Insert Count = 201.
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0a00" // Encoded Required Insert Count 10, Required Insert Count 201,
+ // Delta Base 0, Base 201.
+ "80")); // Emit dynamic table entry with relative index 0.
+}
+
+TEST_P(QpackDecoderTest, NonZeroRequiredInsertCountButNoDynamicEntries) {
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq(":method"), Eq("GET")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count is 1.
+ "d1")); // But the only instruction references the static table.
+}
+
+TEST_P(QpackDecoderTest, AddressEntryNotAllowedByRequiredInsertCount) {
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0201" // Required Insert Count 1 and Delta Base 1.
+ // Base is 1 + 1 = 2.
+ "80")); // Indexed Header Field instruction addressing dynamic table
+ // entry with relative index 0, absolute index 1. This is not
+ // allowed by Required Insert Count.
+
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0201" // Required Insert Count 1 and Delta Base 1.
+ // Base is 1 + 1 = 2.
+ "4000")); // Literal Header Field with Name Reference instruction
+ // addressing dynamic table entry with relative index 0,
+ // absolute index 1. This is not allowed by Required Index
+ // Count.
+
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "10")); // Indexed Header Field with Post-Base Index instruction
+ // addressing dynamic table entry with post-base index 0,
+ // absolute index 1. This is not allowed by Required Insert
+ // Count.
+
+ EXPECT_CALL(
+ handler_,
+ OnDecodingErrorDetected(
+ Eq("Absolute Index must be smaller than Required Insert Count.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0200" // Required Insert Count 1 and Delta Base 0.
+ // Base is 1 + 0 = 1.
+ "0000")); // Literal Header Field with Post-Base Name Reference
+ // instruction addressing dynamic table entry with post-base
+ // index 0, absolute index 1. This is not allowed by Required
+ // Index Count.
+}
+
+TEST_P(QpackDecoderTest, PromisedRequiredInsertCountLargerThanActual) {
+ // Add literal entry with name "foo" and value "bar".
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("6294e703626172"));
+ // Duplicate entry.
+ DecodeEncoderStreamData(QuicTextUtils::HexDecode("00"));
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0300" // Required Insert Count 2 and Delta Base 0.
+ // Base is 2 + 0 = 2.
+ "81")); // Indexed Header Field instruction addressing dynamic table
+ // entry with relative index 1, absolute index 0. Header block
+ // requires insert count of 1, even though Required Insert Count
+ // is 2.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0300" // Required Insert Count 2 and Delta Base 0.
+ // Base is 2 + 0 = 2.
+ "4100")); // Literal Header Field with Name Reference instruction
+ // addressing dynamic table entry with relative index 1,
+ // absolute index 0. Header block requires insert count of 1,
+ // even though Required Insert Count is 2.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("bar")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set.
+ // Base is 3 - 1 - 1 = 1.
+ "10")); // Indexed Header Field with Post-Base Index instruction
+ // addressing dynamic table entry with post-base index 0,
+ // absolute index 1. Header block requires insert count of 2,
+ // even though Required Insert Count is 3.
+
+ EXPECT_CALL(handler_, OnHeaderDecoded(Eq("foo"), Eq("")));
+ EXPECT_CALL(handler_,
+ OnDecodingErrorDetected(Eq("Required Insert Count too large.")));
+
+ DecodeHeaderBlock(QuicTextUtils::HexDecode(
+ "0481" // Required Insert Count 3 and Delta Base 1 with sign bit set.
+ // Base is 3 - 1 - 1 = 1.
+ "0000")); // Literal Header Field with Post-Base Name Reference
+ // instruction addressing dynamic table entry with post-base
+ // index 0, absolute index 1. Header block requires insert
+ // count of 2, even though Required Insert Count is 3.
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc
new file mode 100644
index 00000000000..e8bbd178e62
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.cc
@@ -0,0 +1,82 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+
+#include <algorithm>
+#include <cstddef>
+#include <utility>
+
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace quic {
+namespace test {
+
+void NoopEncoderStreamErrorDelegate::OnEncoderStreamError(
+ QuicStringPiece error_message) {}
+
+void NoopDecoderStreamSenderDelegate::WriteDecoderStreamData(
+ QuicStringPiece data) {}
+
+TestHeadersHandler::TestHeadersHandler()
+ : decoding_completed_(false), decoding_error_detected_(false) {}
+
+void TestHeadersHandler::OnHeaderDecoded(QuicStringPiece name,
+ QuicStringPiece value) {
+ ASSERT_FALSE(decoding_completed_);
+ ASSERT_FALSE(decoding_error_detected_);
+
+ header_list_.AppendValueOrAddHeader(name, value);
+}
+
+void TestHeadersHandler::OnDecodingCompleted() {
+ ASSERT_FALSE(decoding_completed_);
+ ASSERT_FALSE(decoding_error_detected_);
+
+ decoding_completed_ = true;
+}
+
+void TestHeadersHandler::OnDecodingErrorDetected(
+ QuicStringPiece error_message) {
+ ASSERT_FALSE(decoding_completed_);
+ ASSERT_FALSE(decoding_error_detected_);
+
+ decoding_error_detected_ = true;
+}
+
+spdy::SpdyHeaderBlock TestHeadersHandler::ReleaseHeaderList() {
+ DCHECK(decoding_completed_);
+ DCHECK(!decoding_error_detected_);
+
+ return std::move(header_list_);
+}
+
+bool TestHeadersHandler::decoding_completed() const {
+ return decoding_completed_;
+}
+
+bool TestHeadersHandler::decoding_error_detected() const {
+ return decoding_error_detected_;
+}
+
+void QpackDecode(
+ QpackDecoder::EncoderStreamErrorDelegate* encoder_stream_error_delegate,
+ QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate,
+ QpackProgressiveDecoder::HeadersHandlerInterface* handler,
+ const FragmentSizeGenerator& fragment_size_generator,
+ QuicStringPiece data) {
+ QpackDecoder decoder(encoder_stream_error_delegate,
+ decoder_stream_sender_delegate);
+ auto progressive_decoder =
+ decoder.DecodeHeaderBlock(/* stream_id = */ 1, handler);
+ while (!data.empty()) {
+ size_t fragment_size = std::min(fragment_size_generator(), data.size());
+ progressive_decoder->Decode(data.substr(0, fragment_size));
+ data = data.substr(fragment_size);
+ }
+ progressive_decoder->EndHeaderBlock();
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h
new file mode 100644
index 00000000000..ca5b60818fa
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h
@@ -0,0 +1,114 @@
+// 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_QUIC_CORE_QPACK_QPACK_DECODER_TEST_UTILS_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_TEST_UTILS_H_
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+namespace quic {
+namespace test {
+
+// QpackDecoder::EncoderStreamErrorDelegate implementation that does nothing.
+class NoopEncoderStreamErrorDelegate
+ : public QpackDecoder::EncoderStreamErrorDelegate {
+ public:
+ ~NoopEncoderStreamErrorDelegate() override = default;
+
+ void OnEncoderStreamError(QuicStringPiece error_message) override;
+};
+
+// Mock QpackDecoder::EncoderStreamErrorDelegate implementation.
+class MockEncoderStreamErrorDelegate
+ : public QpackDecoder::EncoderStreamErrorDelegate {
+ public:
+ ~MockEncoderStreamErrorDelegate() override = default;
+
+ MOCK_METHOD1(OnEncoderStreamError, void(QuicStringPiece error_message));
+};
+
+// QpackDecoderStreamSender::Delegate implementation that does nothing.
+class NoopDecoderStreamSenderDelegate
+ : public QpackDecoderStreamSender::Delegate {
+ public:
+ ~NoopDecoderStreamSenderDelegate() override = default;
+
+ void WriteDecoderStreamData(QuicStringPiece data) override;
+};
+
+// Mock QpackDecoderStreamSender::Delegate implementation.
+class MockDecoderStreamSenderDelegate
+ : public QpackDecoderStreamSender::Delegate {
+ public:
+ ~MockDecoderStreamSenderDelegate() override = default;
+
+ MOCK_METHOD1(WriteDecoderStreamData, void(QuicStringPiece data));
+};
+
+// HeadersHandlerInterface implementation that collects decoded headers
+// into a SpdyHeaderBlock.
+class TestHeadersHandler
+ : public QpackProgressiveDecoder::HeadersHandlerInterface {
+ public:
+ TestHeadersHandler();
+ ~TestHeadersHandler() override = default;
+
+ // HeadersHandlerInterface implementation:
+ void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override;
+ void OnDecodingCompleted() override;
+ void OnDecodingErrorDetected(QuicStringPiece error_message) override;
+
+ // Release decoded header list. Must only be called if decoding is complete
+ // and no errors have been detected.
+ spdy::SpdyHeaderBlock ReleaseHeaderList();
+
+ bool decoding_completed() const;
+ bool decoding_error_detected() const;
+
+ private:
+ spdy::SpdyHeaderBlock header_list_;
+ bool decoding_completed_;
+ bool decoding_error_detected_;
+};
+
+class MockHeadersHandler
+ : public QpackProgressiveDecoder::HeadersHandlerInterface {
+ public:
+ MockHeadersHandler() = default;
+ MockHeadersHandler(const MockHeadersHandler&) = delete;
+ MockHeadersHandler& operator=(const MockHeadersHandler&) = delete;
+ ~MockHeadersHandler() override = default;
+
+ MOCK_METHOD2(OnHeaderDecoded,
+ void(QuicStringPiece name, QuicStringPiece value));
+ MOCK_METHOD0(OnDecodingCompleted, void());
+ MOCK_METHOD1(OnDecodingErrorDetected, void(QuicStringPiece error_message));
+};
+
+class NoOpHeadersHandler
+ : public QpackProgressiveDecoder::HeadersHandlerInterface {
+ public:
+ ~NoOpHeadersHandler() override = default;
+
+ void OnHeaderDecoded(QuicStringPiece name, QuicStringPiece value) override {}
+ void OnDecodingCompleted() override {}
+ void OnDecodingErrorDetected(QuicStringPiece error_message) override {}
+};
+
+void QpackDecode(
+ QpackDecoder::EncoderStreamErrorDelegate* encoder_stream_error_delegate,
+ QpackDecoderStreamSender::Delegate* decoder_stream_sender_delegate,
+ QpackProgressiveDecoder::HeadersHandlerInterface* handler,
+ const FragmentSizeGenerator& fragment_size_generator,
+ QuicStringPiece data);
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_DECODER_TEST_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc
new file mode 100644
index 00000000000..108ffd57ee4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.cc
@@ -0,0 +1,55 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+QpackEncoder::QpackEncoder(
+ DecoderStreamErrorDelegate* decoder_stream_error_delegate,
+ QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate)
+ : decoder_stream_error_delegate_(decoder_stream_error_delegate),
+ decoder_stream_receiver_(this),
+ encoder_stream_sender_(encoder_stream_sender_delegate) {
+ DCHECK(decoder_stream_error_delegate_);
+ DCHECK(encoder_stream_sender_delegate);
+}
+
+QpackEncoder::~QpackEncoder() {}
+
+std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder>
+QpackEncoder::EncodeHeaderList(QuicStreamId stream_id,
+ const spdy::SpdyHeaderBlock* header_list) {
+ return QuicMakeUnique<QpackProgressiveEncoder>(
+ stream_id, &header_table_, &encoder_stream_sender_, header_list);
+}
+
+void QpackEncoder::DecodeDecoderStreamData(QuicStringPiece data) {
+ decoder_stream_receiver_.Decode(data);
+}
+
+void QpackEncoder::OnInsertCountIncrement(uint64_t increment) {
+ // TODO(bnc): Implement dynamic table management for encoding.
+}
+
+void QpackEncoder::OnHeaderAcknowledgement(QuicStreamId stream_id) {
+ // TODO(bnc): Implement dynamic table management for encoding.
+}
+
+void QpackEncoder::OnStreamCancellation(QuicStreamId stream_id) {
+ // TODO(bnc): Implement dynamic table management for encoding.
+}
+
+void QpackEncoder::OnErrorDetected(QuicStringPiece error_message) {
+ decoder_stream_error_delegate_->OnDecoderStreamError(error_message);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h
new file mode 100644
index 00000000000..4e655329b5f
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h
@@ -0,0 +1,72 @@
+// 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_QUIC_CORE_QPACK_QPACK_ENCODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_H_
+
+#include <cstdint>
+#include <memory>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_receiver.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h"
+
+namespace spdy {
+
+class SpdyHeaderBlock;
+
+}
+
+namespace quic {
+
+// QPACK encoder class. Exactly one instance should exist per QUIC connection.
+// This class vends a new QpackProgressiveEncoder instance for each new header
+// list to be encoded.
+class QUIC_EXPORT_PRIVATE QpackEncoder
+ : public QpackDecoderStreamReceiver::Delegate {
+ public:
+ // Interface for receiving notification that an error has occurred on the
+ // decoder stream. This MUST be treated as a connection error of type
+ // HTTP_QPACK_DECODER_STREAM_ERROR.
+ class QUIC_EXPORT_PRIVATE DecoderStreamErrorDelegate {
+ public:
+ virtual ~DecoderStreamErrorDelegate() {}
+
+ virtual void OnDecoderStreamError(QuicStringPiece error_message) = 0;
+ };
+
+ QpackEncoder(
+ DecoderStreamErrorDelegate* decoder_stream_error_delegate,
+ QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate);
+ ~QpackEncoder() override;
+
+ // This factory method is called to start encoding a header list.
+ // |*header_list| must remain valid and must not change
+ // during the lifetime of the returned ProgressiveEncoder instance.
+ std::unique_ptr<spdy::HpackEncoder::ProgressiveEncoder> EncodeHeaderList(
+ QuicStreamId stream_id,
+ const spdy::SpdyHeaderBlock* header_list);
+
+ // Decode data received on the decoder stream.
+ void DecodeDecoderStreamData(QuicStringPiece data);
+
+ // QpackDecoderStreamReceiver::Delegate implementation
+ void OnInsertCountIncrement(uint64_t increment) override;
+ void OnHeaderAcknowledgement(QuicStreamId stream_id) override;
+ void OnStreamCancellation(QuicStreamId stream_id) override;
+ void OnErrorDetected(QuicStringPiece error_message) override;
+
+ private:
+ DecoderStreamErrorDelegate* const decoder_stream_error_delegate_;
+ QpackDecoderStreamReceiver decoder_stream_receiver_;
+ QpackEncoderStreamSender encoder_stream_sender_;
+ QpackHeaderTable header_table_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc
new file mode 100644
index 00000000000..3f8ef08a7ee
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.cc
@@ -0,0 +1,60 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h"
+
+#include "net/third_party/quiche/src/http2/decoder/decode_buffer.h"
+#include "net/third_party/quiche/src/http2/decoder/decode_status.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+
+namespace quic {
+
+QpackEncoderStreamReceiver::QpackEncoderStreamReceiver(Delegate* delegate)
+ : instruction_decoder_(QpackEncoderStreamLanguage(), this),
+ delegate_(delegate),
+ error_detected_(false) {
+ DCHECK(delegate_);
+}
+
+void QpackEncoderStreamReceiver::Decode(QuicStringPiece data) {
+ if (data.empty() || error_detected_) {
+ return;
+ }
+
+ instruction_decoder_.Decode(data);
+}
+
+bool QpackEncoderStreamReceiver::OnInstructionDecoded(
+ const QpackInstruction* instruction) {
+ if (instruction == InsertWithNameReferenceInstruction()) {
+ delegate_->OnInsertWithNameReference(instruction_decoder_.s_bit(),
+ instruction_decoder_.varint(),
+ instruction_decoder_.value());
+ return true;
+ }
+
+ if (instruction == InsertWithoutNameReferenceInstruction()) {
+ delegate_->OnInsertWithoutNameReference(instruction_decoder_.name(),
+ instruction_decoder_.value());
+ return true;
+ }
+
+ if (instruction == DuplicateInstruction()) {
+ delegate_->OnDuplicate(instruction_decoder_.varint());
+ return true;
+ }
+
+ DCHECK_EQ(instruction, SetDynamicTableCapacityInstruction());
+ delegate_->OnSetDynamicTableCapacity(instruction_decoder_.varint());
+ return true;
+}
+
+void QpackEncoderStreamReceiver::OnError(QuicStringPiece error_message) {
+ DCHECK(!error_detected_);
+
+ error_detected_ = true;
+ delegate_->OnErrorDetected(error_message);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h
new file mode 100644
index 00000000000..5519b4c67a2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h
@@ -0,0 +1,68 @@
+// 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_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_
+
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// This class decodes data received on the encoder stream.
+class QUIC_EXPORT_PRIVATE QpackEncoderStreamReceiver
+ : public QpackInstructionDecoder::Delegate {
+ public:
+ // An interface for handling instructions decoded from the encoder stream, see
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.2
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // 5.2.1. Insert With Name Reference
+ virtual void OnInsertWithNameReference(bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value) = 0;
+ // 5.2.2. Insert Without Name Reference
+ virtual void OnInsertWithoutNameReference(QuicStringPiece name,
+ QuicStringPiece value) = 0;
+ // 5.2.3. Duplicate
+ virtual void OnDuplicate(uint64_t index) = 0;
+ // 5.2.4. Set Dynamic Table Capacity
+ virtual void OnSetDynamicTableCapacity(uint64_t capacity) = 0;
+ // Decoding error
+ virtual void OnErrorDetected(QuicStringPiece error_message) = 0;
+ };
+
+ explicit QpackEncoderStreamReceiver(Delegate* delegate);
+ QpackEncoderStreamReceiver() = delete;
+ QpackEncoderStreamReceiver(const QpackEncoderStreamReceiver&) = delete;
+ QpackEncoderStreamReceiver& operator=(const QpackEncoderStreamReceiver&) =
+ delete;
+ ~QpackEncoderStreamReceiver() override = default;
+
+ // Decode data and call appropriate Delegate method after each decoded
+ // instruction. Once an error occurs, Delegate::OnErrorDetected() is called,
+ // and all further data is ignored.
+ void Decode(QuicStringPiece data);
+
+ // QpackInstructionDecoder::Delegate implementation.
+ bool OnInstructionDecoded(const QpackInstruction* instruction) override;
+ void OnError(QuicStringPiece error_message) override;
+
+ private:
+ QpackInstructionDecoder instruction_decoder_;
+ Delegate* const delegate_;
+
+ // True if a decoding error has been detected.
+ bool error_detected_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_RECEIVER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
new file mode 100644
index 00000000000..dcb2039ccfd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver_test.cc
@@ -0,0 +1,169 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using testing::Eq;
+using testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class MockDelegate : public QpackEncoderStreamReceiver::Delegate {
+ public:
+ ~MockDelegate() override = default;
+
+ MOCK_METHOD3(OnInsertWithNameReference,
+ void(bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value));
+ MOCK_METHOD2(OnInsertWithoutNameReference,
+ void(QuicStringPiece name, QuicStringPiece value));
+ MOCK_METHOD1(OnDuplicate, void(uint64_t index));
+ MOCK_METHOD1(OnSetDynamicTableCapacity, void(uint64_t capacity));
+ MOCK_METHOD1(OnErrorDetected, void(QuicStringPiece error_message));
+};
+
+class QpackEncoderStreamReceiverTest : public QuicTest {
+ protected:
+ QpackEncoderStreamReceiverTest() : stream_(&delegate_) {}
+ ~QpackEncoderStreamReceiverTest() override = default;
+
+ void Decode(QuicStringPiece data) { stream_.Decode(data); }
+ StrictMock<MockDelegate>* delegate() { return &delegate_; }
+
+ private:
+ QpackEncoderStreamReceiver stream_;
+ StrictMock<MockDelegate> delegate_;
+};
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReference) {
+ // Static, index fits in prefix, empty value.
+ EXPECT_CALL(*delegate(), OnInsertWithNameReference(true, 5, Eq("")));
+ // Static, index fits in prefix, Huffman encoded value.
+ EXPECT_CALL(*delegate(), OnInsertWithNameReference(true, 2, Eq("foo")));
+ // Not static, index does not fit in prefix, not Huffman encoded value.
+ EXPECT_CALL(*delegate(), OnInsertWithNameReference(false, 137, Eq("bar")));
+ // Value length does not fit in prefix.
+ // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+ EXPECT_CALL(*delegate(),
+ OnInsertWithNameReference(false, 42, Eq(std::string(127, 'Z'))));
+
+ Decode(QuicTextUtils::HexDecode(
+ "c500"
+ "c28294e7"
+ "bf4a03626172"
+ "aa7f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceIndexTooLarge) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("bfffffffffffffffffffffff"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithNameReferenceValueTooLong) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("c57fffffffffffffffffffff"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, InsertWithoutNameReference) {
+ // Empty name and value.
+ EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq(""), Eq("")));
+ // Huffman encoded short strings.
+ EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq("bar"), Eq("bar")));
+ // Not Huffman encoded short strings.
+ EXPECT_CALL(*delegate(), OnInsertWithoutNameReference(Eq("foo"), Eq("foo")));
+ // Not Huffman encoded long strings; length does not fit on prefix.
+ // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+ EXPECT_CALL(*delegate(),
+ OnInsertWithoutNameReference(Eq(std::string(31, 'Z')),
+ Eq(std::string(127, 'Z'))));
+
+ Decode(QuicTextUtils::HexDecode(
+ "4000"
+ "4362617203626172"
+ "6294e78294e7"
+ "5f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a7f005a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"));
+}
+
+// Name Length value is too large for varint decoder to decode.
+TEST_F(QpackEncoderStreamReceiverTest,
+ InsertWithoutNameReferenceNameTooLongForVarintDecoder) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("5fffffffffffffffffffff"));
+}
+
+// Name Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_F(QpackEncoderStreamReceiverTest,
+ InsertWithoutNameReferenceNameExceedsLimit) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("String literal too long.")));
+
+ Decode(QuicTextUtils::HexDecode("5fffff7f"));
+}
+
+// Value Length value is too large for varint decoder to decode.
+TEST_F(QpackEncoderStreamReceiverTest,
+ InsertWithoutNameReferenceValueTooLongForVarintDecoder) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("436261727fffffffffffffffffffff"));
+}
+
+// Value Length value can be decoded by varint decoder but exceeds 1 MB limit.
+TEST_F(QpackEncoderStreamReceiverTest,
+ InsertWithoutNameReferenceValueExceedsLimit) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("String literal too long.")));
+
+ Decode(QuicTextUtils::HexDecode("436261727fffff7f"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, Duplicate) {
+ // Small index fits in prefix.
+ EXPECT_CALL(*delegate(), OnDuplicate(17));
+ // Large index requires two extension bytes.
+ EXPECT_CALL(*delegate(), OnDuplicate(500));
+
+ Decode(QuicTextUtils::HexDecode("111fd503"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, DuplicateIndexTooLarge) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("1fffffffffffffffffffff"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, SetDynamicTableCapacity) {
+ // Small capacity fits in prefix.
+ EXPECT_CALL(*delegate(), OnSetDynamicTableCapacity(17));
+ // Large capacity requires two extension bytes.
+ EXPECT_CALL(*delegate(), OnSetDynamicTableCapacity(500));
+
+ Decode(QuicTextUtils::HexDecode("313fd503"));
+}
+
+TEST_F(QpackEncoderStreamReceiverTest, SetDynamicTableCapacityTooLarge) {
+ EXPECT_CALL(*delegate(), OnErrorDetected(Eq("Encoded integer too large.")));
+
+ Decode(QuicTextUtils::HexDecode("3fffffffffffffffffffff"));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc
new file mode 100644
index 00000000000..3fb3b33f35e
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.cc
@@ -0,0 +1,81 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h"
+
+#include <cstddef>
+#include <limits>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+QpackEncoderStreamSender::QpackEncoderStreamSender(Delegate* delegate)
+ : delegate_(delegate) {
+ DCHECK(delegate_);
+}
+
+void QpackEncoderStreamSender::SendInsertWithNameReference(
+ bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value) {
+ instruction_encoder_.set_s_bit(is_static);
+ instruction_encoder_.set_varint(name_index);
+ instruction_encoder_.set_value(value);
+
+ instruction_encoder_.Encode(InsertWithNameReferenceInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteEncoderStreamData(output);
+}
+
+void QpackEncoderStreamSender::SendInsertWithoutNameReference(
+ QuicStringPiece name,
+ QuicStringPiece value) {
+ instruction_encoder_.set_name(name);
+ instruction_encoder_.set_value(value);
+
+ instruction_encoder_.Encode(InsertWithoutNameReferenceInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteEncoderStreamData(output);
+}
+
+void QpackEncoderStreamSender::SendDuplicate(uint64_t index) {
+ instruction_encoder_.set_varint(index);
+
+ instruction_encoder_.Encode(DuplicateInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteEncoderStreamData(output);
+}
+
+void QpackEncoderStreamSender::SendSetDynamicTableCapacity(uint64_t capacity) {
+ instruction_encoder_.set_varint(capacity);
+
+ instruction_encoder_.Encode(SetDynamicTableCapacityInstruction());
+
+ std::string output;
+
+ instruction_encoder_.Next(std::numeric_limits<size_t>::max(), &output);
+ DCHECK(!instruction_encoder_.HasNext());
+
+ delegate_->WriteEncoderStreamData(output);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h
new file mode 100644
index 00000000000..ad3456889bd
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h
@@ -0,0 +1,58 @@
+// 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_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_SENDER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_SENDER_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// This class serializes instructions for transmission on the encoder stream.
+class QUIC_EXPORT_PRIVATE QpackEncoderStreamSender {
+ public:
+ // An interface for handling encoded data.
+ class Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // Encoded |data| is ready to be written on the encoder stream.
+ // WriteEncoderStreamData() is called exactly once for each instruction.
+ // |data| contains the entire encoded instruction and it is guaranteed to be
+ // not empty.
+ virtual void WriteEncoderStreamData(QuicStringPiece data) = 0;
+ };
+
+ explicit QpackEncoderStreamSender(Delegate* delegate);
+ QpackEncoderStreamSender() = delete;
+ QpackEncoderStreamSender(const QpackEncoderStreamSender&) = delete;
+ QpackEncoderStreamSender& operator=(const QpackEncoderStreamSender&) = delete;
+
+ // Methods for sending instructions, see
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#rfc.section.5.2
+
+ // 5.2.1. Insert With Name Reference
+ void SendInsertWithNameReference(bool is_static,
+ uint64_t name_index,
+ QuicStringPiece value);
+ // 5.2.2. Insert Without Name Reference
+ void SendInsertWithoutNameReference(QuicStringPiece name,
+ QuicStringPiece value);
+ // 5.2.3. Duplicate
+ void SendDuplicate(uint64_t index);
+ // 5.2.4. Set Dynamic Table Capacity
+ void SendSetDynamicTableCapacity(uint64_t capacity);
+
+ private:
+ Delegate* const delegate_;
+ QpackInstructionEncoder instruction_encoder_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_STREAM_SENDER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc
new file mode 100644
index 00000000000..a2e73763dea
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender_test.cc
@@ -0,0 +1,113 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::Eq;
+using ::testing::StrictMock;
+
+namespace quic {
+namespace test {
+namespace {
+
+class QpackEncoderStreamSenderTest : public QuicTest {
+ protected:
+ QpackEncoderStreamSenderTest() : stream_(&delegate_) {}
+ ~QpackEncoderStreamSenderTest() override = default;
+
+ StrictMock<MockEncoderStreamSenderDelegate> delegate_;
+ QpackEncoderStreamSender stream_;
+};
+
+TEST_F(QpackEncoderStreamSenderTest, InsertWithNameReference) {
+ // Static, index fits in prefix, empty value.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("c500"))));
+ stream_.SendInsertWithNameReference(true, 5, "");
+
+ // Static, index fits in prefix, Huffman encoded value.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("c28294e7"))));
+ stream_.SendInsertWithNameReference(true, 2, "foo");
+
+ // Not static, index does not fit in prefix, not Huffman encoded value.
+ EXPECT_CALL(delegate_, WriteEncoderStreamData(
+ Eq(QuicTextUtils::HexDecode("bf4a03626172"))));
+ stream_.SendInsertWithNameReference(false, 137, "bar");
+
+ // Value length does not fit in prefix.
+ // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+ EXPECT_CALL(
+ delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode(
+ "aa7f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"))));
+ stream_.SendInsertWithNameReference(false, 42, std::string(127, 'Z'));
+}
+
+TEST_F(QpackEncoderStreamSenderTest, InsertWithoutNameReference) {
+ // Empty name and value.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("4000"))));
+ stream_.SendInsertWithoutNameReference("", "");
+
+ // Huffman encoded short strings.
+ EXPECT_CALL(delegate_, WriteEncoderStreamData(
+ Eq(QuicTextUtils::HexDecode("4362617203626172"))));
+ stream_.SendInsertWithoutNameReference("bar", "bar");
+
+ // Not Huffman encoded short strings.
+ EXPECT_CALL(delegate_, WriteEncoderStreamData(
+ Eq(QuicTextUtils::HexDecode("6294e78294e7"))));
+ stream_.SendInsertWithoutNameReference("foo", "foo");
+
+ // Not Huffman encoded long strings; length does not fit on prefix.
+ // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+ EXPECT_CALL(
+ delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode(
+ "5f005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a7f"
+ "005a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"))));
+ stream_.SendInsertWithoutNameReference(std::string(31, 'Z'),
+ std::string(127, 'Z'));
+}
+
+TEST_F(QpackEncoderStreamSenderTest, Duplicate) {
+ // Small index fits in prefix.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("11"))));
+ stream_.SendDuplicate(17);
+
+ // Large index requires two extension bytes.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("1fd503"))));
+ stream_.SendDuplicate(500);
+}
+
+TEST_F(QpackEncoderStreamSenderTest, SetDynamicTableCapacity) {
+ // Small capacity fits in prefix.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("31"))));
+ stream_.SendSetDynamicTableCapacity(17);
+
+ // Large capacity requires two extension bytes.
+ EXPECT_CALL(delegate_,
+ WriteEncoderStreamData(Eq(QuicTextUtils::HexDecode("3fd503"))));
+ stream_.SendSetDynamicTableCapacity(500);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc
new file mode 100644
index 00000000000..fb07d49e1ad
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test.cc
@@ -0,0 +1,168 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h"
+
+#include <string>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::Eq;
+using ::testing::StrictMock;
+using ::testing::Values;
+
+namespace quic {
+namespace test {
+namespace {
+
+class QpackEncoderTest : public QuicTestWithParam<FragmentMode> {
+ protected:
+ QpackEncoderTest() : fragment_mode_(GetParam()) {}
+ ~QpackEncoderTest() override = default;
+
+ std::string Encode(const spdy::SpdyHeaderBlock* header_list) {
+ return QpackEncode(
+ &decoder_stream_error_delegate_, &encoder_stream_sender_delegate_,
+ FragmentModeToFragmentSizeGenerator(fragment_mode_), header_list);
+ }
+
+ StrictMock<MockDecoderStreamErrorDelegate> decoder_stream_error_delegate_;
+ NoopEncoderStreamSenderDelegate encoder_stream_sender_delegate_;
+
+ private:
+ const FragmentMode fragment_mode_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+ QpackEncoderTest,
+ Values(FragmentMode::kSingleChunk,
+ FragmentMode::kOctetByOctet));
+
+TEST_P(QpackEncoderTest, Empty) {
+ spdy::SpdyHeaderBlock header_list;
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("0000"), output);
+}
+
+TEST_P(QpackEncoderTest, EmptyName) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[""] = "foo";
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("0000208294e7"), output);
+}
+
+TEST_P(QpackEncoderTest, EmptyValue) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "";
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("00002a94e700"), output);
+}
+
+TEST_P(QpackEncoderTest, EmptyNameAndValue) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[""] = "";
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("00002000"), output);
+}
+
+TEST_P(QpackEncoderTest, Simple) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "bar";
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("00002a94e703626172"), output);
+}
+
+TEST_P(QpackEncoderTest, Multiple) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "bar";
+ // 'Z' would be Huffman encoded to 8 bits, so no Huffman encoding is used.
+ header_list["ZZZZZZZ"] = std::string(127, 'Z');
+ std::string output = Encode(&header_list);
+
+ EXPECT_EQ(
+ QuicTextUtils::HexDecode(
+ "0000" // prefix
+ "2a94e703626172" // foo: bar
+ "27005a5a5a5a5a5a5a" // 7 octet long header name, the smallest number
+ // that does not fit on a 3-bit prefix.
+ "7f005a5a5a5a5a5a5a" // 127 octet long header value, the smallest
+ "5a5a5a5a5a5a5a5a5a" // number that does not fit on a 7-bit prefix.
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a"
+ "5a5a5a5a5a5a5a5a5a"),
+ output);
+}
+
+TEST_P(QpackEncoderTest, StaticTable) {
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "GET";
+ header_list["accept-encoding"] = "gzip, deflate, br";
+ header_list["location"] = "";
+
+ std::string output = Encode(&header_list);
+ EXPECT_EQ(QuicTextUtils::HexDecode("0000d1dfcc"), output);
+ }
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "POST";
+ header_list["accept-encoding"] = "compress";
+ header_list["location"] = "foo";
+
+ std::string output = Encode(&header_list);
+ EXPECT_EQ(QuicTextUtils::HexDecode("0000d45f108621e9aec2a11f5c8294e7"),
+ output);
+ }
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "TRACE";
+ header_list["accept-encoding"] = "";
+
+ std::string output = Encode(&header_list);
+ EXPECT_EQ(QuicTextUtils::HexDecode("00005f000554524143455f1000"), output);
+ }
+}
+
+TEST_P(QpackEncoderTest, SimpleIndexed) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":path"] = "/";
+
+ QpackEncoder encoder(&decoder_stream_error_delegate_,
+ &encoder_stream_sender_delegate_);
+ auto progressive_encoder =
+ encoder.EncodeHeaderList(/* stream_id = */ 1, &header_list);
+ EXPECT_TRUE(progressive_encoder->HasNext());
+
+ // This indexed header field takes exactly three bytes:
+ // two for the prefix, one for the indexed static entry.
+ std::string output;
+ progressive_encoder->Next(3, &output);
+
+ EXPECT_EQ(QuicTextUtils::HexDecode("0000c1"), output);
+ EXPECT_FALSE(progressive_encoder->HasNext());
+}
+
+TEST_P(QpackEncoderTest, DecoderStreamError) {
+ EXPECT_CALL(decoder_stream_error_delegate_,
+ OnDecoderStreamError(Eq("Encoded integer too large.")));
+
+ QpackEncoder encoder(&decoder_stream_error_delegate_,
+ &encoder_stream_sender_delegate_);
+ encoder.DecodeDecoderStreamData(
+ QuicTextUtils::HexDecode("ffffffffffffffffffffff"));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.cc
new file mode 100644
index 00000000000..dd1ccb31256
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.cc
@@ -0,0 +1,37 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h"
+
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h"
+
+namespace quic {
+namespace test {
+
+void NoopDecoderStreamErrorDelegate::OnDecoderStreamError(
+ QuicStringPiece error_message) {}
+
+void NoopEncoderStreamSenderDelegate::WriteEncoderStreamData(
+ QuicStringPiece data) {}
+
+std::string QpackEncode(
+ QpackEncoder::DecoderStreamErrorDelegate* decoder_stream_error_delegate,
+ QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate,
+ const FragmentSizeGenerator& fragment_size_generator,
+ const spdy::SpdyHeaderBlock* header_list) {
+ QpackEncoder encoder(decoder_stream_error_delegate,
+ encoder_stream_sender_delegate);
+ auto progressive_encoder =
+ encoder.EncodeHeaderList(/* stream_id = */ 1, header_list);
+
+ std::string output;
+ while (progressive_encoder->HasNext()) {
+ progressive_encoder->Next(fragment_size_generator(), &output);
+ }
+
+ return output;
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h
new file mode 100644
index 00000000000..3c9b404a1d4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h
@@ -0,0 +1,64 @@
+// 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_QUIC_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+namespace quic {
+namespace test {
+
+// QpackEncoder::DecoderStreamErrorDelegate implementation that does nothing.
+class NoopDecoderStreamErrorDelegate
+ : public QpackEncoder::DecoderStreamErrorDelegate {
+ public:
+ ~NoopDecoderStreamErrorDelegate() override = default;
+
+ void OnDecoderStreamError(QuicStringPiece error_message) override;
+};
+
+// Mock QpackEncoder::DecoderStreamErrorDelegate implementation.
+class MockDecoderStreamErrorDelegate
+ : public QpackEncoder::DecoderStreamErrorDelegate {
+ public:
+ ~MockDecoderStreamErrorDelegate() override = default;
+
+ MOCK_METHOD1(OnDecoderStreamError, void(QuicStringPiece error_message));
+};
+
+// QpackEncoderStreamSender::Delegate implementation that does nothing.
+class NoopEncoderStreamSenderDelegate
+ : public QpackEncoderStreamSender::Delegate {
+ public:
+ ~NoopEncoderStreamSenderDelegate() override = default;
+
+ void WriteEncoderStreamData(QuicStringPiece data) override;
+};
+
+// Mock QpackEncoderStreamSender::Delegate implementation.
+class MockEncoderStreamSenderDelegate
+ : public QpackEncoderStreamSender::Delegate {
+ public:
+ ~MockEncoderStreamSenderDelegate() override = default;
+
+ MOCK_METHOD1(WriteEncoderStreamData, void(QuicStringPiece data));
+};
+
+std::string QpackEncode(
+ QpackEncoder::DecoderStreamErrorDelegate* decoder_stream_error_delegate,
+ QpackEncoderStreamSender::Delegate* encoder_stream_sender_delegate,
+ const FragmentSizeGenerator& fragment_size_generator,
+ const spdy::SpdyHeaderBlock* header_list);
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_ENCODER_TEST_UTILS_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc
new file mode 100644
index 00000000000..01943301c66
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.cc
@@ -0,0 +1,204 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+const uint64_t kEntrySizeOverhead = 32;
+
+uint64_t EntrySize(QuicStringPiece name, QuicStringPiece value) {
+ return name.size() + value.size() + kEntrySizeOverhead;
+}
+
+} // anonymous namespace
+
+QpackHeaderTable::QpackHeaderTable()
+ : static_entries_(ObtainQpackStaticTable().GetStaticEntries()),
+ static_index_(ObtainQpackStaticTable().GetStaticIndex()),
+ static_name_index_(ObtainQpackStaticTable().GetStaticNameIndex()),
+ dynamic_table_size_(0),
+ dynamic_table_capacity_(0),
+ maximum_dynamic_table_capacity_(0),
+ max_entries_(0),
+ dropped_entry_count_(0) {}
+
+QpackHeaderTable::~QpackHeaderTable() = default;
+
+const QpackEntry* QpackHeaderTable::LookupEntry(bool is_static,
+ uint64_t index) const {
+ if (is_static) {
+ if (index >= static_entries_.size()) {
+ return nullptr;
+ }
+
+ return &static_entries_[index];
+ }
+
+ if (index < dropped_entry_count_) {
+ return nullptr;
+ }
+
+ index -= dropped_entry_count_;
+
+ if (index >= dynamic_entries_.size()) {
+ return nullptr;
+ }
+
+ return &dynamic_entries_[index];
+}
+
+QpackHeaderTable::MatchType QpackHeaderTable::FindHeaderField(
+ QuicStringPiece name,
+ QuicStringPiece value,
+ bool* is_static,
+ uint64_t* index) const {
+ QpackEntry query(name, value);
+
+ // Look for exact match in static table.
+ auto index_it = static_index_.find(&query);
+ if (index_it != static_index_.end()) {
+ DCHECK((*index_it)->IsStatic());
+ *index = (*index_it)->InsertionIndex();
+ *is_static = true;
+ return MatchType::kNameAndValue;
+ }
+
+ // Look for exact match in dynamic table.
+ index_it = dynamic_index_.find(&query);
+ if (index_it != dynamic_index_.end()) {
+ DCHECK(!(*index_it)->IsStatic());
+ *index = (*index_it)->InsertionIndex();
+ *is_static = false;
+ return MatchType::kNameAndValue;
+ }
+
+ // Look for name match in static table.
+ auto name_index_it = static_name_index_.find(name);
+ if (name_index_it != static_name_index_.end()) {
+ DCHECK(name_index_it->second->IsStatic());
+ *index = name_index_it->second->InsertionIndex();
+ *is_static = true;
+ return MatchType::kName;
+ }
+
+ // Look for name match in dynamic table.
+ name_index_it = dynamic_name_index_.find(name);
+ if (name_index_it != dynamic_name_index_.end()) {
+ DCHECK(!name_index_it->second->IsStatic());
+ *index = name_index_it->second->InsertionIndex();
+ *is_static = false;
+ return MatchType::kName;
+ }
+
+ return MatchType::kNoMatch;
+}
+
+const QpackEntry* QpackHeaderTable::InsertEntry(QuicStringPiece name,
+ QuicStringPiece value) {
+ const uint64_t entry_size = EntrySize(name, value);
+ if (entry_size > dynamic_table_capacity_) {
+ return nullptr;
+ }
+
+ const uint64_t index = dropped_entry_count_ + dynamic_entries_.size();
+ dynamic_entries_.push_back({name, value, /* is_static = */ false, index});
+ QpackEntry* const new_entry = &dynamic_entries_.back();
+
+ // Evict entries after inserting the new entry instead of before
+ // in order to avoid invalidating |name| and |value|.
+ dynamic_table_size_ += entry_size;
+ EvictDownToCurrentCapacity();
+
+ auto index_result = dynamic_index_.insert(new_entry);
+ if (!index_result.second) {
+ // An entry with the same name and value already exists. It needs to be
+ // replaced, because |dynamic_index_| tracks the most recent entry for a
+ // given name and value.
+ DCHECK_GT(new_entry->InsertionIndex(),
+ (*index_result.first)->InsertionIndex());
+ dynamic_index_.erase(index_result.first);
+ auto result = dynamic_index_.insert(new_entry);
+ CHECK(result.second);
+ }
+
+ auto name_result = dynamic_name_index_.insert({new_entry->name(), new_entry});
+ if (!name_result.second) {
+ // An entry with the same name already exists. It needs to be replaced,
+ // because |dynamic_name_index_| tracks the most recent entry for a given
+ // name.
+ DCHECK_GT(new_entry->InsertionIndex(),
+ name_result.first->second->InsertionIndex());
+ dynamic_name_index_.erase(name_result.first);
+ auto result = dynamic_name_index_.insert({new_entry->name(), new_entry});
+ CHECK(result.second);
+ }
+
+ return new_entry;
+}
+
+bool QpackHeaderTable::SetDynamicTableCapacity(uint64_t capacity) {
+ if (capacity > maximum_dynamic_table_capacity_) {
+ return false;
+ }
+
+ dynamic_table_capacity_ = capacity;
+ EvictDownToCurrentCapacity();
+
+ DCHECK_LE(dynamic_table_size_, dynamic_table_capacity_);
+
+ return true;
+}
+
+void QpackHeaderTable::SetMaximumDynamicTableCapacity(
+ uint64_t maximum_dynamic_table_capacity) {
+ // This method can only be called once: in the decoding context, shortly after
+ // construction; in the encoding context, upon receiving the SETTINGS frame.
+ DCHECK_EQ(0u, dynamic_table_capacity_);
+ DCHECK_EQ(0u, maximum_dynamic_table_capacity_);
+ DCHECK_EQ(0u, max_entries_);
+
+ dynamic_table_capacity_ = maximum_dynamic_table_capacity;
+ maximum_dynamic_table_capacity_ = maximum_dynamic_table_capacity;
+ max_entries_ = maximum_dynamic_table_capacity / 32;
+}
+
+void QpackHeaderTable::EvictDownToCurrentCapacity() {
+ while (dynamic_table_size_ > dynamic_table_capacity_) {
+ DCHECK(!dynamic_entries_.empty());
+
+ QpackEntry* const entry = &dynamic_entries_.front();
+ const uint64_t entry_size = EntrySize(entry->name(), entry->value());
+
+ DCHECK_GE(dynamic_table_size_, entry_size);
+ dynamic_table_size_ -= entry_size;
+
+ auto index_it = dynamic_index_.find(entry);
+ // Remove |dynamic_index_| entry only if it points to the same
+ // QpackEntry in |dynamic_entries_|. Note that |dynamic_index_| has a
+ // comparison function that only considers name and value, not actual
+ // QpackEntry* address, so find() can return a different entry if name and
+ // value match.
+ if (index_it != dynamic_index_.end() && *index_it == entry) {
+ dynamic_index_.erase(index_it);
+ }
+
+ auto name_it = dynamic_name_index_.find(entry->name());
+ // Remove |dynamic_name_index_| entry only if it points to the same
+ // QpackEntry in |dynamic_entries_|.
+ if (name_it != dynamic_name_index_.end() && name_it->second == entry) {
+ dynamic_name_index_.erase(name_it);
+ }
+
+ dynamic_entries_.pop_front();
+ ++dropped_entry_count_;
+ }
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h
new file mode 100644
index 00000000000..eda7ab525a2
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h
@@ -0,0 +1,144 @@
+// 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_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_
+
+#include <cstdint>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_header_table.h"
+
+namespace quic {
+
+using QpackEntry = spdy::HpackEntry;
+
+// This class manages the QPACK static and dynamic tables. For dynamic entries,
+// it only has a concept of absolute indices. The caller needs to perform the
+// necessary transformations to and from relative indices and post-base indices.
+class QUIC_EXPORT_PRIVATE QpackHeaderTable {
+ public:
+ using EntryTable = spdy::HpackHeaderTable::EntryTable;
+ using EntryHasher = spdy::HpackHeaderTable::EntryHasher;
+ using EntriesEq = spdy::HpackHeaderTable::EntriesEq;
+ using UnorderedEntrySet = spdy::HpackHeaderTable::UnorderedEntrySet;
+ using NameToEntryMap = spdy::HpackHeaderTable::NameToEntryMap;
+
+ // Result of header table lookup.
+ enum class MatchType { kNameAndValue, kName, kNoMatch };
+
+ QpackHeaderTable();
+ QpackHeaderTable(const QpackHeaderTable&) = delete;
+ QpackHeaderTable& operator=(const QpackHeaderTable&) = delete;
+
+ ~QpackHeaderTable();
+
+ // Returns the entry at absolute index |index| from the static or dynamic
+ // table according to |is_static|. |index| is zero based for both the static
+ // and the dynamic table. The returned pointer is valid until the entry is
+ // evicted, even if other entries are inserted into the dynamic table.
+ // Returns nullptr if entry does not exist.
+ const QpackEntry* LookupEntry(bool is_static, uint64_t index) const;
+
+ // Returns the absolute index of an entry with matching name and value if such
+ // exists, otherwise one with matching name is such exists. |index| is zero
+ // based for both the static and the dynamic table.
+ MatchType FindHeaderField(QuicStringPiece name,
+ QuicStringPiece value,
+ bool* is_static,
+ uint64_t* index) const;
+
+ // Insert (name, value) into the dynamic table. May evict entries. Returns a
+ // pointer to the inserted owned entry on success. Returns nullptr if entry
+ // is larger than the capacity of the dynamic table.
+ const QpackEntry* InsertEntry(QuicStringPiece name, QuicStringPiece value);
+
+ // Change dynamic table capacity to |capacity|. Returns true on success.
+ // Returns false is |capacity| exceeds maximum dynamic table capacity.
+ bool SetDynamicTableCapacity(uint64_t capacity);
+
+ // Set |maximum_dynamic_table_capacity_|. The initial value is zero. The
+ // final value is determined by the decoder and is sent to the encoder as
+ // SETTINGS_HEADER_TABLE_SIZE. Therefore in the decoding context the final
+ // value can be set upon connection establishment, whereas in the encoding
+ // context it can be set when the SETTINGS frame is received.
+ // This method must only be called at most once.
+ void SetMaximumDynamicTableCapacity(uint64_t maximum_dynamic_table_capacity);
+
+ // Used on request streams to encode and decode Required Insert Count.
+ uint64_t max_entries() const { return max_entries_; }
+
+ // The number of entries inserted to the dynamic table (including ones that
+ // were dropped since). Used for relative indexing on the encoder stream.
+ uint64_t inserted_entry_count() const {
+ return dynamic_entries_.size() + dropped_entry_count_;
+ }
+
+ // The number of entries dropped from the dynamic table.
+ uint64_t dropped_entry_count() const { return dropped_entry_count_; }
+
+ private:
+ // Evict entries from the dynamic table until table size is less than or equal
+ // to current value of |dynamic_table_capacity_|.
+ void EvictDownToCurrentCapacity();
+
+ // Static Table
+
+ // |static_entries_|, |static_index_|, |static_name_index_| are owned by
+ // QpackStaticTable singleton.
+
+ // Tracks QpackEntries by index.
+ const EntryTable& static_entries_;
+
+ // Tracks the unique static entry for a given header name and value.
+ const UnorderedEntrySet& static_index_;
+
+ // Tracks the first static entry for a given header name.
+ const NameToEntryMap& static_name_index_;
+
+ // Dynamic Table
+
+ // Queue of dynamic table entries, for lookup by index.
+ // |dynamic_entries_| owns the entries in the dynamic table.
+ EntryTable dynamic_entries_;
+
+ // An unordered set of QpackEntry pointers with a comparison operator that
+ // only cares about name and value. This allows fast lookup of the most
+ // recently inserted dynamic entry for a given header name and value pair.
+ // Entries point to entries owned by |dynamic_entries_|.
+ UnorderedEntrySet dynamic_index_;
+
+ // An unordered map of QpackEntry pointers keyed off header name. This allows
+ // fast lookup of the most recently inserted dynamic entry for a given header
+ // name. Entries point to entries owned by |dynamic_entries_|.
+ NameToEntryMap dynamic_name_index_;
+
+ // Size of the dynamic table. This is the sum of the size of its entries.
+ uint64_t dynamic_table_size_;
+
+ // Dynamic Table Capacity is the maximum allowed value of
+ // |dynamic_table_size_|. Entries are evicted if necessary before inserting a
+ // new entry to ensure that dynamic table size never exceeds capacity.
+ // Initial value is |maximum_dynamic_table_capacity_|. Capacity can be
+ // changed by the encoder, as long as it does not exceed
+ // |maximum_dynamic_table_capacity_|.
+ uint64_t dynamic_table_capacity_;
+
+ // Maximum allowed value of |dynamic_table_capacity|. The initial value is
+ // zero. Can be changed by SetMaximumDynamicTableCapacity().
+ uint64_t maximum_dynamic_table_capacity_;
+
+ // MaxEntries, see Section 3.2.2. Calculated based on
+ // |maximum_dynamic_table_capacity_|.
+ uint64_t max_entries_;
+
+ // The number of entries dropped from the dynamic table.
+ uint64_t dropped_entry_count_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_HEADER_TABLE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc
new file mode 100644
index 00000000000..f3ac5b5d3ef
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_header_table_test.cc
@@ -0,0 +1,356 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_entry.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+const uint64_t kMaximumDynamicTableCapacityForTesting = 1024 * 1024;
+
+class QpackHeaderTableTest : public QuicTest {
+ protected:
+ QpackHeaderTableTest() {
+ table_.SetMaximumDynamicTableCapacity(
+ kMaximumDynamicTableCapacityForTesting);
+ }
+ ~QpackHeaderTableTest() override = default;
+
+ void ExpectEntryAtIndex(bool is_static,
+ uint64_t index,
+ QuicStringPiece expected_name,
+ QuicStringPiece expected_value) const {
+ const auto* entry = table_.LookupEntry(is_static, index);
+ ASSERT_TRUE(entry);
+ EXPECT_EQ(expected_name, entry->name());
+ EXPECT_EQ(expected_value, entry->value());
+ }
+
+ void ExpectNoEntryAtIndex(bool is_static, uint64_t index) const {
+ EXPECT_FALSE(table_.LookupEntry(is_static, index));
+ }
+
+ void ExpectMatch(QuicStringPiece name,
+ QuicStringPiece value,
+ QpackHeaderTable::MatchType expected_match_type,
+ bool expected_is_static,
+ uint64_t expected_index) const {
+ // Initialize outparams to a value different from the expected to ensure
+ // that FindHeaderField() sets them.
+ bool is_static = !expected_is_static;
+ uint64_t index = expected_index + 1;
+
+ QpackHeaderTable::MatchType matchtype =
+ table_.FindHeaderField(name, value, &is_static, &index);
+
+ EXPECT_EQ(expected_match_type, matchtype) << name << ": " << value;
+ EXPECT_EQ(expected_is_static, is_static) << name << ": " << value;
+ EXPECT_EQ(expected_index, index) << name << ": " << value;
+ }
+
+ void ExpectNoMatch(QuicStringPiece name, QuicStringPiece value) const {
+ bool is_static = false;
+ uint64_t index = 0;
+
+ QpackHeaderTable::MatchType matchtype =
+ table_.FindHeaderField(name, value, &is_static, &index);
+
+ EXPECT_EQ(QpackHeaderTable::MatchType::kNoMatch, matchtype)
+ << name << ": " << value;
+ }
+
+ void InsertEntry(QuicStringPiece name, QuicStringPiece value) {
+ EXPECT_TRUE(table_.InsertEntry(name, value));
+ }
+
+ void ExpectToFailInsertingEntry(QuicStringPiece name, QuicStringPiece value) {
+ EXPECT_FALSE(table_.InsertEntry(name, value));
+ }
+
+ bool SetDynamicTableCapacity(uint64_t capacity) {
+ return table_.SetDynamicTableCapacity(capacity);
+ }
+
+ uint64_t max_entries() const { return table_.max_entries(); }
+ uint64_t inserted_entry_count() const {
+ return table_.inserted_entry_count();
+ }
+ uint64_t dropped_entry_count() const { return table_.dropped_entry_count(); }
+
+ private:
+ QpackHeaderTable table_;
+};
+
+TEST_F(QpackHeaderTableTest, LookupStaticEntry) {
+ ExpectEntryAtIndex(/* is_static = */ true, 0, ":authority", "");
+
+ ExpectEntryAtIndex(/* is_static = */ true, 1, ":path", "/");
+
+ // 98 is the last entry.
+ ExpectEntryAtIndex(/* is_static = */ true, 98, "x-frame-options",
+ "sameorigin");
+
+ ASSERT_EQ(99u, QpackStaticTableVector().size());
+ ExpectNoEntryAtIndex(/* is_static = */ true, 99);
+}
+
+TEST_F(QpackHeaderTableTest, InsertAndLookupDynamicEntry) {
+ // Dynamic table is initially entry.
+ ExpectNoEntryAtIndex(/* is_static = */ false, 0);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 1);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 2);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 3);
+
+ // Insert one entry.
+ InsertEntry("foo", "bar");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar");
+
+ ExpectNoEntryAtIndex(/* is_static = */ false, 1);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 2);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 3);
+
+ // Insert a different entry.
+ InsertEntry("baz", "bing");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 1, "baz", "bing");
+
+ ExpectNoEntryAtIndex(/* is_static = */ false, 2);
+ ExpectNoEntryAtIndex(/* is_static = */ false, 3);
+
+ // Insert an entry identical to the most recently inserted one.
+ InsertEntry("baz", "bing");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 0, "foo", "bar");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 1, "baz", "bing");
+
+ ExpectEntryAtIndex(/* is_static = */ false, 2, "baz", "bing");
+
+ ExpectNoEntryAtIndex(/* is_static = */ false, 3);
+}
+
+TEST_F(QpackHeaderTableTest, FindStaticHeaderField) {
+ // A header name that has multiple entries with different values.
+ ExpectMatch(":method", "GET", QpackHeaderTable::MatchType::kNameAndValue,
+ true, 17u);
+
+ ExpectMatch(":method", "POST", QpackHeaderTable::MatchType::kNameAndValue,
+ true, 20u);
+
+ ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kName, true,
+ 15u);
+
+ // A header name that has a single entry with non-empty value.
+ ExpectMatch("accept-encoding", "gzip, deflate, br",
+ QpackHeaderTable::MatchType::kNameAndValue, true, 31u);
+
+ ExpectMatch("accept-encoding", "compress", QpackHeaderTable::MatchType::kName,
+ true, 31u);
+
+ ExpectMatch("accept-encoding", "", QpackHeaderTable::MatchType::kName, true,
+ 31u);
+
+ // A header name that has a single entry with empty value.
+ ExpectMatch("location", "", QpackHeaderTable::MatchType::kNameAndValue, true,
+ 12u);
+
+ ExpectMatch("location", "foo", QpackHeaderTable::MatchType::kName, true, 12u);
+
+ // No matching header name.
+ ExpectNoMatch("foo", "");
+ ExpectNoMatch("foo", "bar");
+}
+
+TEST_F(QpackHeaderTableTest, FindDynamicHeaderField) {
+ // Dynamic table is initially entry.
+ ExpectNoMatch("foo", "bar");
+ ExpectNoMatch("foo", "baz");
+
+ // Insert one entry.
+ InsertEntry("foo", "bar");
+
+ // Match name and value.
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, false,
+ 0u);
+
+ // Match name only.
+ ExpectMatch("foo", "baz", QpackHeaderTable::MatchType::kName, false, 0u);
+
+ // Insert an identical entry. FindHeaderField() should return the index of
+ // the most recently inserted matching entry.
+ InsertEntry("foo", "bar");
+
+ // Match name and value.
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue, false,
+ 1u);
+
+ // Match name only.
+ ExpectMatch("foo", "baz", QpackHeaderTable::MatchType::kName, false, 1u);
+}
+
+TEST_F(QpackHeaderTableTest, FindHeaderFieldPrefersStaticTable) {
+ // Insert an entry to the dynamic table that exists in the static table.
+ InsertEntry(":method", "GET");
+
+ // Insertion works.
+ ExpectEntryAtIndex(/* is_static = */ false, 0, ":method", "GET");
+
+ // FindHeaderField() prefers static table if both have name-and-value match.
+ ExpectMatch(":method", "GET", QpackHeaderTable::MatchType::kNameAndValue,
+ true, 17u);
+
+ // FindHeaderField() prefers static table if both have name match but no value
+ // match, and prefers the first entry with matching name.
+ ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kName, true,
+ 15u);
+
+ // Add new entry to the dynamic table.
+ InsertEntry(":method", "TRACE");
+
+ // FindHeaderField prefers name-and-value match in dynamic table over name
+ // only match in static table.
+ ExpectMatch(":method", "TRACE", QpackHeaderTable::MatchType::kNameAndValue,
+ false, 1u);
+}
+
+// MaxEntries is determined by maximum dynamic table capacity,
+// which is set at construction time.
+TEST_F(QpackHeaderTableTest, MaxEntries) {
+ QpackHeaderTable table1;
+ table1.SetMaximumDynamicTableCapacity(1024);
+ EXPECT_EQ(32u, table1.max_entries());
+
+ QpackHeaderTable table2;
+ table2.SetMaximumDynamicTableCapacity(500);
+ EXPECT_EQ(15u, table2.max_entries());
+}
+
+TEST_F(QpackHeaderTableTest, SetDynamicTableCapacity) {
+ // Dynamic table capacity does not affect MaxEntries.
+ EXPECT_TRUE(SetDynamicTableCapacity(1024));
+ EXPECT_EQ(32u * 1024, max_entries());
+
+ EXPECT_TRUE(SetDynamicTableCapacity(500));
+ EXPECT_EQ(32u * 1024, max_entries());
+
+ // Dynamic table capacity cannot exceed maximum dynamic table capacity.
+ EXPECT_FALSE(
+ SetDynamicTableCapacity(2 * kMaximumDynamicTableCapacityForTesting));
+}
+
+TEST_F(QpackHeaderTableTest, EvictByInsertion) {
+ EXPECT_TRUE(SetDynamicTableCapacity(40));
+
+ // Entry size is 3 + 3 + 32 = 38.
+ InsertEntry("foo", "bar");
+ EXPECT_EQ(1u, inserted_entry_count());
+ EXPECT_EQ(0u, dropped_entry_count());
+
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 0u);
+
+ // Inserting second entry evicts the first one.
+ InsertEntry("baz", "qux");
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(1u, dropped_entry_count());
+
+ ExpectNoMatch("foo", "bar");
+ ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 1u);
+
+ // Inserting an entry that does not fit results in error.
+ ExpectToFailInsertingEntry("foobar", "foobar");
+}
+
+TEST_F(QpackHeaderTableTest, EvictByUpdateTableSize) {
+ // Entry size is 3 + 3 + 32 = 38.
+ InsertEntry("foo", "bar");
+ InsertEntry("baz", "qux");
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(0u, dropped_entry_count());
+
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 0u);
+ ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 1u);
+
+ EXPECT_TRUE(SetDynamicTableCapacity(40));
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(1u, dropped_entry_count());
+
+ ExpectNoMatch("foo", "bar");
+ ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 1u);
+
+ EXPECT_TRUE(SetDynamicTableCapacity(20));
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(2u, dropped_entry_count());
+
+ ExpectNoMatch("foo", "bar");
+ ExpectNoMatch("baz", "qux");
+}
+
+TEST_F(QpackHeaderTableTest, EvictOldestOfIdentical) {
+ EXPECT_TRUE(SetDynamicTableCapacity(80));
+
+ // Entry size is 3 + 3 + 32 = 38.
+ // Insert same entry twice.
+ InsertEntry("foo", "bar");
+ InsertEntry("foo", "bar");
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(0u, dropped_entry_count());
+
+ // Find most recently inserted entry.
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 1u);
+
+ // Inserting third entry evicts the first one, not the second.
+ InsertEntry("baz", "qux");
+ EXPECT_EQ(3u, inserted_entry_count());
+ EXPECT_EQ(1u, dropped_entry_count());
+
+ ExpectMatch("foo", "bar", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 1u);
+ ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 2u);
+}
+
+TEST_F(QpackHeaderTableTest, EvictOldestOfSameName) {
+ EXPECT_TRUE(SetDynamicTableCapacity(80));
+
+ // Entry size is 3 + 3 + 32 = 38.
+ // Insert two entries with same name but different values.
+ InsertEntry("foo", "bar");
+ InsertEntry("foo", "baz");
+ EXPECT_EQ(2u, inserted_entry_count());
+ EXPECT_EQ(0u, dropped_entry_count());
+
+ // Find most recently inserted entry with matching name.
+ ExpectMatch("foo", "foo", QpackHeaderTable::MatchType::kName,
+ /* expected_is_static = */ false, 1u);
+
+ // Inserting third entry evicts the first one, not the second.
+ InsertEntry("baz", "qux");
+ EXPECT_EQ(3u, inserted_entry_count());
+ EXPECT_EQ(1u, dropped_entry_count());
+
+ ExpectMatch("foo", "foo", QpackHeaderTable::MatchType::kName,
+ /* expected_is_static = */ false, 1u);
+ ExpectMatch("baz", "qux", QpackHeaderTable::MatchType::kNameAndValue,
+ /* expected_is_static = */ false, 2u);
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc
new file mode 100644
index 00000000000..2076fa7c990
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.cc
@@ -0,0 +1,310 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+
+namespace quic {
+
+namespace {
+
+// Maximum length of header name and header value. This limits the amount of
+// memory the peer can make the decoder allocate when sending string literals.
+const size_t kStringLiteralLengthLimit = 1024 * 1024;
+
+} // namespace
+
+QpackInstructionDecoder::QpackInstructionDecoder(const QpackLanguage* language,
+ Delegate* delegate)
+ : language_(language),
+ delegate_(delegate),
+ s_bit_(false),
+ varint_(0),
+ varint2_(0),
+ is_huffman_encoded_(false),
+ string_length_(0),
+ error_detected_(false),
+ state_(State::kStartInstruction) {}
+
+void QpackInstructionDecoder::Decode(QuicStringPiece data) {
+ DCHECK(!data.empty());
+ DCHECK(!error_detected_);
+
+ while (true) {
+ size_t bytes_consumed = 0;
+
+ switch (state_) {
+ case State::kStartInstruction:
+ DoStartInstruction(data);
+ break;
+ case State::kStartField:
+ DoStartField();
+ break;
+ case State::kReadBit:
+ DoReadBit(data);
+ break;
+ case State::kVarintStart:
+ bytes_consumed = DoVarintStart(data);
+ break;
+ case State::kVarintResume:
+ bytes_consumed = DoVarintResume(data);
+ break;
+ case State::kVarintDone:
+ DoVarintDone();
+ break;
+ case State::kReadString:
+ bytes_consumed = DoReadString(data);
+ break;
+ case State::kReadStringDone:
+ DoReadStringDone();
+ break;
+ }
+
+ if (error_detected_) {
+ return;
+ }
+
+ DCHECK_LE(bytes_consumed, data.size());
+
+ data = QuicStringPiece(data.data() + bytes_consumed,
+ data.size() - bytes_consumed);
+
+ // Stop processing if no more data but next state would require it.
+ if (data.empty() && (state_ != State::kStartField) &&
+ (state_ != State::kVarintDone) && (state_ != State::kReadStringDone)) {
+ return;
+ }
+ }
+}
+
+bool QpackInstructionDecoder::AtInstructionBoundary() const {
+ return state_ == State::kStartInstruction;
+}
+
+void QpackInstructionDecoder::DoStartInstruction(QuicStringPiece data) {
+ DCHECK(!data.empty());
+
+ instruction_ = LookupOpcode(data[0]);
+ field_ = instruction_->fields.begin();
+
+ state_ = State::kStartField;
+}
+
+void QpackInstructionDecoder::DoStartField() {
+ if (field_ == instruction_->fields.end()) {
+ // Completed decoding this instruction.
+
+ if (!delegate_->OnInstructionDecoded(instruction_)) {
+ error_detected_ = true;
+ return;
+ }
+
+ state_ = State::kStartInstruction;
+ return;
+ }
+
+ switch (field_->type) {
+ case QpackInstructionFieldType::kSbit:
+ case QpackInstructionFieldType::kName:
+ case QpackInstructionFieldType::kValue:
+ state_ = State::kReadBit;
+ return;
+ case QpackInstructionFieldType::kVarint:
+ case QpackInstructionFieldType::kVarint2:
+ state_ = State::kVarintStart;
+ return;
+ }
+}
+
+void QpackInstructionDecoder::DoReadBit(QuicStringPiece data) {
+ DCHECK(!data.empty());
+
+ switch (field_->type) {
+ case QpackInstructionFieldType::kSbit: {
+ const uint8_t bitmask = field_->param;
+ s_bit_ = (data[0] & bitmask) == bitmask;
+
+ ++field_;
+ state_ = State::kStartField;
+
+ return;
+ }
+ case QpackInstructionFieldType::kName:
+ case QpackInstructionFieldType::kValue: {
+ const uint8_t prefix_length = field_->param;
+ DCHECK_GE(7, prefix_length);
+ const uint8_t bitmask = 1 << prefix_length;
+ is_huffman_encoded_ = (data[0] & bitmask) == bitmask;
+
+ state_ = State::kVarintStart;
+
+ return;
+ }
+ default:
+ DCHECK(false);
+ }
+}
+
+size_t QpackInstructionDecoder::DoVarintStart(QuicStringPiece data) {
+ DCHECK(!data.empty());
+ DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2 ||
+ field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ http2::DecodeBuffer buffer(data.data() + 1, data.size() - 1);
+ http2::DecodeStatus status =
+ varint_decoder_.Start(data[0], field_->param, &buffer);
+
+ size_t bytes_consumed = 1 + buffer.Offset();
+ switch (status) {
+ case http2::DecodeStatus::kDecodeDone:
+ state_ = State::kVarintDone;
+ return bytes_consumed;
+ case http2::DecodeStatus::kDecodeInProgress:
+ state_ = State::kVarintResume;
+ return bytes_consumed;
+ case http2::DecodeStatus::kDecodeError:
+ OnError("Encoded integer too large.");
+ return bytes_consumed;
+ }
+}
+
+size_t QpackInstructionDecoder::DoVarintResume(QuicStringPiece data) {
+ DCHECK(!data.empty());
+ DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2 ||
+ field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ http2::DecodeBuffer buffer(data);
+ http2::DecodeStatus status = varint_decoder_.Resume(&buffer);
+
+ size_t bytes_consumed = buffer.Offset();
+ switch (status) {
+ case http2::DecodeStatus::kDecodeDone:
+ state_ = State::kVarintDone;
+ return bytes_consumed;
+ case http2::DecodeStatus::kDecodeInProgress:
+ DCHECK_EQ(bytes_consumed, data.size());
+ DCHECK(buffer.Empty());
+ return bytes_consumed;
+ case http2::DecodeStatus::kDecodeError:
+ OnError("Encoded integer too large.");
+ return bytes_consumed;
+ }
+}
+
+void QpackInstructionDecoder::DoVarintDone() {
+ DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2 ||
+ field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ if (field_->type == QpackInstructionFieldType::kVarint) {
+ varint_ = varint_decoder_.value();
+
+ ++field_;
+ state_ = State::kStartField;
+ return;
+ }
+
+ if (field_->type == QpackInstructionFieldType::kVarint2) {
+ varint2_ = varint_decoder_.value();
+
+ ++field_;
+ state_ = State::kStartField;
+ return;
+ }
+
+ string_length_ = varint_decoder_.value();
+ if (string_length_ > kStringLiteralLengthLimit) {
+ OnError("String literal too long.");
+ return;
+ }
+
+ std::string* const string =
+ (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+ string->clear();
+
+ if (string_length_ == 0) {
+ ++field_;
+ state_ = State::kStartField;
+ return;
+ }
+
+ string->reserve(string_length_);
+
+ state_ = State::kReadString;
+}
+
+size_t QpackInstructionDecoder::DoReadString(QuicStringPiece data) {
+ DCHECK(!data.empty());
+ DCHECK(field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ std::string* const string =
+ (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+ DCHECK_LT(string->size(), string_length_);
+
+ size_t bytes_consumed =
+ std::min(string_length_ - string->size(), data.size());
+ string->append(data.data(), bytes_consumed);
+
+ DCHECK_LE(string->size(), string_length_);
+ if (string->size() == string_length_) {
+ state_ = State::kReadStringDone;
+ }
+ return bytes_consumed;
+}
+
+void QpackInstructionDecoder::DoReadStringDone() {
+ DCHECK(field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ std::string* const string =
+ (field_->type == QpackInstructionFieldType::kName) ? &name_ : &value_;
+ DCHECK_EQ(string->size(), string_length_);
+
+ if (is_huffman_encoded_) {
+ huffman_decoder_.Reset();
+ // HpackHuffmanDecoder::Decode() cannot perform in-place decoding.
+ std::string decoded_value;
+ huffman_decoder_.Decode(*string, &decoded_value);
+ if (!huffman_decoder_.InputProperlyTerminated()) {
+ OnError("Error in Huffman-encoded string.");
+ return;
+ }
+ *string = std::move(decoded_value);
+ }
+
+ ++field_;
+ state_ = State::kStartField;
+}
+
+const QpackInstruction* QpackInstructionDecoder::LookupOpcode(
+ uint8_t byte) const {
+ for (const auto* instruction : *language_) {
+ if ((byte & instruction->opcode.mask) == instruction->opcode.value) {
+ return instruction;
+ }
+ }
+ // |language_| should be defined such that instruction opcodes cover every
+ // possible input.
+ DCHECK(false);
+ return nullptr;
+}
+
+void QpackInstructionDecoder::OnError(QuicStringPiece error_message) {
+ DCHECK(!error_detected_);
+
+ error_detected_ = true;
+ delegate_->OnError(error_message);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h
new file mode 100644
index 00000000000..f478c249b07
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h
@@ -0,0 +1,146 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_decoder.h"
+#include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_decoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// Generic instruction decoder class. Takes a QpackLanguage that describes a
+// language, that is, a set of instruction opcodes together with a list of
+// fields that follow each instruction.
+class QUIC_EXPORT_PRIVATE QpackInstructionDecoder {
+ public:
+ // Delegate is notified each time an instruction is decoded or when an error
+ // occurs.
+ class QUIC_EXPORT_PRIVATE Delegate {
+ public:
+ virtual ~Delegate() = default;
+
+ // Called when an instruction (including all its fields) is decoded.
+ // |instruction| points to an entry in |language|.
+ // Returns true if decoded fields are valid.
+ // Returns false otherwise, in which case QpackInstructionDecoder stops
+ // decoding: Delegate methods will not be called, and Decode() must not be
+ // called.
+ virtual bool OnInstructionDecoded(const QpackInstruction* instruction) = 0;
+
+ // Called by QpackInstructionDecoder if an error has occurred.
+ // No more data is processed afterwards.
+ virtual void OnError(QuicStringPiece error_message) = 0;
+ };
+
+ // Both |*language| and |*delegate| must outlive this object.
+ QpackInstructionDecoder(const QpackLanguage* language, Delegate* delegate);
+ QpackInstructionDecoder() = delete;
+ QpackInstructionDecoder(const QpackInstructionDecoder&) = delete;
+ QpackInstructionDecoder& operator=(const QpackInstructionDecoder&) = delete;
+
+ // Provide a data fragment to decode. Must not be called after an error has
+ // occurred. Must not be called with empty |data|.
+ void Decode(QuicStringPiece data);
+
+ // Returns true if no decoding has taken place yet or if the last instruction
+ // has been entirely parsed.
+ bool AtInstructionBoundary() const;
+
+ // Accessors for decoded values. Should only be called for fields that are
+ // part of the most recently decoded instruction, and only after |this| calls
+ // Delegate::OnInstructionDecoded() but before Decode() is called again.
+ bool s_bit() const { return s_bit_; }
+ uint64_t varint() const { return varint_; }
+ uint64_t varint2() const { return varint2_; }
+ const std::string& name() const { return name_; }
+ const std::string& value() const { return value_; }
+
+ private:
+ enum class State {
+ // Identify instruction.
+ kStartInstruction,
+ // Start decoding next field.
+ kStartField,
+ // Read a single bit.
+ kReadBit,
+ // Start reading integer.
+ kVarintStart,
+ // Resume reading integer.
+ kVarintResume,
+ // Done reading integer.
+ kVarintDone,
+ // Read string.
+ kReadString,
+ // Done reading string.
+ kReadStringDone
+ };
+
+ // One method for each state. Some take input data and return the number of
+ // octets processed. Some take input data but do have void return type
+ // because they not consume any bytes. Some do not take any arguments because
+ // they only change internal state.
+ void DoStartInstruction(QuicStringPiece data);
+ void DoStartField();
+ void DoReadBit(QuicStringPiece data);
+ size_t DoVarintStart(QuicStringPiece data);
+ size_t DoVarintResume(QuicStringPiece data);
+ void DoVarintDone();
+ size_t DoReadString(QuicStringPiece data);
+ void DoReadStringDone();
+
+ // Identify instruction based on opcode encoded in |byte|.
+ // Returns a pointer to an element of |*language_|.
+ const QpackInstruction* LookupOpcode(uint8_t byte) const;
+
+ // Stops decoding and calls Delegate::OnError().
+ void OnError(QuicStringPiece error_message);
+
+ // Describes the language used for decoding.
+ const QpackLanguage* const language_;
+
+ // The Delegate to notify of decoded instructions and errors.
+ Delegate* const delegate_;
+
+ // Storage for decoded field values.
+ bool s_bit_;
+ uint64_t varint_;
+ uint64_t varint2_;
+ std::string name_;
+ std::string value_;
+ // Whether the currently decoded header name or value is Huffman encoded.
+ bool is_huffman_encoded_;
+ // Length of string being read into |name_| or |value_|.
+ size_t string_length_;
+
+ // Decoder instance for decoding integers.
+ http2::HpackVarintDecoder varint_decoder_;
+
+ // Decoder instance for decoding Huffman encoded strings.
+ http2::HpackHuffmanDecoder huffman_decoder_;
+
+ // True if a decoding error has been detected either by
+ // QpackInstructionDecoder or by Delegate.
+ bool error_detected_;
+
+ // Decoding state.
+ State state_;
+
+ // Instruction currently being decoded.
+ const QpackInstruction* instruction_;
+
+ // Field currently being decoded.
+ QpackInstructionFields::const_iterator field_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc
new file mode 100644
index 00000000000..f0d1d1d0564
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder_test.cc
@@ -0,0 +1,171 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
+
+#include <algorithm>
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::_;
+using ::testing::Eq;
+using ::testing::Expectation;
+using ::testing::Return;
+using ::testing::StrictMock;
+using ::testing::Values;
+
+namespace quic {
+namespace test {
+namespace {
+
+// This instruction has three fields: an S bit and two varints.
+const QpackInstruction* TestInstruction1() {
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{QpackInstructionOpcode{0x00, 0x80},
+ {{QpackInstructionFieldType::kSbit, 0x40},
+ {QpackInstructionFieldType::kVarint, 6},
+ {QpackInstructionFieldType::kVarint2, 8}}};
+ return instruction;
+}
+
+// This instruction has two fields: a header name with a 6-bit prefix, and a
+// header value with a 7-bit prefix, both preceded by a Huffman bit.
+const QpackInstruction* TestInstruction2() {
+ static const QpackInstruction* const instruction =
+ new QpackInstruction{QpackInstructionOpcode{0x80, 0x80},
+ {{QpackInstructionFieldType::kName, 6},
+ {QpackInstructionFieldType::kValue, 7}}};
+ return instruction;
+}
+
+const QpackLanguage* TestLanguage() {
+ static const QpackLanguage* const language =
+ new QpackLanguage{TestInstruction1(), TestInstruction2()};
+ return language;
+}
+
+class MockDelegate : public QpackInstructionDecoder::Delegate {
+ public:
+ MockDelegate() {
+ ON_CALL(*this, OnInstructionDecoded(_)).WillByDefault(Return(true));
+ }
+
+ MockDelegate(const MockDelegate&) = delete;
+ MockDelegate& operator=(const MockDelegate&) = delete;
+ ~MockDelegate() override = default;
+
+ MOCK_METHOD1(OnInstructionDecoded, bool(const QpackInstruction* instruction));
+ MOCK_METHOD1(OnError, void(QuicStringPiece error_message));
+};
+
+class QpackInstructionDecoderTest : public QuicTestWithParam<FragmentMode> {
+ public:
+ QpackInstructionDecoderTest()
+ : decoder_(TestLanguage(), &delegate_), fragment_mode_(GetParam()) {}
+ ~QpackInstructionDecoderTest() override = default;
+
+ protected:
+ // Decode one full instruction with fragment sizes dictated by
+ // |fragment_mode_|.
+ // Verifies that AtInstructionBoundary() returns true before and after the
+ // instruction, and returns false while decoding is in progress.
+ void DecodeInstruction(QuicStringPiece data) {
+ EXPECT_TRUE(decoder_.AtInstructionBoundary());
+
+ FragmentSizeGenerator fragment_size_generator =
+ FragmentModeToFragmentSizeGenerator(fragment_mode_);
+
+ while (!data.empty()) {
+ size_t fragment_size = std::min(fragment_size_generator(), data.size());
+ decoder_.Decode(data.substr(0, fragment_size));
+ data = data.substr(fragment_size);
+ if (!data.empty()) {
+ EXPECT_FALSE(decoder_.AtInstructionBoundary());
+ }
+ }
+
+ EXPECT_TRUE(decoder_.AtInstructionBoundary());
+ }
+
+ StrictMock<MockDelegate> delegate_;
+ QpackInstructionDecoder decoder_;
+
+ private:
+ const FragmentMode fragment_mode_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+ QpackInstructionDecoderTest,
+ Values(FragmentMode::kSingleChunk,
+ FragmentMode::kOctetByOctet));
+
+TEST_P(QpackInstructionDecoderTest, SBitAndVarint2) {
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()));
+ DecodeInstruction(QuicTextUtils::HexDecode("7f01ff65"));
+
+ EXPECT_TRUE(decoder_.s_bit());
+ EXPECT_EQ(64u, decoder_.varint());
+ EXPECT_EQ(356u, decoder_.varint2());
+
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()));
+ DecodeInstruction(QuicTextUtils::HexDecode("05c8"));
+
+ EXPECT_FALSE(decoder_.s_bit());
+ EXPECT_EQ(5u, decoder_.varint());
+ EXPECT_EQ(200u, decoder_.varint2());
+}
+
+TEST_P(QpackInstructionDecoderTest, NameAndValue) {
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2()));
+ DecodeInstruction(QuicTextUtils::HexDecode("83666f6f03626172"));
+
+ EXPECT_EQ("foo", decoder_.name());
+ EXPECT_EQ("bar", decoder_.value());
+
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2()));
+ DecodeInstruction(QuicTextUtils::HexDecode("8000"));
+
+ EXPECT_EQ("", decoder_.name());
+ EXPECT_EQ("", decoder_.value());
+
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction2()));
+ DecodeInstruction(QuicTextUtils::HexDecode("c294e7838c767f"));
+
+ EXPECT_EQ("foo", decoder_.name());
+ EXPECT_EQ("bar", decoder_.value());
+}
+
+TEST_P(QpackInstructionDecoderTest, InvalidHuffmanEncoding) {
+ EXPECT_CALL(delegate_, OnError(Eq("Error in Huffman-encoded string.")));
+ decoder_.Decode(QuicTextUtils::HexDecode("c1ff"));
+}
+
+TEST_P(QpackInstructionDecoderTest, InvalidVarintEncoding) {
+ EXPECT_CALL(delegate_, OnError(Eq("Encoded integer too large.")));
+ decoder_.Decode(QuicTextUtils::HexDecode("ffffffffffffffffffffff"));
+}
+
+TEST_P(QpackInstructionDecoderTest, DelegateSignalsError) {
+ // First instruction is valid.
+ Expectation first_call =
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
+ .WillOnce(Return(true));
+ // Second instruction is invalid. Decoding must halt.
+ EXPECT_CALL(delegate_, OnInstructionDecoded(TestInstruction1()))
+ .After(first_call)
+ .WillOnce(Return(false));
+ decoder_.Decode(QuicTextUtils::HexDecode("01000200030004000500"));
+
+ EXPECT_EQ(2u, decoder_.varint());
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc
new file mode 100644
index 00000000000..3ae38f65697
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.cc
@@ -0,0 +1,217 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
+
+#include "net/third_party/quiche/src/http2/hpack/huffman/hpack_huffman_encoder.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_utils.h"
+
+namespace quic {
+
+QpackInstructionEncoder::QpackInstructionEncoder()
+ : s_bit_(false),
+ varint_(0),
+ varint2_(0),
+ byte_(0),
+ state_(State::kOpcode),
+ instruction_(nullptr) {}
+
+void QpackInstructionEncoder::Encode(const QpackInstruction* instruction) {
+ DCHECK(!HasNext());
+
+ state_ = State::kOpcode;
+ instruction_ = instruction;
+ field_ = instruction_->fields.begin();
+
+ // Field list must not be empty.
+ DCHECK(field_ != instruction_->fields.end());
+}
+
+bool QpackInstructionEncoder::HasNext() const {
+ return instruction_ && (field_ != instruction_->fields.end());
+}
+
+void QpackInstructionEncoder::Next(size_t max_encoded_bytes,
+ std::string* output) {
+ DCHECK(HasNext());
+ DCHECK_NE(0u, max_encoded_bytes);
+
+ while (max_encoded_bytes > 0 && HasNext()) {
+ size_t encoded_bytes = 0;
+
+ switch (state_) {
+ case State::kOpcode:
+ DoOpcode();
+ break;
+ case State::kStartField:
+ DoStartField();
+ break;
+ case State::kSbit:
+ DoStaticBit();
+ break;
+ case State::kVarintStart:
+ encoded_bytes = DoVarintStart(max_encoded_bytes, output);
+ break;
+ case State::kVarintResume:
+ encoded_bytes = DoVarintResume(max_encoded_bytes, output);
+ break;
+ case State::kStartString:
+ DoStartString();
+ break;
+ case State::kWriteString:
+ encoded_bytes = DoWriteString(max_encoded_bytes, output);
+ break;
+ }
+
+ DCHECK_LE(encoded_bytes, max_encoded_bytes);
+ max_encoded_bytes -= encoded_bytes;
+ }
+}
+
+void QpackInstructionEncoder::DoOpcode() {
+ DCHECK_EQ(0u, byte_);
+
+ byte_ = instruction_->opcode.value;
+
+ state_ = State::kStartField;
+}
+
+void QpackInstructionEncoder::DoStartField() {
+ switch (field_->type) {
+ case QpackInstructionFieldType::kSbit:
+ state_ = State::kSbit;
+ return;
+ case QpackInstructionFieldType::kVarint:
+ case QpackInstructionFieldType::kVarint2:
+ state_ = State::kVarintStart;
+ return;
+ case QpackInstructionFieldType::kName:
+ case QpackInstructionFieldType::kValue:
+ state_ = State::kStartString;
+ return;
+ }
+}
+
+void QpackInstructionEncoder::DoStaticBit() {
+ DCHECK(field_->type == QpackInstructionFieldType::kSbit);
+
+ if (s_bit_) {
+ DCHECK_EQ(0, byte_ & field_->param);
+
+ byte_ |= field_->param;
+ }
+
+ ++field_;
+ state_ = State::kStartField;
+}
+
+size_t QpackInstructionEncoder::DoVarintStart(size_t max_encoded_bytes,
+ std::string* output) {
+ DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2 ||
+ field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+ DCHECK(!varint_encoder_.IsEncodingInProgress());
+
+ uint64_t integer_to_encode;
+ switch (field_->type) {
+ case QpackInstructionFieldType::kVarint:
+ integer_to_encode = varint_;
+ break;
+ case QpackInstructionFieldType::kVarint2:
+ integer_to_encode = varint2_;
+ break;
+ default:
+ integer_to_encode = string_to_write_.size();
+ break;
+ }
+
+ output->push_back(
+ varint_encoder_.StartEncoding(byte_, field_->param, integer_to_encode));
+ byte_ = 0;
+
+ if (varint_encoder_.IsEncodingInProgress()) {
+ state_ = State::kVarintResume;
+ return 1;
+ }
+
+ if (field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2) {
+ ++field_;
+ state_ = State::kStartField;
+ return 1;
+ }
+
+ state_ = State::kWriteString;
+ return 1;
+}
+
+size_t QpackInstructionEncoder::DoVarintResume(size_t max_encoded_bytes,
+ std::string* output) {
+ DCHECK(field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2 ||
+ field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+ DCHECK(varint_encoder_.IsEncodingInProgress());
+
+ const size_t encoded_bytes =
+ varint_encoder_.ResumeEncoding(max_encoded_bytes, output);
+ if (varint_encoder_.IsEncodingInProgress()) {
+ DCHECK_EQ(encoded_bytes, max_encoded_bytes);
+ return encoded_bytes;
+ }
+
+ DCHECK_LE(encoded_bytes, max_encoded_bytes);
+
+ if (field_->type == QpackInstructionFieldType::kVarint ||
+ field_->type == QpackInstructionFieldType::kVarint2) {
+ ++field_;
+ state_ = State::kStartField;
+ return encoded_bytes;
+ }
+
+ state_ = State::kWriteString;
+ return encoded_bytes;
+}
+
+void QpackInstructionEncoder::DoStartString() {
+ DCHECK(field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ string_to_write_ =
+ (field_->type == QpackInstructionFieldType::kName) ? name_ : value_;
+ http2::HuffmanEncode(string_to_write_, &huffman_encoded_string_);
+
+ if (huffman_encoded_string_.size() < string_to_write_.size()) {
+ DCHECK_EQ(0, byte_ & (1 << field_->param));
+
+ byte_ |= (1 << field_->param);
+ string_to_write_ = huffman_encoded_string_;
+ }
+
+ state_ = State::kVarintStart;
+}
+
+size_t QpackInstructionEncoder::DoWriteString(size_t max_encoded_bytes,
+ std::string* output) {
+ DCHECK(field_->type == QpackInstructionFieldType::kName ||
+ field_->type == QpackInstructionFieldType::kValue);
+
+ if (max_encoded_bytes < string_to_write_.size()) {
+ const size_t encoded_bytes = max_encoded_bytes;
+ QuicStrAppend(output, string_to_write_.substr(0, encoded_bytes));
+ string_to_write_ = string_to_write_.substr(encoded_bytes);
+ return encoded_bytes;
+ }
+
+ const size_t encoded_bytes = string_to_write_.size();
+ QuicStrAppend(output, string_to_write_);
+
+ ++field_;
+ state_ = State::kStartField;
+ return encoded_bytes;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h
new file mode 100644
index 00000000000..917378ed8f3
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h
@@ -0,0 +1,118 @@
+// 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.
+
+#ifndef QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_ENCODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_ENCODER_H_
+
+#include <cstddef>
+#include <cstdint>
+#include <string>
+
+#include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+// Generic instruction encoder class. Takes a QpackLanguage that describes a
+// language, that is, a set of instruction opcodes together with a list of
+// fields that follow each instruction.
+class QUIC_EXPORT_PRIVATE QpackInstructionEncoder {
+ public:
+ QpackInstructionEncoder();
+ QpackInstructionEncoder(const QpackInstructionEncoder&) = delete;
+ QpackInstructionEncoder& operator=(const QpackInstructionEncoder&) = delete;
+
+ // Setters for values to be encoded.
+ // |name| and |value| must remain valid until the instruction is encoded.
+ void set_s_bit(bool s_bit) { s_bit_ = s_bit; }
+ void set_varint(uint64_t varint) { varint_ = varint; }
+ void set_varint2(uint64_t varint2) { varint2_ = varint2; }
+ void set_name(QuicStringPiece name) { name_ = name; }
+ void set_value(QuicStringPiece value) { value_ = value; }
+
+ // Start encoding an instruction. Must only be called after the previous
+ // instruction has been completely encoded.
+ void Encode(const QpackInstruction* instruction);
+
+ // Returns true iff more data remains to be encoded for the current
+ // instruction. Returns false if there is no current instruction, that is, if
+ // Encode() has never been called.
+ bool HasNext() const;
+
+ // Encodes the next up to |max_encoded_bytes| octets of the current
+ // instruction, appending to |output|. Must only be called when HasNext()
+ // returns true. |max_encoded_bytes| must be positive.
+ void Next(size_t max_encoded_bytes, std::string* output);
+
+ private:
+ enum class State {
+ // Write instruction opcode to |byte_|.
+ kOpcode,
+ // Select state based on type of current field.
+ kStartField,
+ // Write static bit to |byte_|.
+ kSbit,
+ // Start encoding an integer (|varint_| or |varint2_| or string length) with
+ // a prefix, using |byte_| for the high bits.
+ kVarintStart,
+ // Resume encoding an integer.
+ kVarintResume,
+ // Determine if Huffman encoding should be used for |name_| or |value_|, set
+ // up |name_| or |value_| and |huffman_encoded_string_| accordingly, and
+ // write the Huffman bit to |byte_|.
+ kStartString,
+ // Write string.
+ kWriteString
+ };
+
+ // One method for each state. Some encode up to |max_encoded_bytes| octets,
+ // appending to |output|. Some only change internal state.
+ void DoOpcode();
+ void DoStartField();
+ void DoStaticBit();
+ size_t DoVarintStart(size_t max_encoded_bytes, std::string* output);
+ size_t DoVarintResume(size_t max_encoded_bytes, std::string* output);
+ void DoStartString();
+ size_t DoWriteString(size_t max_encoded_bytes, std::string* output);
+
+ // Storage for field values to be encoded.
+ bool s_bit_;
+ uint64_t varint_;
+ uint64_t varint2_;
+ // The caller must keep the string that |name_| and |value_| point to
+ // valid until they are encoded.
+ QuicStringPiece name_;
+ QuicStringPiece value_;
+
+ // Storage for the Huffman encoded string literal to be written if Huffman
+ // encoding is used.
+ std::string huffman_encoded_string_;
+
+ // If Huffman encoding is used, points to a substring of
+ // |huffman_encoded_string_|.
+ // Otherwise points to a substring of |name_| or |value_|.
+ QuicStringPiece string_to_write_;
+
+ // Storage for a single byte that contains multiple fields, that is, multiple
+ // states are writing it.
+ uint8_t byte_;
+
+ // Encoding state.
+ State state_;
+
+ // Instruction currently being decoded.
+ const QpackInstruction* instruction_;
+
+ // Field currently being decoded.
+ QpackInstructionFields::const_iterator field_;
+
+ // Decoder instance for decoding integers.
+ http2::HpackVarintEncoder varint_encoder_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_INSTRUCTION_ENCODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc
new file mode 100644
index 00000000000..8f6aeaab97a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder_test.cc
@@ -0,0 +1,152 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
+
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_text_utils.h"
+
+using ::testing::Values;
+
+namespace quic {
+namespace test {
+namespace {
+
+class QpackInstructionEncoderTest : public QuicTestWithParam<FragmentMode> {
+ protected:
+ QpackInstructionEncoderTest() : fragment_mode_(GetParam()) {}
+ ~QpackInstructionEncoderTest() override = default;
+
+ // Encode |instruction| with fragment sizes dictated by |fragment_mode_|.
+ std::string EncodeInstruction(const QpackInstruction* instruction) {
+ EXPECT_FALSE(encoder_.HasNext());
+
+ FragmentSizeGenerator fragment_size_generator =
+ FragmentModeToFragmentSizeGenerator(fragment_mode_);
+ std::string output;
+ encoder_.Encode(instruction);
+ while (encoder_.HasNext()) {
+ encoder_.Next(fragment_size_generator(), &output);
+ }
+
+ return output;
+ }
+
+ QpackInstructionEncoder encoder_;
+
+ private:
+ const FragmentMode fragment_mode_;
+};
+
+INSTANTIATE_TEST_SUITE_P(,
+ QpackInstructionEncoderTest,
+ Values(FragmentMode::kSingleChunk,
+ FragmentMode::kOctetByOctet));
+
+TEST_P(QpackInstructionEncoderTest, Varint) {
+ const QpackInstruction instruction{QpackInstructionOpcode{0x00, 0x80},
+ {{QpackInstructionFieldType::kVarint, 7}}};
+
+ encoder_.set_varint(5);
+ EXPECT_EQ(QuicTextUtils::HexDecode("05"), EncodeInstruction(&instruction));
+
+ encoder_.set_varint(127);
+ EXPECT_EQ(QuicTextUtils::HexDecode("7f00"), EncodeInstruction(&instruction));
+}
+
+TEST_P(QpackInstructionEncoderTest, SBitAndTwoVarint2) {
+ const QpackInstruction instruction{
+ QpackInstructionOpcode{0x80, 0xc0},
+ {{QpackInstructionFieldType::kSbit, 0x20},
+ {QpackInstructionFieldType::kVarint, 5},
+ {QpackInstructionFieldType::kVarint2, 8}}};
+
+ encoder_.set_s_bit(true);
+ encoder_.set_varint(5);
+ encoder_.set_varint2(200);
+ EXPECT_EQ(QuicTextUtils::HexDecode("a5c8"), EncodeInstruction(&instruction));
+
+ encoder_.set_s_bit(false);
+ encoder_.set_varint(31);
+ encoder_.set_varint2(356);
+ EXPECT_EQ(QuicTextUtils::HexDecode("9f00ff65"),
+ EncodeInstruction(&instruction));
+}
+
+TEST_P(QpackInstructionEncoderTest, SBitAndVarintAndValue) {
+ const QpackInstruction instruction{QpackInstructionOpcode{0xc0, 0xc0},
+ {{QpackInstructionFieldType::kSbit, 0x20},
+ {QpackInstructionFieldType::kVarint, 5},
+ {QpackInstructionFieldType::kValue, 7}}};
+
+ encoder_.set_s_bit(true);
+ encoder_.set_varint(100);
+ encoder_.set_value("foo");
+ EXPECT_EQ(QuicTextUtils::HexDecode("ff458294e7"),
+ EncodeInstruction(&instruction));
+
+ encoder_.set_s_bit(false);
+ encoder_.set_varint(3);
+ encoder_.set_value("bar");
+ EXPECT_EQ(QuicTextUtils::HexDecode("c303626172"),
+ EncodeInstruction(&instruction));
+}
+
+TEST_P(QpackInstructionEncoderTest, Name) {
+ const QpackInstruction instruction{QpackInstructionOpcode{0xe0, 0xe0},
+ {{QpackInstructionFieldType::kName, 4}}};
+
+ encoder_.set_name("");
+ EXPECT_EQ(QuicTextUtils::HexDecode("e0"), EncodeInstruction(&instruction));
+
+ encoder_.set_name("foo");
+ EXPECT_EQ(QuicTextUtils::HexDecode("f294e7"),
+ EncodeInstruction(&instruction));
+
+ encoder_.set_name("bar");
+ EXPECT_EQ(QuicTextUtils::HexDecode("e3626172"),
+ EncodeInstruction(&instruction));
+}
+
+TEST_P(QpackInstructionEncoderTest, Value) {
+ const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0},
+ {{QpackInstructionFieldType::kValue, 3}}};
+
+ encoder_.set_value("");
+ EXPECT_EQ(QuicTextUtils::HexDecode("f0"), EncodeInstruction(&instruction));
+
+ encoder_.set_value("foo");
+ EXPECT_EQ(QuicTextUtils::HexDecode("fa94e7"),
+ EncodeInstruction(&instruction));
+
+ encoder_.set_value("bar");
+ EXPECT_EQ(QuicTextUtils::HexDecode("f3626172"),
+ EncodeInstruction(&instruction));
+}
+
+TEST_P(QpackInstructionEncoderTest, SBitAndNameAndValue) {
+ const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0},
+ {{QpackInstructionFieldType::kSbit, 0x08},
+ {QpackInstructionFieldType::kName, 2},
+ {QpackInstructionFieldType::kValue, 7}}};
+
+ encoder_.set_s_bit(false);
+ encoder_.set_name("");
+ encoder_.set_value("");
+ EXPECT_EQ(QuicTextUtils::HexDecode("f000"), EncodeInstruction(&instruction));
+
+ encoder_.set_s_bit(true);
+ encoder_.set_name("foo");
+ encoder_.set_value("bar");
+ EXPECT_EQ(QuicTextUtils::HexDecode("fe94e703626172"),
+ EncodeInstruction(&instruction));
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc
new file mode 100644
index 00000000000..05a5fc03d8a
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.cc
@@ -0,0 +1,369 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+
+#include <algorithm>
+#include <limits>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+QpackProgressiveDecoder::QpackProgressiveDecoder(
+ QuicStreamId stream_id,
+ QpackHeaderTable* header_table,
+ QpackDecoderStreamSender* decoder_stream_sender,
+ HeadersHandlerInterface* handler)
+ : stream_id_(stream_id),
+ prefix_decoder_(
+ QuicMakeUnique<QpackInstructionDecoder>(QpackPrefixLanguage(), this)),
+ instruction_decoder_(QpackRequestStreamLanguage(), this),
+ header_table_(header_table),
+ decoder_stream_sender_(decoder_stream_sender),
+ handler_(handler),
+ required_insert_count_(0),
+ base_(0),
+ required_insert_count_so_far_(0),
+ prefix_decoded_(false),
+ decoding_(true),
+ error_detected_(false) {}
+
+// static
+bool QpackProgressiveDecoder::DecodeRequiredInsertCount(
+ uint64_t encoded_required_insert_count,
+ uint64_t max_entries,
+ uint64_t total_number_of_inserts,
+ uint64_t* required_insert_count) {
+ if (encoded_required_insert_count == 0) {
+ *required_insert_count = 0;
+ return true;
+ }
+
+ // |max_entries| is calculated by dividing an unsigned 64-bit integer by 32,
+ // precluding all calculations in this method from overflowing.
+ DCHECK_LE(max_entries, std::numeric_limits<uint64_t>::max() / 32);
+
+ if (encoded_required_insert_count > 2 * max_entries) {
+ return false;
+ }
+
+ *required_insert_count = encoded_required_insert_count - 1;
+ DCHECK_LT(*required_insert_count, std::numeric_limits<uint64_t>::max() / 16);
+
+ uint64_t current_wrapped = total_number_of_inserts % (2 * max_entries);
+ DCHECK_LT(current_wrapped, std::numeric_limits<uint64_t>::max() / 16);
+
+ if (current_wrapped >= *required_insert_count + max_entries) {
+ // Required Insert Count wrapped around 1 extra time.
+ *required_insert_count += 2 * max_entries;
+ } else if (current_wrapped + max_entries < *required_insert_count) {
+ // Decoder wrapped around 1 extra time.
+ current_wrapped += 2 * max_entries;
+ }
+
+ if (*required_insert_count >
+ std::numeric_limits<uint64_t>::max() - total_number_of_inserts) {
+ return false;
+ }
+
+ *required_insert_count += total_number_of_inserts;
+
+ // Prevent underflow, also disallow invalid value 0 for Required Insert Count.
+ if (current_wrapped >= *required_insert_count) {
+ return false;
+ }
+
+ *required_insert_count -= current_wrapped;
+
+ return true;
+}
+
+void QpackProgressiveDecoder::Decode(QuicStringPiece data) {
+ DCHECK(decoding_);
+
+ if (data.empty() || error_detected_) {
+ return;
+ }
+
+ // Decode prefix byte by byte until the first (and only) instruction is
+ // decoded.
+ while (!prefix_decoded_) {
+ prefix_decoder_->Decode(data.substr(0, 1));
+ data = data.substr(1);
+ if (data.empty()) {
+ return;
+ }
+ }
+
+ instruction_decoder_.Decode(data);
+}
+
+void QpackProgressiveDecoder::EndHeaderBlock() {
+ DCHECK(decoding_);
+ decoding_ = false;
+
+ if (error_detected_) {
+ return;
+ }
+
+ if (!instruction_decoder_.AtInstructionBoundary()) {
+ OnError("Incomplete header block.");
+ return;
+ }
+
+ if (!prefix_decoded_) {
+ OnError("Incomplete header data prefix.");
+ return;
+ }
+
+ if (required_insert_count_ != required_insert_count_so_far_) {
+ OnError("Required Insert Count too large.");
+ return;
+ }
+
+ decoder_stream_sender_->SendHeaderAcknowledgement(stream_id_);
+ handler_->OnDecodingCompleted();
+}
+
+bool QpackProgressiveDecoder::OnInstructionDecoded(
+ const QpackInstruction* instruction) {
+ if (instruction == QpackIndexedHeaderFieldInstruction()) {
+ return DoIndexedHeaderFieldInstruction();
+ }
+ if (instruction == QpackIndexedHeaderFieldPostBaseInstruction()) {
+ return DoIndexedHeaderFieldPostBaseInstruction();
+ }
+ if (instruction == QpackLiteralHeaderFieldNameReferenceInstruction()) {
+ return DoLiteralHeaderFieldNameReferenceInstruction();
+ }
+ if (instruction == QpackLiteralHeaderFieldPostBaseInstruction()) {
+ return DoLiteralHeaderFieldPostBaseInstruction();
+ }
+ if (instruction == QpackLiteralHeaderFieldInstruction()) {
+ return DoLiteralHeaderFieldInstruction();
+ }
+ DCHECK_EQ(instruction, QpackPrefixInstruction());
+ return DoPrefixInstruction();
+}
+
+void QpackProgressiveDecoder::OnError(QuicStringPiece error_message) {
+ DCHECK(!error_detected_);
+
+ error_detected_ = true;
+ handler_->OnDecodingErrorDetected(error_message);
+}
+
+bool QpackProgressiveDecoder::DoIndexedHeaderFieldInstruction() {
+ if (!instruction_decoder_.s_bit()) {
+ uint64_t absolute_index;
+ if (!RequestStreamRelativeIndexToAbsoluteIndex(
+ instruction_decoder_.varint(), &absolute_index)) {
+ OnError("Invalid relative index.");
+ return false;
+ }
+
+ if (absolute_index >= required_insert_count_) {
+ OnError("Absolute Index must be smaller than Required Insert Count.");
+ return false;
+ }
+
+ DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+ required_insert_count_so_far_ =
+ std::max(required_insert_count_so_far_, absolute_index + 1);
+
+ auto entry =
+ header_table_->LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ OnError("Dynamic table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), entry->value());
+ return true;
+ }
+
+ auto entry = header_table_->LookupEntry(/* is_static = */ true,
+ instruction_decoder_.varint());
+ if (!entry) {
+ OnError("Static table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), entry->value());
+ return true;
+}
+
+bool QpackProgressiveDecoder::DoIndexedHeaderFieldPostBaseInstruction() {
+ uint64_t absolute_index;
+ if (!PostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(),
+ &absolute_index)) {
+ OnError("Invalid post-base index.");
+ return false;
+ }
+
+ if (absolute_index >= required_insert_count_) {
+ OnError("Absolute Index must be smaller than Required Insert Count.");
+ return false;
+ }
+
+ DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+ required_insert_count_so_far_ =
+ std::max(required_insert_count_so_far_, absolute_index + 1);
+
+ auto entry =
+ header_table_->LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ OnError("Dynamic table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), entry->value());
+ return true;
+}
+
+bool QpackProgressiveDecoder::DoLiteralHeaderFieldNameReferenceInstruction() {
+ if (!instruction_decoder_.s_bit()) {
+ uint64_t absolute_index;
+ if (!RequestStreamRelativeIndexToAbsoluteIndex(
+ instruction_decoder_.varint(), &absolute_index)) {
+ OnError("Invalid relative index.");
+ return false;
+ }
+
+ if (absolute_index >= required_insert_count_) {
+ OnError("Absolute Index must be smaller than Required Insert Count.");
+ return false;
+ }
+
+ DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+ required_insert_count_so_far_ =
+ std::max(required_insert_count_so_far_, absolute_index + 1);
+
+ auto entry =
+ header_table_->LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ OnError("Dynamic table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value());
+ return true;
+ }
+
+ auto entry = header_table_->LookupEntry(/* is_static = */ true,
+ instruction_decoder_.varint());
+ if (!entry) {
+ OnError("Static table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value());
+ return true;
+}
+
+bool QpackProgressiveDecoder::DoLiteralHeaderFieldPostBaseInstruction() {
+ uint64_t absolute_index;
+ if (!PostBaseIndexToAbsoluteIndex(instruction_decoder_.varint(),
+ &absolute_index)) {
+ OnError("Invalid post-base index.");
+ return false;
+ }
+
+ if (absolute_index >= required_insert_count_) {
+ OnError("Absolute Index must be smaller than Required Insert Count.");
+ return false;
+ }
+
+ DCHECK_LT(absolute_index, std::numeric_limits<uint64_t>::max());
+ required_insert_count_so_far_ =
+ std::max(required_insert_count_so_far_, absolute_index + 1);
+
+ auto entry =
+ header_table_->LookupEntry(/* is_static = */ false, absolute_index);
+ if (!entry) {
+ OnError("Dynamic table entry not found.");
+ return false;
+ }
+
+ handler_->OnHeaderDecoded(entry->name(), instruction_decoder_.value());
+ return true;
+}
+
+bool QpackProgressiveDecoder::DoLiteralHeaderFieldInstruction() {
+ handler_->OnHeaderDecoded(instruction_decoder_.name(),
+ instruction_decoder_.value());
+
+ return true;
+}
+
+bool QpackProgressiveDecoder::DoPrefixInstruction() {
+ DCHECK(!prefix_decoded_);
+
+ if (!DecodeRequiredInsertCount(
+ prefix_decoder_->varint(), header_table_->max_entries(),
+ header_table_->inserted_entry_count(), &required_insert_count_)) {
+ OnError("Error decoding Required Insert Count.");
+ return false;
+ }
+
+ const bool sign = prefix_decoder_->s_bit();
+ const uint64_t delta_base = prefix_decoder_->varint2();
+ if (!DeltaBaseToBase(sign, delta_base, &base_)) {
+ OnError("Error calculating Base.");
+ return false;
+ }
+
+ prefix_decoded_ = true;
+
+ return true;
+}
+
+bool QpackProgressiveDecoder::DeltaBaseToBase(bool sign,
+ uint64_t delta_base,
+ uint64_t* base) {
+ if (sign) {
+ if (delta_base == std::numeric_limits<uint64_t>::max() ||
+ required_insert_count_ < delta_base + 1) {
+ return false;
+ }
+ *base = required_insert_count_ - delta_base - 1;
+ return true;
+ }
+
+ if (delta_base >
+ std::numeric_limits<uint64_t>::max() - required_insert_count_) {
+ return false;
+ }
+ *base = required_insert_count_ + delta_base;
+ return true;
+}
+
+bool QpackProgressiveDecoder::RequestStreamRelativeIndexToAbsoluteIndex(
+ uint64_t relative_index,
+ uint64_t* absolute_index) const {
+ if (relative_index == std::numeric_limits<uint64_t>::max() ||
+ relative_index + 1 > base_) {
+ return false;
+ }
+
+ *absolute_index = base_ - 1 - relative_index;
+ return true;
+}
+
+bool QpackProgressiveDecoder::PostBaseIndexToAbsoluteIndex(
+ uint64_t post_base_index,
+ uint64_t* absolute_index) const {
+ if (post_base_index >= std::numeric_limits<uint64_t>::max() - base_) {
+ return false;
+ }
+
+ *absolute_index = base_ + post_base_index;
+ return true;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h
new file mode 100644
index 00000000000..52adf45b236
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h
@@ -0,0 +1,140 @@
+// 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_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_DECODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_DECODER_H_
+
+#include <cstdint>
+#include <memory>
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_stream_sender.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_receiver.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_decoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+class QpackHeaderTable;
+
+// Class to decode a single header block.
+class QUIC_EXPORT_PRIVATE QpackProgressiveDecoder
+ : public QpackInstructionDecoder::Delegate {
+ public:
+ // Interface for receiving decoded header block from the decoder.
+ class QUIC_EXPORT_PRIVATE HeadersHandlerInterface {
+ public:
+ virtual ~HeadersHandlerInterface() {}
+
+ // Called when a new header name-value pair is decoded. Multiple values for
+ // a given name will be emitted as multiple calls to OnHeader.
+ virtual void OnHeaderDecoded(QuicStringPiece name,
+ QuicStringPiece value) = 0;
+
+ // Called when the header block is completely decoded.
+ // Indicates the total number of bytes in this block.
+ // The decoder will not access the handler after this call.
+ // Note that this method might not be called synchronously when the header
+ // block is received on the wire, in case decoding is blocked on receiving
+ // entries on the encoder stream. TODO(bnc): Implement blocked decoding.
+ virtual void OnDecodingCompleted() = 0;
+
+ // Called when a decoding error has occurred. No other methods will be
+ // called afterwards.
+ virtual void OnDecodingErrorDetected(QuicStringPiece error_message) = 0;
+ };
+
+ QpackProgressiveDecoder() = delete;
+ QpackProgressiveDecoder(QuicStreamId stream_id,
+ QpackHeaderTable* header_table,
+ QpackDecoderStreamSender* decoder_stream_sender,
+ HeadersHandlerInterface* handler);
+ QpackProgressiveDecoder(const QpackProgressiveDecoder&) = delete;
+ QpackProgressiveDecoder& operator=(const QpackProgressiveDecoder&) = delete;
+ ~QpackProgressiveDecoder() override = default;
+
+ // Calculate Required Insert Count from Encoded Required Insert Count,
+ // MaxEntries, and total number of dynamic table insertions according to
+ // https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#ric.
+ // Returns true on success, false on invalid input or overflow/underflow.
+ static bool DecodeRequiredInsertCount(uint64_t encoded_required_insert_count,
+ uint64_t max_entries,
+ uint64_t total_number_of_inserts,
+ uint64_t* required_insert_count);
+
+ // Provide a data fragment to decode.
+ void Decode(QuicStringPiece data);
+
+ // Signal that the entire header block has been received and passed in
+ // through Decode(). No methods must be called afterwards.
+ void EndHeaderBlock();
+
+ // QpackInstructionDecoder::Delegate implementation.
+ bool OnInstructionDecoded(const QpackInstruction* instruction) override;
+ void OnError(QuicStringPiece error_message) override;
+
+ private:
+ bool DoIndexedHeaderFieldInstruction();
+ bool DoIndexedHeaderFieldPostBaseInstruction();
+ bool DoLiteralHeaderFieldNameReferenceInstruction();
+ bool DoLiteralHeaderFieldPostBaseInstruction();
+ bool DoLiteralHeaderFieldInstruction();
+ bool DoPrefixInstruction();
+
+ // Calculates Base from |required_insert_count_|, which must be set before
+ // calling this method, and sign bit and Delta Base in the Header Data Prefix,
+ // which are passed in as arguments. Returns true on success, false on
+ // failure due to overflow/underflow.
+ bool DeltaBaseToBase(bool sign, uint64_t delta_base, uint64_t* base);
+
+ // The request stream can use relative index (but different from the kind of
+ // relative index used on the encoder stream), and post-base index.
+ // These methods convert relative index and post-base index to absolute index
+ // (one based). They return true on success, or false if conversion fails due
+ // to overflow/underflow. On success, |*absolute_index| is guaranteed to be
+ // strictly less than std::numeric_limits<uint64_t>::max().
+ bool RequestStreamRelativeIndexToAbsoluteIndex(
+ uint64_t relative_index,
+ uint64_t* absolute_index) const;
+ bool PostBaseIndexToAbsoluteIndex(uint64_t post_base_index,
+ uint64_t* absolute_index) const;
+
+ const QuicStreamId stream_id_;
+
+ // |prefix_decoder_| only decodes a handful of bytes then it can be
+ // destroyed to conserve memory. |instruction_decoder_|, on the other hand,
+ // is used until the entire header block is decoded.
+ std::unique_ptr<QpackInstructionDecoder> prefix_decoder_;
+ QpackInstructionDecoder instruction_decoder_;
+
+ const QpackHeaderTable* const header_table_;
+ QpackDecoderStreamSender* const decoder_stream_sender_;
+ HeadersHandlerInterface* const handler_;
+
+ // Required Insert Count and Base are decoded from the Header Data Prefix.
+ uint64_t required_insert_count_;
+ uint64_t base_;
+
+ // Required Insert Count is one larger than the largest absolute index of all
+ // referenced dynamic table entries, or zero if no dynamic table entries are
+ // referenced. |required_insert_count_so_far_| starts out as zero and keeps
+ // track of the Required Insert Count based on entries decoded so far.
+ // After decoding is completed, it is compared to |required_insert_count_|.
+ uint64_t required_insert_count_so_far_;
+
+ // False until prefix is fully read and decoded.
+ bool prefix_decoded_;
+
+ // True until EndHeaderBlock() is called.
+ bool decoding_;
+
+ // True if a decoding error has been detected.
+ bool error_detected_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_DECODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder_test.cc
new file mode 100644
index 00000000000..b3067023b97
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder_test.cc
@@ -0,0 +1,124 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_decoder.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+namespace {
+
+// For testing valid decodings, the Encoded Required Insert Count is calculated
+// from Required Insert Count, so that there is an expected value to compare
+// the decoded value against, and so that intricate inequalities can be
+// documented.
+struct {
+ uint64_t required_insert_count;
+ uint64_t max_entries;
+ uint64_t total_number_of_inserts;
+} kTestData[] = {
+ // Maximum dynamic table capacity is zero.
+ {0, 0, 0},
+ // No dynamic entries in header.
+ {0, 100, 0},
+ {0, 100, 500},
+ // Required Insert Count has not wrapped around yet, no entries evicted.
+ {15, 100, 25},
+ {20, 100, 10},
+ // Required Insert Count has not wrapped around yet, some entries evicted.
+ {90, 100, 110},
+ // Required Insert Count has wrapped around.
+ {234, 100, 180},
+ // Required Insert Count has wrapped around many times.
+ {5678, 100, 5701},
+ // Lowest and highest possible Required Insert Count values
+ // for given MaxEntries and total number of insertions.
+ {401, 100, 500},
+ {600, 100, 500}};
+
+uint64_t EncodeRequiredInsertCount(uint64_t required_insert_count,
+ uint64_t max_entries) {
+ if (required_insert_count == 0) {
+ return 0;
+ }
+
+ return required_insert_count % (2 * max_entries) + 1;
+}
+
+TEST(QpackProgressiveDecoderTest, DecodeRequiredInsertCount) {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kTestData); ++i) {
+ const uint64_t required_insert_count = kTestData[i].required_insert_count;
+ const uint64_t max_entries = kTestData[i].max_entries;
+ const uint64_t total_number_of_inserts =
+ kTestData[i].total_number_of_inserts;
+
+ if (required_insert_count != 0) {
+ // Dynamic entries cannot be referenced if dynamic table capacity is zero.
+ ASSERT_LT(0u, max_entries) << i;
+ // Entry |total_number_of_inserts - 1 - max_entries| and earlier entries
+ // are evicted. Entry |required_insert_count - 1| is referenced. No
+ // evicted entry can be referenced.
+ ASSERT_LT(total_number_of_inserts, required_insert_count + max_entries)
+ << i;
+ // Entry |required_insert_count - 1 - max_entries| and earlier entries are
+ // evicted, entry |total_number_of_inserts - 1| is the last acknowledged
+ // entry. Every evicted entry must be acknowledged.
+ ASSERT_LE(required_insert_count, total_number_of_inserts + max_entries)
+ << i;
+ }
+
+ uint64_t encoded_required_insert_count =
+ EncodeRequiredInsertCount(required_insert_count, max_entries);
+
+ // Initialize to a value different from the expected output to confirm that
+ // DecodeRequiredInsertCount() modifies the value of
+ // |decoded_required_insert_count|.
+ uint64_t decoded_required_insert_count = required_insert_count + 1;
+ EXPECT_TRUE(QpackProgressiveDecoder::DecodeRequiredInsertCount(
+ encoded_required_insert_count, max_entries, total_number_of_inserts,
+ &decoded_required_insert_count))
+ << i;
+
+ EXPECT_EQ(decoded_required_insert_count, required_insert_count) << i;
+ }
+}
+
+// Failures are tested with hardcoded values for encoded required insert count,
+// to provide test coverage for values that would never be produced by a well
+// behaved encoding function.
+struct {
+ uint64_t encoded_required_insert_count;
+ uint64_t max_entries;
+ uint64_t total_number_of_inserts;
+} kInvalidTestData[] = {
+ // Maximum dynamic table capacity is zero, yet header block
+ // claims to have a reference to a dynamic table entry.
+ {1, 0, 0},
+ {9, 0, 0},
+ // Examples from
+ // https://github.com/quicwg/base-drafts/issues/2112#issue-389626872.
+ {1, 10, 2},
+ {18, 10, 2},
+ // Encoded Required Insert Count value too small or too large
+ // for given MaxEntries and total number of insertions.
+ {400, 100, 500},
+ {601, 100, 500}};
+
+TEST(QpackProgressiveDecoderTest, DecodeRequiredInsertCountError) {
+ for (size_t i = 0; i < QUIC_ARRAYSIZE(kInvalidTestData); ++i) {
+ uint64_t decoded_required_insert_count = 0;
+ EXPECT_FALSE(QpackProgressiveDecoder::DecodeRequiredInsertCount(
+ kInvalidTestData[i].encoded_required_insert_count,
+ kInvalidTestData[i].max_entries,
+ kInvalidTestData[i].total_number_of_inserts,
+ &decoded_required_insert_count))
+ << i;
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.cc
new file mode 100644
index 00000000000..05a025b28be
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.cc
@@ -0,0 +1,139 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h"
+
+#include <string>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_constants.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_header_table.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+QpackProgressiveEncoder::QpackProgressiveEncoder(
+ QuicStreamId stream_id,
+ QpackHeaderTable* header_table,
+ QpackEncoderStreamSender* encoder_stream_sender,
+ const spdy::SpdyHeaderBlock* header_list)
+ : stream_id_(stream_id),
+ header_table_(header_table),
+ encoder_stream_sender_(encoder_stream_sender),
+ header_list_(header_list),
+ header_list_iterator_(header_list_->begin()),
+ prefix_encoded_(false) {
+ // TODO(bnc): Use |stream_id_| for dynamic table entry management, and
+ // remove this dummy DCHECK.
+ DCHECK_LE(0u, stream_id_);
+
+ DCHECK(header_table_);
+ DCHECK(encoder_stream_sender_);
+ DCHECK(header_list_);
+}
+
+bool QpackProgressiveEncoder::HasNext() const {
+ return header_list_iterator_ != header_list_->end() || !prefix_encoded_;
+}
+
+void QpackProgressiveEncoder::Next(size_t max_encoded_bytes,
+ std::string* output) {
+ DCHECK_NE(0u, max_encoded_bytes);
+ DCHECK(HasNext());
+
+ // Since QpackInstructionEncoder::Next() does not indicate the number of bytes
+ // written, save the maximum new size of |*output|.
+ const size_t max_length = output->size() + max_encoded_bytes;
+
+ DCHECK_LT(output->size(), max_length);
+
+ if (!prefix_encoded_ && !instruction_encoder_.HasNext()) {
+ // TODO(bnc): Implement dynamic entries and set Required Insert Count and
+ // Delta Base accordingly.
+ instruction_encoder_.set_varint(0);
+ instruction_encoder_.set_varint2(0);
+ instruction_encoder_.set_s_bit(false);
+
+ instruction_encoder_.Encode(QpackPrefixInstruction());
+
+ DCHECK(instruction_encoder_.HasNext());
+ }
+
+ do {
+ // Call QpackInstructionEncoder::Encode for |*header_list_iterator_| if it
+ // has not been called yet.
+ if (!instruction_encoder_.HasNext()) {
+ DCHECK(prefix_encoded_);
+
+ // Even after |name| and |value| go out of scope, copies of these
+ // QuicStringPieces retained by QpackInstructionEncoder are still valid as
+ // long as |header_list_| is valid.
+ QuicStringPiece name = header_list_iterator_->first;
+ QuicStringPiece value = header_list_iterator_->second;
+
+ // |is_static| and |index| are saved by QpackInstructionEncoder by value,
+ // there are no lifetime concerns.
+ bool is_static;
+ uint64_t index;
+
+ auto match_type =
+ header_table_->FindHeaderField(name, value, &is_static, &index);
+
+ switch (match_type) {
+ case QpackHeaderTable::MatchType::kNameAndValue:
+ DCHECK(is_static) << "Dynamic table entries not supported yet.";
+
+ instruction_encoder_.set_s_bit(is_static);
+ instruction_encoder_.set_varint(index);
+
+ instruction_encoder_.Encode(QpackIndexedHeaderFieldInstruction());
+
+ break;
+ case QpackHeaderTable::MatchType::kName:
+ DCHECK(is_static) << "Dynamic table entries not supported yet.";
+
+ instruction_encoder_.set_s_bit(is_static);
+ instruction_encoder_.set_varint(index);
+ instruction_encoder_.set_value(value);
+
+ instruction_encoder_.Encode(
+ QpackLiteralHeaderFieldNameReferenceInstruction());
+
+ break;
+ case QpackHeaderTable::MatchType::kNoMatch:
+ instruction_encoder_.set_name(name);
+ instruction_encoder_.set_value(value);
+
+ instruction_encoder_.Encode(QpackLiteralHeaderFieldInstruction());
+
+ break;
+ }
+ }
+
+ DCHECK(instruction_encoder_.HasNext());
+
+ instruction_encoder_.Next(max_length - output->size(), output);
+
+ if (instruction_encoder_.HasNext()) {
+ // There was not enough room to completely encode current header field.
+ DCHECK_EQ(output->size(), max_length);
+
+ return;
+ }
+
+ // It is possible that the output buffer was just large enough for encoding
+ // the current header field, hence equality is allowed here.
+ DCHECK_LE(output->size(), max_length);
+
+ if (prefix_encoded_) {
+ // Move on to the next header field.
+ ++header_list_iterator_;
+ } else {
+ // Mark prefix as encoded.
+ prefix_encoded_ = true;
+ }
+ } while (HasNext() && output->size() < max_length);
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h
new file mode 100644
index 00000000000..8e204e23b38
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_progressive_encoder.h
@@ -0,0 +1,57 @@
+// 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_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_ENCODER_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_ENCODER_H_
+
+#include <cstddef>
+
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_stream_sender.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_instruction_encoder.h"
+#include "net/third_party/quiche/src/quic/core/quic_types.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+namespace quic {
+
+class QpackHeaderTable;
+
+// An implementation of ProgressiveEncoder interface that encodes a single
+// header block.
+class QUIC_EXPORT_PRIVATE QpackProgressiveEncoder
+ : public spdy::HpackEncoder::ProgressiveEncoder {
+ public:
+ QpackProgressiveEncoder() = delete;
+ QpackProgressiveEncoder(QuicStreamId stream_id,
+ QpackHeaderTable* header_table,
+ QpackEncoderStreamSender* encoder_stream_sender,
+ const spdy::SpdyHeaderBlock* header_list);
+ QpackProgressiveEncoder(const QpackProgressiveEncoder&) = delete;
+ QpackProgressiveEncoder& operator=(const QpackProgressiveEncoder&) = delete;
+ ~QpackProgressiveEncoder() override = default;
+
+ // Returns true iff more remains to encode.
+ bool HasNext() const override;
+
+ // Encodes up to |max_encoded_bytes| octets, appending to |output|.
+ void Next(size_t max_encoded_bytes, std::string* output) override;
+
+ private:
+ const QuicStreamId stream_id_;
+ QpackInstructionEncoder instruction_encoder_;
+ const QpackHeaderTable* const header_table_;
+ QpackEncoderStreamSender* const encoder_stream_sender_;
+ const spdy::SpdyHeaderBlock* const header_list_;
+
+ // Header field currently being encoded.
+ spdy::SpdyHeaderBlock::const_iterator header_list_iterator_;
+
+ // False until prefix is fully encoded.
+ bool prefix_encoded_;
+};
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_PROGRESSIVE_ENCODER_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc
new file mode 100644
index 00000000000..9b2df550b88
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_round_trip_test.cc
@@ -0,0 +1,137 @@
+// 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 <string>
+#include <tuple>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_decoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_encoder_test_utils.h"
+#include "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h"
+
+using ::testing::Combine;
+using ::testing::Values;
+
+namespace quic {
+namespace test {
+namespace {
+
+class QpackRoundTripTest
+ : public QuicTestWithParam<std::tuple<FragmentMode, FragmentMode>> {
+ public:
+ QpackRoundTripTest()
+ : encoding_fragment_mode_(std::get<0>(GetParam())),
+ decoding_fragment_mode_(std::get<1>(GetParam())) {}
+ ~QpackRoundTripTest() override = default;
+
+ spdy::SpdyHeaderBlock EncodeThenDecode(
+ const spdy::SpdyHeaderBlock& header_list) {
+ NoopDecoderStreamErrorDelegate decoder_stream_error_delegate;
+ NoopEncoderStreamSenderDelegate encoder_stream_sender_delegate;
+ std::string encoded_header_block = QpackEncode(
+ &decoder_stream_error_delegate, &encoder_stream_sender_delegate,
+ FragmentModeToFragmentSizeGenerator(encoding_fragment_mode_),
+ &header_list);
+
+ TestHeadersHandler handler;
+ NoopEncoderStreamErrorDelegate encoder_stream_error_delegate;
+ NoopDecoderStreamSenderDelegate decoder_stream_sender_delegate;
+ QpackDecode(&encoder_stream_error_delegate, &decoder_stream_sender_delegate,
+ &handler,
+ FragmentModeToFragmentSizeGenerator(decoding_fragment_mode_),
+ encoded_header_block);
+
+ EXPECT_TRUE(handler.decoding_completed());
+ EXPECT_FALSE(handler.decoding_error_detected());
+
+ return handler.ReleaseHeaderList();
+ }
+
+ private:
+ const FragmentMode encoding_fragment_mode_;
+ const FragmentMode decoding_fragment_mode_;
+};
+
+INSTANTIATE_TEST_SUITE_P(
+ ,
+ QpackRoundTripTest,
+ Combine(Values(FragmentMode::kSingleChunk, FragmentMode::kOctetByOctet),
+ Values(FragmentMode::kSingleChunk, FragmentMode::kOctetByOctet)));
+
+TEST_P(QpackRoundTripTest, Empty) {
+ spdy::SpdyHeaderBlock header_list;
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+}
+
+TEST_P(QpackRoundTripTest, EmptyName) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "bar";
+ header_list[""] = "bar";
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+}
+
+TEST_P(QpackRoundTripTest, EmptyValue) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "";
+ header_list[""] = "";
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+}
+
+TEST_P(QpackRoundTripTest, MultipleWithLongEntries) {
+ spdy::SpdyHeaderBlock header_list;
+ header_list["foo"] = "bar";
+ header_list[":path"] = "/";
+ header_list["foobaar"] = std::string(127, 'Z');
+ header_list[std::string(1000, 'b')] = std::string(1000, 'c');
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+}
+
+TEST_P(QpackRoundTripTest, StaticTable) {
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "GET";
+ header_list["accept-encoding"] = "gzip, deflate";
+ header_list["cache-control"] = "";
+ header_list["foo"] = "bar";
+ header_list[":path"] = "/";
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+ }
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "POST";
+ header_list["accept-encoding"] = "brotli";
+ header_list["cache-control"] = "foo";
+ header_list["foo"] = "bar";
+ header_list[":path"] = "/";
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+ }
+ {
+ spdy::SpdyHeaderBlock header_list;
+ header_list[":method"] = "CONNECT";
+ header_list["accept-encoding"] = "";
+ header_list["foo"] = "bar";
+ header_list[":path"] = "/";
+
+ spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list);
+ EXPECT_EQ(header_list, output);
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.cc
new file mode 100644
index 00000000000..f1986f798ad
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.cc
@@ -0,0 +1,140 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h"
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_logging.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_ptr_util.h"
+
+namespace quic {
+
+// The "constructor" for a QpackStaticEntry that computes the lengths at
+// compile time.
+#define STATIC_ENTRY(name, value) \
+ { name, QUIC_ARRAYSIZE(name) - 1, value, QUIC_ARRAYSIZE(value) - 1 }
+
+const std::vector<QpackStaticEntry>& QpackStaticTableVector() {
+ static const auto* kQpackStaticTable = new std::vector<QpackStaticEntry>{
+ STATIC_ENTRY(":authority", ""), // 0
+ STATIC_ENTRY(":path", "/"), // 1
+ STATIC_ENTRY("age", "0"), // 2
+ STATIC_ENTRY("content-disposition", ""), // 3
+ STATIC_ENTRY("content-length", "0"), // 4
+ STATIC_ENTRY("cookie", ""), // 5
+ STATIC_ENTRY("date", ""), // 6
+ STATIC_ENTRY("etag", ""), // 7
+ STATIC_ENTRY("if-modified-since", ""), // 8
+ STATIC_ENTRY("if-none-match", ""), // 9
+ STATIC_ENTRY("last-modified", ""), // 10
+ STATIC_ENTRY("link", ""), // 11
+ STATIC_ENTRY("location", ""), // 12
+ STATIC_ENTRY("referer", ""), // 13
+ STATIC_ENTRY("set-cookie", ""), // 14
+ STATIC_ENTRY(":method", "CONNECT"), // 15
+ STATIC_ENTRY(":method", "DELETE"), // 16
+ STATIC_ENTRY(":method", "GET"), // 17
+ STATIC_ENTRY(":method", "HEAD"), // 18
+ STATIC_ENTRY(":method", "OPTIONS"), // 19
+ STATIC_ENTRY(":method", "POST"), // 20
+ STATIC_ENTRY(":method", "PUT"), // 21
+ STATIC_ENTRY(":scheme", "http"), // 22
+ STATIC_ENTRY(":scheme", "https"), // 23
+ STATIC_ENTRY(":status", "103"), // 24
+ STATIC_ENTRY(":status", "200"), // 25
+ STATIC_ENTRY(":status", "304"), // 26
+ STATIC_ENTRY(":status", "404"), // 27
+ STATIC_ENTRY(":status", "503"), // 28
+ STATIC_ENTRY("accept", "*/*"), // 29
+ STATIC_ENTRY("accept", "application/dns-message"), // 30
+ STATIC_ENTRY("accept-encoding", "gzip, deflate, br"), // 31
+ STATIC_ENTRY("accept-ranges", "bytes"), // 32
+ STATIC_ENTRY("access-control-allow-headers", "cache-control"), // 33
+ STATIC_ENTRY("access-control-allow-headers", "content-type"), // 35
+ STATIC_ENTRY("access-control-allow-origin", "*"), // 35
+ STATIC_ENTRY("cache-control", "max-age=0"), // 36
+ STATIC_ENTRY("cache-control", "max-age=2592000"), // 37
+ STATIC_ENTRY("cache-control", "max-age=604800"), // 38
+ STATIC_ENTRY("cache-control", "no-cache"), // 39
+ STATIC_ENTRY("cache-control", "no-store"), // 40
+ STATIC_ENTRY("cache-control", "public, max-age=31536000"), // 41
+ STATIC_ENTRY("content-encoding", "br"), // 42
+ STATIC_ENTRY("content-encoding", "gzip"), // 43
+ STATIC_ENTRY("content-type", "application/dns-message"), // 44
+ STATIC_ENTRY("content-type", "application/javascript"), // 45
+ STATIC_ENTRY("content-type", "application/json"), // 46
+ STATIC_ENTRY("content-type", "application/x-www-form-urlencoded"), // 47
+ STATIC_ENTRY("content-type", "image/gif"), // 48
+ STATIC_ENTRY("content-type", "image/jpeg"), // 49
+ STATIC_ENTRY("content-type", "image/png"), // 50
+ STATIC_ENTRY("content-type", "text/css"), // 51
+ STATIC_ENTRY("content-type", "text/html; charset=utf-8"), // 52
+ STATIC_ENTRY("content-type", "text/plain"), // 53
+ STATIC_ENTRY("content-type", "text/plain;charset=utf-8"), // 54
+ STATIC_ENTRY("range", "bytes=0-"), // 55
+ STATIC_ENTRY("strict-transport-security", "max-age=31536000"), // 56
+ STATIC_ENTRY("strict-transport-security",
+ "max-age=31536000; includesubdomains"), // 57
+ STATIC_ENTRY("strict-transport-security",
+ "max-age=31536000; includesubdomains; preload"), // 58
+ STATIC_ENTRY("vary", "accept-encoding"), // 59
+ STATIC_ENTRY("vary", "origin"), // 60
+ STATIC_ENTRY("x-content-type-options", "nosniff"), // 61
+ STATIC_ENTRY("x-xss-protection", "1; mode=block"), // 62
+ STATIC_ENTRY(":status", "100"), // 63
+ STATIC_ENTRY(":status", "204"), // 64
+ STATIC_ENTRY(":status", "206"), // 65
+ STATIC_ENTRY(":status", "302"), // 66
+ STATIC_ENTRY(":status", "400"), // 67
+ STATIC_ENTRY(":status", "403"), // 68
+ STATIC_ENTRY(":status", "421"), // 69
+ STATIC_ENTRY(":status", "425"), // 70
+ STATIC_ENTRY(":status", "500"), // 71
+ STATIC_ENTRY("accept-language", ""), // 72
+ STATIC_ENTRY("access-control-allow-credentials", "FALSE"), // 73
+ STATIC_ENTRY("access-control-allow-credentials", "TRUE"), // 74
+ STATIC_ENTRY("access-control-allow-headers", "*"), // 75
+ STATIC_ENTRY("access-control-allow-methods", "get"), // 76
+ STATIC_ENTRY("access-control-allow-methods", "get, post, options"), // 77
+ STATIC_ENTRY("access-control-allow-methods", "options"), // 78
+ STATIC_ENTRY("access-control-expose-headers", "content-length"), // 79
+ STATIC_ENTRY("access-control-request-headers", "content-type"), // 80
+ STATIC_ENTRY("access-control-request-method", "get"), // 81
+ STATIC_ENTRY("access-control-request-method", "post"), // 82
+ STATIC_ENTRY("alt-svc", "clear"), // 83
+ STATIC_ENTRY("authorization", ""), // 84
+ STATIC_ENTRY(
+ "content-security-policy",
+ "script-src 'none'; object-src 'none'; base-uri 'none'"), // 85
+ STATIC_ENTRY("early-data", "1"), // 86
+ STATIC_ENTRY("expect-ct", ""), // 87
+ STATIC_ENTRY("forwarded", ""), // 88
+ STATIC_ENTRY("if-range", ""), // 89
+ STATIC_ENTRY("origin", ""), // 90
+ STATIC_ENTRY("purpose", "prefetch"), // 91
+ STATIC_ENTRY("server", ""), // 92
+ STATIC_ENTRY("timing-allow-origin", "*"), // 93
+ STATIC_ENTRY("upgrade-insecure-requests", "1"), // 94
+ STATIC_ENTRY("user-agent", ""), // 95
+ STATIC_ENTRY("x-forwarded-for", ""), // 96
+ STATIC_ENTRY("x-frame-options", "deny"), // 97
+ STATIC_ENTRY("x-frame-options", "sameorigin"), // 98
+ };
+ return *kQpackStaticTable;
+}
+
+#undef STATIC_ENTRY
+
+const QpackStaticTable& ObtainQpackStaticTable() {
+ static const QpackStaticTable* const shared_static_table = []() {
+ auto* table = new QpackStaticTable();
+ table->Initialize(QpackStaticTableVector().data(),
+ QpackStaticTableVector().size());
+ CHECK(table->IsInitialized());
+ return table;
+ }();
+ return *shared_static_table;
+}
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h
new file mode 100644
index 00000000000..d8c255568a4
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h
@@ -0,0 +1,31 @@
+// 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_QUIC_CORE_QPACK_QPACK_STATIC_TABLE_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_STATIC_TABLE_H_
+
+#include <vector>
+
+#include "net/third_party/quiche/src/quic/platform/api/quic_export.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_constants.h"
+#include "net/third_party/quiche/src/spdy/core/hpack/hpack_static_table.h"
+
+namespace quic {
+
+using QpackStaticEntry = spdy::HpackStaticEntry;
+using QpackStaticTable = spdy::HpackStaticTable;
+
+// QPACK static table defined at
+// https://quicwg.org/base-drafts/draft-ietf-quic-qpack.html#static-table.
+QUIC_EXPORT_PRIVATE const std::vector<QpackStaticEntry>&
+QpackStaticTableVector();
+
+// Returns a QpackStaticTable instance initialized with kQpackStaticTable.
+// The instance is read-only, has static lifetime, and is safe to share amoung
+// threads. This function is thread-safe.
+QUIC_EXPORT_PRIVATE const QpackStaticTable& ObtainQpackStaticTable();
+
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_STATIC_TABLE_H_
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc
new file mode 100644
index 00000000000..50289f2e298
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_static_table_test.cc
@@ -0,0 +1,53 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_static_table.h"
+
+#include <set>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_arraysize.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_string_piece.h"
+
+namespace quic {
+
+namespace test {
+
+namespace {
+
+// Check that an initialized instance has the right number of entries.
+TEST(QpackStaticTableTest, Initialize) {
+ QpackStaticTable table;
+ EXPECT_FALSE(table.IsInitialized());
+
+ table.Initialize(QpackStaticTableVector().data(),
+ QpackStaticTableVector().size());
+ EXPECT_TRUE(table.IsInitialized());
+
+ auto static_entries = table.GetStaticEntries();
+ EXPECT_EQ(QpackStaticTableVector().size(), static_entries.size());
+
+ auto static_index = table.GetStaticIndex();
+ EXPECT_EQ(QpackStaticTableVector().size(), static_index.size());
+
+ auto static_name_index = table.GetStaticNameIndex();
+ std::set<QuicStringPiece> names;
+ for (auto entry : static_index) {
+ names.insert(entry->name());
+ }
+ EXPECT_EQ(names.size(), static_name_index.size());
+}
+
+// Test that ObtainQpackStaticTable returns the same instance every time.
+TEST(QpackStaticTableTest, IsSingleton) {
+ const QpackStaticTable* static_table_one = &ObtainQpackStaticTable();
+ const QpackStaticTable* static_table_two = &ObtainQpackStaticTable();
+ EXPECT_EQ(static_table_one, static_table_two);
+}
+
+} // namespace
+
+} // namespace test
+
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.cc b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.cc
new file mode 100644
index 00000000000..2d4a72e71b0
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.cc
@@ -0,0 +1,23 @@
+// 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 "net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h"
+
+#include <limits>
+
+namespace quic {
+namespace test {
+
+FragmentSizeGenerator FragmentModeToFragmentSizeGenerator(
+ FragmentMode fragment_mode) {
+ switch (fragment_mode) {
+ case FragmentMode::kSingleChunk:
+ return []() { return std::numeric_limits<size_t>::max(); };
+ case FragmentMode::kOctetByOctet:
+ return []() { return 1; };
+ }
+}
+
+} // namespace test
+} // namespace quic
diff --git a/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.h
new file mode 100644
index 00000000000..65bb5a20ded
--- /dev/null
+++ b/chromium/net/third_party/quiche/src/quic/core/qpack/qpack_test_utils.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_QUIC_CORE_QPACK_QPACK_TEST_UTILS_H_
+#define QUICHE_QUIC_CORE_QPACK_QPACK_TEST_UTILS_H_
+
+#include <cstddef>
+#include <functional>
+
+namespace quic {
+namespace test {
+
+// Called repeatedly to determine the size of each fragment when encoding or
+// decoding. Must return a positive value.
+using FragmentSizeGenerator = std::function<size_t()>;
+
+enum class FragmentMode {
+ kSingleChunk,
+ kOctetByOctet,
+};
+
+FragmentSizeGenerator FragmentModeToFragmentSizeGenerator(
+ FragmentMode fragment_mode);
+
+} // namespace test
+} // namespace quic
+
+#endif // QUICHE_QUIC_CORE_QPACK_QPACK_TEST_UTILS_H_