'Bar', 'Baz' => 'bam', 'Content-Length' => 2, ], 'hi') ]); $stream = Psr7\Utils::streamFor(); $request = new Psr7\Request('PUT', Server::$url, [ 'Hi' => ' 123', 'Content-Length' => '7' ], 'testing'); $f = new Handler\CurlFactory(3); $result = $f->create($request, ['sink' => $stream]); self::assertInstanceOf(EasyHandle::class, $result); if (\PHP_VERSION_ID >= 80000) { self::assertInstanceOf(\CurlHandle::class, $result->handle); } else { self::assertIsResource($result->handle); } self::assertIsArray($result->headers); self::assertSame($stream, $result->sink); \curl_close($result->handle); self::assertSame('PUT', $_SERVER['_curl'][\CURLOPT_CUSTOMREQUEST]); self::assertSame( 'http://127.0.0.1:8126/', $_SERVER['_curl'][\CURLOPT_URL] ); // Sends via post fields when the request is small enough self::assertSame('testing', $_SERVER['_curl'][\CURLOPT_POSTFIELDS]); self::assertEquals(0, $_SERVER['_curl'][\CURLOPT_RETURNTRANSFER]); self::assertEquals(0, $_SERVER['_curl'][\CURLOPT_HEADER]); self::assertSame(150, $_SERVER['_curl'][\CURLOPT_CONNECTTIMEOUT]); self::assertInstanceOf('Closure', $_SERVER['_curl'][\CURLOPT_HEADERFUNCTION]); if (\defined('CURLOPT_PROTOCOLS')) { self::assertSame( \CURLPROTO_HTTP | \CURLPROTO_HTTPS, $_SERVER['_curl'][\CURLOPT_PROTOCOLS] ); } self::assertContains('Expect:', $_SERVER['_curl'][\CURLOPT_HTTPHEADER]); self::assertContains('Accept:', $_SERVER['_curl'][\CURLOPT_HTTPHEADER]); self::assertContains('Content-Type:', $_SERVER['_curl'][\CURLOPT_HTTPHEADER]); self::assertContains('Hi: 123', $_SERVER['_curl'][\CURLOPT_HTTPHEADER]); self::assertContains('Host: 127.0.0.1:8126', $_SERVER['_curl'][\CURLOPT_HTTPHEADER]); } public function testSendsHeadRequests() { Server::flush(); Server::enqueue([new Psr7\Response()]); $a = new Handler\CurlMultiHandler(); $response = $a(new Psr7\Request('HEAD', Server::$url), []); $response->wait(); self::assertTrue($_SERVER['_curl'][\CURLOPT_NOBODY]); $checks = [\CURLOPT_READFUNCTION, \CURLOPT_FILE, \CURLOPT_INFILE]; foreach ($checks as $check) { self::assertArrayNotHasKey($check, $_SERVER['_curl']); } self::assertEquals('HEAD', Server::received()[0]->getMethod()); } public function testCanAddCustomCurlOptions() { Server::flush(); Server::enqueue([new Psr7\Response()]); $a = new Handler\CurlMultiHandler(); $req = new Psr7\Request('GET', Server::$url); $a($req, ['curl' => [\CURLOPT_LOW_SPEED_LIMIT => 10]]); self::assertEquals(10, $_SERVER['_curl'][\CURLOPT_LOW_SPEED_LIMIT]); } public function testCanChangeCurlOptions() { Server::flush(); Server::enqueue([new Psr7\Response()]); $a = new Handler\CurlMultiHandler(); $req = new Psr7\Request('GET', Server::$url); $a($req, ['curl' => [\CURLOPT_HTTP_VERSION => \CURL_HTTP_VERSION_1_0]]); self::assertEquals(\CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][\CURLOPT_HTTP_VERSION]); } public function testValidatesVerify() { $f = new Handler\CurlFactory(3); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('SSL CA bundle not found: /does/not/exist'); $f->create(new Psr7\Request('GET', Server::$url), ['verify' => '/does/not/exist']); } public function testCanSetVerifyToFile() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', 'http://foo.com'), ['verify' => __FILE__]); self::assertEquals(__FILE__, $_SERVER['_curl'][\CURLOPT_CAINFO]); self::assertEquals(2, $_SERVER['_curl'][\CURLOPT_SSL_VERIFYHOST]); self::assertTrue($_SERVER['_curl'][\CURLOPT_SSL_VERIFYPEER]); } public function testCanSetVerifyToDir() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', 'http://foo.com'), ['verify' => __DIR__]); self::assertEquals(__DIR__, $_SERVER['_curl'][\CURLOPT_CAPATH]); self::assertEquals(2, $_SERVER['_curl'][\CURLOPT_SSL_VERIFYHOST]); self::assertTrue($_SERVER['_curl'][\CURLOPT_SSL_VERIFYPEER]); } public function testAddsVerifyAsTrue() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['verify' => true]); self::assertEquals(2, $_SERVER['_curl'][\CURLOPT_SSL_VERIFYHOST]); self::assertTrue($_SERVER['_curl'][\CURLOPT_SSL_VERIFYPEER]); self::assertArrayNotHasKey(\CURLOPT_CAINFO, $_SERVER['_curl']); } public function testCanDisableVerify() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['verify' => false]); self::assertEquals(0, $_SERVER['_curl'][\CURLOPT_SSL_VERIFYHOST]); self::assertFalse($_SERVER['_curl'][\CURLOPT_SSL_VERIFYPEER]); } public function testAddsProxy() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['proxy' => 'http://bar.com']); self::assertEquals('http://bar.com', $_SERVER['_curl'][\CURLOPT_PROXY]); } public function testAddsViaScheme() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), [ 'proxy' => ['http' => 'http://bar.com', 'https' => 'https://t'], ]); self::assertEquals('http://bar.com', $_SERVER['_curl'][\CURLOPT_PROXY]); $this->checkNoProxyForHost('http://test.test.com', ['test.test.com'], false); $this->checkNoProxyForHost('http://test.test.com', ['.test.com'], false); $this->checkNoProxyForHost('http://test.test.com', ['*.test.com'], true); $this->checkNoProxyForHost('http://test.test.com', ['*'], false); $this->checkNoProxyForHost('http://127.0.0.1', ['127.0.0.*'], true); } private function checkNoProxyForHost($url, $noProxy, $assertUseProxy) { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', $url), [ 'proxy' => [ 'http' => 'http://bar.com', 'https' => 'https://t', 'no' => $noProxy ], ]); if ($assertUseProxy) { self::assertArrayHasKey(\CURLOPT_PROXY, $_SERVER['_curl']); } else { self::assertArrayNotHasKey(\CURLOPT_PROXY, $_SERVER['_curl']); } } public function testUsesProxy() { Server::flush(); Server::enqueue([ new Psr7\Response(200, [ 'Foo' => 'Bar', 'Baz' => 'bam', 'Content-Length' => 2, ], 'hi') ]); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('GET', 'http://www.example.com', [], null, '1.0'); $promise = $handler($request, [ 'proxy' => Server::$url ]); $response = $promise->wait(); self::assertSame(200, $response->getStatusCode()); self::assertSame('Bar', $response->getHeaderLine('Foo')); self::assertSame('2', $response->getHeaderLine('Content-Length')); self::assertSame('hi', (string) $response->getBody()); } public function testValidatesSslKey() { $f = new Handler\CurlFactory(3); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('SSL private key not found: /does/not/exist'); $f->create(new Psr7\Request('GET', Server::$url), ['ssl_key' => '/does/not/exist']); } public function testAddsSslKey() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['ssl_key' => __FILE__]); self::assertEquals(__FILE__, $_SERVER['_curl'][\CURLOPT_SSLKEY]); } public function testAddsSslKeyWithPassword() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['ssl_key' => [__FILE__, 'test']]); self::assertEquals(__FILE__, $_SERVER['_curl'][\CURLOPT_SSLKEY]); self::assertEquals('test', $_SERVER['_curl'][\CURLOPT_SSLKEYPASSWD]); } public function testAddsSslKeyWhenUsingArraySyntaxButNoPassword() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['ssl_key' => [__FILE__]]); self::assertEquals(__FILE__, $_SERVER['_curl'][\CURLOPT_SSLKEY]); } public function testValidatesCert() { $f = new Handler\CurlFactory(3); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('SSL certificate not found: /does/not/exist'); $f->create(new Psr7\Request('GET', Server::$url), ['cert' => '/does/not/exist']); } public function testAddsCert() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['cert' => __FILE__]); self::assertEquals(__FILE__, $_SERVER['_curl'][\CURLOPT_SSLCERT]); } public function testAddsCertWithPassword() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['cert' => [__FILE__, 'test']]); self::assertEquals(__FILE__, $_SERVER['_curl'][\CURLOPT_SSLCERT]); self::assertEquals('test', $_SERVER['_curl'][\CURLOPT_SSLCERTPASSWD]); } public function testAddsDerCert() { $certFile = tempnam(sys_get_temp_dir(), "mock_test_cert"); rename($certFile, $certFile .= '.der'); try { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['cert' => $certFile]); self::assertArrayHasKey(\CURLOPT_SSLCERTTYPE, $_SERVER['_curl']); self::assertEquals('DER', $_SERVER['_curl'][\CURLOPT_SSLCERTTYPE]); } finally { @\unlink($certFile); } } public function testAddsP12Cert() { $certFile = tempnam(sys_get_temp_dir(), "mock_test_cert"); rename($certFile, $certFile .= '.p12'); try { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), ['cert' => $certFile]); self::assertArrayHasKey(\CURLOPT_SSLCERTTYPE, $_SERVER['_curl']); self::assertEquals('P12', $_SERVER['_curl'][\CURLOPT_SSLCERTTYPE]); } finally { @\unlink($certFile); } } public function testValidatesProgress() { $f = new Handler\CurlFactory(3); $this->expectException(\InvalidArgumentException::class); $this->expectExceptionMessage('progress client option must be callable'); $f->create(new Psr7\Request('GET', Server::$url), ['progress' => 'foo']); } public function testEmitsDebugInfoToStream() { $res = \fopen('php://temp', 'r+'); Server::flush(); Server::enqueue([new Psr7\Response()]); $a = new Handler\CurlMultiHandler(); $response = $a(new Psr7\Request('HEAD', Server::$url), ['debug' => $res]); $response->wait(); \rewind($res); $output = \str_replace("\r", '', \stream_get_contents($res)); self::assertStringContainsString("> HEAD / HTTP/1.1", $output); self::assertStringContainsString("< HTTP/1.1 200", $output); \fclose($res); } public function testEmitsProgressToFunction() { Server::flush(); Server::enqueue([new Psr7\Response()]); $a = new Handler\CurlMultiHandler(); $called = []; $request = new Psr7\Request('HEAD', Server::$url); $response = $a($request, [ 'progress' => static function (...$args) use (&$called) { $called[] = $args; }, ]); $response->wait(); self::assertNotEmpty($called); foreach ($called as $call) { self::assertCount(4, $call); } } private function addDecodeResponse($withEncoding = true) { $content = \gzencode('test'); $headers = ['Content-Length' => \strlen($content)]; if ($withEncoding) { $headers['Content-Encoding'] = 'gzip'; } $response = new Psr7\Response(200, $headers, $content); Server::flush(); Server::enqueue([$response]); return $content; } public function testDecodesGzippedResponses() { $this->addDecodeResponse(); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('GET', Server::$url); $response = $handler($request, ['decode_content' => true]); $response = $response->wait(); self::assertEquals('test', (string) $response->getBody()); self::assertEquals('', $_SERVER['_curl'][\CURLOPT_ENCODING]); $sent = Server::received()[0]; self::assertFalse($sent->hasHeader('Accept-Encoding')); } public function testReportsOriginalSizeAndContentEncodingAfterDecoding() { $this->addDecodeResponse(); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('GET', Server::$url); $response = $handler($request, ['decode_content' => true]); $response = $response->wait(); self::assertSame( 'gzip', $response->getHeaderLine('x-encoded-content-encoding') ); self::assertSame( \strlen(\gzencode('test')), (int) $response->getHeaderLine('x-encoded-content-length') ); } public function testDecodesGzippedResponsesWithHeader() { $this->addDecodeResponse(); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('GET', Server::$url, ['Accept-Encoding' => 'gzip']); $response = $handler($request, ['decode_content' => true]); $response = $response->wait(); self::assertEquals('gzip', $_SERVER['_curl'][\CURLOPT_ENCODING]); $sent = Server::received()[0]; self::assertEquals('gzip', $sent->getHeaderLine('Accept-Encoding')); self::assertEquals('test', (string) $response->getBody()); self::assertFalse($response->hasHeader('content-encoding')); self::assertTrue( !$response->hasHeader('content-length') || $response->getHeaderLine('content-length') == $response->getBody()->getSize() ); } /** * https://github.com/guzzle/guzzle/issues/2799 */ public function testDecodesGzippedResponsesWithHeaderForHeadRequest() { $this->addDecodeResponse(); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('HEAD', Server::$url, ['Accept-Encoding' => 'gzip']); $response = $handler($request, ['decode_content' => true]); $response = $response->wait(); self::assertEquals('gzip', $_SERVER['_curl'][\CURLOPT_ENCODING]); $sent = Server::received()[0]; self::assertEquals('gzip', $sent->getHeaderLine('Accept-Encoding')); // Verify that the content-length matches the encoded size. self::assertTrue( !$response->hasHeader('content-length') || $response->getHeaderLine('content-length') == \strlen(\gzencode('test')) ); } public function testDoesNotForceDecode() { $content = $this->addDecodeResponse(); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('GET', Server::$url); $response = $handler($request, ['decode_content' => false]); $response = $response->wait(); $sent = Server::received()[0]; self::assertFalse($sent->hasHeader('Accept-Encoding')); self::assertEquals($content, (string) $response->getBody()); } public function testProtocolVersion() { Server::flush(); Server::enqueue([new Psr7\Response()]); $a = new Handler\CurlMultiHandler(); $request = new Psr7\Request('GET', Server::$url, [], null, '1.0'); $a($request, []); self::assertEquals(\CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][\CURLOPT_HTTP_VERSION]); } public function testSavesToStream() { $stream = \fopen('php://memory', 'r+'); $this->addDecodeResponse(); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('GET', Server::$url); $response = $handler($request, [ 'decode_content' => true, 'sink' => $stream, ]); $response->wait(); \rewind($stream); self::assertEquals('test', \stream_get_contents($stream)); } public function testSavesToGuzzleStream() { $stream = Psr7\Utils::streamFor(); $this->addDecodeResponse(); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('GET', Server::$url); $response = $handler($request, [ 'decode_content' => true, 'sink' => $stream, ]); $response->wait(); self::assertEquals('test', (string) $stream); } public function testSavesToFileOnDisk() { $tmpfile = \tempnam(\sys_get_temp_dir(), 'testfile'); $this->addDecodeResponse(); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('GET', Server::$url); $response = $handler($request, [ 'decode_content' => true, 'sink' => $tmpfile, ]); $response->wait(); self::assertStringEqualsFile($tmpfile, 'test'); @\unlink($tmpfile); } public function testDoesNotAddMultipleContentLengthHeaders() { $this->addDecodeResponse(); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('PUT', Server::$url, ['Content-Length' => 3], 'foo'); $response = $handler($request, []); $response->wait(); $sent = Server::received()[0]; self::assertEquals(3, $sent->getHeaderLine('Content-Length')); self::assertFalse($sent->hasHeader('Transfer-Encoding')); self::assertEquals('foo', (string) $sent->getBody()); } public function testSendsPostWithNoBodyOrDefaultContentType() { Server::flush(); Server::enqueue([new Psr7\Response()]); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('POST', Server::$url); $response = $handler($request, []); $response->wait(); $received = Server::received()[0]; self::assertEquals('POST', $received->getMethod()); self::assertFalse($received->hasHeader('content-type')); self::assertSame('0', $received->getHeaderLine('content-length')); } public function testFailsWhenCannotRewindRetryAfterNoResponse() { $factory = new Handler\CurlFactory(1); $stream = Psr7\Utils::streamFor('abc'); $stream->read(1); $stream = new Psr7\NoSeekStream($stream); $request = new Psr7\Request('PUT', Server::$url, [], $stream); $fn = static function ($request, $options) use (&$fn, $factory) { $easy = $factory->create($request, $options); return Handler\CurlFactory::finish($fn, $easy, $factory); }; $this->expectException(RequestException::class); $this->expectExceptionMessage('but attempting to rewind the request body failed'); $fn($request, [])->wait(); } public function testRetriesWhenBodyCanBeRewound() { $callHandler = $called = false; $fn = static function ($r, $options) use (&$callHandler) { $callHandler = true; return P\Create::promiseFor(new Psr7\Response()); }; $bd = Psr7\FnStream::decorate(Psr7\Utils::streamFor('test'), [ 'tell' => static function () { return 1; }, 'rewind' => static function () use (&$called) { $called = true; } ]); $factory = new Handler\CurlFactory(1); $req = new Psr7\Request('PUT', Server::$url, [], $bd); $easy = $factory->create($req, []); $res = Handler\CurlFactory::finish($fn, $easy, $factory); $res = $res->wait(); self::assertTrue($callHandler); self::assertTrue($called); self::assertEquals('200', $res->getStatusCode()); } public function testFailsWhenRetryMoreThanThreeTimes() { $factory = new Handler\CurlFactory(1); $call = 0; $fn = static function ($request, $options) use (&$mock, &$call, $factory) { $call++; $easy = $factory->create($request, $options); return Handler\CurlFactory::finish($mock, $easy, $factory); }; $mock = new Handler\MockHandler([$fn, $fn, $fn]); $p = $mock(new Psr7\Request('PUT', Server::$url, [], 'test'), []); $p->wait(false); self::assertEquals(3, $call); $this->expectException(RequestException::class); $this->expectExceptionMessage('The cURL request was retried 3 times'); $p->wait(true); } public function testHandles100Continue() { Server::flush(); Server::enqueue([ new Psr7\Response(200, ['Test' => 'Hello', 'Content-Length' => 4], 'test'), ]); $request = new Psr7\Request('PUT', Server::$url, [ 'Expect' => '100-Continue' ], 'test'); $handler = new Handler\CurlMultiHandler(); $response = $handler($request, [])->wait(); self::assertSame(200, $response->getStatusCode()); self::assertSame('OK', $response->getReasonPhrase()); self::assertSame('Hello', $response->getHeaderLine('Test')); self::assertSame('4', $response->getHeaderLine('Content-Length')); self::assertSame('test', (string) $response->getBody()); } public function testCreatesConnectException() { $m = new \ReflectionMethod(CurlFactory::class, 'finishError'); $m->setAccessible(true); $factory = new Handler\CurlFactory(1); $easy = $factory->create(new Psr7\Request('GET', Server::$url), []); $easy->errno = \CURLE_COULDNT_CONNECT; $response = $m->invoke( null, static function () { }, $easy, $factory ); $this->expectException(ConnectException::class); $response->wait(); } public function testAddsTimeouts() { $f = new Handler\CurlFactory(3); $f->create(new Psr7\Request('GET', Server::$url), [ 'timeout' => 0.1, 'connect_timeout' => 0.2 ]); self::assertEquals(100, $_SERVER['_curl'][\CURLOPT_TIMEOUT_MS]); self::assertEquals(200, $_SERVER['_curl'][\CURLOPT_CONNECTTIMEOUT_MS]); } public function testAddsStreamingBody() { $f = new Handler\CurlFactory(3); $bd = Psr7\FnStream::decorate(Psr7\Utils::streamFor('foo'), [ 'getSize' => static function () { return null; } ]); $request = new Psr7\Request('PUT', Server::$url, [], $bd); $f->create($request, []); self::assertEquals(1, $_SERVER['_curl'][\CURLOPT_UPLOAD]); self::assertIsCallable($_SERVER['_curl'][\CURLOPT_READFUNCTION]); } public function testEnsuresDirExistsBeforeThrowingWarning() { $f = new Handler\CurlFactory(3); $this->expectException(\RuntimeException::class); $this->expectExceptionMessage('Directory /does/not/exist/so does not exist for sink value of /does/not/exist/so/error.txt'); $f->create(new Psr7\Request('GET', Server::$url), [ 'sink' => '/does/not/exist/so/error.txt' ]); } public function testClosesIdleHandles() { $f = new Handler\CurlFactory(3); $req = new Psr7\Request('GET', Server::$url); $easy = $f->create($req, []); $h1 = $easy->handle; $f->release($easy); self::assertCount(1, Helpers::readObjectAttribute($f, 'handles')); $easy = $f->create($req, []); self::assertSame($easy->handle, $h1); $easy2 = $f->create($req, []); $easy3 = $f->create($req, []); $easy4 = $f->create($req, []); $f->release($easy); self::assertCount(1, Helpers::readObjectAttribute($f, 'handles')); $f->release($easy2); self::assertCount(2, Helpers::readObjectAttribute($f, 'handles')); $f->release($easy3); self::assertCount(3, Helpers::readObjectAttribute($f, 'handles')); $f->release($easy4); self::assertCount(3, Helpers::readObjectAttribute($f, 'handles')); } public function testRejectsPromiseWhenCreateResponseFails() { Server::flush(); Server::enqueueRaw(999, "Incorrect", ['X-Foo' => 'bar'], 'abc 123'); $req = new Psr7\Request('GET', Server::$url); $handler = new Handler\CurlHandler(); $promise = $handler($req, []); $this->expectException(\GuzzleHttp\Exception\RequestException::class); $this->expectExceptionMessage('An error was encountered while creating the response'); $promise->wait(); } public function testEnsuresOnHeadersIsCallable() { $req = new Psr7\Request('GET', Server::$url); $handler = new Handler\CurlHandler(); $this->expectException(\InvalidArgumentException::class); $handler($req, ['on_headers' => 'error!']); } public function testRejectsPromiseWhenOnHeadersFails() { Server::flush(); Server::enqueue([ new Psr7\Response(200, ['X-Foo' => 'bar'], 'abc 123') ]); $req = new Psr7\Request('GET', Server::$url); $handler = new Handler\CurlHandler(); $promise = $handler($req, [ 'on_headers' => static function () { throw new \Exception('test'); } ]); $this->expectException(RequestException::class); $this->expectExceptionMessage('An error was encountered during the on_headers event'); $promise->wait(); } public function testSuccessfullyCallsOnHeadersBeforeWritingToSink() { Server::flush(); Server::enqueue([ new Psr7\Response(200, ['X-Foo' => 'bar'], 'abc 123') ]); $req = new Psr7\Request('GET', Server::$url); $got = null; $stream = Psr7\Utils::streamFor(); $stream = Psr7\FnStream::decorate($stream, [ 'write' => static function ($data) use ($stream, &$got) { self::assertNotNull($got); return $stream->write($data); } ]); $handler = new Handler\CurlHandler(); $promise = $handler($req, [ 'sink' => $stream, 'on_headers' => static function (ResponseInterface $res) use (&$got) { $got = $res; self::assertEquals('bar', $res->getHeaderLine('X-Foo')); } ]); $response = $promise->wait(); self::assertSame(200, $response->getStatusCode()); self::assertSame('bar', $response->getHeaderLine('X-Foo')); self::assertSame('abc 123', (string) $response->getBody()); } public function testInvokesOnStatsOnSuccess() { Server::flush(); Server::enqueue([new Psr7\Response(200)]); $req = new Psr7\Request('GET', Server::$url); $gotStats = null; $handler = new Handler\CurlHandler(); $promise = $handler($req, [ 'on_stats' => static function (TransferStats $stats) use (&$gotStats) { $gotStats = $stats; } ]); $response = $promise->wait(); self::assertSame(200, $response->getStatusCode()); self::assertSame(200, $gotStats->getResponse()->getStatusCode()); self::assertSame( Server::$url, (string) $gotStats->getEffectiveUri() ); self::assertSame( Server::$url, (string) $gotStats->getRequest()->getUri() ); self::assertGreaterThan(0, $gotStats->getTransferTime()); self::assertArrayHasKey('appconnect_time', $gotStats->getHandlerStats()); } public function testInvokesOnStatsOnError() { $req = new Psr7\Request('GET', 'http://127.0.0.1:123'); $gotStats = null; $handler = new Handler\CurlHandler(); $promise = $handler($req, [ 'connect_timeout' => 0.001, 'timeout' => 0.001, 'on_stats' => static function (TransferStats $stats) use (&$gotStats) { $gotStats = $stats; } ]); $promise->wait(false); self::assertFalse($gotStats->hasResponse()); self::assertSame( 'http://127.0.0.1:123', (string) $gotStats->getEffectiveUri() ); self::assertSame( 'http://127.0.0.1:123', (string) $gotStats->getRequest()->getUri() ); self::assertIsFloat($gotStats->getTransferTime()); self::assertIsInt($gotStats->getHandlerErrorData()); self::assertArrayHasKey('appconnect_time', $gotStats->getHandlerStats()); } public function testRewindsBodyIfPossible() { $body = Psr7\Utils::streamFor(\str_repeat('x', 1024 * 1024 * 2)); $body->seek(1024 * 1024); self::assertSame(1024 * 1024, $body->tell()); $req = new Psr7\Request('POST', 'https://www.example.com', [ 'Content-Length' => 1024 * 1024 * 2, ], $body); $factory = new CurlFactory(1); $factory->create($req, []); self::assertSame(0, $body->tell()); } public function testDoesNotRewindUnseekableBody() { $body = Psr7\Utils::streamFor(\str_repeat('x', 1024 * 1024 * 2)); $body->seek(1024 * 1024); $body = new Psr7\NoSeekStream($body); self::assertSame(1024 * 1024, $body->tell()); $req = new Psr7\Request('POST', 'https://www.example.com', [ 'Content-Length' => 1024 * 1024, ], $body); $factory = new CurlFactory(1); $factory->create($req, []); self::assertSame(1024 * 1024, $body->tell()); } public function testRelease() { $factory = new CurlFactory(1); $easyHandle = new EasyHandle(); $easyHandle->handle = \curl_init(); self::assertEmpty($factory->release($easyHandle)); } /** * https://github.com/guzzle/guzzle/issues/2735 */ public function testBodyEofOnWindows() { $expectedLength = 4097; Server::flush(); Server::enqueue([ new Psr7\Response(200, [ 'Content-Length' => $expectedLength, ], \str_repeat('x', $expectedLength)) ]); $handler = new Handler\CurlMultiHandler(); $request = new Psr7\Request('GET', Server::$url); $promise = $handler($request, []); $response = $promise->wait(); $body = $response->getBody(); $actualLength = 0; while (!$body->eof()) { $chunk = $body->read(4096); $actualLength += \strlen($chunk); } self::assertSame($expectedLength, $actualLength); } public function testHandlesGarbageHttpServerGracefully() { $a = new Handler\CurlMultiHandler(); $this->expectException(RequestException::class); $this->expectExceptionMessage('cURL error 1: Received HTTP/0.9 when not allowed'); $a(new Psr7\Request('GET', Server::$url . 'guzzle-server/garbage'), [])->wait(); } public function testHandlesInvalidStatusCodeGracefully() { $a = new Handler\CurlMultiHandler(); $this->expectException(RequestException::class); $this->expectExceptionMessage('An error was encountered while creating the response'); $a(new Psr7\Request('GET', Server::$url . 'guzzle-server/bad-status'), [])->wait(); } }