redirect_info.cc 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/url_request/redirect_info.h"
  5. #include "base/metrics/histogram_macros.h"
  6. #include "base/strings/string_split.h"
  7. #include "base/strings/string_util.h"
  8. #include "net/url_request/url_request_job.h"
  9. namespace net {
  10. namespace {
  11. std::string ComputeMethodForRedirect(const std::string& method,
  12. int http_status_code) {
  13. // For 303 redirects, all request methods except HEAD are converted to GET,
  14. // as per the latest httpbis draft. The draft also allows POST requests to
  15. // be converted to GETs when following 301/302 redirects, for historical
  16. // reasons. Most major browsers do this and so shall we. Both RFC 2616 and
  17. // the httpbis draft say to prompt the user to confirm the generation of new
  18. // requests, other than GET and HEAD requests, but IE omits these prompts and
  19. // so shall we.
  20. // See: https://tools.ietf.org/html/rfc7231#section-6.4
  21. if ((http_status_code == 303 && method != "HEAD") ||
  22. ((http_status_code == 301 || http_status_code == 302) &&
  23. method == "POST")) {
  24. return "GET";
  25. }
  26. return method;
  27. }
  28. // A redirect response can contain a Referrer-Policy header
  29. // (https://w3c.github.io/webappsec-referrer-policy/). This function checks for
  30. // a Referrer-Policy header, and parses it if present. Returns the referrer
  31. // policy that should be used for the request.
  32. ReferrerPolicy ProcessReferrerPolicyHeaderOnRedirect(
  33. ReferrerPolicy original_referrer_policy,
  34. const absl::optional<std::string>& referrer_policy_header) {
  35. ReferrerPolicy new_policy = original_referrer_policy;
  36. std::vector<base::StringPiece> policy_tokens;
  37. if (referrer_policy_header) {
  38. policy_tokens = base::SplitStringPiece(*referrer_policy_header, ",",
  39. base::TRIM_WHITESPACE,
  40. base::SPLIT_WANT_NONEMPTY);
  41. }
  42. // Per https://w3c.github.io/webappsec-referrer-policy/#unknown-policy-values,
  43. // use the last recognized policy value, and ignore unknown policies.
  44. for (const auto& token : policy_tokens) {
  45. if (base::CompareCaseInsensitiveASCII(token, "no-referrer") == 0) {
  46. new_policy = ReferrerPolicy::NO_REFERRER;
  47. continue;
  48. }
  49. if (base::CompareCaseInsensitiveASCII(token,
  50. "no-referrer-when-downgrade") == 0) {
  51. new_policy = ReferrerPolicy::CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
  52. continue;
  53. }
  54. if (base::CompareCaseInsensitiveASCII(token, "origin") == 0) {
  55. new_policy = ReferrerPolicy::ORIGIN;
  56. continue;
  57. }
  58. if (base::CompareCaseInsensitiveASCII(token, "origin-when-cross-origin") ==
  59. 0) {
  60. new_policy = ReferrerPolicy::ORIGIN_ONLY_ON_TRANSITION_CROSS_ORIGIN;
  61. continue;
  62. }
  63. if (base::CompareCaseInsensitiveASCII(token, "unsafe-url") == 0) {
  64. new_policy = ReferrerPolicy::NEVER_CLEAR;
  65. continue;
  66. }
  67. if (base::CompareCaseInsensitiveASCII(token, "same-origin") == 0) {
  68. new_policy = ReferrerPolicy::CLEAR_ON_TRANSITION_CROSS_ORIGIN;
  69. continue;
  70. }
  71. if (base::CompareCaseInsensitiveASCII(token, "strict-origin") == 0) {
  72. new_policy =
  73. ReferrerPolicy::ORIGIN_CLEAR_ON_TRANSITION_FROM_SECURE_TO_INSECURE;
  74. continue;
  75. }
  76. if (base::CompareCaseInsensitiveASCII(
  77. token, "strict-origin-when-cross-origin") == 0) {
  78. new_policy =
  79. ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
  80. continue;
  81. }
  82. }
  83. return new_policy;
  84. }
  85. } // namespace
  86. RedirectInfo::RedirectInfo() = default;
  87. RedirectInfo::RedirectInfo(const RedirectInfo& other) = default;
  88. RedirectInfo::~RedirectInfo() = default;
  89. RedirectInfo RedirectInfo::ComputeRedirectInfo(
  90. const std::string& original_method,
  91. const GURL& original_url,
  92. const SiteForCookies& original_site_for_cookies,
  93. RedirectInfo::FirstPartyURLPolicy original_first_party_url_policy,
  94. ReferrerPolicy original_referrer_policy,
  95. const std::string& original_referrer,
  96. int http_status_code,
  97. const GURL& new_location,
  98. const absl::optional<std::string>& referrer_policy_header,
  99. bool insecure_scheme_was_upgraded,
  100. bool copy_fragment,
  101. bool is_signed_exchange_fallback_redirect) {
  102. RedirectInfo redirect_info;
  103. redirect_info.status_code = http_status_code;
  104. // The request method may change, depending on the status code.
  105. redirect_info.new_method =
  106. ComputeMethodForRedirect(original_method, http_status_code);
  107. // Move the reference fragment of the old location to the new one if the
  108. // new one has none. This duplicates mozilla's behavior.
  109. if (original_url.is_valid() && original_url.has_ref() &&
  110. !new_location.has_ref() && copy_fragment) {
  111. GURL::Replacements replacements;
  112. // Reference the |ref| directly out of the original URL to avoid a
  113. // malloc.
  114. replacements.SetRefStr(original_url.ref_piece());
  115. redirect_info.new_url = new_location.ReplaceComponents(replacements);
  116. } else {
  117. redirect_info.new_url = new_location;
  118. }
  119. redirect_info.insecure_scheme_was_upgraded = insecure_scheme_was_upgraded;
  120. redirect_info.is_signed_exchange_fallback_redirect =
  121. is_signed_exchange_fallback_redirect;
  122. // Update the first-party URL if appropriate.
  123. if (original_first_party_url_policy ==
  124. FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT) {
  125. redirect_info.new_site_for_cookies =
  126. SiteForCookies::FromUrl(redirect_info.new_url);
  127. } else {
  128. redirect_info.new_site_for_cookies = original_site_for_cookies;
  129. }
  130. redirect_info.new_referrer_policy = ProcessReferrerPolicyHeaderOnRedirect(
  131. original_referrer_policy, referrer_policy_header);
  132. // Alter the referrer if redirecting cross-origin (especially HTTP->HTTPS).
  133. redirect_info.new_referrer =
  134. URLRequestJob::ComputeReferrerForPolicy(redirect_info.new_referrer_policy,
  135. GURL(original_referrer),
  136. redirect_info.new_url)
  137. .spec();
  138. return redirect_info;
  139. }
  140. } // namespace net