Get raw response headers using URLRequest::SetResponseHeadersCallback()

DevTools used to get actual response headers with NetLogObserver. Once we
removed NetLogObserver, we started using URLRequest::response_headers().
However, it returns the original cached headers in case of revalidation
requests, which is not what DevTools expect.
This adds SetResponseHeadersCallback() similar to SetRequestHeadersCallback()
to resolve the issue.

Bug: 758090, 721408
Change-Id: I8d8a7a757601fe282a82b8a092148726cdab85e2
Reviewed-on: https://chromium-review.googlesource.com/631119
Reviewed-by: Matt Menke <mmenke@chromium.org>
Reviewed-by: Dmitry Gozman <dgozman@chromium.org>
Commit-Queue: Andrey Kosyakov <caseq@chromium.org>
Cr-Commit-Position: refs/heads/master@{#498894}
diff --git a/chrome/browser/devtools/devtools_network_transaction.cc b/chrome/browser/devtools/devtools_network_transaction.cc
index 7c16ef3a..6508896 100644
--- a/chrome/browser/devtools/devtools_network_transaction.cc
+++ b/chrome/browser/devtools/devtools_network_transaction.cc
@@ -290,6 +290,11 @@
   network_transaction_->SetRequestHeadersCallback(std::move(callback));
 }
 
+void DevToolsNetworkTransaction::SetResponseHeadersCallback(
+    net::ResponseHeadersCallback callback) {
+  network_transaction_->SetResponseHeadersCallback(std::move(callback));
+}
+
 void DevToolsNetworkTransaction::SetBeforeHeadersSentCallback(
     const BeforeHeadersSentCallback& callback) {
   network_transaction_->SetBeforeHeadersSentCallback(callback);
diff --git a/chrome/browser/devtools/devtools_network_transaction.h b/chrome/browser/devtools/devtools_network_transaction.h
index 7805290..f52ca9f 100644
--- a/chrome/browser/devtools/devtools_network_transaction.h
+++ b/chrome/browser/devtools/devtools_network_transaction.h
@@ -89,6 +89,8 @@
   void SetBeforeHeadersSentCallback(
       const BeforeHeadersSentCallback& callback) override;
   void SetRequestHeadersCallback(net::RequestHeadersCallback callback) override;
+  void SetResponseHeadersCallback(
+      net::ResponseHeadersCallback callback) override;
   int ResumeNetworkStart() override;
   void GetConnectionAttempts(net::ConnectionAttempts* out) const override;
 
diff --git a/chrome/browser/devtools/devtools_sanity_browsertest.cc b/chrome/browser/devtools/devtools_sanity_browsertest.cc
index faf2c46..95c216e 100644
--- a/chrome/browser/devtools/devtools_sanity_browsertest.cc
+++ b/chrome/browser/devtools/devtools_sanity_browsertest.cc
@@ -84,6 +84,7 @@
 #include "net/test/spawned_test_server/spawned_test_server.h"
 #include "net/test/url_request/url_request_mock_http_job.h"
 #include "net/url_request/url_request_context.h"
+#include "net/url_request/url_request_context_getter.h"
 #include "net/url_request/url_request_filter.h"
 #include "net/url_request/url_request_http_job.h"
 #include "third_party/WebKit/public/platform/WebInputEvent.h"
@@ -2116,3 +2117,40 @@
   DevToolsWindowTesting::CloseDevToolsWindowSync(window);
   content::WebUIControllerFactory::UnregisterFactoryForTesting(&test_factory);
 }
+
+void AddHSTSHost(scoped_refptr<net::URLRequestContextGetter> context,
+                 std::string host) {
+  net::TransportSecurityState* transport_security_state =
+      context->GetURLRequestContext()->transport_security_state();
+  base::Time expiry = base::Time::Now() + base::TimeDelta::FromDays(1000);
+  bool include_subdomains = false;
+  transport_security_state->AddHSTS(host, expiry, include_subdomains);
+}
+
+IN_PROC_BROWSER_TEST_F(DevToolsSanityTest, TestRawHeadersWithRedirectAndHSTS) {
+  net::EmbeddedTestServer https_test_server(
+      net::EmbeddedTestServer::TYPE_HTTPS);
+  https_test_server.SetSSLConfig(
+      net::EmbeddedTestServer::CERT_COMMON_NAME_IS_DOMAIN);
+  https_test_server.ServeFilesFromSourceDirectory("chrome/test/data");
+  ASSERT_TRUE(https_test_server.Start());
+  GURL https_url = https_test_server.GetURL("localhost", "/devtools/image.png");
+  BrowserThread::PostTask(
+      BrowserThread::IO, FROM_HERE,
+      base::BindOnce(
+          AddHSTSHost,
+          base::RetainedRef(browser()->profile()->GetRequestContext()),
+          https_url.host()));
+  ASSERT_TRUE(embedded_test_server()->Start());
+
+  OpenDevToolsWindow(std::string(), false);
+  GURL::Replacements replace_scheme;
+  replace_scheme.SetSchemeStr("http");
+  GURL http_url = https_url.ReplaceComponents(replace_scheme);
+  GURL redirect_url =
+      embedded_test_server()->GetURL("/server-redirect?" + http_url.spec());
+
+  DispatchOnTestSuite(window_, "testRawHeadersWithHSTS",
+                      redirect_url.spec().c_str());
+  CloseDevToolsWindow();
+}
diff --git a/content/browser/loader/resource_loader.cc b/content/browser/loader/resource_loader.cc
index ac97abf..a7acc5b2 100644
--- a/content/browser/loader/resource_loader.cc
+++ b/content/browser/loader/resource_loader.cc
@@ -52,7 +52,8 @@
     ResourceRequestInfoImpl* info,
     net::URLRequest* request,
     ResourceResponse* response,
