StreamHandlerTest.php 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  1. <?php
  2. namespace GuzzleHttp\Test\Handler;
  3. use GuzzleHttp\Exception\ConnectException;
  4. use GuzzleHttp\Handler\StreamHandler;
  5. use GuzzleHttp\Psr7;
  6. use GuzzleHttp\Psr7\Request;
  7. use GuzzleHttp\Psr7\Response;
  8. use GuzzleHttp\Psr7\FnStream;
  9. use GuzzleHttp\RequestOptions;
  10. use GuzzleHttp\Tests\Server;
  11. use GuzzleHttp\TransferStats;
  12. use Psr\Http\Message\ResponseInterface;
  13. use PHPUnit\Framework\TestCase;
  14. /**
  15. * @covers \GuzzleHttp\Handler\StreamHandler
  16. */
  17. class StreamHandlerTest extends TestCase
  18. {
  19. private function queueRes()
  20. {
  21. Server::flush();
  22. Server::enqueue([
  23. new Response(200, [
  24. 'Foo' => 'Bar',
  25. 'Content-Length' => 8,
  26. ], 'hi there')
  27. ]);
  28. }
  29. public function testReturnsResponseForSuccessfulRequest()
  30. {
  31. $this->queueRes();
  32. $handler = new StreamHandler();
  33. $response = $handler(
  34. new Request('GET', Server::$url, ['Foo' => 'Bar']),
  35. []
  36. )->wait();
  37. $this->assertSame(200, $response->getStatusCode());
  38. $this->assertSame('OK', $response->getReasonPhrase());
  39. $this->assertSame('Bar', $response->getHeaderLine('Foo'));
  40. $this->assertSame('8', $response->getHeaderLine('Content-Length'));
  41. $this->assertSame('hi there', (string) $response->getBody());
  42. $sent = Server::received()[0];
  43. $this->assertSame('GET', $sent->getMethod());
  44. $this->assertSame('/', $sent->getUri()->getPath());
  45. $this->assertSame('127.0.0.1:8126', $sent->getHeaderLine('Host'));
  46. $this->assertSame('Bar', $sent->getHeaderLine('foo'));
  47. }
  48. /**
  49. * @expectedException \GuzzleHttp\Exception\RequestException
  50. */
  51. public function testAddsErrorToResponse()
  52. {
  53. $handler = new StreamHandler();
  54. $handler(
  55. new Request('GET', 'http://localhost:123'),
  56. ['timeout' => 0.01]
  57. )->wait();
  58. }
  59. public function testStreamAttributeKeepsStreamOpen()
  60. {
  61. $this->queueRes();
  62. $handler = new StreamHandler();
  63. $request = new Request(
  64. 'PUT',
  65. Server::$url . 'foo?baz=bar',
  66. ['Foo' => 'Bar'],
  67. 'test'
  68. );
  69. $response = $handler($request, ['stream' => true])->wait();
  70. $this->assertSame(200, $response->getStatusCode());
  71. $this->assertSame('OK', $response->getReasonPhrase());
  72. $this->assertSame('8', $response->getHeaderLine('Content-Length'));
  73. $body = $response->getBody();
  74. $stream = $body->detach();
  75. $this->assertInternalType('resource', $stream);
  76. $this->assertSame('http', stream_get_meta_data($stream)['wrapper_type']);
  77. $this->assertSame('hi there', stream_get_contents($stream));
  78. fclose($stream);
  79. $sent = Server::received()[0];
  80. $this->assertSame('PUT', $sent->getMethod());
  81. $this->assertSame('http://127.0.0.1:8126/foo?baz=bar', (string) $sent->getUri());
  82. $this->assertSame('Bar', $sent->getHeaderLine('Foo'));
  83. $this->assertSame('test', (string) $sent->getBody());
  84. }
  85. public function testDrainsResponseIntoTempStream()
  86. {
  87. $this->queueRes();
  88. $handler = new StreamHandler();
  89. $request = new Request('GET', Server::$url);
  90. $response = $handler($request, [])->wait();
  91. $body = $response->getBody();
  92. $stream = $body->detach();
  93. $this->assertSame('php://temp', stream_get_meta_data($stream)['uri']);
  94. $this->assertSame('hi', fread($stream, 2));
  95. fclose($stream);
  96. }
  97. public function testDrainsResponseIntoSaveToBody()
  98. {
  99. $r = fopen('php://temp', 'r+');
  100. $this->queueRes();
  101. $handler = new StreamHandler();
  102. $request = new Request('GET', Server::$url);
  103. $response = $handler($request, ['sink' => $r])->wait();
  104. $body = $response->getBody()->detach();
  105. $this->assertSame('php://temp', stream_get_meta_data($body)['uri']);
  106. $this->assertSame('hi', fread($body, 2));
  107. $this->assertSame(' there', stream_get_contents($r));
  108. fclose($r);
  109. }
  110. public function testDrainsResponseIntoSaveToBodyAtPath()
  111. {
  112. $tmpfname = tempnam('/tmp', 'save_to_path');
  113. $this->queueRes();
  114. $handler = new StreamHandler();
  115. $request = new Request('GET', Server::$url);
  116. $response = $handler($request, ['sink' => $tmpfname])->wait();
  117. $body = $response->getBody();
  118. $this->assertSame($tmpfname, $body->getMetadata('uri'));
  119. $this->assertSame('hi', $body->read(2));
  120. $body->close();
  121. unlink($tmpfname);
  122. }
  123. public function testDrainsResponseIntoSaveToBodyAtNonExistentPath()
  124. {
  125. $tmpfname = tempnam('/tmp', 'save_to_path');
  126. unlink($tmpfname);
  127. $this->queueRes();
  128. $handler = new StreamHandler();
  129. $request = new Request('GET', Server::$url);
  130. $response = $handler($request, ['sink' => $tmpfname])->wait();
  131. $body = $response->getBody();
  132. $this->assertSame($tmpfname, $body->getMetadata('uri'));
  133. $this->assertSame('hi', $body->read(2));
  134. $body->close();
  135. unlink($tmpfname);
  136. }
  137. public function testDrainsResponseAndReadsOnlyContentLengthBytes()
  138. {
  139. Server::flush();
  140. Server::enqueue([
  141. new Response(200, [
  142. 'Foo' => 'Bar',
  143. 'Content-Length' => 8,
  144. ], 'hi there... This has way too much data!')
  145. ]);
  146. $handler = new StreamHandler();
  147. $request = new Request('GET', Server::$url);
  148. $response = $handler($request, [])->wait();
  149. $body = $response->getBody();
  150. $stream = $body->detach();
  151. $this->assertSame('hi there', stream_get_contents($stream));
  152. fclose($stream);
  153. }
  154. public function testDoesNotDrainWhenHeadRequest()
  155. {
  156. Server::flush();
  157. // Say the content-length is 8, but return no response.
  158. Server::enqueue([
  159. new Response(200, [
  160. 'Foo' => 'Bar',
  161. 'Content-Length' => 8,
  162. ], '')
  163. ]);
  164. $handler = new StreamHandler();
  165. $request = new Request('HEAD', Server::$url);
  166. $response = $handler($request, [])->wait();
  167. $body = $response->getBody();
  168. $stream = $body->detach();
  169. $this->assertSame('', stream_get_contents($stream));
  170. fclose($stream);
  171. }
  172. public function testAutomaticallyDecompressGzip()
  173. {
  174. Server::flush();
  175. $content = gzencode('test');
  176. Server::enqueue([
  177. new Response(200, [
  178. 'Content-Encoding' => 'gzip',
  179. 'Content-Length' => strlen($content),
  180. ], $content)
  181. ]);
  182. $handler = new StreamHandler();
  183. $request = new Request('GET', Server::$url);
  184. $response = $handler($request, ['decode_content' => true])->wait();
  185. $this->assertSame('test', (string) $response->getBody());
  186. $this->assertFalse($response->hasHeader('content-encoding'));
  187. $this->assertTrue(!$response->hasHeader('content-length') || $response->getHeaderLine('content-length') == $response->getBody()->getSize());
  188. }
  189. public function testReportsOriginalSizeAndContentEncodingAfterDecoding()
  190. {
  191. Server::flush();
  192. $content = gzencode('test');
  193. Server::enqueue([
  194. new Response(200, [
  195. 'Content-Encoding' => 'gzip',
  196. 'Content-Length' => strlen($content),
  197. ], $content)
  198. ]);
  199. $handler = new StreamHandler();
  200. $request = new Request('GET', Server::$url);
  201. $response = $handler($request, ['decode_content' => true])->wait();
  202. $this->assertSame(
  203. 'gzip',
  204. $response->getHeaderLine('x-encoded-content-encoding')
  205. );
  206. $this->assertSame(
  207. strlen($content),
  208. (int) $response->getHeaderLine('x-encoded-content-length')
  209. );
  210. }
  211. public function testDoesNotForceGzipDecode()
  212. {
  213. Server::flush();
  214. $content = gzencode('test');
  215. Server::enqueue([
  216. new Response(200, [
  217. 'Content-Encoding' => 'gzip',
  218. 'Content-Length' => strlen($content),
  219. ], $content)
  220. ]);
  221. $handler = new StreamHandler();
  222. $request = new Request('GET', Server::$url);
  223. $response = $handler($request, ['decode_content' => false])->wait();
  224. $this->assertSame($content, (string) $response->getBody());
  225. $this->assertSame('gzip', $response->getHeaderLine('content-encoding'));
  226. $this->assertEquals(strlen($content), $response->getHeaderLine('content-length'));
  227. }
  228. public function testProtocolVersion()
  229. {
  230. $this->queueRes();
  231. $handler = new StreamHandler();
  232. $request = new Request('GET', Server::$url, [], null, '1.0');
  233. $handler($request, []);
  234. $this->assertSame('1.0', Server::received()[0]->getProtocolVersion());
  235. }
  236. protected function getSendResult(array $opts)
  237. {
  238. $this->queueRes();
  239. $handler = new StreamHandler();
  240. $opts['stream'] = true;
  241. $request = new Request('GET', Server::$url);
  242. return $handler($request, $opts)->wait();
  243. }
  244. /**
  245. * @expectedException \GuzzleHttp\Exception\ConnectException
  246. * @expectedExceptionMessage Connection refused
  247. */
  248. public function testAddsProxy()
  249. {
  250. $this->getSendResult(['proxy' => '127.0.0.1:8125']);
  251. }
  252. public function testAddsProxyByProtocol()
  253. {
  254. $url = str_replace('http', 'tcp', Server::$url);
  255. // Workaround until #1823 is fixed properly
  256. $url = rtrim($url, '/');
  257. $res = $this->getSendResult(['proxy' => ['http' => $url]]);
  258. $opts = stream_context_get_options($res->getBody()->detach());
  259. $this->assertSame($url, $opts['http']['proxy']);
  260. }
  261. public function testAddsProxyButHonorsNoProxy()
  262. {
  263. $url = str_replace('http', 'tcp', Server::$url);
  264. $res = $this->getSendResult(['proxy' => [
  265. 'http' => $url,
  266. 'no' => ['*']
  267. ]]);
  268. $opts = stream_context_get_options($res->getBody()->detach());
  269. $this->assertArrayNotHasKey('proxy', $opts['http']);
  270. }
  271. public function testAddsTimeout()
  272. {
  273. $res = $this->getSendResult(['stream' => true, 'timeout' => 200]);
  274. $opts = stream_context_get_options($res->getBody()->detach());
  275. $this->assertEquals(200, $opts['http']['timeout']);
  276. }
  277. /**
  278. * @expectedException \GuzzleHttp\Exception\RequestException
  279. * @expectedExceptionMessage SSL CA bundle not found: /does/not/exist
  280. */
  281. public function testVerifiesVerifyIsValidIfPath()
  282. {
  283. $this->getSendResult(['verify' => '/does/not/exist']);
  284. }
  285. public function testVerifyCanBeDisabled()
  286. {
  287. $handler = $this->getSendResult(['verify' => false]);
  288. $this->assertInstanceOf('GuzzleHttp\Psr7\Response', $handler);
  289. }
  290. /**
  291. * @expectedException \GuzzleHttp\Exception\RequestException
  292. * @expectedExceptionMessage SSL certificate not found: /does/not/exist
  293. */
  294. public function testVerifiesCertIfValidPath()
  295. {
  296. $this->getSendResult(['cert' => '/does/not/exist']);
  297. }
  298. public function testVerifyCanBeSetToPath()
  299. {
  300. $path = $path = \GuzzleHttp\default_ca_bundle();
  301. $res = $this->getSendResult(['verify' => $path]);
  302. $opts = stream_context_get_options($res->getBody()->detach());
  303. $this->assertTrue($opts['ssl']['verify_peer']);
  304. $this->assertTrue($opts['ssl']['verify_peer_name']);
  305. $this->assertSame($path, $opts['ssl']['cafile']);
  306. $this->assertFileExists($opts['ssl']['cafile']);
  307. }
  308. public function testUsesSystemDefaultBundle()
  309. {
  310. $path = $path = \GuzzleHttp\default_ca_bundle();
  311. $res = $this->getSendResult(['verify' => true]);
  312. $opts = stream_context_get_options($res->getBody()->detach());
  313. if (PHP_VERSION_ID < 50600) {
  314. $this->assertSame($path, $opts['ssl']['cafile']);
  315. } else {
  316. $this->assertArrayNotHasKey('cafile', $opts['ssl']);
  317. }
  318. }
  319. /**
  320. * @expectedException \InvalidArgumentException
  321. * @expectedExceptionMessage Invalid verify request option
  322. */
  323. public function testEnsuresVerifyOptionIsValid()
  324. {
  325. $this->getSendResult(['verify' => 10]);
  326. }
  327. public function testCanSetPasswordWhenSettingCert()
  328. {
  329. $path = __FILE__;
  330. $res = $this->getSendResult(['cert' => [$path, 'foo']]);
  331. $opts = stream_context_get_options($res->getBody()->detach());
  332. $this->assertSame($path, $opts['ssl']['local_cert']);
  333. $this->assertSame('foo', $opts['ssl']['passphrase']);
  334. }
  335. public function testDebugAttributeWritesToStream()
  336. {
  337. $this->queueRes();
  338. $f = fopen('php://temp', 'w+');
  339. $this->getSendResult(['debug' => $f]);
  340. fseek($f, 0);
  341. $contents = stream_get_contents($f);
  342. $this->assertContains('<GET http://127.0.0.1:8126/> [CONNECT]', $contents);
  343. $this->assertContains('<GET http://127.0.0.1:8126/> [FILE_SIZE_IS]', $contents);
  344. $this->assertContains('<GET http://127.0.0.1:8126/> [PROGRESS]', $contents);
  345. }
  346. public function testDebugAttributeWritesStreamInfoToBuffer()
  347. {
  348. $called = false;
  349. $this->queueRes();
  350. $buffer = fopen('php://temp', 'r+');
  351. $this->getSendResult([
  352. 'progress' => function () use (&$called) { $called = true; },
  353. 'debug' => $buffer,
  354. ]);
  355. fseek($buffer, 0);
  356. $contents = stream_get_contents($buffer);
  357. $this->assertContains('<GET http://127.0.0.1:8126/> [CONNECT]', $contents);
  358. $this->assertContains('<GET http://127.0.0.1:8126/> [FILE_SIZE_IS] message: "Content-Length: 8"', $contents);
  359. $this->assertContains('<GET http://127.0.0.1:8126/> [PROGRESS] bytes_max: "8"', $contents);
  360. $this->assertTrue($called);
  361. }
  362. public function testEmitsProgressInformation()
  363. {
  364. $called = [];
  365. $this->queueRes();
  366. $this->getSendResult([
  367. 'progress' => function () use (&$called) {
  368. $called[] = func_get_args();
  369. },
  370. ]);
  371. $this->assertNotEmpty($called);
  372. $this->assertEquals(8, $called[0][0]);
  373. $this->assertEquals(0, $called[0][1]);
  374. }
  375. public function testEmitsProgressInformationAndDebugInformation()
  376. {
  377. $called = [];
  378. $this->queueRes();
  379. $buffer = fopen('php://memory', 'w+');
  380. $this->getSendResult([
  381. 'debug' => $buffer,
  382. 'progress' => function () use (&$called) {
  383. $called[] = func_get_args();
  384. },
  385. ]);
  386. $this->assertNotEmpty($called);
  387. $this->assertEquals(8, $called[0][0]);
  388. $this->assertEquals(0, $called[0][1]);
  389. rewind($buffer);
  390. $this->assertNotEmpty(stream_get_contents($buffer));
  391. fclose($buffer);
  392. }
  393. public function testPerformsShallowMergeOfCustomContextOptions()
  394. {
  395. $res = $this->getSendResult([
  396. 'stream_context' => [
  397. 'http' => [
  398. 'request_fulluri' => true,
  399. 'method' => 'HEAD',
  400. ],
  401. 'socket' => [
  402. 'bindto' => '127.0.0.1:0',
  403. ],
  404. 'ssl' => [
  405. 'verify_peer' => false,
  406. ],
  407. ],
  408. ]);
  409. $opts = stream_context_get_options($res->getBody()->detach());
  410. $this->assertSame('HEAD', $opts['http']['method']);
  411. $this->assertTrue($opts['http']['request_fulluri']);
  412. $this->assertSame('127.0.0.1:0', $opts['socket']['bindto']);
  413. $this->assertFalse($opts['ssl']['verify_peer']);
  414. }
  415. /**
  416. * @expectedException \InvalidArgumentException
  417. * @expectedExceptionMessage stream_context must be an array
  418. */
  419. public function testEnsuresThatStreamContextIsAnArray()
  420. {
  421. $this->getSendResult(['stream_context' => 'foo']);
  422. }
  423. public function testDoesNotAddContentTypeByDefault()
  424. {
  425. $this->queueRes();
  426. $handler = new StreamHandler();
  427. $request = new Request('PUT', Server::$url, ['Content-Length' => 3], 'foo');
  428. $handler($request, []);
  429. $req = Server::received()[0];
  430. $this->assertEquals('', $req->getHeaderLine('Content-Type'));
  431. $this->assertEquals(3, $req->getHeaderLine('Content-Length'));
  432. }
  433. public function testAddsContentLengthByDefault()
  434. {
  435. $this->queueRes();
  436. $handler = new StreamHandler();
  437. $request = new Request('PUT', Server::$url, [], 'foo');
  438. $handler($request, []);
  439. $req = Server::received()[0];
  440. $this->assertEquals(3, $req->getHeaderLine('Content-Length'));
  441. }
  442. public function testAddsContentLengthEvenWhenEmpty()
  443. {
  444. $this->queueRes();
  445. $handler = new StreamHandler();
  446. $request = new Request('PUT', Server::$url, [], '');
  447. $handler($request, []);
  448. $req = Server::received()[0];
  449. $this->assertEquals(0, $req->getHeaderLine('Content-Length'));
  450. }
  451. public function testSupports100Continue()
  452. {
  453. Server::flush();
  454. $response = new Response(200, ['Test' => 'Hello', 'Content-Length' => '4'], 'test');
  455. Server::enqueue([$response]);
  456. $request = new Request('PUT', Server::$url, ['Expect' => '100-Continue'], 'test');
  457. $handler = new StreamHandler();
  458. $response = $handler($request, [])->wait();
  459. $this->assertSame(200, $response->getStatusCode());
  460. $this->assertSame('Hello', $response->getHeaderLine('Test'));
  461. $this->assertSame('4', $response->getHeaderLine('Content-Length'));
  462. $this->assertSame('test', (string) $response->getBody());
  463. }
  464. public function testDoesSleep()
  465. {
  466. $response = new response(200);
  467. Server::enqueue([$response]);
  468. $a = new StreamHandler();
  469. $request = new Request('GET', Server::$url);
  470. $s = microtime(true);
  471. $a($request, ['delay' => 0.1])->wait();
  472. $this->assertGreaterThan(0.0001, microtime(true) - $s);
  473. }
  474. /**
  475. * @expectedException \InvalidArgumentException
  476. */
  477. public function testEnsuresOnHeadersIsCallable()
  478. {
  479. $req = new Request('GET', Server::$url);
  480. $handler = new StreamHandler();
  481. $handler($req, ['on_headers' => 'error!']);
  482. }
  483. /**
  484. * @expectedException \GuzzleHttp\Exception\RequestException
  485. * @expectedExceptionMessage An error was encountered during the on_headers event
  486. * @expectedExceptionMessage test
  487. */
  488. public function testRejectsPromiseWhenOnHeadersFails()
  489. {
  490. Server::flush();
  491. Server::enqueue([
  492. new Response(200, ['X-Foo' => 'bar'], 'abc 123')
  493. ]);
  494. $req = new Request('GET', Server::$url);
  495. $handler = new StreamHandler();
  496. $promise = $handler($req, [
  497. 'on_headers' => function () {
  498. throw new \Exception('test');
  499. }
  500. ]);
  501. $promise->wait();
  502. }
  503. public function testSuccessfullyCallsOnHeadersBeforeWritingToSink()
  504. {
  505. Server::flush();
  506. Server::enqueue([
  507. new Response(200, ['X-Foo' => 'bar'], 'abc 123')
  508. ]);
  509. $req = new Request('GET', Server::$url);
  510. $got = null;
  511. $stream = Psr7\stream_for();
  512. $stream = FnStream::decorate($stream, [
  513. 'write' => function ($data) use ($stream, &$got) {
  514. $this->assertNotNull($got);
  515. return $stream->write($data);
  516. }
  517. ]);
  518. $handler = new StreamHandler();
  519. $promise = $handler($req, [
  520. 'sink' => $stream,
  521. 'on_headers' => function (ResponseInterface $res) use (&$got) {
  522. $got = $res;
  523. $this->assertSame('bar', $res->getHeaderLine('X-Foo'));
  524. }
  525. ]);
  526. $response = $promise->wait();
  527. $this->assertSame(200, $response->getStatusCode());
  528. $this->assertSame('bar', $response->getHeaderLine('X-Foo'));
  529. $this->assertSame('abc 123', (string) $response->getBody());
  530. }
  531. public function testInvokesOnStatsOnSuccess()
  532. {
  533. Server::flush();
  534. Server::enqueue([new Psr7\Response(200)]);
  535. $req = new Psr7\Request('GET', Server::$url);
  536. $gotStats = null;
  537. $handler = new StreamHandler();
  538. $promise = $handler($req, [
  539. 'on_stats' => function (TransferStats $stats) use (&$gotStats) {
  540. $gotStats = $stats;
  541. }
  542. ]);
  543. $response = $promise->wait();
  544. $this->assertSame(200, $response->getStatusCode());
  545. $this->assertSame(200, $gotStats->getResponse()->getStatusCode());
  546. $this->assertSame(
  547. Server::$url,
  548. (string) $gotStats->getEffectiveUri()
  549. );
  550. $this->assertSame(
  551. Server::$url,
  552. (string) $gotStats->getRequest()->getUri()
  553. );
  554. $this->assertGreaterThan(0, $gotStats->getTransferTime());
  555. }
  556. public function testInvokesOnStatsOnError()
  557. {
  558. $req = new Psr7\Request('GET', 'http://127.0.0.1:123');
  559. $gotStats = null;
  560. $handler = new StreamHandler();
  561. $promise = $handler($req, [
  562. 'connect_timeout' => 0.001,
  563. 'timeout' => 0.001,
  564. 'on_stats' => function (TransferStats $stats) use (&$gotStats) {
  565. $gotStats = $stats;
  566. }
  567. ]);
  568. $promise->wait(false);
  569. $this->assertFalse($gotStats->hasResponse());
  570. $this->assertSame(
  571. 'http://127.0.0.1:123',
  572. (string) $gotStats->getEffectiveUri()
  573. );
  574. $this->assertSame(
  575. 'http://127.0.0.1:123',
  576. (string) $gotStats->getRequest()->getUri()
  577. );
  578. $this->assertInternalType('float', $gotStats->getTransferTime());
  579. $this->assertInstanceOf(
  580. ConnectException::class,
  581. $gotStats->getHandlerErrorData()
  582. );
  583. }
  584. public function testStreamIgnoresZeroTimeout()
  585. {
  586. Server::flush();
  587. Server::enqueue([new Psr7\Response(200)]);
  588. $req = new Psr7\Request('GET', Server::$url);
  589. $gotStats = null;
  590. $handler = new StreamHandler();
  591. $promise = $handler($req, [
  592. 'connect_timeout' => 10,
  593. 'timeout' => 0
  594. ]);
  595. $response = $promise->wait();
  596. $this->assertSame(200, $response->getStatusCode());
  597. }
  598. public function testDrainsResponseAndReadsAllContentWhenContentLengthIsZero()
  599. {
  600. Server::flush();
  601. Server::enqueue([
  602. new Response(200, [
  603. 'Foo' => 'Bar',
  604. 'Content-Length' => '0',
  605. ], 'hi there... This has a lot of data!')
  606. ]);
  607. $handler = new StreamHandler();
  608. $request = new Request('GET', Server::$url);
  609. $response = $handler($request, [])->wait();
  610. $body = $response->getBody();
  611. $stream = $body->detach();
  612. $this->assertSame('hi there... This has a lot of data!', stream_get_contents($stream));
  613. fclose($stream);
  614. }
  615. public function testHonorsReadTimeout()
  616. {
  617. Server::flush();
  618. $handler = new StreamHandler();
  619. $response = $handler(
  620. new Request('GET', Server::$url . 'guzzle-server/read-timeout'),
  621. [
  622. RequestOptions::READ_TIMEOUT => 1,
  623. RequestOptions::STREAM => true,
  624. ]
  625. )->wait();
  626. $this->assertSame(200, $response->getStatusCode());
  627. $this->assertSame('OK', $response->getReasonPhrase());
  628. $body = $response->getBody()->detach();
  629. $line = fgets($body);
  630. $this->assertSame("sleeping 60 seconds ...\n", $line);
  631. $line = fgets($body);
  632. $this->assertFalse($line);
  633. $this->assertTrue(stream_get_meta_data($body)['timed_out']);
  634. $this->assertFalse(feof($body));
  635. }
  636. }