blob: b657bf9054348fea407fc32e9fe919758d7569ac [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/memory/weak_ptr.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/threading/thread_task_runner_handle.h"
#include "net/base/isolation_info.h"
#include "net/cookies/canonical_cookie.h"
#include "net/cookies/canonical_cookie_test_helpers.h"
#include "net/cookies/cookie_access_result.h"
#include "net/cookies/cookie_store.h"
#include "net/cookies/cookie_util.h"
#include "net/http/http_request_headers.h"
#include "net/socket/socket_test_util.h"
#include "net/websockets/websocket_stream_create_test_base.h"
#include "net/websockets/websocket_test_util.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/gurl.h"
#include "url/origin.h"
namespace net {
namespace {
using ::testing::TestWithParam;
using ::testing::ValuesIn;
const WebSocketExtraHeaders kNoCookieHeader = {};
class TestBase : public WebSocketStreamCreateTestBase {
public:
void CreateAndConnect(const GURL& url,
const url::Origin& origin,
const SiteForCookies& site_for_cookies,
const IsolationInfo& isolation_info,
const WebSocketExtraHeaders& cookie_header,
const std::string& response_body) {
url_request_context_host_.SetExpectations(
WebSocketStandardRequestWithCookies(
url.path(), url.host(), origin, cookie_header,
/*send_additional_request_headers=*/{}, /*extra_headers=*/{}),
response_body);
CreateAndConnectStream(url, NoSubProtocols(), origin, site_for_cookies,
isolation_info, HttpRequestHeaders(), nullptr);
}
};
struct ClientUseCookieParameter {
// The URL for the WebSocket connection.
const char* const url;
// The URL for the previously set cookies.
const char* const cookie_url;
// The previously set cookies contents.
const char* const cookie_line;
// The Cookie: HTTP header expected to appear in the WS request.
const WebSocketExtraHeaders cookie_header;
};
class WebSocketStreamClientUseCookieTest
: public TestBase,
public TestWithParam<ClientUseCookieParameter> {
public:
~WebSocketStreamClientUseCookieTest() override {
// Permit any endpoint locks to be released.
stream_request_.reset();
stream_.reset();
base::RunLoop().RunUntilIdle();
}
static void SetCookieHelperFunction(const base::RepeatingClosure& task,
base::WeakPtr<bool> weak_is_called,
base::WeakPtr<bool> weak_result,
CookieAccessResult access_result) {
*weak_is_called = true;
*weak_result = access_result.status.IsInclude();
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, task);
}
};
struct ServerSetCookieParameter {
// The URL for the WebSocket connection.
const char* const url;
// The URL used to query cookies after the response received.
const char* const cookie_url;
// The cookies expected to appear for |cookie_url| inquiry.
const char* const cookie_line;
// The Set-Cookie: HTTP header attached to the response.
const WebSocketExtraHeaders cookie_header;
};
class WebSocketStreamServerSetCookieTest
: public TestBase,
public TestWithParam<ServerSetCookieParameter> {
public:
~WebSocketStreamServerSetCookieTest() override {
// Permit any endpoint locks to be released.
stream_request_.reset();
stream_.reset();
base::RunLoop().RunUntilIdle();
}
static void GetCookieListHelperFunction(
base::OnceClosure task,
base::WeakPtr<bool> weak_is_called,
base::WeakPtr<CookieList> weak_result,
const CookieAccessResultList& cookie_list,
const CookieAccessResultList& excluded_cookies) {
*weak_is_called = true;
*weak_result = cookie_util::StripAccessResults(cookie_list);
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE, std::move(task));
}
};
TEST_P(WebSocketStreamClientUseCookieTest, ClientUseCookie) {
// For wss tests.
url_request_context_host_.AddSSLSocketDataProvider(
std::make_unique<SSLSocketDataProvider>(ASYNC, OK));
CookieStore* store =
url_request_context_host_.GetURLRequestContext()->cookie_store();
const GURL url(GetParam().url);
const GURL cookie_url(GetParam().cookie_url);
const url::Origin origin = url::Origin::Create(GURL(GetParam().url));
const SiteForCookies site_for_cookies = SiteForCookies::FromOrigin(origin);
const IsolationInfo isolation_info =
IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin, origin,
SiteForCookies::FromOrigin(origin));
const std::string cookie_line(GetParam().cookie_line);
bool is_called = false;
bool set_cookie_result = false;
base::WeakPtrFactory<bool> weak_is_called(&is_called);
base::WeakPtrFactory<bool> weak_set_cookie_result(&set_cookie_result);
base::RunLoop run_loop;
auto cookie =
CanonicalCookie::Create(cookie_url, cookie_line, base::Time::Now(),
absl::nullopt /* server_time */,
absl::nullopt /* cookie_partition_key */);
store->SetCanonicalCookieAsync(
std::move(cookie), cookie_url, net::CookieOptions::MakeAllInclusive(),
base::BindOnce(&SetCookieHelperFunction, run_loop.QuitClosure(),
weak_is_called.GetWeakPtr(),
weak_set_cookie_result.GetWeakPtr()));
run_loop.Run();
ASSERT_TRUE(is_called);
ASSERT_TRUE(set_cookie_result);
CreateAndConnect(url, origin, site_for_cookies, isolation_info,
GetParam().cookie_header, WebSocketStandardResponse(""));
WaitUntilConnectDone();
EXPECT_FALSE(has_failed());
}
TEST_P(WebSocketStreamServerSetCookieTest, ServerSetCookie) {
// For wss tests.
url_request_context_host_.AddSSLSocketDataProvider(
std::make_unique<SSLSocketDataProvider>(ASYNC, OK));
const GURL url(GetParam().url);
const GURL cookie_url(GetParam().cookie_url);
const url::Origin origin = url::Origin::Create(GURL(GetParam().url));
const SiteForCookies site_for_cookies = SiteForCookies::FromOrigin(origin);
const IsolationInfo isolation_info =
IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin, origin,
SiteForCookies::FromOrigin(origin));
const std::string cookie_line(GetParam().cookie_line);
HttpRequestHeaders headers;
for (const auto& [key, value] : GetParam().cookie_header)
headers.SetHeader(key, value);
std::string cookie_header(headers.ToString());
const std::string response = base::StringPrintf(
"HTTP/1.1 101 Switching Protocols\r\n"
"Upgrade: websocket\r\n"
"Connection: Upgrade\r\n"
"Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n"
"%s",
cookie_header.c_str());
CookieStore* store =
url_request_context_host_.GetURLRequestContext()->cookie_store();
CreateAndConnect(url, origin, site_for_cookies, isolation_info,
/*cookie_header=*/{}, response);
WaitUntilConnectDone();
EXPECT_FALSE(has_failed()) << failure_message();
bool is_called = false;
CookieList get_cookie_list_result;
base::WeakPtrFactory<bool> weak_is_called(&is_called);
base::WeakPtrFactory<CookieList> weak_get_cookie_list_result(
&get_cookie_list_result);
base::RunLoop run_loop;
store->GetCookieListWithOptionsAsync(
cookie_url, net::CookieOptions::MakeAllInclusive(),
CookiePartitionKeyCollection(),
base::BindOnce(&GetCookieListHelperFunction, run_loop.QuitClosure(),
weak_is_called.GetWeakPtr(),
weak_get_cookie_list_result.GetWeakPtr()));
run_loop.Run();
EXPECT_TRUE(is_called);
EXPECT_THAT(get_cookie_list_result, MatchesCookieLine(cookie_line));
}
// Test parameters definitions follow...
const ClientUseCookieParameter kClientUseCookieParameters[] = {
// Non-secure cookies for ws
{"ws://www.example.com",
"http://www.example.com",
"test-cookie",
{{"Cookie", "test-cookie"}}},
{"ws://www.example.com",
"https://www.example.com",
"test-cookie",
{{"Cookie", "test-cookie"}}},
{"ws://www.example.com",
"ws://www.example.com",
"test-cookie",
{{"Cookie", "test-cookie"}}},
{"ws://www.example.com",
"wss://www.example.com",
"test-cookie",
{{"Cookie", "test-cookie"}}},
// Non-secure cookies for wss
{"wss://www.example.com",
"http://www.example.com",
"test-cookie",
{{"Cookie", "test-cookie"}}},
{"wss://www.example.com",
"https://www.example.com",
"test-cookie",
{{"Cookie", "test-cookie"}}},
{"wss://www.example.com",
"ws://www.example.com",
"test-cookie",
{{"Cookie", "test-cookie"}}},
{"wss://www.example.com",
"wss://www.example.com",
"test-cookie",
{{"Cookie", "test-cookie"}}},
// Secure-cookies for ws
{"ws://www.example.com", "https://www.example.com", "test-cookie; secure",
kNoCookieHeader},
{"ws://www.example.com", "wss://www.example.com", "test-cookie; secure",
kNoCookieHeader},
// Secure-cookies for wss
{"wss://www.example.com",
"https://www.example.com",
"test-cookie; secure",
{{"Cookie", "test-cookie"}}},
{"wss://www.example.com",
"wss://www.example.com",
"test-cookie; secure",
{{"Cookie", "test-cookie"}}},
// Non-secure cookies for ws (sharing domain)
{"ws://www.example.com",
"http://www2.example.com",
"test-cookie; Domain=example.com",
{{"Cookie", "test-cookie"}}},
{"ws://www.example.com",
"https://www2.example.com",
"test-cookie; Domain=example.com",
{{"Cookie", "test-cookie"}}},
{"ws://www.example.com",
"ws://www2.example.com",
"test-cookie; Domain=example.com",
{{"Cookie", "test-cookie"}}},
{"ws://www.example.com",
"wss://www2.example.com",
"test-cookie; Domain=example.com",
{{"Cookie", "test-cookie"}}},
// Non-secure cookies for wss (sharing domain)
{"wss://www.example.com",
"http://www2.example.com",
"test-cookie; Domain=example.com",
{{"Cookie", "test-cookie"}}},
{"wss://www.example.com",
"https://www2.example.com",
"test-cookie; Domain=example.com",
{{"Cookie", "test-cookie"}}},
{"wss://www.example.com",
"ws://www2.example.com",
"test-cookie; Domain=example.com",
{{"Cookie", "test-cookie"}}},
{"wss://www.example.com",
"wss://www2.example.com",
"test-cookie; Domain=example.com",
{{"Cookie", "test-cookie"}}},
// Secure-cookies for ws (sharing domain)
{"ws://www.example.com", "https://www2.example.com",
"test-cookie; Domain=example.com; secure", kNoCookieHeader},
{"ws://www.example.com", "wss://www2.example.com",
"test-cookie; Domain=example.com; secure", kNoCookieHeader},
// Secure-cookies for wss (sharing domain)
{"wss://www.example.com",
"https://www2.example.com",
"test-cookie; Domain=example.com; secure",
{{"Cookie", "test-cookie"}}},
{"wss://www.example.com",
"wss://www2.example.com",
"test-cookie; Domain=example.com; secure",
{{"Cookie", "test-cookie"}}},
// Non-matching cookies for ws
{"ws://www.example.com", "http://www2.example.com", "test-cookie",
kNoCookieHeader},
{"ws://www.example.com", "https://www2.example.com", "test-cookie",
kNoCookieHeader},
{"ws://www.example.com", "ws://www2.example.com", "test-cookie",
kNoCookieHeader},
{"ws://www.example.com", "wss://www2.example.com", "test-cookie",
kNoCookieHeader},
// Non-matching cookies for wss
{"wss://www.example.com", "http://www2.example.com", "test-cookie",
kNoCookieHeader},
{"wss://www.example.com", "https://www2.example.com", "test-cookie",
kNoCookieHeader},
{"wss://www.example.com", "ws://www2.example.com", "test-cookie",
kNoCookieHeader},
{"wss://www.example.com", "wss://www2.example.com", "test-cookie",
kNoCookieHeader},
};
INSTANTIATE_TEST_SUITE_P(WebSocketStreamClientUseCookieTest,
WebSocketStreamClientUseCookieTest,
ValuesIn(kClientUseCookieParameters));
const ServerSetCookieParameter kServerSetCookieParameters[] = {
// Cookies coming from ws
{"ws://www.example.com",
"http://www.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie"}}},
{"ws://www.example.com",
"https://www.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie"}}},
{"ws://www.example.com",
"ws://www.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie"}}},
{"ws://www.example.com",
"wss://www.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie"}}},
// Cookies coming from wss
{"wss://www.example.com",
"http://www.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie"}}},
{"wss://www.example.com",
"https://www.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie"}}},
{"wss://www.example.com",
"ws://www.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie"}}},
{"wss://www.example.com",
"wss://www.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie"}}},
// cookies coming from ws (sharing domain)
{"ws://www.example.com",
"http://www2.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie; Domain=example.com"}}},
{"ws://www.example.com",
"https://www2.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie; Domain=example.com"}}},
{"ws://www.example.com",
"ws://www2.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie; Domain=example.com"}}},
{"ws://www.example.com",
"wss://www2.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie; Domain=example.com"}}},
// cookies coming from wss (sharing domain)
{"wss://www.example.com",
"http://www2.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie; Domain=example.com"}}},
{"wss://www.example.com",
"https://www2.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie; Domain=example.com"}}},
{"wss://www.example.com",
"ws://www2.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie; Domain=example.com"}}},
{"wss://www.example.com",
"wss://www2.example.com",
"test-cookie",
{{"Set-Cookie", "test-cookie; Domain=example.com"}}},
// Non-matching cookies coming from ws
{"ws://www.example.com",
"http://www2.example.com",
"",
{{"Set-Cookie", "test-cookie"}}},
{"ws://www.example.com",
"https://www2.example.com",
"",
{{"Set-Cookie", "test-cookie"}}},
{"ws://www.example.com",
"ws://www2.example.com",
"",
{{"Set-Cookie", "test-cookie"}}},
{"ws://www.example.com",
"wss://www2.example.com",
"",
{{"Set-Cookie", "test-cookie"}}},
// Non-matching cookies coming from wss
{"wss://www.example.com",
"http://www2.example.com",
"",
{{"Set-Cookie", "test-cookie"}}},
{"wss://www.example.com",
"https://www2.example.com",
"",
{{"Set-Cookie", "test-cookie"}}},
{"wss://www.example.com",
"ws://www2.example.com",
"",
{{"Set-Cookie", "test-cookie"}}},
{"wss://www.example.com",
"wss://www2.example.com",
"",
{{"Set-Cookie", "test-cookie"}}},
};
INSTANTIATE_TEST_SUITE_P(WebSocketStreamServerSetCookieTest,
WebSocketStreamServerSetCookieTest,
ValuesIn(kServerSetCookieParameters));
} // namespace
} // namespace net