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);