ClientTest.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * Redistributions of files must retain the above copyright notice.
  8. *
  9. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  10. * @link https://cakephp.org CakePHP(tm) Project
  11. * @since 3.0.0
  12. * @license https://opensource.org/licenses/mit-license.php MIT License
  13. */
  14. namespace Cake\Test\TestCase\Http;
  15. use Cake\Http\Client;
  16. use Cake\Http\Client\Request;
  17. use Cake\Http\Client\Response;
  18. use Cake\Http\Cookie\Cookie;
  19. use Cake\Http\Cookie\CookieCollection;
  20. use Cake\TestSuite\TestCase;
  21. use InvalidArgumentException;
  22. /**
  23. * HTTP client test.
  24. */
  25. class ClientTest extends TestCase
  26. {
  27. /**
  28. * Test storing config options and modifying them.
  29. *
  30. * @return void
  31. */
  32. public function testConstructConfig()
  33. {
  34. $config = [
  35. 'scheme' => 'http',
  36. 'host' => 'example.org',
  37. ];
  38. $http = new Client($config);
  39. $result = $http->getConfig();
  40. foreach ($config as $key => $val) {
  41. $this->assertEquals($val, $result[$key]);
  42. }
  43. $result = $http->setConfig([
  44. 'auth' => ['username' => 'mark', 'password' => 'secret'],
  45. ]);
  46. $this->assertSame($result, $http);
  47. $result = $http->getConfig();
  48. $expected = [
  49. 'scheme' => 'http',
  50. 'host' => 'example.org',
  51. 'auth' => ['username' => 'mark', 'password' => 'secret'],
  52. 'protocolVersion' => '1.1',
  53. ];
  54. foreach ($expected as $key => $val) {
  55. $this->assertEquals($val, $result[$key]);
  56. }
  57. }
  58. /**
  59. * testAdapterInstanceCheck
  60. *
  61. * @return void
  62. */
  63. public function testAdapterInstanceCheck()
  64. {
  65. $this->expectException(InvalidArgumentException::class);
  66. $this->expectExceptionMessage('Adapter must be an instance of Cake\Http\Client\AdapterInterface');
  67. new Client(['adapter' => 'stdClass']);
  68. }
  69. /**
  70. * Data provider for buildUrl() tests
  71. *
  72. * @return array
  73. */
  74. public static function urlProvider()
  75. {
  76. return [
  77. [
  78. 'http://example.com/test.html',
  79. 'http://example.com/test.html',
  80. [],
  81. null,
  82. 'Null options',
  83. ],
  84. [
  85. 'http://example.com/test.html',
  86. 'http://example.com/test.html',
  87. [],
  88. [],
  89. 'Simple string',
  90. ],
  91. [
  92. 'http://example.com/test.html',
  93. '/test.html',
  94. [],
  95. ['host' => 'example.com'],
  96. 'host name option',
  97. ],
  98. [
  99. 'https://example.com/test.html',
  100. '/test.html',
  101. [],
  102. ['host' => 'example.com', 'scheme' => 'https'],
  103. 'HTTPS',
  104. ],
  105. [
  106. 'http://example.com:8080/test.html',
  107. '/test.html',
  108. [],
  109. ['host' => 'example.com', 'port' => '8080'],
  110. 'Non standard port',
  111. ],
  112. [
  113. 'http://example.com/test.html',
  114. '/test.html',
  115. [],
  116. ['host' => 'example.com', 'port' => '80'],
  117. 'standard port, does not display',
  118. ],
  119. [
  120. 'https://example.com/test.html',
  121. '/test.html',
  122. [],
  123. ['host' => 'example.com', 'scheme' => 'https', 'port' => '443'],
  124. 'standard port, does not display',
  125. ],
  126. [
  127. 'http://example.com/test.html',
  128. 'http://example.com/test.html',
  129. [],
  130. ['host' => 'example.com', 'scheme' => 'https'],
  131. 'options do not duplicate',
  132. ],
  133. [
  134. 'http://example.com/search?q=hi+there&cat%5Bid%5D%5B0%5D=2&cat%5Bid%5D%5B1%5D=3',
  135. 'http://example.com/search',
  136. ['q' => 'hi there', 'cat' => ['id' => [2, 3]]],
  137. [],
  138. 'query string data.',
  139. ],
  140. [
  141. 'http://example.com/search?q=hi+there&id=12',
  142. 'http://example.com/search?q=hi+there',
  143. ['id' => '12'],
  144. [],
  145. 'query string data with some already on the url.',
  146. ],
  147. [
  148. 'http://example.com/test.html',
  149. '//test.html',
  150. [],
  151. [
  152. 'scheme' => 'http',
  153. 'host' => 'example.com',
  154. 'protocolRelative' => false,
  155. ],
  156. 'url with a double slash',
  157. ],
  158. [
  159. 'http://example.com/test.html',
  160. '//example.com/test.html',
  161. [],
  162. [
  163. 'scheme' => 'http',
  164. 'protocolRelative' => true,
  165. ],
  166. 'protocol relative url',
  167. ],
  168. ];
  169. }
  170. /**
  171. * @dataProvider urlProvider
  172. */
  173. public function testBuildUrl($expected, $url, $query, $opts)
  174. {
  175. $http = new Client();
  176. $result = $http->buildUrl($url, $query, $opts);
  177. $this->assertEquals($expected, $result);
  178. }
  179. /**
  180. * test simple get request with headers & cookies.
  181. *
  182. * @return void
  183. */
  184. public function testGetSimpleWithHeadersAndCookies()
  185. {
  186. $response = new Response();
  187. $headers = [
  188. 'User-Agent' => 'Cake',
  189. 'Connection' => 'close',
  190. 'Content-Type' => 'application/x-www-form-urlencoded',
  191. ];
  192. $cookies = [
  193. 'split' => 'value',
  194. ];
  195. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  196. ->setMethods(['send'])
  197. ->getMock();
  198. $mock->expects($this->once())
  199. ->method('send')
  200. ->with($this->callback(function ($request) use ($headers) {
  201. $this->assertInstanceOf('Cake\Http\Client\Request', $request);
  202. $this->assertEquals(Request::METHOD_GET, $request->getMethod());
  203. $this->assertSame('2', $request->getProtocolVersion());
  204. $this->assertEquals('http://cakephp.org/test.html', $request->getUri() . '');
  205. $this->assertEquals('split=value', $request->getHeaderLine('Cookie'));
  206. $this->assertEquals($headers['Content-Type'], $request->getHeaderLine('content-type'));
  207. $this->assertEquals($headers['Connection'], $request->getHeaderLine('connection'));
  208. return true;
  209. }))
  210. ->will($this->returnValue([$response]));
  211. $http = new Client(['adapter' => $mock, 'protocolVersion' => '2']);
  212. $result = $http->get('http://cakephp.org/test.html', [], [
  213. 'headers' => $headers,
  214. 'cookies' => $cookies,
  215. ]);
  216. $this->assertSame($result, $response);
  217. }
  218. /**
  219. * test get request with no data
  220. *
  221. * @return void
  222. */
  223. public function testGetNoData()
  224. {
  225. $response = new Response();
  226. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  227. ->setMethods(['send'])
  228. ->getMock();
  229. $mock->expects($this->once())
  230. ->method('send')
  231. ->with($this->callback(function ($request) {
  232. $this->assertEquals(Request::METHOD_GET, $request->getMethod());
  233. $this->assertEmpty($request->getHeaderLine('Content-Type'), 'Should have no content-type set');
  234. $this->assertEquals(
  235. 'http://cakephp.org/search',
  236. $request->getUri() . ''
  237. );
  238. return true;
  239. }))
  240. ->will($this->returnValue([$response]));
  241. $http = new Client([
  242. 'host' => 'cakephp.org',
  243. 'adapter' => $mock,
  244. ]);
  245. $result = $http->get('/search');
  246. $this->assertSame($result, $response);
  247. }
  248. /**
  249. * test get request with querystring data
  250. *
  251. * @return void
  252. */
  253. public function testGetQuerystring()
  254. {
  255. $response = new Response();
  256. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  257. ->setMethods(['send'])
  258. ->getMock();
  259. $mock->expects($this->once())
  260. ->method('send')
  261. ->with($this->callback(function ($request) {
  262. $this->assertEquals(Request::METHOD_GET, $request->getMethod());
  263. $this->assertEquals(
  264. 'http://cakephp.org/search?q=hi+there&Category%5Bid%5D%5B0%5D=2&Category%5Bid%5D%5B1%5D=3',
  265. $request->getUri() . ''
  266. );
  267. return true;
  268. }))
  269. ->will($this->returnValue([$response]));
  270. $http = new Client([
  271. 'host' => 'cakephp.org',
  272. 'adapter' => $mock,
  273. ]);
  274. $result = $http->get('/search', [
  275. 'q' => 'hi there',
  276. 'Category' => ['id' => [2, 3]],
  277. ]);
  278. $this->assertSame($result, $response);
  279. }
  280. /**
  281. * test get request with string of query data.
  282. *
  283. * @return void
  284. */
  285. public function testGetQuerystringString()
  286. {
  287. $response = new Response();
  288. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  289. ->setMethods(['send'])
  290. ->getMock();
  291. $mock->expects($this->once())
  292. ->method('send')
  293. ->with($this->callback(function ($request) {
  294. $this->assertEquals(
  295. 'http://cakephp.org/search?q=hi+there&Category%5Bid%5D%5B0%5D=2&Category%5Bid%5D%5B1%5D=3',
  296. $request->getUri() . ''
  297. );
  298. return true;
  299. }))
  300. ->will($this->returnValue([$response]));
  301. $http = new Client([
  302. 'host' => 'cakephp.org',
  303. 'adapter' => $mock,
  304. ]);
  305. $data = [
  306. 'q' => 'hi there',
  307. 'Category' => ['id' => [2, 3]],
  308. ];
  309. $result = $http->get('/search', http_build_query($data));
  310. $this->assertSame($response, $result);
  311. }
  312. /**
  313. * Test a GET with a request body. Services like
  314. * elasticsearch use this feature.
  315. *
  316. * @return void
  317. */
  318. public function testGetWithContent()
  319. {
  320. $response = new Response();
  321. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  322. ->setMethods(['send'])
  323. ->getMock();
  324. $mock->expects($this->once())
  325. ->method('send')
  326. ->with($this->callback(function ($request) {
  327. $this->assertEquals(Request::METHOD_GET, $request->getMethod());
  328. $this->assertEquals('http://cakephp.org/search', '' . $request->getUri());
  329. $this->assertEquals('some data', '' . $request->getBody());
  330. return true;
  331. }))
  332. ->will($this->returnValue([$response]));
  333. $http = new Client([
  334. 'host' => 'cakephp.org',
  335. 'adapter' => $mock,
  336. ]);
  337. $result = $http->get('/search', [
  338. '_content' => 'some data',
  339. ]);
  340. $this->assertSame($result, $response);
  341. }
  342. /**
  343. * Test invalid authentication types throw exceptions.
  344. *
  345. * @return void
  346. */
  347. public function testInvalidAuthenticationType()
  348. {
  349. $this->expectException(\Cake\Core\Exception\Exception::class);
  350. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  351. ->setMethods(['send'])
  352. ->getMock();
  353. $mock->expects($this->never())
  354. ->method('send');
  355. $http = new Client([
  356. 'host' => 'cakephp.org',
  357. 'adapter' => $mock,
  358. ]);
  359. $result = $http->get('/', [], [
  360. 'auth' => ['type' => 'horribly broken'],
  361. ]);
  362. }
  363. /**
  364. * Test setting basic authentication with get
  365. *
  366. * @return void
  367. */
  368. public function testGetWithAuthenticationAndProxy()
  369. {
  370. $response = new Response();
  371. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  372. ->setMethods(['send'])
  373. ->getMock();
  374. $headers = [
  375. 'Authorization' => 'Basic ' . base64_encode('mark:secret'),
  376. 'Proxy-Authorization' => 'Basic ' . base64_encode('mark:pass'),
  377. ];
  378. $mock->expects($this->once())
  379. ->method('send')
  380. ->with($this->callback(function ($request) use ($headers) {
  381. $this->assertEquals(Request::METHOD_GET, $request->getMethod());
  382. $this->assertEquals('http://cakephp.org/', '' . $request->getUri());
  383. $this->assertEquals($headers['Authorization'], $request->getHeaderLine('Authorization'));
  384. $this->assertEquals($headers['Proxy-Authorization'], $request->getHeaderLine('Proxy-Authorization'));
  385. return true;
  386. }))
  387. ->will($this->returnValue([$response]));
  388. $http = new Client([
  389. 'host' => 'cakephp.org',
  390. 'adapter' => $mock,
  391. ]);
  392. $result = $http->get('/', [], [
  393. 'auth' => ['username' => 'mark', 'password' => 'secret'],
  394. 'proxy' => ['username' => 'mark', 'password' => 'pass'],
  395. ]);
  396. $this->assertSame($result, $response);
  397. }
  398. /**
  399. * Test authentication adapter that mutates request.
  400. *
  401. * @group deprecated
  402. * @return void
  403. */
  404. public function testAuthenticationWithMutation()
  405. {
  406. $this->deprecated(function () {
  407. static::setAppNamespace();
  408. $response = new Response();
  409. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  410. ->setMethods(['send'])
  411. ->getMock();
  412. $headers = [
  413. 'Authorization' => 'Bearer abc123',
  414. 'Proxy-Authorization' => 'Bearer abc123',
  415. ];
  416. $mock->expects($this->once())
  417. ->method('send')
  418. ->with($this->callback(function ($request) use ($headers) {
  419. $this->assertEquals(Request::METHOD_GET, $request->getMethod());
  420. $this->assertEquals('http://cakephp.org/', '' . $request->getUri());
  421. $this->assertEquals($headers['Authorization'], $request->getHeaderLine('Authorization'));
  422. $this->assertEquals($headers['Proxy-Authorization'], $request->getHeaderLine('Proxy-Authorization'));
  423. return true;
  424. }))
  425. ->will($this->returnValue([$response]));
  426. $http = new Client([
  427. 'host' => 'cakephp.org',
  428. 'adapter' => $mock,
  429. ]);
  430. $result = $http->get('/', [], [
  431. 'auth' => ['type' => 'TestApp\Http\CompatAuth'],
  432. 'proxy' => ['type' => 'TestApp\Http\CompatAuth'],
  433. ]);
  434. $this->assertSame($result, $response);
  435. });
  436. }
  437. /**
  438. * Return a list of HTTP methods.
  439. *
  440. * @return array
  441. */
  442. public static function methodProvider()
  443. {
  444. return [
  445. [Request::METHOD_GET],
  446. [Request::METHOD_POST],
  447. [Request::METHOD_PUT],
  448. [Request::METHOD_DELETE],
  449. [Request::METHOD_PATCH],
  450. [Request::METHOD_OPTIONS],
  451. [Request::METHOD_TRACE],
  452. ];
  453. }
  454. /**
  455. * test simple POST request.
  456. *
  457. * @dataProvider methodProvider
  458. * @return void
  459. */
  460. public function testMethodsSimple($method)
  461. {
  462. $response = new Response();
  463. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  464. ->setMethods(['send'])
  465. ->getMock();
  466. $mock->expects($this->once())
  467. ->method('send')
  468. ->with($this->callback(function ($request) use ($method) {
  469. $this->assertInstanceOf('Cake\Http\Client\Request', $request);
  470. $this->assertEquals($method, $request->getMethod());
  471. $this->assertEquals('http://cakephp.org/projects/add', '' . $request->getUri());
  472. return true;
  473. }))
  474. ->will($this->returnValue([$response]));
  475. $http = new Client([
  476. 'host' => 'cakephp.org',
  477. 'adapter' => $mock,
  478. ]);
  479. $result = $http->{$method}('/projects/add');
  480. $this->assertSame($result, $response);
  481. }
  482. /**
  483. * Provider for testing the type option.
  484. *
  485. * @return array
  486. */
  487. public static function typeProvider()
  488. {
  489. return [
  490. ['application/json', 'application/json'],
  491. ['json', 'application/json'],
  492. ['xml', 'application/xml'],
  493. ['application/xml', 'application/xml'],
  494. ];
  495. }
  496. /**
  497. * Test that using the 'type' option sets the correct headers
  498. *
  499. * @dataProvider typeProvider
  500. * @return void
  501. */
  502. public function testPostWithTypeKey($type, $mime)
  503. {
  504. $response = new Response();
  505. $data = 'some data';
  506. $headers = [
  507. 'Content-Type' => $mime,
  508. 'Accept' => $mime,
  509. ];
  510. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  511. ->setMethods(['send'])
  512. ->getMock();
  513. $mock->expects($this->once())
  514. ->method('send')
  515. ->with($this->callback(function ($request) use ($headers) {
  516. $this->assertEquals(Request::METHOD_POST, $request->getMethod());
  517. $this->assertEquals($headers['Content-Type'], $request->getHeaderLine('Content-Type'));
  518. $this->assertEquals($headers['Accept'], $request->getHeaderLine('Accept'));
  519. return true;
  520. }))
  521. ->will($this->returnValue([$response]));
  522. $http = new Client([
  523. 'host' => 'cakephp.org',
  524. 'adapter' => $mock,
  525. ]);
  526. $http->post('/projects/add', $data, ['type' => $type]);
  527. }
  528. /**
  529. * Test that string payloads with no content type have a default content-type set.
  530. *
  531. * @return void
  532. */
  533. public function testPostWithStringDataDefaultsToFormEncoding()
  534. {
  535. $response = new Response();
  536. $data = 'some=value&more=data';
  537. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  538. ->setMethods(['send'])
  539. ->getMock();
  540. $mock->expects($this->any())
  541. ->method('send')
  542. ->with($this->callback(function ($request) use ($data) {
  543. $this->assertEquals($data, '' . $request->getBody());
  544. $this->assertEquals('application/x-www-form-urlencoded', $request->getHeaderLine('content-type'));
  545. return true;
  546. }))
  547. ->will($this->returnValue([$response]));
  548. $http = new Client([
  549. 'host' => 'cakephp.org',
  550. 'adapter' => $mock,
  551. ]);
  552. $http->post('/projects/add', $data);
  553. $http->put('/projects/add', $data);
  554. $http->delete('/projects/add', $data);
  555. }
  556. /**
  557. * Test that exceptions are raised on invalid types.
  558. *
  559. * @return void
  560. */
  561. public function testExceptionOnUnknownType()
  562. {
  563. $this->expectException(\Cake\Core\Exception\Exception::class);
  564. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  565. ->setMethods(['send'])
  566. ->getMock();
  567. $mock->expects($this->never())
  568. ->method('send');
  569. $http = new Client([
  570. 'host' => 'cakephp.org',
  571. 'adapter' => $mock,
  572. ]);
  573. $http->post('/projects/add', 'it works', ['type' => 'invalid']);
  574. }
  575. /**
  576. * Test that Client stores cookies
  577. *
  578. * @return void
  579. */
  580. public function testCookieStorage()
  581. {
  582. $adapter = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  583. ->setMethods(['send'])
  584. ->getMock();
  585. $headers = [
  586. 'HTTP/1.0 200 Ok',
  587. 'Set-Cookie: first=1',
  588. 'Set-Cookie: expiring=now; Expires=Wed, 09-Jun-1999 10:18:14 GMT',
  589. ];
  590. $response = new Response($headers, '');
  591. $adapter->expects($this->at(0))
  592. ->method('send')
  593. ->will($this->returnValue([$response]));
  594. $http = new Client([
  595. 'host' => 'cakephp.org',
  596. 'adapter' => $adapter,
  597. ]);
  598. $http->get('/projects');
  599. $cookies = $http->cookies();
  600. $this->assertCount(1, $cookies);
  601. $this->assertTrue($cookies->has('first'));
  602. $this->assertFalse($cookies->has('expiring'));
  603. }
  604. /**
  605. * Test cookieJar config option.
  606. *
  607. * @return void
  608. */
  609. public function testCookieJar()
  610. {
  611. $jar = new CookieCollection();
  612. $http = new Client([
  613. 'cookieJar' => $jar,
  614. ]);
  615. $this->assertSame($jar, $http->cookies());
  616. }
  617. /**
  618. * Test addCookie() method.
  619. *
  620. * @return void
  621. */
  622. public function testAddCookie()
  623. {
  624. $client = new Client();
  625. $cookie = new Cookie('foo', '', null, '/', 'example.com');
  626. $this->assertFalse($client->cookies()->has('foo'));
  627. $client->addCookie($cookie);
  628. $this->assertTrue($client->cookies()->has('foo'));
  629. }
  630. /**
  631. * Test addCookie() method without a domain.
  632. *
  633. * @return void
  634. */
  635. public function testAddCookieWithoutDomain()
  636. {
  637. $this->expectException(\InvalidArgumentException::class);
  638. $this->expectExceptionMessage('Cookie must have a domain and a path set.');
  639. $client = new Client();
  640. $cookie = new Cookie('foo', '', null, '/', '');
  641. $this->assertFalse($client->cookies()->has('foo'));
  642. $client->addCookie($cookie);
  643. $this->assertTrue($client->cookies()->has('foo'));
  644. }
  645. /**
  646. * Test addCookie() method without a path.
  647. *
  648. * @return void
  649. */
  650. public function testAddCookieWithoutPath()
  651. {
  652. $this->expectException(\InvalidArgumentException::class);
  653. $this->expectExceptionMessage('Cookie must have a domain and a path set.');
  654. $client = new Client();
  655. $cookie = new Cookie('foo', '', null, '', 'example.com');
  656. $this->assertFalse($client->cookies()->has('foo'));
  657. $client->addCookie($cookie);
  658. $this->assertTrue($client->cookies()->has('foo'));
  659. }
  660. /**
  661. * test head request with querystring data
  662. *
  663. * @return void
  664. */
  665. public function testHeadQuerystring()
  666. {
  667. $response = new Response();
  668. $mock = $this->getMockBuilder('Cake\Http\Client\Adapter\Stream')
  669. ->setMethods(['send'])
  670. ->getMock();
  671. $mock->expects($this->once())
  672. ->method('send')
  673. ->with($this->callback(function ($request) {
  674. $this->assertInstanceOf('Cake\Http\Client\Request', $request);
  675. $this->assertEquals(Request::METHOD_HEAD, $request->getMethod());
  676. $this->assertEquals('http://cakephp.org/search?q=hi+there', '' . $request->getUri());
  677. return true;
  678. }))
  679. ->will($this->returnValue([$response]));
  680. $http = new Client([
  681. 'host' => 'cakephp.org',
  682. 'adapter' => $mock,
  683. ]);
  684. $result = $http->head('/search', [
  685. 'q' => 'hi there',
  686. ]);
  687. $this->assertSame($result, $response);
  688. }
  689. /**
  690. * test redirects
  691. *
  692. * @return void
  693. */
  694. public function testRedirects()
  695. {
  696. $url = 'http://cakephp.org';
  697. $adapter = $this->getMockBuilder(Client\Adapter\Stream::class)
  698. ->setMethods(['send'])
  699. ->getMock();
  700. $redirect = new Response([
  701. 'HTTP/1.0 301',
  702. 'Location: http://cakephp.org/redirect1?foo=bar',
  703. 'Set-Cookie: redirect1=true;path=/',
  704. ]);
  705. $adapter->expects($this->at(0))
  706. ->method('send')
  707. ->with(
  708. $this->callback(function ($request) use ($url) {
  709. $this->assertInstanceOf(Request::class, $request);
  710. $this->assertEquals($url, $request->getUri());
  711. return true;
  712. }),
  713. $this->callback(function ($options) {
  714. $this->assertArrayNotHasKey('redirect', $options);
  715. return true;
  716. })
  717. )
  718. ->willReturn([$redirect]);
  719. $redirect2 = new Response([
  720. 'HTTP/1.0 301',
  721. 'Location: /redirect2#foo',
  722. 'Set-Cookie: redirect2=true;path=/',
  723. ]);
  724. $adapter->expects($this->at(1))
  725. ->method('send')
  726. ->with(
  727. $this->callback(function ($request) use ($url) {
  728. $this->assertInstanceOf(Request::class, $request);
  729. $this->assertEquals($url . '/redirect1?foo=bar', $request->getUri());
  730. return true;
  731. }),
  732. $this->callback(function ($options) {
  733. $this->assertArrayNotHasKey('redirect', $options);
  734. return true;
  735. })
  736. )
  737. ->willReturn([$redirect2]);
  738. $response = new Response([
  739. 'HTTP/1.0 200',
  740. ]);
  741. $adapter->expects($this->at(2))
  742. ->method('send')
  743. ->with($this->callback(function ($request) use ($url) {
  744. $this->assertInstanceOf(Request::class, $request);
  745. $this->assertEquals($url . '/redirect2#foo', $request->getUri());
  746. return true;
  747. }))
  748. ->willReturn([$response]);
  749. $client = new Client([
  750. 'adapter' => $adapter,
  751. ]);
  752. $result = $client->send(new Request($url), [
  753. 'redirect' => 10,
  754. ]);
  755. $this->assertInstanceOf(Response::class, $result);
  756. $this->assertTrue($result->isOk());
  757. $cookies = $client->cookies();
  758. $this->assertTrue($cookies->has('redirect1'));
  759. $this->assertTrue($cookies->has('redirect2'));
  760. }
  761. /**
  762. * test redirect across sub domains
  763. *
  764. * @return void
  765. */
  766. public function testRedirectDifferentSubDomains()
  767. {
  768. $adapter = $this->getMockBuilder(Client\Adapter\Stream::class)
  769. ->setMethods(['send'])
  770. ->getMock();
  771. $url = 'http://auth.example.org';
  772. $redirect = new Response([
  773. 'HTTP/1.0 301',
  774. 'Location: http://backstage.example.org',
  775. ]);
  776. $adapter->expects($this->at(0))
  777. ->method('send')
  778. ->willReturn([$redirect]);
  779. $response = new Response([
  780. 'HTTP/1.0 200',
  781. ]);
  782. $adapter->expects($this->at(1))
  783. ->method('send')
  784. ->with($this->callback(function ($request) {
  785. $this->assertSame('http://backstage.example.org', (string)$request->getUri());
  786. $this->assertSame('session=backend', $request->getHeaderLine('Cookie'));
  787. return true;
  788. }))
  789. ->willReturn([$response]);
  790. $client = new Client([
  791. 'adapter' => $adapter,
  792. ]);
  793. $client->addCookie(new Cookie('session', 'backend', null, '/', 'backstage.example.org'));
  794. $client->addCookie(new Cookie('session', 'authz', null, '/', 'auth.example.org'));
  795. $result = $client->send(new Request($url), [
  796. 'redirect' => 10,
  797. ]);
  798. $this->assertInstanceOf(Response::class, $result);
  799. $this->assertSame($response, $result);
  800. }
  801. }