-    const net::HttpRawRequestHeaders& raw_request_headers) {
+    const net::HttpRawRequestHeaders& raw_request_headers,
+    const net::HttpResponseHeaders* raw_response_headers) {
   response->head.request_time = request->request_time();
   response->head.response_time = request->response_time();
   response->head.headers = request->response_headers();
@@ -72,7 +73,7 @@
     response->head.previews_state = request_info->GetPreviewsState();
   if (info->ShouldReportRawHeaders()) {
     response->head.devtools_info =
-        BuildDevToolsInfo(*request, raw_request_headers);
+        BuildDevToolsInfo(*request, raw_request_headers, raw_response_headers);
   }
 
   response->head.effective_connection_type =
@@ -342,7 +343,10 @@
 
   scoped_refptr<ResourceResponse> response = new ResourceResponse();
   PopulateResourceResponse(info, request_.get(), response.get(),
-                           raw_request_headers_);
+                           raw_request_headers_, raw_response_headers_.get());
+  raw_request_headers_ = net::HttpRawRequestHeaders();
+  raw_response_headers_ = nullptr;
+
   delegate_->DidReceiveRedirect(this, redirect_info.new_url, response.get());
 
   // Can't used ScopedDeferral here, because on sync completion, need to set
@@ -573,6 +577,8 @@
     request_->SetRequestHeadersCallback(
         base::Bind(&net::HttpRawRequestHeaders::Assign,
                    base::Unretained(&raw_request_headers_)));
+    request_->SetResponseHeadersCallback(base::Bind(
+        &ResourceLoader::SetRawResponseHeaders, base::Unretained(this)));
   }
   request_->Start();
 
@@ -640,7 +646,9 @@
   ResourceRequestInfoImpl* info = GetRequestInfo();
   scoped_refptr<ResourceResponse> response = new ResourceResponse();
   PopulateResourceResponse(info, request_.get(), response.get(),
-                           raw_request_headers_);
+                           raw_request_headers_, raw_response_headers_.get());
+  raw_request_headers_ = net::HttpRawRequestHeaders();
+  raw_response_headers_ = nullptr;
 
   delegate_->DidReceiveResponse(this, response.get());
 
@@ -821,4 +829,9 @@
   }
 }
 
+void ResourceLoader::SetRawResponseHeaders(
+    scoped_refptr<const net::HttpResponseHeaders> headers) {
+  raw_response_headers_ = headers;
+}
+
 }  // namespace content
diff --git a/content/browser/loader/resource_loader.h b/content/browser/loader/resource_loader.h
index 924c368..259d498 100644
--- a/content/browser/loader/resource_loader.h
+++ b/content/browser/loader/resource_loader.h
@@ -22,6 +22,7 @@
 #include "url/gurl.h"
 
 namespace net {
+class HttpResponseHeaders;
 class X509Certificate;
 }
 
@@ -114,6 +115,8 @@
   void ResponseCompleted();
   void CallDidFinishLoading();
   void RecordHistograms();
+  void SetRawResponseHeaders(
+      scoped_refptr<const net::HttpResponseHeaders> headers);
 
   bool is_deferred() const { return deferred_stage_ != DEFERRED_NONE; }
 
@@ -180,6 +183,7 @@
   int read_buffer_size_;
 
   net::HttpRawRequestHeaders raw_request_headers_;
+  scoped_refptr<const net::HttpResponseHeaders> raw_response_headers_;
 
   base::ThreadChecker thread_checker_;
 
diff --git a/content/common/loader_util.cc b/content/common/loader_util.cc
index 27170c67..0886c2b 100644
--- a/content/common/loader_util.cc
+++ b/content/common/loader_util.cc
@@ -41,7 +41,8 @@
 
 scoped_refptr<ResourceDevToolsInfo> BuildDevToolsInfo(
     const net::URLRequest& request,
-    const net::HttpRawRequestHeaders& raw_request_headers) {
+    const net::HttpRawRequestHeaders& raw_request_headers,
+    const net::HttpResponseHeaders* raw_response_headers) {
   scoped_refptr<ResourceDevToolsInfo> info = new ResourceDevToolsInfo();
 
   const net::HttpResponseInfo& response_info = request.response_info();
@@ -65,21 +66,22 @@
     info->request_headers_text = std::move(text);
   }
 
-  const net::HttpResponseHeaders* response_headers = request.response_headers();
-  if (response_headers) {
-    info->http_status_code = response_headers->response_code();
-    info->http_status_text = response_headers->GetStatusText();
+  if (!raw_response_headers)
+    raw_response_headers = request.response_headers();
+  if (raw_response_headers) {
+    info->http_status_code = raw_response_headers->response_code();
+    info->http_status_text = raw_response_headers->GetStatusText();
 
     std::string name;
     std::string value;
     for (size_t it = 0;
-         response_headers->EnumerateHeaderLines(&it, &name, &value);) {
+         raw_response_headers->EnumerateHeaderLines(&it, &name, &value);) {
       info->response_headers.push_back(std::make_pair(name, value));
     }
     if (report_headers_text) {
       info->response_headers_text =
           net::HttpUtil::ConvertHeadersBackToHTTPResponse(
-              response_headers->raw_headers());
+              raw_response_headers->raw_headers());
     }
   }
   return info;
diff --git a/content/common/loader_util.h b/content/common/loader_util.h
index 99e74664..8f232c3 100644
--- a/content/common/loader_util.h
+++ b/content/common/loader_util.h
@@ -9,6 +9,7 @@
 
 namespace net {
 class HttpRawRequestHeaders;
+class HttpResponseHeaders;
 class URLRequest;
 }
 
@@ -27,7 +28,8 @@
 // Fill ResourceDevToolsInfo based on raw headers.
 scoped_refptr<ResourceDevToolsInfo> BuildDevToolsInfo(
     const net::URLRequest& request,
-    const net::HttpRawRequestHeaders& raw_request_headers);
+    const net::HttpRawRequestHeaders& raw_request_headers,
+    const net::HttpResponseHeaders* raw_response_headers);
 
 }  // namespace content
 
