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