• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
2 // Copyright (c) 2018 The Chromium Authors. All rights reserved. Use of this
3 // source code is governed by a BSD-style license that can be found in the
4 // LICENSE file.
5 
6 #include "libcef/common/net_service/net_service_util.h"
7 
8 #include "libcef/common/time_util.h"
9 
10 #include <set>
11 
12 #include "base/logging.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "net/cookies/canonical_cookie.h"
15 #include "net/cookies/cookie_util.h"
16 #include "net/cookies/parsed_cookie.h"
17 #include "net/http/http_request_headers.h"
18 #include "net/http/http_response_headers.h"
19 #include "net/http/http_status_code.h"
20 #include "net/url_request/redirect_info.h"
21 #include "net/url_request/redirect_util.h"
22 #include "net/url_request/referrer_policy.h"
23 #include "net/url_request/url_request.h"
24 #include "services/network/public/cpp/resource_request.h"
25 
26 namespace net_service {
27 
28 namespace {
29 
30 // Determine the cookie domain to use for setting the specified cookie.
31 // From net/cookies/cookie_store.cc.
GetCookieDomain(const GURL & url,const net::ParsedCookie & pc,std::string * result)32 bool GetCookieDomain(const GURL& url,
33                      const net::ParsedCookie& pc,
34                      std::string* result) {
35   std::string domain_string;
36   if (pc.HasDomain())
37     domain_string = pc.Domain();
38   return net::cookie_util::GetCookieDomainWithString(url, domain_string,
39                                                      result);
40 }
41 
MakeCefCookieSameSite(net::CookieSameSite value)42 cef_cookie_same_site_t MakeCefCookieSameSite(net::CookieSameSite value) {
43   switch (value) {
44     case net::CookieSameSite::UNSPECIFIED:
45       return CEF_COOKIE_SAME_SITE_UNSPECIFIED;
46     case net::CookieSameSite::NO_RESTRICTION:
47       return CEF_COOKIE_SAME_SITE_NO_RESTRICTION;
48     case net::CookieSameSite::LAX_MODE:
49       return CEF_COOKIE_SAME_SITE_LAX_MODE;
50     case net::CookieSameSite::STRICT_MODE:
51       return CEF_COOKIE_SAME_SITE_STRICT_MODE;
52   }
53 }
54 
MakeCefCookiePriority(net::CookiePriority value)55 cef_cookie_priority_t MakeCefCookiePriority(net::CookiePriority value) {
56   switch (value) {
57     case net::COOKIE_PRIORITY_LOW:
58       return CEF_COOKIE_PRIORITY_LOW;
59     case net::COOKIE_PRIORITY_MEDIUM:
60       return CEF_COOKIE_PRIORITY_MEDIUM;
61     case net::COOKIE_PRIORITY_HIGH:
62       return CEF_COOKIE_PRIORITY_HIGH;
63   }
64 }
65 
66 }  // namespace
67 
68 const char kHTTPLocationHeaderName[] = "Location";
69 const char kHTTPSetCookieHeaderName[] = "Set-Cookie";
70 
71 const char kContentTypeApplicationFormURLEncoded[] =
72     "application/x-www-form-urlencoded";
73 
MakeStatusLine(int status_code,const std::string & status_text,bool for_replacement)74 std::string MakeStatusLine(int status_code,
75                            const std::string& status_text,
76                            bool for_replacement) {
77   std::string status("HTTP/1.1 ");
78   status.append(base::NumberToString(status_code));
79   status.append(" ");
80 
81   if (status_text.empty()) {
82     const std::string& text =
83         net::GetHttpReasonPhrase(static_cast<net::HttpStatusCode>(status_code));
84     DCHECK(!text.empty());
85     status.append(text);
86   } else {
87     status.append(status_text);
88   }
89 
90   if (!for_replacement) {
91     // The HttpResponseHeaders constructor expects its input string to be
92     // terminated by two NULs.
93     status.append("\0\0", 2);
94   }
95   return status;
96 }
97 
MakeContentTypeValue(const std::string & mime_type,const std::string & charset)98 std::string MakeContentTypeValue(const std::string& mime_type,
99                                  const std::string& charset) {
100   DCHECK(!mime_type.empty());
101   std::string value = mime_type;
102   if (!charset.empty()) {
103     value.append("; charset=");
104     value.append(charset);
105   }
106   return value;
107 }
108 
MakeResponseHeaders(int status_code,const std::string & status_text,const std::string & mime_type,const std::string & charset,int64_t content_length,const std::multimap<std::string,std::string> & extra_headers,bool allow_existing_header_override)109 scoped_refptr<net::HttpResponseHeaders> MakeResponseHeaders(
110     int status_code,
111     const std::string& status_text,
112     const std::string& mime_type,
113     const std::string& charset,
114     int64_t content_length,
115     const std::multimap<std::string, std::string>& extra_headers,
116     bool allow_existing_header_override) {
117   if (status_code <= 0)
118     status_code = 200;
119 
120   auto headers = WrapRefCounted(new net::HttpResponseHeaders(
121       MakeStatusLine(status_code, status_text, false)));
122 
123   // Track the headers that have already been set. Perform all comparisons in
124   // lowercase.
125   std::set<std::string> set_headers_lowercase;
126   if ((status_code >= 200 && status_code < 300) &&
127       status_code != net::HTTP_NO_CONTENT &&
128       status_code != net::HTTP_RESET_CONTENT) {
129     if (!mime_type.empty()) {
130       headers->AddHeader(net::HttpRequestHeaders::kContentType,
131                          MakeContentTypeValue(mime_type, charset));
132       set_headers_lowercase.insert(
133           base::ToLowerASCII(net::HttpRequestHeaders::kContentType));
134     }
135 
136     if (content_length >= 0) {
137       headers->AddHeader(net::HttpRequestHeaders::kContentLength,
138                          base::NumberToString(content_length));
139       set_headers_lowercase.insert(
140           base::ToLowerASCII(net::HttpRequestHeaders::kContentLength));
141     }
142   }
143 
144   for (const auto& pair : extra_headers) {
145     if (!set_headers_lowercase.empty()) {
146       // Check if the header has already been set.
147       const std::string& name_lowercase = base::ToLowerASCII(pair.first);
148       if (set_headers_lowercase.find(name_lowercase) !=
149           set_headers_lowercase.end()) {
150         if (allow_existing_header_override)
151           headers->RemoveHeader(pair.first);
152         else
153           continue;
154       }
155     }
156 
157     headers->AddHeader(pair.first, pair.second);
158   }
159 
160   return headers;
161 }
162 
MakeRedirectInfo(const network::ResourceRequest & request,const net::HttpResponseHeaders * headers,const GURL & new_location,int status_code)163 net::RedirectInfo MakeRedirectInfo(const network::ResourceRequest& request,
164                                    const net::HttpResponseHeaders* headers,
165                                    const GURL& new_location,
166                                    int status_code) {
167   bool insecure_scheme_was_upgraded = false;
168 
169   GURL location = new_location;
170   if (status_code == 0)
171     status_code = net::HTTP_TEMPORARY_REDIRECT;
172 
173   // If this a redirect to HTTP of a request that had the
174   // 'upgrade-insecure-requests' policy set, upgrade it to HTTPS.
175   if (request.upgrade_if_insecure) {
176     if (location.SchemeIs("http")) {
177       insecure_scheme_was_upgraded = true;
178       GURL::Replacements replacements;
179       replacements.SetSchemeStr("https");
180       location = location.ReplaceComponents(replacements);
181     }
182   }
183 
184   auto first_party_url_policy =
185       request.update_first_party_url_on_redirect
186           ? net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT
187           : net::RedirectInfo::FirstPartyURLPolicy::NEVER_CHANGE_URL;
188   return net::RedirectInfo::ComputeRedirectInfo(
189       request.method, request.url, request.site_for_cookies,
190       first_party_url_policy, request.referrer_policy, request.referrer.spec(),
191       status_code, location,
192       net::RedirectUtil::GetReferrerPolicyHeader(headers),
193       insecure_scheme_was_upgraded);
194 }
195 
MakeCookieSameSite(cef_cookie_same_site_t value)196 net::CookieSameSite MakeCookieSameSite(cef_cookie_same_site_t value) {
197   switch (value) {
198     case CEF_COOKIE_SAME_SITE_UNSPECIFIED:
199       return net::CookieSameSite::UNSPECIFIED;
200     case CEF_COOKIE_SAME_SITE_NO_RESTRICTION:
201       return net::CookieSameSite::NO_RESTRICTION;
202     case CEF_COOKIE_SAME_SITE_LAX_MODE:
203       return net::CookieSameSite::LAX_MODE;
204     case CEF_COOKIE_SAME_SITE_STRICT_MODE:
205       return net::CookieSameSite::STRICT_MODE;
206   }
207 }
208 
MakeCookiePriority(cef_cookie_priority_t value)209 net::CookiePriority MakeCookiePriority(cef_cookie_priority_t value) {
210   switch (value) {
211     case CEF_COOKIE_PRIORITY_LOW:
212       return net::COOKIE_PRIORITY_LOW;
213     case CEF_COOKIE_PRIORITY_MEDIUM:
214       return net::COOKIE_PRIORITY_MEDIUM;
215     case CEF_COOKIE_PRIORITY_HIGH:
216       return net::COOKIE_PRIORITY_HIGH;
217   }
218 }
219 
MakeCefCookie(const net::CanonicalCookie & cc,CefCookie & cookie)220 bool MakeCefCookie(const net::CanonicalCookie& cc, CefCookie& cookie) {
221   CefString(&cookie.name).FromString(cc.Name());
222   CefString(&cookie.value).FromString(cc.Value());
223   CefString(&cookie.domain).FromString(cc.Domain());
224   CefString(&cookie.path).FromString(cc.Path());
225   cookie.secure = cc.IsSecure();
226   cookie.httponly = cc.IsHttpOnly();
227   cef_time_from_basetime(cc.CreationDate(), cookie.creation);
228   cef_time_from_basetime(cc.LastAccessDate(), cookie.last_access);
229   cookie.has_expires = cc.IsPersistent();
230   if (cookie.has_expires)
231     cef_time_from_basetime(cc.ExpiryDate(), cookie.expires);
232   cookie.same_site = MakeCefCookieSameSite(cc.SameSite());
233   cookie.priority = MakeCefCookiePriority(cc.Priority());
234 
235   return true;
236 }
237 
MakeCefCookie(const GURL & url,const std::string & cookie_line,CefCookie & cookie)238 bool MakeCefCookie(const GURL& url,
239                    const std::string& cookie_line,
240                    CefCookie& cookie) {
241   // Parse the cookie.
242   net::ParsedCookie pc(cookie_line);
243   if (!pc.IsValid())
244     return false;
245 
246   std::string cookie_domain;
247   if (!GetCookieDomain(url, pc, &cookie_domain))
248     return false;
249 
250   std::string path_string;
251   if (pc.HasPath())
252     path_string = pc.Path();
253   std::string cookie_path =
254       net::CanonicalCookie::CanonPathWithString(url, path_string);
255   base::Time creation_time = base::Time::Now();
256   base::Time cookie_expires =
257       net::CanonicalCookie::CanonExpiration(pc, creation_time, creation_time);
258 
259   CefString(&cookie.name).FromString(pc.Name());
260   CefString(&cookie.value).FromString(pc.Value());
261   CefString(&cookie.domain).FromString(cookie_domain);
262   CefString(&cookie.path).FromString(cookie_path);
263   cookie.secure = pc.IsSecure();
264   cookie.httponly = pc.IsHttpOnly();
265   cef_time_from_basetime(creation_time, cookie.creation);
266   cef_time_from_basetime(creation_time, cookie.last_access);
267   cookie.has_expires = !cookie_expires.is_null();
268   if (cookie.has_expires)
269     cef_time_from_basetime(cookie_expires, cookie.expires);
270   cookie.same_site = MakeCefCookieSameSite(pc.SameSite());
271   cookie.priority = MakeCefCookiePriority(pc.Priority());
272 
273   return true;
274 }
275 
276 }  // namespace net_service