From 0362ec5c5c5abecf7fda0cc5365615d7840263d4 Mon Sep 17 00:00:00 2001
From: Jakub Sobon <mumak@google.com>
Date: Fri, 4 Dec 2020 14:42:02 -0500
Subject: [PATCH 1/4] Deprecating v2 primitive in request source service.proto.

Signed-off-by: Jakub Sobon <mumak@google.com>
---
 api/request_source/BUILD         |  1 +
 api/request_source/service.proto | 18 ++++++++++++++----
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/api/request_source/BUILD b/api/request_source/BUILD
index 9cf50e7ff..2473d6a2e 100644
--- a/api/request_source/BUILD
+++ b/api/request_source/BUILD
@@ -13,6 +13,7 @@ api_cc_py_proto_library(
         "@envoy_api//envoy/api/v2/auth:pkg",
         "@envoy_api//envoy/api/v2/cluster:pkg",
         "@envoy_api//envoy/api/v2/core:pkg",
+        "@envoy_api//envoy/config/core/v3:pkg",
     ],
 )
 
diff --git a/api/request_source/service.proto b/api/request_source/service.proto
index 9ab934558..e1acdb135 100644
--- a/api/request_source/service.proto
+++ b/api/request_source/service.proto
@@ -2,8 +2,9 @@ syntax = "proto3";
 
 package nighthawk.request_source;
 
-import "google/protobuf/wrappers.proto";
 import "envoy/api/v2/core/base.proto";
+import "envoy/config/core/v3/base.proto";
+import "google/protobuf/wrappers.proto";
 
 // Used to request a RequestStreamResponse.
 message RequestStreamRequest {
@@ -37,9 +38,18 @@ message RequestSpecifier {
   // Request content length. The client will transfer the number of bytes specified here for the
   // request body.
   google.protobuf.UInt32Value content_length = 4;
-  // Request header replacements. Any existing header(s) with the same name will be removed
-  // before setting.
-  envoy.api.v2.core.HeaderMap headers = 5;
+
+  oneof oneof_headers {
+    // Request header replacements. Any existing header(s) with the same name will be removed
+    // before setting.
+    //
+    // Envoy deprecated its v2 API, prefer to use v3_headers instead.
+    envoy.api.v2.core.HeaderMap headers = 5 [deprecated = true];
+
+    // Request header replacements. Any existing header(s) with the same name will be removed
+    // before setting.
+    envoy.config.core.v3.HeaderMap v3_headers = 6;
+  }
   // TODO(oschaaf): nice to have
   // google.protobuf.StringValue sni_hostname = 10;
 }

From 2a8579a1f28c842ea117daddea44fa6c64e8bd1c Mon Sep 17 00:00:00 2001
From: Jakub Sobon <mumak@google.com>
Date: Fri, 4 Dec 2020 14:43:11 -0500
Subject: [PATCH 2/4] gRPC client of request source can consume both v2 and v3
 headers.

Signed-off-by: Jakub Sobon <mumak@google.com>
---
 source/common/BUILD                           |  2 +
 .../common/request_stream_grpc_client_impl.cc | 26 +++++++++--
 test/BUILD                                    |  3 ++
 test/request_stream_grpc_client_test.cc       | 45 ++++++++++++++++---
 4 files changed, 65 insertions(+), 11 deletions(-)

diff --git a/source/common/BUILD b/source/common/BUILD
index fd8cc3701..951c2a411 100644
--- a/source/common/BUILD
+++ b/source/common/BUILD
@@ -60,6 +60,8 @@ envoy_cc_library(
         "@envoy//source/common/grpc:typed_async_client_lib_with_external_headers",
         "@envoy//source/common/http:header_map_lib_with_external_headers",
         "@envoy//source/common/http:headers_lib_with_external_headers",
+        "@envoy_api//envoy/api/v2/core:pkg_cc_proto",
+        "@envoy_api//envoy/config/core/v3:pkg_cc_proto",
     ],
 )
 
diff --git a/source/common/request_stream_grpc_client_impl.cc b/source/common/request_stream_grpc_client_impl.cc
index da0095042..31db65a69 100644
--- a/source/common/request_stream_grpc_client_impl.cc
+++ b/source/common/request_stream_grpc_client_impl.cc
@@ -2,16 +2,22 @@
 
 #include <string>
 
+#include "envoy/api/v2/core/base.pb.h"
+#include "envoy/config/core/v3/base.pb.h"
 #include "envoy/stats/scope.h"
 
 #include "external/envoy/source/common/common/assert.h"
 #include "external/envoy/source/common/http/header_map_impl.h"
 #include "external/envoy/source/common/http/headers.h"
 
