BodyParserMiddlewareTest.php 11 KB

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