blob: 3f68e240a0baff41c6fa49733aaf3c5748e417fa [file] [log] [blame]
// Copyright (c) 2019 The Chromium Embedded Framework Authors. Portions
// Copyright (c) 2018 The Chromium Authors. All rights reserved. Use of this
// source code is governed by a BSD-style license that can be found in the
// LICENSE file.
#include "libcef/common/net_service/net_service_util.h"
#include "libcef/common/time_util.h"
#include <set>
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/cookie_util.h"
#include "net/cookies/parsed_cookie.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/http/http_status_code.h"
#include "net/url_request/redirect_info.h"
#include "net/url_request/redirect_util.h"
#include "net/url_request/url_request.h"
#include "services/network/public/cpp/resource_request.h"
namespace net_service {
namespace {
// Determine the cookie domain to use for setting the specified cookie.
// From net/cookies/cookie_store.cc.
bool GetCookieDomain(const GURL& url,
const net::ParsedCookie& pc,
std::string* result) {
std::string domain_string;
if (pc.HasDomain())
domain_string = pc.Domain();
return net::cookie_util::GetCookieDomainWithString(url, domain_string,
result);
}
cef_cookie_same_site_t MakeCefCookieSameSite(net::CookieSameSite value) {
switch (value) {
case net::CookieSameSite::UNSPECIFIED:
return CEF_COOKIE_SAME_SITE_UNSPECIFIED;
case net::CookieSameSite::NO_RESTRICTION:
return CEF_COOKIE_SAME_SITE_NO_RESTRICTION;
case net::CookieSameSite::LAX_MODE:
return CEF_COOKIE_SAME_SITE_LAX_MODE;
case net::CookieSameSite::STRICT_MODE:
return CEF_COOKIE_SAME_SITE_STRICT_MODE;
}
}
cef_cookie_priority_t MakeCefCookiePriority(net::CookiePriority value) {
switch (value) {
case net::COOKIE_PRIORITY_LOW:
return CEF_COOKIE_PRIORITY_LOW;
case net::COOKIE_PRIORITY_MEDIUM:
return CEF_COOKIE_PRIORITY_MEDIUM;
case net::COOKIE_PRIORITY_HIGH:
return CEF_COOKIE_PRIORITY_HIGH;
}
}
} // namespace
const char kHTTPLocationHeaderName[] = "Location";
const char kHTTPSetCookieHeaderName[] = "Set-Cookie";
const char kContentTypeApplicationFormURLEncoded[] =
"application/x-www-form-urlencoded";
const char kHTTPHeaderSep[] = ": ";
std::string MakeHeader(const std::string& name, const std::string& value) {
std::string header(name);
header.append(kHTTPHeaderSep);
header.append(value);
return header;
}
std::string MakeStatusLine(int status_code,
const std::string& status_text,
bool for_replacement) {
std::string status("HTTP/1.1 ");
status.append(base::NumberToString(status_code));
status.append(" ");
if (status_text.empty()) {
const std::string& text =
net::GetHttpReasonPhrase(static_cast<net::HttpStatusCode>(status_code));
DCHECK(!text.empty());
status.append(text);
} else {
status.append(status_text);
}
if (!for_replacement) {
// The HttpResponseHeaders constructor expects its input string to be
// terminated by two NULs.
status.append("\0\0", 2);
}
return status;
}
std::string MakeContentTypeValue(const std::string& mime_type,
const std::string& charset) {
DCHECK(!mime_type.empty());
std::string value = mime_type;
if (!charset.empty()) {
value.append("; charset=");
value.append(charset);
}
return value;
}
scoped_refptr<net::HttpResponseHeaders> 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) {
if (status_code <= 0)
status_code = 200;
auto headers = WrapRefCounted(new net::HttpResponseHeaders(
MakeStatusLine(status_code, status_text, false)));
// Track the headers that have already been set. Perform all comparisons in
// lowercase.
std::set<std::string> set_headers_lowercase;
if ((status_code >= 200 && status_code < 300) &&
status_code != net::HTTP_NO_CONTENT &&
status_code != net::HTTP_RESET_CONTENT) {
if (!mime_type.empty()) {
headers->AddHeader(MakeHeader(net::HttpRequestHeaders::kContentType,
MakeContentTypeValue(mime_type, charset)));
set_headers_lowercase.insert(
base::ToLowerASCII(net::HttpRequestHeaders::kContentType));
}
if (content_length >= 0) {
headers->AddHeader(MakeHeader(net::HttpRequestHeaders::kContentLength,
base::NumberToString(content_length)));
set_headers_lowercase.insert(
base::ToLowerASCII(net::HttpRequestHeaders::kContentLength));
}
}
for (const auto& pair : extra_headers) {
if (!set_headers_lowercase.empty()) {
// Check if the header has already been set.
const std::string& name_lowercase = base::ToLowerASCII(pair.first);
if (set_headers_lowercase.find(name_lowercase) !=
set_headers_lowercase.end()) {
if (allow_existing_header_override)
headers->RemoveHeader(pair.first);
else
continue;
}
}
headers->AddHeader(MakeHeader(pair.first, pair.second));
}
return headers;
}
net::RedirectInfo MakeRedirectInfo(const network::ResourceRequest& request,
const net::HttpResponseHeaders* headers,
const GURL& new_location,
int status_code) {
bool insecure_scheme_was_upgraded = false;
GURL location = new_location;
if (status_code == 0)
status_code = net::HTTP_TEMPORARY_REDIRECT;
// If this a redirect to HTTP of a request that had the
// 'upgrade-insecure-requests' policy set, upgrade it to HTTPS.
if (request.upgrade_if_insecure) {
if (location.SchemeIs("http")) {
insecure_scheme_was_upgraded = true;
GURL::Replacements replacements;
replacements.SetSchemeStr("https");
location = location.ReplaceComponents(replacements);
}
}
net::URLRequest::FirstPartyURLPolicy first_party_url_policy =
request.update_first_party_url_on_redirect
? net::URLRequest::UPDATE_FIRST_PARTY_URL_ON_REDIRECT
: net::URLRequest::NEVER_CHANGE_FIRST_PARTY_URL;
return net::RedirectInfo::ComputeRedirectInfo(
request.method, request.url, request.site_for_cookies,
first_party_url_policy, request.referrer_policy, request.referrer.spec(),
status_code, location,
net::RedirectUtil::GetReferrerPolicyHeader(headers),
insecure_scheme_was_upgraded);
}
net::CookieSameSite MakeCookieSameSite(cef_cookie_same_site_t value) {
switch (value) {
case CEF_COOKIE_SAME_SITE_UNSPECIFIED:
return net::CookieSameSite::UNSPECIFIED;
case CEF_COOKIE_SAME_SITE_NO_RESTRICTION:
return net::CookieSameSite::NO_RESTRICTION;
case CEF_COOKIE_SAME_SITE_LAX_MODE:
return net::CookieSameSite::LAX_MODE;
case CEF_COOKIE_SAME_SITE_STRICT_MODE:
return net::CookieSameSite::STRICT_MODE;
}
}
net::CookiePriority MakeCookiePriority(cef_cookie_priority_t value) {
switch (value) {
case CEF_COOKIE_PRIORITY_LOW:
return net::COOKIE_PRIORITY_LOW;
case CEF_COOKIE_PRIORITY_MEDIUM:
return net::COOKIE_PRIORITY_MEDIUM;
case CEF_COOKIE_PRIORITY_HIGH:
return net::COOKIE_PRIORITY_HIGH;
}
}
bool MakeCefCookie(const net::CanonicalCookie& cc, CefCookie& cookie) {
CefString(&cookie.name).FromString(cc.Name());
CefString(&cookie.value).FromString(cc.Value());
CefString(&cookie.domain).FromString(cc.Domain());
CefString(&cookie.path).FromString(cc.Path());
cookie.secure = cc.IsSecure();
cookie.httponly = cc.IsHttpOnly();
cef_time_from_basetime(cc.CreationDate(), cookie.creation);
cef_time_from_basetime(cc.LastAccessDate(), cookie.last_access);
cookie.has_expires = cc.IsPersistent();
if (cookie.has_expires)
cef_time_from_basetime(cc.ExpiryDate(), cookie.expires);
cookie.same_site = MakeCefCookieSameSite(cc.SameSite());
cookie.priority = MakeCefCookiePriority(cc.Priority());
return true;
}
bool MakeCefCookie(const GURL& url,
const std::string& cookie_line,
CefCookie& cookie) {
// Parse the cookie.
net::ParsedCookie pc(cookie_line);
if (!pc.IsValid())
return false;
std::string cookie_domain;
if (!GetCookieDomain(url, pc, &cookie_domain))
return false;
std::string path_string;
if (pc.HasPath())
path_string = pc.Path();
std::string cookie_path =
net::CanonicalCookie::CanonPathWithString(url, path_string);
base::Time creation_time = base::Time::Now();
base::Time cookie_expires =
net::CanonicalCookie::CanonExpiration(pc, creation_time, creation_time);
CefString(&cookie.name).FromString(pc.Name());
CefString(&cookie.value).FromString(pc.Value());
CefString(&cookie.domain).FromString(cookie_domain);
CefString(&cookie.path).FromString(cookie_path);
cookie.secure = pc.IsSecure();
cookie.httponly = pc.IsHttpOnly();
cef_time_from_basetime(creation_time, cookie.creation);
cef_time_from_basetime(creation_time, cookie.last_access);
cookie.has_expires = !cookie_expires.is_null();
if (cookie.has_expires)
cef_time_from_basetime(cookie_expires, cookie.expires);
cookie.same_site = MakeCefCookieSameSite(pc.SameSite());
cookie.priority = MakeCefCookiePriority(pc.Priority());
return true;
}
} // namespace net_service