Add callbacks for network request access to cookies (issue #2374)

This commit is contained in:
Marshall Greenblatt 2018-02-05 19:21:14 -05:00
parent d7e1312e9d
commit d34d3d1b3b
15 changed files with 1177 additions and 11 deletions

View File

@ -33,7 +33,7 @@
// by hand. See the translator.README.txt file in the tools directory for
// more information.
//
// $hash=52ba75e5a733ed0be9a064b9794c0181e714c090$
// $hash=b8b5a62b11dbc48f0733c0522864e4dbda8b4f59$
//
#ifndef CEF_INCLUDE_CAPI_CEF_REQUEST_HANDLER_CAPI_H_
@ -247,6 +247,29 @@ typedef struct _cef_request_handler_t {
const cef_string_t* scheme,
struct _cef_auth_callback_t* callback);
///
// Called on the IO thread before sending a network request with a "Cookie"
// request header. Return true (1) to allow cookies to be included in the
// network request or false (0) to block cookies. The |request| object should
// not be modified in this callback.
///
int(CEF_CALLBACK* can_get_cookies)(struct _cef_request_handler_t* self,
struct _cef_browser_t* browser,
struct _cef_frame_t* frame,
struct _cef_request_t* request);
///
// Called on the IO thread when receiving a network request with a "Set-
// Cookie" response header value represented by |cookie|. Return true (1) to
// allow the cookie to be stored or false (0) to block the cookie. The
// |request| object should not be modified in this callback.
///
int(CEF_CALLBACK* can_set_cookie)(struct _cef_request_handler_t* self,
struct _cef_browser_t* browser,
struct _cef_frame_t* frame,
struct _cef_request_t* request,
const struct _cef_cookie_t* cookie);
///
// Called on the IO thread when JavaScript requests a specific storage quota
// size via the webkitStorageInfo.requestQuota function. |origin_url| is the

View File

@ -252,6 +252,33 @@ class CefRequestHandler : public virtual CefBaseRefCounted {
return false;
}
///
// Called on the IO thread before sending a network request with a "Cookie"
// request header. Return true to allow cookies to be included in the network
// request or false to block cookies. The |request| object should not be
// modified in this callback.
///
/*--cef()--*/
virtual bool CanGetCookies(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) {
return true;
}
///
// Called on the IO thread when receiving a network request with a
// "Set-Cookie" response header value represented by |cookie|. Return true to
// allow the cookie to be stored or false to block the cookie. The |request|
// object should not be modified in this callback.
///
/*--cef()--*/
virtual bool CanSetCookie(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
const CefCookie& cookie) {
return true;
}
///
// Called on the IO thread when JavaScript requests a specific storage quota
// size via the webkitStorageInfo.requestQuota function. |origin_url| is the

View File

@ -1583,7 +1583,7 @@ void CefBrowserHostImpl::CancelContextMenu() {
}
CefRefPtr<CefFrame> CefBrowserHostImpl::GetFrameForRequest(
net::URLRequest* request) {
const net::URLRequest* request) {
CEF_REQUIRE_IOT();
const content::ResourceRequestInfo* info =
content::ResourceRequestInfo::ForRequest(request);

View File

@ -303,7 +303,7 @@ class CefBrowserHostImpl : public CefBrowserHost,
#endif
// Returns the frame associated with the specified URLRequest.
CefRefPtr<CefFrame> GetFrameForRequest(net::URLRequest* request);
CefRefPtr<CefFrame> GetFrameForRequest(const net::URLRequest* request);
// Navigate as specified by the |params| argument.
void Navigate(const CefNavigateParams& params);

View File

@ -9,7 +9,7 @@
namespace net_util {
bool IsInternalRequest(net::URLRequest* request) {
bool IsInternalRequest(const net::URLRequest* request) {
// With PlzNavigate we now receive blob URLs. Ignore these URLs.
// See https://crbug.com/776884 for details.
if (request->url().SchemeIs(url::kBlobScheme)) {

View File

@ -14,7 +14,7 @@ namespace net_util {
// Returns true if |request| is handled internally and should not be exposed via
// the CEF API.
bool IsInternalRequest(net::URLRequest* request);
bool IsInternalRequest(const net::URLRequest* request);
}; // namespace net_util

View File

@ -9,6 +9,7 @@
#include "include/cef_urlrequest.h"
#include "libcef/browser/browser_host_impl.h"
#include "libcef/browser/cookie_manager_impl.h"
#include "libcef/browser/net/net_util.h"
#include "libcef/browser/net/source_stream.h"
#include "libcef/browser/net/url_request_user_data.h"
@ -440,6 +441,64 @@ net::NetworkDelegate::AuthRequiredResponse CefNetworkDelegate::OnAuthRequired(
return AUTH_REQUIRED_RESPONSE_NO_ACTION;
}
bool CefNetworkDelegate::OnCanGetCookies(const net::URLRequest& request,
const net::CookieList& cookie_list) {
if (net_util::IsInternalRequest(&request))
return true;
CefRefPtr<CefBrowserHostImpl> browser =
CefBrowserHostImpl::GetBrowserForRequest(&request);
if (browser.get()) {
CefRefPtr<CefClient> client = browser->GetClient();
if (client.get()) {
CefRefPtr<CefRequestHandler> handler = client->GetRequestHandler();
if (handler.get()) {
CefRefPtr<CefFrame> frame = browser->GetFrameForRequest(&request);
CefRefPtr<CefRequestImpl> cefRequest = new CefRequestImpl();
cefRequest->Set(&request);
cefRequest->SetReadOnly(true);
return handler->CanGetCookies(browser.get(), frame, cefRequest.get());
}
}
}
return true;
}
bool CefNetworkDelegate::OnCanSetCookie(const net::URLRequest& request,
const net::CanonicalCookie& cookie,
net::CookieOptions* options) {
if (net_util::IsInternalRequest(&request))
return true;
CefRefPtr<CefBrowserHostImpl> browser =
CefBrowserHostImpl::GetBrowserForRequest(&request);
if (browser.get()) {
CefRefPtr<CefClient> client = browser->GetClient();
if (client.get()) {
CefRefPtr<CefRequestHandler> handler = client->GetRequestHandler();
if (handler.get()) {
CefRefPtr<CefFrame> frame = browser->GetFrameForRequest(&request);
CefRefPtr<CefRequestImpl> cefRequest = new CefRequestImpl();
cefRequest->Set(&request);
cefRequest->SetReadOnly(true);
CefCookie cefCookie;
if (!CefCookieManagerImpl::GetCefCookie(cookie, cefCookie))
return true;
return handler->CanSetCookie(browser.get(), frame, cefRequest.get(),
cefCookie);
}
}
}
return true;
}
bool CefNetworkDelegate::OnCanAccessFile(
const net::URLRequest& request,
const base::FilePath& original_path,

View File

@ -38,6 +38,11 @@ class CefNetworkDelegate : public net::NetworkDelegateImpl {
const AuthCallback& callback,
net::AuthCredentials* credentials) override;
void OnCompleted(net::URLRequest* request, bool started) override;
bool OnCanGetCookies(const net::URLRequest& request,
const net::CookieList& cookie_list) override;
bool OnCanSetCookie(const net::URLRequest& request,
const net::CanonicalCookie& cookie,
net::CookieOptions* options) override;
bool OnCanAccessFile(const net::URLRequest& request,
const base::FilePath& original_path,
const base::FilePath& absolute_path) const override;

View File

@ -411,7 +411,7 @@ void CefResourceRequestJob::DoLoadCookies() {
void CefResourceRequestJob::CheckCookiePolicyAndLoad(
const net::CookieList& cookie_list) {
bool can_get_cookies = CanGetCookies(cookie_list);
bool can_get_cookies = !cookie_list.empty() && CanGetCookies(cookie_list);
if (can_get_cookies) {
net::CookieList::const_iterator it = cookie_list.begin();
for (; it != cookie_list.end(); ++it) {

View File

@ -445,7 +445,7 @@ uint64 CefRequestImpl::GetIdentifier() {
return identifier_;
}
void CefRequestImpl::Set(net::URLRequest* request) {
void CefRequestImpl::Set(const net::URLRequest* request) {
base::AutoLock lock_scope(lock_);
CHECK_READONLY_RETURN_VOID();

View File

@ -80,7 +80,7 @@ class CefRequestImpl : public CefRequest {
uint64 GetIdentifier() override;
// Populate this object from the URLRequest object.
void Set(net::URLRequest* request);
void Set(const net::URLRequest* request);
// Populate the URLRequest object from this object.
// If |changed_only| is true then only the changed fields will be updated.

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=419fbbe47f1b127719554892609c02a3b135c44d$
// $hash=b4c3eec00b1889871f4e13ea05d7c00a12da1fa0$
//
#include "libcef_dll/cpptoc/request_handler_cpptoc.h"
@ -367,6 +367,80 @@ request_handler_get_auth_credentials(struct _cef_request_handler_t* self,
return _retval;
}
int CEF_CALLBACK
request_handler_can_get_cookies(struct _cef_request_handler_t* self,
cef_browser_t* browser,
cef_frame_t* frame,
cef_request_t* request) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return 0;
// Verify param: frame; type: refptr_diff
DCHECK(frame);
if (!frame)
return 0;
// Verify param: request; type: refptr_diff
DCHECK(request);
if (!request)
return 0;
// Execute
bool _retval = CefRequestHandlerCppToC::Get(self)->CanGetCookies(
CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
CefRequestCToCpp::Wrap(request));
// Return type: bool
return _retval;
}
int CEF_CALLBACK
request_handler_can_set_cookie(struct _cef_request_handler_t* self,
cef_browser_t* browser,
cef_frame_t* frame,
cef_request_t* request,
const struct _cef_cookie_t* cookie) {
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
DCHECK(self);
if (!self)
return 0;
// Verify param: browser; type: refptr_diff
DCHECK(browser);
if (!browser)
return 0;
// Verify param: frame; type: refptr_diff
DCHECK(frame);
if (!frame)
return 0;
// Verify param: request; type: refptr_diff
DCHECK(request);
if (!request)
return 0;
// Verify param: cookie; type: struct_byref_const
DCHECK(cookie);
if (!cookie)
return 0;
// Translate param: cookie; type: struct_byref_const
CefCookie cookieObj;
if (cookie)
cookieObj.Set(*cookie, false);
// Execute
bool _retval = CefRequestHandlerCppToC::Get(self)->CanSetCookie(
CefBrowserCToCpp::Wrap(browser), CefFrameCToCpp::Wrap(frame),
CefRequestCToCpp::Wrap(request), cookieObj);
// Return type: bool
return _retval;
}
int CEF_CALLBACK
request_handler_on_quota_request(struct _cef_request_handler_t* self,
cef_browser_t* browser,
@ -603,6 +677,8 @@ CefRequestHandlerCppToC::CefRequestHandlerCppToC() {
GetStruct()->on_resource_load_complete =
request_handler_on_resource_load_complete;
GetStruct()->get_auth_credentials = request_handler_get_auth_credentials;
GetStruct()->can_get_cookies = request_handler_can_get_cookies;
GetStruct()->can_set_cookie = request_handler_can_set_cookie;
GetStruct()->on_quota_request = request_handler_on_quota_request;
GetStruct()->on_protocol_execution = request_handler_on_protocol_execution;
GetStruct()->on_certificate_error = request_handler_on_certificate_error;

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=9bcd78135c26925b3e33662e92497d1ef7d85642$
// $hash=fbd234d6060aadc1bf5ee0efce5d8cb2c4a0e022$
//
#include "libcef_dll/ctocpp/request_handler_ctocpp.h"
@ -353,6 +353,69 @@ bool CefRequestHandlerCToCpp::GetAuthCredentials(
return _retval ? true : false;
}
bool CefRequestHandlerCToCpp::CanGetCookies(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) {
cef_request_handler_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, can_get_cookies))
return false;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return false;
// Verify param: frame; type: refptr_diff
DCHECK(frame.get());
if (!frame.get())
return false;
// Verify param: request; type: refptr_diff
DCHECK(request.get());
if (!request.get())
return false;
// Execute
int _retval = _struct->can_get_cookies(
_struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
CefRequestCppToC::Wrap(request));
// Return type: bool
return _retval ? true : false;
}
bool CefRequestHandlerCToCpp::CanSetCookie(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
const CefCookie& cookie) {
cef_request_handler_t* _struct = GetStruct();
if (CEF_MEMBER_MISSING(_struct, can_set_cookie))
return false;
// AUTO-GENERATED CONTENT - DELETE THIS COMMENT BEFORE MODIFYING
// Verify param: browser; type: refptr_diff
DCHECK(browser.get());
if (!browser.get())
return false;
// Verify param: frame; type: refptr_diff
DCHECK(frame.get());
if (!frame.get())
return false;
// Verify param: request; type: refptr_diff
DCHECK(request.get());
if (!request.get())
return false;
// Execute
int _retval = _struct->can_set_cookie(
_struct, CefBrowserCppToC::Wrap(browser), CefFrameCppToC::Wrap(frame),
CefRequestCppToC::Wrap(request), &cookie);
// Return type: bool
return _retval ? true : false;
}
bool CefRequestHandlerCToCpp::OnQuotaRequest(
CefRefPtr<CefBrowser> browser,
const CefString& origin_url,

View File

@ -9,7 +9,7 @@
// implementations. See the translator.README.txt file in the tools directory
// for more information.
//
// $hash=a28a81ae22b7edb9dfb3a9df72c48a96971b7017$
// $hash=cde69cb97f29cb75da85bb709470f73d9e57f5bc$
//
#ifndef CEF_LIBCEF_DLL_CTOCPP_REQUEST_HANDLER_CTOCPP_H_
@ -80,6 +80,13 @@ class CefRequestHandlerCToCpp
const CefString& realm,
const CefString& scheme,
CefRefPtr<CefAuthCallback> callback) override;
bool CanGetCookies(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) override;
bool CanSetCookie(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
const CefCookie& cookie) override;
bool OnQuotaRequest(CefRefPtr<CefBrowser> browser,
const CefString& origin_url,
int64 new_size,

View File

@ -10,8 +10,10 @@
#include "include/base/cef_bind.h"
#include "include/base/cef_scoped_ptr.h"
#include "include/cef_cookie.h"
#include "include/cef_server.h"
#include "include/wrapper/cef_closure_task.h"
#include "include/wrapper/cef_stream_resource_handler.h"
#include "tests/ceftests/routing_test_handler.h"
#include "tests/ceftests/test_handler.h"
#include "tests/ceftests/test_util.h"
#include "tests/gtest/include/gtest/gtest.h"
@ -1681,6 +1683,910 @@ TEST(RequestHandlerTest, ResponseFilterError) {
ReleaseAndWaitForDestructor(handler);
}
namespace {
const char kCookieAccessScheme[] = "http";
const char kCookieAccessDomain[] = "test-cookies.com";
const char kCookieAccessServerIP[] = "127.0.0.1";
const uint16 kCookieAccessServerPort = 8099;
std::string GetCookieAccessOrigin(bool server_backend) {
std::stringstream ss;
if (server_backend) {
ss << kCookieAccessScheme << "://" << kCookieAccessServerIP << ":"
<< kCookieAccessServerPort;
} else {
ss << kCookieAccessScheme << "://" << kCookieAccessDomain;
}
ss << "/";
return ss.str();
}
std::string GetCookieAccessUrl1(bool server_backend) {
return GetCookieAccessOrigin(server_backend) + "cookie1.html";
}
std::string GetCookieAccessUrl2(bool server_backend) {
return GetCookieAccessOrigin(server_backend) + "cookie2.html";
}
void TestCookieString(const std::string& cookie_str,
TrackCallback& got_cookie_js,
TrackCallback& got_cookie_net) {
if (cookie_str.find("name_js=value_js") != std::string::npos) {
got_cookie_js.yes();
}
if (cookie_str.find("name_net=value_net") != std::string::npos) {
got_cookie_net.yes();
}
}
struct CookieAccessData {
CefRefPtr<CefResponse> response;
std::string response_data;
TrackCallback got_request_;
TrackCallback got_cookie_js_;
TrackCallback got_cookie_net_;
// Only used with scheme handler backend.
TrackCallback got_can_set_cookie_js_;
TrackCallback got_can_set_cookie_net_;
TrackCallback got_can_get_cookie_js_;
TrackCallback got_can_get_cookie_net_;
};
class CookieAccessResponseHandler {
public:
CookieAccessResponseHandler() {}
virtual void AddResponse(const std::string& url, CookieAccessData* data) = 0;
protected:
virtual ~CookieAccessResponseHandler() {}
};
std::string GetHeaderValue(const CefServer::HeaderMap& header_map,
const std::string& header_name) {
CefServer::HeaderMap::const_iterator it = header_map.find(header_name);
if (it != header_map.end())
return it->second;
return std::string();
}
// Serves request responses.
class CookieAccessSchemeHandler : public CefResourceHandler {
public:
explicit CookieAccessSchemeHandler(CookieAccessData* data)
: data_(data), offset_(0) {}
bool ProcessRequest(CefRefPtr<CefRequest> request,
CefRefPtr<CefCallback> callback) override {
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
CefRequest::HeaderMap headerMap;
request->GetHeaderMap(headerMap);
const std::string& cookie_str = GetHeaderValue(headerMap, "Cookie");
TestCookieString(cookie_str, data_->got_cookie_js_, data_->got_cookie_net_);
// Continue immediately.
callback->Continue();
return true;
}
void GetResponseHeaders(CefRefPtr<CefResponse> response,
int64& response_length,
CefString& redirectUrl) override {
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
response->SetStatus(data_->response->GetStatus());
response->SetStatusText(data_->response->GetStatusText());
response->SetMimeType(data_->response->GetMimeType());
CefResponse::HeaderMap headerMap;
data_->response->GetHeaderMap(headerMap);
response->SetHeaderMap(headerMap);
response_length = data_->response_data.length();
}
bool ReadResponse(void* response_data_out,
int bytes_to_read,
int& bytes_read,
CefRefPtr<CefCallback> callback) override {
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
bool has_data = false;
bytes_read = 0;
size_t size = data_->response_data.length();
if (offset_ < size) {
int transfer_size =
std::min(bytes_to_read, static_cast<int>(size - offset_));
memcpy(response_data_out, data_->response_data.c_str() + offset_,
transfer_size);
offset_ += transfer_size;
bytes_read = transfer_size;
has_data = true;
}
return has_data;
}
bool CanGetCookie(const CefCookie& cookie) override {
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
TestCookie(cookie, data_->got_can_get_cookie_js_,
data_->got_can_get_cookie_net_);
return true;
}
bool CanSetCookie(const CefCookie& cookie) override {
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
TestCookie(cookie, data_->got_can_set_cookie_js_,
data_->got_can_set_cookie_net_);
return true;
}
void Cancel() override { EXPECT_TRUE(CefCurrentlyOn(TID_IO)); }
private:
static void TestCookie(const CefCookie& cookie,
TrackCallback& got_cookie_js,
TrackCallback& got_cookie_net) {
const std::string& cookie_name = CefString(&cookie.name);
const std::string& cookie_val = CefString(&cookie.value);
if (cookie_name == "name_js") {
EXPECT_STREQ("value_js", cookie_val.c_str());
got_cookie_js.yes();
} else if (cookie_name == "name_net") {
EXPECT_STREQ("value_net", cookie_val.c_str());
got_cookie_net.yes();
} else {
ADD_FAILURE() << "Unexpected cookie: " << cookie_name;
}
}
// |data_| is not owned by this object.
CookieAccessData* data_;
size_t offset_;
IMPLEMENT_REFCOUNTING(CookieAccessSchemeHandler);
};
class CookieAccessSchemeHandlerFactory : public CefSchemeHandlerFactory,
public CookieAccessResponseHandler {
public:
CookieAccessSchemeHandlerFactory() {}
CefRefPtr<CefResourceHandler> Create(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
const CefString& scheme_name,
CefRefPtr<CefRequest> request) override {
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
const std::string& url = request->GetURL();
ResponseDataMap::const_iterator it = data_map_.find(url);
if (it != data_map_.end()) {
it->second->got_request_.yes();
// There should be no cookie data in this callback.
CefRequest::HeaderMap headerMap;
request->GetHeaderMap(headerMap);
EXPECT_TRUE(headerMap.find("Cookie") == headerMap.end());
return new CookieAccessSchemeHandler(it->second);
}
// Unknown test.
ADD_FAILURE() << "Unexpected url: " << url;
return nullptr;
}
void AddResponse(const std::string& url, CookieAccessData* data) override {
data_map_.insert(std::make_pair(url, data));
}
void Shutdown(const base::Closure& complete_callback) {
if (!CefCurrentlyOn(TID_IO)) {
CefPostTask(TID_IO,
base::Bind(&CookieAccessSchemeHandlerFactory::Shutdown, this,
complete_callback));
return;
}
complete_callback.Run();
}
private:
// Map of URL to Data.
typedef std::map<std::string, CookieAccessData*> ResponseDataMap;
ResponseDataMap data_map_;
IMPLEMENT_REFCOUNTING(CookieAccessSchemeHandlerFactory);
};
// HTTP server handler.
class CookieAccessServerHandler : public CefServerHandler,
public CookieAccessResponseHandler {
public:
CookieAccessServerHandler()
: initialized_(false),
expected_connection_ct_(-1),
actual_connection_ct_(0),
expected_http_request_ct_(-1),
actual_http_request_ct_(0) {}
virtual ~CookieAccessServerHandler() { RunCompleteCallback(); }
// Must be called before CreateServer().
void AddResponse(const std::string& url, CookieAccessData* data) override {
EXPECT_FALSE(initialized_);
data_map_.insert(std::make_pair(url, data));
}
// Must be called before CreateServer().
void SetExpectedRequestCount(int count) {
EXPECT_FALSE(initialized_);
expected_connection_ct_ = expected_http_request_ct_ = count;
}
// |complete_callback| will be executed on the UI thread after the server is
// started.
void CreateServer(const base::Closure& complete_callback) {
EXPECT_UI_THREAD();
if (expected_connection_ct_ < 0) {
// Default to the assumption of one request per registered URL.
SetExpectedRequestCount(static_cast<int>(data_map_.size()));
}
EXPECT_FALSE(initialized_);
initialized_ = true;
EXPECT_TRUE(complete_callback_.is_null());
complete_callback_ = complete_callback;
CefServer::CreateServer(kCookieAccessServerIP, kCookieAccessServerPort, 10,
this);
}
// Results in a call to VerifyResults() and eventual execution of the
// |complete_callback| on the UI thread via CookieAccessServerHandler
// destruction.
void ShutdownServer(const base::Closure& complete_callback) {
EXPECT_UI_THREAD();
EXPECT_TRUE(complete_callback_.is_null());
complete_callback_ = complete_callback;
EXPECT_TRUE(server_);
if (server_)
server_->Shutdown();
}
void OnServerCreated(CefRefPtr<CefServer> server) override {
EXPECT_TRUE(server);
EXPECT_TRUE(server->IsRunning());
EXPECT_FALSE(server->HasConnection());
EXPECT_FALSE(got_server_created_);
got_server_created_.yes();
EXPECT_FALSE(server_);
server_ = server;
EXPECT_FALSE(server_runner_);
server_runner_ = server_->GetTaskRunner();
EXPECT_TRUE(server_runner_);
EXPECT_TRUE(server_runner_->BelongsToCurrentThread());
CefPostTask(
TID_UI,
base::Bind(&CookieAccessServerHandler::RunCompleteCallback, this));
}
void OnServerDestroyed(CefRefPtr<CefServer> server) override {
EXPECT_TRUE(VerifyServer(server));
EXPECT_FALSE(server->IsRunning());
EXPECT_FALSE(server->HasConnection());
EXPECT_FALSE(got_server_destroyed_);
got_server_destroyed_.yes();
server_ = nullptr;
VerifyResults();
}
void OnClientConnected(CefRefPtr<CefServer> server,
int connection_id) override {
EXPECT_TRUE(VerifyServer(server));
EXPECT_TRUE(server->HasConnection());
EXPECT_TRUE(server->IsValidConnection(connection_id));
EXPECT_TRUE(connection_id_set_.find(connection_id) ==
connection_id_set_.end());
connection_id_set_.insert(connection_id);
actual_connection_ct_++;
}
void OnClientDisconnected(CefRefPtr<CefServer> server,
int connection_id) override {
EXPECT_TRUE(VerifyServer(server));
EXPECT_FALSE(server->IsValidConnection(connection_id));
ConnectionIdSet::iterator it = connection_id_set_.find(connection_id);
EXPECT_TRUE(it != connection_id_set_.end());
connection_id_set_.erase(it);
}
void OnHttpRequest(CefRefPtr<CefServer> server,
int connection_id,
const CefString& client_address,
CefRefPtr<CefRequest> request) override {
EXPECT_TRUE(VerifyServer(server));
EXPECT_TRUE(VerifyConnection(connection_id));
EXPECT_FALSE(client_address.empty());
// Log the requests for better error reporting.
request_log_ += request->GetMethod().ToString() + " " +
request->GetURL().ToString() + "\n";
HandleRequest(server, connection_id, request);
actual_http_request_ct_++;
}
void OnWebSocketRequest(CefRefPtr<CefServer> server,
int connection_id,
const CefString& client_address,
CefRefPtr<CefRequest> request,
CefRefPtr<CefCallback> callback) override {
NOTREACHED();
}
void OnWebSocketConnected(CefRefPtr<CefServer> server,
int connection_id) override {
NOTREACHED();
}
void OnWebSocketMessage(CefRefPtr<CefServer> server,
int connection_id,
const void* data,
size_t data_size) override {
NOTREACHED();
}
private:
bool RunningOnServerThread() {
return server_runner_ && server_runner_->BelongsToCurrentThread();
}
bool VerifyServer(CefRefPtr<CefServer> server) {
V_DECLARE();
V_EXPECT_TRUE(RunningOnServerThread());
V_EXPECT_TRUE(server);
V_EXPECT_TRUE(server_);
V_EXPECT_TRUE(server->GetAddress().ToString() ==
server_->GetAddress().ToString());
V_RETURN();
}
bool VerifyConnection(int connection_id) {
return connection_id_set_.find(connection_id) != connection_id_set_.end();
}
void VerifyResults() {
EXPECT_TRUE(RunningOnServerThread());
EXPECT_TRUE(got_server_created_);
EXPECT_TRUE(got_server_destroyed_);
EXPECT_TRUE(connection_id_set_.empty());
EXPECT_EQ(expected_connection_ct_, actual_connection_ct_) << request_log_;
EXPECT_EQ(expected_http_request_ct_, actual_http_request_ct_)
<< request_log_;
}
void HandleRequest(CefRefPtr<CefServer> server,
int connection_id,
CefRefPtr<CefRequest> request) {
const std::string& url = request->GetURL();
ResponseDataMap::const_iterator it = data_map_.find(url);
if (it != data_map_.end()) {
it->second->got_request_.yes();
CefRequest::HeaderMap headerMap;
request->GetHeaderMap(headerMap);
const std::string& cookie_str = GetHeaderValue(headerMap, "cookie");
TestCookieString(cookie_str, it->second->got_cookie_js_,
it->second->got_cookie_net_);
SendResponse(server, connection_id, it->second->response,
it->second->response_data);
} else {
// Unknown test.
ADD_FAILURE() << "Unexpected url: " << url;
server->SendHttp500Response(connection_id, "Unknown test");
}
}
void SendResponse(CefRefPtr<CefServer> server,
int connection_id,
CefRefPtr<CefResponse> response,
const std::string& response_data) {
int response_code = response->GetStatus();
const CefString& content_type = response->GetMimeType();
int64 content_length = static_cast<int64>(response_data.size());
CefResponse::HeaderMap extra_headers;
response->GetHeaderMap(extra_headers);
server->SendHttpResponse(connection_id, response_code, content_type,
content_length, extra_headers);
if (content_length != 0) {
server->SendRawData(connection_id, response_data.data(),
response_data.size());
server->CloseConnection(connection_id);
}
// The connection should be closed.
EXPECT_FALSE(server->IsValidConnection(connection_id));
}
void RunCompleteCallback() {
EXPECT_UI_THREAD();
EXPECT_FALSE(complete_callback_.is_null());
complete_callback_.Run();
complete_callback_.Reset();
}
// Map of URL to Data.
typedef std::map<std::string, CookieAccessData*> ResponseDataMap;
ResponseDataMap data_map_;
CefRefPtr<CefServer> server_;
CefRefPtr<CefTaskRunner> server_runner_;
bool initialized_;
// Only accessed on the UI thread.
base::Closure complete_callback_;
// After initialization the below members are only accessed on the server
// thread.
TrackCallback got_server_created_;
TrackCallback got_server_destroyed_;
typedef std::set<int> ConnectionIdSet;
ConnectionIdSet connection_id_set_;
int expected_connection_ct_;
int actual_connection_ct_;
int expected_http_request_ct_;
int actual_http_request_ct_;
std::string request_log_;
IMPLEMENT_REFCOUNTING(CookieAccessServerHandler);
DISALLOW_COPY_AND_ASSIGN(CookieAccessServerHandler);
};
class CookieAccessTestHandler : public RoutingTestHandler {
public:
enum TestMode {
ALLOW = 0,
BLOCK_READ = 1 << 0,
BLOCK_WRITE = 1 << 1,
BLOCK_ALL = BLOCK_READ | BLOCK_WRITE,
};
CookieAccessTestHandler(TestMode test_mode, bool server_backend)
: test_mode_(test_mode), server_backend_(server_backend) {}
void RunTest() override {
cookie_manager_ = CefCookieManager::GetGlobalManager(nullptr);
SetTestTimeout();
CefPostTask(TID_UI,
base::Bind(&CookieAccessTestHandler::StartBackend, this,
base::Bind(&CookieAccessTestHandler::RunTestContinue,
this)));
}
void DestroyTest() override {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI,
base::Bind(&CookieAccessTestHandler::DestroyTest, this));
return;
}
cookie_manager_ = NULL;
// Always get a call to CanSetCookie for the 1st network request due to the
// network cookie.
EXPECT_TRUE(got_can_set_cookie1_);
// Always get a call to CanGetCookies for the 2nd network request due to the
// JS cookie.
EXPECT_TRUE(got_can_get_cookies2_);
// Always get the JS cookie via JS.
EXPECT_TRUE(got_cookie_js1_);
EXPECT_TRUE(got_cookie_js2_);
EXPECT_TRUE(got_cookie_js3_);
// Only get the net cookie via JS if cookie write was allowed.
if (test_mode_ & BLOCK_WRITE) {
EXPECT_FALSE(got_cookie_net1_);
EXPECT_FALSE(got_cookie_net2_);
EXPECT_FALSE(got_cookie_net3_);
} else {
EXPECT_TRUE(got_cookie_net1_);
EXPECT_TRUE(got_cookie_net2_);
EXPECT_TRUE(got_cookie_net3_);
}
// Got both network requests.
EXPECT_TRUE(data1_.got_request_);
EXPECT_TRUE(data2_.got_request_);
// No cookies sent for the 1st network request.
EXPECT_FALSE(data1_.got_cookie_js_);
EXPECT_FALSE(data1_.got_cookie_net_);
// 2nd network request...
if (test_mode_ & BLOCK_READ) {
// No cookies sent if reading was blocked.
EXPECT_FALSE(data2_.got_cookie_js_);
EXPECT_FALSE(data2_.got_cookie_net_);
} else if (test_mode_ & BLOCK_WRITE) {
// Only JS cookie sent if writing was blocked.
EXPECT_TRUE(data2_.got_cookie_js_);
EXPECT_FALSE(data2_.got_cookie_net_);
} else {
// All cookies sent.
EXPECT_TRUE(data2_.got_cookie_js_);
EXPECT_TRUE(data2_.got_cookie_net_);
}
if (!server_backend_) {
// No query to get cookies with the 1st network request because none have
// been set yet.
EXPECT_FALSE(data1_.got_can_get_cookie_js_);
EXPECT_FALSE(data1_.got_can_get_cookie_net_);
// JS cookie is not set via a network request.
EXPECT_FALSE(data1_.got_can_set_cookie_js_);
EXPECT_FALSE(data2_.got_can_set_cookie_js_);
// No query to set the net cookie for the 1st network request if write was
// blocked.
if (test_mode_ & BLOCK_WRITE) {
EXPECT_FALSE(data1_.got_can_set_cookie_net_);
} else {
EXPECT_TRUE(data1_.got_can_set_cookie_net_);
}
// Net cookie is not set via the 2nd network request.
EXPECT_FALSE(data2_.got_can_set_cookie_net_);
// No query to get the JS cookie for the 2nd network request if read was
// blocked.
if (test_mode_ & BLOCK_READ) {
EXPECT_FALSE(data2_.got_can_get_cookie_js_);
} else {
EXPECT_TRUE(data2_.got_can_get_cookie_js_);
}
// No query to get the net cookie for the 2nd network request if read or
// write (of the net cookie) was blocked.
if (test_mode_ & (BLOCK_READ | BLOCK_WRITE)) {
EXPECT_FALSE(data2_.got_can_get_cookie_net_);
} else {
EXPECT_TRUE(data2_.got_can_get_cookie_net_);
}
}
TestHandler::DestroyTest();
}
bool CanGetCookies(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request) override {
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
const std::string& url = request->GetURL();
if (url == GetCookieAccessUrl2(server_backend_)) {
EXPECT_FALSE(got_can_get_cookies2_);
got_can_get_cookies2_.yes();
} else {
ADD_FAILURE() << "Unexpected url: " << url;
}
return !(test_mode_ & BLOCK_READ);
}
bool CanSetCookie(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefRequest> request,
const CefCookie& cookie) override {
EXPECT_TRUE(CefCurrentlyOn(TID_IO));
// Expecting the network cookie only.
EXPECT_STREQ("name_net", CefString(&cookie.name).ToString().c_str());
EXPECT_STREQ("value_net", CefString(&cookie.value).ToString().c_str());
const std::string& url = request->GetURL();
if (url == GetCookieAccessUrl1(server_backend_)) {
EXPECT_FALSE(got_can_set_cookie1_);
got_can_set_cookie1_.yes();
} else {
ADD_FAILURE() << "Unexpected url: " << url;
}
return !(test_mode_ & BLOCK_WRITE);
}
bool OnQuery(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
int64 query_id,
const CefString& request,
bool persistent,
CefRefPtr<Callback> callback) override {
const std::string& url = frame->GetURL();
const std::string& cookie_str = request.ToString();
if (url == GetCookieAccessUrl1(server_backend_)) {
TestCookieString(cookie_str, got_cookie_js1_, got_cookie_net1_);
browser->GetMainFrame()->LoadURL(GetCookieAccessUrl2(server_backend_));
} else if (url == GetCookieAccessUrl2(server_backend_)) {
TestCookieString(cookie_str, got_cookie_js2_, got_cookie_net2_);
FinishTest();
} else {
ADD_FAILURE() << "Unexpected url: " << url;
}
return true;
}
private:
void AddResponses(CookieAccessResponseHandler* handler) {
// 1st request sets a cookie via net response headers and JS, then retrieves
// the cookies via JS.
{
data1_.response = CefResponse::Create();
data1_.response->SetMimeType("text/html");
data1_.response->SetStatus(200);
data1_.response->SetStatusText("OK");
CefResponse::HeaderMap headerMap;
data1_.response->GetHeaderMap(headerMap);
headerMap.insert(std::make_pair("Set-Cookie", "name_net=value_net"));
data1_.response->SetHeaderMap(headerMap);
data1_.response_data =
"<html><head>"
"<script>"
"document.cookie='name_js=value_js';"
"window.testQuery({request:document.cookie});"
"</script>"
"</head><body>COOKIE ACCESS TEST 1</body></html>";
handler->AddResponse(GetCookieAccessUrl1(server_backend_), &data1_);
}
// 2nd request retrieves the cookies via JS.
{
data2_.response = CefResponse::Create();
data2_.response->SetMimeType("text/html");
data2_.response->SetStatus(200);
data2_.response->SetStatusText("OK");
data2_.response_data =
"<html><head>"
"<script>"
"window.testQuery({request:document.cookie});"
"</script>"
"</head><body>COOKIE ACCESS TEST 2</body></html>";
handler->AddResponse(GetCookieAccessUrl2(server_backend_), &data2_);
}
}
void StartBackend(const base::Closure& complete_callback) {
if (server_backend_) {
StartServer(complete_callback);
} else {
StartSchemeHandler(complete_callback);
}
}
void StartServer(const base::Closure& complete_callback) {
EXPECT_FALSE(server_handler_);
server_handler_ = new CookieAccessServerHandler();
AddResponses(server_handler_.get());
server_handler_->CreateServer(complete_callback);
}
void StartSchemeHandler(const base::Closure& complete_callback) {
// Add the factory registration.
scheme_factory_ = new CookieAccessSchemeHandlerFactory();
AddResponses(scheme_factory_.get());
CefRegisterSchemeHandlerFactory(kCookieAccessScheme, kCookieAccessDomain,
scheme_factory_.get());
complete_callback.Run();
}
void RunTestContinue() {
if (!CefCurrentlyOn(TID_UI)) {
CefPostTask(TID_UI,
base::Bind(&CookieAccessTestHandler::RunTestContinue, this));
return;
}
CreateBrowser(GetCookieAccessUrl1(server_backend_));
}
void FinishTest() {
// Verify that cookies were set correctly.
class TestVisitor : public CefCookieVisitor {
public:
explicit TestVisitor(CookieAccessTestHandler* handler)
: handler_(handler) {}
~TestVisitor() override {
// Destroy the test.
CefPostTask(
TID_UI,
base::Bind(
&CookieAccessTestHandler::ShutdownBackend, handler_,
base::Bind(&CookieAccessTestHandler::DestroyTest, handler_)));
}
bool Visit(const CefCookie& cookie,
int count,
int total,
bool& deleteCookie) override {
const std::string& name = CefString(&cookie.name);
const std::string& value = CefString(&cookie.value);
if (name == "name_js" && value == "value_js")
handler_->got_cookie_js3_.yes();
else if (name == "name_net" && value == "value_net")
handler_->got_cookie_net3_.yes();
// Clean up the cookies.
deleteCookie = true;
return true;
}
private:
CookieAccessTestHandler* handler_;
IMPLEMENT_REFCOUNTING(TestVisitor);
};
cookie_manager_->VisitAllCookies(new TestVisitor(this));
}
void ShutdownBackend(const base::Closure& complete_callback) {
if (server_backend_) {
ShutdownServer(complete_callback);
} else {
ShutdownSchemeHandler(complete_callback);
}
}
void ShutdownServer(const base::Closure& complete_callback) {
EXPECT_TRUE(server_handler_);
server_handler_->ShutdownServer(complete_callback);
server_handler_ = nullptr;
}
void ShutdownSchemeHandler(const base::Closure& complete_callback) {
EXPECT_TRUE(scheme_factory_);
CefRegisterSchemeHandlerFactory(kCookieAccessScheme, kCookieAccessDomain,
nullptr);
scheme_factory_->Shutdown(complete_callback);
scheme_factory_ = nullptr;
}
TestMode test_mode_;
bool server_backend_;
CefRefPtr<CefCookieManager> cookie_manager_;
CefRefPtr<CookieAccessServerHandler> server_handler_;
CefRefPtr<CookieAccessSchemeHandlerFactory> scheme_factory_;
CookieAccessData data1_;
CookieAccessData data2_;
// 1st request.
TrackCallback got_can_set_cookie1_;
TrackCallback got_cookie_js1_;
TrackCallback got_cookie_net1_;
// 2nd request.
TrackCallback got_can_get_cookies2_;
TrackCallback got_cookie_js2_;
TrackCallback got_cookie_net2_;
// From cookie manager.
TrackCallback got_cookie_js3_;
TrackCallback got_cookie_net3_;
DISALLOW_COPY_AND_ASSIGN(CookieAccessTestHandler);
IMPLEMENT_REFCOUNTING(CookieAccessTestHandler);
};
} // namespace
// Allow reading and writing of cookies with server backend.
TEST(RequestHandlerTest, CookieAccessServerAllow) {
CefRefPtr<CookieAccessTestHandler> handler =
new CookieAccessTestHandler(CookieAccessTestHandler::ALLOW, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Block reading of cookies with server backend.
TEST(RequestHandlerTest, CookieAccessServerBlockRead) {
CefRefPtr<CookieAccessTestHandler> handler =
new CookieAccessTestHandler(CookieAccessTestHandler::BLOCK_READ, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Block writing of cookies with server backend.
TEST(RequestHandlerTest, CookieAccessServerBlockWrite) {
CefRefPtr<CookieAccessTestHandler> handler =
new CookieAccessTestHandler(CookieAccessTestHandler::BLOCK_WRITE, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Block reading and writing of cookies with server backend.
TEST(RequestHandlerTest, CookieAccessServerBlockAll) {
CefRefPtr<CookieAccessTestHandler> handler =
new CookieAccessTestHandler(CookieAccessTestHandler::BLOCK_ALL, true);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Allow reading and writing of cookies with scheme handler backend.
TEST(RequestHandlerTest, CookieAccessSchemeAllow) {
CefRefPtr<CookieAccessTestHandler> handler =
new CookieAccessTestHandler(CookieAccessTestHandler::ALLOW, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Block reading of cookies with scheme handler backend.
TEST(RequestHandlerTest, CookieAccessSchemeBlockRead) {
CefRefPtr<CookieAccessTestHandler> handler =
new CookieAccessTestHandler(CookieAccessTestHandler::BLOCK_READ, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Block writing of cookies with scheme handler backend.
TEST(RequestHandlerTest, CookieAccessSchemeBlockWrite) {
CefRefPtr<CookieAccessTestHandler> handler =
new CookieAccessTestHandler(CookieAccessTestHandler::BLOCK_WRITE, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Block reading and writing of cookies with scheme handler backend.
TEST(RequestHandlerTest, CookieAccessSchemeBlockAll) {
CefRefPtr<CookieAccessTestHandler> handler =
new CookieAccessTestHandler(CookieAccessTestHandler::BLOCK_ALL, false);
handler->ExecuteTest();
ReleaseAndWaitForDestructor(handler);
}
// Entry point for creating request handler browser test objects.
// Called from client_app_delegates.cc.
void CreateRequestHandlerBrowserTests(