HttpTest.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. <?php
  2. /**
  3. *
  4. * Copyright © Magento, Inc. All rights reserved.
  5. * See COPYING.txt for license details.
  6. */
  7. namespace Magento\Framework\App\Test\Unit\Request;
  8. use Magento\Framework\App\Config\ScopeConfigInterface;
  9. use Magento\Framework\App\Request\Http;
  10. /**
  11. * @SuppressWarnings(PHPMD.TooManyMethods)
  12. * @SuppressWarnings(PHPMD.TooManyPublicMethods)
  13. */
  14. class HttpTest extends \PHPUnit\Framework\TestCase
  15. {
  16. /**
  17. * @var \Magento\Framework\App\Request\Http
  18. */
  19. private $model;
  20. /**
  21. * @var \Magento\Framework\App\Route\ConfigInterface\Proxy | \PHPUnit_Framework_MockObject_MockObject
  22. */
  23. private $routerListMock;
  24. /**
  25. * @var \Magento\Framework\App\Request\PathInfoProcessorInterface | \PHPUnit_Framework_MockObject_MockObject
  26. */
  27. private $infoProcessorMock;
  28. /**
  29. * @var \Magento\Framework\App\Request\PathInfo
  30. */
  31. private $pathInfo;
  32. /**
  33. * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager | \PHPUnit_Framework_MockObject_MockObject
  34. */
  35. private $objectManagerMock;
  36. /**
  37. * @var \Magento\Framework\Stdlib\StringUtils | \PHPUnit_Framework_MockObject_MockObject
  38. */
  39. private $converterMock;
  40. /**
  41. * @var \Magento\Framework\TestFramework\Unit\Helper\ObjectManager
  42. */
  43. private $objectManager;
  44. /**
  45. * @var array
  46. */
  47. private $serverArray;
  48. protected function setUp()
  49. {
  50. $this->routerListMock = $this->createPartialMock(
  51. \Magento\Framework\App\Route\ConfigInterface\Proxy::class,
  52. ['getRouteFrontName', 'getRouteByFrontName', '__wakeup']
  53. );
  54. $this->infoProcessorMock = $this->createMock(\Magento\Framework\App\Request\PathInfoProcessorInterface::class);
  55. $this->infoProcessorMock->expects($this->any())->method('process')->will($this->returnArgument(1));
  56. $this->objectManagerMock = $this->createMock(\Magento\Framework\ObjectManagerInterface::class);
  57. $this->converterMock = $this->getMockBuilder(\Magento\Framework\Stdlib\StringUtils::class)
  58. ->disableOriginalConstructor()
  59. ->setMethods(['cleanString'])
  60. ->getMock();
  61. $this->converterMock->expects($this->any())->method('cleanString')->will($this->returnArgument(0));
  62. // Stash the $_SERVER array to protect it from modification in test
  63. $this->serverArray = $_SERVER;
  64. $this->objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
  65. $this->pathInfo = $this->objectManager->getObject(\Magento\Framework\App\Request\PathInfo::class);
  66. }
  67. public function tearDown()
  68. {
  69. $_SERVER = $this->serverArray;
  70. }
  71. /**
  72. * @return \Magento\Framework\App\Request\Http
  73. */
  74. private function getModel($uri = null, $appConfigMock = true)
  75. {
  76. $model = $this->objectManager->getObject(
  77. \Magento\Framework\App\Request\Http::class,
  78. [
  79. 'routeConfig' => $this->routerListMock,
  80. 'pathInfoProcessor' => $this->infoProcessorMock,
  81. 'pathInfoService' => $this->pathInfo,
  82. 'objectManager' => $this->objectManagerMock,
  83. 'converter' => $this->converterMock,
  84. 'uri' => $uri,
  85. ]
  86. );
  87. if ($appConfigMock) {
  88. $configMock = $this->createMock(\Magento\Framework\App\Config::class);
  89. $this->objectManager->setBackwardCompatibleProperty($model, 'appConfig', $configMock);
  90. }
  91. return $model;
  92. }
  93. public function testGetOriginalPathInfoWithTestUri()
  94. {
  95. $uri = 'http://test.com/value?key=value';
  96. $this->model = $this->getModel($uri);
  97. $this->assertEquals('/value', $this->model->getOriginalPathInfo());
  98. }
  99. public function testGetOriginalPathInfoWithEmptyUri()
  100. {
  101. $this->model = $this->getModel();
  102. $this->assertEmpty($this->model->getOriginalPathInfo());
  103. }
  104. public function testGetBasePathWithPath()
  105. {
  106. $this->model = $this->getModel();
  107. $this->model->setBasePath('http:\/test.com\one/two');
  108. $this->assertEquals('http://test.com/one/two', $this->model->getBasePath());
  109. }
  110. public function testGetBasePathWithoutPath()
  111. {
  112. $this->model = $this->getModel();
  113. $this->model->setBasePath(null);
  114. $this->assertEquals('/', $this->model->getBasePath());
  115. }
  116. public function testSetRouteNameWithRouter()
  117. {
  118. $router = $this->createMock(\Magento\Framework\App\Route\ConfigInterface::class);
  119. $this->routerListMock->expects($this->any())->method('getRouteFrontName')->will($this->returnValue($router));
  120. $this->model = $this->getModel();
  121. $this->model->setRouteName('RouterName');
  122. $this->assertEquals('RouterName', $this->model->getRouteName());
  123. }
  124. public function testSetRouteNameWithNullRouterValue()
  125. {
  126. $this->model = $this->getModel();
  127. $this->routerListMock->expects($this->once())->method('getRouteFrontName')->will($this->returnValue(null));
  128. $this->model->setRouteName('RouterName');
  129. }
  130. public function testGetFrontName()
  131. {
  132. $uri = 'http://test.com/one/two';
  133. $this->model = $this->getModel($uri);
  134. $this->assertEquals('one', $this->model->getFrontName());
  135. }
  136. public function testGetRouteNameWithNullValueRouteName()
  137. {
  138. $this->model = $this->getModel();
  139. $this->model->setRouteName('RouteName');
  140. $this->assertEquals('RouteName', $this->model->getRouteName());
  141. }
  142. public function testGetRouteName()
  143. {
  144. $this->model = $this->getModel();
  145. $expected = 'RouteName';
  146. $this->model->setRouteName($expected);
  147. $this->assertEquals($expected, $this->model->getRouteName());
  148. }
  149. public function testGetFullActionName()
  150. {
  151. $this->model = $this->getModel();
  152. /* empty request */
  153. $this->assertEquals('__', $this->model->getFullActionName());
  154. $this->model->setRouteName('test')->setControllerName('controller')->setActionName('action');
  155. $this->assertEquals('test/controller/action', $this->model->getFullActionName('/'));
  156. }
  157. public function testInitForward()
  158. {
  159. $expected = $this->initForward();
  160. $this->assertEquals($expected, $this->model->getBeforeForwardInfo());
  161. }
  162. public function testGetBeforeForwardInfo()
  163. {
  164. $beforeForwardInfo = $this->initForward();
  165. $this->assertNull($this->model->getBeforeForwardInfo('not_existing_forward_info_key'));
  166. foreach (array_keys($beforeForwardInfo) as $key) {
  167. $this->assertEquals($beforeForwardInfo[$key], $this->model->getBeforeForwardInfo($key));
  168. }
  169. $this->assertEquals($beforeForwardInfo, $this->model->getBeforeForwardInfo());
  170. }
  171. /**
  172. * Initialize $_beforeForwardInfo
  173. *
  174. * @return array Contents of $_beforeForwardInfo
  175. */
  176. private function initForward()
  177. {
  178. $this->model = $this->getModel();
  179. $beforeForwardInfo = [
  180. 'params' => ['one' => '111', 'two' => '222'],
  181. 'action_name' => 'ActionName',
  182. 'controller_name' => 'ControllerName',
  183. 'module_name' => 'ModuleName',
  184. 'route_name' => 'RouteName'
  185. ];
  186. $this->model->setParams($beforeForwardInfo['params']);
  187. $this->model->setActionName($beforeForwardInfo['action_name']);
  188. $this->model->setControllerName($beforeForwardInfo['controller_name']);
  189. $this->model->setModuleName($beforeForwardInfo['module_name']);
  190. $this->model->setRouteName($beforeForwardInfo['route_name']);
  191. $this->model->initForward();
  192. return $beforeForwardInfo;
  193. }
  194. public function testIsAjax()
  195. {
  196. $this->model = $this->getModel();
  197. $this->assertFalse($this->model->isAjax());
  198. $this->model->clearParams();
  199. $this->model->setParam('ajax', 1);
  200. $this->assertTrue($this->model->isAjax());
  201. $this->model->clearParams();
  202. $this->model->setParam('isAjax', 1);
  203. $this->assertTrue($this->model->isAjax());
  204. $this->model->clearParams();
  205. $this->model->getHeaders()->addHeaderLine('X-Requested-With', 'XMLHttpRequest');
  206. $this->assertTrue($this->model->isAjax());
  207. $this->model->getHeaders()->clearHeaders();
  208. $this->model->getHeaders()->addHeaderLine('X-Requested-With', 'NotXMLHttpRequest');
  209. $this->assertFalse($this->model->isAjax());
  210. }
  211. /**
  212. * @param array $serverVariables
  213. * @param string $expectedResult
  214. * @dataProvider serverVariablesProvider
  215. */
  216. public function testGetDistroBaseUrl($serverVariables, $expectedResult)
  217. {
  218. $originalServerValue = $_SERVER;
  219. $_SERVER = $serverVariables;
  220. $this->model = $this->getModel();
  221. $this->assertEquals($expectedResult, $this->model->getDistroBaseUrl());
  222. $_SERVER = $originalServerValue;
  223. }
  224. /**
  225. * @param string $scriptName
  226. * @param string $expected
  227. * @dataProvider getDistroBaseUrlPathDataProvider
  228. */
  229. public function testGetDistroBaseUrlPath($scriptName, $expected)
  230. {
  231. $this->assertEquals($expected, Http::getDistroBaseUrlPath(['SCRIPT_NAME' => $scriptName]));
  232. }
  233. /**
  234. * @return array
  235. */
  236. public function getDistroBaseUrlPathDataProvider()
  237. {
  238. return [
  239. [null, '/'],
  240. ['./index.php', '/'],
  241. ['.\\index.php', '/'],
  242. ['/index.php', '/'],
  243. ['\\index.php', '/'],
  244. ['subdir/script.php', 'subdir/'],
  245. ['subdir\\script.php', 'subdir/'],
  246. ['sub\\dir\\script.php', 'sub/dir/'],
  247. ];
  248. }
  249. /**
  250. * @return array
  251. */
  252. public function serverVariablesProvider()
  253. {
  254. $returnValue = [];
  255. $defaultServerData = [
  256. 'SCRIPT_NAME' => 'index.php',
  257. 'HTTP_HOST' => 'sample.host.com',
  258. 'SERVER_PORT' => '80',
  259. 'HTTPS' => '1'
  260. ];
  261. $secureUnusualPort = $noHttpsData = $httpsOffData = $noHostData = $noScriptNameData = $defaultServerData;
  262. unset($noScriptNameData['SCRIPT_NAME']);
  263. $returnValue['no SCRIPT_NAME'] = [$noScriptNameData, 'http://localhost/'];
  264. unset($noHostData['HTTP_HOST']);
  265. $returnValue['no HTTP_HOST'] = [$noHostData, 'http://localhost/'];
  266. $httpsOffData['HTTPS'] = 'off';
  267. $returnValue['HTTPS off'] = [$httpsOffData, 'http://sample.host.com/'];
  268. unset($noHttpsData['HTTPS']);
  269. $returnValue['no HTTPS'] = [$noHttpsData, 'http://sample.host.com/'];
  270. $noHttpsNoServerPort = $noHttpsData;
  271. unset($noHttpsNoServerPort['SERVER_PORT']);
  272. $returnValue['no SERVER_PORT'] = [$noHttpsNoServerPort, 'http://sample.host.com/'];
  273. $noHttpsButSecurePort = $noHttpsData;
  274. $noHttpsButSecurePort['SERVER_PORT'] = 443;
  275. $returnValue['no HTTP but secure port'] = [$noHttpsButSecurePort, 'https://sample.host.com/'];
  276. $notSecurePort = $noHttpsData;
  277. $notSecurePort['SERVER_PORT'] = 81;
  278. $notSecurePort['HTTP_HOST'] = 'sample.host.com:81';
  279. $returnValue['not secure not standard port'] = [$notSecurePort, 'http://sample.host.com:81/'];
  280. $secureUnusualPort['SERVER_PORT'] = 441;
  281. $secureUnusualPort['HTTP_HOST'] = 'sample.host.com:441';
  282. $returnValue['not standard secure port'] = [$secureUnusualPort, 'https://sample.host.com:441/'];
  283. $customUrlPathData = $noHttpsData;
  284. $customUrlPathData['SCRIPT_FILENAME'] = '/some/dir/custom.php';
  285. $returnValue['custom path'] = [$customUrlPathData, 'http://sample.host.com/'];
  286. return $returnValue;
  287. }
  288. /**
  289. * @dataProvider isSecureDataProvider
  290. *
  291. * @param bool $isSecure expected output of isSecure method
  292. * @param string $serverHttps value of $_SERVER['HTTPS']
  293. * @param string $headerOffloadKey <Name-Of-Offload-Header>
  294. * @param string $headerOffloadValue value of $_SERVER[<Name-Of-Offload-Header>]
  295. * @param int $configCall number of times config->getValue is expected to be called
  296. */
  297. public function testIsSecure($isSecure, $serverHttps, $headerOffloadKey, $headerOffloadValue, $configCall)
  298. {
  299. $this->model = $this->getModel(null, false);
  300. $configOffloadHeader = 'Header-From-Proxy';
  301. $configMock = $this->getMockBuilder(\Magento\Framework\App\Config::class)
  302. ->disableOriginalConstructor()
  303. ->setMethods(['getValue'])
  304. ->getMock();
  305. $configMock->expects($this->exactly($configCall))
  306. ->method('getValue')
  307. ->with(
  308. \Magento\Framework\App\Request\Http::XML_PATH_OFFLOADER_HEADER,
  309. ScopeConfigInterface::SCOPE_TYPE_DEFAULT
  310. )->willReturn($configOffloadHeader);
  311. $this->objectManager->setBackwardCompatibleProperty($this->model, 'appConfig', $configMock);
  312. $this->objectManager->setBackwardCompatibleProperty($this->model, 'sslOffloadHeader', null);
  313. $this->model->getServer()->set($headerOffloadKey, $headerOffloadValue);
  314. $this->model->getServer()->set('HTTPS', $serverHttps);
  315. $this->assertSame($isSecure, $this->model->isSecure());
  316. }
  317. /**
  318. * @dataProvider httpSafeMethodProvider
  319. * @backupGlobals enabled
  320. * @param string $httpMethod value of $_SERVER['REQUEST_METHOD']
  321. */
  322. public function testIsSafeMethodTrue($httpMethod)
  323. {
  324. $this->model = $this->getModel();
  325. $_SERVER['REQUEST_METHOD'] = $httpMethod;
  326. $this->assertEquals(true, $this->model->isSafeMethod());
  327. }
  328. /**
  329. * @dataProvider httpNotSafeMethodProvider
  330. * @backupGlobals enabled
  331. * @param string $httpMethod value of $_SERVER['REQUEST_METHOD']
  332. */
  333. public function testIsSafeMethodFalse($httpMethod)
  334. {
  335. $this->model = $this->getModel();
  336. $_SERVER['REQUEST_METHOD'] = $httpMethod;
  337. $this->assertEquals(false, $this->model->isSafeMethod());
  338. }
  339. /**
  340. * @return array
  341. */
  342. public function httpSafeMethodProvider()
  343. {
  344. return [
  345. 'Test 1' => ['GET'],
  346. 'Test 2' => ['HEAD'],
  347. 'Test 3' => ['TRACE'],
  348. 'Test 4' => ['OPTIONS']
  349. ];
  350. }
  351. /**
  352. * @return array
  353. */
  354. public function httpNotSafeMethodProvider()
  355. {
  356. return [
  357. 'Test 1' => ['POST'],
  358. 'Test 2' => ['PUT'],
  359. 'Test 3' => ['DELETE'],
  360. 'Test 4' => ['PATCH'],
  361. 'Test 5' => ['CONNECT'],
  362. 'Test 6' => [null]
  363. ];
  364. }
  365. /**
  366. * @return array
  367. */
  368. public function isSecureDataProvider()
  369. {
  370. /**
  371. * Data structure:
  372. * 'Test #' => [
  373. * expected output of isSecure method
  374. * value of $_SERVER['HTTPS'],
  375. * <Name-Of-Offload-Header>,
  376. * value of $_SERVER[<Name-Of-Offload-Header>]
  377. * number of times config->getValue is expected to be called
  378. * ]
  379. */
  380. return [
  381. 'Test 1' => [true, 'on', 'HEADER_FROM_PROXY', 'https', 0],
  382. 'Test 2' => [true, 'off', 'HEADER_FROM_PROXY', 'https', 1],
  383. 'Test 3' => [true, 'any-string', 'HEADER_FROM_PROXY', 'https', 0],
  384. 'Test 4' => [true, 'on', 'HEADER_FROM_PROXY', 'http', 0],
  385. 'Test 5' => [false, 'off', 'HEADER_FROM_PROXY', 'http', 1],
  386. 'Test 6' => [true, 'any-string', 'HEADER_FROM_PROXY', 'http', 0],
  387. 'Test 7' => [true, 'on', 'HEADER_FROM_PROXY', 'any-string', 0],
  388. 'Test 8' => [false, 'off', 'HEADER_FROM_PROXY', 'any-string', 1],
  389. 'Test 9' => [true, 'any-string', 'HEADER_FROM_PROXY', 'any-string', 0],
  390. 'blank HTTPS with proxy set https' => [true, '', 'HEADER_FROM_PROXY', 'https', 1],
  391. 'blank HTTPS with proxy set http' => [false, '', 'HEADER_FROM_PROXY', 'http', 1],
  392. 'HTTPS off with HTTP_ prefixed proxy set to https' => [true, 'off', 'HTTP_HEADER_FROM_PROXY', 'https', 1],
  393. ];
  394. }
  395. /**
  396. * @dataProvider setPathInfoDataProvider
  397. * @param string $requestUri
  398. * @param string $basePath$
  399. * @param string $expected
  400. */
  401. public function testGetPathInfo($requestUri, $basePath, $expected)
  402. {
  403. $this->model = $this->getModel($requestUri);
  404. $this->model->setBaseUrl($basePath);
  405. $this->assertEquals($expected, $this->model->getPathInfo());
  406. $this->assertEquals($expected, $this->model->getOriginalPathInfo());
  407. }
  408. public function testSetPathInfo()
  409. {
  410. $requestUri = 'http://svr.com//module/route/mypage/myproduct?param1=1';
  411. $basePath = '/module/route/';
  412. $this->model = $this->getModel($requestUri);
  413. $this->model->setBaseUrl($basePath);
  414. $expected = '/mypage/myproduct';
  415. $this->assertEquals($expected, $this->model->getOriginalPathInfo());
  416. $this->model->setPathInfo('http://svr.com/something/route?param1=1');
  417. $this->assertEquals('http://svr.com/something/route?param1=1', $this->model->getPathInfo());
  418. $this->assertEquals($expected, $this->model->getOriginalPathInfo());
  419. }
  420. /**
  421. * @return array
  422. */
  423. public function setPathInfoDataProvider()
  424. {
  425. return [
  426. ['http://svr.com/', '', ''],
  427. ['http://svr.com', '', ''],
  428. ['http://svr.com?param1=1', '', ''],
  429. ['http://svr.com/?param1=1', '', '/'],
  430. ['http://svr.com?param1=1&param2=2', '', ''],
  431. ['http://svr.com/?param1=1&param2=2', '', '/'],
  432. ['http://svr.com/module', '', '/module'],
  433. ['http://svr.com/module/', '', '/module/'],
  434. ['http://svr.com/module/route', '', '/module/route'],
  435. ['http://svr.com/module/route/', '', '/module/route/'],
  436. ['http://svr.com/index.php', '/index.php', ''],
  437. ['http://svr.com/index.php/', '/index.php', '/'],
  438. ['http://svr.com/index.phpmodule', '/index.php', 'noroute'],
  439. ['http://svr.com/index.phpmodule/contact', '/index.php/', 'noroute'],
  440. ['http://svr.com//index.phpmodule/contact', 'index.php', 'noroute'],
  441. ['http://svr.com/index.phpmodule/contact/', '/index.php/', 'noroute'],
  442. ['http://svr.com//index.phpmodule/contact/', 'index.php', 'noroute'],
  443. ];
  444. }
  445. }