| // 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 "quic/core/qpack/qpack_instruction_encoder.h" |
| |
| #include "absl/strings/escaping.h" |
| #include "absl/strings/string_view.h" |
| #include "quic/platform/api/quic_logging.h" |
| #include "quic/platform/api/quic_test.h" |
| #include "common/platform/api/quiche_text_utils.h" |
| |
| namespace quic { |
| namespace test { |
| |
| class QpackInstructionWithValuesPeer { |
| public: |
| static QpackInstructionWithValues CreateQpackInstructionWithValues( |
| const QpackInstruction* instruction) { |
| QpackInstructionWithValues instruction_with_values; |
| instruction_with_values.instruction_ = instruction; |
| return instruction_with_values; |
| } |
| |
| static void set_s_bit(QpackInstructionWithValues* instruction_with_values, |
| bool s_bit) { |
| instruction_with_values->s_bit_ = s_bit; |
| } |
| |
| static void set_varint(QpackInstructionWithValues* instruction_with_values, |
| uint64_t varint) { |
| instruction_with_values->varint_ = varint; |
| } |
| |
| static void set_varint2(QpackInstructionWithValues* instruction_with_values, |
| uint64_t varint2) { |
| instruction_with_values->varint2_ = varint2; |
| } |
| |
| static void set_name(QpackInstructionWithValues* instruction_with_values, |
| absl::string_view name) { |
| instruction_with_values->name_ = name; |
| } |
| |
| static void set_value(QpackInstructionWithValues* instruction_with_values, |
| absl::string_view value) { |
| instruction_with_values->value_ = value; |
| } |
| }; |
| |
| namespace { |
| |
| class QpackInstructionEncoderTest : public QuicTest { |
| protected: |
| QpackInstructionEncoderTest() : verified_position_(0) {} |
| ~QpackInstructionEncoderTest() override = default; |
| |
| // Append encoded |instruction| to |output_|. |
| void EncodeInstruction( |
| const QpackInstructionWithValues& instruction_with_values) { |
| encoder_.Encode(instruction_with_values, &output_); |
| } |
| |
| // Compare substring appended to |output_| since last EncodedSegmentMatches() |
| // call against hex-encoded argument. |
| bool EncodedSegmentMatches(absl::string_view hex_encoded_expected_substring) { |
| auto recently_encoded = |
| absl::string_view(output_).substr(verified_position_); |
| auto expected = absl::HexStringToBytes(hex_encoded_expected_substring); |
| verified_position_ = output_.size(); |
| return recently_encoded == expected; |
| } |
| |
| private: |
| QpackInstructionEncoder encoder_; |
| std::string output_; |
| std::string::size_type verified_position_; |
| }; |
| |
| TEST_F(QpackInstructionEncoderTest, Varint) { |
| const QpackInstruction instruction{QpackInstructionOpcode{0x00, 0x80}, |
| {{QpackInstructionFieldType::kVarint, 7}}}; |
| |
| auto instruction_with_values = |
| QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( |
| &instruction); |
| QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 5); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("05")); |
| |
| QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 127); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("7f00")); |
| } |
| |
| TEST_F(QpackInstructionEncoderTest, SBitAndTwoVarint2) { |
| const QpackInstruction instruction{ |
| QpackInstructionOpcode{0x80, 0xc0}, |
| {{QpackInstructionFieldType::kSbit, 0x20}, |
| {QpackInstructionFieldType::kVarint, 5}, |
| {QpackInstructionFieldType::kVarint2, 8}}}; |
| |
| auto instruction_with_values = |
| QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( |
| &instruction); |
| QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true); |
| QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 5); |
| QpackInstructionWithValuesPeer::set_varint2(&instruction_with_values, 200); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("a5c8")); |
| |
| QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false); |
| QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 31); |
| QpackInstructionWithValuesPeer::set_varint2(&instruction_with_values, 356); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("9f00ff65")); |
| } |
| |
| TEST_F(QpackInstructionEncoderTest, SBitAndVarintAndValue) { |
| const QpackInstruction instruction{QpackInstructionOpcode{0xc0, 0xc0}, |
| {{QpackInstructionFieldType::kSbit, 0x20}, |
| {QpackInstructionFieldType::kVarint, 5}, |
| {QpackInstructionFieldType::kValue, 7}}}; |
| |
| auto instruction_with_values = |
| QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( |
| &instruction); |
| QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true); |
| QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 100); |
| QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "foo"); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("ff458294e7")); |
| |
| QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false); |
| QpackInstructionWithValuesPeer::set_varint(&instruction_with_values, 3); |
| QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar"); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("c303626172")); |
| } |
| |
| TEST_F(QpackInstructionEncoderTest, Name) { |
| const QpackInstruction instruction{QpackInstructionOpcode{0xe0, 0xe0}, |
| {{QpackInstructionFieldType::kName, 4}}}; |
| |
| auto instruction_with_values = |
| QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( |
| &instruction); |
| QpackInstructionWithValuesPeer::set_name(&instruction_with_values, ""); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("e0")); |
| |
| QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "foo"); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("f294e7")); |
| |
| QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "bar"); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("e3626172")); |
| } |
| |
| TEST_F(QpackInstructionEncoderTest, Value) { |
| const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0}, |
| {{QpackInstructionFieldType::kValue, 3}}}; |
| |
| auto instruction_with_values = |
| QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( |
| &instruction); |
| QpackInstructionWithValuesPeer::set_value(&instruction_with_values, ""); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("f0")); |
| |
| QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "foo"); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("fa94e7")); |
| |
| QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar"); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("f3626172")); |
| } |
| |
| TEST_F(QpackInstructionEncoderTest, SBitAndNameAndValue) { |
| const QpackInstruction instruction{QpackInstructionOpcode{0xf0, 0xf0}, |
| {{QpackInstructionFieldType::kSbit, 0x08}, |
| {QpackInstructionFieldType::kName, 2}, |
| {QpackInstructionFieldType::kValue, 7}}}; |
| |
| auto instruction_with_values = |
| QpackInstructionWithValuesPeer::CreateQpackInstructionWithValues( |
| &instruction); |
| QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, false); |
| QpackInstructionWithValuesPeer::set_name(&instruction_with_values, ""); |
| QpackInstructionWithValuesPeer::set_value(&instruction_with_values, ""); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("f000")); |
| |
| QpackInstructionWithValuesPeer::set_s_bit(&instruction_with_values, true); |
| QpackInstructionWithValuesPeer::set_name(&instruction_with_values, "foo"); |
| QpackInstructionWithValuesPeer::set_value(&instruction_with_values, "bar"); |
| EncodeInstruction(instruction_with_values); |
| EXPECT_TRUE(EncodedSegmentMatches("fe94e703626172")); |
| } |
| |
| } // namespace |
| } // namespace test |
| } // namespace quic |