diff --git a/content/network/url_loader_impl.cc b/content/network/url_loader_impl.cc
index 61254bb..40e38e56 100644
--- a/content/network/url_loader_impl.cc
+++ b/content/network/url_loader_impl.cc
@@ -211,6 +211,8 @@
     url_request_->SetRequestHeadersCallback(
         base::Bind(&net::HttpRawRequestHeaders::Assign,
                    base::Unretained(&raw_request_headers_)));
+    url_request_->SetResponseHeadersCallback(base::Bind(
+        &URLLoaderImpl::SetRawResponseHeaders, base::Unretained(this)));
   }
   url_request_->Start();
 }
@@ -253,8 +255,10 @@
   scoped_refptr<ResourceResponse> response = new ResourceResponse();
   PopulateResourceResponse(url_request_.get(), response.get());
   if (report_raw_headers_) {
-    response->head.devtools_info =
-        BuildDevToolsInfo(*url_request_, raw_request_headers_);
+    response->head.devtools_info = BuildDevToolsInfo(
+        *url_request_, raw_request_headers_, raw_response_headers_.get());
+    raw_request_headers_ = net::HttpRawRequestHeaders();
+    raw_response_headers_ = nullptr;
   }
   url_loader_client_->OnReceiveRedirect(redirect_info, response->head);
 }
@@ -272,8 +276,10 @@
   response_ = new ResourceResponse();
   PopulateResourceResponse(url_request_.get(), response_.get());
   if (report_raw_headers_) {
-    response_->head.devtools_info =
-        BuildDevToolsInfo(*url_request_, raw_request_headers_);
+    response_->head.devtools_info = BuildDevToolsInfo(
+        *url_request_, raw_request_headers_, raw_response_headers_.get());
+    raw_request_headers_ = net::HttpRawRequestHeaders();
+    raw_response_headers_ = nullptr;
   }
 
   mojo::DataPipe data_pipe(kDefaultAllocationSize);
@@ -481,4 +487,9 @@
   total_written_bytes_ += pending_write_buffer_offset_;
 }
 
+void URLLoaderImpl::SetRawResponseHeaders(
+    scoped_refptr<const net::HttpResponseHeaders> headers) {
+  raw_response_headers_ = headers;
+}
+
 }  // namespace content
diff --git a/content/network/url_loader_impl.h b/content/network/url_loader_impl.h
index c9f6677..c4e0bc5 100644
--- a/content/network/url_loader_impl.h
+++ b/content/network/url_loader_impl.h
@@ -19,6 +19,10 @@
 #include "net/traffic_annotation/network_traffic_annotation.h"
 #include "net/url_request/url_request.h"
 
+namespace net {
+class HttpResponseHeaders;
+}
+
 namespace network {
 class NetToMojoPendingBuffer;
 }
@@ -67,6 +71,7 @@
   void DeleteIfNeeded();
   void SendResponseToClient();
   void CompletePendingWrite();
+  void SetRawResponseHeaders(scoped_refptr<const net::HttpResponseHeaders>);
 
   NetworkContext* context_;
   int32_t options_;
@@ -90,6 +95,7 @@
 
   bool report_raw_headers_;
   net::HttpRawRequestHeaders raw_request_headers_;
+  scoped_refptr<const net::HttpResponseHeaders> raw_response_headers_;
 
   base::WeakPtrFactory<URLLoaderImpl> weak_ptr_factory_;
 
diff --git a/net/http/failing_http_transaction_factory.cc b/net/http/failing_http_transaction_factory.cc
index 18c757fd..d6202bf 100644
--- a/net/http/failing_http_transaction_factory.cc
+++ b/net/http/failing_http_transaction_factory.cc
@@ -71,6 +71,7 @@
   int ResumeNetworkStart() override;
   void GetConnectionAttempts(ConnectionAttempts* out) const override;
   void SetRequestHeadersCallback(RequestHeadersCallback) override {}
+  void SetResponseHeadersCallback(ResponseHeadersCallback) override {}
 
  private:
   Error error_;
diff --git a/net/http/http_cache_transaction.cc b/net/http/http_cache_transaction.cc
index 1439c700..8912d2e 100644
--- a/net/http/http_cache_transaction.cc
+++ b/net/http/http_cache_transaction.cc
@@ -555,6 +555,12 @@
   request_headers_callback_ = std::move(callback);
 }
 
+void HttpCache::Transaction::SetResponseHeadersCallback(
+    ResponseHeadersCallback callback) {
+  DCHECK(!network_trans_);
+  response_headers_callback_ = std::move(callback);
+}
+
 int HttpCache::Transaction::ResumeNetworkStart() {
   if (network_trans_)
     return network_trans_->ResumeNetworkStart();
@@ -1501,6 +1507,7 @@
   network_trans_->SetBeforeNetworkStartCallback(before_network_start_callback_);
   network_trans_->SetBeforeHeadersSentCallback(before_headers_sent_callback_);
   network_trans_->SetRequestHeadersCallback(request_headers_callback_);
+  network_trans_->SetResponseHeadersCallback(response_headers_callback_);
 
   // Old load timing information, if any, is now obsolete.
   old_network_trans_load_timing_.reset();
diff --git a/net/http/http_cache_transaction.h b/net/http/http_cache_transaction.h
index 6357334..8c709a754 100644
--- a/net/http/http_cache_transaction.h
+++ b/net/http/http_cache_transaction.h
@@ -169,7 +169,8 @@
       const BeforeNetworkStartCallback& callback) override;
   void SetBeforeHeadersSentCallback(
       const BeforeHeadersSentCallback& callback) override;
-  void SetRequestHeadersCallback(RequestHeadersCallback) override;
+  void SetRequestHeadersCallback(RequestHeadersCallback callback) override;
+  void SetResponseHeadersCallback(ResponseHeadersCallback callback) override;
   int ResumeNetworkStart() override;
   void GetConnectionAttempts(ConnectionAttempts* out) const override;
 
