xref: /aosp_15_r20/external/cronet/net/websockets/websocket_stream_cookie_test.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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