ResponseTest.php 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 2.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Http;
  17. include_once CORE_TEST_CASES . DS . 'Http' . DS . 'server_mocks.php';
  18. use Cake\Core\Configure;
  19. use Cake\Http\Cookie\Cookie;
  20. use Cake\Http\Cookie\CookieCollection;
  21. use Cake\Http\CorsBuilder;
  22. use Cake\Http\Exception\NotFoundException;
  23. use Cake\Http\Response;
  24. use Cake\Http\ServerRequest;
  25. use Cake\I18n\FrozenTime;
  26. use Cake\TestSuite\TestCase;
  27. use Zend\Diactoros\Stream;
  28. /**
  29. * ResponseTest
  30. */
  31. class ResponseTest extends TestCase
  32. {
  33. /**
  34. * SERVER variable backup.
  35. *
  36. * @var array
  37. */
  38. protected $server = [];
  39. /**
  40. * setup
  41. *
  42. * @return void
  43. */
  44. public function setUp()
  45. {
  46. parent::setUp();
  47. $this->server = $_SERVER;
  48. }
  49. /**
  50. * teardown
  51. *
  52. * @return void
  53. */
  54. public function tearDown()
  55. {
  56. parent::tearDown();
  57. $_SERVER = $this->server;
  58. unset($GLOBALS['mockedHeadersSent']);
  59. }
  60. /**
  61. * Tests the request object constructor
  62. *
  63. * @return void
  64. */
  65. public function testConstruct()
  66. {
  67. $response = new Response();
  68. $this->assertSame('', (string)$response->getBody());
  69. $this->assertEquals('UTF-8', $response->getCharset());
  70. $this->assertEquals('text/html', $response->getType());
  71. $this->assertEquals('text/html; charset=UTF-8', $response->getHeaderLine('Content-Type'));
  72. $this->assertEquals(200, $response->getStatusCode());
  73. $options = [
  74. 'body' => 'This is the body',
  75. 'charset' => 'my-custom-charset',
  76. 'type' => 'mp3',
  77. 'status' => 203,
  78. ];
  79. $response = new Response($options);
  80. $this->assertEquals('This is the body', (string)$response->getBody());
  81. $this->assertEquals('my-custom-charset', $response->getCharset());
  82. $this->assertEquals('audio/mpeg', $response->getType());
  83. $this->assertEquals('audio/mpeg', $response->getHeaderLine('Content-Type'));
  84. $this->assertEquals(203, $response->getStatusCode());
  85. }
  86. /**
  87. * Tests the getCharset/withCharset methods
  88. *
  89. * @return void
  90. */
  91. public function testWithCharset()
  92. {
  93. $response = new Response();
  94. $this->assertEquals('text/html; charset=UTF-8', $response->getHeaderLine('Content-Type'));
  95. $new = $response->withCharset('iso-8859-1');
  96. $this->assertNotContains('iso', $response->getHeaderLine('Content-Type'), 'Old instance not changed');
  97. $this->assertSame('iso-8859-1', $new->getCharset());
  98. $this->assertEquals('text/html; charset=iso-8859-1', $new->getHeaderLine('Content-Type'));
  99. }
  100. /**
  101. * Tests the getType method
  102. *
  103. * @return void
  104. */
  105. public function testGetType()
  106. {
  107. $response = new Response();
  108. $this->assertEquals('text/html', $response->getType());
  109. $this->assertEquals(
  110. 'application/pdf',
  111. $response->withType('pdf')->getType()
  112. );
  113. $this->assertEquals(
  114. 'custom/stuff',
  115. $response->withType('custom/stuff')->getType()
  116. );
  117. $this->assertEquals(
  118. 'application/json',
  119. $response->withType('json')->getType()
  120. );
  121. }
  122. /**
  123. * Tests the withType method
  124. *
  125. * @return void
  126. */
  127. public function testWithTypeAlias()
  128. {
  129. $response = new Response();
  130. $this->assertEquals(
  131. 'text/html; charset=UTF-8',
  132. $response->getHeaderLine('Content-Type'),
  133. 'Default content-type should match'
  134. );
  135. $new = $response->withType('pdf');
  136. $this->assertNotSame($new, $response, 'Should be a new instance');
  137. $this->assertSame(
  138. 'text/html; charset=UTF-8',
  139. $response->getHeaderLine('Content-Type'),
  140. 'Original object should not be modified'
  141. );
  142. $this->assertSame('application/pdf', $new->getHeaderLine('Content-Type'));
  143. $this->assertSame(
  144. 'application/json; charset=UTF-8',
  145. $new->withType('json')->getHeaderLine('Content-Type')
  146. );
  147. }
  148. /**
  149. * test withType() and full mime-types
  150. *
  151. * @return void
  152. */
  153. public function withTypeFull()
  154. {
  155. $response = new Response();
  156. $this->assertEquals(
  157. 'application/json',
  158. $response->withType('application/json')->getHeaderLine('Content-Type'),
  159. 'Should not add charset to explicit type'
  160. );
  161. $this->assertEquals(
  162. 'custom/stuff',
  163. $response->withType('custom/stuff')->getHeaderLine('Content-Type'),
  164. 'Should allow arbitrary types'
  165. );
  166. }
  167. /**
  168. * Test that an invalid type raises an exception
  169. *
  170. * @return void
  171. */
  172. public function testWithTypeInvalidType()
  173. {
  174. $this->expectException(\InvalidArgumentException::class);
  175. $this->expectExceptionMessage('"beans" is an invalid content type');
  176. $response = new Response();
  177. $response->withType('beans');
  178. }
  179. /**
  180. * Data provider for content type tests.
  181. *
  182. * @return array
  183. */
  184. public static function charsetTypeProvider()
  185. {
  186. return [
  187. ['mp3', 'audio/mpeg'],
  188. ['js', 'application/javascript; charset=UTF-8'],
  189. ['json', 'application/json; charset=UTF-8'],
  190. ['xml', 'application/xml; charset=UTF-8'],
  191. ['txt', 'text/plain; charset=UTF-8'],
  192. ];
  193. }
  194. /**
  195. * Test that setting certain status codes clears the status code.
  196. *
  197. * @return void
  198. */
  199. public function testWithStatusClearsContentType()
  200. {
  201. $response = new Response();
  202. $new = $response->withType('pdf')
  203. ->withStatus(204);
  204. $this->assertFalse($new->hasHeader('Content-Type'));
  205. $this->assertSame(204, $new->getStatusCode());
  206. $response = new Response();
  207. $new = $response->withStatus(304)
  208. ->withType('pdf');
  209. $this->assertFalse($new->hasHeader('Content-Type'));
  210. }
  211. /**
  212. * Tests the withDisabledCache method
  213. *
  214. * @return void
  215. */
  216. public function testWithDisabledCache()
  217. {
  218. $response = new Response();
  219. $expected = [
  220. 'Expires' => ['Mon, 26 Jul 1997 05:00:00 GMT'],
  221. 'Last-Modified' => [gmdate('D, d M Y H:i:s') . ' GMT'],
  222. 'Cache-Control' => ['no-store, no-cache, must-revalidate, post-check=0, pre-check=0'],
  223. 'Content-Type' => ['text/html; charset=UTF-8'],
  224. ];
  225. $new = $response->withDisabledCache();
  226. $this->assertFalse($response->hasHeader('Expires'), 'Old instance not mutated.');
  227. $this->assertEquals($expected, $new->getHeaders());
  228. }
  229. /**
  230. * Tests the withCache method
  231. *
  232. * @return void
  233. */
  234. public function testWithCache()
  235. {
  236. $response = new Response();
  237. $since = $time = time();
  238. $new = $response->withCache($since, $time);
  239. $this->assertFalse($response->hasHeader('Date'));
  240. $this->assertFalse($response->hasHeader('Last-Modified'));
  241. $this->assertEquals(gmdate('D, j M Y G:i:s ', $since) . 'GMT', $new->getHeaderLine('Date'));
  242. $this->assertEquals(gmdate('D, j M Y H:i:s ', $since) . 'GMT', $new->getHeaderLine('Last-Modified'));
  243. $this->assertEquals(gmdate('D, j M Y H:i:s', $time) . ' GMT', $new->getHeaderLine('Expires'));
  244. $this->assertEquals('public, max-age=0', $new->getHeaderLine('Cache-Control'));
  245. }
  246. /**
  247. * Tests the compress method
  248. *
  249. * @return void
  250. */
  251. public function testCompress()
  252. {
  253. $this->skipIf(defined('HHVM_VERSION'), 'HHVM does not implement ob_gzhandler');
  254. $response = new Response();
  255. if (ini_get('zlib.output_compression') === '1' || !extension_loaded('zlib')) {
  256. $this->assertFalse($response->compress());
  257. $this->markTestSkipped('Is not possible to test output compression');
  258. }
  259. $_SERVER['HTTP_ACCEPT_ENCODING'] = '';
  260. $result = $response->compress();
  261. $this->assertFalse($result);
  262. $_SERVER['HTTP_ACCEPT_ENCODING'] = 'gzip';
  263. $result = $response->compress();
  264. $this->assertTrue($result);
  265. $this->assertContains('ob_gzhandler', ob_list_handlers());
  266. ob_get_clean();
  267. }
  268. /**
  269. * Tests the withDownload method
  270. *
  271. * @return void
  272. */
  273. public function testWithDownload()
  274. {
  275. $response = new Response();
  276. $new = $response->withDownload('myfile.mp3');
  277. $this->assertFalse($response->hasHeader('Content-Disposition'), 'No mutation');
  278. $expected = 'attachment; filename="myfile.mp3"';
  279. $this->assertEquals($expected, $new->getHeaderLine('Content-Disposition'));
  280. }
  281. /**
  282. * Tests the mapType method
  283. *
  284. * @return void
  285. */
  286. public function testMapType()
  287. {
  288. $response = new Response();
  289. $this->assertEquals('wav', $response->mapType('audio/x-wav'));
  290. $this->assertEquals('pdf', $response->mapType('application/pdf'));
  291. $this->assertEquals('xml', $response->mapType('text/xml'));
  292. $this->assertEquals('html', $response->mapType('*/*'));
  293. $this->assertEquals('csv', $response->mapType('application/vnd.ms-excel'));
  294. $expected = ['json', 'xhtml', 'css'];
  295. $result = $response->mapType(['application/json', 'application/xhtml+xml', 'text/css']);
  296. $this->assertEquals($expected, $result);
  297. }
  298. /**
  299. * Tests the outputCompressed method
  300. *
  301. * @return void
  302. */
  303. public function testOutputCompressed()
  304. {
  305. $response = new Response();
  306. $_SERVER['HTTP_ACCEPT_ENCODING'] = 'gzip';
  307. $result = $response->outputCompressed();
  308. $this->assertFalse($result);
  309. $_SERVER['HTTP_ACCEPT_ENCODING'] = '';
  310. $result = $response->outputCompressed();
  311. $this->assertFalse($result);
  312. if (!extension_loaded('zlib')) {
  313. $this->markTestSkipped('Skipping further tests for outputCompressed as zlib extension is not loaded');
  314. }
  315. $this->skipIf(defined('HHVM_VERSION'), 'HHVM does not implement ob_gzhandler');
  316. if (ini_get('zlib.output_compression') !== '1') {
  317. ob_start('ob_gzhandler');
  318. }
  319. $_SERVER['HTTP_ACCEPT_ENCODING'] = 'gzip';
  320. $result = $response->outputCompressed();
  321. $this->assertTrue($result);
  322. $_SERVER['HTTP_ACCEPT_ENCODING'] = '';
  323. $result = $response->outputCompressed();
  324. $this->assertFalse($result);
  325. if (ini_get('zlib.output_compression') !== '1') {
  326. ob_get_clean();
  327. }
  328. }
  329. /**
  330. * Tests settings the content length
  331. *
  332. * @return void
  333. */
  334. public function testWithLength()
  335. {
  336. $response = new Response();
  337. $this->assertFalse($response->hasHeader('Content-Length'));
  338. $new = $response->withLength(100);
  339. $this->assertFalse($response->hasHeader('Content-Length'), 'Old instance not modified');
  340. $this->assertSame('100', $new->getHeaderLine('Content-Length'));
  341. }
  342. /**
  343. * Tests settings the link
  344. *
  345. * @return void
  346. */
  347. public function testWithAddedLink()
  348. {
  349. $response = new Response();
  350. $this->assertFalse($response->hasHeader('Link'));
  351. $new = $response->withAddedLink('http://example.com', ['rel' => 'prev']);
  352. $this->assertFalse($response->hasHeader('Link'), 'Old instance not modified');
  353. $this->assertEquals('<http://example.com>; rel="prev"', $new->getHeaderLine('Link'));
  354. $new = $response->withAddedLink('http://example.com');
  355. $this->assertEquals('<http://example.com>', $new->getHeaderLine('Link'));
  356. $new = $response->withAddedLink('http://example.com?p=1', ['rel' => 'prev'])
  357. ->withAddedLink('http://example.com?p=2', ['rel' => 'next', 'foo' => 'bar']);
  358. $this->assertEquals('<http://example.com?p=1>; rel="prev",<http://example.com?p=2>; rel="next"; foo="bar"', $new->getHeaderLine('Link'));
  359. }
  360. /**
  361. * Tests the withExpires method
  362. *
  363. * @return void
  364. */
  365. public function testWithExpires()
  366. {
  367. $format = 'D, j M Y H:i:s';
  368. $response = new Response();
  369. $now = new \DateTime('now', new \DateTimeZone('America/Los_Angeles'));
  370. $new = $response->withExpires($now);
  371. $this->assertFalse($response->hasHeader('Expires'));
  372. $now->setTimeZone(new \DateTimeZone('UTC'));
  373. $this->assertEquals($now->format($format) . ' GMT', $new->getHeaderLine('Expires'));
  374. $now = time();
  375. $new = $response->withExpires($now);
  376. $this->assertEquals(gmdate($format) . ' GMT', $new->getHeaderLine('Expires'));
  377. $time = new \DateTime('+1 day', new \DateTimeZone('UTC'));
  378. $new = $response->withExpires('+1 day');
  379. $this->assertEquals($time->format($format) . ' GMT', $new->getHeaderLine('Expires'));
  380. }
  381. /**
  382. * Tests the withModified method
  383. *
  384. * @return void
  385. */
  386. public function testWithModified()
  387. {
  388. $format = 'D, j M Y H:i:s';
  389. $response = new Response();
  390. $now = new \DateTime('now', new \DateTimeZone('America/Los_Angeles'));
  391. $new = $response->withModified($now);
  392. $this->assertFalse($response->hasHeader('Last-Modified'));
  393. $now->setTimeZone(new \DateTimeZone('UTC'));
  394. $this->assertEquals($now->format($format) . ' GMT', $new->getHeaderLine('Last-Modified'));
  395. $now = time();
  396. $new = $response->withModified($now);
  397. $this->assertEquals(gmdate($format) . ' GMT', $new->getHeaderLine('Last-Modified'));
  398. $time = new \DateTime('+1 day', new \DateTimeZone('UTC'));
  399. $new = $response->withModified('+1 day');
  400. $this->assertEquals($time->format($format) . ' GMT', $new->getHeaderLine('Last-Modified'));
  401. }
  402. /**
  403. * Tests withSharable()
  404. *
  405. * @return void
  406. */
  407. public function testWithSharable()
  408. {
  409. $response = new Response();
  410. $new = $response->withSharable(true);
  411. $this->assertFalse($response->hasHeader('Cache-Control'), 'old instance unchanged');
  412. $this->assertEquals('public', $new->getHeaderLine('Cache-Control'));
  413. $new = $response->withSharable(false);
  414. $this->assertEquals('private', $new->getHeaderLine('Cache-Control'));
  415. $new = $response->withSharable(true, 3600);
  416. $this->assertEquals('public, max-age=3600', $new->getHeaderLine('Cache-Control'));
  417. $new = $response->withSharable(false, 3600);
  418. $this->assertEquals('private, max-age=3600', $new->getHeaderLine('Cache-Control'));
  419. }
  420. /**
  421. * Tests withMaxAge()
  422. *
  423. * @return void
  424. */
  425. public function testWithMaxAge()
  426. {
  427. $response = new Response();
  428. $this->assertFalse($response->hasHeader('Cache-Control'));
  429. $new = $response->withMaxAge(3600);
  430. $this->assertEquals('max-age=3600', $new->getHeaderLine('Cache-Control'));
  431. $new = $response->withMaxAge(3600)
  432. ->withSharable(false);
  433. $this->assertEquals('max-age=3600, private', $new->getHeaderLine('Cache-Control'));
  434. }
  435. /**
  436. * Tests setting of s-maxage Cache-Control directive
  437. *
  438. * @return void
  439. */
  440. public function testWithSharedMaxAge()
  441. {
  442. $response = new Response();
  443. $new = $response->withSharedMaxAge(3600);
  444. $this->assertFalse($response->hasHeader('Cache-Control'));
  445. $this->assertEquals('s-maxage=3600', $new->getHeaderLine('Cache-Control'));
  446. $new = $response->withSharedMaxAge(3600)->withSharable(true);
  447. $this->assertEquals('s-maxage=3600, public', $new->getHeaderLine('Cache-Control'));
  448. }
  449. /**
  450. * Tests setting of must-revalidate Cache-Control directive
  451. *
  452. * @return void
  453. */
  454. public function testWithMustRevalidate()
  455. {
  456. $response = new Response();
  457. $this->assertFalse($response->hasHeader('Cache-Control'));
  458. $new = $response->withMustRevalidate(true);
  459. $this->assertFalse($response->hasHeader('Cache-Control'));
  460. $this->assertEquals('must-revalidate', $new->getHeaderLine('Cache-Control'));
  461. $new = $new->withMustRevalidate(false);
  462. $this->assertEmpty($new->getHeaderLine('Cache-Control'));
  463. }
  464. /**
  465. * Tests withVary()
  466. *
  467. * @return void
  468. */
  469. public function testWithVary()
  470. {
  471. $response = new Response();
  472. $new = $response->withVary('Accept-encoding');
  473. $this->assertFalse($response->hasHeader('Vary'));
  474. $this->assertEquals('Accept-encoding', $new->getHeaderLine('Vary'));
  475. $new = $response->withVary(['Accept-encoding', 'Accept-language']);
  476. $this->assertFalse($response->hasHeader('Vary'));
  477. $this->assertEquals('Accept-encoding,Accept-language', $new->getHeaderLine('Vary'));
  478. }
  479. /**
  480. * Tests withEtag()
  481. *
  482. * @return void
  483. */
  484. public function testWithEtag()
  485. {
  486. $response = new Response();
  487. $new = $response->withEtag('something');
  488. $this->assertFalse($response->hasHeader('Etag'));
  489. $this->assertEquals('"something"', $new->getHeaderLine('Etag'));
  490. $new = $response->withEtag('something', true);
  491. $this->assertEquals('W/"something"', $new->getHeaderLine('Etag'));
  492. }
  493. /**
  494. * Tests that the response is able to be marked as not modified
  495. *
  496. * @return void
  497. */
  498. public function testNotModified()
  499. {
  500. $response = new Response();
  501. $response = $response->withStringBody('something')
  502. ->withStatus(200)
  503. ->withLength(100)
  504. ->withModified('now');
  505. $response->notModified();
  506. $this->assertFalse($response->hasHeader('Content-Length'));
  507. $this->assertFalse($response->hasHeader('Modified'));
  508. $this->assertEmpty((string)$response->getBody());
  509. $this->assertEquals(304, $response->getStatusCode());
  510. }
  511. /**
  512. * Tests withNotModified()
  513. *
  514. * @return void
  515. */
  516. public function testWithNotModified()
  517. {
  518. $response = new Response(['body' => 'something']);
  519. $response = $response->withLength(100)
  520. ->withStatus(200)
  521. ->withHeader('Last-Modified', 'value')
  522. ->withHeader('Content-Language', 'en-EN')
  523. ->withHeader('X-things', 'things')
  524. ->withType('application/json');
  525. $new = $response->withNotModified();
  526. $this->assertTrue($response->hasHeader('Content-Language'), 'old instance not changed');
  527. $this->assertTrue($response->hasHeader('Content-Length'), 'old instance not changed');
  528. $this->assertFalse($new->hasHeader('Content-Type'));
  529. $this->assertFalse($new->hasHeader('Content-Length'));
  530. $this->assertFalse($new->hasHeader('Content-Language'));
  531. $this->assertFalse($new->hasHeader('Last-Modified'));
  532. $this->assertSame('things', $new->getHeaderLine('X-things'), 'Other headers are retained');
  533. $this->assertSame(304, $new->getStatusCode());
  534. $this->assertSame('', $new->getBody()->getContents());
  535. }
  536. /**
  537. * Test checkNotModified method
  538. *
  539. * @return void
  540. */
  541. public function testCheckNotModifiedByEtagStar()
  542. {
  543. $request = new ServerRequest();
  544. $request = $request->withHeader('If-None-Match', '*');
  545. $response = new Response();
  546. $response = $response->withEtag('something')
  547. ->withHeader('Content-Length', 99);
  548. $this->assertTrue($response->checkNotModified($request));
  549. $this->assertFalse($response->hasHeader('Content-Type'), 'etags match, should be unmodified');
  550. }
  551. /**
  552. * Test checkNotModified method
  553. *
  554. * @return void
  555. */
  556. public function testCheckNotModifiedByEtagExact()
  557. {
  558. $request = new ServerRequest();
  559. $request = $request->withHeader('If-None-Match', 'W/"something", "other"');
  560. $response = new Response();
  561. $response = $response->withEtag('something', true)
  562. ->withHeader('Content-Length', 99);
  563. $this->assertTrue($response->checkNotModified($request));
  564. $this->assertFalse($response->hasHeader('Content-Type'), 'etags match, should be unmodified');
  565. }
  566. /**
  567. * Test checkNotModified method
  568. *
  569. * @return void
  570. */
  571. public function testCheckNotModifiedByEtagAndTime()
  572. {
  573. $request = new ServerRequest();
  574. $request = $request->withHeader('If-Modified-Since', '2012-01-01 00:00:00')
  575. ->withHeader('If-None-Match', 'W/"something", "other"');
  576. $response = new Response();
  577. $response = $response->withModified('2012-01-01 00:00:00')
  578. ->withEtag('something', true)
  579. ->withHeader('Content-Length', 99);
  580. $this->assertTrue($response->checkNotModified($request));
  581. $this->assertFalse($response->hasHeader('Content-Length'), 'etags match, should be unmodified');
  582. }
  583. /**
  584. * Test checkNotModified method
  585. *
  586. * @return void
  587. */
  588. public function testCheckNotModifiedByEtagAndTimeMismatch()
  589. {
  590. $request = new ServerRequest();
  591. $request = $request->withHeader('If-Modified-Since', '2012-01-01 00:00:00')
  592. ->withHeader('If-None-Match', 'W/"something", "other"');
  593. $response = new Response();
  594. $response = $response->withModified('2012-01-01 00:00:01')
  595. ->withEtag('something', true)
  596. ->withHeader('Content-Length', 99);
  597. $this->assertFalse($response->checkNotModified($request));
  598. $this->assertTrue($response->hasHeader('Content-Length'), 'timestamp in response is newer');
  599. }
  600. /**
  601. * Test checkNotModified method
  602. *
  603. * @return void
  604. */
  605. public function testCheckNotModifiedByEtagMismatch()
  606. {
  607. $request = new ServerRequest();
  608. $request = $request->withHeader('If-Modified-Since', '2012-01-01 00:00:00')
  609. ->withHeader('If-None-Match', 'W/"something-else", "other"');
  610. $response = new Response();
  611. $response = $response->withModified('2012-01-01 00:00:00')
  612. ->withEtag('something', true)
  613. ->withHeader('Content-Length', 99);
  614. $this->assertFalse($response->checkNotModified($request));
  615. $this->assertTrue($response->hasHeader('Content-Length'), 'etags do not match');
  616. }
  617. /**
  618. * Test checkNotModified method
  619. *
  620. * @return void
  621. */
  622. public function testCheckNotModifiedByTime()
  623. {
  624. $request = new ServerRequest();
  625. $request = $request->withHeader('If-Modified-Since', '2012-01-01 00:00:00');
  626. $response = new Response();
  627. $response = $response->withModified('2012-01-01 00:00:00')
  628. ->withHeader('Content-Length', 99);
  629. $this->assertTrue($response->checkNotModified($request));
  630. $this->assertFalse($response->hasHeader('Content-Length'), 'modified time matches');
  631. }
  632. /**
  633. * Test checkNotModified method
  634. *
  635. * @return void
  636. */
  637. public function testCheckNotModifiedNoHints()
  638. {
  639. $request = new ServerRequest();
  640. $request = $request->withHeader('If-None-Match', 'W/"something", "other"')
  641. ->withHeader('If-Modified-Since', '2012-01-01 00:00:00');
  642. $response = $this->getMockBuilder('Cake\Http\Response')
  643. ->setMethods(['notModified'])
  644. ->getMock();
  645. $response->expects($this->never())->method('notModified');
  646. $this->assertFalse($response->checkNotModified($request));
  647. }
  648. /**
  649. * Test setting cookies with no value
  650. *
  651. * @return void
  652. */
  653. public function testWithCookieEmpty()
  654. {
  655. $response = new Response();
  656. $new = $response->withCookie(new Cookie('testing'));
  657. $this->assertNull($response->getCookie('testing'), 'withCookie does not mutate');
  658. $expected = [
  659. 'name' => 'testing',
  660. 'value' => '',
  661. 'expire' => 0,
  662. 'path' => '/',
  663. 'domain' => '',
  664. 'secure' => false,
  665. 'httpOnly' => false];
  666. $result = $new->getCookie('testing');
  667. $this->assertEquals($expected, $result);
  668. }
  669. /**
  670. * Test setting cookies with scalar values
  671. *
  672. * @return void
  673. */
  674. public function testWithCookieScalar()
  675. {
  676. $response = new Response();
  677. $new = $response->withCookie(new Cookie('testing', 'abc123'));
  678. $this->assertNull($response->getCookie('testing'), 'withCookie does not mutate');
  679. $this->assertEquals('abc123', $new->getCookie('testing')['value']);
  680. $new = $response->withCookie(new Cookie('testing', 99));
  681. $this->assertEquals(99, $new->getCookie('testing')['value']);
  682. $new = $response->withCookie(new Cookie('testing', false));
  683. $this->assertFalse($new->getCookie('testing')['value']);
  684. $new = $response->withCookie(new Cookie('testing', true));
  685. $this->assertTrue($new->getCookie('testing')['value']);
  686. }
  687. /**
  688. * Test withCookie() and duplicate data
  689. *
  690. * @return void
  691. * @throws \Exception
  692. */
  693. public function testWithDuplicateCookie()
  694. {
  695. $expiry = new \DateTimeImmutable('+24 hours');
  696. $response = new Response();
  697. $cookie = new Cookie(
  698. 'testing',
  699. '[a,b,c]',
  700. $expiry,
  701. '/test',
  702. '',
  703. true
  704. );
  705. $new = $response->withCookie($cookie);
  706. $this->assertNull($response->getCookie('testing'), 'withCookie does not mutate');
  707. $expected = [
  708. 'name' => 'testing',
  709. 'value' => '[a,b,c]',
  710. 'expire' => $expiry,
  711. 'path' => '/test',
  712. 'domain' => '',
  713. 'secure' => true,
  714. 'httpOnly' => false,
  715. ];
  716. // Match the date time formatting to Response::convertCookieToArray
  717. $expected['expire'] = $expiry->format('U');
  718. $this->assertEquals($expected, $new->getCookie('testing'));
  719. }
  720. /**
  721. * Test withCookie() and a cookie instance
  722. *
  723. * @return void
  724. */
  725. public function testWithCookieObject()
  726. {
  727. $response = new Response();
  728. $cookie = new Cookie('yay', 'a value');
  729. $new = $response->withCookie($cookie);
  730. $this->assertNull($response->getCookie('yay'), 'withCookie does not mutate');
  731. $this->assertNotEmpty($new->getCookie('yay'));
  732. $this->assertSame($cookie, $new->getCookieCollection()->get('yay'));
  733. }
  734. public function testWithExpiredCookieScalar()
  735. {
  736. $response = new Response();
  737. $response = $response->withCookie(new Cookie('testing', 'abc123'));
  738. $this->assertEquals('abc123', $response->getCookie('testing')['value']);
  739. $new = $response->withExpiredCookie(new Cookie('testing'));
  740. $this->assertNull($response->getCookie('testing')['expire']);
  741. $this->assertLessThan(FrozenTime::createFromTimestamp(1), $new->getCookie('testing')['expire']);
  742. }
  743. /**
  744. * @throws \Exception If DateImmutable emits an error.
  745. */
  746. public function testWithExpiredCookieOptions()
  747. {
  748. $options = [
  749. 'name' => 'testing',
  750. 'value' => 'abc123',
  751. 'domain' => 'cakephp.org',
  752. 'path' => '/custompath/',
  753. 'secure' => true,
  754. 'httpOnly' => true,
  755. 'expire' => new \DateTimeImmutable('+14 days'),
  756. ];
  757. $cookie = new Cookie(
  758. $options['name'],
  759. $options['value'],
  760. $options['expire'],
  761. $options['path'],
  762. $options['domain'],
  763. $options['secure'],
  764. $options['httpOnly']
  765. );
  766. $response = new Response();
  767. $response = $response->withCookie($cookie);
  768. // Change the timestamp format to match the Response::convertCookieToArray
  769. $options['expire'] = $options['expire']->format('U');
  770. $this->assertEquals($options, $response->getCookie('testing'));
  771. $expiredCookie = $response->withExpiredCookie($cookie);
  772. $this->assertEquals($options['expire'], $response->getCookie('testing')['expire']);
  773. $this->assertLessThan(FrozenTime::createFromTimestamp(1), $expiredCookie->getCookie('testing')['expire']);
  774. }
  775. public function testWithExpiredCookieObject()
  776. {
  777. $response = new Response();
  778. $cookie = new Cookie('yay', 'a value');
  779. $response = $response->withCookie($cookie);
  780. $this->assertEquals('a value', $response->getCookie('yay')['value']);
  781. $new = $response->withExpiredCookie($cookie);
  782. $this->assertNull($response->getCookie('yay')['expire']);
  783. $this->assertEquals('1', $new->getCookie('yay')['expire']);
  784. }
  785. /**
  786. * Test getCookies() and array data.
  787. *
  788. * @return void
  789. */
  790. public function testGetCookies()
  791. {
  792. $response = new Response();
  793. $new = $response->withCookie(new Cookie('testing', 'a'))
  794. ->withCookie(new Cookie('test2', 'b', null, '/test', '', true));
  795. $expected = [
  796. 'testing' => [
  797. 'name' => 'testing',
  798. 'value' => 'a',
  799. 'expire' => null,
  800. 'path' => '/',
  801. 'domain' => '',
  802. 'secure' => false,
  803. 'httpOnly' => false,
  804. ],
  805. 'test2' => [
  806. 'name' => 'test2',
  807. 'value' => 'b',
  808. 'expire' => null,
  809. 'path' => '/test',
  810. 'domain' => '',
  811. 'secure' => true,
  812. 'httpOnly' => false,
  813. ],
  814. ];
  815. $this->assertEquals($expected, $new->getCookies());
  816. }
  817. /**
  818. * Test getCookies() and array data.
  819. *
  820. * @return void
  821. */
  822. public function testGetCookiesArrayValue()
  823. {
  824. $response = new Response();
  825. $cookie = (new Cookie('urmc'))
  826. ->withValue(['user_id' => 1, 'token' => 'abc123'])
  827. ->withHttpOnly(true);
  828. $new = $response->withCookie($cookie);
  829. $expected = [
  830. 'urmc' => [
  831. 'name' => 'urmc',
  832. 'value' => '{"user_id":1,"token":"abc123"}',
  833. 'expire' => null,
  834. 'path' => '/',
  835. 'domain' => '',
  836. 'secure' => false,
  837. 'httpOnly' => true,
  838. ],
  839. ];
  840. $this->assertEquals($expected, $new->getCookies());
  841. }
  842. /**
  843. * Test getCookieCollection() as array data
  844. *
  845. * @return void
  846. */
  847. public function testGetCookieCollection()
  848. {
  849. $response = new Response();
  850. $new = $response->withCookie(new Cookie('testing', 'a'))
  851. ->withCookie(new Cookie('test2', 'b', null, '/test', '', true));
  852. $cookies = $response->getCookieCollection();
  853. $this->assertInstanceOf(CookieCollection::class, $cookies);
  854. $this->assertCount(0, $cookies, 'Original response not mutated');
  855. $cookies = $new->getCookieCollection();
  856. $this->assertInstanceOf(CookieCollection::class, $cookies);
  857. $this->assertCount(2, $cookies);
  858. $this->assertTrue($cookies->has('testing'));
  859. $this->assertTrue($cookies->has('test2'));
  860. }
  861. /**
  862. * Test that cors() returns a builder.
  863. *
  864. * @return void
  865. */
  866. public function testCors()
  867. {
  868. $request = new ServerRequest([
  869. 'environment' => ['HTTP_ORIGIN' => 'http://example.com'],
  870. ]);
  871. $response = new Response();
  872. $builder = $response->cors($request);
  873. $this->assertInstanceOf(CorsBuilder::class, $builder);
  874. $this->assertSame($response, $builder->build(), 'Empty builder returns same object');
  875. }
  876. /**
  877. * test withFile() not found
  878. *
  879. * @return void
  880. */
  881. public function testWithFileNotFound()
  882. {
  883. $this->expectException(\Cake\Http\Exception\NotFoundException::class);
  884. $this->expectExceptionMessage('The requested file /some/missing/folder/file.jpg was not found');
  885. $response = new Response();
  886. $response->withFile('/some/missing/folder/file.jpg');
  887. }
  888. /**
  889. * test withFile() not found
  890. *
  891. * @return void
  892. */
  893. public function testWithFileNotFoundNoDebug()
  894. {
  895. Configure::write('debug', 0);
  896. $this->expectException(\Cake\Http\Exception\NotFoundException::class);
  897. $this->expectExceptionMessage('The requested file was not found');
  898. $response = new Response();
  899. $response->withFile('/some/missing/folder/file.jpg');
  900. }
  901. /**
  902. * Provider for various kinds of unacceptable files.
  903. *
  904. * @return array
  905. */
  906. public function invalidFileProvider()
  907. {
  908. return [
  909. ['my/../cat.gif', 'The requested file contains `..` and will not be read.'],
  910. ['my\..\cat.gif', 'The requested file contains `..` and will not be read.'],
  911. ['my/ca..t.gif', 'my/ca..t.gif was not found or not readable'],
  912. ['my/ca..t/image.gif', 'my/ca..t/image.gif was not found or not readable'],
  913. ];
  914. }
  915. /**
  916. * test withFile and invalid paths
  917. *
  918. * @dataProvider invalidFileProvider
  919. * @return void
  920. */
  921. public function testWithFileInvalidPath($path, $expectedMessage)
  922. {
  923. $this->expectException(NotFoundException::class);
  924. $this->expectExceptionMessage($expectedMessage);
  925. $response = new Response();
  926. $response->withFile($path);
  927. }
  928. /**
  929. * test withFile() + download & name
  930. *
  931. * @return void
  932. */
  933. public function testWithFileDownloadAndName()
  934. {
  935. $response = new Response();
  936. $new = $response->withFile(
  937. TEST_APP . 'vendor' . DS . 'css' . DS . 'test_asset.css',
  938. [
  939. 'name' => 'something_special.css',
  940. 'download' => true,
  941. ]
  942. );
  943. $this->assertEquals(
  944. 'text/html; charset=UTF-8',
  945. $response->getHeaderLine('Content-Type'),
  946. 'No mutation'
  947. );
  948. $this->assertEquals(
  949. 'text/css; charset=UTF-8',
  950. $new->getHeaderLine('Content-Type')
  951. );
  952. $this->assertEquals(
  953. 'attachment; filename="something_special.css"',
  954. $new->getHeaderLine('Content-Disposition')
  955. );
  956. $this->assertEquals('bytes', $new->getHeaderLine('Accept-Ranges'));
  957. $this->assertEquals('binary', $new->getHeaderLine('Content-Transfer-Encoding'));
  958. $body = $new->getBody();
  959. $this->assertInstanceOf('Zend\Diactoros\Stream', $body);
  960. $expected = '/* this is the test asset css file */';
  961. $this->assertEquals($expected, trim($body->getContents()));
  962. $this->assertEquals($expected, trim($new->getFile()->read()));
  963. }
  964. /**
  965. * test withFile() + a generic agent
  966. *
  967. * @return void
  968. */
  969. public function testWithFileUnknownFileTypeGeneric()
  970. {
  971. $response = new Response();
  972. $new = $response->withFile(CONFIG . 'no_section.ini');
  973. $this->assertEquals('text/html; charset=UTF-8', $new->getHeaderLine('Content-Type'));
  974. $this->assertEquals(
  975. 'attachment; filename="no_section.ini"',
  976. $new->getHeaderLine('Content-Disposition')
  977. );
  978. $this->assertEquals('bytes', $new->getHeaderLine('Accept-Ranges'));
  979. $body = $new->getBody();
  980. $expected = "some_key = some_value\nbool_key = 1\n";
  981. $this->assertEquals($expected, $body->getContents());
  982. }
  983. /**
  984. * test withFile() + opera
  985. *
  986. * @return void
  987. */
  988. public function testWithFileUnknownFileTypeOpera()
  989. {
  990. $_SERVER['HTTP_USER_AGENT'] = 'Opera/9.80 (Windows NT 6.0; U; en) Presto/2.8.99 Version/11.10';
  991. $response = new Response();
  992. $new = $response->withFile(CONFIG . 'no_section.ini');
  993. $this->assertEquals('application/octet-stream', $new->getHeaderLine('Content-Type'));
  994. $this->assertEquals(
  995. 'attachment; filename="no_section.ini"',
  996. $new->getHeaderLine('Content-Disposition')
  997. );
  998. }
  999. /**
  1000. * test withFile() + old IE
  1001. *
  1002. * @return void
  1003. */
  1004. public function testWithFileUnknownFileTypeOldIe()
  1005. {
  1006. $_SERVER['HTTP_USER_AGENT'] = 'Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; Media Center PC 4.0; SLCC1; .NET CLR 3.0.04320)';
  1007. $response = new Response();
  1008. $new = $response->withFile(CONFIG . 'no_section.ini');
  1009. $this->assertEquals('application/force-download', $new->getHeaderLine('Content-Type'));
  1010. }
  1011. /**
  1012. * test withFile() + no download
  1013. *
  1014. * @return void
  1015. */
  1016. public function testWithFileNoDownload()
  1017. {
  1018. $response = new Response();
  1019. $new = $response->withFile(CONFIG . 'no_section.ini', [
  1020. 'download' => false,
  1021. ]);
  1022. $this->assertEquals(
  1023. 'text/html; charset=UTF-8',
  1024. $new->getHeaderLine('Content-Type')
  1025. );
  1026. $this->assertFalse($new->hasHeader('Content-Disposition'));
  1027. $this->assertFalse($new->hasHeader('Content-Transfer-Encoding'));
  1028. }
  1029. /**
  1030. * Test that uppercase extensions result in correct content-types
  1031. *
  1032. * @return void
  1033. */
  1034. public function testWithFileUpperExtension()
  1035. {
  1036. $response = new Response();
  1037. $new = $response->withFile(TEST_APP . 'vendor/img/test_2.JPG');
  1038. $this->assertEquals('image/jpeg', $new->getHeaderLine('Content-Type'));
  1039. }
  1040. /**
  1041. * A data provider for testing various ranges
  1042. *
  1043. * @return array
  1044. */
  1045. public static function rangeProvider()
  1046. {
  1047. return [
  1048. // suffix-byte-range
  1049. [
  1050. 'bytes=-25', 25, 'bytes 13-37/38',
  1051. ],
  1052. [
  1053. 'bytes=0-', 38, 'bytes 0-37/38',
  1054. ],
  1055. [
  1056. 'bytes=10-', 28, 'bytes 10-37/38',
  1057. ],
  1058. [
  1059. 'bytes=10-20', 11, 'bytes 10-20/38',
  1060. ],
  1061. // Spaced out
  1062. [
  1063. 'bytes = 10 - 20', 11, 'bytes 10-20/38',
  1064. ],
  1065. ];
  1066. }
  1067. /**
  1068. * Test withFile() & the various range offset types.
  1069. *
  1070. * @dataProvider rangeProvider
  1071. * @return void
  1072. */
  1073. public function testWithFileRangeOffsets($range, $length, $offsetResponse)
  1074. {
  1075. $_SERVER['HTTP_RANGE'] = $range;
  1076. $response = new Response();
  1077. $new = $response->withFile(
  1078. TEST_APP . 'vendor' . DS . 'css' . DS . 'test_asset.css',
  1079. ['download' => true]
  1080. );
  1081. $this->assertEquals(
  1082. 'attachment; filename="test_asset.css"',
  1083. $new->getHeaderLine('Content-Disposition')
  1084. );
  1085. $this->assertEquals('binary', $new->getHeaderLine('Content-Transfer-Encoding'));
  1086. $this->assertEquals('bytes', $new->getHeaderLine('Accept-Ranges'));
  1087. $this->assertEquals($length, $new->getHeaderLine('Content-Length'));
  1088. $this->assertEquals($offsetResponse, $new->getHeaderLine('Content-Range'));
  1089. }
  1090. /**
  1091. * Test withFile() fetching ranges from a file.
  1092. *
  1093. * @return void
  1094. */
  1095. public function testWithFileRange()
  1096. {
  1097. $_SERVER['HTTP_RANGE'] = 'bytes=8-25';
  1098. $response = new Response();
  1099. $new = $response->withFile(
  1100. TEST_APP . 'vendor' . DS . 'css' . DS . 'test_asset.css',
  1101. ['download' => true]
  1102. );
  1103. $this->assertEquals(
  1104. 'attachment; filename="test_asset.css"',
  1105. $new->getHeaderLine('Content-Disposition')
  1106. );
  1107. $this->assertEquals('binary', $new->getHeaderLine('Content-Transfer-Encoding'));
  1108. $this->assertEquals('bytes', $new->getHeaderLine('Accept-Ranges'));
  1109. $this->assertEquals('18', $new->getHeaderLine('Content-Length'));
  1110. $this->assertEquals('bytes 8-25/38', $new->getHeaderLine('Content-Range'));
  1111. $this->assertEquals(206, $new->getStatusCode());
  1112. }
  1113. /**
  1114. * Provider for invalid range header values.
  1115. *
  1116. * @return array
  1117. */
  1118. public function invalidFileRangeProvider()
  1119. {
  1120. return [
  1121. // malformed range
  1122. [
  1123. 'bytes=0,38',
  1124. ],
  1125. // malformed punctuation
  1126. [
  1127. 'bytes: 0 - 38',
  1128. ],
  1129. ];
  1130. }
  1131. /**
  1132. * Test withFile() and invalid ranges
  1133. *
  1134. * @dataProvider invalidFileRangeProvider
  1135. * @return void
  1136. */
  1137. public function testWithFileInvalidRange($range)
  1138. {
  1139. $_SERVER['HTTP_RANGE'] = $range;
  1140. $response = new Response();
  1141. $new = $response->withFile(
  1142. TEST_APP . 'vendor' . DS . 'css' . DS . 'test_asset.css',
  1143. ['download' => true]
  1144. );
  1145. $this->assertEquals(
  1146. 'attachment; filename="test_asset.css"',
  1147. $new->getHeaderLine('Content-Disposition')
  1148. );
  1149. $this->assertEquals('binary', $new->getHeaderLine('Content-Transfer-Encoding'));
  1150. $this->assertEquals('bytes', $new->getHeaderLine('Accept-Ranges'));
  1151. $this->assertEquals('38', $new->getHeaderLine('Content-Length'));
  1152. $this->assertEquals('bytes 0-37/38', $new->getHeaderLine('Content-Range'));
  1153. $this->assertEquals(206, $new->getStatusCode());
  1154. }
  1155. /**
  1156. * Test withFile() and a reversed range
  1157. *
  1158. * @return void
  1159. */
  1160. public function testWithFileReversedRange()
  1161. {
  1162. $_SERVER['HTTP_RANGE'] = 'bytes=30-2';
  1163. $response = new Response();
  1164. $new = $response->withFile(
  1165. TEST_APP . 'vendor' . DS . 'css' . DS . 'test_asset.css',
  1166. ['download' => true]
  1167. );
  1168. $this->assertEquals(
  1169. 'attachment; filename="test_asset.css"',
  1170. $new->getHeaderLine('Content-Disposition')
  1171. );
  1172. $this->assertEquals('binary', $new->getHeaderLine('Content-Transfer-Encoding'));
  1173. $this->assertEquals('bytes', $new->getHeaderLine('Accept-Ranges'));
  1174. $this->assertEquals('bytes 0-37/38', $new->getHeaderLine('Content-Range'));
  1175. $this->assertEquals(416, $new->getStatusCode());
  1176. }
  1177. /**
  1178. * Test the withLocation method.
  1179. *
  1180. * @return void
  1181. */
  1182. public function testWithLocation()
  1183. {
  1184. $response = new Response();
  1185. $this->assertSame('', $response->getHeaderLine('Location'), 'No header should be set.');
  1186. $new = $response->withLocation('http://example.org');
  1187. $this->assertNotSame($new, $response);
  1188. $this->assertSame('', $response->getHeaderLine('Location'), 'No header should be set');
  1189. $this->assertSame('http://example.org', $new->getHeaderLine('Location'), 'Header should be set');
  1190. $this->assertSame(302, $new->getStatusCode(), 'Status should be updated');
  1191. }
  1192. /**
  1193. * Test get protocol version.
  1194. *
  1195. * @return void
  1196. */
  1197. public function getProtocolVersion()
  1198. {
  1199. $response = new Response();
  1200. $version = $response->getProtocolVersion();
  1201. $this->assertEquals('1.1', $version);
  1202. }
  1203. /**
  1204. * Test with protocol.
  1205. *
  1206. * @return void
  1207. */
  1208. public function testWithProtocol()
  1209. {
  1210. $response = new Response();
  1211. $version = $response->getProtocolVersion();
  1212. $this->assertEquals('1.1', $version);
  1213. $response2 = $response->withProtocolVersion('1.0');
  1214. $version = $response2->getProtocolVersion();
  1215. $this->assertEquals('1.0', $version);
  1216. $version = $response->getProtocolVersion();
  1217. $this->assertEquals('1.1', $version);
  1218. $this->assertNotEquals($response, $response2);
  1219. }
  1220. /**
  1221. * Test with protocol.
  1222. *
  1223. * @return void
  1224. */
  1225. public function testWithStatusCode()
  1226. {
  1227. $response = new Response();
  1228. $statusCode = $response->getStatusCode();
  1229. $this->assertEquals(200, $statusCode);
  1230. $response2 = $response->withStatus(404);
  1231. $statusCode = $response2->getStatusCode();
  1232. $this->assertEquals(404, $statusCode);
  1233. $statusCode = $response->getStatusCode();
  1234. $this->assertEquals(200, $statusCode);
  1235. $this->assertNotEquals($response, $response2);
  1236. }
  1237. /**
  1238. * Test invalid status codes
  1239. *
  1240. * @return void
  1241. */
  1242. public function testWithStatusInvalid()
  1243. {
  1244. $this->expectException(\InvalidArgumentException::class);
  1245. $this->expectExceptionMessage('Invalid status code: 1001. Use a valid HTTP status code in range 1xx - 5xx.');
  1246. $response = new Response();
  1247. $response->withStatus(1001);
  1248. }
  1249. /**
  1250. * Test get reason phrase.
  1251. *
  1252. * @return void
  1253. */
  1254. public function testGetReasonPhrase()
  1255. {
  1256. $response = new Response();
  1257. $this->assertSame('OK', $response->getReasonPhrase());
  1258. $response = $response->withStatus(404);
  1259. $reasonPhrase = $response->getReasonPhrase();
  1260. $this->assertEquals('Not Found', $reasonPhrase);
  1261. }
  1262. /**
  1263. * Test with body.
  1264. *
  1265. * @return void
  1266. */
  1267. public function testWithBody()
  1268. {
  1269. $response = new Response();
  1270. $body = $response->getBody();
  1271. $body->rewind();
  1272. $result = $body->getContents();
  1273. $this->assertEquals('', $result);
  1274. $stream = new Stream('php://memory', 'wb+');
  1275. $stream->write('test1');
  1276. $response2 = $response->withBody($stream);
  1277. $body = $response2->getBody();
  1278. $body->rewind();
  1279. $result = $body->getContents();
  1280. $this->assertEquals('test1', $result);
  1281. $body = $response->getBody();
  1282. $body->rewind();
  1283. $result = $body->getContents();
  1284. $this->assertEquals('', $result);
  1285. }
  1286. /**
  1287. * Test with string body.
  1288. *
  1289. * @return void
  1290. */
  1291. public function testWithStringBody()
  1292. {
  1293. $response = new Response();
  1294. $newResponse = $response->withStringBody('Foo');
  1295. $body = $newResponse->getBody();
  1296. $this->assertSame('Foo', (string)$body);
  1297. $this->assertNotSame($response, $newResponse);
  1298. $response = new Response();
  1299. $newResponse = $response->withStringBody('');
  1300. $body = $newResponse->getBody();
  1301. $this->assertSame('', (string)$body);
  1302. $this->assertNotSame($response, $newResponse);
  1303. $response = new Response();
  1304. $newResponse = $response->withStringBody(null);
  1305. $body = $newResponse->getBody();
  1306. $this->assertSame('', (string)$body);
  1307. $this->assertNotSame($response, $newResponse);
  1308. $response = new Response();
  1309. $newResponse = $response->withStringBody('1337');
  1310. $body = $newResponse->getBody();
  1311. $this->assertSame('1337', (string)$body);
  1312. $this->assertNotSame($response, $newResponse);
  1313. }
  1314. /**
  1315. * Test get Body.
  1316. *
  1317. * @return void
  1318. */
  1319. public function testGetBody()
  1320. {
  1321. $response = new Response();
  1322. $stream = $response->getBody();
  1323. $this->assertInstanceOf('Psr\Http\Message\StreamInterface', $stream);
  1324. }
  1325. /**
  1326. * Test with header.
  1327. *
  1328. * @return void
  1329. */
  1330. public function testWithHeader()
  1331. {
  1332. $response = new Response();
  1333. $response2 = $response->withHeader('Accept', 'application/json');
  1334. $result = $response2->getHeaders();
  1335. $expected = [
  1336. 'Content-Type' => ['text/html; charset=UTF-8'],
  1337. 'Accept' => ['application/json'],
  1338. ];
  1339. $this->assertEquals($expected, $result);
  1340. $this->assertFalse($response->hasHeader('Accept'));
  1341. }
  1342. /**
  1343. * Test get headers.
  1344. *
  1345. * @return void
  1346. */
  1347. public function testGetHeaders()
  1348. {
  1349. $response = new Response();
  1350. $headers = $response->getHeaders();
  1351. $response = $response->withAddedHeader('Location', 'localhost');
  1352. $response = $response->withAddedHeader('Accept', 'application/json');
  1353. $headers = $response->getHeaders();
  1354. $expected = [
  1355. 'Content-Type' => ['text/html; charset=UTF-8'],
  1356. 'Location' => ['localhost'],
  1357. 'Accept' => ['application/json'],
  1358. ];
  1359. $this->assertEquals($expected, $headers);
  1360. }
  1361. /**
  1362. * Test without header.
  1363. *
  1364. * @return void
  1365. */
  1366. public function testWithoutHeader()
  1367. {
  1368. $response = new Response();
  1369. $response = $response->withAddedHeader('Location', 'localhost');
  1370. $response = $response->withAddedHeader('Accept', 'application/json');
  1371. $response2 = $response->withoutHeader('Location');
  1372. $headers = $response2->getHeaders();
  1373. $expected = [
  1374. 'Content-Type' => ['text/html; charset=UTF-8'],
  1375. 'Accept' => ['application/json'],
  1376. ];
  1377. $this->assertEquals($expected, $headers);
  1378. }
  1379. /**
  1380. * Test get header.
  1381. *
  1382. * @return void
  1383. */
  1384. public function testGetHeader()
  1385. {
  1386. $response = new Response();
  1387. $response = $response->withAddedHeader('Location', 'localhost');
  1388. $result = $response->getHeader('Location');
  1389. $this->assertEquals(['localhost'], $result);
  1390. $result = $response->getHeader('location');
  1391. $this->assertEquals(['localhost'], $result);
  1392. $result = $response->getHeader('does-not-exist');
  1393. $this->assertEquals([], $result);
  1394. }
  1395. /**
  1396. * Test get header line.
  1397. *
  1398. * @return void
  1399. */
  1400. public function testGetHeaderLine()
  1401. {
  1402. $response = new Response();
  1403. $headers = $response->getHeaderLine('Accept');
  1404. $this->assertEquals('', $headers);
  1405. $response = $response->withAddedHeader('Accept', 'application/json');
  1406. $response = $response->withAddedHeader('Accept', 'application/xml');
  1407. $result = $response->getHeaderLine('Accept');
  1408. $expected = 'application/json,application/xml';
  1409. $this->assertEquals($expected, $result);
  1410. $result = $response->getHeaderLine('accept');
  1411. $this->assertEquals($expected, $result);
  1412. }
  1413. /**
  1414. * Test has header.
  1415. *
  1416. * @return void
  1417. */
  1418. public function testHasHeader()
  1419. {
  1420. $response = new Response();
  1421. $response = $response->withAddedHeader('Location', 'localhost');
  1422. $this->assertTrue($response->hasHeader('Location'));
  1423. $this->assertTrue($response->hasHeader('location'));
  1424. $this->assertTrue($response->hasHeader('locATIon'));
  1425. $this->assertFalse($response->hasHeader('Accept'));
  1426. $this->assertFalse($response->hasHeader('accept'));
  1427. }
  1428. /**
  1429. * Tests __debugInfo
  1430. *
  1431. * @return void
  1432. */
  1433. public function testDebugInfo()
  1434. {
  1435. $response = new Response();
  1436. $response = $response->withStringBody('Foo');
  1437. $result = $response->__debugInfo();
  1438. $expected = [
  1439. 'status' => 200,
  1440. 'contentType' => 'text/html',
  1441. 'headers' => [
  1442. 'Content-Type' => ['text/html; charset=UTF-8'],
  1443. ],
  1444. 'file' => null,
  1445. 'fileRange' => [],
  1446. 'cookies' => new CookieCollection(),
  1447. 'cacheDirectives' => [],
  1448. 'body' => 'Foo',
  1449. ];
  1450. $this->assertEquals($expected, $result);
  1451. }
  1452. }