@@ -569,6 +570,7 @@
   BeforeNetworkStartCallback before_network_start_callback_;
   BeforeHeadersSentCallback before_headers_sent_callback_;
   RequestHeadersCallback request_headers_callback_;
+  ResponseHeadersCallback response_headers_callback_;
 
   // True if the Transaction is currently processing the DoLoop.
   bool in_do_loop_;
diff --git a/net/http/http_network_transaction.cc b/net/http/http_network_transaction.cc
index 4f0deafa4d..2290f32 100644
--- a/net/http/http_network_transaction.cc
+++ b/net/http/http_network_transaction.cc
@@ -457,6 +457,12 @@
   request_headers_callback_ = std::move(callback);
 }
 
+void HttpNetworkTransaction::SetResponseHeadersCallback(
+    ResponseHeadersCallback callback) {
+  DCHECK(!stream_);
+  response_headers_callback_ = std::move(callback);
+}
+
 int HttpNetworkTransaction::ResumeNetworkStart() {
   DCHECK_EQ(next_state_, STATE_CREATE_STREAM);
   return DoLoop(OK);
@@ -1280,6 +1286,8 @@
   net_log_.AddEvent(
       NetLogEventType::HTTP_TRANSACTION_READ_RESPONSE_HEADERS,
       base::Bind(&HttpResponseHeaders::NetLogCallback, response_.headers));
+  if (response_headers_callback_)
+    response_headers_callback_.Run(response_.headers);
 
   if (response_.headers->GetHttpVersion() < HttpVersion(1, 0)) {
     // HTTP/0.9 doesn't support the PUT method, so lack of response headers
diff --git a/net/http/http_network_transaction.h b/net/http/http_network_transaction.h
index 735e3d2..866b5a2 100644
--- a/net/http/http_network_transaction.h
+++ b/net/http/http_network_transaction.h
@@ -90,7 +90,8 @@
       const BeforeNetworkStartCallback& callback) override;
   void SetBeforeHeadersSentCallback(
       const BeforeHeadersSentCallback& callback) override;
-  void SetRequestHeadersCallback(RequestHeadersCallback) override;
+  void SetRequestHeadersCallback(RequestHeadersCallback callback) override;
+  void SetResponseHeadersCallback(ResponseHeadersCallback callback) override;
 
   int ResumeNetworkStart() override;
 
@@ -398,6 +399,7 @@
   BeforeNetworkStartCallback before_network_start_callback_;
   BeforeHeadersSentCallback before_headers_sent_callback_;
   RequestHeadersCallback request_headers_callback_;
+  ResponseHeadersCallback response_headers_callback_;
 
   ConnectionAttempts connection_attempts_;
   IPEndPoint remote_endpoint_;
diff --git a/net/http/http_response_headers.h b/net/http/http_response_headers.h
index ea27a47..839dd75d8 100644
--- a/net/http/http_response_headers.h
+++ b/net/http/http_response_headers.h
@@ -390,6 +390,9 @@
   DISALLOW_COPY_AND_ASSIGN(HttpResponseHeaders);
 };
 
+using ResponseHeadersCallback =
+    base::Callback<void(scoped_refptr<const HttpResponseHeaders>)>;
+
 }  // namespace net
 
 #endif  // NET_HTTP_HTTP_RESPONSE_HEADERS_H_
diff --git a/net/http/http_transaction.h b/net/http/http_transaction.h
index 28fe77e..a921206 100644
--- a/net/http/http_transaction.h
+++ b/net/http/http_transaction.h
@@ -14,6 +14,7 @@
 #include "net/base/request_priority.h"
 #include "net/base/upload_progress.h"
 #include "net/http/http_raw_request_headers.h"
+#include "net/http/http_response_headers.h"
 #include "net/socket/connection_attempts.h"
 #include "net/websockets/websocket_handshake_stream_base.h"
 
@@ -193,7 +194,8 @@
   virtual void SetBeforeHeadersSentCallback(
       const BeforeHeadersSentCallback& callback) = 0;
 
-  virtual void SetRequestHeadersCallback(RequestHeadersCallback) = 0;
+  virtual void SetRequestHeadersCallback(RequestHeadersCallback callback) = 0;
+  virtual void SetResponseHeadersCallback(ResponseHeadersCallback callback) = 0;
 
   // Resumes the transaction after being deferred.
   virtual int ResumeNetworkStart() = 0;
diff --git a/net/http/http_transaction_test_util.h b/net/http/http_transaction_test_util.h
index 419719a..bc834575 100644
--- a/net/http/http_transaction_test_util.h
+++ b/net/http/http_transaction_test_util.h
@@ -238,6 +238,7 @@
       const BeforeHeadersSentCallback& callback) override;
 
   void SetRequestHeadersCallback(RequestHeadersCallback callback) override {}
+  void SetResponseHeadersCallback(ResponseHeadersCallback) override {}
 
   int ResumeNetworkStart() override;
 
diff --git a/net/url_request/url_request.cc b/net/url_request/url_request.cc
index 5ccc821..a66d192b 100644
--- a/net/url_request/url_request.cc
+++ b/net/url_request/url_request.cc
@@ -28,7 +28,6 @@
 #include "net/base/network_change_notifier.h"
 #include "net/base/network_delegate.h"
 #include "net/base/upload_data_stream.h"
-#include "net/http/http_response_headers.h"
 #include "net/http/http_util.h"
 #include "net/log/net_log.h"
 #include "net/log/net_log_event_type.h"
@@ -646,6 +645,7 @@
   job_->SetExtraRequestHeaders(extra_request_headers_);
   job_->SetPriority(priority_);
   job_->SetRequestHeadersCallback(request_headers_callback_);
+  job_->SetResponseHeadersCallback(response_headers_callback_);
 
   if (upload_data_stream_.get())
     job_->SetUpload(upload_data_stream_.get());
@@ -1220,6 +1220,12 @@
   request_headers_callback_ = std::move(callback);
 }
 
