gfe-relnote: In QuicSpdyClientBase, automatically convert HTTP request header names to lower case. Protected by default true, non-feature flag --quic_client_convert_http_header_name_to_lowercase.

This code should not be used in production GFEs. The only place where QuicClient is used AFAIK is in L0's udp proxy, which only connects to L1s and never sends HTTP requests.

Tested with a quic_client sending a request with --headers=X-Return-Encrypted-Headers:all, which succeeded(200) when --quic_client_convert_http_header_name_to_lowercase is true, failed(400) when the flag is false.

PiperOrigin-RevId: 258548285
Change-Id: I04ebd4041b9da3c80a0f5ff8b30b20627e02caf1
diff --git a/quic/tools/quic_client_bin.cc b/quic/tools/quic_client_bin.cc
index 06760ce..226e647 100644
--- a/quic/tools/quic_client_bin.cc
+++ b/quic/tools/quic_client_bin.cc
@@ -20,7 +20,7 @@
 //   quic_client www.google.com --body="this is a POST body"
 //
 // Append additional headers to the request:
-//   quic_client www.google.com --headers="Header-A: 1234; Header-B: 5678"
+//   quic_client www.google.com --headers="header-a: 1234; header-b: 5678"
 //
 // Connect to a host different to the URL being requested:
 //   quic_client mail.google.com --host=www.google.com
diff --git a/quic/tools/quic_spdy_client_base.cc b/quic/tools/quic_spdy_client_base.cc
index d857dec..06abc96 100644
--- a/quic/tools/quic_spdy_client_base.cc
+++ b/quic/tools/quic_spdy_client_base.cc
@@ -103,14 +103,31 @@
 void QuicSpdyClientBase::SendRequest(const SpdyHeaderBlock& headers,
                                      QuicStringPiece body,
                                      bool fin) {
+  if (GetQuicFlag(FLAGS_quic_client_convert_http_header_name_to_lowercase)) {
+    QUIC_CODE_COUNT(quic_client_convert_http_header_name_to_lowercase);
+    SpdyHeaderBlock sanitized_headers;
+    for (const auto& p : headers) {
+      sanitized_headers[QuicTextUtils::ToLower(p.first)] = p.second;
+    }
+
+    SendRequestInternal(std::move(sanitized_headers), body, fin);
+  } else {
+    SendRequestInternal(headers.Clone(), body, fin);
+  }
+}
+
+void QuicSpdyClientBase::SendRequestInternal(SpdyHeaderBlock sanitized_headers,
+                                             QuicStringPiece body,
+                                             bool fin) {
   QuicClientPushPromiseIndex::TryHandle* handle;
-  QuicAsyncStatus rv = push_promise_index()->Try(headers, this, &handle);
+  QuicAsyncStatus rv =
+      push_promise_index()->Try(sanitized_headers, this, &handle);
   if (rv == QUIC_SUCCESS)
     return;
 
   if (rv == QUIC_PENDING) {
     // May need to retry request if asynchronous rendezvous fails.
-    AddPromiseDataToResend(headers, body, fin);
+    AddPromiseDataToResend(sanitized_headers, body, fin);
     return;
   }
 
@@ -119,7 +136,7 @@
     QUIC_BUG << "stream creation failed!";
     return;
   }
-  stream->SendRequest(headers.Clone(), body, fin);
+  stream->SendRequest(std::move(sanitized_headers), body, fin);
 }
 
 void QuicSpdyClientBase::SendRequestAndWaitForResponse(
diff --git a/quic/tools/quic_spdy_client_base.h b/quic/tools/quic_spdy_client_base.h
index 3de7186..87fc664 100644
--- a/quic/tools/quic_spdy_client_base.h
+++ b/quic/tools/quic_spdy_client_base.h
@@ -177,6 +177,10 @@
     QuicSpdyClientBase* client_;
   };
 
+  void SendRequestInternal(spdy::SpdyHeaderBlock sanitized_headers,
+                           QuicStringPiece body,
+                           bool fin);
+
   // Index of pending promised streams. Must outlive |session_|.
   QuicClientPushPromiseIndex push_promise_index_;