Internal QUICHE change PiperOrigin-RevId: 245354565 Change-Id: Iab740490415f1f1729ed1568d2d6427890715526
diff --git a/quic/core/qpack/qpack_encoder.cc b/quic/core/qpack/qpack_encoder.cc index 108ffd5..09c4286 100644 --- a/quic/core/qpack/qpack_encoder.cc +++ b/quic/core/qpack/qpack_encoder.cc
@@ -9,7 +9,6 @@ #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 {
diff --git a/quic/core/qpack/qpack_encoder.h b/quic/core/qpack/qpack_encoder.h index 4e65532..2c450b1 100644 --- a/quic/core/qpack/qpack_encoder.h +++ b/quic/core/qpack/qpack_encoder.h
@@ -13,6 +13,7 @@ #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/quic/platform/api/quic_string_piece.h" #include "net/third_party/quiche/src/spdy/core/hpack/hpack_encoder.h" namespace spdy {
diff --git a/quic/core/qpack/qpack_encoder_test.cc b/quic/core/qpack/qpack_encoder_test.cc index fb07d49..9ea4556 100644 --- a/quic/core/qpack/qpack_encoder_test.cc +++ b/quic/core/qpack/qpack_encoder_test.cc
@@ -163,6 +163,19 @@ QuicTextUtils::HexDecode("ffffffffffffffffffffff")); } +TEST_P(QpackEncoderTest, SplitAlongNullCharacter) { + spdy::SpdyHeaderBlock header_list; + header_list["foo"] = QuicStringPiece("bar\0bar\0baz", 11); + std::string output = Encode(&header_list); + + EXPECT_EQ(QuicTextUtils::HexDecode("0000" // prefix + "2a94e703626172" // foo: bar + "2a94e703626172" // foo: bar + "2a94e70362617a" // foo: baz + ), + output); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/qpack/qpack_progressive_encoder.cc b/quic/core/qpack/qpack_progressive_encoder.cc index 05a025b..731ec10 100644 --- a/quic/core/qpack/qpack_progressive_encoder.cc +++ b/quic/core/qpack/qpack_progressive_encoder.cc
@@ -22,7 +22,7 @@ header_table_(header_table), encoder_stream_sender_(encoder_stream_sender), header_list_(header_list), - header_list_iterator_(header_list_->begin()), + header_list_iterator_(header_list_.begin()), prefix_encoded_(false) { // TODO(bnc): Use |stream_id_| for dynamic table entry management, and // remove this dummy DCHECK. @@ -30,11 +30,10 @@ DCHECK(header_table_); DCHECK(encoder_stream_sender_); - DCHECK(header_list_); } bool QpackProgressiveEncoder::HasNext() const { - return header_list_iterator_ != header_list_->end() || !prefix_encoded_; + return header_list_iterator_ != header_list_.end() || !prefix_encoded_; } void QpackProgressiveEncoder::Next(size_t max_encoded_bytes, @@ -61,7 +60,7 @@ } do { - // Call QpackInstructionEncoder::Encode for |*header_list_iterator_| if it + // Call QpackInstructionEncoder::Encode() for |*header_list_iterator_| if it // has not been called yet. if (!instruction_encoder_.HasNext()) { DCHECK(prefix_encoded_);
diff --git a/quic/core/qpack/qpack_progressive_encoder.h b/quic/core/qpack/qpack_progressive_encoder.h index 8e204e2..98a3cfe 100644 --- a/quic/core/qpack/qpack_progressive_encoder.h +++ b/quic/core/qpack/qpack_progressive_encoder.h
@@ -9,6 +9,7 @@ #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/qpack/value_splitting_header_list.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" @@ -24,6 +25,8 @@ : public spdy::HpackEncoder::ProgressiveEncoder { public: QpackProgressiveEncoder() = delete; + // |header_table|, |encoder_stream_sender|, and |header_list| must all outlive + // this object. QpackProgressiveEncoder(QuicStreamId stream_id, QpackHeaderTable* header_table, QpackEncoderStreamSender* encoder_stream_sender, @@ -43,10 +46,10 @@ QpackInstructionEncoder instruction_encoder_; const QpackHeaderTable* const header_table_; QpackEncoderStreamSender* const encoder_stream_sender_; - const spdy::SpdyHeaderBlock* const header_list_; + const ValueSplittingHeaderList header_list_; // Header field currently being encoded. - spdy::SpdyHeaderBlock::const_iterator header_list_iterator_; + ValueSplittingHeaderList::const_iterator header_list_iterator_; // False until prefix is fully encoded. bool prefix_encoded_;
diff --git a/quic/core/qpack/qpack_round_trip_test.cc b/quic/core/qpack/qpack_round_trip_test.cc index 9b2df55..419a2c3 100644 --- a/quic/core/qpack/qpack_round_trip_test.cc +++ b/quic/core/qpack/qpack_round_trip_test.cc
@@ -132,6 +132,14 @@ } } +TEST_P(QpackRoundTripTest, ValueHasNullCharacter) { + spdy::SpdyHeaderBlock header_list; + header_list["foo"] = QuicStringPiece("bar\0bar\0baz", 11); + + spdy::SpdyHeaderBlock output = EncodeThenDecode(header_list); + EXPECT_EQ(header_list, output); +} + } // namespace } // namespace test } // namespace quic
diff --git a/quic/core/qpack/value_splitting_header_list.cc b/quic/core/qpack/value_splitting_header_list.cc new file mode 100644 index 0000000..d8a0ee8 --- /dev/null +++ b/quic/core/qpack/value_splitting_header_list.cc
@@ -0,0 +1,88 @@ +// 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/value_splitting_header_list.h" + +namespace quic { + +ValueSplittingHeaderList::const_iterator::const_iterator( + const spdy::SpdyHeaderBlock* header_list, + spdy::SpdyHeaderBlock::const_iterator header_list_iterator) + : header_list_(header_list), + header_list_iterator_(header_list_iterator), + value_start_(0) { + UpdateHeaderField(); +} + +bool ValueSplittingHeaderList::const_iterator::operator==( + const const_iterator& other) const { + return header_list_iterator_ == other.header_list_iterator_ && + value_start_ == other.value_start_; +} + +bool ValueSplittingHeaderList::const_iterator::operator!=( + const const_iterator& other) const { + return !(*this == other); +} + +const ValueSplittingHeaderList::const_iterator& +ValueSplittingHeaderList::const_iterator::operator++() { + if (value_end_ == QuicStringPiece::npos) { + // This was the last frament within |*header_list_iterator_|, + // move on to the next header element of |header_list_|. + ++header_list_iterator_; + value_start_ = 0; + } else { + // Find the next fragment within |*header_list_iterator_|. + value_start_ = value_end_ + 1; + } + UpdateHeaderField(); + + return *this; +} + +const ValueSplittingHeaderList::value_type& + ValueSplittingHeaderList::const_iterator::operator*() const { + return header_field_; +} +const ValueSplittingHeaderList::value_type* + ValueSplittingHeaderList::const_iterator::operator->() const { + return &header_field_; +} + +void ValueSplittingHeaderList::const_iterator::UpdateHeaderField() { + DCHECK(value_start_ != QuicStringPiece::npos); + + if (header_list_iterator_ == header_list_->end()) { + return; + } + + const QuicStringPiece name = header_list_iterator_->first; + + value_end_ = header_list_iterator_->second.find('\0', value_start_); + const QuicStringPiece::size_type value_length = + value_end_ == QuicStringPiece::npos ? QuicStringPiece::npos + : value_end_ - value_start_; + const QuicStringPiece value = + header_list_iterator_->second.substr(value_start_, value_length); + + header_field_ = std::make_pair(name, value); +} + +ValueSplittingHeaderList::ValueSplittingHeaderList( + const spdy::SpdyHeaderBlock* header_list) + : header_list_(header_list) { + DCHECK(header_list_); +} + +ValueSplittingHeaderList::const_iterator ValueSplittingHeaderList::begin() + const { + return const_iterator(header_list_, header_list_->begin()); +} + +ValueSplittingHeaderList::const_iterator ValueSplittingHeaderList::end() const { + return const_iterator(header_list_, header_list_->end()); +} + +} // namespace quic
diff --git a/quic/core/qpack/value_splitting_header_list.h b/quic/core/qpack/value_splitting_header_list.h new file mode 100644 index 0000000..e47567e --- /dev/null +++ b/quic/core/qpack/value_splitting_header_list.h
@@ -0,0 +1,61 @@ +// 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_VALUE_SPLITTING_HEADER_LIST_H_ +#define QUICHE_QUIC_CORE_QPACK_VALUE_SPLITTING_HEADER_LIST_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" +#include "net/third_party/quiche/src/spdy/core/spdy_header_block.h" + +namespace quic { + +// A wrapper class around SpdyHeaderBlock that splits header values along '\0' +// characters. +class QUIC_EXPORT_PRIVATE ValueSplittingHeaderList { + public: + using value_type = spdy::SpdyHeaderBlock::value_type; + + class const_iterator { + public: + // |header_list| must outlive this object. + const_iterator(const spdy::SpdyHeaderBlock* header_list, + spdy::SpdyHeaderBlock::const_iterator header_list_iterator); + const_iterator(const const_iterator&) = default; + const_iterator& operator=(const const_iterator&) = default; + + bool operator==(const const_iterator& other) const; + bool operator!=(const const_iterator& other) const; + + const const_iterator& operator++(); + + const value_type& operator*() const; + const value_type* operator->() const; + + private: + // Find next '\0' character; update |value_end_| and |header_field_|. + void UpdateHeaderField(); + + const spdy::SpdyHeaderBlock* const header_list_; + spdy::SpdyHeaderBlock::const_iterator header_list_iterator_; + QuicStringPiece::size_type value_start_; + QuicStringPiece::size_type value_end_; + value_type header_field_; + }; + + // |header_list| must outlive this object. + explicit ValueSplittingHeaderList(const spdy::SpdyHeaderBlock* header_list); + ValueSplittingHeaderList(const ValueSplittingHeaderList&) = delete; + ValueSplittingHeaderList& operator=(const ValueSplittingHeaderList&) = delete; + + const_iterator begin() const; + const_iterator end() const; + + private: + const spdy::SpdyHeaderBlock* const header_list_; +}; + +} // namespace quic + +#endif // QUICHE_QUIC_CORE_QPACK_VALUE_SPLITTING_HEADER_LIST_H_
diff --git a/quic/core/qpack/value_splitting_header_list_test.cc b/quic/core/qpack/value_splitting_header_list_test.cc new file mode 100644 index 0000000..9ece65d --- /dev/null +++ b/quic/core/qpack/value_splitting_header_list_test.cc
@@ -0,0 +1,121 @@ +// 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/value_splitting_header_list.h" + +#include "net/third_party/quiche/src/quic/platform/api/quic_test.h" + +namespace quic { +namespace test { +namespace { + +using ::testing::ElementsAre; +using ::testing::Pair; + +TEST(ValueSplittingHeaderListTest, Comparison) { + spdy::SpdyHeaderBlock block; + block["foo"] = QuicStringPiece("bar\0baz", 7); + block["baz"] = "qux"; + + ValueSplittingHeaderList headers(&block); + ValueSplittingHeaderList::const_iterator it1 = headers.begin(); + const int kEnd = 4; + for (int i = 0; i < kEnd; ++i) { + // Compare to begin(). + if (i == 0) { + EXPECT_TRUE(it1 == headers.begin()); + EXPECT_TRUE(headers.begin() == it1); + EXPECT_FALSE(it1 != headers.begin()); + EXPECT_FALSE(headers.begin() != it1); + } else { + EXPECT_FALSE(it1 == headers.begin()); + EXPECT_FALSE(headers.begin() == it1); + EXPECT_TRUE(it1 != headers.begin()); + EXPECT_TRUE(headers.begin() != it1); + } + + // Compare to end(). + if (i == kEnd - 1) { + EXPECT_TRUE(it1 == headers.end()); + EXPECT_TRUE(headers.end() == it1); + EXPECT_FALSE(it1 != headers.end()); + EXPECT_FALSE(headers.end() != it1); + } else { + EXPECT_FALSE(it1 == headers.end()); + EXPECT_FALSE(headers.end() == it1); + EXPECT_TRUE(it1 != headers.end()); + EXPECT_TRUE(headers.end() != it1); + } + + // Compare to another iterator walking through the container. + ValueSplittingHeaderList::const_iterator it2 = headers.begin(); + for (int j = 0; j < kEnd; ++j) { + if (i == j) { + EXPECT_TRUE(it1 == it2); + EXPECT_FALSE(it1 != it2); + } else { + EXPECT_FALSE(it1 == it2); + EXPECT_TRUE(it1 != it2); + } + ++it2; + } + + ++it1; + } +} + +TEST(ValueSplittingHeaderListTest, Empty) { + spdy::SpdyHeaderBlock block; + + ValueSplittingHeaderList headers(&block); + EXPECT_THAT(headers, ElementsAre()); + EXPECT_EQ(headers.begin(), headers.end()); +} + +TEST(ValueSplittingHeaderListTest, Simple) { + spdy::SpdyHeaderBlock block; + block["foo"] = "bar"; + block["baz"] = "qux"; + + ValueSplittingHeaderList headers(&block); + EXPECT_THAT(headers, ElementsAre(Pair("foo", "bar"), Pair("baz", "qux"))); + EXPECT_NE(headers.begin(), headers.end()); +} + +TEST(ValueSplittingHeaderListTest, EmptyValue) { + spdy::SpdyHeaderBlock block; + block["foo"] = ""; + + ValueSplittingHeaderList headers(&block); + EXPECT_THAT(headers, ElementsAre(Pair("foo", ""))); +} + +TEST(ValueSplittingHeaderListTest, SimpleSplit) { + spdy::SpdyHeaderBlock block; + block["foo"] = QuicStringPiece("bar\0baz", 7); + block["baz"] = QuicStringPiece("foo\0foo", 7); + + ValueSplittingHeaderList headers(&block); + EXPECT_THAT(headers, ElementsAre(Pair("foo", "bar"), Pair("foo", "baz"), + Pair("baz", "foo"), Pair("baz", "foo"))); +} + +TEST(ValueSplittingHeaderListTest, EmptyFragments) { + spdy::SpdyHeaderBlock block; + block["foo"] = QuicStringPiece("\0", 1); + block["bar"] = QuicStringPiece("foo\0", 4); + block["baz"] = QuicStringPiece("\0bar", 4); + block["qux"] = QuicStringPiece("\0foobar\0", 8); + + ValueSplittingHeaderList headers(&block); + EXPECT_THAT( + headers, + ElementsAre(Pair("foo", ""), Pair("foo", ""), Pair("bar", "foo"), + Pair("bar", ""), Pair("baz", ""), Pair("baz", "bar"), + Pair("qux", ""), Pair("qux", "foobar"), Pair("qux", ""))); +} + +} // namespace +} // namespace test +} // namespace quic