Add ACCEPT_CH HTTP/3 frame type struct; add HttpFramesTest.

PiperOrigin-RevId: 345227858
Change-Id: I7368e52b7ba70c59d40c79f62f9d5ac1465a405f
diff --git a/quic/core/http/http_frames.h b/quic/core/http/http_frames.h
index ed7ad2c..4959879 100644
--- a/quic/core/http/http_frames.h
+++ b/quic/core/http/http_frames.h
@@ -5,9 +5,11 @@
 #ifndef QUICHE_QUIC_CORE_HTTP_HTTP_FRAMES_H_
 #define QUICHE_QUIC_CORE_HTTP_HTTP_FRAMES_H_
 
+#include <algorithm>
 #include <cstdint>
 #include <map>
 #include <ostream>
+#include <sstream>
 
 #include "absl/strings/string_view.h"
 #include "net/third_party/quiche/src/quic/core/http/spdy_utils.h"
@@ -28,6 +30,8 @@
   MAX_PUSH_ID = 0xD,
   // https://tools.ietf.org/html/draft-ietf-httpbis-priority-01
   PRIORITY_UPDATE = 0XF,
+  // https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02
+  ACCEPT_CH = 0x89,
   // https://tools.ietf.org/html/draft-ietf-httpbis-priority-02
   PRIORITY_UPDATE_REQUEST_STREAM = 0xF0700,
 };
@@ -176,6 +180,42 @@
   }
 };
 
+// ACCEPT_CH
+// https://tools.ietf.org/html/draft-davidben-http-client-hint-reliability-02
+//
+struct QUIC_EXPORT_PRIVATE AcceptChFrame {
+  struct QUIC_EXPORT_PRIVATE OriginValuePair {
+    std::string origin;
+    std::string value;
+    bool operator==(const OriginValuePair& rhs) const {
+      return origin == rhs.origin && value == rhs.value;
+    }
+  };
+
+  std::vector<OriginValuePair> entries;
+
+  bool operator==(const AcceptChFrame& rhs) const {
+    return entries.size() == rhs.entries.size() &&
+           std::equal(entries.begin(), entries.end(), rhs.entries.begin());
+  }
+
+  std::string ToString() const {
+    std::stringstream s;
+    s << *this;
+    return s.str();
+  }
+
+  friend QUIC_EXPORT_PRIVATE std::ostream& operator<<(
+      std::ostream& os,
+      const AcceptChFrame& frame) {
+    os << "ACCEPT_CH frame with " << frame.entries.size() << " entries: ";
+    for (auto& entry : frame.entries) {
+      os << "origin: " << entry.origin << "; value: " << entry.value;
+    }
+    return os;
+  }
+};
+
 }  // namespace quic
 
 #endif  // QUICHE_QUIC_CORE_HTTP_HTTP_FRAMES_H_
diff --git a/quic/core/http/http_frames_test.cc b/quic/core/http/http_frames_test.cc
new file mode 100644
index 0000000..d2027c6
--- /dev/null
+++ b/quic/core/http/http_frames_test.cc
@@ -0,0 +1,131 @@
+// Copyright (c) 2020 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/http/http_frames.h"
+
+#include <sstream>
+
+#include "net/third_party/quiche/src/quic/core/http/http_constants.h"
+#include "net/third_party/quiche/src/quic/platform/api/quic_test.h"
+
+namespace quic {
+namespace test {
+
+TEST(HttpFramesTest, CancelPushFrame) {
+  CancelPushFrame a{1};
+  EXPECT_TRUE(a == a);
+
+  CancelPushFrame b{1};
+  EXPECT_TRUE(a == b);
+
+  b.push_id = 2;
+  EXPECT_FALSE(a == b);
+}
+
+TEST(HttpFramesTest, SettingsFrame) {
+  SettingsFrame a;
+  EXPECT_TRUE(a == a);
+  EXPECT_EQ("", a.ToString());
+
+  SettingsFrame b;
+  b.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 1;
+  EXPECT_FALSE(a == b);
+  EXPECT_TRUE(b == b);
+
+  a.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 2;
+  EXPECT_FALSE(a == b);
+  a.values[SETTINGS_QPACK_MAX_TABLE_CAPACITY] = 1;
+  EXPECT_TRUE(a == b);
+
+  EXPECT_EQ("SETTINGS_QPACK_MAX_TABLE_CAPACITY = 1; ", b.ToString());
+  std::stringstream s;
+  s << b;
+  EXPECT_EQ("SETTINGS_QPACK_MAX_TABLE_CAPACITY = 1; ", s.str());
+}
+
+TEST(HttpFramesTest, PushPromiseFrame) {
+  PushPromiseFrame a{1, ""};
+  EXPECT_TRUE(a == a);
+
+  PushPromiseFrame b{2, ""};
+  EXPECT_FALSE(a == b);
+
+  b.push_id = 1;
+  EXPECT_TRUE(a == b);
+
+  b.headers = "foo";
+  EXPECT_FALSE(a == b);
+  EXPECT_TRUE(b == b);
+}
+
+TEST(HttpFramesTest, GoAwayFrame) {
+  GoAwayFrame a{1};
+  EXPECT_TRUE(a == a);
+
+  GoAwayFrame b{2};
+  EXPECT_FALSE(a == b);
+
+  b.id = 1;
+  EXPECT_TRUE(a == b);
+}
+
+TEST(HttpFramesTest, MaxPushIdFrame) {
+  MaxPushIdFrame a{1};
+  EXPECT_TRUE(a == a);
+
+  MaxPushIdFrame b{2};
+  EXPECT_FALSE(a == b);
+
+  b.push_id = 1;
+  EXPECT_TRUE(a == b);
+}
+
+TEST(HttpFramesTest, PriorityUpdateFrame) {
+  PriorityUpdateFrame a{REQUEST_STREAM, 0, ""};
+  EXPECT_TRUE(a == a);
+  PriorityUpdateFrame b{REQUEST_STREAM, 4, ""};
+  EXPECT_FALSE(a == b);
+  a.prioritized_element_id = 4;
+  EXPECT_TRUE(a == b);
+
+  a.prioritized_element_type = PUSH_STREAM;
+  EXPECT_FALSE(a == b);
+  b.prioritized_element_type = PUSH_STREAM;
+  EXPECT_TRUE(a == b);
+
+  a.priority_field_value = "foo";
+  EXPECT_FALSE(a == b);
+
+  EXPECT_EQ(
+      "Priority Frame : {prioritized_element_type: 128, "
+      "prioritized_element_id: 4, priority_field_value: foo}",
+      a.ToString());
+  std::stringstream s;
+  s << a;
+  EXPECT_EQ(
+      "Priority Frame : {prioritized_element_type: 128, "
+      "prioritized_element_id: 4, priority_field_value: foo}",
+      s.str());
+}
+
+TEST(HttpFramesTest, AcceptChFrame) {
+  AcceptChFrame a;
+  EXPECT_TRUE(a == a);
+  EXPECT_EQ("ACCEPT_CH frame with 0 entries: ", a.ToString());
+
+  AcceptChFrame b{{{"foo", "bar"}}};
+  EXPECT_FALSE(a == b);
+
+  a.entries.push_back({"foo", "bar"});
+  EXPECT_TRUE(a == b);
+
+  EXPECT_EQ("ACCEPT_CH frame with 1 entries: origin: foo; value: bar",
+            a.ToString());
+  std::stringstream s;
+  s << a;
+  EXPECT_EQ("ACCEPT_CH frame with 1 entries: origin: foo; value: bar", s.str());
+}
+
+}  // namespace test
+}  // namespace quic