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