Project import generated by Copybara.
PiperOrigin-RevId: 224614037
Change-Id: I14e53449d4aeccb328f86828c76b5f09dea0d4b8
diff --git a/http2/hpack/tools/hpack_block_builder.cc b/http2/hpack/tools/hpack_block_builder.cc
new file mode 100644
index 0000000..7a7d348
--- /dev/null
+++ b/http2/hpack/tools/hpack_block_builder.cc
@@ -0,0 +1,80 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/http2/hpack/varint/hpack_varint_encoder.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+
+namespace http2 {
+namespace test {
+
+void HpackBlockBuilder::AppendHighBitsAndVarint(uint8_t high_bits,
+ uint8_t prefix_length,
+ uint64_t varint) {
+ EXPECT_LE(3, prefix_length);
+ EXPECT_LE(prefix_length, 7);
+
+ HpackVarintEncoder varint_encoder;
+
+ unsigned char c =
+ varint_encoder.StartEncoding(high_bits, prefix_length, varint);
+ buffer_.push_back(c);
+
+ if (!varint_encoder.IsEncodingInProgress()) {
+ return;
+ }
+
+ // After the prefix, at most 63 bits can remain to be encoded.
+ // Each octet holds 7 bits, so at most 9 octets are necessary.
+ // TODO(bnc): Move this into a constant in HpackVarintEncoder.
+ varint_encoder.ResumeEncoding(/* max_encoded_bytes = */ 10, &buffer_);
+ DCHECK(!varint_encoder.IsEncodingInProgress());
+}
+
+void HpackBlockBuilder::AppendEntryTypeAndVarint(HpackEntryType entry_type,
+ uint64_t varint) {
+ uint8_t high_bits;
+ uint8_t prefix_length; // Bits of the varint prefix in the first byte.
+ switch (entry_type) {
+ case HpackEntryType::kIndexedHeader:
+ high_bits = 0x80;
+ prefix_length = 7;
+ break;
+ case HpackEntryType::kDynamicTableSizeUpdate:
+ high_bits = 0x20;
+ prefix_length = 5;
+ break;
+ case HpackEntryType::kIndexedLiteralHeader:
+ high_bits = 0x40;
+ prefix_length = 6;
+ break;
+ case HpackEntryType::kUnindexedLiteralHeader:
+ high_bits = 0x00;
+ prefix_length = 4;
+ break;
+ case HpackEntryType::kNeverIndexedLiteralHeader:
+ high_bits = 0x10;
+ prefix_length = 4;
+ break;
+ default:
+ HTTP2_BUG << "Unreached, entry_type=" << entry_type;
+ high_bits = 0;
+ prefix_length = 0;
+ break;
+ }
+ AppendHighBitsAndVarint(high_bits, prefix_length, varint);
+}
+
+void HpackBlockBuilder::AppendString(bool is_huffman_encoded,
+ Http2StringPiece str) {
+ uint8_t high_bits = is_huffman_encoded ? 0x80 : 0;
+ uint8_t prefix_length = 7;
+ AppendHighBitsAndVarint(high_bits, prefix_length, str.size());
+ buffer_.append(str.data(), str.size());
+}
+
+} // namespace test
+} // namespace http2
diff --git a/http2/hpack/tools/hpack_block_builder.h b/http2/hpack/tools/hpack_block_builder.h
new file mode 100644
index 0000000..560953c
--- /dev/null
+++ b/http2/hpack/tools/hpack_block_builder.h
@@ -0,0 +1,96 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_HTTP2_HPACK_TOOLS_HPACK_BLOCK_BUILDER_H_
+#define QUICHE_HTTP2_HPACK_TOOLS_HPACK_BLOCK_BUILDER_H_
+
+// HpackBlockBuilder builds wire-format HPACK blocks (or fragments thereof)
+// from components.
+
+// Supports very large varints to enable tests to create HPACK blocks with
+// values that the decoder should reject. For now, this is only intended for
+// use in tests, and thus has EXPECT* in the code. If desired to use it in an
+// encoder, it will need optimization work, especially w.r.t memory mgmt, and
+// the EXPECT* will need to be removed or replaced with DCHECKs. And of course
+// the support for very large varints will not be needed in production code.
+
+#include <stddef.h>
+
+#include <cstdint>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/http2/hpack/http2_hpack_constants.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
+
+namespace http2 {
+namespace test {
+
+class HpackBlockBuilder {
+ public:
+ explicit HpackBlockBuilder(Http2StringPiece initial_contents)
+ : buffer_(initial_contents.data(), initial_contents.size()) {}
+ HpackBlockBuilder() {}
+ ~HpackBlockBuilder() {}
+
+ size_t size() const { return buffer_.size(); }
+ const Http2String& buffer() const { return buffer_; }
+
+ //----------------------------------------------------------------------------
+ // Methods for appending a valid HPACK entry.
+
+ void AppendIndexedHeader(uint64_t index) {
+ AppendEntryTypeAndVarint(HpackEntryType::kIndexedHeader, index);
+ }
+
+ void AppendDynamicTableSizeUpdate(uint64_t size) {
+ AppendEntryTypeAndVarint(HpackEntryType::kDynamicTableSizeUpdate, size);
+ }
+
+ void AppendNameIndexAndLiteralValue(HpackEntryType entry_type,
+ uint64_t name_index,
+ bool value_is_huffman_encoded,
+ Http2StringPiece value) {
+ // name_index==0 would indicate that the entry includes a literal name.
+ // Call AppendLiteralNameAndValue in that case.
+ EXPECT_NE(0u, name_index);
+ AppendEntryTypeAndVarint(entry_type, name_index);
+ AppendString(value_is_huffman_encoded, value);
+ }
+
+ void AppendLiteralNameAndValue(HpackEntryType entry_type,
+ bool name_is_huffman_encoded,
+ Http2StringPiece name,
+ bool value_is_huffman_encoded,
+ Http2StringPiece value) {
+ AppendEntryTypeAndVarint(entry_type, 0);
+ AppendString(name_is_huffman_encoded, name);
+ AppendString(value_is_huffman_encoded, value);
+ }
+
+ //----------------------------------------------------------------------------
+ // Primitive methods that are not guaranteed to write a valid HPACK entry.
+
+ // Appends a varint, with the specified high_bits above the prefix of the
+ // varint.
+ void AppendHighBitsAndVarint(uint8_t high_bits,
+ uint8_t prefix_length,
+ uint64_t varint);
+
+ // Append the start of an HPACK entry for the specified type, with the
+ // specified varint.
+ void AppendEntryTypeAndVarint(HpackEntryType entry_type, uint64_t varint);
+
+ // Append a header string (i.e. a header name or value) in HPACK format.
+ // Does NOT perform Huffman encoding.
+ void AppendString(bool is_huffman_encoded, Http2StringPiece str);
+
+ private:
+ Http2String buffer_;
+};
+
+} // namespace test
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_TOOLS_HPACK_BLOCK_BUILDER_H_
diff --git a/http2/hpack/tools/hpack_block_builder_test.cc b/http2/hpack/tools/hpack_block_builder_test.cc
new file mode 100644
index 0000000..a7b8064
--- /dev/null
+++ b/http2/hpack/tools/hpack_block_builder_test.cc
@@ -0,0 +1,169 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/http2/hpack/tools/hpack_block_builder.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
+
+namespace http2 {
+namespace test {
+namespace {
+const bool kUncompressed = false;
+const bool kCompressed = true;
+
+// TODO(jamessynge): Once static table code is checked in, switch to using
+// constants from there.
+const uint32_t kStaticTableMethodGET = 2;
+const uint32_t kStaticTablePathSlash = 4;
+const uint32_t kStaticTableSchemeHttp = 6;
+
+// Tests of encoding per the RFC. See:
+// http://httpwg.org/specs/rfc7541.html#header.field.representation.examples
+// The expected values have been copied from the RFC.
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC2) {
+ {
+ HpackBlockBuilder b;
+ b.AppendLiteralNameAndValue(HpackEntryType::kIndexedLiteralHeader,
+ kUncompressed, "custom-key", kUncompressed,
+ "custom-header");
+ EXPECT_EQ(26u, b.size());
+
+ const char kExpected[] =
+ "\x40" // == Literal indexed ==
+ "\x0a" // Name length (10)
+ "custom-key" // Name
+ "\x0d" // Value length (13)
+ "custom-header"; // Value
+ EXPECT_EQ(kExpected, b.buffer());
+ }
+ {
+ HpackBlockBuilder b;
+ b.AppendNameIndexAndLiteralValue(HpackEntryType::kUnindexedLiteralHeader, 4,
+ kUncompressed, "/sample/path");
+ EXPECT_EQ(14u, b.size());
+
+ const char kExpected[] =
+ "\x04" // == Literal unindexed, name index 0x04 ==
+ "\x0c" // Value length (12)
+ "/sample/path"; // Value
+ EXPECT_EQ(kExpected, b.buffer());
+ }
+ {
+ HpackBlockBuilder b;
+ b.AppendLiteralNameAndValue(HpackEntryType::kNeverIndexedLiteralHeader,
+ kUncompressed, "password", kUncompressed,
+ "secret");
+ EXPECT_EQ(17u, b.size());
+
+ const char kExpected[] =
+ "\x10" // == Literal never indexed ==
+ "\x08" // Name length (8)
+ "password" // Name
+ "\x06" // Value length (6)
+ "secret"; // Value
+ EXPECT_EQ(kExpected, b.buffer());
+ }
+ {
+ HpackBlockBuilder b;
+ b.AppendIndexedHeader(2);
+ EXPECT_EQ(1u, b.size());
+
+ const char kExpected[] = "\x82"; // == Indexed (2) ==
+ EXPECT_EQ(kExpected, b.buffer());
+ }
+}
+
+// Tests of encoding per the RFC. See:
+// http://httpwg.org/specs/rfc7541.html#request.examples.without.huffman.coding
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC3) {
+ {
+ // Header block to encode:
+ // :method: GET
+ // :scheme: http
+ // :path: /
+ // :authority: www.example.com
+ HpackBlockBuilder b;
+ b.AppendIndexedHeader(2); // :method: GET
+ b.AppendIndexedHeader(6); // :scheme: http
+ b.AppendIndexedHeader(4); // :path: /
+ b.AppendNameIndexAndLiteralValue(HpackEntryType::kIndexedLiteralHeader, 1,
+ kUncompressed, "www.example.com");
+ EXPECT_EQ(20u, b.size());
+
+ // Hex dump of encoded data (copied from RFC):
+ // 0x0000: 8286 8441 0f77 7777 2e65 7861 6d70 6c65 ...A.www.example
+ // 0x0010: 2e63 6f6d .com
+
+ const Http2String expected =
+ Http2HexDecode("828684410f7777772e6578616d706c652e636f6d");
+ EXPECT_EQ(expected, b.buffer());
+ }
+}
+
+// Tests of encoding per the RFC. See:
+// http://httpwg.org/specs/rfc7541.html#request.examples.with.huffman.coding
+TEST(HpackBlockBuilderTest, ExamplesFromSpecC4) {
+ {
+ // Header block to encode:
+ // :method: GET
+ // :scheme: http
+ // :path: /
+ // :authority: www.example.com (Huffman encoded)
+ HpackBlockBuilder b;
+ b.AppendIndexedHeader(kStaticTableMethodGET);
+ b.AppendIndexedHeader(kStaticTableSchemeHttp);
+ b.AppendIndexedHeader(kStaticTablePathSlash);
+ const char kHuffmanWwwExampleCom[] = {'\xf1', '\xe3', '\xc2', '\xe5',
+ '\xf2', '\x3a', '\x6b', '\xa0',
+ '\xab', '\x90', '\xf4', '\xff'};
+ b.AppendNameIndexAndLiteralValue(
+ HpackEntryType::kIndexedLiteralHeader, 1, kCompressed,
+ Http2StringPiece(kHuffmanWwwExampleCom, sizeof kHuffmanWwwExampleCom));
+ EXPECT_EQ(17u, b.size());
+
+ // Hex dump of encoded data (copied from RFC):
+ // 0x0000: 8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ...A......:k....
+ // 0x0010: ff .
+
+ const Http2String expected =
+ Http2HexDecode("828684418cf1e3c2e5f23a6ba0ab90f4ff");
+ EXPECT_EQ(expected, b.buffer());
+ }
+}
+
+TEST(HpackBlockBuilderTest, DynamicTableSizeUpdate) {
+ {
+ HpackBlockBuilder b;
+ b.AppendDynamicTableSizeUpdate(0);
+ EXPECT_EQ(1u, b.size());
+
+ const char kData[] = {'\x20'};
+ Http2StringPiece expected(kData, sizeof kData);
+ EXPECT_EQ(expected, b.buffer());
+ }
+ {
+ HpackBlockBuilder b;
+ b.AppendDynamicTableSizeUpdate(4096); // The default size.
+ EXPECT_EQ(3u, b.size());
+
+ const char kData[] = {'\x3f', '\xe1', '\x1f'};
+ Http2StringPiece expected(kData, sizeof kData);
+ EXPECT_EQ(expected, b.buffer());
+ }
+ {
+ HpackBlockBuilder b;
+ b.AppendDynamicTableSizeUpdate(1000000000000); // A very large value.
+ EXPECT_EQ(7u, b.size());
+
+ const char kData[] = {'\x3f', '\xe1', '\x9f', '\x94',
+ '\xa5', '\x8d', '\x1d'};
+ Http2StringPiece expected(kData, sizeof kData);
+ EXPECT_EQ(expected, b.buffer());
+ }
+}
+
+} // namespace
+} // namespace test
+} // namespace http2
diff --git a/http2/hpack/tools/hpack_example.cc b/http2/hpack/tools/hpack_example.cc
new file mode 100644
index 0000000..d20eb35
--- /dev/null
+++ b/http2/hpack/tools/hpack_example.cc
@@ -0,0 +1,58 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "net/third_party/quiche/src/http2/hpack/tools/hpack_example.h"
+
+#include <ctype.h>
+
+#include "base/logging.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_bug_tracker.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_string_utils.h"
+
+namespace http2 {
+namespace test {
+namespace {
+
+void HpackExampleToStringOrDie(Http2StringPiece example, Http2String* output) {
+ while (!example.empty()) {
+ const char c0 = example[0];
+ if (isxdigit(c0)) {
+ CHECK_GT(example.size(), 1u) << "Truncated hex byte?";
+ const char c1 = example[1];
+ CHECK(isxdigit(c1)) << "Found half a byte?";
+ *output += Http2HexDecode(example.substr(0, 2));
+ example.remove_prefix(2);
+ continue;
+ }
+ if (isspace(c0)) {
+ example.remove_prefix(1);
+ continue;
+ }
+ if (!example.empty() && example[0] == '|') {
+ // Start of a comment. Skip to end of line or of input.
+ auto pos = example.find('\n');
+ if (pos == Http2StringPiece::npos) {
+ // End of input.
+ break;
+ }
+ example.remove_prefix(pos + 1);
+ continue;
+ }
+ HTTP2_BUG << "Can't parse byte " << static_cast<int>(c0)
+ << Http2StrCat(" (0x", Http2Hex(c0), ")")
+ << "\nExample: " << example;
+ }
+ CHECK_LT(0u, output->size()) << "Example is empty.";
+}
+
+} // namespace
+
+Http2String HpackExampleToStringOrDie(Http2StringPiece example) {
+ Http2String output;
+ HpackExampleToStringOrDie(example, &output);
+ return output;
+}
+
+} // namespace test
+} // namespace http2
diff --git a/http2/hpack/tools/hpack_example.h b/http2/hpack/tools/hpack_example.h
new file mode 100644
index 0000000..ddb22a3
--- /dev/null
+++ b/http2/hpack/tools/hpack_example.h
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUICHE_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_
+#define QUICHE_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_
+
+#include "net/third_party/quiche/src/http2/platform/api/http2_string.h"
+#include "net/third_party/quiche/src/http2/platform/api/http2_string_piece.h"
+
+// Parses HPACK examples in the format seen in the HPACK specification,
+// RFC 7541. For example:
+//
+// 10 | == Literal never indexed ==
+// 08 | Literal name (len = 8)
+// 7061 7373 776f 7264 | password
+// 06 | Literal value (len = 6)
+// 7365 6372 6574 | secret
+// | -> password: secret
+//
+// (excluding the leading "//").
+
+namespace http2 {
+namespace test {
+
+Http2String HpackExampleToStringOrDie(Http2StringPiece example);
+
+} // namespace test
+} // namespace http2
+
+#endif // QUICHE_HTTP2_HPACK_TOOLS_HPACK_EXAMPLE_H_