+#include "api/request_source/service.pb.h"
+
 #include "common/request_impl.h"
 
 namespace Nighthawk {
 
+using ::nighthawk::request_source::RequestSpecifier;
+
 const std::string RequestStreamGrpcClientImpl::METHOD_NAME =
     "nighthawk.request_source.NighthawkRequestSourceService.RequestStream";
 
@@ -75,15 +81,27 @@ RequestPtr ProtoRequestHelper::messageToRequest(
   RequestPtr request = std::make_unique<RequestImpl>(header);
 
   if (message.has_request_specifier()) {
-    const auto& request_specifier = message.request_specifier();
-    if (request_specifier.has_headers()) {
-      const auto& message_request_headers = request_specifier.headers();
-      for (const auto& message_header : message_request_headers.headers()) {
+    const RequestSpecifier& request_specifier = message.request_specifier();
+
+    if (request_specifier.has_v3_headers()) {
+      const envoy::config::core::v3::HeaderMap& message_request_headers =
+          request_specifier.v3_headers();
+      for (const envoy::config::core::v3::HeaderValue& message_header :
+           message_request_headers.headers()) {
+        Envoy::Http::LowerCaseString header_name(message_header.key());
+        header->remove(header_name);
+        header->addCopy(header_name, message_header.value());
+      }
+    } else if (request_specifier.has_headers()) {
+      const envoy::api::v2::core::HeaderMap& message_request_headers = request_specifier.headers();
+      for (const envoy::api::v2::core::HeaderValue& message_header :
+           message_request_headers.headers()) {
         Envoy::Http::LowerCaseString header_name(message_header.key());
         header->remove(header_name);
         header->addCopy(header_name, message_header.value());
       }
     }
+
     if (request_specifier.has_content_length()) {
       std::string s_content_length = absl::StrCat("", request_specifier.content_length().value());
       header->remove(Envoy::Http::Headers::get().ContentLength);
diff --git a/test/BUILD b/test/BUILD
index 326d971ee..92190a3c6 100644
--- a/test/BUILD
+++ b/test/BUILD
@@ -325,8 +325,11 @@ envoy_cc_test(
     srcs = ["request_stream_grpc_client_test.cc"],
     repository = "@envoy",
     deps = [
+        "//api/request_source:grpc_request_source_service_lib",
         "//source/common:request_stream_grpc_client_lib",
         "@envoy//test/test_common:utility_lib",
+        "@envoy_api//envoy/api/v2/core:pkg_cc_proto",
+        "@envoy_api//envoy/config/core/v3:pkg_cc_proto",
     ],
 )
 
diff --git a/test/request_stream_grpc_client_test.cc b/test/request_stream_grpc_client_test.cc
index 91661ea52..81f298eac 100644
--- a/test/request_stream_grpc_client_test.cc
+++ b/test/request_stream_grpc_client_test.cc
@@ -1,5 +1,10 @@
+#include "envoy/api/v2/core/base.pb.h"
+#include "envoy/config/core/v3/base.pb.h"
+
 #include "external/envoy/test/test_common/utility.h"
 
+#include "api/request_source/service.pb.h"
+
 #include "common/request_impl.h"
 #include "common/request_stream_grpc_client_impl.h"
 
@@ -8,6 +13,9 @@
 using namespace testing;
 
 namespace Nighthawk {
+namespace {
+
+using ::nighthawk::request_source::RequestSpecifier;
 
 // The grpc client itself is tested via the python based integration tests.
 // It is convenient to test message translation here.
@@ -18,7 +26,9 @@ class ProtoRequestHelperTest : public Test {
     // We test for equality. If we observe mismatch, we use EXPECT_EQ which is guaranteed
     // to fail -- but will provide much more helpful output.
     if (!Envoy::TestUtility::headerMapEqualIgnoreOrder(expected_header_, *request->header())) {
-      EXPECT_EQ(expected_header_, *request->header());
+      EXPECT_EQ(expected_header_, *request->header()) << "expected headers:\n"
+                                                      << expected_header_ << "\nactual headers:\n"
+                                                      << *request->header() << "\n";
     };
   }
 
@@ -42,14 +52,34 @@ TEST_F(ProtoRequestHelperTest, ExplicitFields) {
   translateExpectingEqual();
 }
 
-// Test the generic header api we offer in the proto api.
-TEST_F(ProtoRequestHelperTest, GenericHeaderFields) {
-  auto* request_specifier = response_.mutable_request_specifier();
-  auto* headers = request_specifier->mutable_headers();
-  auto* header_1 = headers->add_headers();
+// Test the generic header api we offer in the proto api using Envoy API v2
+// primitives.
+TEST_F(ProtoRequestHelperTest, GenericHeaderFieldsUsingDeprecatedEnvoyV2Api) {
+  RequestSpecifier* request_specifier = response_.mutable_request_specifier();
+  envoy::api::v2::core::HeaderMap* headers = request_specifier->mutable_headers();
+  envoy::api::v2::core::HeaderValue* header_1 = headers->add_headers();
+  header_1->set_key("header1");
+  header_1->set_value("value1");
+  envoy::api::v2::core::HeaderValue* header_2 = headers->add_headers();
+  header_2->set_key("header2");
+  header_2->set_value("value2");
+  // We re-add the same header, but do not expect that to show up in the translation because we
+  // always replace.
+  headers->add_headers()->MergeFrom(*header_2);
+  expected_header_ =
+      Envoy::Http::TestRequestHeaderMapImpl{{"header1", "value1"}, {"header2", "value2"}};
+  translateExpectingEqual();
+}
+
+// Test the generic header api we offer in the proto api using Envoy API v3
+// primitives.
+TEST_F(ProtoRequestHelperTest, GenericHeaderFieldsUsingDeprecatedEnvoyV3Api) {
+  RequestSpecifier* request_specifier = response_.mutable_request_specifier();
+  envoy::config::core::v3::HeaderMap* headers = request_specifier->mutable_v3_headers();
+  envoy::config::core::v3::HeaderValue* header_1 = headers->add_headers();
   header_1->set_key("header1");
   header_1->set_value("value1");
-  auto* header_2 = headers->add_headers();
+  envoy::config::core::v3::HeaderValue* header_2 = headers->add_headers();
   header_2->set_key("header2");
   header_2->set_value("value2");
   // We re-add the same header, but do not expect that to show up in the translation because we
@@ -74,4 +104,5 @@ TEST_F(ProtoRequestHelperTest, AmbiguousHost) {
   translateExpectingEqual();
 }
 
+} // namespace
 } // namespace Nighthawk

From 0faf9caad9f32a280bf5b1fb6b1cd40267727b72 Mon Sep 17 00:00:00 2001
From: Jakub Sobon <mumak@google.com>
Date: Fri, 4 Dec 2020 14:54:49 -0500
Subject: [PATCH 3/4] Migrate our dummy request source to Envoy API v3
 primitives.

Signed-off-by: Jakub Sobon <mumak@google.com>
---
 source/client/BUILD           | 1 +
 source/client/service_impl.cc | 8 +++++---
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/source/client/BUILD b/source/client/BUILD
index b89c90bd7..4fe97d170 100644
--- a/source/client/BUILD
+++ b/source/client/BUILD
@@ -156,6 +156,7 @@ envoy_cc_library(
         "//api/client:grpc_service_lib",
         "//api/request_source:grpc_request_source_service_lib",
         "@envoy//source/common/common:thread_lib_with_external_headers",
+        "@envoy_api//envoy/config/core/v3:pkg_cc_proto",
     ],
 )
 
diff --git a/source/client/service_impl.cc b/source/client/service_impl.cc
index 693955a79..fda3d69d7 100644
--- a/source/client/service_impl.cc
+++ b/source/client/service_impl.cc
@@ -2,6 +2,8 @@
 
 #include <grpc++/grpc++.h>
 
+#include "envoy/config/core/v3/base.pb.h"
+
 #include "common/request_source_impl.h"
 
 #include "client/client.h"
@@ -106,7 +108,7 @@ ::grpc::Status ServiceImpl::ExecutionStream(
 }
 
 namespace {
-void addHeader(envoy::api::v2::core::HeaderMap* map, absl::string_view key,
+void addHeader(envoy::config::core::v3::HeaderMap* map, absl::string_view key,
                absl::string_view value) {
   auto* request_header = map->add_headers();
   request_header->set_key(std::string(key));
@@ -144,7 +146,7 @@ ::grpc::Status RequestSourceServiceImpl::RequestStream(
       HeaderMapPtr headers = request->header();
       nighthawk::request_source::RequestStreamResponse response;
       auto* request_specifier = response.mutable_request_specifier();
-      auto* request_headers = request_specifier->mutable_headers();
+      auto* request_headers = request_specifier->mutable_v3_headers();
       headers->iterate([&request_headers](const Envoy::Http::HeaderEntry& header)
                            -> Envoy::Http::HeaderMap::Iterate {
         addHeader(request_headers, header.key().getStringView(), header.value().getStringView());
@@ -163,4 +165,4 @@ ::grpc::Status RequestSourceServiceImpl::RequestStream(
 }
 
 } // namespace Client
-} // namespace Nighthawk
\ No newline at end of file
+} // namespace Nighthawk

From 32d51238cf10e70ae8420ac44986d9ed026abd3b Mon Sep 17 00:00:00 2001
From: Jakub Sobon <mumak@google.com>
Date: Mon, 7 Dec 2020 23:51:42 -0500
Subject: [PATCH 4/4] Removing incorrect word "deprecated" from a test case
 name.

Signed-off-by: Jakub Sobon <mumak@google.com>
---
 test/request_stream_grpc_client_test.cc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/request_stream_grpc_client_test.cc b/test/request_stream_grpc_client_test.cc
index 81f298eac..b78b32f32 100644
--- a/test/request_stream_grpc_client_test.cc
+++ b/test/request_stream_grpc_client_test.cc
@@ -73,7 +73,7 @@ TEST_F(ProtoRequestHelperTest, GenericHeaderFieldsUsingDeprecatedEnvoyV2Api) {
 
 // Test the generic header api we offer in the proto api using Envoy API v3
 // primitives.
-TEST_F(ProtoRequestHelperTest, GenericHeaderFieldsUsingDeprecatedEnvoyV3Api) {
+TEST_F(ProtoRequestHelperTest, GenericHeaderFieldsUsingEnvoyV3Api) {
   RequestSpecifier* request_specifier = response_.mutable_request_specifier();
   envoy::config::core::v3::HeaderMap* headers = request_specifier->mutable_v3_headers();
   envoy::config::core::v3::HeaderValue* header_1 = headers->add_headers();