BodyParserMiddlewareTest.php 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (http://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. (http://cakefoundation.org)
  12. * @link http://cakephp.org CakePHP(tm) Project
  13. * @since 3.5.0
  14. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Test\TestCase\Http\Middleware;
  17. use Cake\Http\Exception\BadRequestException;
  18. use Cake\Http\Middleware\BodyParserMiddleware;
  19. use Cake\Http\Response;
  20. use Cake\Http\ServerRequest;
  21. use Cake\TestSuite\TestCase;
  22. use TestApp\Http\TestRequestHandler;
  23. /**
  24. * Test for BodyParser
  25. */
  26. class BodyParserMiddlewareTest extends TestCase
  27. {
  28. /**
  29. * Data provider for HTTP method tests.
  30. *
  31. * HEAD and GET do not populate $_POST or request->data.
  32. *
  33. * @return array
  34. */
  35. public static function safeHttpMethodProvider()
  36. {
  37. return [
  38. ['GET'],
  39. ['HEAD'],
  40. ];
  41. }
  42. /**
  43. * Data provider for HTTP methods that can contain request bodies.
  44. *
  45. * @return array
  46. */
  47. public static function httpMethodProvider()
  48. {
  49. return [
  50. ['PATCH'], ['PUT'], ['POST'], ['DELETE'],
  51. ];
  52. }
  53. /**
  54. * test constructor options
  55. *
  56. * @return void
  57. */
  58. public function testConstructorMethodsOption()
  59. {
  60. $parser = new BodyParserMiddleware(['methods' => ['PUT']]);
  61. $this->assertEquals(['PUT'], $parser->getMethods());
  62. }
  63. /**
  64. * test constructor options
  65. *
  66. * @return void
  67. */
  68. public function testConstructorXmlOption()
  69. {
  70. $parser = new BodyParserMiddleware(['json' => false]);
  71. $this->assertEquals([], $parser->getParsers(), 'Xml off by default');
  72. $parser = new BodyParserMiddleware(['json' => false, 'xml' => false]);
  73. $this->assertEquals([], $parser->getParsers(), 'No Xml types set.');
  74. $parser = new BodyParserMiddleware(['json' => false, 'xml' => true]);
  75. $expected = [
  76. 'application/xml' => [$parser, 'decodeXml'],
  77. 'text/xml' => [$parser, 'decodeXml'],
  78. ];
  79. $this->assertEquals($expected, $parser->getParsers(), 'Xml types are incorrect.');
  80. }
  81. /**
  82. * test constructor options
  83. *
  84. * @return void
  85. */
  86. public function testConstructorJsonOption()
  87. {
  88. $parser = new BodyParserMiddleware(['json' => false]);
  89. $this->assertEquals([], $parser->getParsers(), 'No JSON types set.');
  90. $parser = new BodyParserMiddleware([]);
  91. $expected = [
  92. 'application/json' => [$parser, 'decodeJson'],
  93. 'text/json' => [$parser, 'decodeJson'],
  94. ];
  95. $this->assertEquals($expected, $parser->getParsers(), 'JSON types are incorrect.');
  96. }
  97. /**
  98. * test setMethods()
  99. *
  100. * @return void
  101. */
  102. public function testSetMethodsReturn()
  103. {
  104. $parser = new BodyParserMiddleware();
  105. $this->assertSame($parser, $parser->setMethods(['PUT']));
  106. $this->assertEquals(['PUT'], $parser->getMethods());
  107. }
  108. /**
  109. * test addParser()
  110. *
  111. * @return void
  112. */
  113. public function testAddParserReturn()
  114. {
  115. $parser = new BodyParserMiddleware(['json' => false]);
  116. $this->assertSame($parser, $parser->addParser(['application/json'], 'json_decode'));
  117. }
  118. /**
  119. * test last parser defined wins
  120. *
  121. * @return void
  122. */
  123. public function testAddParserOverwrite()
  124. {
  125. $parser = new BodyParserMiddleware(['json' => false]);
  126. $parser->addParser(['application/json'], 'json_decode');
  127. $parser->addParser(['application/json'], 'strpos');
  128. $this->assertEquals(['application/json' => 'strpos'], $parser->getParsers());
  129. }
  130. /**
  131. * test skipping parsing on unknown type
  132. *
  133. * @dataProvider httpMethodProvider
  134. * @return void
  135. */
  136. public function testInvokeMismatchedType($method)
  137. {
  138. $parser = new BodyParserMiddleware();
  139. $request = new ServerRequest([
  140. 'environment' => [
  141. 'REQUEST_METHOD' => $method,
  142. 'CONTENT_TYPE' => 'text/csv',
  143. ],
  144. 'input' => 'a,b,c',
  145. ]);
  146. $handler = new TestRequestHandler(function ($req) {
  147. $this->assertEquals([], $req->getParsedBody());
  148. return new Response();
  149. });
  150. $parser->process($request, $handler);
  151. }
  152. /**
  153. * test parsing on valid http method
  154. *
  155. * @dataProvider httpMethodProvider
  156. * @return void
  157. */
  158. public function testInvokeCaseInsensitiveContentType($method)
  159. {
  160. $parser = new BodyParserMiddleware();
  161. $request = new ServerRequest([
  162. 'environment' => [
  163. 'REQUEST_METHOD' => $method,
  164. 'CONTENT_TYPE' => 'ApPlIcAtIoN/JSoN',
  165. ],
  166. 'input' => '{"title": "yay"}',
  167. ]);
  168. $handler = new TestRequestHandler(function ($req) {
  169. $this->assertEquals(['title' => 'yay'], $req->getParsedBody());
  170. return new Response();
  171. });
  172. $parser->process($request, $handler);
  173. }
  174. /**
  175. * test parsing on valid http method
  176. *
  177. * @dataProvider httpMethodProvider
  178. * @return void
  179. */
  180. public function testInvokeParse($method)
  181. {
  182. $parser = new BodyParserMiddleware();
  183. $request = new ServerRequest([
  184. 'environment' => [
  185. 'REQUEST_METHOD' => $method,
  186. 'CONTENT_TYPE' => 'application/json',
  187. ],
  188. 'input' => '{"title": "yay"}',
  189. ]);
  190. $handler = new TestRequestHandler(function ($req) {
  191. $this->assertEquals(['title' => 'yay'], $req->getParsedBody());
  192. return new Response();
  193. });
  194. $parser->process($request, $handler);
  195. }
  196. /**
  197. * test parsing on valid http method with charset
  198. *
  199. * @return void
  200. */
  201. public function testInvokeParseStripCharset()
  202. {
  203. $parser = new BodyParserMiddleware();
  204. $request = new ServerRequest([
  205. 'environment' => [
  206. 'REQUEST_METHOD' => 'POST',
  207. 'CONTENT_TYPE' => 'application/json; charset=utf-8',
  208. ],
  209. 'input' => '{"title": "yay"}',
  210. ]);
  211. $handler = new TestRequestHandler(function ($req) {
  212. $this->assertEquals(['title' => 'yay'], $req->getParsedBody());
  213. return new Response();
  214. });
  215. $parser->process($request, $handler);
  216. }
  217. /**
  218. * test parsing on ignored http method
  219. *
  220. * @dataProvider safeHttpMethodProvider
  221. * @return void
  222. */
  223. public function testInvokeNoParseOnSafe($method)
  224. {
  225. $parser = new BodyParserMiddleware();
  226. $request = new ServerRequest([
  227. 'environment' => [
  228. 'REQUEST_METHOD' => $method,
  229. 'CONTENT_TYPE' => 'application/json',
  230. ],
  231. 'input' => '{"title": "yay"}',
  232. ]);
  233. $handler = new TestRequestHandler(function ($req) {
  234. $this->assertEquals([], $req->getParsedBody());
  235. return new Response();
  236. });
  237. $parser->process($request, $handler);
  238. }
  239. /**
  240. * test parsing XML bodies.
  241. *
  242. * @return void
  243. */
  244. public function testInvokeXml()
  245. {
  246. $xml = <<<XML
  247. <?xml version="1.0" encoding="utf-8"?>
  248. <article>
  249. <title>yay</title>
  250. </article>
  251. XML;
  252. $request = new ServerRequest([
  253. 'environment' => [
  254. 'REQUEST_METHOD' => 'POST',
  255. 'CONTENT_TYPE' => 'application/xml',
  256. ],
  257. 'input' => $xml,
  258. ]);
  259. $handler = new TestRequestHandler(function ($req) {
  260. $expected = [
  261. 'article' => ['title' => 'yay'],
  262. ];
  263. $this->assertEquals($expected, $req->getParsedBody());
  264. return new Response();
  265. });
  266. $parser = new BodyParserMiddleware(['xml' => true]);
  267. $parser->process($request, $handler);
  268. }
  269. /**
  270. * Test that CDATA is removed in XML data.
  271. *
  272. * @return void
  273. */
  274. public function testInvokeXmlCdata()
  275. {
  276. $xml = <<<XML
  277. <?xml version="1.0" encoding="utf-8"?>
  278. <article>
  279. <id>1</id>
  280. <title><![CDATA[first]]></title>
  281. </article>
  282. XML;
  283. $request = new ServerRequest([
  284. 'environment' => [
  285. 'REQUEST_METHOD' => 'POST',
  286. 'CONTENT_TYPE' => 'application/xml',
  287. ],
  288. 'input' => $xml,
  289. ]);
  290. $handler = new TestRequestHandler(function ($req) {
  291. $expected = [
  292. 'article' => [
  293. 'id' => 1,
  294. 'title' => 'first',
  295. ],
  296. ];
  297. $this->assertEquals($expected, $req->getParsedBody());
  298. return new Response();
  299. });
  300. $parser = new BodyParserMiddleware(['xml' => true]);
  301. $parser->process($request, $handler);
  302. }
  303. /**
  304. * Test that internal entity recursion is ignored.
  305. *
  306. * @return void
  307. */
  308. public function testInvokeXmlInternalEntities()
  309. {
  310. $xml = <<<XML
  311. <?xml version="1.0" encoding="UTF-8"?>
  312. <!DOCTYPE item [
  313. <!ENTITY item "item">
  314. <!ENTITY item1 "&item;&item;&item;&item;&item;&item;">
  315. <!ENTITY item2 "&item1;&item1;&item1;&item1;&item1;&item1;&item1;&item1;&item1;">
  316. <!ENTITY item3 "&item2;&item2;&item2;&item2;&item2;&item2;&item2;&item2;&item2;">
  317. <!ENTITY item4 "&item3;&item3;&item3;&item3;&item3;&item3;&item3;&item3;&item3;">
  318. <!ENTITY item5 "&item4;&item4;&item4;&item4;&item4;&item4;&item4;&item4;&item4;">
  319. <!ENTITY item6 "&item5;&item5;&item5;&item5;&item5;&item5;&item5;&item5;&item5;">
  320. <!ENTITY item7 "&item6;&item6;&item6;&item6;&item6;&item6;&item6;&item6;&item6;">
  321. <!ENTITY item8 "&item7;&item7;&item7;&item7;&item7;&item7;&item7;&item7;&item7;">
  322. ]>
  323. <item>
  324. <description>&item8;</description>
  325. </item>
  326. XML;
  327. $request = new ServerRequest([
  328. 'environment' => [
  329. 'REQUEST_METHOD' => 'POST',
  330. 'CONTENT_TYPE' => 'application/xml',
  331. ],
  332. 'input' => $xml,
  333. ]);
  334. $response = new Response();
  335. $handler = new TestRequestHandler(function ($req) {
  336. $this->assertEquals([], $req->getParsedBody());
  337. return new Response();
  338. });
  339. $parser = new BodyParserMiddleware(['xml' => true]);
  340. $parser->process($request, $handler);
  341. }
  342. /**
  343. * test parsing fails will raise a bad request.
  344. *
  345. * @return void
  346. */
  347. public function testInvokeParseNoArray()
  348. {
  349. $request = new ServerRequest([
  350. 'environment' => [
  351. 'REQUEST_METHOD' => 'POST',
  352. 'CONTENT_TYPE' => 'application/json',
  353. ],
  354. 'input' => 'lol',
  355. ]);
  356. $handler = new TestRequestHandler(function ($req) {
  357. return new Response();
  358. });
  359. $this->expectException(BadRequestException::class);
  360. $parser = new BodyParserMiddleware();
  361. $parser->process($request, $handler);
  362. }
  363. }