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