xref: /haiku/src/tests/kits/net/service/CookieTest.cpp (revision a5a3b2d9a3d95cbae71eaf371708c73a1780ac0d)
1 /*
2  * Copyright 2014, Haiku, inc.
3  * Distributed under the terms of the MIT licence
4  */
5 
6 
7 #include "CookieTest.h"
8 
9 
10 #include <cstdlib>
11 #include <cstring>
12 #include <cstdio>
13 
14 #include <NetworkKit.h>
15 
16 #include <cppunit/TestCaller.h>
17 #include <cppunit/TestSuite.h>
18 
19 
20 CookieTest::CookieTest()
21 {
22 }
23 
24 
25 CookieTest::~CookieTest()
26 {
27 }
28 
29 
30 // #pragma mark - Positive functionality tests
31 
32 
33 void
34 CookieTest::SimpleTest()
35 {
36 	BNetworkCookieJar jar;
37 	char buffer[256];
38 	BUrl url("http://www.chipchapin.com/WebTools/cookietest.php");
39 
40 	time_t t = time(NULL) + 6400; // Cookies expire in 1h45
41 	struct tm* now = gmtime(&t);
42 	status_t result;
43 
44 	// Add various cookies
45 	result = jar.AddCookie("testcookie1=present;", url);
46 	CPPUNIT_ASSERT(result == B_OK);
47 
48 	strftime(buffer, sizeof(buffer),
49 		"testcookie2=present; expires=%A, %d-%b-%Y %H:%M:%S", now);
50 	result = jar.AddCookie(buffer, url);
51 	CPPUNIT_ASSERT(result == B_OK);
52 
53 	strftime(buffer, sizeof(buffer),
54 		"testcookie3=present; expires=%A, %d-%b-%Y %H:%M:%S; path=/", now);
55 	result = jar.AddCookie(buffer, url);
56 	CPPUNIT_ASSERT(result == B_OK);
57 
58 	t += 3200; // expire in 2h40
59 	now = gmtime(&t);
60 	strftime(buffer, sizeof(buffer),
61 		"testcookie4=present; path=/; domain=www.chipchapin.com; "
62 		"expires=%A, %d-%b-%Y %H:%M:%S", now);
63 	result = jar.AddCookie(buffer, url);
64 	CPPUNIT_ASSERT(result == B_OK);
65 
66 	// Now check they were all properly added
67 	BNetworkCookieJar::Iterator it = jar.GetIterator();
68 
69 	int count = 0;
70 	while (it.HasNext()) {
71 		count ++;
72 		it.Next();
73 	}
74 
75 	CPPUNIT_ASSERT_EQUAL(4, count);
76 }
77 
78 
79 void
80 CookieTest::StandardTest()
81 {
82 	BNetworkCookieJar jar;
83 	status_t result;
84 
85 	BUrl url("http://testsuites.opera.com/cookies/001.php");
86 	result = jar.AddCookie("001=1", url);
87 	CPPUNIT_ASSERT(result == B_OK);
88 
89 	url.SetUrlString("http://testsuites.opera.com/cookies/001-1.php");
90 	const BNetworkCookie* cookie = _GetCookie(jar, url, "001");
91 
92 	CPPUNIT_ASSERT(cookie != NULL);
93 
94 	CPPUNIT_ASSERT(cookie->HasValue());
95 	CPPUNIT_ASSERT_EQUAL(BString("1"), cookie->Value());
96 	CPPUNIT_ASSERT(cookie->IsSessionCookie());
97 	CPPUNIT_ASSERT(cookie->ShouldDeleteAtExit());
98 
99 	CPPUNIT_ASSERT(!cookie->HasExpirationDate());
100 	CPPUNIT_ASSERT(!cookie->Secure());
101 	CPPUNIT_ASSERT(!cookie->HttpOnly());
102 }
103 
104 
105 void
106 CookieTest::ExpireTest()
107 {
108 	BNetworkCookieJar jar;
109 	status_t result;
110 
111 	BUrl url("http://testsuites.opera.com/cookies/003.php");
112 	result = jar.AddCookie("003=1", url);
113 	CPPUNIT_ASSERT(result == B_OK);
114 
115 	url.SetUrlString("http://testsuites.opera.com/cookies/003-1.php");
116 	result = jar.AddCookie("003=1; expires=Thu, 01-Jan-1970 00:00:10 GMT", url);
117 	CPPUNIT_ASSERT(result == B_OK);
118 
119 	url.SetUrlString("http://testsuites.opera.com/cookies/003-2.php");
120 	// The cookie should be expired.
121 	CPPUNIT_ASSERT(_GetCookie(jar, url, "003") == NULL);
122 }
123 
124 
125 void
126 CookieTest::PathTest()
127 {
128 	BNetworkCookieJar jar;
129 	status_t result;
130 
131 	BUrl url("http://testsuites.opera.com/cookies/004/004.php");
132 	result = jar.AddCookie("004=1", url);
133 	CPPUNIT_ASSERT(result == B_OK);
134 
135 	// Page in the same path can access the cookie
136 	url.SetUrlString("http://testsuites.opera.com/cookies/004/004-1.php");
137 	CPPUNIT_ASSERT(_GetCookie(jar, url, "004") != NULL);
138 
139 	// Page in parent directory cannot access the cookie
140 	url.SetUrlString("http://testsuites.opera.com/cookies/004-2.php");
141 	CPPUNIT_ASSERT(_GetCookie(jar, url, "004") == NULL);
142 }
143 
144 
145 void
146 CookieTest::MaxSizeTest()
147 {
148 	BNetworkCookieJar jar;
149 	status_t result;
150 
151 	BUrl url("http://testsuites.opera.com/cookies/006.php");
152 	BString cookieString("006=");
153 	for (int i = 0; i < 128; i++) {
154 		cookieString << "00xxxxxxxxxxxxxx16xxxxxxxxxxxxxx";
155 	}
156 	result = jar.AddCookie(cookieString, url);
157 	CPPUNIT_ASSERT(result == B_OK);
158 
159 	url.SetUrlString("http://testsuites.opera.com/cookies/006-1.php");
160 	const BNetworkCookie* cookie = _GetCookie(jar, url, "006");
161 	CPPUNIT_ASSERT(cookie != NULL);
162 	CPPUNIT_ASSERT(cookie->Value().Length() == 4096);
163 }
164 
165 
166 void
167 CookieTest::MaxNumberTest()
168 {
169 	BNetworkCookieJar jar;
170 	status_t result;
171 
172 	BUrl url("http://testsuites.opera.com/cookies/007.php");
173 	BString cookieString;
174 
175 	for (int i = 1; i <= 20; i++)
176 	{
177 		cookieString.SetToFormat("007-%d=1", i);
178 		result = jar.AddCookie(cookieString, url);
179 		CPPUNIT_ASSERT(result == B_OK);
180 	}
181 
182 	url.SetUrlString("http://testsuites.opera.com/cookies/007-1.php");
183 	for (int i = 1; i <= 20; i++)
184 	{
185 		cookieString.SetToFormat("007-%d", i);
186 		const BNetworkCookie* cookie = _GetCookie(jar, url, cookieString.String());
187 		CPPUNIT_ASSERT(cookie != NULL);
188 		CPPUNIT_ASSERT(cookie->Value() == "1");
189 	}
190 }
191 
192 
193 void
194 CookieTest::UpdateTest()
195 {
196 	BNetworkCookieJar jar;
197 	status_t result;
198 
199 	BUrl url("http://testsuites.opera.com/cookies/008.php");
200 	result = jar.AddCookie("008=1", url);
201 	CPPUNIT_ASSERT(result == B_OK);
202 
203 	url.SetUrlString("http://testsuites.opera.com/cookies/008-1.php");
204 	result = jar.AddCookie("008=2", url);
205 	CPPUNIT_ASSERT(result == B_OK);
206 
207 	url.SetUrlString("http://testsuites.opera.com/cookies/008-2.php");
208 	const BNetworkCookie* cookie = _GetCookie(jar, url, "008");
209 	CPPUNIT_ASSERT(cookie != NULL);
210 	CPPUNIT_ASSERT(cookie->Value() == "2");
211 }
212 
213 
214 void
215 CookieTest::HttpOnlyTest()
216 {
217 	BNetworkCookieJar jar;
218 	status_t result;
219 
220 	BUrl url("http://testsuites.opera.com/cookies/010.php");
221 	result = jar.AddCookie("010=1; Httponly; Max-age=1", url);
222 	CPPUNIT_ASSERT(result == B_OK);
223 
224 	url.SetUrlString("http://testsuites.opera.com/cookies/010-1.php");
225 	const BNetworkCookie* cookie = _GetCookie(jar, url, "010");
226 	CPPUNIT_ASSERT(cookie != NULL);
227 	CPPUNIT_ASSERT(cookie->Value() == "1");
228 	CPPUNIT_ASSERT(cookie->HttpOnly());
229 	CPPUNIT_ASSERT(cookie->HasExpirationDate());
230 }
231 
232 
233 void
234 CookieTest::EncodingTest()
235 {
236 	BNetworkCookieJar jar;
237 	status_t result;
238 
239 	BUrl url("http://testsuites.opera.com/cookies/011.php");
240 	result = jar.AddCookie("011=UTF-8 \303\246\303\270\303\245 \346\227\245\346\234\254;", url);
241 	CPPUNIT_ASSERT(result == B_OK);
242 
243 	url.SetUrlString("http://testsuites.opera.com/cookies/011-1.php");
244 	const BNetworkCookie* cookie = _GetCookie(jar, url, "011");
245 	CPPUNIT_ASSERT(cookie != NULL);
246 	CPPUNIT_ASSERT_EQUAL(
247 		BString("UTF-8 \303\246\303\270\303\245 \346\227\245\346\234\254"),
248 		cookie->Value());
249 }
250 
251 
252 void
253 CookieTest::DomainTest()
254 {
255 	BNetworkCookieJar jar;
256 	status_t result;
257 
258 	BUrl url("http://testsuites.opera.com/cookies/012.php");
259 	result = jar.AddCookie("012-1=1; Domain=\"opera.com\"", url);
260 	CPPUNIT_ASSERT(result == B_OK);
261 	result = jar.AddCookie("012-1=1; Domain=\"example.com\"", url);
262 	CPPUNIT_ASSERT(result != B_OK);
263 
264 	url.SetUrlString("http://testsuites.opera.com/cookies/012-1.php");
265 	const BNetworkCookie* cookie = _GetCookie(jar, url, "012-1");
266 	CPPUNIT_ASSERT(cookie != NULL);
267 	CPPUNIT_ASSERT(cookie->Value() == "1");
268 
269 	cookie = _GetCookie(jar, url, "012-2");
270 	CPPUNIT_ASSERT(cookie == NULL);
271 }
272 
273 
274 void
275 CookieTest::PersistantTest()
276 {
277 	BNetworkCookieJar jar;
278 	status_t result;
279 	char buffer[256];
280 
281 	time_t t = time(NULL) + 100000;
282 	struct tm* now = gmtime(&t);
283 
284 	NextSubTest();
285 
286 	BUrl url("http://testsuites.opera.com/cookies/013.php");
287 
288 	strftime(buffer, sizeof(buffer),
289 		"013-1=1; Expires=%a, %d-%b-%Y %H:%M:%S", now);
290 	result = jar.AddCookie(buffer, url);
291 	CPPUNIT_ASSERT(result == B_OK);
292 
293 	result = jar.AddCookie("013-2=1", url);
294 	CPPUNIT_ASSERT(result == B_OK);
295 
296 	NextSubTest();
297 
298 	url.SetUrlString("http://testsuites.opera.com/cookies/013-1.php");
299 
300 	const BNetworkCookie* cookie = _GetCookie(jar, url, "013-1");
301 	CPPUNIT_ASSERT(cookie != NULL);
302 	CPPUNIT_ASSERT(cookie->Value() == "1");
303 	CPPUNIT_ASSERT(cookie->HasExpirationDate());
304 
305 	cookie = _GetCookie(jar, url, "013-2");
306 	CPPUNIT_ASSERT(cookie != NULL);
307 	CPPUNIT_ASSERT(cookie->Value() == "1");
308 	CPPUNIT_ASSERT(!cookie->HasExpirationDate());
309 
310 	// Simulate exit
311 	jar.PurgeForExit();
312 
313 	NextSubTest();
314 
315 	cookie = _GetCookie(jar, url, "013-1");
316 	CPPUNIT_ASSERT(cookie != NULL);
317 	CPPUNIT_ASSERT(cookie->Value() == "1");
318 	CPPUNIT_ASSERT(cookie->HasExpirationDate());
319 
320 	cookie = _GetCookie(jar, url, "013-2");
321 	CPPUNIT_ASSERT(cookie == NULL);
322 }
323 
324 
325 void
326 CookieTest::OverwriteTest()
327 {
328 	BNetworkCookieJar jar;
329 	status_t result;
330 
331 	BUrl url("http://testsuites.opera.com/cookies/015/015.php");
332 	result = jar.AddCookie("015-01=1", url);
333 	CPPUNIT_ASSERT(result == B_OK);
334 
335 	url.SetUrlString("http://testsuites.opera.com/cookies/015-1.php");
336 	result = jar.AddCookie("015-01=1", url);
337 	result = jar.AddCookie("015-02=1", url);
338 	CPPUNIT_ASSERT(result == B_OK);
339 
340 	url.SetUrlString("http://testsuites.opera.com/cookies/015/015-2.php");
341 	result = jar.AddCookie("015-01=1", url);
342 	result = jar.AddCookie("015-02=1", url);
343 	CPPUNIT_ASSERT(result == B_OK);
344 
345 	url.SetUrlString("http://testsuites.opera.com/cookies/015/015-3.php");
346 	BNetworkCookieJar::UrlIterator it = jar.GetUrlIterator(url);
347 	int count = 0;
348 
349 	while (it.HasNext()) {
350 		it.Next();
351 		count++;
352 	}
353 
354 	CPPUNIT_ASSERT_EQUAL(4, count);
355 }
356 
357 
358 void
359 CookieTest::OrderTest()
360 {
361 	BNetworkCookieJar jar;
362 	status_t result;
363 
364 	BUrl url("http://testsuites.opera.com/cookies/016.php");
365 	result = jar.AddCookie("016-01=1", url);
366 	CPPUNIT_ASSERT(result == B_OK);
367 
368 	url.SetUrlString("http://testsuites.opera.com/cookies/016/016-1.php");
369 	result = jar.AddCookie("016-02=1", url);
370 	CPPUNIT_ASSERT(result == B_OK);
371 
372 	url.SetUrlString("http://testsuites.opera.com/cookies/016/016-2.php");
373 	BNetworkCookieJar::UrlIterator it = jar.GetUrlIterator(url);
374 	int count = 0;
375 
376 	// Check that the cookie with the most specific path is sent first
377 	while (it.HasNext()) {
378 		const BNetworkCookie* cookie = it.Next();
379 		switch(count)
380 		{
381 			case 0:
382 				CPPUNIT_ASSERT_EQUAL(BString("016-02"), cookie->Name());
383 				break;
384 			case 1:
385 				CPPUNIT_ASSERT_EQUAL(BString("016-01"), cookie->Name());
386 				break;
387 		}
388 		count++;
389 	}
390 
391 	CPPUNIT_ASSERT_EQUAL(2, count);
392 }
393 
394 
395 // #pragma mark - Error handling and extended tests
396 
397 
398 void
399 CookieTest::ExpireParsingTest()
400 {
401 	BUrl url("http://testsuites.opera.com/cookies/301.php");
402 	BNetworkCookie cookie;
403 	status_t result;
404 
405 	BString bigData("301-16=1; Expires=\"");
406 	for (int i = 0; i < 1500; i++)
407 		bigData << "abcdefghijklmnopqrstuvwxyz";
408 	bigData << "\";";
409 
410 	struct Test {
411 		const char* cookieString;
412 		bool canParse;
413 		bool sessionOnly;
414 		bool expired;
415 	};
416 
417 	Test tests[] = {
418 		{ "301-01=1; Expires=\"notAValidValue\";",
419 			true, true, false }, // Obviously invalid date
420 		{ "301-02=1; Expires=\"Wed, 08-Nov-2035 01:04:33\";",
421 			true, false, false }, // Valid date
422 		{ "301-03=1; Expires=\"Tue, 19-Jan-2038 03:14:06\";",
423 			true, false, false }, // Valid date, after year 2038 time_t overflow
424 		{ "301-04=1; Expires=\"Fri, 13-Dec-1901 20:45:51\";",
425 			true, false, true }, // Valid date, in the past
426 		{ "301-05=1; Expires=\"Thu, 33-Nov-2035 01:04:33\";",
427 			true, true, false }, // Invalid day
428 		{ "301-06=1; Expires=\"Wed, 08-Siz-2035 01:04:33\";",
429 			true, true, false }, // Invalid month
430 		{ "301-07=1; Expires=\"Wed, 08-Nov-9035 01:04:33\";",
431 			true, false, false }, // Very far in the future
432 			// NOTE: Opera testsuite considers it should be a session cookie.
433 		{ "301-08=1; Expires=\"Wed, 08-Nov-2035 75:04:33\";",
434 			true, true, false }, // Invalid hour
435 		{ "301-09=1; Expires=\"Wed, 08-Nov-2035 01:75:33\";",
436 			true, true, false }, // Invalid minute
437 		{ "301-10=1; Expires=\"Wed, 08-Nov-2035 01:04:75\";",
438 			true, true, false }, // Invalid second
439 		{ "301-11=1; Expires=\"XXX, 08-Nov-2035 01:04:33\";",
440 			true, true, false }, // Invalid weekday
441 		{ "301-12=1; Expires=\"Thu, XX-Nov-2035 01:04:33\";",
442 			true, true, false }, // Non-digit day
443 		{ "301-13=1; Expires=\"Thu, 08-Nov-XXXX 01:04:33\";",
444 			true, true, false }, // Non-digit year
445 		{ "301-14=1; Expires=\"Thu, 08-Nov-2035 XX:XX:33\";",
446 			true, true, false }, // Non-digit hour and minute
447 		{ "301-15=1; Expires=\"Thu, 08-Nov-2035 01:04:XX\";",
448 			true, true, false }, // Non-digit second
449 		{ bigData.String(),
450 			true, true, false }, // Very long invalid string
451 			// NOTE: Opera doesn't register the cookie at all.
452 		{ "301-17=1; Expires=\"Thu, 99999-Nov-2035 01:04:33\";",
453 			true, true, false }, // Day with many digits
454 		{ "301-18=1; Expires=\"Thu, 25-Nov-99999 01:04:33\";",
455 			true, true, false }, // Year with many digits
456 			// NOTE: Opera tests 301-17 twice and misses this test.
457 		{ "301-19=1; Expires=\"Thu, 25-Nov-2035 99999:04:33\";",
458 			true, true, false }, // Hour with many digits
459 		{ "301-20=1; Expires=\"Thu, 25-Nov-2035 01:99999:33\";",
460 			true, true, false }, // Minute with many digits
461 		{ "301-21=1; Expires=\"Thu, 25-Nov-2035 01:04:99999\";",
462 			true, true, false }, // Second with many digits
463 		{ "301-22=1; Expires=\"99999999999999999999\";",
464 			true, true, false }, // Huge number
465 		{ "301-23=1; Expires=\"Fri, 13-Dec-101 20:45:51\";",
466 			true, false, true }, // Very far in the past
467 		{ "301-24=1; EXPiReS=\"Wed, 08-Nov-2035 01:04:33\";",
468 			true, false, false }, // Case insensitive key parsing
469 		{ "301-25=1; Expires=Wed, 08-Nov-2035 01:04:33\";",
470 			true, true, false }, // Missing opening quote
471 			// NOTE: unlike Opera, we accept badly quoted values for cookie
472 			// attributes. This allows to handle unquoted values from the early
473 			// cookie spec properly. However, a date with a quote inside it
474 			// should not be accepted, so this will be a session cookie.
475 		{ "301-26=1; Expires=\"Wed, 08-Nov-2035 01:04:33;",
476 			true, true, false }, // Missing closing quote
477 		{ "301-27=1; Expires:\"Wed, 08-Nov-2035 01:04:33\";",
478 			true, true, false }, // Uses : instead of =
479 			// NOTE: unlike Opera, we allow this as a cookie with a strange
480 			// name and no value
481 		{ "301-28=1; Expires;=\"Wed, 08-Nov-2035 01:04:33\";",
482 			true, true, false }, // Extra ; after name
483 		{ "301-29=1; Expired=\"Wed, 08-Nov-2035 01:04:33\";",
484 			true, true, false }, // Expired instead of Expires
485 		{ "301-30=1; Expires=\"Wed; 08-Nov-2035 01:04:33\";",
486 			true, true, false }, // ; in value
487 		{ "301-31=1; Expires=\"Wed,\\r\\n 08-Nov-2035 01:04:33\";",
488 			true, true, false }, // escaped newline in value
489 			// NOTE: Only here for completeness. This is what the Opera
490 			// testsuite sends in test 31, but I suspect they were trying to
491 			// test the following case.
492 		{ "301-31b=1; Expires=\"Wed,\r\n 08-Nov-2035 01:04:33\";",
493 			true, false, false }, // newline in value
494 			// NOTE: This can't really happen when we get cookies from HTTP
495 			// headers. It could happen when the cookie is set from meta html
496 			// tags or from JS.
497 	};
498 
499 	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
500 	{
501 		NextSubTest();
502 
503 		result = cookie.ParseCookieString(tests[i].cookieString, url);
504 		CPPUNIT_ASSERT_EQUAL_MESSAGE("Cookie can be parsed",
505 			tests[i].canParse, result == B_OK);
506 		CPPUNIT_ASSERT_EQUAL_MESSAGE("Cookie is session only",
507 			tests[i].sessionOnly, cookie.IsSessionCookie());
508 		CPPUNIT_ASSERT_EQUAL_MESSAGE("Cookie has expired",
509 			tests[i].expired, cookie.ShouldDeleteNow());
510 	}
511 }
512 
513 
514 void
515 CookieTest::PathMatchingTest()
516 {
517 	const BUrl url("http://testsuites.opera.com/cookies/302/302.php");
518 	const BUrl url1("http://testsuites.opera.com/cookies/302-5.php");
519 	const BUrl url2("http://testsuites.opera.com/cookies/302/302-3.php");
520 	const BUrl url3("http://testsuites.opera.com/cookies/302/sub/302-4.php");
521 	const BUrl url4("http://testsuites.opera.com/cookies/302-2/302-6.php");
522 	BNetworkCookie cookie;
523 	status_t result;
524 
525 	BString bigData("302-24=1; Path=\"/cookies/302/");
526 	for (int i = 0; i < 1500; i++)
527 		bigData << "abcdefghijklmnopqrstuvwxyz";
528 	bigData << "\";";
529 
530 	struct Test {
531 		const char* cookieString;
532 		bool canSet;
533 		bool url1;
534 		bool url2;
535 		bool url3;
536 		bool url4;
537 	};
538 
539 	const Test tests[] = {
540 		{ "302-01=1; Path=\"/\"",
541 			true, true, true, true, true },
542 		{ "302-02=1; Path=\"/cookies\"",
543 			true, true, true, true, true },
544 		{ "302-03=1; Path=\"/cookies/\"",
545 			true, true, true, true, true },
546 		{ "302-04=1; Path=\"/cookies/302\"",
547 			true, false, true, true, true },
548 		{ "302-05=1; Path=\"/cookies/302/\"",
549 			true, false, true, true, false },
550 		{ "302-06=1; Path=\"/cookies/302/.\"",
551 			false, false, false, false, false },
552 		{ "302-07=1; Path=\"/cookies/302/../302\"",
553 			false, false, false, false, false },
554 		{ "302-08=1; Path=\"/cookies/302/../302-2\"",
555 			false, false, false, false, false },
556 		{ "302-09=1; Path=\"/side\"",
557 			false, false, false, false, false },
558 		{ "302-10=1; Path=\"/cookies/302-2\"",
559 			true, false, false, false, true },
560 		{ "302-11=1; Path=\"/cookies/302-2/\"",
561 			true, false, false, false, true },
562 		{ "302-12=1; Path=\"sub\"",
563 			false, false, false, false, false },
564 		{ "302-13=1; Path=\"sub/\"",
565 			false, false, false, false, false },
566 		{ "302-14=1; Path=\".\"",
567 			false, false, false, false, false },
568 		{ "302-15=1; Path=\"/cookies/302/sub\"",
569 			true, false, false, true, false },
570 		{ "302-16=1; Path=\"/cookies/302/sub/\"",
571 			true, false, false, true, false },
572 		{ "302-17=1; Path=\"/cookies/302/sub/..\"",
573 			false, false, false, false, false },
574 		{ "302-18=1; Path=/",
575 			true, true, true, true, true },
576 		{ "302-19=1; Path=/ /",
577 			false, false, false, false, false },
578 		{ "302-20=1; Path=\"/",
579 			false, false, false, false, false },
580 		{ "302-21=1; Path=/\"",
581 			false, false, false, false, false },
582 		{ "302-22=1; Path=\\/",
583 			false, false, false, false, false },
584 		{ "302-23=1; Path=\n/",
585 			false, false, false, false, false },
586 		{ bigData,
587 			false, false, false, false, false },
588 	};
589 
590 	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
591 	{
592 		NextSubTest();
593 
594 		result = cookie.ParseCookieString(tests[i].cookieString, url);
595 
596 		CPPUNIT_ASSERT_EQUAL_MESSAGE("Allowed to set cookie",
597 			tests[i].canSet, result == B_OK);
598 		CPPUNIT_ASSERT_EQUAL(tests[i].url1, cookie.IsValidForUrl(url1));
599 		CPPUNIT_ASSERT_EQUAL(tests[i].url2, cookie.IsValidForUrl(url2));
600 		CPPUNIT_ASSERT_EQUAL(tests[i].url3, cookie.IsValidForUrl(url3));
601 		CPPUNIT_ASSERT_EQUAL(tests[i].url4, cookie.IsValidForUrl(url4));
602 	}
603 }
604 
605 
606 void
607 CookieTest::DomainMatchingTest()
608 {
609 	const BUrl setter("http://testsuites.opera.com/cookies/304.php");
610 	const BUrl getter("http://testsuites.opera.com/cookies/304-1.php");
611 
612 	BString bigData("304-12=1; Domain=\"");
613 	for (int i = 0; i < 1500; i++)
614 		bigData << "abcdefghijklmnopqrstuvwxyz";
615 	bigData << "\";";
616 
617 	struct Test {
618 		const char* cookieString;
619 		bool shouldMatch;
620 	};
621 
622 	const Test tests[] = {
623 		{ "304-01=1; Domain=\"opera.com\"", true },
624 		{ "304-02=1; Domain=\".opera.com\"", true },
625 		{ "304-03=1; Domain=\".com\"", false },
626 		{ "304-04=1; Domain=\"pera.com\"", false },
627 		{ "304-05=1; Domain=\"?pera.com\"", false },
628 		{ "304-06=1; Domain=\"*.opera.com\"", false },
629 		{ "304-07=1; Domain=\"300.300.300.300\"", false },
630 		{ "304-08=1; Domain=\"123456123456123456\"", false },
631 		{ "304-09=1; Domain=\"/opera.com\"", false },
632 		{ "304-10=1; Domain=\"opera.com/\"", false },
633 		{ "304-11=1; Domain=\"example.com\"", false },
634 		{ bigData, false },
635 		{ "304-13=1; Domain=\"opera com\"", false },
636 		{ "304-14=1; Domain=opera.com\"", false },
637 		{ "304-15=1; Domain=\"opera.com", false },
638 		{ "304-16=1; Domain=opera.com", true },
639 		{ "304-17=1; Domain=\"*.com\"", false },
640 		{ "304-18=1; Domain=\"*opera.com\"", false },
641 		{ "304-19=1; Domain=\"*pera.com\"", false },
642 		{ "304-20=1; Domain=\"\"", false },
643 		{ "304-21=1; Domain=\"\346\227\245\346\234\254\"", false },
644 		{ "304-22=1; Domain=\"OPERA.COM\"", true },
645 		{ "304-23=1; Domain=\"195.189.143.182\"", false },
646 	};
647 
648 	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
649 	{
650 		NextSubTest();
651 
652 		BNetworkCookie cookie(tests[i].cookieString, setter);
653 		CPPUNIT_ASSERT_EQUAL(tests[i].shouldMatch,
654 			cookie.IsValidForUrl(getter));
655 	}
656 }
657 
658 
659 void
660 CookieTest::MaxAgeParsingTest()
661 {
662 	const BUrl setter("http://testsuites.opera.com/cookies/305.php");
663 
664 	BString bigData("305-12=1; Max-Age=\"");
665 	for (int i = 0; i < 1500; i++)
666 		bigData << "abcdefghijklmnopqrstuvwxyz";
667 	bigData << "\";";
668 
669 	struct Test {
670 		const char* cookieString;
671 		bool expired;
672 	};
673 
674 	const Test tests[] = {
675 		{ "305-01=1; Max-Age=\"notAValidValue\"", true },
676 		{ "305-02=1; Max-Age=\"Wed, 08-Nov-2035 01:04:33\"", true },
677 		{ "305-03=1; Max-Age=\"0\"", true },
678 		{ "305-04=1; Max-Age=\"1\"", false },
679 		{ "305-05=1; Max-Age=\"10000\"", false },
680 		{ "305-06=1; Max-Age=\"-1\"", true },
681 		{ "305-07=1; Max-Age=\"0.5\"", true },
682 		{ "305-08=1; Max-Age=\"9999999999999999999999999\"", false },
683 		{ "305-09=1; Max-Age=\"-9999999999999999999999999\"", true },
684 		{ "305-10=1; Max-Age=\"+10000\"", false },
685 		{ "305-11=1; Max-Age=\"Fri, 13-Dec-1901 20:45:52\"", true },
686 		{ bigData, true },
687 		{ "305-13=1; Max-Age=\"0+10000\"", true },
688 		{ "305-14=1; Max-Age=\"10000+0\"", true },
689 		{ "305-15=1; Max-Age=10000\"", true },
690 		{ "305-16=1; Max-Age=\"10000", true },
691 		{ "305-17=1; Max-Age=10000", false },
692 		{ "305-18=1; Max-Age=100\"00", true },
693 	};
694 
695 	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
696 	{
697 		NextSubTest();
698 
699 		BNetworkCookie cookie(tests[i].cookieString, setter);
700 		CPPUNIT_ASSERT_EQUAL(tests[i].expired, cookie.ShouldDeleteNow());
701 	}
702 }
703 
704 
705 void
706 CookieTest::ExplodeTest()
707 {
708 	struct Test {
709 		const char* cookieString;
710 		const char*	url;
711 		struct
712 		{
713 			bool		valid;
714 			const char* name;
715 			const char* value;
716 			const char* domain;
717 			const char* path;
718 			bool 		secure;
719 			bool 		httponly;
720 			bool		session;
721 			BDateTime	expire;
722 		} expected;
723 	};
724 
725 	Test tests[] = {
726 	//     Cookie string      URL
727 	//     ------------- -------------
728 	//		   Valid     Name     Value	       Domain         Path      Secure  HttpOnly Session  Expiration
729 	//       --------- -------- --------- ----------------- ---------  -------- -------- -------  ----------
730 		// Normal cookies
731 		{ "name=value", "http://www.example.com/path/path",
732 			{  true,    "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
733 		{ "name=value; domain=example.com; path=/; secure", "http://www.example.com/path/path",
734 			{  true,    "name",  "value",   "example.com",   "/"    ,   true,    false,   true,   BDateTime() } },
735 		{ "name=value; httponly; secure", "http://www.example.com/path/path",
736 			{  true,    "name",  "value", "www.example.com", "/path",   true,    true,    true,   BDateTime() } },
737 		{ "name=value; expires=Wed, 20-Feb-2013 20:00:00 UTC", "http://www.example.com/path/path",
738 			{  true,    "name",  "value", "www.example.com", "/path",   false,   false,   false,
739 				BDateTime(BDate(2013, 2, 20), BTime(20, 0, 0, 0)) } },
740 		// Valid cookie with bad form
741 		{ "name=  ;  domain   =example.com  ;path=/;  secure = yup  ; blahblah ;)", "http://www.example.com/path/path",
742 			{  true,    "name",  "",   "example.com",   "/"    ,   true,    false,   true,   BDateTime() } },
743 		// Invalid path
744 		{ "name=value; path=invalid", "http://www.example.com/path/path",
745 			{  false,    "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
746 		// Setting for other subdomain (invalid)
747 		{ "name=value; domain=subdomain.example.com", "http://www.example.com/path/path",
748 			{  false,   "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
749 		// Various invalid cookies
750 		{ "name", "http://www.example.com/path/path",
751 			{  false,   "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
752 		{ "; domain=example.com", "http://www.example.com/path/path",
753 			{  false,   "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } }
754 	};
755 
756 	BNetworkCookie cookie;
757 
758 	for (uint32 i = 0; i < (sizeof(tests) / sizeof(Test)); i++) {
759 		NextSubTest();
760 
761 		BUrl url(tests[i].url);
762 		cookie.ParseCookieString(tests[i].cookieString, url);
763 
764 		CPPUNIT_ASSERT(tests[i].expected.valid == cookie.IsValid());
765 
766 		if (!tests[i].expected.valid)
767 			continue;
768 
769 		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.name), cookie.Name());
770 		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.value), cookie.Value());
771 		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.domain),
772 			cookie.Domain());
773 		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.path), cookie.Path());
774 		CPPUNIT_ASSERT(tests[i].expected.secure == cookie.Secure());
775 		CPPUNIT_ASSERT(tests[i].expected.httponly == cookie.HttpOnly());
776 		CPPUNIT_ASSERT(tests[i].expected.session == cookie.IsSessionCookie());
777 
778 		if (!cookie.IsSessionCookie())
779 			CPPUNIT_ASSERT_EQUAL(tests[i].expected.expire.Time_t(),
780 				cookie.ExpirationDate());
781 	}
782 }
783 
784 
785 /* static */ void
786 CookieTest::AddTests(BTestSuite& parent)
787 {
788 	CppUnit::TestSuite& suite = *new CppUnit::TestSuite("CookieTest");
789 
790 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::SimpleTest",
791 		&CookieTest::SimpleTest));
792 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
793 		"CookieTest::StandardTest", &CookieTest::StandardTest));
794 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::ExpireTest",
795 		&CookieTest::ExpireTest));
796 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::PathTest",
797 		&CookieTest::PathTest));
798 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::MaxSizeTest",
799 		&CookieTest::MaxSizeTest));
800 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
801 		"CookieTest::MaxNumberTest", &CookieTest::MaxNumberTest));
802 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::UpdateTest",
803 		&CookieTest::UpdateTest));
804 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
805 		"CookieTest::HttpOnlyTest", &CookieTest::HttpOnlyTest));
806 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
807 		"CookieTest::EncodingTest", &CookieTest::EncodingTest));
808 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::DomainTest",
809 		&CookieTest::DomainTest));
810 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
811 		"CookieTest::PersistantTest", &CookieTest::PersistantTest));
812 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
813 		"CookieTest::OverwriteTest", &CookieTest::OverwriteTest));
814 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
815 		"CookieTest::OrderTest", &CookieTest::OrderTest));
816 
817 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
818 		"CookieTest::ExpireParsingTest", &CookieTest::ExpireParsingTest));
819 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
820 		"CookieTest::PathMatchingTest", &CookieTest::PathMatchingTest));
821 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
822 		"CookieTest::DomainMatchingTest", &CookieTest::DomainMatchingTest));
823 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
824 		"CookieTest::MaxAgeParsingTest", &CookieTest::MaxAgeParsingTest));
825 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
826 		"CookieTest::ExplodeTest", &CookieTest::ExplodeTest));
827 
828 	parent.addTest("CookieTest", &suite);
829 }
830 
831 
832 const BNetworkCookie*
833 CookieTest::_GetCookie(BNetworkCookieJar& jar, const BUrl& url,
834 	const char* name)
835 {
836 	BNetworkCookieJar::UrlIterator it = jar.GetUrlIterator(url);
837 	const BNetworkCookie* result = NULL;
838 
839 	while (it.HasNext()) {
840 		const BNetworkCookie* cookie = it.Next();
841 		if (cookie->Name() == name) {
842 			// Make sure the cookie is found only once.
843 			CPPUNIT_ASSERT(result == NULL);
844 
845 			result = cookie;
846 		}
847 	}
848 
849 	return result;
850 }
851