mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-01-29 18:49:52 +01:00
cc56720bd2
A CORS preflight request is an "OPTIONS" request sent to a server prior to a cross-origin XMLHttpRequest or Fetch request. The server's response determines which HTTP request methods are allowed and supported, and whether credentials such as Cookies and HTTP Authentication should be sent with requests. A CORS preflight request will only be sent if certain conditions are met. For example, it will be sent for requests that have potentially unsafe HTTP methods [1] or request headers [2]. See the NeedsPreflight function in services/network/cors/cors_url_loader.cc for full details. CORS preflight functionality is implemented in the network service and will not be triggered if the client handles the request instead of allowing it to proceed over the network. Since the preflight request itself also runs in the network service it cannot be intercepted by the client. [1] https://fetch.spec.whatwg.org/#cors-safelisted-method [2] https://fetch.spec.whatwg.org/#cors-safelisted-request-header
1621 lines
65 KiB
C++
1621 lines
65 KiB
C++
// Copyright (c) 2020 The Chromium Embedded Framework 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 <algorithm>
|
|
#include <set>
|
|
#include <vector>
|
|
|
|
#include "include/base/cef_bind.h"
|
|
#include "include/cef_callback.h"
|
|
#include "include/cef_origin_whitelist.h"
|
|
#include "include/cef_scheme.h"
|
|
#include "include/wrapper/cef_closure_task.h"
|
|
#include "tests/ceftests/routing_test_handler.h"
|
|
#include "tests/ceftests/test_request.h"
|
|
#include "tests/ceftests/test_server.h"
|
|
#include "tests/ceftests/test_util.h"
|
|
|
|
namespace {
|
|
|
|
const char kMimeTypeHtml[] = "text/html";
|
|
const char kMimeTypeText[] = "text/plain";
|
|
|
|
const char kDefaultHtml[] = "<html><body>TEST</body></html>";
|
|
const char kDefaultText[] = "TEST";
|
|
const char kDefaultCookie[] = "testCookie=testVal";
|
|
|
|
const char kSuccessMsg[] = "CorsTestHandler.Success";
|
|
const char kFailureMsg[] = "CorsTestHandler.Failure";
|
|
|
|
// Source that will handle the request.
|
|
enum class HandlerType {
|
|
SERVER,
|
|
HTTP_SCHEME,
|
|
CUSTOM_STANDARD_SCHEME,
|
|
CUSTOM_NONSTANDARD_SCHEME,
|
|
};
|
|
|
|
std::string GetOrigin(HandlerType handler) {
|
|
switch (handler) {
|
|
case HandlerType::SERVER:
|
|
return test_server::kServerOrigin;
|
|
case HandlerType::HTTP_SCHEME:
|
|
return "http://corstest.com";
|
|
case HandlerType::CUSTOM_STANDARD_SCHEME:
|
|
// Standard scheme that is CORS and fetch enabled.
|
|
// Registered in scheme_handler_unittest.cc.
|
|
return "customstdfetch://corstest";
|
|
case HandlerType::CUSTOM_NONSTANDARD_SCHEME:
|
|
// Non-sandard scheme that is not CORS or fetch enabled.
|
|
// Registered in scheme_handler_unittest.cc.
|
|
return "customnonstd:corstest";
|
|
}
|
|
NOTREACHED();
|
|
return std::string();
|
|
}
|
|
|
|
std::string GetPathURL(HandlerType handler, const std::string& path) {
|
|
return GetOrigin(handler) + path;
|
|
}
|
|
|
|
struct Resource {
|
|
// Uniquely identifies the resource.
|
|
HandlerType handler = HandlerType::SERVER;
|
|
std::string path;
|
|
// If non-empty the method value must match.
|
|
std::string method;
|
|
|
|
// Response information that will be returned.
|
|
CefRefPtr<CefResponse> response;
|
|
std::string response_data;
|
|
|
|
// Expected error code in OnLoadError.
|
|
cef_errorcode_t expected_error_code = ERR_NONE;
|
|
|
|
// Expected number of responses.
|
|
int expected_response_ct = 1;
|
|
|
|
// Expected number of OnQuery calls.
|
|
int expected_success_query_ct = 0;
|
|
int expected_failure_query_ct = 0;
|
|
|
|
// Actual number of responses.
|
|
int response_ct = 0;
|
|
|
|
// Actual number of OnQuery calls.
|
|
int success_query_ct = 0;
|
|
int failure_query_ct = 0;
|
|
|
|
Resource() {}
|
|
Resource(HandlerType request_handler,
|
|
const std::string& request_path,
|
|
const std::string& mime_type = kMimeTypeHtml,
|
|
const std::string& data = kDefaultHtml,
|
|
int status = 200) {
|
|
Init(request_handler, request_path, mime_type, data, status);
|
|
}
|
|
|
|
// Perform basic initialization.
|
|
void Init(HandlerType request_handler,
|
|
const std::string& request_path,
|
|
const std::string& mime_type = kMimeTypeHtml,
|
|
const std::string& data = kDefaultHtml,
|
|
int status = 200) {
|
|
handler = request_handler;
|
|
path = request_path;
|
|
response_data = data;
|
|
response = CefResponse::Create();
|
|
response->SetMimeType(mime_type);
|
|
response->SetStatus(status);
|
|
}
|
|
|
|
// Validate expected initial state.
|
|
void Validate() const {
|
|
DCHECK(!path.empty());
|
|
DCHECK(response);
|
|
DCHECK(!response->GetMimeType().empty());
|
|
DCHECK_EQ(0, response_ct);
|
|
DCHECK_GE(expected_response_ct, 0);
|
|
}
|
|
|
|
std::string GetPathURL() const { return ::GetPathURL(handler, path); }
|
|
|
|
// Returns true if all expectations have been met.
|
|
bool IsDone() const {
|
|
return response_ct == expected_response_ct &&
|
|
success_query_ct == expected_success_query_ct &&
|
|
failure_query_ct == expected_failure_query_ct;
|
|
}
|
|
|
|
void AssertDone() const {
|
|
EXPECT_EQ(expected_response_ct, response_ct) << GetPathURL();
|
|
EXPECT_EQ(expected_success_query_ct, success_query_ct) << GetPathURL();
|
|
EXPECT_EQ(expected_failure_query_ct, failure_query_ct) << GetPathURL();
|
|
}
|
|
|
|
// Optionally override to verify request contents.
|
|
virtual bool VerifyRequest(CefRefPtr<CefRequest> request) const {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
struct TestSetup {
|
|
// Available resources.
|
|
typedef std::vector<Resource*> ResourceList;
|
|
ResourceList resources;
|
|
|
|
// Used for testing received console messages.
|
|
std::vector<std::string> console_messages;
|
|
|
|
// If true cookies will be cleared after every test run.
|
|
bool clear_cookies = false;
|
|
|
|
void AddResource(Resource* resource) {
|
|
DCHECK(resource);
|
|
resource->Validate();
|
|
resources.push_back(resource);
|
|
}
|
|
|
|
void AddConsoleMessage(const std::string& message) {
|
|
DCHECK(!message.empty());
|
|
console_messages.push_back(message);
|
|
}
|
|
|
|
Resource* GetResource(const std::string& url,
|
|
const std::string& method = std::string()) const {
|
|
if (resources.empty())
|
|
return nullptr;
|
|
|
|
std::set<std::string> matching_methods;
|
|
if (method.empty()) {
|
|
// Match standard HTTP methods.
|
|
matching_methods.insert("GET");
|
|
matching_methods.insert("POST");
|
|
} else {
|
|
matching_methods.insert(method);
|
|
}
|
|
|
|
const std::string& path_url = test_request::GetPathURL(url);
|
|
ResourceList::const_iterator it = resources.begin();
|
|
for (; it != resources.end(); ++it) {
|
|
Resource* resource = *it;
|
|
if (resource->GetPathURL() == path_url &&
|
|
(resource->method.empty() ||
|
|
matching_methods.find(resource->method) != matching_methods.end())) {
|
|
return resource;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
Resource* GetResource(CefRefPtr<CefRequest> request) const {
|
|
return GetResource(request->GetURL(), request->GetMethod());
|
|
}
|
|
|
|
// Validate expected initial state.
|
|
void Validate() const { DCHECK(!resources.empty()); }
|
|
|
|
std::string GetMainURL() const { return resources.front()->GetPathURL(); }
|
|
|
|
// Returns true if the server will be used.
|
|
bool NeedsServer() const {
|
|
ResourceList::const_iterator it = resources.begin();
|
|
for (; it != resources.end(); ++it) {
|
|
Resource* resource = *it;
|
|
if (resource->handler == HandlerType::SERVER)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Returns true if all expectations have been met.
|
|
bool IsDone() const {
|
|
ResourceList::const_iterator it = resources.begin();
|
|
for (; it != resources.end(); ++it) {
|
|
Resource* resource = *it;
|
|
if (!resource->IsDone())
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void AssertDone() const {
|
|
ResourceList::const_iterator it = resources.begin();
|
|
for (; it != resources.end(); ++it) {
|
|
(*it)->AssertDone();
|
|
}
|
|
}
|
|
|
|
// Optionally override to verify cleared cookie contents.
|
|
virtual bool VerifyClearedCookies(
|
|
const test_request::CookieVector& cookies) const {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
class TestServerObserver : public test_server::ObserverHelper {
|
|
public:
|
|
typedef base::Callback<bool()> CheckDoneCallback;
|
|
|
|
TestServerObserver(TestSetup* setup,
|
|
const base::Closure& ready_callback,
|
|
const base::Closure& done_callback)
|
|
: setup_(setup),
|
|
ready_callback_(ready_callback),
|
|
done_callback_(done_callback),
|
|
weak_ptr_factory_(this) {
|
|
DCHECK(setup);
|
|
Initialize();
|
|
}
|
|
|
|
~TestServerObserver() override { done_callback_.Run(); }
|
|
|
|
void OnInitialized(const std::string& server_origin) override {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
ready_callback_.Run();
|
|
}
|
|
|
|
bool OnHttpRequest(CefRefPtr<CefServer> server,
|
|
int connection_id,
|
|
const CefString& client_address,
|
|
CefRefPtr<CefRequest> request) override {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
Resource* resource = setup_->GetResource(request);
|
|
if (!resource) {
|
|
// Not a request we handle.
|
|
return false;
|
|
}
|
|
|
|
resource->response_ct++;
|
|
EXPECT_TRUE(resource->VerifyRequest(request))
|
|
<< request->GetURL().ToString();
|
|
test_server::SendResponse(server, connection_id, resource->response,
|
|
resource->response_data);
|
|
|
|
// Stop propagating the callback.
|
|
return true;
|
|
}
|
|
|
|
void OnShutdown() override {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
delete this;
|
|
}
|
|
|
|
private:
|
|
TestSetup* const setup_;
|
|
const base::Closure ready_callback_;
|
|
const base::Closure done_callback_;
|
|
|
|
base::WeakPtrFactory<TestServerObserver> weak_ptr_factory_;
|
|
|
|
DISALLOW_COPY_AND_ASSIGN(TestServerObserver);
|
|
};
|
|
|
|
class CorsTestHandler : public RoutingTestHandler {
|
|
public:
|
|
explicit CorsTestHandler(TestSetup* setup) : setup_(setup) {
|
|
setup_->Validate();
|
|
}
|
|
|
|
void RunTest() override {
|
|
StartServer(base::Bind(&CorsTestHandler::TriggerCreateBrowser, this));
|
|
|
|
// Time out the test after a reasonable period of time.
|
|
SetTestTimeout();
|
|
}
|
|
|
|
// Necessary to make the method public in order to destroy the test from
|
|
// ClientSchemeHandlerType::ProcessRequest().
|
|
void DestroyTest() override {
|
|
EXPECT_TRUE(shutting_down_);
|
|
|
|
if (setup_->NeedsServer()) {
|
|
EXPECT_TRUE(got_stopped_server_);
|
|
} else {
|
|
EXPECT_FALSE(got_stopped_server_);
|
|
}
|
|
|
|
if (setup_->clear_cookies) {
|
|
EXPECT_TRUE(got_cleared_cookies_);
|
|
} else {
|
|
EXPECT_FALSE(got_cleared_cookies_);
|
|
}
|
|
|
|
setup_->AssertDone();
|
|
EXPECT_TRUE(setup_->console_messages.empty())
|
|
<< "Did not receive expected console message: "
|
|
<< setup_->console_messages.front();
|
|
|
|
RoutingTestHandler::DestroyTest();
|
|
}
|
|
|
|
CefRefPtr<CefResourceHandler> GetResourceHandler(
|
|
CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
CefRefPtr<CefRequest> request) override {
|
|
CEF_REQUIRE_IO_THREAD();
|
|
const std::string& url = request->GetURL();
|
|
const std::string& method = request->GetMethod();
|
|
if (method == "OPTIONS") {
|
|
// We should never see the CORS preflight request.
|
|
ADD_FAILURE() << "Unexpected CORS preflight for " << url;
|
|
}
|
|
|
|
Resource* resource = setup_->GetResource(request);
|
|
if (resource && resource->handler != HandlerType::SERVER) {
|
|
resource->response_ct++;
|
|
EXPECT_TRUE(resource->VerifyRequest(request)) << url;
|
|
return test_request::CreateResourceHandler(resource->response,
|
|
resource->response_data);
|
|
}
|
|
return RoutingTestHandler::GetResourceHandler(browser, frame, request);
|
|
}
|
|
|
|
void OnLoadEnd(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int httpStatusCode) override {
|
|
const std::string& url = frame->GetURL();
|
|
Resource* resource = GetResource(url);
|
|
if (!resource)
|
|
return;
|
|
|
|
const int expected_status = resource->response->GetStatus();
|
|
if (url == main_url_ || expected_status != 200) {
|
|
// Test that the status code is correct.
|
|
EXPECT_EQ(expected_status, httpStatusCode) << url;
|
|
}
|
|
|
|
TriggerDestroyTestIfDone();
|
|
}
|
|
|
|
void OnLoadError(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
ErrorCode errorCode,
|
|
const CefString& errorText,
|
|
const CefString& failedUrl) override {
|
|
Resource* resource = GetResource(failedUrl);
|
|
if (!resource)
|
|
return;
|
|
|
|
const cef_errorcode_t expected_error = resource->response->GetError();
|
|
|
|
// Tests sometimes also fail with ERR_ABORTED.
|
|
if (!(expected_error == ERR_NONE && errorCode == ERR_ABORTED)) {
|
|
EXPECT_EQ(expected_error, errorCode) << failedUrl.ToString();
|
|
}
|
|
|
|
TriggerDestroyTestIfDone();
|
|
}
|
|
|
|
bool OnQuery(CefRefPtr<CefBrowser> browser,
|
|
CefRefPtr<CefFrame> frame,
|
|
int64 query_id,
|
|
const CefString& request,
|
|
bool persistent,
|
|
CefRefPtr<Callback> callback) override {
|
|
Resource* resource = GetResource(frame->GetURL());
|
|
if (!resource)
|
|
return false;
|
|
|
|
if (request.ToString() == kSuccessMsg ||
|
|
request.ToString() == kFailureMsg) {
|
|
callback->Success("");
|
|
if (request.ToString() == kSuccessMsg)
|
|
resource->success_query_ct++;
|
|
else
|
|
resource->failure_query_ct++;
|
|
TriggerDestroyTestIfDone();
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool OnConsoleMessage(CefRefPtr<CefBrowser> browser,
|
|
cef_log_severity_t level,
|
|
const CefString& message,
|
|
const CefString& source,
|
|
int line) override {
|
|
bool expected = false;
|
|
if (!setup_->console_messages.empty()) {
|
|
std::vector<std::string>::iterator it = setup_->console_messages.begin();
|
|
for (; it != setup_->console_messages.end(); ++it) {
|
|
const std::string& possible = *it;
|
|
const std::string& actual = message.ToString();
|
|
if (actual.find(possible) == 0U) {
|
|
expected = true;
|
|
setup_->console_messages.erase(it);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
EXPECT_TRUE(expected) << "Unexpected console message: "
|
|
<< message.ToString();
|
|
return false;
|
|
}
|
|
|
|
protected:
|
|
void TriggerCreateBrowser() {
|
|
main_url_ = setup_->GetMainURL();
|
|
CreateBrowser(main_url_);
|
|
}
|
|
|
|
void TriggerDestroyTestIfDone() {
|
|
CefPostTask(TID_UI, base::Bind(&CorsTestHandler::DestroyTestIfDone, this));
|
|
}
|
|
|
|
void DestroyTestIfDone() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (shutting_down_)
|
|
return;
|
|
|
|
if (setup_->IsDone()) {
|
|
shutting_down_ = true;
|
|
StopServer();
|
|
}
|
|
}
|
|
|
|
void StartServer(const base::Closure& next_step) {
|
|
if (!CefCurrentlyOn(TID_UI)) {
|
|
CefPostTask(TID_UI,
|
|
base::Bind(&CorsTestHandler::StartServer, this, next_step));
|
|
return;
|
|
}
|
|
|
|
if (!setup_->NeedsServer()) {
|
|
next_step.Run();
|
|
return;
|
|
}
|
|
|
|
// Will delete itself after the server stops.
|
|
server_ = new TestServerObserver(
|
|
setup_, next_step, base::Bind(&CorsTestHandler::StoppedServer, this));
|
|
}
|
|
|
|
void StopServer() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (!server_) {
|
|
DCHECK(!setup_->NeedsServer());
|
|
AfterStoppedServer();
|
|
return;
|
|
}
|
|
|
|
// Results in a call to StoppedServer().
|
|
server_->Shutdown();
|
|
}
|
|
|
|
void StoppedServer() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
got_stopped_server_.yes();
|
|
server_ = nullptr;
|
|
AfterStoppedServer();
|
|
}
|
|
|
|
void AfterStoppedServer() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
if (setup_->clear_cookies) {
|
|
ClearCookies();
|
|
} else {
|
|
DestroyTest();
|
|
}
|
|
}
|
|
|
|
void ClearCookies() {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
DCHECK(setup_->clear_cookies);
|
|
test_request::GetAllCookies(
|
|
CefCookieManager::GetGlobalManager(nullptr), /*delete_cookies=*/true,
|
|
base::Bind(&CorsTestHandler::ClearedCookies, this));
|
|
}
|
|
|
|
void ClearedCookies(const test_request::CookieVector& cookies) {
|
|
CEF_REQUIRE_UI_THREAD();
|
|
got_cleared_cookies_.yes();
|
|
EXPECT_TRUE(setup_->VerifyClearedCookies(cookies));
|
|
DestroyTest();
|
|
}
|
|
|
|
Resource* GetResource(const std::string& url) const {
|
|
Resource* resource = setup_->GetResource(url);
|
|
EXPECT_TRUE(resource) << url;
|
|
return resource;
|
|
}
|
|
|
|
TestSetup* setup_;
|
|
std::string main_url_;
|
|
TestServerObserver* server_ = nullptr;
|
|
bool shutting_down_ = false;
|
|
|
|
TrackCallback got_stopped_server_;
|
|
TrackCallback got_cleared_cookies_;
|
|
|
|
IMPLEMENT_REFCOUNTING(CorsTestHandler);
|
|
DISALLOW_COPY_AND_ASSIGN(CorsTestHandler);
|
|
};
|
|
|
|
// JS that results in a call to CorsTestHandler::OnQuery.
|
|
std::string GetMsgJS(const std::string& msg) {
|
|
return "window.testQuery({request:'" + msg + "'});";
|
|
}
|
|
|
|
std::string GetSuccessMsgJS() {
|
|
return GetMsgJS(kSuccessMsg);
|
|
}
|
|
std::string GetFailureMsgJS() {
|
|
return GetMsgJS(kFailureMsg);
|
|
}
|
|
|
|
std::string GetDefaultSuccessMsgHtml() {
|
|
return "<html><body>TEST<script>" + GetSuccessMsgJS() +
|
|
"</script></body></html>";
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Verify the test harness for server requests.
|
|
TEST(CorsTest, BasicServer) {
|
|
TestSetup setup;
|
|
Resource resource(HandlerType::SERVER, "/CorsTest.BasicServer");
|
|
setup.AddResource(&resource);
|
|
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Like above, but also send a query JS message.
|
|
TEST(CorsTest, BasicServerWithQuery) {
|
|
TestSetup setup;
|
|
Resource resource(HandlerType::SERVER, "/CorsTest.BasicServerWithQuery",
|
|
kMimeTypeHtml, GetDefaultSuccessMsgHtml());
|
|
resource.expected_success_query_ct = 1;
|
|
setup.AddResource(&resource);
|
|
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Verify the test harness for http scheme requests.
|
|
TEST(CorsTest, BasicHttpScheme) {
|
|
TestSetup setup;
|
|
Resource resource(HandlerType::HTTP_SCHEME, "/CorsTest.BasicHttpScheme");
|
|
setup.AddResource(&resource);
|
|
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Like above, but also send a query JS message.
|
|
TEST(CorsTest, BasicHttpSchemeWithQuery) {
|
|
TestSetup setup;
|
|
Resource resource(HandlerType::HTTP_SCHEME,
|
|
"/CorsTest.BasicHttpSchemeWithQuery", kMimeTypeHtml,
|
|
GetDefaultSuccessMsgHtml());
|
|
resource.expected_success_query_ct = 1;
|
|
setup.AddResource(&resource);
|
|
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Verify the test harness for custom standard scheme requests.
|
|
TEST(CorsTest, BasicCustomStandardScheme) {
|
|
TestSetup setup;
|
|
Resource resource(HandlerType::CUSTOM_STANDARD_SCHEME,
|
|
"/CorsTest.BasicCustomStandardScheme");
|
|
setup.AddResource(&resource);
|
|
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
// Like above, but also send a query JS message.
|
|
TEST(CorsTest, BasicCustomStandardSchemeWithQuery) {
|
|
TestSetup setup;
|
|
Resource resource(HandlerType::CUSTOM_STANDARD_SCHEME,
|
|
"/CorsTest.BasicCustomStandardSchemeWithQuery",
|
|
kMimeTypeHtml, GetDefaultSuccessMsgHtml());
|
|
resource.expected_success_query_ct = 1;
|
|
setup.AddResource(&resource);
|
|
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup);
|
|
handler->ExecuteTest();
|
|
ReleaseAndWaitForDestructor(handler);
|
|
}
|
|
|
|
namespace {
|
|
|
|
struct CookieTestSetup : TestSetup {
|
|
CookieTestSetup() {}
|
|
|
|
bool expect_cookie = false;
|
|
|
|
bool VerifyClearedCookies(
|
|
const test_request::CookieVector& cookies) const override {
|
|
if (!expect_cookie) {
|
|
EXPECT_TRUE(cookies.empty());
|
|
return cookies.empty();
|
|
}
|
|
|
|
EXPECT_EQ(1U, cookies.size());
|
|
const std::string& cookie = CefString(&cookies[0].name).ToString() + "=" +
|
|
CefString(&cookies[0].value).ToString();
|
|
EXPECT_STREQ(kDefaultCookie, cookie.c_str());
|
|
return cookie == kDefaultCookie;
|
|
}
|
|
};
|
|
|
|
struct CookieResource : Resource {
|
|
CookieResource() {}
|
|
|
|
bool expect_cookie = false;
|
|
|
|
void InitSetCookie() {
|
|
response->SetHeaderByName("Set-Cookie", kDefaultCookie,
|
|
/*override=*/true);
|
|
}
|
|
|
|
bool VerifyRequest(CefRefPtr<CefRequest> request) const override {
|
|
const std::string& cookie = request->GetHeaderByName("Cookie");
|
|
const std::string& expected_cookie =
|
|
expect_cookie ? kDefaultCookie : std::string();
|
|
EXPECT_STREQ(expected_cookie.c_str(), cookie.c_str()) << GetPathURL();
|
|
return expected_cookie == cookie;
|
|
}
|
|
};
|
|
|
|
void SetupCookieExpectations(CookieTestSetup* setup,
|
|
CookieResource* main_resource,
|
|
CookieResource* sub_resource) {
|
|
// All schemes except custom non-standard support cookies.
|
|
const bool supports_cookies =
|
|
main_resource->handler != HandlerType::CUSTOM_NONSTANDARD_SCHEME;
|
|
|
|
// The main resource may set the cookie (if cookies are supported), but should
|
|
// not receive one.
|
|
main_resource->InitSetCookie();
|
|
main_resource->expect_cookie = false;
|
|
|
|
// A cookie will be set only for schemes that support cookies.
|
|
setup->expect_cookie = supports_cookies;
|
|
// Always clear cookies so we can verify that one wasn't set unexpectedly.
|
|
setup->clear_cookies = true;
|
|
|
|
// Expect the sub-resource to receive the cookie for same-origin requests
|
|
// only.
|
|
sub_resource->expect_cookie =
|
|
supports_cookies && main_resource->handler == sub_resource->handler;
|
|
}
|
|
|
|
std::string GetIframeMainHtml(const std::string& iframe_url,
|
|
const std::string& sandbox_attribs) {
|
|
return "<html><body>TEST<iframe src=\"" + iframe_url + "\" sandbox=\"" +
|
|
sandbox_attribs + "\"></iframe></body></html>";
|
|
}
|
|
|
|
std::string GetIframeSubHtml() {
|
|
// Try to script the parent frame, then send the SuccessMsg.
|
|
return "<html><body>TEST<script>try { parent.document.body; } catch "
|
|
"(exception) { console.log(exception.toString()); }" +
|
|
GetSuccessMsgJS() + "</script></body></html>";
|
|
}
|
|
|
|
bool HasSandboxAttrib(const std::string& sandbox_attribs,
|
|
const std::string& attrib) {
|
|
return sandbox_attribs.find(attrib) != std::string::npos;
|
|
}
|
|
|
|
void SetupIframeRequest(CookieTestSetup* setup,
|
|
const std::string& test_name,
|
|
HandlerType main_handler,
|
|
CookieResource* main_resource,
|
|
HandlerType iframe_handler,
|
|
CookieResource* iframe_resource,
|
|
const std::string& sandbox_attribs) {
|
|
const std::string& base_path = "/" + test_name;
|
|
|
|
// Expect a single iframe request.
|
|
iframe_resource->Init(iframe_handler, base_path + ".iframe.html",
|
|
kMimeTypeHtml, GetIframeSubHtml());
|
|
|
|
// Expect a single main frame request.
|
|
const std::string& iframe_url = iframe_resource->GetPathURL();
|
|
main_resource->Init(main_handler, base_path, kMimeTypeHtml,
|
|
GetIframeMainHtml(iframe_url, sandbox_attribs));
|
|
|
|
SetupCookieExpectations(setup, main_resource, iframe_resource);
|
|
|
|
if (HasSandboxAttrib(sandbox_attribs, "allow-scripts")) {
|
|
// Expect the iframe to load successfully and send the SuccessMsg.
|
|
iframe_resource->expected_success_query_ct = 1;
|
|
|
|
const bool has_same_origin =
|
|
HasSandboxAttrib(sandbox_attribs, "allow-same-origin");
|
|
if (!has_same_origin ||
|
|
(has_same_origin &&
|
|
(main_handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME ||
|
|
main_handler != iframe_handler))) {
|
|
// Expect parent frame scripting to fail if:
|
|
// - "allow-same-origin" is not specified;
|
|
// - the main frame is a non-standard scheme (e.g. CORS disabled);
|
|
// - the main frame and iframe origins don't match.
|
|
// The reported origin will be "null" if "allow-same-origin" is not
|
|
// specified, or if the iframe is hosted on a non-standard scheme.
|
|
const std::string& origin =
|
|
!has_same_origin ||
|
|
iframe_handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME
|
|
? "null"
|
|
: GetOrigin(iframe_handler);
|
|
setup->AddConsoleMessage("SecurityError: Blocked a frame with origin \"" +
|
|
origin +
|
|
"\" from accessing a cross-origin frame.");
|
|
}
|
|
} else {
|
|
// Expect JavaScript execution to fail.
|
|
setup->AddConsoleMessage("Blocked script execution in '" + iframe_url +
|
|
"' because the document's frame is sandboxed and "
|
|
"the 'allow-scripts' permission is not set.");
|
|
}
|
|
|
|
setup->AddResource(main_resource);
|
|
setup->AddResource(iframe_resource);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Test iframe sandbox attributes with different origin combinations.
|
|
#define CORS_TEST_IFRAME(test_name, handler_main, handler_iframe, \
|
|
sandbox_attribs) \
|
|
TEST(CorsTest, Iframe##test_name) { \
|
|
CookieTestSetup setup; \
|
|
CookieResource resource_main, resource_iframe; \
|
|
SetupIframeRequest(&setup, "CorsTest.Iframe" #test_name, \
|
|
HandlerType::handler_main, &resource_main, \
|
|
HandlerType::handler_iframe, &resource_iframe, \
|
|
sandbox_attribs); \
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup); \
|
|
handler->ExecuteTest(); \
|
|
ReleaseAndWaitForDestructor(handler); \
|
|
}
|
|
|
|
// Test all origin combinations (same and cross-origin).
|
|
#define CORS_TEST_IFRAME_ALL(name, sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##ServerToServer, SERVER, SERVER, sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##ServerToHttpScheme, SERVER, HTTP_SCHEME, \
|
|
sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##ServerToCustomStandardScheme, SERVER, \
|
|
CUSTOM_STANDARD_SCHEME, sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##ServerToCustomNonStandardScheme, SERVER, \
|
|
CUSTOM_NONSTANDARD_SCHEME, sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##HttpSchemeToServer, HTTP_SCHEME, SERVER, \
|
|
sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##HttpSchemeToHttpScheme, HTTP_SCHEME, HTTP_SCHEME, \
|
|
sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##HttpSchemeToCustomStandardScheme, HTTP_SCHEME, \
|
|
CUSTOM_STANDARD_SCHEME, sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##HttpSchemeToCustomNonStandardScheme, HTTP_SCHEME, \
|
|
CUSTOM_NONSTANDARD_SCHEME, sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##CustomStandardSchemeToServer, CUSTOM_STANDARD_SCHEME, \
|
|
SERVER, sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##CustomStandardSchemeToHttpScheme, \
|
|
CUSTOM_STANDARD_SCHEME, HTTP_SCHEME, sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##CustomStandardSchemeToCustomStandardScheme, \
|
|
CUSTOM_STANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, \
|
|
sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##CustomStandardSchemeToCustomNonStandardScheme, \
|
|
CUSTOM_STANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME, \
|
|
sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##CustomNonStandardSchemeToServer, \
|
|
CUSTOM_NONSTANDARD_SCHEME, SERVER, sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##CustomNonStandardSchemeToHttpScheme, \
|
|
CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME, sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##CustomNonStandardSchemeToCustomStandardScheme, \
|
|
CUSTOM_NONSTANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, \
|
|
sandbox_attribs) \
|
|
CORS_TEST_IFRAME(name##CustomNonStandardSchemeToCustomNonStandardScheme, \
|
|
CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME, \
|
|
sandbox_attribs)
|
|
|
|
// Everything is blocked.
|
|
CORS_TEST_IFRAME_ALL(None, "")
|
|
|
|
// JavaScript execution is allowed.
|
|
CORS_TEST_IFRAME_ALL(AllowScripts, "allow-scripts")
|
|
|
|
// JavaScript execution is allowed and scripting the parent is allowed for
|
|
// same-origin only.
|
|
CORS_TEST_IFRAME_ALL(AllowScriptsAndSameOrigin,
|
|
"allow-scripts allow-same-origin")
|
|
|
|
namespace {
|
|
|
|
const char kSubRequestMethod[] = "GET";
|
|
const char kSubUnsafeHeaderName[] = "x-unsafe-header";
|
|
const char kSubUnsafeHeaderValue[] = "not-safe";
|
|
|
|
struct SubResource : CookieResource {
|
|
SubResource() {}
|
|
|
|
std::string main_origin;
|
|
bool supports_cors = false;
|
|
bool is_cross_origin = false;
|
|
|
|
void InitCors(HandlerType main_handler, bool add_header) {
|
|
// Must specify the method to differentiate from the preflight request.
|
|
method = kSubRequestMethod;
|
|
|
|
// Origin is always "null" for non-standard schemes.
|
|
main_origin = main_handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME
|
|
? "null"
|
|
: GetOrigin(main_handler);
|
|
|
|
// True if cross-origin requests are allowed. XHR requests to non-standard
|
|
// schemes are not allowed (due to the "null" origin).
|
|
supports_cors = handler != HandlerType::CUSTOM_NONSTANDARD_SCHEME;
|
|
if (!supports_cors) {
|
|
// Don't expect the xhr request.
|
|
expected_response_ct = 0;
|
|
}
|
|
|
|
// True if the request is considered cross-origin. Any requests between
|
|
// non-standard schemes are considered cross-origin (due to the "null"
|
|
// origin).
|
|
is_cross_origin = main_handler != handler ||
|
|
(main_handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME &&
|
|
handler == main_handler);
|
|
|
|
if (is_cross_origin && add_header) {
|
|
response->SetHeaderByName("Access-Control-Allow-Origin", main_origin,
|
|
false);
|
|
}
|
|
}
|
|
|
|
bool VerifyRequest(CefRefPtr<CefRequest> request) const override {
|
|
if (!CookieResource::VerifyRequest(request))
|
|
return false;
|
|
|
|
const std::string& request_method = request->GetMethod();
|
|
EXPECT_STREQ(method.c_str(), request_method.c_str()) << GetPathURL();
|
|
if (request_method != method)
|
|
return false;
|
|
|
|
// Verify that the "Origin" header contains the expected value.
|
|
const std::string& origin = request->GetHeaderByName("Origin");
|
|
const std::string& expected_origin =
|
|
is_cross_origin ? main_origin : std::string();
|
|
EXPECT_STREQ(expected_origin.c_str(), origin.c_str()) << GetPathURL();
|
|
if (expected_origin != origin)
|
|
return false;
|
|
|
|
// Verify that the "X-Unsafe-Header" header contains the expected value.
|
|
const std::string& unsafe_header =
|
|
request->GetHeaderByName(kSubUnsafeHeaderName);
|
|
EXPECT_STREQ(kSubUnsafeHeaderValue, unsafe_header.c_str()) << GetPathURL();
|
|
return unsafe_header == kSubUnsafeHeaderValue;
|
|
}
|
|
};
|
|
|
|
// See https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request
|
|
// for details of CORS preflight behavior.
|
|
struct PreflightResource : Resource {
|
|
std::string main_origin;
|
|
|
|
void InitPreflight(HandlerType main_handler) {
|
|
// CORS preflight requests originate from PreflightController in the network
|
|
// process, so we only expect them for server requests.
|
|
EXPECT_EQ(HandlerType::SERVER, handler);
|
|
|
|
// Origin is always "null" for non-standard schemes.
|
|
main_origin = main_handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME
|
|
? "null"
|
|
: GetOrigin(main_handler);
|
|
|
|
method = "OPTIONS";
|
|
response->SetHeaderByName("Access-Control-Allow-Methods",
|
|
"GET,HEAD,OPTIONS,POST", false);
|
|
response->SetHeaderByName("Access-Control-Allow-Headers",
|
|
kSubUnsafeHeaderName, false);
|
|
response->SetHeaderByName("Access-Control-Allow-Origin", main_origin,
|
|
false);
|
|
}
|
|
|
|
bool VerifyRequest(CefRefPtr<CefRequest> request) const override {
|
|
const std::string& request_method = request->GetMethod();
|
|
EXPECT_STREQ(method.c_str(), request_method.c_str()) << GetPathURL();
|
|
if (request_method != method)
|
|
return false;
|
|
|
|
const std::string& origin = request->GetHeaderByName("Origin");
|
|
EXPECT_STREQ(main_origin.c_str(), origin.c_str()) << GetPathURL();
|
|
if (main_origin != origin)
|
|
return false;
|
|
|
|
const std::string& ac_request_method =
|
|
request->GetHeaderByName("Access-Control-Request-Method");
|
|
EXPECT_STREQ(kSubRequestMethod, ac_request_method.c_str()) << GetPathURL();
|
|
if (ac_request_method != kSubRequestMethod)
|
|
return false;
|
|
|
|
const std::string& ac_request_headers =
|
|
request->GetHeaderByName("Access-Control-Request-Headers");
|
|
EXPECT_STREQ(kSubUnsafeHeaderName, ac_request_headers.c_str())
|
|
<< GetPathURL();
|
|
if (ac_request_headers != kSubUnsafeHeaderName)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
enum class ExecMode {
|
|
XHR,
|
|
FETCH,
|
|
};
|
|
|
|
std::string GetXhrExecJS(const std::string& sub_url) {
|
|
// Inclusion of an unsafe header triggers CORS preflight for cross-origin
|
|
// requests to the server.
|
|
return "xhr = new XMLHttpRequest();\n"
|
|
"xhr.open(\"GET\", \"" +
|
|
sub_url +
|
|
"\", true)\n;"
|
|
"xhr.setRequestHeader('" +
|
|
kSubUnsafeHeaderName + "', '" + kSubUnsafeHeaderValue +
|
|
"');\n"
|
|
"xhr.onload = function(e) {\n"
|
|
" if (xhr.readyState === 4) {\n"
|
|
" if (xhr.status === 200) {\n"
|
|
" onResult(xhr.responseText);\n"
|
|
" } else {\n"
|
|
" console.log('XMLHttpRequest failed with status ' + "
|
|
"xhr.status);\n"
|
|
" onResult('FAILURE');\n"
|
|
" }\n"
|
|
" }\n"
|
|
"};\n"
|
|
"xhr.onerror = function(e) {\n"
|
|
" onResult('FAILURE');\n"
|
|
"};\n"
|
|
"xhr.send();\n";
|
|
}
|
|
|
|
std::string GetFetchExecJS(const std::string& sub_url) {
|
|
// Inclusion of an unsafe header triggers CORS preflight for cross-origin
|
|
// requests to the server.
|
|
return std::string() +
|
|
"let h = new Headers();\n"
|
|
"h.append('" +
|
|
kSubUnsafeHeaderName + "', '" + kSubUnsafeHeaderValue +
|
|
"');\n"
|
|
"fetch('" +
|
|
sub_url +
|
|
"', {headers: h})\n"
|
|
".then(function(response) {\n"
|
|
" if (response.status === 200) {\n"
|
|
" response.text().then(function(text) {\n"
|
|
" onResult(text);\n"
|
|
" }).catch(function(e) {\n"
|
|
" onResult('FAILURE')\n; "
|
|
" })\n;"
|
|
" } else {\n"
|
|
" onResult('FAILURE');\n"
|
|
" }\n"
|
|
"}).catch(function(e) {\n"
|
|
" onResult('FAILURE');\n"
|
|
"});\n";
|
|
}
|
|
|
|
std::string GetExecMainHtml(ExecMode mode, const std::string& sub_url) {
|
|
return std::string() +
|
|
"<html><head>\n"
|
|
"<script language=\"JavaScript\">\n" +
|
|
"function onResult(val) {\n"
|
|
" if (val === '" +
|
|
kDefaultText + "') {" + GetSuccessMsgJS() + "} else {" +
|
|
GetFailureMsgJS() +
|
|
"}\n}\n"
|
|
"function execRequest() {\n" +
|
|
(mode == ExecMode::XHR ? GetXhrExecJS(sub_url)
|
|
: GetFetchExecJS(sub_url)) +
|
|
"}\n</script>\n"
|
|
"</head><body onload=\"execRequest();\">"
|
|
"Running execRequest..."
|
|
"</body></html>";
|
|
}
|
|
|
|
// XHR and fetch requests behave the same, except for console message contents.
|
|
// In addition to basic CORS header behaviors and request blocking, this test
|
|
// verifies that CORS preflight requests are sent and received when expected.
|
|
// Since preflight behavior is implemented in the network process we expect it
|
|
// to already have substantial test coverage in Chromium.
|
|
void SetupExecRequest(ExecMode mode,
|
|
CookieTestSetup* setup,
|
|
const std::string& test_name,
|
|
HandlerType main_handler,
|
|
CookieResource* main_resource,
|
|
HandlerType sub_handler,
|
|
SubResource* sub_resource,
|
|
PreflightResource* preflight_resource,
|
|
bool add_header) {
|
|
const std::string& base_path = "/" + test_name;
|
|
|
|
// Expect a single xhr request.
|
|
const std::string& sub_path = base_path + ".sub.txt";
|
|
sub_resource->Init(sub_handler, sub_path, kMimeTypeText, kDefaultText);
|
|
sub_resource->InitCors(main_handler, add_header);
|
|
|
|
// Expect a single main frame request.
|
|
const std::string& sub_url = sub_resource->GetPathURL();
|
|
main_resource->Init(main_handler, base_path, kMimeTypeHtml,
|
|
GetExecMainHtml(mode, sub_url));
|
|
|
|
SetupCookieExpectations(setup, main_resource, sub_resource);
|
|
|
|
// Cross-origin requests to a server sub-resource will receive a CORS
|
|
// preflight request because we add an unsafe header.
|
|
const bool expect_cors_preflight =
|
|
sub_resource->is_cross_origin && sub_handler == HandlerType::SERVER;
|
|
|
|
if (sub_resource->is_cross_origin &&
|
|
(!sub_resource->supports_cors || !add_header)) {
|
|
// Expect the cross-origin XHR to be blocked.
|
|
main_resource->expected_failure_query_ct = 1;
|
|
|
|
if (sub_resource->supports_cors && !add_header) {
|
|
// The request supports CORS, but we didn't add the
|
|
// "Access-Control-Allow-Origin" header.
|
|
if (!expect_cors_preflight || preflight_resource != nullptr) {
|
|
// This is the error message when not expecting a CORS preflight
|
|
// request, or when the preflight request is handled by the server.
|
|
// Unhandled preflight requests will output a different error message
|
|
// (see below).
|
|
if (mode == ExecMode::XHR) {
|
|
setup->AddConsoleMessage(
|
|
"Access to XMLHttpRequest at '" + sub_url + "' from origin '" +
|
|
sub_resource->main_origin +
|
|
"' has been blocked by CORS policy: No "
|
|
"'Access-Control-Allow-Origin' "
|
|
"header is present on the requested resource.");
|
|
} else {
|
|
setup->AddConsoleMessage(
|
|
"Access to fetch at '" + sub_url + "' from origin '" +
|
|
sub_resource->main_origin +
|
|
"' has been blocked by CORS policy: No "
|
|
"'Access-Control-Allow-Origin' header is present on the "
|
|
"requested "
|
|
"resource. If an opaque response serves your needs, set the "
|
|
"request's mode to 'no-cors' to fetch the resource with CORS "
|
|
"disabled.");
|
|
}
|
|
}
|
|
} else if (mode == ExecMode::XHR) {
|
|
setup->AddConsoleMessage(
|
|
"Access to XMLHttpRequest at '" + sub_url + "' from origin '" +
|
|
sub_resource->main_origin +
|
|
"' has been blocked by CORS policy: Cross origin requests are only "
|
|
"supported for protocol schemes:");
|
|
} else {
|
|
setup->AddConsoleMessage(
|
|
"Fetch API cannot load " + sub_url +
|
|
". URL scheme must be \"http\" or \"https\" for CORS request.");
|
|
}
|
|
} else {
|
|
// Expect the (possibly cross-origin) XHR to be allowed.
|
|
main_resource->expected_success_query_ct = 1;
|
|
}
|
|
|
|
setup->AddResource(main_resource);
|
|
setup->AddResource(sub_resource);
|
|
|
|
if (expect_cors_preflight) {
|
|
// Expect a CORS preflight request.
|
|
if (preflight_resource) {
|
|
// The server will handle the preflight request. The cross-origin XHR may
|
|
// still be blocked if the "Access-Control-Allow-Origin" header is missing
|
|
// (see above).
|
|
preflight_resource->Init(sub_handler, sub_path, kMimeTypeText,
|
|
std::string());
|
|
preflight_resource->InitPreflight(main_handler);
|
|
setup->AddResource(preflight_resource);
|
|
} else {
|
|
// The server will not handle the preflight request. Expect the
|
|
// cross-origin XHR to be blocked.
|
|
main_resource->expected_failure_query_ct = 1;
|
|
main_resource->expected_success_query_ct = 0;
|
|
sub_resource->expected_response_ct = 0;
|
|
|
|
if (mode == ExecMode::XHR) {
|
|
setup->AddConsoleMessage(
|
|
"Access to XMLHttpRequest at '" + sub_url + "' from origin '" +
|
|
sub_resource->main_origin +
|
|
"' has been blocked by CORS policy: Response to preflight request "
|
|
"doesn't pass access control check: No "
|
|
"'Access-Control-Allow-Origin' header is present on the requested "
|
|
"resource.");
|
|
} else {
|
|
setup->AddConsoleMessage(
|
|
"Access to fetch at '" + sub_url + "' from origin '" +
|
|
sub_resource->main_origin +
|
|
"' has been blocked by CORS policy: Response to preflight request "
|
|
"doesn't pass access control check: No "
|
|
"'Access-Control-Allow-Origin' header is present on the requested "
|
|
"resource. If an opaque response serves your needs, set the "
|
|
"request's mode to 'no-cors' to fetch the resource with CORS "
|
|
"disabled.");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Test XHR requests with different origin combinations.
|
|
#define CORS_TEST_XHR(test_name, handler_main, handler_sub, add_header) \
|
|
TEST(CorsTest, Xhr##test_name) { \
|
|
CookieTestSetup setup; \
|
|
CookieResource resource_main; \
|
|
SubResource resource_sub; \
|
|
PreflightResource resource_preflight; \
|
|
SetupExecRequest(ExecMode::XHR, &setup, "CorsTest.Xhr" #test_name, \
|
|
HandlerType::handler_main, &resource_main, \
|
|
HandlerType::handler_sub, &resource_sub, \
|
|
&resource_preflight, add_header); \
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup); \
|
|
handler->ExecuteTest(); \
|
|
ReleaseAndWaitForDestructor(handler); \
|
|
}
|
|
|
|
// Test all origin combinations (same and cross-origin).
|
|
#define CORS_TEST_XHR_ALL(name, add_header) \
|
|
CORS_TEST_XHR(name##ServerToServer, SERVER, SERVER, add_header) \
|
|
CORS_TEST_XHR(name##ServerToHttpScheme, SERVER, HTTP_SCHEME, add_header) \
|
|
CORS_TEST_XHR(name##ServerToCustomStandardScheme, SERVER, \
|
|
CUSTOM_STANDARD_SCHEME, add_header) \
|
|
CORS_TEST_XHR(name##ServerToCustomNonStandardScheme, SERVER, \
|
|
CUSTOM_NONSTANDARD_SCHEME, add_header) \
|
|
CORS_TEST_XHR(name##HttpSchemeToServer, HTTP_SCHEME, SERVER, add_header) \
|
|
CORS_TEST_XHR(name##HttpSchemeToHttpScheme, HTTP_SCHEME, HTTP_SCHEME, \
|
|
add_header) \
|
|
CORS_TEST_XHR(name##HttpSchemeToCustomStandardScheme, HTTP_SCHEME, \
|
|
CUSTOM_STANDARD_SCHEME, add_header) \
|
|
CORS_TEST_XHR(name##HttpSchemeToCustomNonStandardScheme, HTTP_SCHEME, \
|
|
CUSTOM_NONSTANDARD_SCHEME, add_header) \
|
|
CORS_TEST_XHR(name##CustomStandardSchemeToServer, CUSTOM_STANDARD_SCHEME, \
|
|
SERVER, add_header) \
|
|
CORS_TEST_XHR(name##CustomStandardSchemeToHttpScheme, \
|
|
CUSTOM_STANDARD_SCHEME, HTTP_SCHEME, add_header) \
|
|
CORS_TEST_XHR(name##CustomStandardSchemeToCustomStandardScheme, \
|
|
CUSTOM_STANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, add_header) \
|
|
CORS_TEST_XHR(name##CustomStandardSchemeToCustomNonStandardScheme, \
|
|
CUSTOM_STANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME, add_header) \
|
|
CORS_TEST_XHR(name##CustomNonStandardSchemeToServer, \
|
|
CUSTOM_NONSTANDARD_SCHEME, SERVER, add_header) \
|
|
CORS_TEST_XHR(name##CustomNonStandardSchemeToHttpScheme, \
|
|
CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME, add_header) \
|
|
CORS_TEST_XHR(name##CustomNonStandardSchemeToCustomStandardScheme, \
|
|
CUSTOM_NONSTANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, add_header) \
|
|
CORS_TEST_XHR(name##CustomNonStandardSchemeToCustomNonStandardScheme, \
|
|
CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME, \
|
|
add_header)
|
|
|
|
// XHR requests without the "Access-Control-Allow-Origin" header.
|
|
CORS_TEST_XHR_ALL(NoHeader, false)
|
|
|
|
// XHR requests with the "Access-Control-Allow-Origin" header.
|
|
CORS_TEST_XHR_ALL(WithHeader, true)
|
|
|
|
// Like above, but without handling CORS preflight requests.
|
|
#define CORS_TEST_XHR_NO_PREFLIGHT(test_name, handler_main, handler_sub, \
|
|
add_header) \
|
|
TEST(CorsTest, Xhr##test_name) { \
|
|
CookieTestSetup setup; \
|
|
CookieResource resource_main; \
|
|
SubResource resource_sub; \
|
|
SetupExecRequest(ExecMode::XHR, &setup, "CorsTest.Xhr" #test_name, \
|
|
HandlerType::handler_main, &resource_main, \
|
|
HandlerType::handler_sub, &resource_sub, nullptr, \
|
|
add_header); \
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup); \
|
|
handler->ExecuteTest(); \
|
|
ReleaseAndWaitForDestructor(handler); \
|
|
}
|
|
|
|
#define CORS_TEST_XHR_NO_PREFLIGHT_SERVER(name, add_header) \
|
|
CORS_TEST_XHR_NO_PREFLIGHT(name##ServerToServer, SERVER, SERVER, add_header) \
|
|
CORS_TEST_XHR_NO_PREFLIGHT(name##HttpSchemeToServer, HTTP_SCHEME, SERVER, \
|
|
add_header) \
|
|
CORS_TEST_XHR_NO_PREFLIGHT(name##CustomStandardSchemeToServer, \
|
|
CUSTOM_STANDARD_SCHEME, SERVER, add_header) \
|
|
CORS_TEST_XHR_NO_PREFLIGHT(name##CustomNonStandardSchemeToServer, \
|
|
CUSTOM_NONSTANDARD_SCHEME, SERVER, add_header)
|
|
|
|
// XHR requests without the "Access-Control-Allow-Origin" header.
|
|
CORS_TEST_XHR_NO_PREFLIGHT_SERVER(NoHeaderNoPreflight, false)
|
|
|
|
// XHR requests with the "Access-Control-Allow-Origin" header.
|
|
CORS_TEST_XHR_NO_PREFLIGHT_SERVER(WithHeaderNoPreflight, true)
|
|
|
|
// Test fetch requests with different origin combinations.
|
|
#define CORS_TEST_FETCH(test_name, handler_main, handler_sub, add_header) \
|
|
TEST(CorsTest, Fetch##test_name) { \
|
|
CookieTestSetup setup; \
|
|
CookieResource resource_main; \
|
|
SubResource resource_sub; \
|
|
PreflightResource resource_preflight; \
|
|
SetupExecRequest(ExecMode::FETCH, &setup, "CorsTest.Fetch" #test_name, \
|
|
HandlerType::handler_main, &resource_main, \
|
|
HandlerType::handler_sub, &resource_sub, \
|
|
&resource_preflight, add_header); \
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup); \
|
|
handler->ExecuteTest(); \
|
|
ReleaseAndWaitForDestructor(handler); \
|
|
}
|
|
|
|
// Test all origin combinations (same and cross-origin).
|
|
#define CORS_TEST_FETCH_ALL(name, add_header) \
|
|
CORS_TEST_FETCH(name##ServerToServer, SERVER, SERVER, add_header) \
|
|
CORS_TEST_FETCH(name##ServerToHttpScheme, SERVER, HTTP_SCHEME, add_header) \
|
|
CORS_TEST_FETCH(name##ServerToCustomStandardScheme, SERVER, \
|
|
CUSTOM_STANDARD_SCHEME, add_header) \
|
|
CORS_TEST_FETCH(name##ServerToCustomNonStandardScheme, SERVER, \
|
|
CUSTOM_NONSTANDARD_SCHEME, add_header) \
|
|
CORS_TEST_FETCH(name##HttpSchemeToServer, HTTP_SCHEME, SERVER, add_header) \
|
|
CORS_TEST_FETCH(name##HttpSchemeToHttpScheme, HTTP_SCHEME, HTTP_SCHEME, \
|
|
add_header) \
|
|
CORS_TEST_FETCH(name##HttpSchemeToCustomStandardScheme, HTTP_SCHEME, \
|
|
CUSTOM_STANDARD_SCHEME, add_header) \
|
|
CORS_TEST_FETCH(name##HttpSchemeToCustomNonStandardScheme, HTTP_SCHEME, \
|
|
CUSTOM_NONSTANDARD_SCHEME, add_header) \
|
|
CORS_TEST_FETCH(name##CustomStandardSchemeToServer, CUSTOM_STANDARD_SCHEME, \
|
|
SERVER, add_header) \
|
|
CORS_TEST_FETCH(name##CustomStandardSchemeToHttpScheme, \
|
|
CUSTOM_STANDARD_SCHEME, HTTP_SCHEME, add_header) \
|
|
CORS_TEST_FETCH(name##CustomStandardSchemeToCustomStandardScheme, \
|
|
CUSTOM_STANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, add_header) \
|
|
CORS_TEST_FETCH(name##CustomStandardSchemeToCustomNonStandardScheme, \
|
|
CUSTOM_STANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME, \
|
|
add_header) \
|
|
CORS_TEST_FETCH(name##CustomNonStandardSchemeToServer, \
|
|
CUSTOM_NONSTANDARD_SCHEME, SERVER, add_header) \
|
|
CORS_TEST_FETCH(name##CustomNonStandardSchemeToHttpScheme, \
|
|
CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME, add_header) \
|
|
CORS_TEST_FETCH(name##CustomNonStandardSchemeToCustomStandardScheme, \
|
|
CUSTOM_NONSTANDARD_SCHEME, CUSTOM_STANDARD_SCHEME, \
|
|
add_header) \
|
|
CORS_TEST_FETCH(name##CustomNonStandardSchemeToCustomNonStandardScheme, \
|
|
CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME, \
|
|
add_header)
|
|
|
|
// Fetch requests without the "Access-Control-Allow-Origin" header.
|
|
CORS_TEST_FETCH_ALL(NoHeader, false)
|
|
|
|
// Fetch requests with the "Access-Control-Allow-Origin" header.
|
|
CORS_TEST_FETCH_ALL(WithHeader, true)
|
|
|
|
// Like above, but without handling CORS preflight requests.
|
|
#define CORS_TEST_FETCH_NO_PREFLIGHT(test_name, handler_main, handler_sub, \
|
|
add_header) \
|
|
TEST(CorsTest, Fetch##test_name) { \
|
|
CookieTestSetup setup; \
|
|
CookieResource resource_main; \
|
|
SubResource resource_sub; \
|
|
SetupExecRequest(ExecMode::FETCH, &setup, "CorsTest.Fetch" #test_name, \
|
|
HandlerType::handler_main, &resource_main, \
|
|
HandlerType::handler_sub, &resource_sub, nullptr, \
|
|
add_header); \
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup); \
|
|
handler->ExecuteTest(); \
|
|
ReleaseAndWaitForDestructor(handler); \
|
|
}
|
|
|
|
#define CORS_TEST_FETCH_NO_PREFLIGHT_SERVER(name, add_header) \
|
|
CORS_TEST_FETCH_NO_PREFLIGHT(name##ServerToServer, SERVER, SERVER, \
|
|
add_header) \
|
|
CORS_TEST_FETCH_NO_PREFLIGHT(name##HttpSchemeToServer, HTTP_SCHEME, SERVER, \
|
|
add_header) \
|
|
CORS_TEST_FETCH_NO_PREFLIGHT(name##CustomStandardSchemeToServer, \
|
|
CUSTOM_STANDARD_SCHEME, SERVER, add_header) \
|
|
CORS_TEST_FETCH_NO_PREFLIGHT(name##CustomNonStandardSchemeToServer, \
|
|
CUSTOM_NONSTANDARD_SCHEME, SERVER, add_header)
|
|
|
|
// Fetch requests without the "Access-Control-Allow-Origin" header.
|
|
CORS_TEST_FETCH_NO_PREFLIGHT_SERVER(NoHeaderNoPreflight, false)
|
|
|
|
// Fetch requests with the "Access-Control-Allow-Origin" header.
|
|
CORS_TEST_FETCH_NO_PREFLIGHT_SERVER(WithHeaderNoPreflight, true)
|
|
|
|
namespace {
|
|
|
|
enum class RedirectMode {
|
|
MODE_302,
|
|
MODE_307,
|
|
};
|
|
|
|
struct RedirectGetResource : CookieResource {
|
|
RedirectGetResource() {}
|
|
|
|
bool VerifyRequest(CefRefPtr<CefRequest> request) const override {
|
|
if (!CookieResource::VerifyRequest(request))
|
|
return false;
|
|
|
|
// The "Origin" header should never be present for a redirect.
|
|
const std::string& origin = request->GetHeaderByName("Origin");
|
|
EXPECT_TRUE(origin.empty()) << GetPathURL();
|
|
return origin.empty();
|
|
}
|
|
};
|
|
|
|
void SetupRedirectResponse(RedirectMode mode,
|
|
const std::string& redirect_url,
|
|
CefRefPtr<CefResponse> response) {
|
|
if (mode == RedirectMode::MODE_302)
|
|
response->SetStatus(302);
|
|
else if (mode == RedirectMode::MODE_307)
|
|
response->SetStatus(307);
|
|
else
|
|
NOTREACHED();
|
|
|
|
response->SetHeaderByName("Location", redirect_url,
|
|
/*override=*/false);
|
|
}
|
|
|
|
// Test redirect requests.
|
|
void SetupRedirectGetRequest(RedirectMode mode,
|
|
CookieTestSetup* setup,
|
|
const std::string& test_name,
|
|
HandlerType main_handler,
|
|
CookieResource* main_resource,
|
|
HandlerType redirect_handler,
|
|
RedirectGetResource* redirect_resource) {
|
|
const std::string& base_path = "/" + test_name;
|
|
|
|
// Expect a single redirect request that sends SuccessMsg.
|
|
redirect_resource->Init(redirect_handler, base_path + ".redirect.html",
|
|
kMimeTypeHtml, GetDefaultSuccessMsgHtml());
|
|
redirect_resource->expected_success_query_ct = 1;
|
|
|
|
// Expect a single main request that results in a redirect.
|
|
const std::string& redirect_url = redirect_resource->GetPathURL();
|
|
main_resource->Init(main_handler, base_path, kMimeTypeHtml, std::string());
|
|
SetupRedirectResponse(mode, redirect_url, main_resource->response);
|
|
|
|
SetupCookieExpectations(setup, main_resource, redirect_resource);
|
|
|
|
setup->AddResource(main_resource);
|
|
setup->AddResource(redirect_resource);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Test redirect GET requests with different origin combinations.
|
|
#define CORS_TEST_REDIRECT_GET(test_name, mode, handler_main, \
|
|
handler_redirect) \
|
|
TEST(CorsTest, RedirectGet##test_name) { \
|
|
CookieTestSetup setup; \
|
|
CookieResource resource_main; \
|
|
RedirectGetResource resource_redirect; \
|
|
SetupRedirectGetRequest( \
|
|
RedirectMode::mode, &setup, "CorsTest.RedirectGet" #test_name, \
|
|
HandlerType::handler_main, &resource_main, \
|
|
HandlerType::handler_redirect, &resource_redirect); \
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup); \
|
|
handler->ExecuteTest(); \
|
|
ReleaseAndWaitForDestructor(handler); \
|
|
}
|
|
|
|
// Test all redirect GET combinations (same and cross-origin).
|
|
#define CORS_TEST_REDIRECT_GET_ALL(name, mode) \
|
|
CORS_TEST_REDIRECT_GET(name##ServerToServer, mode, SERVER, SERVER) \
|
|
CORS_TEST_REDIRECT_GET(name##ServerToHttpScheme, mode, SERVER, HTTP_SCHEME) \
|
|
CORS_TEST_REDIRECT_GET(name##ServerToCustomStandardScheme, mode, SERVER, \
|
|
CUSTOM_STANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_GET(name##ServerToCustomNonStandardScheme, mode, SERVER, \
|
|
CUSTOM_NONSTANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_GET(name##HttpSchemeToServer, mode, HTTP_SCHEME, SERVER) \
|
|
CORS_TEST_REDIRECT_GET(name##HttpSchemeToHttpScheme, mode, HTTP_SCHEME, \
|
|
HTTP_SCHEME) \
|
|
CORS_TEST_REDIRECT_GET(name##HttpSchemeToCustomStandardScheme, mode, \
|
|
HTTP_SCHEME, CUSTOM_STANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_GET(name##HttpSchemeToCustomNonStandardScheme, mode, \
|
|
HTTP_SCHEME, CUSTOM_NONSTANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_GET(name##CustomStandardSchemeToServer, mode, \
|
|
CUSTOM_STANDARD_SCHEME, SERVER) \
|
|
CORS_TEST_REDIRECT_GET(name##CustomStandardSchemeToHttpScheme, mode, \
|
|
CUSTOM_STANDARD_SCHEME, HTTP_SCHEME) \
|
|
CORS_TEST_REDIRECT_GET(name##CustomStandardSchemeToCustomStandardScheme, \
|
|
mode, CUSTOM_STANDARD_SCHEME, CUSTOM_STANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_GET(name##CustomStandardSchemeToCustomNonStandardScheme, \
|
|
mode, CUSTOM_STANDARD_SCHEME, \
|
|
CUSTOM_NONSTANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_GET(name##CustomNonStandardSchemeToServer, mode, \
|
|
CUSTOM_NONSTANDARD_SCHEME, SERVER) \
|
|
CORS_TEST_REDIRECT_GET(name##CustomNonStandardSchemeToHttpScheme, mode, \
|
|
CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME) \
|
|
CORS_TEST_REDIRECT_GET(name##CustomNonStandardSchemeToCustomStandardScheme, \
|
|
mode, CUSTOM_NONSTANDARD_SCHEME, \
|
|
CUSTOM_STANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_GET( \
|
|
name##CustomNonStandardSchemeToCustomNonStandardScheme, mode, \
|
|
CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME)
|
|
|
|
// Redirect GET requests.
|
|
CORS_TEST_REDIRECT_GET_ALL(302, MODE_302)
|
|
CORS_TEST_REDIRECT_GET_ALL(307, MODE_307)
|
|
|
|
namespace {
|
|
|
|
struct PostResource : CookieResource {
|
|
PostResource() {}
|
|
|
|
bool expect_downgrade_to_get = false;
|
|
bool was_redirected = false;
|
|
|
|
std::string main_origin;
|
|
bool is_cross_origin;
|
|
|
|
void InitOrigin(HandlerType main_handler) {
|
|
// Origin is always "null" for non-HTTP(S) schemes.
|
|
// This should only be "null" for non-standard schemes, but Blink is likely
|
|
// using SchemeIsHTTPOrHTTPS() when submitting the form request.
|
|
main_origin = main_handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME ||
|
|
main_handler == HandlerType::CUSTOM_STANDARD_SCHEME
|
|
? "null"
|
|
: GetOrigin(main_handler);
|
|
|
|
// True if the request is considered cross-origin. Any requests between
|
|
// non-standard schemes are considered cross-origin (due to the "null"
|
|
// origin).
|
|
is_cross_origin = main_handler != handler ||
|
|
(main_handler == HandlerType::CUSTOM_NONSTANDARD_SCHEME &&
|
|
handler == main_handler);
|
|
}
|
|
|
|
bool VerifyRequest(CefRefPtr<CefRequest> request) const override {
|
|
if (!CookieResource::VerifyRequest(request))
|
|
return false;
|
|
|
|
// The "Origin" header should be present if the request is POST, and was not
|
|
// redirected cross-origin.
|
|
std::string expected_origin;
|
|
if (!expect_downgrade_to_get) {
|
|
if (was_redirected && is_cross_origin) {
|
|
// Always "null" for cross-origin redirects.
|
|
expected_origin = "null";
|
|
} else {
|
|
expected_origin = main_origin;
|
|
}
|
|
}
|
|
|
|
const std::string& origin = request->GetHeaderByName("Origin");
|
|
EXPECT_STREQ(expected_origin.c_str(), origin.c_str()) << GetPathURL();
|
|
if (expected_origin != origin)
|
|
return false;
|
|
|
|
const std::string& method = request->GetMethod();
|
|
const bool has_post_data = request->GetPostData() != nullptr;
|
|
if (expect_downgrade_to_get) {
|
|
EXPECT_FALSE(has_post_data) << GetPathURL();
|
|
EXPECT_STREQ("GET", method.c_str()) << GetPathURL();
|
|
return !has_post_data && method == "GET";
|
|
} else {
|
|
EXPECT_TRUE(has_post_data) << GetPathURL();
|
|
EXPECT_STREQ("POST", method.c_str()) << GetPathURL();
|
|
return has_post_data && method == "POST";
|
|
}
|
|
}
|
|
};
|
|
|
|
std::string GetPostFormHtml(const std::string& submit_url) {
|
|
return "<html><body>"
|
|
"<form id=\"f\" action=\"" +
|
|
submit_url +
|
|
"\" method=\"post\">"
|
|
"<input type=\"hidden\" name=\"n\" value=\"v\"></form>"
|
|
"<script>document.getElementById('f').submit();</script>"
|
|
"</body></html>";
|
|
}
|
|
|
|
// Test redirect requests.
|
|
void SetupRedirectPostRequest(RedirectMode mode,
|
|
CookieTestSetup* setup,
|
|
const std::string& test_name,
|
|
HandlerType main_handler,
|
|
CookieResource* main_resource,
|
|
PostResource* submit_resource,
|
|
HandlerType redirect_handler,
|
|
PostResource* redirect_resource) {
|
|
const std::string& base_path = "/" + test_name;
|
|
|
|
// Expect a single redirect request that sends SuccessMsg.
|
|
redirect_resource->Init(redirect_handler, base_path + ".redirect.html",
|
|
kMimeTypeHtml, GetDefaultSuccessMsgHtml());
|
|
redirect_resource->InitOrigin(main_handler);
|
|
redirect_resource->expected_success_query_ct = 1;
|
|
|
|
// 302 redirects will downgrade POST requests to GET.
|
|
redirect_resource->expect_downgrade_to_get = mode == RedirectMode::MODE_302;
|
|
redirect_resource->was_redirected = true;
|
|
|
|
// Expect a single submit request that redirects the response.
|
|
const std::string& redirect_url = redirect_resource->GetPathURL();
|
|
submit_resource->Init(main_handler, base_path + ".submit.html", kMimeTypeHtml,
|
|
std::string());
|
|
submit_resource->InitOrigin(main_handler);
|
|
SetupRedirectResponse(mode, redirect_url, submit_resource->response);
|
|
|
|
// Expect a single main request that submits the form.
|
|
const std::string& submit_url = submit_resource->GetPathURL();
|
|
main_resource->Init(main_handler, base_path, kMimeTypeHtml,
|
|
GetPostFormHtml(submit_url));
|
|
|
|
SetupCookieExpectations(setup, main_resource, submit_resource);
|
|
SetupCookieExpectations(setup, main_resource, redirect_resource);
|
|
|
|
setup->AddResource(main_resource);
|
|
setup->AddResource(submit_resource);
|
|
setup->AddResource(redirect_resource);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
// Test redirect GET requests with different origin combinations.
|
|
#define CORS_TEST_REDIRECT_POST(test_name, mode, handler_main, \
|
|
handler_redirect) \
|
|
TEST(CorsTest, RedirectPost##test_name) { \
|
|
CookieTestSetup setup; \
|
|
CookieResource resource_main; \
|
|
PostResource resource_submit, resource_redirect; \
|
|
SetupRedirectPostRequest( \
|
|
RedirectMode::mode, &setup, "CorsTest.RedirectPost" #test_name, \
|
|
HandlerType::handler_main, &resource_main, &resource_submit, \
|
|
HandlerType::handler_redirect, &resource_redirect); \
|
|
CefRefPtr<CorsTestHandler> handler = new CorsTestHandler(&setup); \
|
|
handler->ExecuteTest(); \
|
|
ReleaseAndWaitForDestructor(handler); \
|
|
}
|
|
|
|
// Test all redirect GET combinations (same and cross-origin).
|
|
#define CORS_TEST_REDIRECT_POST_ALL(name, mode) \
|
|
CORS_TEST_REDIRECT_POST(name##ServerToServer, mode, SERVER, SERVER) \
|
|
CORS_TEST_REDIRECT_POST(name##ServerToHttpScheme, mode, SERVER, HTTP_SCHEME) \
|
|
CORS_TEST_REDIRECT_POST(name##ServerToCustomStandardScheme, mode, SERVER, \
|
|
CUSTOM_STANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_POST(name##ServerToCustomNonStandardScheme, mode, SERVER, \
|
|
CUSTOM_NONSTANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_POST(name##HttpSchemeToServer, mode, HTTP_SCHEME, SERVER) \
|
|
CORS_TEST_REDIRECT_POST(name##HttpSchemeToHttpScheme, mode, HTTP_SCHEME, \
|
|
HTTP_SCHEME) \
|
|
CORS_TEST_REDIRECT_POST(name##HttpSchemeToCustomStandardScheme, mode, \
|
|
HTTP_SCHEME, CUSTOM_STANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_POST(name##HttpSchemeToCustomNonStandardScheme, mode, \
|
|
HTTP_SCHEME, CUSTOM_NONSTANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_POST(name##CustomStandardSchemeToServer, mode, \
|
|
CUSTOM_STANDARD_SCHEME, SERVER) \
|
|
CORS_TEST_REDIRECT_POST(name##CustomStandardSchemeToHttpScheme, mode, \
|
|
CUSTOM_STANDARD_SCHEME, HTTP_SCHEME) \
|
|
CORS_TEST_REDIRECT_POST(name##CustomStandardSchemeToCustomStandardScheme, \
|
|
mode, CUSTOM_STANDARD_SCHEME, \
|
|
CUSTOM_STANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_POST(name##CustomStandardSchemeToCustomNonStandardScheme, \
|
|
mode, CUSTOM_STANDARD_SCHEME, \
|
|
CUSTOM_NONSTANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_POST(name##CustomNonStandardSchemeToServer, mode, \
|
|
CUSTOM_NONSTANDARD_SCHEME, SERVER) \
|
|
CORS_TEST_REDIRECT_POST(name##CustomNonStandardSchemeToHttpScheme, mode, \
|
|
CUSTOM_NONSTANDARD_SCHEME, HTTP_SCHEME) \
|
|
CORS_TEST_REDIRECT_POST(name##CustomNonStandardSchemeToCustomStandardScheme, \
|
|
mode, CUSTOM_NONSTANDARD_SCHEME, \
|
|
CUSTOM_STANDARD_SCHEME) \
|
|
CORS_TEST_REDIRECT_POST( \
|
|
name##CustomNonStandardSchemeToCustomNonStandardScheme, mode, \
|
|
CUSTOM_NONSTANDARD_SCHEME, CUSTOM_NONSTANDARD_SCHEME)
|
|
|
|
// Redirect GET requests.
|
|
CORS_TEST_REDIRECT_POST_ALL(302, MODE_302)
|
|
CORS_TEST_REDIRECT_POST_ALL(307, MODE_307)
|