CurlTest.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  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.7.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\Http\Client\Adapter;
  16. use Cake\Http\Client\Adapter\Curl;
  17. use Cake\Http\Client\Exception\NetworkException;
  18. use Cake\Http\Client\Request;
  19. use Cake\Http\Client\Response;
  20. use Cake\TestSuite\TestCase;
  21. use Composer\CaBundle\CaBundle;
  22. /**
  23. * HTTP curl adapter test.
  24. */
  25. class CurlTest extends TestCase
  26. {
  27. /**
  28. * @var \Cake\Http\Client\Adapter\Curl
  29. */
  30. protected $curl;
  31. /**
  32. * @var string
  33. */
  34. protected $caFile;
  35. public function setUp(): void
  36. {
  37. parent::setUp();
  38. $this->skipIf(!function_exists('curl_init'), 'Skipping as ext/curl is not installed.');
  39. $this->curl = new Curl();
  40. $this->caFile = CaBundle::getBundledCaBundlePath();
  41. }
  42. /**
  43. * Test the send method
  44. */
  45. public function testSendLive(): void
  46. {
  47. $request = new Request('http://localhost', 'GET', [
  48. 'User-Agent' => 'CakePHP TestSuite',
  49. 'Cookie' => 'testing=value',
  50. ]);
  51. try {
  52. $responses = $this->curl->send($request, []);
  53. } catch (NetworkException) {
  54. $this->markTestSkipped('Could not connect to localhost, skipping');
  55. }
  56. $this->assertCount(1, $responses);
  57. /** @var \Cake\Http\Response $response */
  58. $response = $responses[0];
  59. $this->assertInstanceOf(Response::class, $response);
  60. $this->assertNotEmpty($response->getHeaders());
  61. }
  62. /**
  63. * Test the send method
  64. */
  65. public function testSendLiveResponseCheck(): void
  66. {
  67. $request = new Request('https://api.cakephp.org/3.0/', 'GET', [
  68. 'User-Agent' => 'CakePHP TestSuite',
  69. ]);
  70. try {
  71. $responses = $this->curl->send($request, []);
  72. } catch (NetworkException) {
  73. $this->markTestSkipped('Could not connect to api.cakephp.org, skipping');
  74. }
  75. $this->assertCount(1, $responses);
  76. /** @var \Cake\Http\Response $response */
  77. $response = $responses[0];
  78. $this->assertInstanceOf(Response::class, $response);
  79. $this->assertTrue($response->hasHeader('Date'));
  80. $this->assertTrue($response->hasHeader('Content-type'));
  81. $this->assertStringContainsString('<html', $response->getBody()->getContents());
  82. }
  83. /**
  84. * Test converting client options into curl ones.
  85. */
  86. public function testBuildOptionsGet(): void
  87. {
  88. $options = [
  89. 'timeout' => 5,
  90. ];
  91. $request = new Request(
  92. 'http://localhost/things',
  93. 'GET',
  94. ['Cookie' => 'testing=value']
  95. );
  96. $result = $this->curl->buildOptions($request, $options);
  97. $expected = [
  98. CURLOPT_URL => 'http://localhost/things',
  99. CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  100. CURLOPT_RETURNTRANSFER => true,
  101. CURLOPT_HEADER => true,
  102. CURLOPT_HTTPHEADER => [
  103. 'Cookie: testing=value',
  104. 'Connection: close',
  105. 'User-Agent: CakePHP',
  106. ],
  107. CURLOPT_HTTPGET => true,
  108. CURLOPT_TIMEOUT => 5,
  109. CURLOPT_CAINFO => $this->caFile,
  110. ];
  111. $this->assertSame($expected, $result);
  112. }
  113. /**
  114. * Test converting client options into curl ones.
  115. */
  116. public function testBuildOptionsGetWithBody(): void
  117. {
  118. $options = [
  119. 'timeout' => 5,
  120. ];
  121. $request = new Request(
  122. 'http://localhost/things',
  123. 'GET',
  124. ['Cookie' => 'testing=value'],
  125. '{"some":"body"}'
  126. );
  127. $result = $this->curl->buildOptions($request, $options);
  128. $expected = [
  129. CURLOPT_URL => 'http://localhost/things',
  130. CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  131. CURLOPT_RETURNTRANSFER => true,
  132. CURLOPT_HEADER => true,
  133. CURLOPT_HTTPHEADER => [
  134. 'Cookie: testing=value',
  135. 'Connection: close',
  136. 'User-Agent: CakePHP',
  137. ],
  138. CURLOPT_HTTPGET => true,
  139. CURLOPT_POSTFIELDS => '{"some":"body"}',
  140. CURLOPT_CUSTOMREQUEST => 'get',
  141. CURLOPT_TIMEOUT => 5,
  142. CURLOPT_CAINFO => $this->caFile,
  143. ];
  144. $this->assertSame($expected, $result);
  145. }
  146. /**
  147. * Test converting client options into curl ones.
  148. */
  149. public function testBuildOptionsPost(): void
  150. {
  151. $options = [];
  152. $request = new Request(
  153. 'http://localhost/things',
  154. 'POST',
  155. ['Cookie' => 'testing=value'],
  156. ['name' => 'cakephp', 'yes' => 1]
  157. );
  158. $result = $this->curl->buildOptions($request, $options);
  159. $expected = [
  160. CURLOPT_URL => 'http://localhost/things',
  161. CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  162. CURLOPT_RETURNTRANSFER => true,
  163. CURLOPT_HEADER => true,
  164. CURLOPT_HTTPHEADER => [
  165. 'Cookie: testing=value',
  166. 'Connection: close',
  167. 'User-Agent: CakePHP',
  168. 'Content-Type: application/x-www-form-urlencoded',
  169. ],
  170. CURLOPT_POST => true,
  171. CURLOPT_POSTFIELDS => 'name=cakephp&yes=1',
  172. CURLOPT_CAINFO => $this->caFile,
  173. ];
  174. $this->assertSame($expected, $result);
  175. }
  176. /**
  177. * Test converting client options into curl ones.
  178. */
  179. public function testBuildOptionsPut(): void
  180. {
  181. $options = [];
  182. $request = new Request(
  183. 'http://localhost/things',
  184. 'PUT',
  185. ['Cookie' => 'testing=value']
  186. );
  187. $result = $this->curl->buildOptions($request, $options);
  188. $expected = [
  189. CURLOPT_URL => 'http://localhost/things',
  190. CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  191. CURLOPT_RETURNTRANSFER => true,
  192. CURLOPT_HEADER => true,
  193. CURLOPT_HTTPHEADER => [
  194. 'Cookie: testing=value',
  195. 'Connection: close',
  196. 'User-Agent: CakePHP',
  197. ],
  198. CURLOPT_POST => true,
  199. CURLOPT_CUSTOMREQUEST => 'PUT',
  200. CURLOPT_CAINFO => $this->caFile,
  201. ];
  202. $this->assertSame($expected, $result);
  203. }
  204. /**
  205. * Test converting client options into curl ones.
  206. */
  207. public function testBuildOptionsJsonPost(): void
  208. {
  209. $options = [];
  210. $content = json_encode(['a' => 1, 'b' => 2]);
  211. $request = new Request(
  212. 'http://localhost/things',
  213. 'POST',
  214. ['Content-type' => 'application/json'],
  215. $content
  216. );
  217. $result = $this->curl->buildOptions($request, $options);
  218. $expected = [
  219. CURLOPT_URL => 'http://localhost/things',
  220. CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  221. CURLOPT_RETURNTRANSFER => true,
  222. CURLOPT_HEADER => true,
  223. CURLOPT_HTTPHEADER => [
  224. 'Content-type: application/json',
  225. 'Connection: close',
  226. 'User-Agent: CakePHP',
  227. ],
  228. CURLOPT_POST => true,
  229. CURLOPT_POSTFIELDS => $content,
  230. CURLOPT_CAINFO => $this->caFile,
  231. ];
  232. $this->assertSame($expected, $result);
  233. }
  234. /**
  235. * Test converting client options into curl ones.
  236. */
  237. public function testBuildOptionsSsl(): void
  238. {
  239. $options = [
  240. 'ssl_verify_host' => true,
  241. 'ssl_verify_peer' => true,
  242. 'ssl_verify_peer_name' => true,
  243. // These options do nothing in curl.
  244. 'ssl_verify_depth' => 9000,
  245. 'ssl_allow_self_signed' => false,
  246. ];
  247. $request = new Request('http://localhost/things', 'GET');
  248. $result = $this->curl->buildOptions($request, $options);
  249. $expected = [
  250. CURLOPT_URL => 'http://localhost/things',
  251. CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  252. CURLOPT_RETURNTRANSFER => true,
  253. CURLOPT_HEADER => true,
  254. CURLOPT_HTTPHEADER => [
  255. 'Connection: close',
  256. 'User-Agent: CakePHP',
  257. ],
  258. CURLOPT_HTTPGET => true,
  259. CURLOPT_SSL_VERIFYPEER => true,
  260. CURLOPT_SSL_VERIFYHOST => 2,
  261. CURLOPT_CAINFO => $this->caFile,
  262. ];
  263. $this->assertSame($expected, $result);
  264. }
  265. /**
  266. * Test converting client options into curl ones.
  267. */
  268. public function testBuildOptionsProxy(): void
  269. {
  270. $options = [
  271. 'proxy' => [
  272. 'proxy' => '127.0.0.1:8080',
  273. 'username' => 'frodo',
  274. 'password' => 'one_ring',
  275. ],
  276. ];
  277. $request = new Request('http://localhost/things', 'GET');
  278. $result = $this->curl->buildOptions($request, $options);
  279. $expected = [
  280. CURLOPT_URL => 'http://localhost/things',
  281. CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  282. CURLOPT_RETURNTRANSFER => true,
  283. CURLOPT_HEADER => true,
  284. CURLOPT_HTTPHEADER => [
  285. 'Connection: close',
  286. 'User-Agent: CakePHP',
  287. ],
  288. CURLOPT_HTTPGET => true,
  289. CURLOPT_CAINFO => $this->caFile,
  290. CURLOPT_PROXY => '127.0.0.1:8080',
  291. CURLOPT_PROXYUSERPWD => 'frodo:one_ring',
  292. ];
  293. $this->assertSame($expected, $result);
  294. }
  295. /**
  296. * Test converting client options into curl ones.
  297. */
  298. public function testBuildOptionsHead(): void
  299. {
  300. $options = [];
  301. $request = new Request(
  302. 'http://localhost/things',
  303. 'HEAD',
  304. ['Cookie' => 'testing=value']
  305. );
  306. $result = $this->curl->buildOptions($request, $options);
  307. $expected = [
  308. CURLOPT_URL => 'http://localhost/things',
  309. CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
  310. CURLOPT_RETURNTRANSFER => true,
  311. CURLOPT_HEADER => true,
  312. CURLOPT_HTTPHEADER => [
  313. 'Cookie: testing=value',
  314. 'Connection: close',
  315. 'User-Agent: CakePHP',
  316. ],
  317. CURLOPT_NOBODY => true,
  318. CURLOPT_CAINFO => $this->caFile,
  319. ];
  320. $this->assertSame($expected, $result);
  321. }
  322. /**
  323. * Test converting client options into curl ones.
  324. */
  325. public function testBuildOptionsCurlOptions(): void
  326. {
  327. $options = [
  328. 'curl' => [
  329. CURLOPT_USERAGENT => 'Super-secret',
  330. ],
  331. ];
  332. $request = new Request('http://localhost/things', 'GET');
  333. $request = $request->withProtocolVersion('1.0');
  334. $result = $this->curl->buildOptions($request, $options);
  335. $expected = [
  336. CURLOPT_URL => 'http://localhost/things',
  337. CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_0,
  338. CURLOPT_RETURNTRANSFER => true,
  339. CURLOPT_HEADER => true,
  340. CURLOPT_HTTPHEADER => [
  341. 'Connection: close',
  342. 'User-Agent: CakePHP',
  343. ],
  344. CURLOPT_HTTPGET => true,
  345. CURLOPT_CAINFO => $this->caFile,
  346. CURLOPT_USERAGENT => 'Super-secret',
  347. ];
  348. $this->assertSame($expected, $result);
  349. }
  350. /**
  351. * Test that an exception is raised when timed out.
  352. */
  353. public function testNetworkException(): void
  354. {
  355. $this->expectException(NetworkException::class);
  356. $this->expectExceptionMessageMatches('/(Could not resolve|Resolving timed out)/');
  357. $request = new Request('http://dummy/?sleep');
  358. $options = [
  359. 'timeout' => 2,
  360. ];
  361. $this->curl->send($request, $options);
  362. }
  363. /**
  364. * Test converting client options into curl ones.
  365. */
  366. public function testBuildOptionsProtocolVersion(): void
  367. {
  368. $this->skipIf(!defined('CURL_HTTP_VERSION_2TLS'), 'Requires libcurl 7.42');
  369. $options = [];
  370. $request = new Request('http://localhost/things', 'GET');
  371. $request = $request->withProtocolVersion('2');
  372. $result = $this->curl->buildOptions($request, $options);
  373. $this->assertSame(CURL_HTTP_VERSION_2TLS, $result[CURLOPT_HTTP_VERSION]);
  374. }
  375. }