Index: net/url_request/url_request_job.cc |
diff --git a/net/url_request/url_request_job.cc b/net/url_request/url_request_job.cc |
index b9c8646f729e75e45b45d65b635d1f51e78da254..ceb0a508746bc455761c70bbe12c6fcdd9ec0dd9 100644 |
--- a/net/url_request/url_request_job.cc |
+++ b/net/url_request/url_request_job.cc |
@@ -14,6 +14,7 @@ |
#include "base/profiler/scoped_tracker.h" |
#include "base/single_thread_task_runner.h" |
#include "base/strings/string_number_conversions.h" |
+#include "base/strings/string_split.h" |
#include "base/strings/string_util.h" |
#include "base/threading/thread_task_runner_handle.h" |
#include "base/values.h" |
@@ -62,6 +63,104 @@ std::string ComputeMethodForRedirect(const std::string& method, |
return method; |
} |
+// Given a referrer policy |token| (from the list of policies defined in |
+// https://w3c.github.io/webappsec-referrer-policy/#referrer-policies), |
+// this function sets request->referrer_policy() to the appropriate |
+// URLRequest::ReferrerPolicy, and sets |new_referrer_source| to the |
+// value that should be used as input to ComputeReferrerForRedirect(). |
+// ComputeReferrerForRedirect() is responsible for applying the |
+// request's URLRequest::ReferrerPolicy to compute the final referrer. |
+// |
+// This function does not modify |request| or |new_referrer_source| if |
+// |token| does not match any policy. |
+// |
+// Note that |
+// https://w3c.github.io/webappsec-referrer-policy/#determine-policy-for-token |
+// defines legacy keywords "never", "default", and "always", which this |
+// function respects even though authors are discouraged from using them. |
+void GetReferrerPolicyFromToken(const std::string& token, |
+ const std::string& referrer_source, |
+ URLRequest* request, |
+ std::string* new_referrer_source) { |
+ // For a referrer policy of 'no-referrer', clear the referrer |
+ // source. With an empty referrer source, the request's referrer |
+ // policy doesn't really matter because the final referrer will always |
+ // be empty. |
+ if (base::CompareCaseInsensitiveASCII(token, "never") == 0 || |
+ base::CompareCaseInsensitiveASCII(token, "no-referrer") == 0) { |
+ request->set_referrer_policy(URLRequest::NEVER_CLEAR_REFERRER); |
estark
2016/06/25 14:42:15
Hrm. These set_referrer_policy() calls run afoul o
|
+ *new_referrer_source = std::string(); |
+ return; |
+ } |
+ |
+ // For a referrer policy of 'no-referrer-when-downgrade', setting the |
+ // URLRequest's ReferrerPolicy is sufficient; |
+ // ComputeReferrerForRedirect() will apply this policy to strip the |
+ // referrer if necessary. |
+ if (base::CompareCaseInsensitiveASCII(token, "default") == 0 || |
+ base::CompareCaseInsensitiveASCII(token, "no-referrer-when-downgrade") == |
+ 0) { |
+ request->set_referrer_policy( |
+ URLRequest::CLEAR_REFERRER_ON_TRANSITION_FROM_SECURE_TO_INSECURE); |
+ *new_referrer_source = referrer_source; |
+ return; |
+ } |
+ |
+ // For a referrer policy of 'origin', strip the referrer source to the |
+ // origin. After doing so, there is no need to modify the referrer |
+ // further, so set the policy to leave the referrer alone. |
+ if (base::CompareCaseInsensitiveASCII(token, "origin") == 0) { |
+ request->set_referrer_policy(URLRequest::NEVER_CLEAR_REFERRER); |
+ *new_referrer_source = GURL(referrer_source).GetOrigin().spec(); |
+ return; |
+ } |
+ |
+ // For a referrer policy of 'origin-when-cross-origin', setting the |
+ // URLRequest's ReferrerPolicy is sufficient; |
+ // ComputeReferrerForRedirect() will apply this policy to strip the |
+ // referrer if necessary. |
+ if (base::CompareCaseInsensitiveASCII(token, "origin-when-cross-origin") == |
+ 0) { |
+ request->set_referrer_policy( |
+ URLRequest::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN); |
+ *new_referrer_source = referrer_source; |
+ return; |
+ } |
+ |
+ // For a referrer policy of 'unsafe-url', leave the referrer source |
+ // untouched and set the policy so that ComputeReferrerForRedirect() |
+ // will not modify it. |
+ if (base::CompareCaseInsensitiveASCII(token, "always") == 0 || |
+ base::CompareCaseInsensitiveASCII(token, "unsafe-url") == 0) { |
+ request->set_referrer_policy(URLRequest::NEVER_CLEAR_REFERRER); |
+ *new_referrer_source = referrer_source; |
+ return; |
+ } |
+} |
+ |
+// A redirect response can contain a Referrer-Policy header |
+// (https://w3c.github.io/webappsec-referrer-policy/). This function |
+// checks for a Referrer-Policy header, and parses it if present. If the |
+// header parses to a valid policy, |request|'s referrer policy will be |
+// updated if necessary, and |new_referrer_source| will be set to the |
+// URL that should be used as what the spec calls the "referrerSource" |
+// -- that is, the referrer that will be used as input to |
+// ComputeReferrerForRedirect(). |
+void ProcessReferrerPolicyHeaderOnRedirect(URLRequest* request, |
+ const std::string& referrer_source, |
+ std::string* new_referrer_source) { |
+ std::string referrer_policy_header; |
+ request->GetResponseHeaderByName("Referrer-Policy", &referrer_policy_header); |
+ std::vector<std::string> policy_tokens = |
+ base::SplitString(referrer_policy_header, ",", base::TRIM_WHITESPACE, |
+ base::SPLIT_WANT_NONEMPTY); |
+ *new_referrer_source = referrer_source; |
+ for (const auto& token : policy_tokens) { |
+ GetReferrerPolicyFromToken(token, referrer_source, request, |
+ new_referrer_source); |
+ } |
+} |
+ |
} // namespace |
URLRequestJob::URLRequestJob(URLRequest* request, |
@@ -398,6 +497,7 @@ void URLRequestJob::NotifyHeadersComplete() { |
GURL new_location; |
int http_status_code; |
+ |
if (IsRedirectResponse(&new_location, &http_status_code)) { |
// Redirect response bodies are not read. Notify the transaction |
// so it does not treat being stopped as an error. |
@@ -944,11 +1044,22 @@ RedirectInfo URLRequestJob::ComputeRedirectInfo(const GURL& location, |
request_->first_party_for_cookies(); |
} |
+ // |referrer_source| is the starting-point referrer, which may be |
+ // modified by ComputeReferrerForRedirect() to produce a new referrer |
+ // that obeys the request's referrer policy. |
+ std::string referrer_source; |
+ if (request_->context()->enable_referrer_policy_header()) { |
+ ProcessReferrerPolicyHeaderOnRedirect(request_, request_->referrer(), |
+ &referrer_source); |
+ } else { |
+ referrer_source = request_->referrer(); |
+ } |
+ |
// Alter the referrer if redirecting cross-origin (especially HTTP->HTTPS). |
redirect_info.new_referrer = |
- ComputeReferrerForRedirect(request_->referrer_policy(), |
- request_->referrer(), |
- redirect_info.new_url).spec(); |
+ ComputeReferrerForRedirect(request_->referrer_policy(), referrer_source, |
+ redirect_info.new_url) |
+ .spec(); |
std::string include_referer; |
request_->GetResponseHeaderByName("include-referer-token-binding-id", |