ResponseEmitterTest.php 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.3.5
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase;
  16. use Cake\Http\CallbackStream;
  17. use Cake\Http\Response;
  18. use Cake\Http\ResponseEmitter;
  19. use Cake\TestSuite\TestCase;
  20. use Zend\Diactoros\ServerRequestFactory;
  21. use Zend\Diactoros\Stream;
  22. require_once __DIR__ . '/server_mocks.php';
  23. /**
  24. * Response emitter test.
  25. */
  26. class ResponseEmitterTest extends TestCase
  27. {
  28. protected $emitter;
  29. public function setUp()
  30. {
  31. parent::setUp();
  32. $GLOBALS['mockedHeaders'] = $GLOBALS['mockedCookies'] = [];
  33. $this->emitter = new ResponseEmitter();
  34. }
  35. /**
  36. * Test emitting simple responses.
  37. *
  38. * @return void
  39. */
  40. public function testEmitResponseSimple()
  41. {
  42. $response = (new Response())
  43. ->withStatus(201)
  44. ->withHeader('Content-Type', 'text/html')
  45. ->withHeader('Location', 'http://example.com/cake/1');
  46. $response->getBody()->write('It worked');
  47. ob_start();
  48. $this->emitter->emit($response);
  49. $out = ob_get_clean();
  50. $this->assertEquals('It worked', $out);
  51. $expected = [
  52. 'HTTP/1.1 201 Created',
  53. 'Content-Type: text/html',
  54. 'Location: http://example.com/cake/1'
  55. ];
  56. $this->assertEquals($expected, $GLOBALS['mockedHeaders']);
  57. }
  58. /**
  59. * Test emitting a no-content response
  60. *
  61. * @return void
  62. */
  63. public function testEmitNoContentResponse()
  64. {
  65. $response = (new Response())
  66. ->withHeader('X-testing', 'value')
  67. ->withStatus(204);
  68. $response->getBody()->write('It worked');
  69. ob_start();
  70. $this->emitter->emit($response);
  71. $out = ob_get_clean();
  72. $this->assertEquals('', $out);
  73. $expected = [
  74. 'HTTP/1.1 204 No Content',
  75. 'X-testing: value',
  76. ];
  77. $this->assertEquals($expected, $GLOBALS['mockedHeaders']);
  78. }
  79. /**
  80. * Test emitting responses with array cookes
  81. *
  82. * @return void
  83. */
  84. public function testEmitResponseArrayCookies()
  85. {
  86. $response = (new Response())
  87. ->withCookie('simple', ['value' => 'val', 'secure' => true])
  88. ->withAddedHeader('Set-Cookie', 'google=not=nice;Path=/accounts; HttpOnly')
  89. ->withHeader('Content-Type', 'text/plain');
  90. $response->getBody()->write('ok');
  91. ob_start();
  92. $this->emitter->emit($response);
  93. $out = ob_get_clean();
  94. $this->assertEquals('ok', $out);
  95. $expected = [
  96. 'HTTP/1.1 200 OK',
  97. 'Content-Type: text/plain'
  98. ];
  99. $this->assertEquals($expected, $GLOBALS['mockedHeaders']);
  100. $expected = [
  101. [
  102. 'name' => 'simple',
  103. 'value' => 'val',
  104. 'path' => '/',
  105. 'expire' => 0,
  106. 'domain' => '',
  107. 'secure' => true,
  108. 'httponly' => false
  109. ],
  110. [
  111. 'name' => 'google',
  112. 'value' => 'not=nice',
  113. 'path' => '/accounts',
  114. 'expire' => 0,
  115. 'domain' => '',
  116. 'secure' => false,
  117. 'httponly' => true
  118. ],
  119. ];
  120. $this->assertEquals($expected, $GLOBALS['mockedCookies']);
  121. }
  122. /**
  123. * Test emitting responses with cookies
  124. *
  125. * @return void
  126. */
  127. public function testEmitResponseCookies()
  128. {
  129. $response = (new Response())
  130. ->withAddedHeader('Set-Cookie', "simple=val;\tSecure")
  131. ->withAddedHeader('Set-Cookie', 'people=jim,jack,jonny";";Path=/accounts')
  132. ->withAddedHeader('Set-Cookie', 'google=not=nice;Path=/accounts; HttpOnly')
  133. ->withAddedHeader('Set-Cookie', 'a=b; Expires=Wed, 13 Jan 2021 22:23:01 GMT; Domain=www.example.com;')
  134. ->withAddedHeader('Set-Cookie', 'list%5B%5D=a%20b%20c')
  135. ->withHeader('Content-Type', 'text/plain');
  136. $response->getBody()->write('ok');
  137. ob_start();
  138. $this->emitter->emit($response);
  139. $out = ob_get_clean();
  140. $this->assertEquals('ok', $out);
  141. $expected = [
  142. 'HTTP/1.1 200 OK',
  143. 'Content-Type: text/plain'
  144. ];
  145. $this->assertEquals($expected, $GLOBALS['mockedHeaders']);
  146. $expected = [
  147. [
  148. 'name' => 'simple',
  149. 'value' => 'val',
  150. 'path' => '',
  151. 'expire' => 0,
  152. 'domain' => '',
  153. 'secure' => true,
  154. 'httponly' => false
  155. ],
  156. [
  157. 'name' => 'people',
  158. 'value' => 'jim,jack,jonny";"',
  159. 'path' => '/accounts',
  160. 'expire' => 0,
  161. 'domain' => '',
  162. 'secure' => false,
  163. 'httponly' => false
  164. ],
  165. [
  166. 'name' => 'google',
  167. 'value' => 'not=nice',
  168. 'path' => '/accounts',
  169. 'expire' => 0,
  170. 'domain' => '',
  171. 'secure' => false,
  172. 'httponly' => true
  173. ],
  174. [
  175. 'name' => 'a',
  176. 'value' => 'b',
  177. 'path' => '',
  178. 'expire' => 1610576581,
  179. 'domain' => 'www.example.com',
  180. 'secure' => false,
  181. 'httponly' => false
  182. ],
  183. [
  184. 'name' => 'list[]',
  185. 'value' => 'a b c',
  186. 'path' => '',
  187. 'expire' => 0,
  188. 'domain' => '',
  189. 'secure' => false,
  190. 'httponly' => false
  191. ],
  192. ];
  193. $this->assertEquals($expected, $GLOBALS['mockedCookies']);
  194. }
  195. /**
  196. * Test emitting responses using callback streams.
  197. *
  198. * We use callback streams for closure based responses.
  199. *
  200. * @return void
  201. */
  202. public function testEmitResponseCallbackStream()
  203. {
  204. $stream = new CallbackStream(function () {
  205. echo 'It worked';
  206. });
  207. $response = (new Response())
  208. ->withStatus(201)
  209. ->withBody($stream)
  210. ->withHeader('Content-Type', 'text/plain');
  211. ob_start();
  212. $this->emitter->emit($response);
  213. $out = ob_get_clean();
  214. $this->assertEquals('It worked', $out);
  215. $expected = [
  216. 'HTTP/1.1 201 Created',
  217. 'Content-Type: text/plain',
  218. ];
  219. $this->assertEquals($expected, $GLOBALS['mockedHeaders']);
  220. }
  221. /**
  222. * Test valid body ranges.
  223. *
  224. * @return void
  225. */
  226. public function testEmitResponseBodyRange()
  227. {
  228. $response = (new Response())
  229. ->withHeader('Content-Type', 'text/plain')
  230. ->withHeader('Content-Range', 'bytes 1-4/9');
  231. $response->getBody()->write('It worked');
  232. ob_start();
  233. $this->emitter->emit($response);
  234. $out = ob_get_clean();
  235. $this->assertEquals('t wo', $out);
  236. $expected = [
  237. 'HTTP/1.1 200 OK',
  238. 'Content-Type: text/plain',
  239. 'Content-Range: bytes 1-4/9',
  240. ];
  241. $this->assertEquals($expected, $GLOBALS['mockedHeaders']);
  242. }
  243. /**
  244. * Test valid body ranges.
  245. *
  246. * @return void
  247. */
  248. public function testEmitResponseBodyRangeComplete()
  249. {
  250. $response = (new Response())
  251. ->withHeader('Content-Type', 'text/plain')
  252. ->withHeader('Content-Range', 'bytes 0-20/9');
  253. $response->getBody()->write('It worked');
  254. ob_start();
  255. $this->emitter->emit($response, 2);
  256. $out = ob_get_clean();
  257. $this->assertEquals('It worked', $out);
  258. }
  259. /**
  260. * Test out of bounds body ranges.
  261. *
  262. * @return void
  263. */
  264. public function testEmitResponseBodyRangeOverflow()
  265. {
  266. $response = (new Response())
  267. ->withHeader('Content-Type', 'text/plain')
  268. ->withHeader('Content-Range', 'bytes 5-20/9');
  269. $response->getBody()->write('It worked');
  270. ob_start();
  271. $this->emitter->emit($response);
  272. $out = ob_get_clean();
  273. $this->assertEquals('rked', $out);
  274. }
  275. /**
  276. * Test malformed content-range header
  277. *
  278. * @return void
  279. */
  280. public function testEmitResponseBodyRangeMalformed()
  281. {
  282. $response = (new Response())
  283. ->withHeader('Content-Type', 'text/plain')
  284. ->withHeader('Content-Range', 'bytes 9-ba/a');
  285. $response->getBody()->write('It worked');
  286. ob_start();
  287. $this->emitter->emit($response);
  288. $out = ob_get_clean();
  289. $this->assertEquals('It worked', $out);
  290. }
  291. /**
  292. * Test callback streams returning content and ranges
  293. *
  294. * @return void
  295. */
  296. public function testEmitResponseBodyRangeCallbackStream()
  297. {
  298. $stream = new CallbackStream(function () {
  299. return 'It worked';
  300. });
  301. $response = (new Response())
  302. ->withStatus(201)
  303. ->withBody($stream)
  304. ->withHeader('Content-Range', 'bytes 1-4/9')
  305. ->withHeader('Content-Type', 'text/plain');
  306. ob_start();
  307. $this->emitter->emit($response);
  308. $out = ob_get_clean();
  309. $this->assertEquals('t wo', $out);
  310. $expected = [
  311. 'HTTP/1.1 201 Created',
  312. 'Content-Range: bytes 1-4/9',
  313. 'Content-Type: text/plain',
  314. ];
  315. $this->assertEquals($expected, $GLOBALS['mockedHeaders']);
  316. }
  317. }