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 <optional>
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include "base/functional/bind.h"
12 #include "base/functional/callback_forward.h"
13 #include "base/location.h"
14 #include "base/memory/scoped_refptr.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/run_loop.h"
17 #include "base/strings/strcat.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/task/single_thread_task_runner.h"
21 #include "base/time/time.h"
22 #include "base/timer/timer.h"
23 #include "net/base/isolation_info.h"
24 #include "net/base/net_errors.h"
25 #include "net/cookies/canonical_cookie.h"
26 #include "net/cookies/canonical_cookie_test_helpers.h"
27 #include "net/cookies/cookie_access_result.h"
28 #include "net/cookies/cookie_inclusion_status.h"
29 #include "net/cookies/cookie_options.h"
30 #include "net/cookies/cookie_partition_key.h"
31 #include "net/cookies/cookie_partition_key_collection.h"
32 #include "net/cookies/cookie_store.h"
33 #include "net/cookies/cookie_util.h"
34 #include "net/cookies/site_for_cookies.h"
35 #include "net/http/http_request_headers.h"
36 #include "net/socket/socket_test_util.h"
37 #include "net/url_request/url_request_context.h"
38 #include "net/websockets/websocket_stream.h"
39 #include "net/websockets/websocket_stream_create_test_base.h"
40 #include "net/websockets/websocket_test_util.h"
41 #include "testing/gmock/include/gmock/gmock.h"
42 #include "testing/gtest/include/gtest/gtest.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 constexpr 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 = CanonicalCookie::CreateForTesting(cookie_url, cookie_line,
165 base::Time::Now());
166 store->SetCanonicalCookieAsync(
167 std::move(cookie), cookie_url, net::CookieOptions::MakeAllInclusive(),
168 base::BindOnce(&SetCookieHelperFunction, run_loop.QuitClosure(),
169 weak_is_called.GetWeakPtr(),
170 weak_set_cookie_result.GetWeakPtr()));
171 run_loop.Run();
172 ASSERT_TRUE(is_called);
173 ASSERT_TRUE(set_cookie_result);
174
175 CreateAndConnect(url, origin, site_for_cookies, isolation_info,
176 GetParam().cookie_header, WebSocketStandardResponse(""));
177 WaitUntilConnectDone();
178 EXPECT_FALSE(has_failed());
179 }
180
TEST_P(WebSocketStreamServerSetCookieTest,ServerSetCookie)181 TEST_P(WebSocketStreamServerSetCookieTest, ServerSetCookie) {
182 // For wss tests.
183 url_request_context_host_.AddSSLSocketDataProvider(
184 std::make_unique<SSLSocketDataProvider>(ASYNC, OK));
185
186 const GURL url(GetParam().url);
187 const GURL cookie_url(GetParam().cookie_url);
188 const url::Origin origin = url::Origin::Create(GURL(GetParam().url));
189 const SiteForCookies site_for_cookies = SiteForCookies::FromOrigin(origin);
190 const IsolationInfo isolation_info =
191 IsolationInfo::Create(IsolationInfo::RequestType::kOther, origin, origin,
192 SiteForCookies::FromOrigin(origin));
193 const std::string cookie_line(GetParam().cookie_line);
194 HttpRequestHeaders headers;
195 for (const auto& [key, value] : GetParam().cookie_header)
196 headers.SetHeader(key, value);
197 std::string cookie_header(headers.ToString());
198 const std::string response =
199 base::StrCat({"HTTP/1.1 101 Switching Protocols\r\n"
200 "Upgrade: websocket\r\n"
201 "Connection: Upgrade\r\n"
202 "Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=\r\n",
203 cookie_header});
204 CookieStore* store =
205 url_request_context_host_.GetURLRequestContext()->cookie_store();
206
207 CreateAndConnect(url, origin, site_for_cookies, isolation_info,
208 /*cookie_header=*/{}, response);
209 WaitUntilConnectDone();
210 EXPECT_FALSE(has_failed()) << failure_message();
211
212 bool is_called = false;
213 CookieList get_cookie_list_result;
214 base::WeakPtrFactory<bool> weak_is_called(&is_called);
215 base::WeakPtrFactory<CookieList> weak_get_cookie_list_result(
216 &get_cookie_list_result);
217 base::RunLoop run_loop;
218 store->GetCookieListWithOptionsAsync(
219 cookie_url, net::CookieOptions::MakeAllInclusive(),
220 CookiePartitionKeyCollection(),
221 base::BindOnce(&GetCookieListHelperFunction, run_loop.QuitClosure(),
222 weak_is_called.GetWeakPtr(),
223 weak_get_cookie_list_result.GetWeakPtr()));
224 run_loop.Run();
225 EXPECT_TRUE(is_called);
226 EXPECT_THAT(get_cookie_list_result, MatchesCookieLine(cookie_line));
227 }
228
229 // Test parameters definitions follow...
230
231 // The WebSocketExtraHeaders field can't be initialized at compile time, so this
232 // array is constructed at startup, but that's okay in a test.
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 // As with `kClientUseCookieParameters`, this is initialised at runtime.
386 const ServerSetCookieParameter kServerSetCookieParameters[] = {
387 // Cookies coming from ws
388 {"ws://www.example.com",
389 "http://www.example.com",
390 "test-cookie",
391 {{"Set-Cookie", "test-cookie"}}},
392
393 {"ws://www.example.com",
394 "https://www.example.com",
395 "test-cookie",
396 {{"Set-Cookie", "test-cookie"}}},
397
398 {"ws://www.example.com",
399 "ws://www.example.com",
400 "test-cookie",
401 {{"Set-Cookie", "test-cookie"}}},
402
403 {"ws://www.example.com",
404 "wss://www.example.com",
405 "test-cookie",
406 {{"Set-Cookie", "test-cookie"}}},
407
408 // Cookies coming from wss
409 {"wss://www.example.com",
410 "http://www.example.com",
411 "test-cookie",
412 {{"Set-Cookie", "test-cookie"}}},
413
414 {"wss://www.example.com",
415 "https://www.example.com",
416 "test-cookie",
417 {{"Set-Cookie", "test-cookie"}}},
418
419 {"wss://www.example.com",
420 "ws://www.example.com",
421 "test-cookie",
422 {{"Set-Cookie", "test-cookie"}}},
423
424 {"wss://www.example.com",
425 "wss://www.example.com",
426 "test-cookie",
427 {{"Set-Cookie", "test-cookie"}}},
428
429 // cookies coming from ws (sharing domain)
430 {"ws://www.example.com",
431 "http://www2.example.com",
432 "test-cookie",
433 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
434
435 {"ws://www.example.com",
436 "https://www2.example.com",
437 "test-cookie",
438 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
439
440 {"ws://www.example.com",
441 "ws://www2.example.com",
442 "test-cookie",
443 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
444
445 {"ws://www.example.com",
446 "wss://www2.example.com",
447 "test-cookie",
448 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
449
450 // cookies coming from wss (sharing domain)
451 {"wss://www.example.com",
452 "http://www2.example.com",
453 "test-cookie",
454 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
455
456 {"wss://www.example.com",
457 "https://www2.example.com",
458 "test-cookie",
459 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
460
461 {"wss://www.example.com",
462 "ws://www2.example.com",
463 "test-cookie",
464 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
465
466 {"wss://www.example.com",
467 "wss://www2.example.com",
468 "test-cookie",
469 {{"Set-Cookie", "test-cookie; Domain=example.com"}}},
470
471 // Non-matching cookies coming from ws
472 {"ws://www.example.com",
473 "http://www2.example.com",
474 "",
475 {{"Set-Cookie", "test-cookie"}}},
476
477 {"ws://www.example.com",
478 "https://www2.example.com",
479 "",
480 {{"Set-Cookie", "test-cookie"}}},
481
482 {"ws://www.example.com",
483 "ws://www2.example.com",
484 "",
485 {{"Set-Cookie", "test-cookie"}}},
486
487 {"ws://www.example.com",
488 "wss://www2.example.com",
489 "",
490 {{"Set-Cookie", "test-cookie"}}},
491
492 // Non-matching cookies coming from wss
493 {"wss://www.example.com",
494 "http://www2.example.com",
495 "",
496 {{"Set-Cookie", "test-cookie"}}},
497
498 {"wss://www.example.com",
499 "https://www2.example.com",
500 "",
501 {{"Set-Cookie", "test-cookie"}}},
502
503 {"wss://www.example.com",
504 "ws://www2.example.com",
505 "",
506 {{"Set-Cookie", "test-cookie"}}},
507
508 {"wss://www.example.com",
509 "wss://www2.example.com",
510 "",
511 {{"Set-Cookie", "test-cookie"}}},
512 };
513
514 INSTANTIATE_TEST_SUITE_P(WebSocketStreamServerSetCookieTest,
515 WebSocketStreamServerSetCookieTest,
516 ValuesIn(kServerSetCookieParameters));
517
518 } // namespace
519 } // namespace net
520