1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <memory>
6 #include <string>
7 #include <utility>
8 #include <vector>
9
10 #include "base/functional/bind.h"
11 #include "base/functional/callback_forward.h"
12 #include "base/location.h"
13 #include "base/memory/scoped_refptr.h"
14 #include "base/memory/weak_ptr.h"
15 #include "base/run_loop.h"
16 #include "base/strings/strcat.h"
17 #include "base/strings/string_piece.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/task/single_thread_task_runner.h"
20 #include "base/time/time.h"
21 #include "base/timer/timer.h"
22 #include "net/base/isolation_info.h"
23 #include "net/base/net_errors.h"
24 #include "net/cookies/canonical_cookie.h"
25 #include "net/cookies/canonical_cookie_test_helpers.h"
26 #include "net/cookies/cookie_access_result.h"
27 #include "net/cookies/cookie_inclusion_status.h"
28 #include "net/cookies/cookie_options.h"
29 #include "net/cookies/cookie_partition_key.h"
30 #include "net/cookies/cookie_partition_key_collection.h"
31 #include "net/cookies/cookie_store.h"
32 #include "net/cookies/cookie_util.h"
33 #include "net/cookies/site_for_cookies.h"
34 #include "net/http/http_request_headers.h"
35 #include "net/socket/socket_test_util.h"
36 #include "net/url_request/url_request_context.h"
37 #include "net/websockets/websocket_stream.h"
38 #include "net/websockets/websocket_stream_create_test_base.h"
39 #include "net/websockets/websocket_test_util.h"
40 #include "testing/gmock/include/gmock/gmock.h"
41 #include "testing/gtest/include/gtest/gtest.h"
42 #include "third_party/abseil-cpp/absl/types/optional.h"
43 #include "url/gurl.h"
44 #include "url/origin.h"
45
46 namespace net {
47 namespace {
48
49 using ::testing::TestWithParam;
50 using ::testing::ValuesIn;
51
52 const WebSocketExtraHeaders kNoCookieHeader = {};
53
54 class TestBase : public WebSocketStreamCreateTestBase {
55 public:
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)56 void CreateAndConnect(const GURL& url,
57 const url::Origin& origin,
58 const SiteForCookies& site_for_cookies,
59 const IsolationInfo& isolation_info,
60 const WebSocketExtraHeaders& cookie_header,
61 const std::string& response_body) {
62 url_request_context_host_.SetExpectations(
63 WebSocketStandardRequestWithCookies(
64 url.path(), url.host(), origin, cookie_header,
65 /*send_additional_request_headers=*/{}, /*extra_headers=*/{}),
66 response_body);
67 CreateAndConnectStream(url, NoSubProtocols(), origin, site_for_cookies,
68 /*has_storage_access=*/false, isolation_info,
69 HttpRequestHeaders(), nullptr);
70 }
71 };
72
73 struct ClientUseCookieParameter {
74 // The URL for the WebSocket connection.
75 const char* const url;
76 // The URL for the previously set cookies.
77 const char* const cookie_url;
78 // The previously set cookies contents.
79 const char* const cookie_line;
80 // The Cookie: HTTP header expected to appear in the WS request.
81 const WebSocketExtraHeaders cookie_header;
82 };
83
84 class WebSocketStreamClientUseCookieTest
85 : public TestBase,
86 public TestWithParam<ClientUseCookieParameter> {
87 public:
~WebSocketStreamClientUseCookieTest()88 ~WebSocketStreamClientUseCookieTest() override {
89 // Permit any endpoint locks to be released.
90 stream_request_.reset();
91 stream_.reset();
92 base::RunLoop().RunUntilIdle();
93 }
94
SetCookieHelperFunction(const base::RepeatingClosure & task,base::WeakPtr<bool> weak_is_called,base::WeakPtr<bool> weak_result,CookieAccessResult access_result)95 static void SetCookieHelperFunction(const base::RepeatingClosure& task,
96 base::WeakPtr<bool> weak_is_called,
97 base::WeakPtr<bool> weak_result,
98 CookieAccessResult access_result) {
99 *weak_is_called = true;
100 *weak_result = access_result.status.IsInclude();
101 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(FROM_HERE,
102 task);
103 }
104 };
105
106 struct ServerSetCookieParameter {
107 // The URL for the WebSocket connection.
108 const char* const url;
109 // The URL used to query cookies after the response received.
110 const char* const cookie_url;
111 // The cookies expected to appear for |cookie_url| inquiry.
112 const char* const cookie_line;
113 // The Set-Cookie: HTTP header attached to the response.
114 const WebSocketExtraHeaders cookie_header;
115 };
116
117 class WebSocketStreamServerSetCookieTest
118 : public TestBase,
119 public TestWithParam<ServerSetCookieParameter> {
120 public:
~WebSocketStreamServerSetCookieTest()121 ~WebSocketStreamServerSetCookieTest() override {
122 // Permit any endpoint locks to be released.
123 stream_request_.reset();
124 stream_.reset();
125 base::RunLoop().RunUntilIdle();
126 }
127
GetCookieListHelperFunction(base::OnceClosure task,base::WeakPtr<bool> weak_is_called,base::WeakPtr<CookieList> weak_result,const CookieAccessResultList & cookie_list,const CookieAccessResultList & excluded_cookies)128 static void GetCookieListHelperFunction(
129 base::OnceClosure task,
130 base::WeakPtr<bool> weak_is_called,
131 base::WeakPtr<CookieList> weak_result,
132 const CookieAccessResultList& cookie_list,
133 const CookieAccessResultList& excluded_cookies) {
134 *weak_is_called = true;
135 *weak_result = cookie_util::StripAccessResults(cookie_list);
136 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
137 FROM_HERE, std::move(task));
138 }
139 };
140
TEST_P(WebSocketStreamClientUseCookieTest,ClientUseCookie)141 TEST_P(WebSocketStreamClientUseCookieTest, ClientUseCookie) {
142 // For wss tests.
143 url_request_context_host_.AddSSLSocketDataProvider(
144 std::make_unique<SSLSocketDataProvider>(ASYNC, OK));
145
146 CookieStore* store =
147 url_request_context_host_.GetURLRequestContext()->cookie_store();
148
149 const GURL url(GetParam().url);
150 const GURL cookie_url(GetParam().cookie_url);
151 const url::Origin origin = url::Origin::Create(GURL(GetParam().url));
152 const SiteForCookies site_for_cookies = SiteForCookies::FromOrigin(origin);
153 const IsolationInfo isolation_info =
154 IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin, origin,
155 SiteForCookies::FromOrigin(origin));
156 const std::string cookie_line(GetParam().cookie_line);
157
158 bool is_called = false;
159 bool set_cookie_result = false;
160 base::WeakPtrFactory<bool> weak_is_called(&is_called);
161 base::WeakPtrFactory<bool> weak_set_cookie_result(&set_cookie_result);
162
163 base::RunLoop run_loop;
164 auto cookie =
165 CanonicalCookie::Create(cookie_url, cookie_line, base::Time::Now(),
166 absl::nullopt /* server_time */,
167 absl::nullopt /* cookie_partition_key */);
168 store->SetCanonicalCookieAsync(
169 std::move(cookie), cookie_url, net::CookieOptions::MakeAllInclusive(),
170 base::BindOnce(&SetCookieHelperFunction, run_loop.QuitClosure(),
171 weak_is_called.GetWeakPtr(),
172 weak_set_cookie_result.GetWeakPtr()));
173 run_loop.Run();
174 ASSERT_TRUE(is_called);
175 ASSERT_TRUE(set_cookie_result);
176
177 CreateAndConnect(url, origin, site_for_cookies, isolation_info,
178 GetParam().cookie_header, WebSocketStandardResponse(""));
179 WaitUntilConnectDone();
180 EXPECT_FALSE(has_failed());
181 }
182
TEST_P(WebSocketStreamServerSetCookieTest,ServerSetCookie)183 TEST_P(WebSocketStreamServerSetCookieTest, ServerSetCookie) {
184 // For wss tests.
185 url_request_context_host_.AddSSLSocketDataProvider(
186 std::make_unique<SSLSocketDataProvider>(ASYNC, OK));
187
188 const GURL url(GetParam().url);
189 const GURL cookie_url(GetParam().cookie_url);
190 const url::Origin origin = url::Origin::Create(GURL(GetParam().url));
191 const SiteForCookies site_for_cookies = SiteForCookies::FromOrigin(origin);
192 const IsolationInfo isolation_info =
193 IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin, origin,
194 SiteForCookies::FromOrigin(origin));
195 const std::string cookie_line(GetParam().cookie_line);
196 HttpRequestHeaders headers;
197 for (const auto& [key, value] : GetParam().cookie_header)
198 headers.SetHeader(key, value);
199 std::string cookie_header(headers.ToString());
200 const std::string response =
201 base::StrCat({"HTTP/1.1 101 Switching Protocols\r\n"
202 "Upgrade: websocket\r\n"
203 "Connection: Upgrade\r\n"
204 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n",
205 cookie_header});
206 CookieStore* store =
207 url_request_context_host_.GetURLRequestContext()->cookie_store();
208
209 CreateAndConnect(url, origin, site_for_cookies, isolation_info,
210 /*cookie_header=*/{}, response);
211 WaitUntilConnectDone();
212 EXPECT_FALSE(has_failed()) << failure_message();
213
214 bool is_called = false;
215 CookieList get_cookie_list_result;
216 base::WeakPtrFactory<bool> weak_is_called(&is_called);
217 base::WeakPtrFactory<CookieList> weak_get_cookie_list_result(
218 &get_cookie_list_result);
219 base::RunLoop run_loop;
220 store->GetCookieListWithOptionsAsync(
221 cookie_url, net::CookieOptions::MakeAllInclusive(),
222 CookiePartitionKeyCollection(),
223 base::BindOnce(&GetCookieListHelperFunction, run_loop.QuitClosure(),
224 weak_is_called.GetWeakPtr(),
225 weak_get_cookie_list_result.GetWeakPtr()));
226 run_loop.Run();
227 EXPECT_TRUE(is_called);
228 EXPECT_THAT(get_cookie_list_result, MatchesCookieLine(cookie_line));
229 }
230
231 // Test parameters definitions follow...
232
233 const ClientUseCookieParameter kClientUseCookieParameters[] = {
234 // Non-secure cookies for ws
235 {"ws://www.example.com",
236 "http://www.example.com",
237 "test-cookie",
238 {{"Cookie", "test-cookie"}}},
239
240 {"ws://www.example.com",
241 "https://www.example.com",
242 "test-cookie",
243 {{"Cookie", "test-cookie"}}},
244
245 {"ws://www.example.com",
246 "ws://www.example.com",
247 "test-cookie",
248 {{"Cookie", "test-cookie"}}},
249
250 {"ws://www.example.com",
251 "wss://www.example.com",
252 "test-cookie",
253 {{"Cookie", "test-cookie"}}},
254
255 // Non-secure cookies for wss
256 {"wss://www.example.com",
257 "http://www.example.com",
258 "test-cookie",
259 {{"Cookie", "test-cookie"}}},
260
261 {"wss://www.example.com",
262 "https://www.example.com",
263 "test-cookie",
264 {{"Cookie", "test-cookie"}}},
265
266 {"wss://www.example.com",
267 "ws://www.example.com",
268 "test-cookie",
269 {{"Cookie", "test-cookie"}}},
270
271 {"wss://www.example.com",
272 "wss://www.example.com",
273 "test-cookie",
274 {{"Cookie", "test-cookie"}}},
275
276 // Secure-cookies for ws
277 {"ws://www.example.com", "https://www.example.com", "test-cookie; secure",
278 kNoCookieHeader},
279
280 {"ws://www.example.com", "wss://www.example.com", "test-cookie; secure",
281 kNoCookieHeader},
282
283 // Secure-cookies for wss
284 {"wss://www.example.com",
285 "https://www.example.com",
286 "test-cookie; secure",
287 {{"Cookie", "test-cookie"}}},
288
289 {"wss://www.example.com",
290 "wss://www.example.com",
291 "test-cookie; secure",
292 {{"Cookie", "test-cookie"}}},
293
294 // Non-secure cookies for ws (sharing domain)
295 {"ws://www.example.com",
296 "http://www2.example.com",
297 "test-cookie; Domain=example.com",
298 {{"Cookie", "test-cookie"}}},
299
300 {"ws://www.example.com",
301 "https://www2.example.com",
302 "test-cookie; Domain=example.com",
303 {{"Cookie", "test-cookie"}}},
304
305 {"ws://www.example.com",
306 "ws://www2.example.com",
307 "test-cookie; Domain=example.com",
308 {{"Cookie", "test-cookie"}}},
309
310 {"ws://www.example.com",
311 "wss://www2.example.com",
312 "test-cookie; Domain=example.com",
313 {{"Cookie", "test-cookie"}}},
314
315 // Non-secure cookies for wss (sharing domain)
316 {"wss://www.example.com",
317 "http://www2.example.com",
318 "test-cookie; Domain=example.com",
319 {{"Cookie", "test-cookie"}}},
320
321 {"wss://www.example.com",
322 "https://www2.example.com",
323 "test-cookie; Domain=example.com",
324 {{"Cookie", "test-cookie"}}},
325
326 {"wss://www.example.com",
327 "ws://www2.example.com",
328 "test-cookie; Domain=example.com",
329 {{"Cookie", "test-cookie"}}},
330
331 {"wss://www.example.com",
332 "wss://www2.example.com",
333 "test-cookie; Domain=example.com",
334 {{"Cookie", "test-cookie"}}},
335
336 // Secure-cookies for ws (sharing domain)
337 {"ws://www.example.com", "https://www2.example.com",
338 "test-cookie; Domain=example.com; secure", kNoCookieHeader},
339
340 {"ws://www.example.com", "wss://www2.example.com",
341 "test-cookie; Domain=example.com; secure", kNoCookieHeader},
342
343 // Secure-cookies for wss (sharing domain)
344 {"wss://www.example.com",
345 "https://www2.example.com",
346 "test-cookie; Domain=example.com; secure",
347 {{"Cookie", "test-cookie"}}},
348
349 {"wss://www.example.com",
350 "wss://www2.example.com",
351 "test-cookie; Domain=example.com; secure",
352 {{"Cookie", "test-cookie"}}},
353
354 // Non-matching cookies for ws
355 {"ws://www.example.com", "http://www2.example.com", "test-cookie",
356 kNoCookieHeader},
357
358 {"ws://www.example.com", "https://www2.example.com", "test-cookie",
359 kNoCookieHeader},
360
361 {"ws://www.example.com", "ws://www2.example.com", "test-cookie",
362 kNoCookieHeader},
363
364 {"ws://www.example.com", "wss://www2.example.com", "test-cookie",
365 kNoCookieHeader},
366
367 // Non-matching cookies for wss
368 {"wss://www.example.com", "http://www2.example.com", "test-cookie",
369 kNoCookieHeader},
370
371 {"wss://www.example.com", "https://www2.example.com", "test-cookie",
372 kNoCookieHeader},
373
374 {"wss://www.example.com", "ws://www2.example.com", "test-cookie",
375 kNoCookieHeader},
376
377 {"wss://www.example.com", "wss://www2.example.com", "test-cookie",
378 kNoCookieHeader},
379 };
380
381 INSTANTIATE_TEST_SUITE_P(WebSocketStreamClientUseCookieTest,
382 WebSocketStreamClientUseCookieTest,
383 ValuesIn(kClientUseCookieParameters));
384
385 const ServerSetCookieParameter kServerSetCookieParameters[] = {
386 // Cookies coming from ws
387 {"ws://www.example.com",
388 "http://www.example.com",
389 "test-cookie",
390 {{"Set-Cookie", "test-cookie"}}},
391
392 {"ws://www.example.com",
393 "https://www.example.com",
394 "test-cookie",
395 {{"Set-Cookie", "test-cookie"}}},
396
397 {"ws://www.example.com",
398 "ws://www.example.com",
399 "test-cookie",
400 {{"Set-Cookie", "test-cookie"}}},
401
402 {"ws://www.example.com",
403 "wss://www.example.com",
404 "test-cookie",
405 {{"Set-Cookie", "test-cookie"}}},
406
407 // Cookies coming from wss
408 {"wss://www.example.com",
409 "http://www.example.com",
410 "test-cookie",
411 {{"Set-Cookie", "test-cookie"}}},
412
413 {"wss://www.example.com",
414 "https://www.example.com",
415 "test-cookie",
416 {{"Set-Cookie", "test-cookie"}}},
417
418 {"wss://www.example.com",
419 "ws://www.example.com",
420 "test-cookie",
421 {{"Set-Cookie", "test-cookie"}}},
422
423 {"wss://www.example.com",
424 "wss://www.example.com",
425 "test-cookie",
426 {{"Set-Cookie", "test-cookie"}}},
427
428 // cookies coming from ws (sharing domain)
429 {"ws://www.example.com",
430 "http://www2.example.com",
431 "test-cookie",
432 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
433
434 {"ws://www.example.com",
435 "https://www2.example.com",
436 "test-cookie",
437 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
438
439 {"ws://www.example.com",
440 "ws://www2.example.com",
441 "test-cookie",
442 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
443
444 {"ws://www.example.com",
445 "wss://www2.example.com",
446 "test-cookie",
447 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
448
449 // cookies coming from wss (sharing domain)
450 {"wss://www.example.com",
451 "http://www2.example.com",
452 "test-cookie",
453 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
454
455 {"wss://www.example.com",
456 "https://www2.example.com",
457 "test-cookie",
458 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
459
460 {"wss://www.example.com",
461 "ws://www2.example.com",
462 "test-cookie",
463 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
464
465 {"wss://www.example.com",
466 "wss://www2.example.com",
467 "test-cookie",
468 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
469
470 // Non-matching cookies coming from ws
471 {"ws://www.example.com",
472 "http://www2.example.com",
473 "",
474 {{"Set-Cookie", "test-cookie"}}},
475
476 {"ws://www.example.com",
477 "https://www2.example.com",
478 "",
479 {{"Set-Cookie", "test-cookie"}}},
480
481 {"ws://www.example.com",
482 "ws://www2.example.com",
483 "",
484 {{"Set-Cookie", "test-cookie"}}},
485
486 {"ws://www.example.com",
487 "wss://www2.example.com",
488 "",
489 {{"Set-Cookie", "test-cookie"}}},
490
491 // Non-matching cookies coming from wss
492 {"wss://www.example.com",
493 "http://www2.example.com",
494 "",
495 {{"Set-Cookie", "test-cookie"}}},
496
497 {"wss://www.example.com",
498 "https://www2.example.com",
499 "",
500 {{"Set-Cookie", "test-cookie"}}},
501
502 {"wss://www.example.com",
503 "ws://www2.example.com",
504 "",
505 {{"Set-Cookie", "test-cookie"}}},
506
507 {"wss://www.example.com",
508 "wss://www2.example.com",
509 "",
510 {{"Set-Cookie", "test-cookie"}}},
511 };
512
513 INSTANTIATE_TEST_SUITE_P(WebSocketStreamServerSetCookieTest,
514 WebSocketStreamServerSetCookieTest,
515 ValuesIn(kServerSetCookieParameters));
516
517 } // namespace
518 } // namespace net
519