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