ClientTest.php 27 KB

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