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