SetCookieTest.php 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445
  1. <?php
  2. namespace GuzzleHttp\Tests\CookieJar;
  3. use GuzzleHttp\Cookie\SetCookie;
  4. use PHPUnit\Framework\TestCase;
  5. /**
  6. * @covers GuzzleHttp\Cookie\SetCookie
  7. */
  8. class SetCookieTest extends TestCase
  9. {
  10. public function testInitializesDefaultValues()
  11. {
  12. $cookie = new SetCookie();
  13. $this->assertSame('/', $cookie->getPath());
  14. }
  15. public function testConvertsDateTimeMaxAgeToUnixTimestamp()
  16. {
  17. $cookie = new SetCookie(['Expires' => 'November 20, 1984']);
  18. $this->assertInternalType('integer', $cookie->getExpires());
  19. }
  20. public function testAddsExpiresBasedOnMaxAge()
  21. {
  22. $t = time();
  23. $cookie = new SetCookie(['Max-Age' => 100]);
  24. $this->assertEquals($t + 100, $cookie->getExpires());
  25. }
  26. public function testHoldsValues()
  27. {
  28. $t = time();
  29. $data = array(
  30. 'Name' => 'foo',
  31. 'Value' => 'baz',
  32. 'Path' => '/bar',
  33. 'Domain' => 'baz.com',
  34. 'Expires' => $t,
  35. 'Max-Age' => 100,
  36. 'Secure' => true,
  37. 'Discard' => true,
  38. 'HttpOnly' => true,
  39. 'foo' => 'baz',
  40. 'bar' => 'bam'
  41. );
  42. $cookie = new SetCookie($data);
  43. $this->assertEquals($data, $cookie->toArray());
  44. $this->assertSame('foo', $cookie->getName());
  45. $this->assertSame('baz', $cookie->getValue());
  46. $this->assertSame('baz.com', $cookie->getDomain());
  47. $this->assertSame('/bar', $cookie->getPath());
  48. $this->assertSame($t, $cookie->getExpires());
  49. $this->assertSame(100, $cookie->getMaxAge());
  50. $this->assertTrue($cookie->getSecure());
  51. $this->assertTrue($cookie->getDiscard());
  52. $this->assertTrue($cookie->getHttpOnly());
  53. $this->assertSame('baz', $cookie->toArray()['foo']);
  54. $this->assertSame('bam', $cookie->toArray()['bar']);
  55. $cookie->setName('a');
  56. $cookie->setValue('b');
  57. $cookie->setPath('c');
  58. $cookie->setDomain('bar.com');
  59. $cookie->setExpires(10);
  60. $cookie->setMaxAge(200);
  61. $cookie->setSecure(false);
  62. $cookie->setHttpOnly(false);
  63. $cookie->setDiscard(false);
  64. $this->assertSame('a', $cookie->getName());
  65. $this->assertSame('b', $cookie->getValue());
  66. $this->assertSame('c', $cookie->getPath());
  67. $this->assertSame('bar.com', $cookie->getDomain());
  68. $this->assertSame(10, $cookie->getExpires());
  69. $this->assertSame(200, $cookie->getMaxAge());
  70. $this->assertFalse($cookie->getSecure());
  71. $this->assertFalse($cookie->getDiscard());
  72. $this->assertFalse($cookie->getHttpOnly());
  73. }
  74. public function testDeterminesIfExpired()
  75. {
  76. $c = new SetCookie();
  77. $c->setExpires(10);
  78. $this->assertTrue($c->isExpired());
  79. $c->setExpires(time() + 10000);
  80. $this->assertFalse($c->isExpired());
  81. }
  82. public function testMatchesDomain()
  83. {
  84. $cookie = new SetCookie();
  85. $this->assertTrue($cookie->matchesDomain('baz.com'));
  86. $cookie->setDomain('baz.com');
  87. $this->assertTrue($cookie->matchesDomain('baz.com'));
  88. $this->assertFalse($cookie->matchesDomain('bar.com'));
  89. $cookie->setDomain('.baz.com');
  90. $this->assertTrue($cookie->matchesDomain('.baz.com'));
  91. $this->assertTrue($cookie->matchesDomain('foo.baz.com'));
  92. $this->assertFalse($cookie->matchesDomain('baz.bar.com'));
  93. $this->assertTrue($cookie->matchesDomain('baz.com'));
  94. $cookie->setDomain('.127.0.0.1');
  95. $this->assertTrue($cookie->matchesDomain('127.0.0.1'));
  96. $cookie->setDomain('127.0.0.1');
  97. $this->assertTrue($cookie->matchesDomain('127.0.0.1'));
  98. $cookie->setDomain('.com.');
  99. $this->assertFalse($cookie->matchesDomain('baz.com'));
  100. $cookie->setDomain('.local');
  101. $this->assertTrue($cookie->matchesDomain('example.local'));
  102. $cookie->setDomain('example.com/'); // malformed domain
  103. $this->assertFalse($cookie->matchesDomain('example.com'));
  104. }
  105. public function pathMatchProvider()
  106. {
  107. return [
  108. ['/foo', '/foo', true],
  109. ['/foo', '/Foo', false],
  110. ['/foo', '/fo', false],
  111. ['/foo', '/foo/bar', true],
  112. ['/foo', '/foo/bar/baz', true],
  113. ['/foo', '/foo/bar//baz', true],
  114. ['/foo', '/foobar', false],
  115. ['/foo/bar', '/foo', false],
  116. ['/foo/bar', '/foobar', false],
  117. ['/foo/bar', '/foo/bar', true],
  118. ['/foo/bar', '/foo/bar/', true],
  119. ['/foo/bar', '/foo/bar/baz', true],
  120. ['/foo/bar/', '/foo/bar', false],
  121. ['/foo/bar/', '/foo/bar/', true],
  122. ['/foo/bar/', '/foo/bar/baz', true],
  123. ];
  124. }
  125. /**
  126. * @dataProvider pathMatchProvider
  127. */
  128. public function testMatchesPath($cookiePath, $requestPath, $isMatch)
  129. {
  130. $cookie = new SetCookie();
  131. $cookie->setPath($cookiePath);
  132. $this->assertSame($isMatch, $cookie->matchesPath($requestPath));
  133. }
  134. public function cookieValidateProvider()
  135. {
  136. return array(
  137. array('foo', 'baz', 'bar', true),
  138. array('0', '0', '0', true),
  139. array('foo[bar]', 'baz', 'bar', true),
  140. array('', 'baz', 'bar', 'The cookie name must not be empty'),
  141. array('foo', '', 'bar', 'The cookie value must not be empty'),
  142. array('foo', 'baz', '', 'The cookie domain must not be empty'),
  143. array("foo\r", 'baz', '0', 'Cookie name must not contain invalid characters: ASCII Control characters (0-31;127), space, tab and the following characters: ()<>@,;:\"/?={}'),
  144. );
  145. }
  146. /**
  147. * @dataProvider cookieValidateProvider
  148. */
  149. public function testValidatesCookies($name, $value, $domain, $result)
  150. {
  151. $cookie = new SetCookie(array(
  152. 'Name' => $name,
  153. 'Value' => $value,
  154. 'Domain' => $domain
  155. ));
  156. $this->assertSame($result, $cookie->validate());
  157. }
  158. public function testDoesNotMatchIp()
  159. {
  160. $cookie = new SetCookie(['Domain' => '192.168.16.']);
  161. $this->assertFalse($cookie->matchesDomain('192.168.16.121'));
  162. }
  163. public function testConvertsToString()
  164. {
  165. $t = 1382916008;
  166. $cookie = new SetCookie([
  167. 'Name' => 'test',
  168. 'Value' => '123',
  169. 'Domain' => 'foo.com',
  170. 'Expires' => $t,
  171. 'Path' => '/abc',
  172. 'HttpOnly' => true,
  173. 'Secure' => true
  174. ]);
  175. $this->assertSame(
  176. 'test=123; Domain=foo.com; Path=/abc; Expires=Sun, 27 Oct 2013 23:20:08 GMT; Secure; HttpOnly',
  177. (string) $cookie
  178. );
  179. }
  180. /**
  181. * Provides the parsed information from a cookie
  182. *
  183. * @return array
  184. */
  185. public function cookieParserDataProvider()
  186. {
  187. return array(
  188. array(
  189. 'ASIHTTPRequestTestCookie=This+is+the+value; expires=Sat, 26-Jul-2008 17:00:42 GMT; path=/tests; domain=allseeing-i.com; PHPSESSID=6c951590e7a9359bcedde25cda73e43c; path=/;',
  190. array(
  191. 'Domain' => 'allseeing-i.com',
  192. 'Path' => '/',
  193. 'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c',
  194. 'Max-Age' => null,
  195. 'Expires' => 'Sat, 26-Jul-2008 17:00:42 GMT',
  196. 'Secure' => null,
  197. 'Discard' => null,
  198. 'Name' => 'ASIHTTPRequestTestCookie',
  199. 'Value' => 'This+is+the+value',
  200. 'HttpOnly' => false
  201. )
  202. ),
  203. array('', []),
  204. array('foo', []),
  205. array('; foo', []),
  206. array(
  207. 'foo="bar"',
  208. [
  209. 'Name' => 'foo',
  210. 'Value' => '"bar"',
  211. 'Discard' => null,
  212. 'Domain' => null,
  213. 'Expires' => null,
  214. 'Max-Age' => null,
  215. 'Path' => '/',
  216. 'Secure' => null,
  217. 'HttpOnly' => false
  218. ]
  219. ),
  220. // Test setting a blank value for a cookie
  221. array(array(
  222. 'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '),
  223. array(
  224. 'Name' => 'foo',
  225. 'Value' => '',
  226. 'Discard' => null,
  227. 'Domain' => null,
  228. 'Expires' => null,
  229. 'Max-Age' => null,
  230. 'Path' => '/',
  231. 'Secure' => null,
  232. 'HttpOnly' => false
  233. )
  234. ),
  235. // Test setting a value and removing quotes
  236. array(array(
  237. 'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;'),
  238. array(
  239. 'Name' => 'foo',
  240. 'Value' => '1',
  241. 'Discard' => null,
  242. 'Domain' => null,
  243. 'Expires' => null,
  244. 'Max-Age' => null,
  245. 'Path' => '/',
  246. 'Secure' => null,
  247. 'HttpOnly' => false
  248. )
  249. ),
  250. // Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php
  251. array(
  252. 'justacookie=foo; domain=example.com',
  253. array(
  254. 'Name' => 'justacookie',
  255. 'Value' => 'foo',
  256. 'Domain' => 'example.com',
  257. 'Discard' => null,
  258. 'Expires' => null,
  259. 'Max-Age' => null,
  260. 'Path' => '/',
  261. 'Secure' => null,
  262. 'HttpOnly' => false
  263. )
  264. ),
  265. array(
  266. 'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com',
  267. array(
  268. 'Name' => 'expires',
  269. 'Value' => 'tomorrow',
  270. 'Domain' => '.example.com',
  271. 'Path' => '/Space Out/',
  272. 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
  273. 'Discard' => null,
  274. 'Secure' => true,
  275. 'Max-Age' => null,
  276. 'HttpOnly' => false
  277. )
  278. ),
  279. array(
  280. 'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/',
  281. array(
  282. 'Name' => 'domain',
  283. 'Value' => 'unittests',
  284. 'Domain' => 'example.com',
  285. 'Path' => '/some value/',
  286. 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
  287. 'Secure' => false,
  288. 'Discard' => null,
  289. 'Max-Age' => null,
  290. 'HttpOnly' => false
  291. )
  292. ),
  293. array(
  294. 'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT',
  295. array(
  296. 'Name' => 'path',
  297. 'Value' => 'indexAction',
  298. 'Domain' => '.foo.com',
  299. 'Path' => '/',
  300. 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
  301. 'Secure' => false,
  302. 'Discard' => null,
  303. 'Max-Age' => null,
  304. 'HttpOnly' => false
  305. )
  306. ),
  307. array(
  308. 'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400',
  309. array(
  310. 'Name' => 'secure',
  311. 'Value' => 'sha1',
  312. 'Domain' => 'some.really.deep.domain.com',
  313. 'Path' => '/',
  314. 'Secure' => true,
  315. 'Discard' => null,
  316. 'Expires' => time() + 86400,
  317. 'Max-Age' => 86400,
  318. 'HttpOnly' => false,
  319. 'version' => '1'
  320. )
  321. ),
  322. array(
  323. 'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;',
  324. array(
  325. 'Name' => 'PHPSESSID',
  326. 'Value' => '123456789+abcd%2Cef',
  327. 'Domain' => '.localdomain',
  328. 'Path' => '/foo/baz',
  329. 'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
  330. 'Secure' => true,
  331. 'Discard' => true,
  332. 'Max-Age' => null,
  333. 'HttpOnly' => false
  334. )
  335. ),
  336. );
  337. }
  338. /**
  339. * @dataProvider cookieParserDataProvider
  340. */
  341. public function testParseCookie($cookie, $parsed)
  342. {
  343. foreach ((array) $cookie as $v) {
  344. $c = SetCookie::fromString($v);
  345. $p = $c->toArray();
  346. if (isset($p['Expires'])) {
  347. // Remove expires values from the assertion if they are relatively equal
  348. if (abs($p['Expires'] != strtotime($parsed['Expires'])) < 40) {
  349. unset($p['Expires']);
  350. unset($parsed['Expires']);
  351. }
  352. }
  353. if (!empty($parsed)) {
  354. foreach ($parsed as $key => $value) {
  355. $this->assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true));
  356. }
  357. foreach ($p as $key => $value) {
  358. $this->assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true));
  359. }
  360. } else {
  361. $this->assertSame([
  362. 'Name' => null,
  363. 'Value' => null,
  364. 'Domain' => null,
  365. 'Path' => '/',
  366. 'Max-Age' => null,
  367. 'Expires' => null,
  368. 'Secure' => false,
  369. 'Discard' => false,
  370. 'HttpOnly' => false,
  371. ], $p);
  372. }
  373. }
  374. }
  375. /**
  376. * Provides the data for testing isExpired
  377. *
  378. * @return array
  379. */
  380. public function isExpiredProvider()
  381. {
  382. return array(
  383. array(
  384. 'FOO=bar; expires=Thu, 01 Jan 1970 00:00:00 GMT;',
  385. true,
  386. ),
  387. array(
  388. 'FOO=bar; expires=Thu, 01 Jan 1970 00:00:01 GMT;',
  389. true,
  390. ),
  391. array(
  392. 'FOO=bar; expires='.date(\DateTime::RFC1123, time()+10).';',
  393. false,
  394. ),
  395. array(
  396. 'FOO=bar; expires='.date(\DateTime::RFC1123, time()-10).';',
  397. true,
  398. ),
  399. array(
  400. 'FOO=bar;',
  401. false,
  402. ),
  403. );
  404. }
  405. /**
  406. * @dataProvider isExpiredProvider
  407. */
  408. public function testIsExpired($cookie, $expired)
  409. {
  410. $this->assertSame(
  411. $expired,
  412. SetCookie::fromString($cookie)->isExpired()
  413. );
  414. }
  415. }