From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: deepak1556 <hop2deep@gmail.com>
Date: Tue, 17 Aug 2021 22:42:42 -0700
Subject: feat: expose raw response headers from URLLoader

With https://chromium-review.googlesource.com/c/chromium/src/+/2856099
unfiltered response headers are only available via trusted channel
through //services/network/public/mojom/devtools_observer.mojom.
https://github.com/electron/electron/pull/30503/commits/28f4da1582d046e96cb58f3cbb590503e89dfd0d
was an attempt to use this interface but given the original response is
signalled on a different interface via URLLoaderClient::OnReceiveResponse
it is harder to sync these data from two different channels for a synchronous
event emitted on the SimpleURLLoaderWrapper::OnResponseStarted.

This patch does the minimal approach to add back the raw response
headers, moving forward we should find a way in upstream to provide
access to these headers for loader clients created on the browser process.

diff --git a/services/network/public/cpp/resource_request.cc b/services/network/public/cpp/resource_request.cc
index 65921550e62dbcd79f77192951b95991c3e904a1..c45a2a8c5adf3df78b4e4c4c0d4d945ade2a6826 100644
--- a/services/network/public/cpp/resource_request.cc
+++ b/services/network/public/cpp/resource_request.cc
@@ -234,6 +234,7 @@ bool ResourceRequest::EqualsForTesting(const ResourceRequest& request) const {
          do_not_prompt_for_login == request.do_not_prompt_for_login &&
          is_outermost_main_frame == request.is_outermost_main_frame &&
          transition_type == request.transition_type &&
+         report_raw_headers == request.report_raw_headers &&
          previews_state == request.previews_state &&
          upgrade_if_insecure == request.upgrade_if_insecure &&
          is_revalidating == request.is_revalidating &&
diff --git a/services/network/public/cpp/resource_request.h b/services/network/public/cpp/resource_request.h
index 1f1c2bac7d7775898652bbd133b21d7032b92b86..fd6bbe55f5e4373b75f9d9c1b835a241f9920ad1 100644
--- a/services/network/public/cpp/resource_request.h
+++ b/services/network/public/cpp/resource_request.h
@@ -159,6 +159,7 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE) ResourceRequest {
   bool do_not_prompt_for_login = false;
   bool is_outermost_main_frame = false;
   int transition_type = 0;
+  bool report_raw_headers = false;
   int previews_state = 0;
   bool upgrade_if_insecure = false;
   bool is_revalidating = false;
diff --git a/services/network/public/cpp/url_request_mojom_traits.cc b/services/network/public/cpp/url_request_mojom_traits.cc
index 50abd829e02c45cc4911ba37191c6b572ab222fd..f25950bd150bcbb6a14846bb7856993066257492 100644
--- a/services/network/public/cpp/url_request_mojom_traits.cc
+++ b/services/network/public/cpp/url_request_mojom_traits.cc
@@ -209,6 +209,7 @@ bool StructTraits<
   out->do_not_prompt_for_login = data.do_not_prompt_for_login();
   out->is_outermost_main_frame = data.is_outermost_main_frame();
   out->transition_type = data.transition_type();
+  out->report_raw_headers = data.report_raw_headers();
   out->previews_state = data.previews_state();
   out->upgrade_if_insecure = data.upgrade_if_insecure();
   out->is_revalidating = data.is_revalidating();
diff --git a/services/network/public/cpp/url_request_mojom_traits.h b/services/network/public/cpp/url_request_mojom_traits.h
index f5910b122a7e25a9846633ca98bd63b670dc7639..5a2d95d4eac682063afa89eb2b6c7b89ac6b6b5d 100644
--- a/services/network/public/cpp/url_request_mojom_traits.h
+++ b/services/network/public/cpp/url_request_mojom_traits.h
@@ -269,6 +269,9 @@ struct COMPONENT_EXPORT(NETWORK_CPP_BASE)
   static int32_t transition_type(const network::ResourceRequest& request) {
     return request.transition_type;
   }
+  static bool report_raw_headers(const network::ResourceRequest& request) {
+    return request.report_raw_headers;
+  }
   static int32_t previews_state(const network::ResourceRequest& request) {
     return request.previews_state;
   }
diff --git a/services/network/public/mojom/url_request.mojom b/services/network/public/mojom/url_request.mojom
index 3c81eb68a21e2762173d6de055f6fedff457c4b6..8da97492aa055c384a539de4701d7b2171cf71e8 100644
--- a/services/network/public/mojom/url_request.mojom
+++ b/services/network/public/mojom/url_request.mojom
@@ -312,6 +312,9 @@ struct URLRequest {
   // about this.
   int32 transition_type;
 
+  // Whether to provide unfiltered response headers.
+  bool report_raw_headers;
+
   // Whether or not to request a Preview version of the resource or let the
   // browser decide.
   // Note: this is an enum of type PreviewsState.
diff --git a/services/network/public/mojom/url_response_head.mojom b/services/network/public/mojom/url_response_head.mojom
index dd8e82d32cba09733b98f48ff08e750b4dec82d2..0680b97af6a933c060d759740da2a2942c7ca1a6 100644
--- a/services/network/public/mojom/url_response_head.mojom
+++ b/services/network/public/mojom/url_response_head.mojom
@@ -9,6 +9,7 @@ import "mojo/public/mojom/base/unguessable_token.mojom";
 import "services/network/public/mojom/alternate_protocol_usage.mojom";
 import "services/network/public/mojom/fetch_api.mojom";
 import "services/network/public/mojom/ip_address_space.mojom";
+import "services/network/public/mojom/http_raw_headers.mojom";
 import "services/network/public/mojom/ip_endpoint.mojom";
 import "services/network/public/mojom/load_timing_info.mojom";
 import "services/network/public/mojom/network_param.mojom";
@@ -29,6 +30,9 @@ struct URLResponseHead {
   // The response headers or NULL if the URL type does not support headers.
   HttpResponseHeaders headers;
 
+  // Actual response headers, as obtained from the network stack.
+  array<HttpRawHeaderPair> raw_response_headers;
+
   // The mime type of the response.  This may be a derived value.
   string mime_type;
 
diff --git a/services/network/url_loader.cc b/services/network/url_loader.cc
index 60a520d9f637a588d10e72239818fa5b862c9865..d3d1d572277605dddf2a2a2a78919bff92c00a22 100644
--- a/services/network/url_loader.cc
+++ b/services/network/url_loader.cc
@@ -478,6 +478,7 @@ URLLoader::URLLoader(
                                   mojo::SimpleWatcher::ArmingPolicy::MANUAL,
                                   base::SequencedTaskRunnerHandle::Get()),
       per_factory_corb_state_(context.GetMutableCorbState()),
+      report_raw_headers_(request.report_raw_headers),
       devtools_request_id_(request.devtools_request_id),
       request_mode_(request.mode),
       request_credentials_mode_(request.credentials_mode),
@@ -660,7 +661,7 @@ URLLoader::URLLoader(
   url_request_->SetRequestHeadersCallback(base::BindRepeating(
       &URLLoader::SetRawRequestHeadersAndNotify, base::Unretained(this)));
 
-  if (devtools_request_id()) {
+  if (devtools_request_id() || report_raw_headers_) {
     url_request_->SetResponseHeadersCallback(base::BindRepeating(
         &URLLoader::SetRawResponseHeaders, base::Unretained(this)));
   }
@@ -1442,6 +1443,19 @@ void URLLoader::OnResponseStarted(net::URLRequest* url_request, int net_error) {
   }
 
   response_ = BuildResponseHead();
+  if (raw_response_headers_ && report_raw_headers_) {
+    std::vector<network::mojom::HttpRawHeaderPairPtr> header_array;
+    size_t iterator = 0;
+    std::string name, value;
+    while (raw_response_headers_->EnumerateHeaderLines(&iterator, &name, &value)) {
+      network::mojom::HttpRawHeaderPairPtr pair =
+          network::mojom::HttpRawHeaderPair::New();
+      pair->key = name;
+      pair->value = value;
+      header_array.push_back(std::move(pair));
+    }
+    response_->raw_response_headers = std::move(header_array);
+  }
   DispatchOnRawResponse();
 
   // Parse and remove the Trust Tokens response headers, if any are expected,
diff --git a/services/network/url_loader.h b/services/network/url_loader.h
index 9be9f703b189050332ca506dd3066609c3fa3992..1a15939f15fcc93de02cf0ff67faaefc5adbee53 100644
--- a/services/network/url_loader.h
+++ b/services/network/url_loader.h
@@ -526,6 +526,8 @@ class COMPONENT_EXPORT(NETWORK_SERVICE) URLLoader
   std::unique_ptr<ResourceScheduler::ScheduledResourceRequest>
       resource_scheduler_request_handle_;
 
+  // Whether client requested raw headers.
+  bool report_raw_headers_ = false;
   bool enable_reporting_raw_headers_ = false;
   bool seen_raw_request_headers_ = false;
   scoped_refptr<const net::HttpResponseHeaders> raw_response_headers_;