+void URLRequest::SetResponseHeadersCallback(ResponseHeadersCallback callback) {
+  DCHECK(!job_.get());
+  DCHECK(response_headers_callback_.is_null());
+  response_headers_callback_ = std::move(callback);
+}
+
 void URLRequest::set_status(URLRequestStatus status) {
   DCHECK(status_.is_io_pending() || status_.is_success() ||
          (!status.is_success() && !status.is_io_pending()));
diff --git a/net/url_request/url_request.h b/net/url_request/url_request.h
index 603c4bd..ae6bba1 100644
--- a/net/url_request/url_request.h
+++ b/net/url_request/url_request.h
@@ -31,6 +31,7 @@
 #include "net/cookies/canonical_cookie.h"
 #include "net/http/http_raw_request_headers.h"
 #include "net/http/http_request_headers.h"
+#include "net/http/http_response_headers.h"
 #include "net/http/http_response_info.h"
 #include "net/log/net_log_with_source.h"
 #include "net/proxy/proxy_server.h"
@@ -673,6 +674,13 @@
   // before the request is started.
   void SetRequestHeadersCallback(RequestHeadersCallback callback);
 
+  // Sets a callback that will be invoked each time the response is received
+  // from the remote party with the actual response headers recieved. Note this
+  // is different from response_headers() getter in that in case of revalidation
+  // request, the latter will return cached headers, while the callback will be
+  // called with a response from the server.
+  void SetResponseHeadersCallback(ResponseHeadersCallback callback);
+
  protected:
   // Allow the URLRequestJob class to control the is_pending() flag.
   void set_is_pending(bool value) { is_pending_ = value; }
@@ -877,8 +885,9 @@
 
   const NetworkTrafficAnnotationTag traffic_annotation_;
 
-  // See SetRequestHeadersCallback() above for details.
+  // See Set{Request|Response}HeadersCallback() above for details.
   RequestHeadersCallback request_headers_callback_;
+  ResponseHeadersCallback response_headers_callback_;
 
   THREAD_CHECKER(thread_checker_);
 
diff --git a/net/url_request/url_request_http_job.cc b/net/url_request/url_request_http_job.cc
index 7a61c851..6b115afc 100644
--- a/net/url_request/url_request_http_job.cc
+++ b/net/url_request/url_request_http_job.cc
@@ -576,6 +576,7 @@
           base::Bind(&URLRequestHttpJob::NotifyBeforeSendHeadersCallback,
                      base::Unretained(this)));
       transaction_->SetRequestHeadersCallback(request_headers_callback_);
+      transaction_->SetResponseHeadersCallback(response_headers_callback_);
 
       if (!throttling_entry_.get() ||
           !throttling_entry_->ShouldRejectRequest(*request_)) {
@@ -1558,6 +1559,13 @@
   request_headers_callback_ = std::move(callback);
 }
 
