mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-03-08 07:48:32 +01:00
Requests created using CefURLRequest::Create are not associated with a browser/frame. When originating from the render process these requests cannot be intercepted and consequently only http(s) and blob requests are supported. To work around this limitation a new CefFrame::CreateURLRequest method has been added that allows the request to be associated with that browser/frame for interception purposes. This change also fixes an issue with the NetworkService implementation where redirected requests could result in two parallel requests being sent to the target server. To test: URLRequestTest.* tests pass with NetworkService enabled.
232 lines
7.7 KiB
C++
232 lines
7.7 KiB
C++
// 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"
|
|
#include "services/network/public/cpp/resource_response.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);
|
|
}
|
|
|
|
} // 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;
|
|
}
|
|
|
|
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 = 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 == net::HTTP_OK) {
|
|
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,
|
|
request.top_frame_origin, first_party_url_policy, request.referrer_policy,
|
|
request.referrer.spec(), status_code, location,
|
|
net::RedirectUtil::GetReferrerPolicyHeader(headers),
|
|
insecure_scheme_was_upgraded);
|
|
}
|
|
|
|
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);
|
|
|
|
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);
|
|
|
|
return true;
|
|
}
|
|
|
|
} // namespace net_service
|