xref: /haiku/src/tests/kits/net/service/CookieTest.cpp (revision 21258e2674226d6aa732321b6f8494841895af5f)
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 		{ "301-32=1; expires=Mon, 31-Oct-2035 08:08:40 GMT;",
498 			true, false, false }, // Wrong weekday
499 		{ "301-33=1; expires=Tue, 19-Oct-66 07:08:40;",
500 			true, false, false }, // RFC1036 format with 2 digit year
501 		{ "301-34=1; expires=Sat, 21-Oct-56 07:08:40 GMT;",
502 			true, false, false }, // RFC1036 format with 2 digit year
503 	};
504 
505 	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
506 	{
507 		NextSubTest();
508 
509 		result = cookie.ParseCookieString(tests[i].cookieString, url);
510 		CPPUNIT_ASSERT_EQUAL_MESSAGE("Cookie can be parsed",
511 			tests[i].canParse, result == B_OK);
512 		CPPUNIT_ASSERT_EQUAL_MESSAGE("Cookie is session only",
513 			tests[i].sessionOnly, cookie.IsSessionCookie());
514 		CPPUNIT_ASSERT_EQUAL_MESSAGE("Cookie has expired",
515 			tests[i].expired, cookie.ShouldDeleteNow());
516 	}
517 }
518 
519 
520 void
521 CookieTest::PathMatchingTest()
522 {
523 	const BUrl url("http://testsuites.opera.com/cookies/302/302.php");
524 	const BUrl url1("http://testsuites.opera.com/cookies/302-5.php");
525 	const BUrl url2("http://testsuites.opera.com/cookies/302/302-3.php");
526 	const BUrl url3("http://testsuites.opera.com/cookies/302/sub/302-4.php");
527 	const BUrl url4("http://testsuites.opera.com/cookies/302-2/302-6.php");
528 	BNetworkCookie cookie;
529 	status_t result;
530 
531 	BString bigData("302-24=1; Path=\"/cookies/302/");
532 	for (int i = 0; i < 1500; i++)
533 		bigData << "abcdefghijklmnopqrstuvwxyz";
534 	bigData << "\";";
535 
536 	struct Test {
537 		const char* cookieString;
538 		bool canSet;
539 		bool url1;
540 		bool url2;
541 		bool url3;
542 		bool url4;
543 	};
544 
545 	const Test tests[] = {
546 		{ "302-01=1; Path=\"/\"",
547 			true, true, true, true, true },
548 		{ "302-02=1; Path=\"/cookies\"",
549 			true, true, true, true, true },
550 		{ "302-03=1; Path=\"/cookies/\"",
551 			true, true, true, true, true },
552 		{ "302-04=1; Path=\"/cookies/302\"",
553 			true, false, true, true, true },
554 		{ "302-05=1; Path=\"/cookies/302/\"",
555 			true, false, true, true, false },
556 		{ "302-06=1; Path=\"/cookies/302/.\"",
557 			false, false, false, false, false },
558 		{ "302-07=1; Path=\"/cookies/302/../302\"",
559 			false, false, false, false, false },
560 		{ "302-08=1; Path=\"/cookies/302/../302-2\"",
561 			false, false, false, false, false },
562 		{ "302-09=1; Path=\"/side\"",
563 			false, false, false, false, false },
564 		{ "302-10=1; Path=\"/cookies/302-2\"",
565 			true, false, false, false, true },
566 		{ "302-11=1; Path=\"/cookies/302-2/\"",
567 			true, false, false, false, true },
568 		{ "302-12=1; Path=\"sub\"",
569 			false, false, false, false, false },
570 		{ "302-13=1; Path=\"sub/\"",
571 			false, false, false, false, false },
572 		{ "302-14=1; Path=\".\"",
573 			false, false, false, false, false },
574 		{ "302-15=1; Path=\"/cookies/302/sub\"",
575 			true, false, false, true, false },
576 		{ "302-16=1; Path=\"/cookies/302/sub/\"",
577 			true, false, false, true, false },
578 		{ "302-17=1; Path=\"/cookies/302/sub/..\"",
579 			false, false, false, false, false },
580 		{ "302-18=1; Path=/",
581 			true, true, true, true, true },
582 		{ "302-19=1; Path=/ /",
583 			false, false, false, false, false },
584 		{ "302-20=1; Path=\"/",
585 			false, false, false, false, false },
586 		{ "302-21=1; Path=/\"",
587 			false, false, false, false, false },
588 		{ "302-22=1; Path=\\/",
589 			false, false, false, false, false },
590 		{ "302-23=1; Path=\n/",
591 			false, false, false, false, false },
592 		{ bigData,
593 			false, false, false, false, false },
594 	};
595 
596 	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
597 	{
598 		NextSubTest();
599 
600 		result = cookie.ParseCookieString(tests[i].cookieString, url);
601 
602 		CPPUNIT_ASSERT_EQUAL_MESSAGE("Allowed to set cookie",
603 			tests[i].canSet, result == B_OK);
604 		CPPUNIT_ASSERT_EQUAL(tests[i].url1, cookie.IsValidForUrl(url1));
605 		CPPUNIT_ASSERT_EQUAL(tests[i].url2, cookie.IsValidForUrl(url2));
606 		CPPUNIT_ASSERT_EQUAL(tests[i].url3, cookie.IsValidForUrl(url3));
607 		CPPUNIT_ASSERT_EQUAL(tests[i].url4, cookie.IsValidForUrl(url4));
608 	}
609 }
610 
611 
612 void
613 CookieTest::DomainMatchingTest()
614 {
615 	const BUrl setter("http://testsuites.opera.com/cookies/304.php");
616 	const BUrl getter("http://testsuites.opera.com/cookies/304-1.php");
617 
618 	BString bigData("304-12=1; Domain=\"");
619 	for (int i = 0; i < 1500; i++)
620 		bigData << "abcdefghijklmnopqrstuvwxyz";
621 	bigData << "\";";
622 
623 	struct Test {
624 		const char* cookieString;
625 		bool shouldMatch;
626 	};
627 
628 	const Test tests[] = {
629 		{ "304-01=1; Domain=\"opera.com\"", true },
630 		{ "304-02=1; Domain=\".opera.com\"", true },
631 		{ "304-03=1; Domain=\".com\"", false },
632 		{ "304-04=1; Domain=\"pera.com\"", false },
633 		{ "304-05=1; Domain=\"?pera.com\"", false },
634 		{ "304-06=1; Domain=\"*.opera.com\"", false },
635 		{ "304-07=1; Domain=\"300.300.300.300\"", false },
636 		{ "304-08=1; Domain=\"123456123456123456\"", false },
637 		{ "304-09=1; Domain=\"/opera.com\"", false },
638 		{ "304-10=1; Domain=\"opera.com/\"", false },
639 		{ "304-11=1; Domain=\"example.com\"", false },
640 		{ bigData, false },
641 		{ "304-13=1; Domain=\"opera com\"", false },
642 		{ "304-14=1; Domain=opera.com\"", false },
643 		{ "304-15=1; Domain=\"opera.com", false },
644 		{ "304-16=1; Domain=opera.com", true },
645 		{ "304-17=1; Domain=\"*.com\"", false },
646 		{ "304-18=1; Domain=\"*opera.com\"", false },
647 		{ "304-19=1; Domain=\"*pera.com\"", false },
648 		{ "304-20=1; Domain=\"\"", false },
649 		{ "304-21=1; Domain=\"\346\227\245\346\234\254\"", false },
650 		{ "304-22=1; Domain=\"OPERA.COM\"", true },
651 		{ "304-23=1; Domain=\"195.189.143.182\"", false },
652 	};
653 
654 	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
655 	{
656 		NextSubTest();
657 
658 		BNetworkCookie cookie(tests[i].cookieString, setter);
659 		CPPUNIT_ASSERT_EQUAL(tests[i].shouldMatch,
660 			cookie.IsValidForUrl(getter));
661 	}
662 }
663 
664 
665 void
666 CookieTest::MaxAgeParsingTest()
667 {
668 	const BUrl setter("http://testsuites.opera.com/cookies/305.php");
669 
670 	BString bigData("305-12=1; Max-Age=\"");
671 	for (int i = 0; i < 1500; i++)
672 		bigData << "abcdefghijklmnopqrstuvwxyz";
673 	bigData << "\";";
674 
675 	struct Test {
676 		const char* cookieString;
677 		bool expired;
678 	};
679 
680 	const Test tests[] = {
681 		{ "305-01=1; Max-Age=\"notAValidValue\"", true },
682 		{ "305-02=1; Max-Age=\"Wed, 08-Nov-2035 01:04:33\"", true },
683 		{ "305-03=1; Max-Age=\"0\"", true },
684 		{ "305-04=1; Max-Age=\"1\"", false },
685 		{ "305-05=1; Max-Age=\"10000\"", false },
686 		{ "305-06=1; Max-Age=\"-1\"", true },
687 		{ "305-07=1; Max-Age=\"0.5\"", true },
688 		{ "305-08=1; Max-Age=\"9999999999999999999999999\"", false },
689 		{ "305-09=1; Max-Age=\"-9999999999999999999999999\"", true },
690 		{ "305-10=1; Max-Age=\"+10000\"", false },
691 		{ "305-11=1; Max-Age=\"Fri, 13-Dec-1901 20:45:52\"", true },
692 		{ bigData, true },
693 		{ "305-13=1; Max-Age=\"0+10000\"", true },
694 		{ "305-14=1; Max-Age=\"10000+0\"", true },
695 		{ "305-15=1; Max-Age=10000\"", true },
696 		{ "305-16=1; Max-Age=\"10000", true },
697 		{ "305-17=1; Max-Age=10000", false },
698 		{ "305-18=1; Max-Age=100\"00", true },
699 	};
700 
701 	for (unsigned int i = 0; i < sizeof(tests) / sizeof(Test); i++)
702 	{
703 		NextSubTest();
704 
705 		BNetworkCookie cookie(tests[i].cookieString, setter);
706 		CPPUNIT_ASSERT_EQUAL(tests[i].expired, cookie.ShouldDeleteNow());
707 	}
708 }
709 
710 
711 void
712 CookieTest::ExplodeTest()
713 {
714 	struct Test {
715 		const char* cookieString;
716 		const char*	url;
717 		struct
718 		{
719 			bool		valid;
720 			const char* name;
721 			const char* value;
722 			const char* domain;
723 			const char* path;
724 			bool 		secure;
725 			bool 		httponly;
726 			bool		session;
727 			BDateTime	expire;
728 		} expected;
729 	};
730 
731 	Test tests[] = {
732 	//     Cookie string      URL
733 	//     ------------- -------------
734 	//		   Valid     Name     Value	       Domain         Path      Secure  HttpOnly Session  Expiration
735 	//       --------- -------- --------- ----------------- ---------  -------- -------- -------  ----------
736 		// Normal cookies
737 		{ "name=value", "http://www.example.com/path/path",
738 			{  true,    "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
739 		{ "name=value; domain=example.com; path=/; secure", "http://www.example.com/path/path",
740 			{  true,    "name",  "value",   "example.com",   "/"    ,   true,    false,   true,   BDateTime() } },
741 		{ "name=value; httponly; secure", "http://www.example.com/path/path",
742 			{  true,    "name",  "value", "www.example.com", "/path",   true,    true,    true,   BDateTime() } },
743 		{ "name=value; expires=Wed, 20-Feb-2013 20:00:00 UTC", "http://www.example.com/path/path",
744 			{  true,    "name",  "value", "www.example.com", "/path",   false,   false,   false,
745 				BDateTime(BDate(2013, 2, 20), BTime(20, 0, 0, 0)) } },
746 		// Valid cookie with bad form
747 		{ "name=  ;  domain   =example.com  ;path=/;  secure = yup  ; blahblah ;)", "http://www.example.com/path/path",
748 			{  true,    "name",  "",   "example.com",   "/"    ,   true,    false,   true,   BDateTime() } },
749 		// Invalid path
750 		{ "name=value; path=invalid", "http://www.example.com/path/path",
751 			{  false,    "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
752 		// Setting for other subdomain (invalid)
753 		{ "name=value; domain=subdomain.example.com", "http://www.example.com/path/path",
754 			{  false,   "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
755 		// Various invalid cookies
756 		{ "name", "http://www.example.com/path/path",
757 			{  false,   "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } },
758 		{ "; domain=example.com", "http://www.example.com/path/path",
759 			{  false,   "name",  "value", "www.example.com", "/path",   false,   false,   true,   BDateTime() } }
760 	};
761 
762 	BNetworkCookie cookie;
763 
764 	for (uint32 i = 0; i < (sizeof(tests) / sizeof(Test)); i++) {
765 		NextSubTest();
766 
767 		BUrl url(tests[i].url);
768 		cookie.ParseCookieString(tests[i].cookieString, url);
769 
770 		CPPUNIT_ASSERT(tests[i].expected.valid == cookie.IsValid());
771 
772 		if (!tests[i].expected.valid)
773 			continue;
774 
775 		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.name), cookie.Name());
776 		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.value), cookie.Value());
777 		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.domain),
778 			cookie.Domain());
779 		CPPUNIT_ASSERT_EQUAL(BString(tests[i].expected.path), cookie.Path());
780 		CPPUNIT_ASSERT(tests[i].expected.secure == cookie.Secure());
781 		CPPUNIT_ASSERT(tests[i].expected.httponly == cookie.HttpOnly());
782 		CPPUNIT_ASSERT(tests[i].expected.session == cookie.IsSessionCookie());
783 
784 		if (!cookie.IsSessionCookie())
785 			CPPUNIT_ASSERT_EQUAL(tests[i].expected.expire.Time_t(),
786 				cookie.ExpirationDate());
787 	}
788 }
789 
790 
791 /* static */ void
792 CookieTest::AddTests(BTestSuite& parent)
793 {
794 	CppUnit::TestSuite& suite = *new CppUnit::TestSuite("CookieTest");
795 
796 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::SimpleTest",
797 		&CookieTest::SimpleTest));
798 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
799 		"CookieTest::StandardTest", &CookieTest::StandardTest));
800 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::ExpireTest",
801 		&CookieTest::ExpireTest));
802 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::PathTest",
803 		&CookieTest::PathTest));
804 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::MaxSizeTest",
805 		&CookieTest::MaxSizeTest));
806 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
807 		"CookieTest::MaxNumberTest", &CookieTest::MaxNumberTest));
808 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::UpdateTest",
809 		&CookieTest::UpdateTest));
810 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
811 		"CookieTest::HttpOnlyTest", &CookieTest::HttpOnlyTest));
812 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
813 		"CookieTest::EncodingTest", &CookieTest::EncodingTest));
814 	suite.addTest(new CppUnit::TestCaller<CookieTest>("CookieTest::DomainTest",
815 		&CookieTest::DomainTest));
816 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
817 		"CookieTest::PersistantTest", &CookieTest::PersistantTest));
818 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
819 		"CookieTest::OverwriteTest", &CookieTest::OverwriteTest));
820 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
821 		"CookieTest::OrderTest", &CookieTest::OrderTest));
822 
823 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
824 		"CookieTest::ExpireParsingTest", &CookieTest::ExpireParsingTest));
825 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
826 		"CookieTest::PathMatchingTest", &CookieTest::PathMatchingTest));
827 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
828 		"CookieTest::DomainMatchingTest", &CookieTest::DomainMatchingTest));
829 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
830 		"CookieTest::MaxAgeParsingTest", &CookieTest::MaxAgeParsingTest));
831 	suite.addTest(new CppUnit::TestCaller<CookieTest>(
832 		"CookieTest::ExplodeTest", &CookieTest::ExplodeTest));
833 
834 	parent.addTest("CookieTest", &suite);
835 }
836 
837 
838 const BNetworkCookie*
839 CookieTest::_GetCookie(BNetworkCookieJar& jar, const BUrl& url,
840 	const char* name)
841 {
842 	BNetworkCookieJar::UrlIterator it = jar.GetUrlIterator(url);
843 	const BNetworkCookie* result = NULL;
844 
845 	while (it.HasNext()) {
846 		const BNetworkCookie* cookie = it.Next();
847 		if (cookie->Name() == name) {
848 			// Make sure the cookie is found only once.
849 			CPPUNIT_ASSERT(result == NULL);
850 
851 			result = cookie;
852 		}
853 	}
854 
855 	return result;
856 }
857