+void URLRequestHttpJob::SetResponseHeadersCallback(
+    ResponseHeadersCallback callback) {
+  DCHECK(!transaction_);
+  DCHECK(!response_headers_callback_);
+  response_headers_callback_ = std::move(callback);
+}
+
 void URLRequestHttpJob::RecordPerfHistograms(CompletionCause reason) {
   if (start_time_.is_null())
     return;
diff --git a/net/url_request/url_request_http_job.h b/net/url_request/url_request_http_job.h
index 989c5c8f..55101ce 100644
--- a/net/url_request/url_request_http_job.h
+++ b/net/url_request/url_request_http_job.h
@@ -50,6 +50,7 @@
   // access it.
   void RecordPacketStats(SdchPolicyDelegate::StatisticSelector statistic) const;
   void SetRequestHeadersCallback(RequestHeadersCallback callback) override;
+  void SetResponseHeadersCallback(ResponseHeadersCallback callback) override;
 
  protected:
   URLRequestHttpJob(URLRequest* request,
@@ -271,6 +272,7 @@
   int64_t total_sent_bytes_from_previous_transactions_;
 
   RequestHeadersCallback request_headers_callback_;
+  ResponseHeadersCallback response_headers_callback_;
 
   base::WeakPtrFactory<URLRequestHttpJob> weak_factory_;
 
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc
index d7feb59..9112c44 100644
--- a/net/url_request/url_request_job.cc
+++ b/net/url_request/url_request_job.cc
@@ -27,7 +27,6 @@
 #include "net/base/load_states.h"
 #include "net/base/net_errors.h"
 #include "net/base/network_delegate.h"
-#include "net/http/http_response_headers.h"
 #include "net/log/net_log.h"
 #include "net/log/net_log_capture_mode.h"
 #include "net/log/net_log_event_type.h"
@@ -382,10 +381,6 @@
   out->clear();
 }
 
-void URLRequestJob::SetRequestHeadersCallback(RequestHeadersCallback callback) {
-  request_headers_callback_ = std::move(callback);
-}
-
 // static
 GURL URLRequestJob::ComputeReferrerForPolicy(URLRequest::ReferrerPolicy policy,
                                              const GURL& original_referrer,
diff --git a/net/url_request/url_request_job.h b/net/url_request/url_request_job.h
index a844f74..aaed867 100644
--- a/net/url_request/url_request_job.h
+++ b/net/url_request/url_request_job.h
@@ -22,6 +22,7 @@
 #include "net/cookies/canonical_cookie.h"
 #include "net/filter/source_stream.h"
 #include "net/http/http_raw_request_headers.h"
+#include "net/http/http_response_headers.h"
 #include "net/socket/connection_attempts.h"
 #include "net/url_request/redirect_info.h"
 #include "net/url_request/url_request.h"
@@ -234,7 +235,11 @@
   // be actually sent and will receive actual request headers that are about
   // to hit the wire, including SPDY/QUIC internal headers and any additional
   // request headers set via BeforeSendHeaders hooks.
-  virtual void SetRequestHeadersCallback(RequestHeadersCallback callback);
+  virtual void SetRequestHeadersCallback(RequestHeadersCallback callback) {}
+
+  // Sets a callback that will be invoked each time the response is received
+  // from the remote party with the actual response headers recieved.
+  virtual void SetResponseHeadersCallback(ResponseHeadersCallback callback) {}
 
   // Given |policy|, |referrer|, and |destination|, returns the
   // referrer URL mandated by |request|'s referrer policy.
@@ -450,9 +455,6 @@
   // completed.
   CompletionCallback read_raw_callback_;
 
-  // See SetRequestHeadersCallback() above for details.
-  RequestHeadersCallback request_headers_callback_;
-
   base::WeakPtrFactory<URLRequestJob> weak_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(URLRequestJob);
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 0794a3a..937bae4 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -11716,7 +11716,7 @@
   EXPECT_EQ(0, d.received_redirect_count());
 }
 
-TEST_F(URLRequestTestHTTP, RequestHeadersCallback) {
+TEST_F(URLRequestTestHTTP, HeadersCallbacks) {
   ASSERT_TRUE(http_test_server()->Start());
   TestURLRequestContext context;
   GURL url(http_test_server()->GetURL("/cachetime"));
@@ -11725,44 +11725,54 @@
   extra_headers.SetHeader("X-Foo", "bar");
 
   {
-    HttpRawRequestHeaders raw_headers;
+    HttpRawRequestHeaders raw_req_headers;
+    scoped_refptr<const HttpResponseHeaders> raw_resp_headers;
+
     std::unique_ptr<URLRequest> r(context.CreateRequest(
         url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
     r->SetExtraRequestHeaders(extra_headers);
-    r->SetRequestHeadersCallback(base::Bind(&HttpRawRequestHeaders::Assign,
-                                            base::Unretained(&raw_headers)));
+    r->SetRequestHeadersCallback(base::Bind(
+        &HttpRawRequestHeaders::Assign, base::Unretained(&raw_req_headers)));
+    r->SetResponseHeadersCallback(base::Bind(
+        [](scoped_refptr<const HttpResponseHeaders>* left,
+           scoped_refptr<const HttpResponseHeaders> right) { *left = right; },
+        base::Unretained(&raw_resp_headers)));
     r->Start();
     while (!delegate.response_started_count())
       base::RunLoop().RunUntilIdle();
-    EXPECT_FALSE(raw_headers.headers().empty());
+    EXPECT_FALSE(raw_req_headers.headers().empty());
     std::string value;
-    EXPECT_TRUE(raw_headers.FindHeaderForTest("X-Foo", &value));
+    EXPECT_TRUE(raw_req_headers.FindHeaderForTest("X-Foo", &value));
     EXPECT_EQ("bar", value);
-    EXPECT_TRUE(raw_headers.FindHeaderForTest("Accept-Encoding", &value));
+    EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Accept-Encoding", &value));
     EXPECT_EQ("gzip, deflate", value);
-    EXPECT_TRUE(raw_headers.FindHeaderForTest("Connection", &value));
-    EXPECT_TRUE(raw_headers.FindHeaderForTest("Host", &value));
-    EXPECT_EQ("GET /cachetime HTTP/1.1\r\n", raw_headers.request_line());
+    EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Connection", &value));
+    EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Host", &value));
+    EXPECT_EQ("GET /cachetime HTTP/1.1\r\n", raw_req_headers.request_line());
+    EXPECT_EQ(raw_resp_headers.get(), r->response_headers());
   }
   {
-    HttpRawRequestHeaders raw_headers;
     std::unique_ptr<URLRequest> r(context.CreateRequest(
         url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
     r->SetExtraRequestHeaders(extra_headers);
-    r->SetRequestHeadersCallback(base::Bind([](net::HttpRawRequestHeaders) {
+    r->SetRequestHeadersCallback(base::Bind([](HttpRawRequestHeaders) {
       FAIL() << "Callback should not be called unless request is sent";
     }));
+    r->SetResponseHeadersCallback(
+        base::Bind([](scoped_refptr<const HttpResponseHeaders>) {
+          FAIL() << "Callback should not be called unless request is sent";
+        }));
     r->Start();
     base::RunLoop().Run();
     EXPECT_TRUE(r->was_cached());
-    EXPECT_TRUE(raw_headers.headers().empty());
-    EXPECT_TRUE(raw_headers.request_line().empty());
   }
 }
 
-TEST_F(URLRequestTestHTTP, RequestHeadersCallbackWithRedirect) {
+TEST_F(URLRequestTestHTTP, HeadersCallbacksWithRedirect) {
   ASSERT_TRUE(http_test_server()->Start());
-  HttpRawRequestHeaders raw_headers;
+  HttpRawRequestHeaders raw_req_headers;
+  scoped_refptr<const HttpResponseHeaders> raw_resp_headers;
+
   TestURLRequestContext context;
   TestDelegate delegate;
   HttpRequestHeaders extra_headers;
@@ -11773,30 +11783,41 @@
       url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
   r->SetExtraRequestHeaders(extra_headers);
   r->SetRequestHeadersCallback(base::Bind(&HttpRawRequestHeaders::Assign,
-                                          base::Unretained(&raw_headers)));
+                                          base::Unretained(&raw_req_headers)));
+  r->SetResponseHeadersCallback(base::Bind(
+      [](scoped_refptr<const HttpResponseHeaders>* left,
+         scoped_refptr<const HttpResponseHeaders> right) { *left = right; },
+      base::Unretained(&raw_resp_headers)));
   r->Start();
   base::RunLoop().Run();
 
   ASSERT_EQ(1, delegate.received_redirect_count());
   std::string value;
-  EXPECT_TRUE(raw_headers.FindHeaderForTest("X-Foo", &value));
+  EXPECT_TRUE(raw_req_headers.FindHeaderForTest("X-Foo", &value));
   EXPECT_EQ("bar", value);
-  EXPECT_TRUE(raw_headers.FindHeaderForTest("Accept-Encoding", &value));
+  EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Accept-Encoding", &value));
   EXPECT_EQ("gzip, deflate", value);
   EXPECT_EQ(1, delegate.received_redirect_count());
-  EXPECT_EQ("GET /redirect-test.html HTTP/1.1\r\n", raw_headers.request_line());
+  EXPECT_EQ("GET /redirect-test.html HTTP/1.1\r\n",
+            raw_req_headers.request_line());
+  EXPECT_TRUE(raw_resp_headers->HasHeader("Location"));
+  EXPECT_EQ(302, raw_resp_headers->response_code());
+  EXPECT_EQ("Redirect", raw_resp_headers->GetStatusText());
 
-  raw_headers = HttpRawRequestHeaders();
+  raw_req_headers = HttpRawRequestHeaders();
+  raw_resp_headers = nullptr;
   r->FollowDeferredRedirect();
   base::RunLoop().Run();
-  EXPECT_TRUE(raw_headers.FindHeaderForTest("X-Foo", &value));
+  EXPECT_TRUE(raw_req_headers.FindHeaderForTest("X-Foo", &value));
   EXPECT_EQ("bar", value);
-  EXPECT_TRUE(raw_headers.FindHeaderForTest("Accept-Encoding", &value));
+  EXPECT_TRUE(raw_req_headers.FindHeaderForTest("Accept-Encoding", &value));
   EXPECT_EQ("gzip, deflate", value);
-  EXPECT_EQ("GET /with-headers.html HTTP/1.1\r\n", raw_headers.request_line());
+  EXPECT_EQ("GET /with-headers.html HTTP/1.1\r\n",
+            raw_req_headers.request_line());
+  EXPECT_EQ(r->response_headers(), raw_resp_headers.get());
 }
 
-TEST_F(URLRequestTest, RequestHeadersCallbackConnectFailed) {
+TEST_F(URLRequestTest, HeadersCallbacksConnectFailed) {
   TestDelegate request_delegate;
 
   std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
@@ -11805,12 +11826,16 @@
   r->SetRequestHeadersCallback(base::Bind([](net::HttpRawRequestHeaders) {
     FAIL() << "Callback should not be called unless request is sent";
   }));
+  r->SetResponseHeadersCallback(
+      base::Bind([](scoped_refptr<const net::HttpResponseHeaders>) {
+        FAIL() << "Callback should not be called unless request is sent";
+      }));
   r->Start();
   base::RunLoop().Run();
   EXPECT_FALSE(r->is_pending());
 }
 
-TEST_F(URLRequestTestHTTP, RequestHeadersCallbackAuthRetry) {
+TEST_F(URLRequestTestHTTP, HeadersCallbacksAuthRetry) {
   ASSERT_TRUE(http_test_server()->Start());
   GURL url(http_test_server()->GetURL("/auth-basic"));
 
@@ -11821,38 +11846,75 @@
   HttpRequestHeaders extra_headers;
   extra_headers.SetHeader("X-Foo", "bar");
 
-  using HeadersVector = std::vector<std::unique_ptr<HttpRawRequestHeaders>>;
-  HeadersVector raw_headers;
+  using ReqHeadersVector = std::vector<std::unique_ptr<HttpRawRequestHeaders>>;
+  ReqHeadersVector raw_req_headers;
 
+  using RespHeadersVector =
+      std::vector<scoped_refptr<const HttpResponseHeaders>>;
+  RespHeadersVector raw_resp_headers;
+
+  auto req_headers_callback = base::Bind(
+      [](ReqHeadersVector* vec, HttpRawRequestHeaders headers) {
+        vec->emplace_back(new HttpRawRequestHeaders(std::move(headers)));
+      },
+      &raw_req_headers);
+  auto resp_headers_callback = base::Bind(
+      [](RespHeadersVector* vec,
+         scoped_refptr<const HttpResponseHeaders> headers) {
+        vec->push_back(headers);
+      },
+      &raw_resp_headers);
   std::unique_ptr<URLRequest> r(context.CreateRequest(
       url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
   r->SetExtraRequestHeaders(extra_headers);
-  r->SetRequestHeadersCallback(base::Bind(
-      [](HeadersVector* vec, HttpRawRequestHeaders headers) {
-        vec->emplace_back(new HttpRawRequestHeaders(std::move(headers)));
-      },
-      &raw_headers));
+  r->SetRequestHeadersCallback(req_headers_callback);
+  r->SetResponseHeadersCallback(resp_headers_callback);
   r->Start();
   base::RunLoop().Run();
   EXPECT_FALSE(r->is_pending());
-  ASSERT_GE(raw_headers.size(), 2u);
+  ASSERT_EQ(raw_req_headers.size(), 2u);
+  ASSERT_EQ(raw_resp_headers.size(), 2u);
   std::string value;
-  EXPECT_FALSE(raw_headers[0]->FindHeaderForTest("Authorization", &value));
-  EXPECT_TRUE(raw_headers[0]->FindHeaderForTest("X-Foo", &value));
+  EXPECT_FALSE(raw_req_headers[0]->FindHeaderForTest("Authorization", &value));
+  EXPECT_TRUE(raw_req_headers[0]->FindHeaderForTest("X-Foo", &value));
   EXPECT_EQ("bar", value);
-  EXPECT_TRUE(raw_headers[1]->FindHeaderForTest("Authorization", &value));
-  EXPECT_TRUE(raw_headers[1]->FindHeaderForTest("X-Foo", &value));
+  EXPECT_TRUE(raw_req_headers[1]->FindHeaderForTest("Authorization", &value));
+  EXPECT_TRUE(raw_req_headers[1]->FindHeaderForTest("X-Foo", &value));
   EXPECT_EQ("bar", value);
+  EXPECT_EQ(raw_resp_headers[1], r->response_headers());
+  EXPECT_NE(raw_resp_headers[0], raw_resp_headers[1]);
+  EXPECT_EQ(401, raw_resp_headers[0]->response_code());
+  EXPECT_EQ("Unauthorized", raw_resp_headers[0]->GetStatusText());
+
+  std::unique_ptr<URLRequest> r2(context.CreateRequest(
+      url, DEFAULT_PRIORITY, &delegate, TRAFFIC_ANNOTATION_FOR_TESTS));
+  r2->SetExtraRequestHeaders(extra_headers);
+  r2->SetRequestHeadersCallback(req_headers_callback);
+  r2->SetResponseHeadersCallback(resp_headers_callback);
+  r2->SetLoadFlags(LOAD_VALIDATE_CACHE);
+  r2->Start();
+  base::RunLoop().Run();
+  EXPECT_FALSE(r2->is_pending());
+  ASSERT_EQ(raw_req_headers.size(), 3u);
+  ASSERT_EQ(raw_resp_headers.size(), 3u);
+  EXPECT_TRUE(raw_req_headers[2]->FindHeaderForTest("If-None-Match", &value));
+  EXPECT_NE(raw_resp_headers[2].get(), r2->response_headers());
+  EXPECT_EQ(304, raw_resp_headers[2]->response_code());
+  EXPECT_EQ("Not Modified", raw_resp_headers[2]->GetStatusText());
 }
 
-TEST_F(URLRequestTest, RequestHeadersCallbackNonHTTP) {
+TEST_F(URLRequestTest, HeadersCallbacksNonHTTP) {
   GURL data_url("data:text/html,<html><body>Hello!</body></html>");
   TestDelegate d;
   std::unique_ptr<URLRequest> r(default_context_.CreateRequest(
       data_url, DEFAULT_PRIORITY, &d, TRAFFIC_ANNOTATION_FOR_TESTS));
   r->SetRequestHeadersCallback(base::Bind([](net::HttpRawRequestHeaders) {
-    FAIL() << "Callback should not be called unless request is sent";
+    FAIL() << "Callback should not be called for non-HTTP schemes";
   }));
+  r->SetResponseHeadersCallback(
+      base::Bind([](scoped_refptr<const net::HttpResponseHeaders>) {
+        FAIL() << "Callback should not be called for non-HTTP schemes";
+      }));
   r->Start();
   base::RunLoop().Run();
   EXPECT_FALSE(r->is_pending());
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/raw-response-headers-expected.txt b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/raw-response-headers-expected.txt
new file mode 100644
index 0000000..fb71abb
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/raw-response-headers-expected.txt
@@ -0,0 +1,5 @@
+Tests that raw response headers are correctly reported in case of revalidation.
+Response status: 304 Not Modified
+Response headers text present: true
+Content-Length present: false
+
diff --git a/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/raw-response-headers.js b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/raw-response-headers.js
new file mode 100644
index 0000000..03aa16e
--- /dev/null
+++ b/third_party/WebKit/LayoutTests/http/tests/inspector-protocol/network/raw-response-headers.js
@@ -0,0 +1,16 @@
+(async function(testRunner) {
+  var {page, session, dp} = await testRunner.startURL(
+      '../resources/test-page.html',
+      `Tests that raw response headers are correctly reported in case of revalidation.`);
+
+  dp.Network.enable();
+  session.protocol.Page.reload();
+  const response = (await dp.Network.onceResponseReceived()).params.response;
+
+  const haveContentLength = Object.keys(response.headers).indexOf('Content-Length') >= 0;
+  testRunner.log(`Response status: ${response.status} ${response.statusText}`);
+  testRunner.log(`Response headers text present: ${!!response.headersText.length}`);
+  testRunner.log(`Content-Length present: ${haveContentLength}`);
+
+  testRunner.completeTest();
+})
\ No newline at end of file
diff --git a/third_party/WebKit/Source/devtools/front_end/Tests.js b/third_party/WebKit/Source/devtools/front_end/Tests.js
index 7d2ee70f..deeccf62 100644
--- a/third_party/WebKit/Source/devtools/front_end/Tests.js
+++ b/third_party/WebKit/Source/devtools/front_end/Tests.js
@@ -930,6 +930,50 @@
     }
   };
 
+  TestSuite.prototype.testRawHeadersWithHSTS = function(url) {
+    var test = this;
+    test.takeControl();
+    SDK.targetManager.addModelListener(
+        SDK.NetworkManager, SDK.NetworkManager.Events.ResponseReceived, onResponseReceived);
+
+    this.evaluateInConsole_(`
+      var img = document.createElement('img');
+      img.src = "${url}";
+      document.body.appendChild(img);
+    `, () => {});
+
+    var count = 0;
+    function onResponseReceived(event) {
+      var networkRequest = event.data;
+      if (!networkRequest.url().startsWith('http'))
+        return;
+      switch (++count) {
+        case 1:  // Original redirect
+          test.assertEquals(301, networkRequest.statusCode);
+          test.assertEquals('Moved Permanently', networkRequest.statusText);
+          test.assertTrue(url.endsWith(networkRequest.responseHeaderValue('Location')));
+          break;
+
+        case 2:  // HSTS internal redirect
+          test.assertTrue(networkRequest.url().startsWith('http://'));
+          test.assertEquals(undefined, networkRequest.requestHeadersText());
+          test.assertEquals(307, networkRequest.statusCode);
+          test.assertEquals('Internal Redirect', networkRequest.statusText);
+          test.assertEquals('HSTS', networkRequest.responseHeaderValue('Non-Authoritative-Reason'));
+          test.assertTrue(networkRequest.responseHeaderValue('Location').startsWith('https://'));
+          break;
+
+        case 3:  // Final response
+          test.assertTrue(networkRequest.url().startsWith('https://'));
+          test.assertTrue(networkRequest.requestHeaderValue('Referer').startsWith('http://127.0.0.1'));
+          test.assertEquals(200, networkRequest.statusCode);
+          test.assertEquals('OK', networkRequest.statusText);
+          test.assertEquals('132', networkRequest.responseHeaderValue('Content-Length'));
+          test.releaseControl();
+      }
+    }
+  };
+
   TestSuite.prototype.waitForTestResultsInConsole = function() {
     var messages = ConsoleModel.consoleModel.messages();
     for (var i = 0; i < messages.length; ++i) {