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