Updates the representation of a header name or value.

This representation can either be a string_view reference to a static string, or a std::string. This will make it easier to incorporate zero-copy header handling into the adapter interface.

PiperOrigin-RevId: 374236816
diff --git a/http2/adapter/http2_protocol.cc b/http2/adapter/http2_protocol.cc
index 18e8985..8d59aee 100644
--- a/http2/adapter/http2_protocol.cc
+++ b/http2/adapter/http2_protocol.cc
@@ -12,6 +12,15 @@
 const char kHttp2PathPseudoHeader[] = ":path";
 const char kHttp2StatusPseudoHeader[] = ":status";
 
+std::pair<absl::string_view, bool> GetStringView(const HeaderRep& rep) {
+  if (absl::holds_alternative<absl::string_view>(rep)) {
+    return std::make_pair(absl::get<absl::string_view>(rep), true);
+  } else {
+    absl::string_view view = absl::get<std::string>(rep);
+    return std::make_pair(view, false);
+  }
+}
+
 absl::string_view Http2SettingsIdToString(uint16_t id) {
   switch (id) {
     case Http2KnownSettingsId::HEADER_TABLE_SIZE:
diff --git a/http2/adapter/http2_protocol.h b/http2/adapter/http2_protocol.h
index 35abea0..1e1dd39 100644
--- a/http2/adapter/http2_protocol.h
+++ b/http2/adapter/http2_protocol.h
@@ -8,6 +8,7 @@
 #include "base/integral_types.h"
 #include "absl/base/attributes.h"
 #include "absl/strings/string_view.h"
+#include "absl/types/variant.h"
 
 namespace http2 {
 namespace adapter {
@@ -21,9 +22,16 @@
 // Represents the payload of an HTTP/2 PING frame.
 using Http2PingId = uint64_t;
 
+// Represents a single header name or value.
+using HeaderRep = absl::variant<absl::string_view, std::string>;
+
+// Boolean return value is true if |rep| holds a string_view, which is assumed
+// to have an indefinite lifetime.
+std::pair<absl::string_view, bool> GetStringView(const HeaderRep& rep);
+
 // Represents an HTTP/2 header field. A header field is a key-value pair with
 // lowercase keys (as specified in RFC 7540 Section 8.1.2).
-using Header = std::pair<std::string, std::string>;
+using Header = std::pair<HeaderRep, HeaderRep>;
 
 // Represents an HTTP/2 SETTINGS key-value parameter.
 struct Http2Setting {
diff --git a/http2/adapter/nghttp2_adapter_test.cc b/http2/adapter/nghttp2_adapter_test.cc
index d7e1fe6..8708eb8 100644
--- a/http2/adapter/nghttp2_adapter_test.cc
+++ b/http2/adapter/nghttp2_adapter_test.cc
@@ -65,22 +65,25 @@
   EXPECT_THAT(serialized, EqualsFrames({spdy::SpdyFrameType::SETTINGS,
                                         spdy::SpdyFrameType::PING}));
 
-  const std::vector<Header> headers1 = {{":method", "GET"},
-                                        {":scheme", "http"},
-                                        {":authority", "example.com"},
-                                        {":path", "/this/is/request/one"}};
+  const std::vector<const Header> headers1 =
+      ToHeaders({{":method", "GET"},
+                 {":scheme", "http"},
+                 {":authority", "example.com"},
+                 {":path", "/this/is/request/one"}});
   const auto nvs1 = GetRequestNghttp2Nvs(headers1);
 
-  const std::vector<Header> headers2 = {{":method", "GET"},
-                                        {":scheme", "http"},
-                                        {":authority", "example.com"},
-                                        {":path", "/this/is/request/two"}};
+  const std::vector<const Header> headers2 =
+      ToHeaders({{":method", "GET"},
+                 {":scheme", "http"},
+                 {":authority", "example.com"},
+                 {":path", "/this/is/request/two"}});
   const auto nvs2 = GetRequestNghttp2Nvs(headers2);
 
-  const std::vector<Header> headers3 = {{":method", "GET"},
-                                        {":scheme", "http"},
-                                        {":authority", "example.com"},
-                                        {":path", "/this/is/request/three"}};
+  const std::vector<const Header> headers3 =
+      ToHeaders({{":method", "GET"},
+                 {":scheme", "http"},
+                 {":authority", "example.com"},
+                 {":path", "/this/is/request/three"}});
   const auto nvs3 = GetRequestNghttp2Nvs(headers3);
 
   const int32_t stream_id1 =
diff --git a/http2/adapter/nghttp2_session_test.cc b/http2/adapter/nghttp2_session_test.cc
index 85f8922..790e331 100644
--- a/http2/adapter/nghttp2_session_test.cc
+++ b/http2/adapter/nghttp2_session_test.cc
@@ -103,22 +103,25 @@
                                         spdy::SpdyFrameType::PING}));
   visitor_.Clear();
 
-  const std::vector<Header> headers1 = {{":method", "GET"},
-                                        {":scheme", "http"},
-                                        {":authority", "example.com"},
-                                        {":path", "/this/is/request/one"}};
+  const std::vector<const Header> headers1 =
+      ToHeaders({{":method", "GET"},
+                 {":scheme", "http"},
+                 {":authority", "example.com"},
+                 {":path", "/this/is/request/one"}});
   const auto nvs1 = GetRequestNghttp2Nvs(headers1);
 
-  const std::vector<Header> headers2 = {{":method", "GET"},
-                                        {":scheme", "http"},
-                                        {":authority", "example.com"},
-                                        {":path", "/this/is/request/two"}};
+  const std::vector<const Header> headers2 =
+      ToHeaders({{":method", "GET"},
+                 {":scheme", "http"},
+                 {":authority", "example.com"},
+                 {":path", "/this/is/request/two"}});
   const auto nvs2 = GetRequestNghttp2Nvs(headers2);
 
-  const std::vector<Header> headers3 = {{":method", "GET"},
-                                        {":scheme", "http"},
-                                        {":authority", "example.com"},
-                                        {":path", "/this/is/request/three"}};
+  const std::vector<const Header> headers3 =
+      ToHeaders({{":method", "GET"},
+                 {":scheme", "http"},
+                 {":authority", "example.com"},
+                 {":path", "/this/is/request/three"}});
   const auto nvs3 = GetRequestNghttp2Nvs(headers3);
 
   const int32_t stream_id1 = nghttp2_submit_request(
diff --git a/http2/adapter/nghttp2_util.cc b/http2/adapter/nghttp2_util.cc
index 6dd218e..8e720a6 100644
--- a/http2/adapter/nghttp2_util.cc
+++ b/http2/adapter/nghttp2_util.cc
@@ -59,11 +59,21 @@
   auto nghttp2_nvs = std::vector<nghttp2_nv>(num_headers);
   for (int i = 0; i < num_headers; ++i) {
     nghttp2_nv header;
-    header.name = ToUint8Ptr(&headers[i].first[0]);
-    header.namelen = headers[i].first.size();
-    header.value = ToUint8Ptr(&headers[i].second[0]);
-    header.valuelen = headers[i].second.size();
-    header.flags = NGHTTP2_FLAG_NONE;
+    uint8_t flags = NGHTTP2_NV_FLAG_NONE;
+
+    const auto [name, no_copy_name] = GetStringView(headers[i].first);
+    header.name = ToUint8Ptr(name.data());
+    header.namelen = name.size();
+    if (no_copy_name) {
+      flags |= NGHTTP2_NV_FLAG_NO_COPY_NAME;
+    }
+    const auto [value, no_copy_value] = GetStringView(headers[i].second);
+    header.value = ToUint8Ptr(value.data());
+    header.valuelen = value.size();
+    if (no_copy_value) {
+      flags |= NGHTTP2_NV_FLAG_NO_COPY_VALUE;
+    }
+    header.flags = flags;
     nghttp2_nvs.push_back(std::move(header));
   }
 
diff --git a/http2/adapter/test_frame_sequence.cc b/http2/adapter/test_frame_sequence.cc
index 46d33b6..a4d3806 100644
--- a/http2/adapter/test_frame_sequence.cc
+++ b/http2/adapter/test_frame_sequence.cc
@@ -7,6 +7,15 @@
 namespace adapter {
 namespace test {
 
+std::vector<const Header> ToHeaders(
+    absl::Span<const std::pair<absl::string_view, absl::string_view>> headers) {
+  std::vector<const Header> out;
+  for (auto [name, value] : headers) {
+    out.push_back(std::make_pair(HeaderRep(name), HeaderRep(value)));
+  }
+  return out;
+}
+
 TestFrameSequence& TestFrameSequence::ClientPreface() {
   preface_ = spdy::kHttp2ConnectionHeaderPrefix;
   frames_.push_back(absl::make_unique<spdy::SpdySettingsIR>());
@@ -75,6 +84,13 @@
   return *this;
 }
 
+TestFrameSequence& TestFrameSequence::Headers(
+    Http2StreamId stream_id,
+    absl::Span<const std::pair<absl::string_view, absl::string_view>> headers,
+    bool fin) {
+  return Headers(stream_id, ToHeaders(headers), fin);
+}
+
 TestFrameSequence& TestFrameSequence::Headers(Http2StreamId stream_id,
                                               spdy::Http2HeaderBlock block,
                                               bool fin) {
@@ -90,7 +106,9 @@
                                               bool fin) {
   spdy::SpdyHeaderBlock block;
   for (const Header& header : headers) {
-    block[header.first] = header.second;
+    absl::string_view name = GetStringView(header.first).first;
+    absl::string_view value = GetStringView(header.second).first;
+    block[name] = value;
   }
   return Headers(stream_id, std::move(block), fin);
 }
diff --git a/http2/adapter/test_frame_sequence.h b/http2/adapter/test_frame_sequence.h
index dd110c1..cc5e8b5 100644
--- a/http2/adapter/test_frame_sequence.h
+++ b/http2/adapter/test_frame_sequence.h
@@ -12,6 +12,9 @@
 namespace adapter {
 namespace test {
 
+std::vector<const Header> ToHeaders(
+    absl::Span<const std::pair<absl::string_view, absl::string_view>> headers);
+
 class TestFrameSequence {
  public:
   TestFrameSequence() = default;
@@ -30,6 +33,10 @@
   TestFrameSequence& GoAway(Http2StreamId last_good_stream_id,
                             Http2ErrorCode error,
                             absl::string_view payload = "");
+  TestFrameSequence& Headers(
+      Http2StreamId stream_id,
+      absl::Span<const std::pair<absl::string_view, absl::string_view>> headers,
+      bool fin = false);
   TestFrameSequence& Headers(Http2StreamId stream_id,
                              spdy::Http2HeaderBlock block,
                              bool fin = false);