Drupal 8.0.0 beta 12. More info: https://www.drupal.org/node/2514176

This commit is contained in:
Pantheon Automation 2015-08-17 17:00:26 -07:00 committed by Greg Anderson
commit 9921556621
13277 changed files with 1459781 additions and 0 deletions

View file

@ -0,0 +1,792 @@
<?php
// Override curl_setopt_array() to get the last set curl options
namespace GuzzleHttp\Ring\Client {
function curl_setopt_array($handle, array $options) {
if (!empty($_SERVER['curl_test'])) {
$_SERVER['_curl'] = $options;
} else {
unset($_SERVER['_curl']);
}
\curl_setopt_array($handle, $options);
}
}
namespace GuzzleHttp\Tests\Ring\Client {
use GuzzleHttp\Ring\Client\CurlFactory;
use GuzzleHttp\Ring\Client\CurlMultiHandler;
use GuzzleHttp\Ring\Client\MockHandler;
use GuzzleHttp\Ring\Core;
use GuzzleHttp\Stream\FnStream;
use GuzzleHttp\Stream\NoSeekStream;
use GuzzleHttp\Stream\Stream;
class CurlFactoryTest extends \PHPUnit_Framework_TestCase
{
public static function setUpBeforeClass()
{
$_SERVER['curl_test'] = true;
unset($_SERVER['_curl']);
}
public static function tearDownAfterClass()
{
unset($_SERVER['_curl'], $_SERVER['curl_test']);
}
public function testCreatesCurlHandle()
{
Server::flush();
Server::enqueue([[
'status' => 200,
'headers' => [
'Foo' => ['Bar'],
'Baz' => ['bam'],
'Content-Length' => [2],
],
'body' => 'hi',
]]);
$stream = Stream::factory();
$request = [
'http_method' => 'PUT',
'headers' => [
'host' => [Server::$url],
'Hi' => [' 123'],
],
'body' => 'testing',
'client' => ['save_to' => $stream],
];
$f = new CurlFactory();
$result = $f($request);
$this->assertInternalType('array', $result);
$this->assertCount(3, $result);
$this->assertInternalType('resource', $result[0]);
$this->assertInternalType('array', $result[1]);
$this->assertSame($stream, $result[2]);
curl_close($result[0]);
$this->assertEquals('PUT', $_SERVER['_curl'][CURLOPT_CUSTOMREQUEST]);
$this->assertEquals(
'http://http://127.0.0.1:8125/',
$_SERVER['_curl'][CURLOPT_URL]
);
// Sends via post fields when the request is small enough
$this->assertEquals('testing', $_SERVER['_curl'][CURLOPT_POSTFIELDS]);
$this->assertEquals(0, $_SERVER['_curl'][CURLOPT_RETURNTRANSFER]);
$this->assertEquals(0, $_SERVER['_curl'][CURLOPT_HEADER]);
$this->assertEquals(150, $_SERVER['_curl'][CURLOPT_CONNECTTIMEOUT]);
$this->assertInstanceOf('Closure', $_SERVER['_curl'][CURLOPT_HEADERFUNCTION]);
if (defined('CURLOPT_PROTOCOLS')) {
$this->assertEquals(
CURLPROTO_HTTP | CURLPROTO_HTTPS,
$_SERVER['_curl'][CURLOPT_PROTOCOLS]
);
}
$this->assertContains('Expect:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]);
$this->assertContains('Accept:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]);
$this->assertContains('Content-Type:', $_SERVER['_curl'][CURLOPT_HTTPHEADER]);
$this->assertContains('Hi: 123', $_SERVER['_curl'][CURLOPT_HTTPHEADER]);
$this->assertContains('host: http://127.0.0.1:8125/', $_SERVER['_curl'][CURLOPT_HTTPHEADER]);
}
public function testSendsHeadRequests()
{
Server::flush();
Server::enqueue([['status' => 200]]);
$a = new CurlMultiHandler();
$response = $a([
'http_method' => 'HEAD',
'headers' => ['host' => [Server::$host]],
]);
$response->wait();
$this->assertEquals(true, $_SERVER['_curl'][CURLOPT_NOBODY]);
$checks = [CURLOPT_WRITEFUNCTION, CURLOPT_READFUNCTION, CURLOPT_FILE, CURLOPT_INFILE];
foreach ($checks as $check) {
$this->assertArrayNotHasKey($check, $_SERVER['_curl']);
}
$this->assertEquals('HEAD', Server::received()[0]['http_method']);
}
public function testCanAddCustomCurlOptions()
{
Server::flush();
Server::enqueue([['status' => 200]]);
$a = new CurlMultiHandler();
$a([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'client' => ['curl' => [CURLOPT_LOW_SPEED_LIMIT => 10]],
]);
$this->assertEquals(10, $_SERVER['_curl'][CURLOPT_LOW_SPEED_LIMIT]);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage SSL CA bundle not found: /does/not/exist
*/
public function testValidatesVerify()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['verify' => '/does/not/exist'],
]);
}
public function testCanSetVerifyToFile()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['verify' => __FILE__],
]);
$this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_CAINFO]);
$this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]);
$this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]);
}
public function testAddsVerifyAsTrue()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['verify' => true],
]);
$this->assertEquals(2, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]);
$this->assertEquals(true, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]);
$this->assertArrayNotHasKey(CURLOPT_CAINFO, $_SERVER['_curl']);
}
public function testCanDisableVerify()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['verify' => false],
]);
$this->assertEquals(0, $_SERVER['_curl'][CURLOPT_SSL_VERIFYHOST]);
$this->assertEquals(false, $_SERVER['_curl'][CURLOPT_SSL_VERIFYPEER]);
}
public function testAddsProxy()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['proxy' => 'http://bar.com'],
]);
$this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]);
}
public function testAddsViaScheme()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'scheme' => 'http',
'headers' => ['host' => ['foo.com']],
'client' => [
'proxy' => ['http' => 'http://bar.com', 'https' => 'https://t'],
],
]);
$this->assertEquals('http://bar.com', $_SERVER['_curl'][CURLOPT_PROXY]);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage SSL private key not found: /does/not/exist
*/
public function testValidatesSslKey()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['ssl_key' => '/does/not/exist'],
]);
}
public function testAddsSslKey()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['ssl_key' => __FILE__],
]);
$this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]);
}
public function testAddsSslKeyWithPassword()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['ssl_key' => [__FILE__, 'test']],
]);
$this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLKEY]);
$this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLKEYPASSWD]);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage SSL certificate not found: /does/not/exist
*/
public function testValidatesCert()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['cert' => '/does/not/exist'],
]);
}
public function testAddsCert()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['cert' => __FILE__],
]);
$this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]);
}
public function testAddsCertWithPassword()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['cert' => [__FILE__, 'test']],
]);
$this->assertEquals(__FILE__, $_SERVER['_curl'][CURLOPT_SSLCERT]);
$this->assertEquals('test', $_SERVER['_curl'][CURLOPT_SSLCERTPASSWD]);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage progress client option must be callable
*/
public function testValidatesProgress()
{
$f = new CurlFactory();
$f([
'http_method' => 'GET',
'headers' => ['host' => ['foo.com']],
'client' => ['progress' => 'foo'],
]);
}
public function testEmitsDebugInfoToStream()
{
$res = fopen('php://memory', 'r+');
Server::flush();
Server::enqueue([['status' => 200]]);
$a = new CurlMultiHandler();
$response = $a([
'http_method' => 'HEAD',
'headers' => ['host' => [Server::$host]],
'client' => ['debug' => $res],
]);
$response->wait();
rewind($res);
$output = str_replace("\r", '', stream_get_contents($res));
$this->assertContains(
"> HEAD / HTTP/1.1\nhost: 127.0.0.1:8125\n\n",
$output
);
$this->assertContains("< HTTP/1.1 200", $output);
fclose($res);
}
public function testEmitsProgressToFunction()
{
Server::flush();
Server::enqueue([['status' => 200]]);
$a = new CurlMultiHandler();
$called = [];
$response = $a([
'http_method' => 'HEAD',
'headers' => ['host' => [Server::$host]],
'client' => [
'progress' => function () use (&$called) {
$called[] = func_get_args();
},
],
]);
$response->wait();
$this->assertNotEmpty($called);
foreach ($called as $call) {
$this->assertCount(4, $call);
}
}
private function addDecodeResponse($withEncoding = true)
{
$content = gzencode('test');
$response = [
'status' => 200,
'reason' => 'OK',
'headers' => ['Content-Length' => [strlen($content)]],
'body' => $content,
];
if ($withEncoding) {
$response['headers']['Content-Encoding'] = ['gzip'];
}
Server::flush();
Server::enqueue([$response]);
return $content;
}
public function testDecodesGzippedResponses()
{
$this->addDecodeResponse();
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'client' => ['decode_content' => true],
]);
$response->wait();
$this->assertEquals('test', Core::body($response));
$this->assertEquals('', $_SERVER['_curl'][CURLOPT_ENCODING]);
$sent = Server::received()[0];
$this->assertNull(Core::header($sent, 'Accept-Encoding'));
}
public function testDecodesGzippedResponsesWithHeader()
{
$this->addDecodeResponse();
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => [
'host' => [Server::$host],
'Accept-Encoding' => ['gzip'],
],
'client' => ['decode_content' => true],
]);
$response->wait();
$this->assertEquals('gzip', $_SERVER['_curl'][CURLOPT_ENCODING]);
$sent = Server::received()[0];
$this->assertEquals('gzip', Core::header($sent, 'Accept-Encoding'));
$this->assertEquals('test', Core::body($response));
}
public function testDoesNotForceDecode()
{
$content = $this->addDecodeResponse();
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'client' => ['decode_content' => false],
]);
$response->wait();
$sent = Server::received()[0];
$this->assertNull(Core::header($sent, 'Accept-Encoding'));
$this->assertEquals($content, Core::body($response));
}
public function testProtocolVersion()
{
Server::flush();
Server::enqueue([['status' => 200]]);
$a = new CurlMultiHandler();
$a([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'version' => 1.0,
]);
$this->assertEquals(CURL_HTTP_VERSION_1_0, $_SERVER['_curl'][CURLOPT_HTTP_VERSION]);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testValidatesSaveTo()
{
$handler = new CurlMultiHandler();
$handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'client' => ['save_to' => true],
]);
}
public function testSavesToStream()
{
$stream = fopen('php://memory', 'r+');
$this->addDecodeResponse();
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'client' => [
'decode_content' => true,
'save_to' => $stream,
],
]);
$response->wait();
rewind($stream);
$this->assertEquals('test', stream_get_contents($stream));
}
public function testSavesToGuzzleStream()
{
$stream = Stream::factory();
$this->addDecodeResponse();
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'client' => [
'decode_content' => true,
'save_to' => $stream,
],
]);
$response->wait();
$this->assertEquals('test', (string) $stream);
}
public function testSavesToFileOnDisk()
{
$tmpfile = tempnam(sys_get_temp_dir(), 'testfile');
$this->addDecodeResponse();
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'client' => [
'decode_content' => true,
'save_to' => $tmpfile,
],
]);
$response->wait();
$this->assertEquals('test', file_get_contents($tmpfile));
unlink($tmpfile);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testValidatesBody()
{
$handler = new CurlMultiHandler();
$handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'body' => false,
]);
}
public function testAddsLargePayloadFromStreamWithNoSizeUsingChunked()
{
$stream = Stream::factory('foo');
$stream = FnStream::decorate($stream, [
'getSize' => function () {
return null;
}
]);
$this->addDecodeResponse();
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'body' => $stream,
]);
$response->wait();
$sent = Server::received()[0];
$this->assertEquals('chunked', Core::header($sent, 'Transfer-Encoding'));
$this->assertNull(Core::header($sent, 'Content-Length'));
$this->assertEquals('foo', $sent['body']);
}
public function testAddsPayloadFromIterator()
{
$iter = new \ArrayIterator(['f', 'o', 'o']);
$this->addDecodeResponse();
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'body' => $iter,
]);
$response->wait();
$sent = Server::received()[0];
$this->assertEquals('chunked', Core::header($sent, 'Transfer-Encoding'));
$this->assertNull(Core::header($sent, 'Content-Length'));
$this->assertEquals('foo', $sent['body']);
}
public function testAddsPayloadFromResource()
{
$res = fopen('php://memory', 'r+');
$data = str_repeat('.', 1000000);
fwrite($res, $data);
rewind($res);
$this->addDecodeResponse();
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => [
'host' => [Server::$host],
'content-length' => [1000000],
],
'body' => $res,
]);
$response->wait();
$sent = Server::received()[0];
$this->assertNull(Core::header($sent, 'Transfer-Encoding'));
$this->assertEquals(1000000, Core::header($sent, 'Content-Length'));
$this->assertEquals($data, $sent['body']);
}
public function testAddsContentLengthFromStream()
{
$stream = Stream::factory('foo');
$this->addDecodeResponse();
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'body' => $stream,
]);
$response->wait();
$sent = Server::received()[0];
$this->assertEquals(3, Core::header($sent, 'Content-Length'));
$this->assertNull(Core::header($sent, 'Transfer-Encoding'));
$this->assertEquals('foo', $sent['body']);
}
public function testDoesNotAddMultipleContentLengthHeaders()
{
$this->addDecodeResponse();
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => [
'host' => [Server::$host],
'content-length' => [3],
],
'body' => 'foo',
]);
$response->wait();
$sent = Server::received()[0];
$this->assertEquals(3, Core::header($sent, 'Content-Length'));
$this->assertNull(Core::header($sent, 'Transfer-Encoding'));
$this->assertEquals('foo', $sent['body']);
}
public function testSendsPostWithNoBodyOrDefaultContentType()
{
Server::flush();
Server::enqueue([['status' => 200]]);
$handler = new CurlMultiHandler();
$response = $handler([
'http_method' => 'POST',
'uri' => '/',
'headers' => ['host' => [Server::$host]],
]);
$response->wait();
$received = Server::received()[0];
$this->assertEquals('POST', $received['http_method']);
$this->assertNull(Core::header($received, 'content-type'));
$this->assertSame('0', Core::firstHeader($received, 'content-length'));
}
public function testFailsWhenNoResponseAndNoBody()
{
$res = CurlFactory::createResponse(function () {}, [], [], [], null);
$this->assertInstanceOf('GuzzleHttp\Ring\Exception\RingException', $res['error']);
$this->assertContains(
'No response was received for a request with no body',
$res['error']->getMessage()
);
}
public function testFailsWhenCannotRewindRetry()
{
$res = CurlFactory::createResponse(function () {}, [
'body' => new NoSeekStream(Stream::factory('foo'))
], [], [], null);
$this->assertInstanceOf('GuzzleHttp\Ring\Exception\RingException', $res['error']);
$this->assertContains(
'rewind the request body failed',
$res['error']->getMessage()
);
}
public function testRetriesWhenBodyCanBeRewound()
{
$callHandler = $called = false;
$res = CurlFactory::createResponse(function () use (&$callHandler) {
$callHandler = true;
return ['status' => 200];
}, [
'body' => FnStream::decorate(Stream::factory('test'), [
'seek' => function () use (&$called) {
$called = true;
return true;
}
])
], [], [], null);
$this->assertTrue($callHandler);
$this->assertTrue($called);
$this->assertEquals('200', $res['status']);
}
public function testFailsWhenRetryMoreThanThreeTimes()
{
$call = 0;
$mock = new MockHandler(function (array $request) use (&$mock, &$call) {
$call++;
return CurlFactory::createResponse($mock, $request, [], [], null);
});
$response = $mock([
'http_method' => 'GET',
'body' => 'test',
]);
$this->assertEquals(3, $call);
$this->assertArrayHasKey('error', $response);
$this->assertContains(
'The cURL request was retried 3 times',
$response['error']->getMessage()
);
}
public function testHandles100Continue()
{
Server::flush();
Server::enqueue([
[
'status' => '200',
'reason' => 'OK',
'headers' => [
'Test' => ['Hello'],
'Content-Length' => ['4'],
],
'body' => 'test',
],
]);
$request = [
'http_method' => 'PUT',
'headers' => [
'Host' => [Server::$host],
'Expect' => ['100-Continue'],
],
'body' => 'test',
];
$handler = new CurlMultiHandler();
$response = $handler($request)->wait();
$this->assertEquals(200, $response['status']);
$this->assertEquals('OK', $response['reason']);
$this->assertEquals(['Hello'], $response['headers']['Test']);
$this->assertEquals(['4'], $response['headers']['Content-Length']);
$this->assertEquals('test', Core::body($response));
}
public function testCreatesConnectException()
{
$m = new \ReflectionMethod('GuzzleHttp\Ring\Client\CurlFactory', 'createErrorResponse');
$m->setAccessible(true);
$response = $m->invoke(
null,
function () {},
[],
[
'err_message' => 'foo',
'curl' => [
'errno' => CURLE_COULDNT_CONNECT,
]
]
);
$this->assertInstanceOf('GuzzleHttp\Ring\Exception\ConnectException', $response['error']);
}
public function testParsesLastResponseOnly()
{
$response1 = [
'status' => 301,
'headers' => [
'Content-Length' => ['0'],
'Location' => ['/foo']
]
];
$response2 = [
'status' => 200,
'headers' => [
'Content-Length' => ['0'],
'Foo' => ['bar']
]
];
Server::flush();
Server::enqueue([$response1, $response2]);
$a = new CurlMultiHandler();
$response = $a([
'http_method' => 'GET',
'headers' => ['Host' => [Server::$host]],
'client' => [
'curl' => [
CURLOPT_FOLLOWLOCATION => true
]
]
])->wait();
$this->assertEquals(1, $response['transfer_stats']['redirect_count']);
$this->assertEquals('http://127.0.0.1:8125/foo', $response['effective_url']);
$this->assertEquals(['bar'], $response['headers']['Foo']);
$this->assertEquals(200, $response['status']);
$this->assertFalse(Core::hasHeader($response, 'Location'));
}
public function testMaintainsMultiHeaderOrder()
{
Server::flush();
Server::enqueue([
[
'status' => 200,
'headers' => [
'Content-Length' => ['0'],
'Foo' => ['a', 'b'],
'foo' => ['c', 'd'],
]
]
]);
$a = new CurlMultiHandler();
$response = $a([
'http_method' => 'GET',
'headers' => ['Host' => [Server::$host]]
])->wait();
$this->assertEquals(
['a', 'b', 'c', 'd'],
Core::headerLines($response, 'Foo')
);
}
}
}

View file

@ -0,0 +1,96 @@
<?php
namespace GuzzleHttp\Tests\Ring\Client;
use GuzzleHttp\Ring\Client\CurlHandler;
class CurlHandlerTest extends \PHPUnit_Framework_TestCase
{
protected function setUp()
{
if (!function_exists('curl_reset')) {
$this->markTestSkipped('curl_reset() is not available');
}
}
protected function getHandler($factory = null, $options = [])
{
return new CurlHandler($options);
}
public function testCanSetMaxHandles()
{
$a = new CurlHandler(['max_handles' => 10]);
$this->assertEquals(10, $this->readAttribute($a, 'maxHandles'));
}
public function testCreatesCurlErrors()
{
$handler = new CurlHandler();
$response = $handler([
'http_method' => 'GET',
'uri' => '/',
'headers' => ['host' => ['localhost:123']],
'client' => ['timeout' => 0.001, 'connect_timeout' => 0.001],
]);
$this->assertNull($response['status']);
$this->assertNull($response['reason']);
$this->assertEquals([], $response['headers']);
$this->assertInstanceOf(
'GuzzleHttp\Ring\Exception\RingException',
$response['error']
);
$this->assertEquals(
1,
preg_match('/^cURL error \d+: .*$/', $response['error']->getMessage())
);
}
public function testReleasesAdditionalEasyHandles()
{
Server::flush();
$response = [
'status' => 200,
'headers' => ['Content-Length' => [4]],
'body' => 'test',
];
Server::enqueue([$response, $response, $response, $response]);
$a = new CurlHandler(['max_handles' => 2]);
$fn = function () use (&$calls, $a, &$fn) {
if (++$calls < 4) {
$a([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'client' => ['progress' => $fn],
]);
}
};
$request = [
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'client' => [
'progress' => $fn,
],
];
$a($request);
$this->assertCount(2, $this->readAttribute($a, 'handles'));
}
public function testReusesHandles()
{
Server::flush();
$response = ['status' => 200];
Server::enqueue([$response, $response]);
$a = new CurlHandler();
$request = [
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
];
$a($request);
$a($request);
}
}

View file

@ -0,0 +1,165 @@
<?php
namespace GuzzleHttp\Tests\Ring\Client;
use GuzzleHttp\Ring\Client\CurlMultiHandler;
class CurlMultiHandlerTest extends \PHPUnit_Framework_TestCase
{
public function testSendsRequest()
{
Server::enqueue([['status' => 200]]);
$a = new CurlMultiHandler();
$response = $a([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
]);
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
$this->assertEquals(200, $response['status']);
$this->assertArrayHasKey('transfer_stats', $response);
$realUrl = trim($response['transfer_stats']['url'], '/');
$this->assertEquals(trim(Server::$url, '/'), $realUrl);
$this->assertArrayHasKey('effective_url', $response);
$this->assertEquals(
trim(Server::$url, '/'),
trim($response['effective_url'], '/')
);
}
public function testCreatesErrorResponses()
{
$url = 'http://localhost:123/';
$a = new CurlMultiHandler();
$response = $a([
'http_method' => 'GET',
'headers' => ['host' => ['localhost:123']],
]);
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
$this->assertNull($response['status']);
$this->assertNull($response['reason']);
$this->assertEquals([], $response['headers']);
$this->assertArrayHasKey('error', $response);
$this->assertContains('cURL error ', $response['error']->getMessage());
$this->assertArrayHasKey('transfer_stats', $response);
$this->assertEquals(
trim($url, '/'),
trim($response['transfer_stats']['url'], '/')
);
$this->assertArrayHasKey('effective_url', $response);
$this->assertEquals(
trim($url, '/'),
trim($response['effective_url'], '/')
);
}
public function testSendsFuturesWhenDestructed()
{
Server::enqueue([['status' => 200]]);
$a = new CurlMultiHandler();
$response = $a([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
]);
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
$a->__destruct();
$this->assertEquals(200, $response['status']);
}
public function testCanSetMaxHandles()
{
$a = new CurlMultiHandler(['max_handles' => 2]);
$this->assertEquals(2, $this->readAttribute($a, 'maxHandles'));
}
public function testCanSetSelectTimeout()
{
$a = new CurlMultiHandler(['select_timeout' => 2]);
$this->assertEquals(2, $this->readAttribute($a, 'selectTimeout'));
}
public function testSendsFuturesWhenMaxHandlesIsReached()
{
$request = [
'http_method' => 'PUT',
'headers' => ['host' => [Server::$host]],
'future' => 'lazy', // passing this to control the test
];
$response = ['status' => 200];
Server::flush();
Server::enqueue([$response, $response, $response]);
$a = new CurlMultiHandler(['max_handles' => 3]);
for ($i = 0; $i < 5; $i++) {
$responses[] = $a($request);
}
$this->assertCount(3, Server::received());
$responses[3]->cancel();
$responses[4]->cancel();
}
public function testCanCancel()
{
Server::flush();
$response = ['status' => 200];
Server::enqueue(array_fill_keys(range(0, 10), $response));
$a = new CurlMultiHandler();
$responses = [];
for ($i = 0; $i < 10; $i++) {
$response = $a([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'future' => 'lazy',
]);
$response->cancel();
$responses[] = $response;
}
$this->assertCount(0, Server::received());
foreach ($responses as $response) {
$this->assertTrue($this->readAttribute($response, 'isRealized'));
}
}
public function testCannotCancelFinished()
{
Server::flush();
Server::enqueue([['status' => 200]]);
$a = new CurlMultiHandler();
$response = $a([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
]);
$response->wait();
$response->cancel();
}
public function testDelaysInParallel()
{
Server::flush();
Server::enqueue([['status' => 200]]);
$a = new CurlMultiHandler();
$expected = microtime(true) + (100 / 1000);
$response = $a([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'client' => ['delay' => 100],
]);
$response->wait();
$this->assertGreaterThanOrEqual($expected, microtime(true));
}
public function testSendsNonLazyFutures()
{
$request = [
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'future' => true,
];
Server::flush();
Server::enqueue([['status' => 202]]);
$a = new CurlMultiHandler();
$response = $a($request);
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
$this->assertEquals(202, $response['status']);
}
}

View file

@ -0,0 +1,65 @@
<?php
namespace GuzzleHttp\Tests\Ring\Client;
use GuzzleHttp\Ring\Client\Middleware;
use GuzzleHttp\Ring\Future\CompletedFutureArray;
class MiddlewareTest extends \PHPUnit_Framework_TestCase
{
public function testFutureCallsDefaultHandler()
{
$future = new CompletedFutureArray(['status' => 200]);
$calledA = false;
$a = function (array $req) use (&$calledA, $future) {
$calledA = true;
return $future;
};
$calledB = false;
$b = function (array $req) use (&$calledB) { $calledB = true; };
$s = Middleware::wrapFuture($a, $b);
$s([]);
$this->assertTrue($calledA);
$this->assertFalse($calledB);
}
public function testFutureCallsStreamingHandler()
{
$future = new CompletedFutureArray(['status' => 200]);
$calledA = false;
$a = function (array $req) use (&$calledA) { $calledA = true; };
$calledB = false;
$b = function (array $req) use (&$calledB, $future) {
$calledB = true;
return $future;
};
$s = Middleware::wrapFuture($a, $b);
$result = $s(['client' => ['future' => true]]);
$this->assertFalse($calledA);
$this->assertTrue($calledB);
$this->assertSame($future, $result);
}
public function testStreamingCallsDefaultHandler()
{
$calledA = false;
$a = function (array $req) use (&$calledA) { $calledA = true; };
$calledB = false;
$b = function (array $req) use (&$calledB) { $calledB = true; };
$s = Middleware::wrapStreaming($a, $b);
$s([]);
$this->assertTrue($calledA);
$this->assertFalse($calledB);
}
public function testStreamingCallsStreamingHandler()
{
$calledA = false;
$a = function (array $req) use (&$calledA) { $calledA = true; };
$calledB = false;
$b = function (array $req) use (&$calledB) { $calledB = true; };
$s = Middleware::wrapStreaming($a, $b);
$s(['client' => ['stream' => true]]);
$this->assertFalse($calledA);
$this->assertTrue($calledB);
}
}

View file

@ -0,0 +1,86 @@
<?php
namespace GuzzleHttp\Tests\Ring\Client;
use GuzzleHttp\Ring\Client\MockHandler;
use GuzzleHttp\Ring\Future\FutureArray;
use React\Promise\Deferred;
class MockHandlerTest extends \PHPUnit_Framework_TestCase
{
public function testReturnsArray()
{
$mock = new MockHandler(['status' => 200]);
$response = $mock([]);
$this->assertEquals(200, $response['status']);
$this->assertEquals([], $response['headers']);
$this->assertNull($response['body']);
$this->assertNull($response['reason']);
$this->assertNull($response['effective_url']);
}
public function testReturnsFutures()
{
$deferred = new Deferred();
$future = new FutureArray(
$deferred->promise(),
function () use ($deferred) {
$deferred->resolve(['status' => 200]);
}
);
$mock = new MockHandler($future);
$response = $mock([]);
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
$this->assertEquals(200, $response['status']);
}
public function testReturnsFuturesWithThenCall()
{
$deferred = new Deferred();
$future = new FutureArray(
$deferred->promise(),
function () use ($deferred) {
$deferred->resolve(['status' => 200]);
}
);
$mock = new MockHandler($future);
$response = $mock([]);
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
$this->assertEquals(200, $response['status']);
$req = null;
$promise = $response->then(function ($value) use (&$req) {
$req = $value;
$this->assertEquals(200, $req['status']);
});
$this->assertInstanceOf('React\Promise\PromiseInterface', $promise);
$this->assertEquals(200, $req['status']);
}
public function testReturnsFuturesAndProxiesCancel()
{
$c = null;
$deferred = new Deferred();
$future = new FutureArray(
$deferred->promise(),
function () {},
function () use (&$c) {
$c = true;
return true;
}
);
$mock = new MockHandler($future);
$response = $mock([]);
$this->assertInstanceOf('GuzzleHttp\Ring\Future\FutureArray', $response);
$response->cancel();
$this->assertTrue($c);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Response must be an array or FutureArrayInterface. Found
*/
public function testEnsuresMockIsValid()
{
$mock = new MockHandler('foo');
$mock([]);
}
}

View file

@ -0,0 +1,183 @@
<?php
namespace GuzzleHttp\Tests\Ring\Client;
use GuzzleHttp\Ring\Client\StreamHandler;
use GuzzleHttp\Ring\Core;
/**
* Class uses to control the test webserver.
*
* Queued responses will be served to requests using a FIFO order. All requests
* received by the server are stored on the node.js server and can be retrieved
* by calling {@see Server::received()}.
*
* Mock responses that don't require data to be transmitted over HTTP a great
* for testing. Mock response, however, cannot test the actual sending of an
* HTTP request using cURL. This test server allows the simulation of any
* number of HTTP request response transactions to test the actual sending of
* requests over the wire without having to leave an internal network.
*/
class Server
{
public static $started;
public static $url = 'http://127.0.0.1:8125/';
public static $host = '127.0.0.1:8125';
public static $port = 8125;
/**
* Flush the received requests from the server
* @throws \RuntimeException
*/
public static function flush()
{
self::send('DELETE', '/guzzle-server/requests');
}
/**
* Queue an array of responses or a single response on the server.
*
* Any currently queued responses will be overwritten. Subsequent requests
* on the server will return queued responses in FIFO order.
*
* @param array $responses An array of responses. The shape of a response
* is the shape described in the RingPHP spec.
* @throws \Exception
*/
public static function enqueue(array $responses)
{
$data = [];
foreach ($responses as $response) {
if (!is_array($response)) {
throw new \Exception('Each response must be an array');
}
if (isset($response['body'])) {
$response['body'] = base64_encode($response['body']);
}
$response += ['headers' => [], 'reason' => '', 'body' => ''];
$data[] = $response;
}
self::send('PUT', '/guzzle-server/responses', json_encode($data));
}
/**
* Get all of the received requests as a RingPHP request structure.
*
* @return array
* @throws \RuntimeException
*/
public static function received()
{
if (!self::$started) {
return [];
}
$response = self::send('GET', '/guzzle-server/requests');
$body = Core::body($response);
$result = json_decode($body, true);
if ($result === false) {
throw new \RuntimeException('Error decoding response: '
. json_last_error());
}
foreach ($result as &$res) {
if (isset($res['uri'])) {
$res['resource'] = $res['uri'];
}
if (isset($res['query_string'])) {
$res['resource'] .= '?' . $res['query_string'];
}
if (!isset($res['resource'])) {
$res['resource'] = '';
}
// Ensure that headers are all arrays
if (isset($res['headers'])) {
foreach ($res['headers'] as &$h) {
$h = (array) $h;
}
unset($h);
}
}
unset($res);
return $result;
}
/**
* Stop running the node.js server
*/
public static function stop()
{
if (self::$started) {
self::send('DELETE', '/guzzle-server');
}
self::$started = false;
}
public static function wait($maxTries = 20)
{
$tries = 0;
while (!self::isListening() && ++$tries < $maxTries) {
usleep(100000);
}
if (!self::isListening()) {
throw new \RuntimeException('Unable to contact node.js server');
}
}
public static function start()
{
if (self::$started) {
return;
}
try {
self::wait();
} catch (\Exception $e) {
exec('node ' . __DIR__ . \DIRECTORY_SEPARATOR . 'server.js '
. self::$port . ' >> /tmp/server.log 2>&1 &');
self::wait();
}
self::$started = true;
}
private static function isListening()
{
$response = self::send('GET', '/guzzle-server/perf', null, [
'connect_timeout' => 1,
'timeout' => 1
]);
return !isset($response['error']);
}
private static function send(
$method,
$path,
$body = null,
array $client = []
) {
$handler = new StreamHandler();
$request = [
'http_method' => $method,
'uri' => $path,
'request_port' => 8125,
'headers' => ['host' => ['127.0.0.1:8125']],
'body' => $body,
'client' => $client,
];
if ($body) {
$request['headers']['content-length'] = [strlen($body)];
}
return $handler($request);
}
}

View file

@ -0,0 +1,479 @@
<?php
namespace GuzzleHttp\Tests\Ring\Client;
use GuzzleHttp\Ring\Client\ClientUtils;
use GuzzleHttp\Ring\Core;
use GuzzleHttp\Ring\Client\StreamHandler;
class StreamHandlerTest extends \PHPUnit_Framework_TestCase
{
public function testReturnsResponseForSuccessfulRequest()
{
$this->queueRes();
$handler = new StreamHandler();
$response = $handler([
'http_method' => 'GET',
'uri' => '/',
'headers' => [
'host' => [Server::$host],
'Foo' => ['Bar'],
],
]);
$this->assertEquals(200, $response['status']);
$this->assertEquals('OK', $response['reason']);
$this->assertEquals(['Bar'], $response['headers']['Foo']);
$this->assertEquals(['8'], $response['headers']['Content-Length']);
$this->assertEquals('hi there', Core::body($response));
$sent = Server::received()[0];
$this->assertEquals('GET', $sent['http_method']);
$this->assertEquals('/', $sent['resource']);
$this->assertEquals(['127.0.0.1:8125'], $sent['headers']['host']);
$this->assertEquals('Bar', Core::header($sent, 'foo'));
}
public function testAddsErrorToResponse()
{
$handler = new StreamHandler();
$result = $handler([
'http_method' => 'GET',
'headers' => ['host' => ['localhost:123']],
'client' => ['timeout' => 0.01],
]);
$this->assertInstanceOf(
'GuzzleHttp\Ring\Future\CompletedFutureArray',
$result
);
$this->assertNull($result['status']);
$this->assertNull($result['body']);
$this->assertEquals([], $result['headers']);
$this->assertInstanceOf(
'GuzzleHttp\Ring\Exception\RingException',
$result['error']
);
}
public function testEnsuresTheHttpProtocol()
{
$handler = new StreamHandler();
$result = $handler([
'http_method' => 'GET',
'url' => 'ftp://localhost:123',
]);
$this->assertArrayHasKey('error', $result);
$this->assertContains(
'URL is invalid: ftp://localhost:123',
$result['error']->getMessage()
);
}
public function testStreamAttributeKeepsStreamOpen()
{
$this->queueRes();
$handler = new StreamHandler();
$response = $handler([
'http_method' => 'PUT',
'uri' => '/foo',
'query_string' => 'baz=bar',
'headers' => [
'host' => [Server::$host],
'Foo' => ['Bar'],
],
'body' => 'test',
'client' => ['stream' => true],
]);
$this->assertEquals(200, $response['status']);
$this->assertEquals('OK', $response['reason']);
$this->assertEquals('8', Core::header($response, 'Content-Length'));
$body = $response['body'];
$this->assertTrue(is_resource($body));
$this->assertEquals('http', stream_get_meta_data($body)['wrapper_type']);
$this->assertEquals('hi there', stream_get_contents($body));
fclose($body);
$sent = Server::received()[0];
$this->assertEquals('PUT', $sent['http_method']);
$this->assertEquals('/foo', $sent['uri']);
$this->assertEquals('baz=bar', $sent['query_string']);
$this->assertEquals('/foo?baz=bar', $sent['resource']);
$this->assertEquals('127.0.0.1:8125', Core::header($sent, 'host'));
$this->assertEquals('Bar', Core::header($sent, 'foo'));
}
public function testDrainsResponseIntoTempStream()
{
$this->queueRes();
$handler = new StreamHandler();
$response = $handler([
'http_method' => 'GET',
'uri' => '/',
'headers' => ['host' => [Server::$host]],
]);
$body = $response['body'];
$this->assertEquals('php://temp', stream_get_meta_data($body)['uri']);
$this->assertEquals('hi', fread($body, 2));
fclose($body);
}
public function testDrainsResponseIntoSaveToBody()
{
$r = fopen('php://temp', 'r+');
$this->queueRes();
$handler = new StreamHandler();
$response = $handler([
'http_method' => 'GET',
'uri' => '/',
'headers' => ['host' => [Server::$host]],
'client' => ['save_to' => $r],
]);
$body = $response['body'];
$this->assertEquals('php://temp', stream_get_meta_data($body)['uri']);
$this->assertEquals('hi', fread($body, 2));
$this->assertEquals(' there', stream_get_contents($r));
fclose($r);
}
public function testDrainsResponseIntoSaveToBodyAtPath()
{
$tmpfname = tempnam('/tmp', 'save_to_path');
$this->queueRes();
$handler = new StreamHandler();
$response = $handler([
'http_method' => 'GET',
'uri' => '/',
'headers' => ['host' => [Server::$host]],
'client' => ['save_to' => $tmpfname],
]);
$body = $response['body'];
$this->assertInstanceOf('GuzzleHttp\Stream\StreamInterface', $body);
$this->assertEquals($tmpfname, $body->getMetadata('uri'));
$this->assertEquals('hi', $body->read(2));
$body->close();
unlink($tmpfname);
}
public function testAutomaticallyDecompressGzip()
{
Server::flush();
$content = gzencode('test');
Server::enqueue([
[
'status' => 200,
'reason' => 'OK',
'headers' => [
'Content-Encoding' => ['gzip'],
'Content-Length' => [strlen($content)],
],
'body' => $content,
],
]);
$handler = new StreamHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'uri' => '/',
'client' => ['decode_content' => true],
]);
$this->assertEquals('test', Core::body($response));
}
public function testDoesNotForceGzipDecode()
{
Server::flush();
$content = gzencode('test');
Server::enqueue([
[
'status' => 200,
'reason' => 'OK',
'headers' => [
'Content-Encoding' => ['gzip'],
'Content-Length' => [strlen($content)],
],
'body' => $content,
],
]);
$handler = new StreamHandler();
$response = $handler([
'http_method' => 'GET',
'headers' => ['host' => [Server::$host]],
'uri' => '/',
'client' => ['stream' => true, 'decode_content' => false],
]);
$this->assertSame($content, Core::body($response));
}
public function testProtocolVersion()
{
$this->queueRes();
$handler = new StreamHandler();
$handler([
'http_method' => 'GET',
'uri' => '/',
'headers' => ['host' => [Server::$host]],
'version' => 1.0,
]);
$this->assertEquals(1.0, Server::received()[0]['version']);
}
protected function getSendResult(array $opts)
{
$this->queueRes();
$handler = new StreamHandler();
$opts['stream'] = true;
return $handler([
'http_method' => 'GET',
'uri' => '/',
'headers' => ['host' => [Server::$host]],
'client' => $opts,
]);
}
public function testAddsProxy()
{
$res = $this->getSendResult(['stream' => true, 'proxy' => '127.0.0.1:8125']);
$opts = stream_context_get_options($res['body']);
$this->assertEquals('127.0.0.1:8125', $opts['http']['proxy']);
}
public function testAddsTimeout()
{
$res = $this->getSendResult(['stream' => true, 'timeout' => 200]);
$opts = stream_context_get_options($res['body']);
$this->assertEquals(200, $opts['http']['timeout']);
}
public function testVerifiesVerifyIsValidIfPath()
{
$res = $this->getSendResult(['verify' => '/does/not/exist']);
$this->assertContains(
'SSL CA bundle not found: /does/not/exist',
(string) $res['error']
);
}
public function testVerifyCanBeDisabled()
{
$res = $this->getSendResult(['verify' => false]);
$this->assertArrayNotHasKey('error', $res);
}
public function testVerifiesCertIfValidPath()
{
$res = $this->getSendResult(['cert' => '/does/not/exist']);
$this->assertContains(
'SSL certificate not found: /does/not/exist',
(string) $res['error']
);
}
public function testVerifyCanBeSetToPath()
{
$path = $path = ClientUtils::getDefaultCaBundle();
$res = $this->getSendResult(['verify' => $path]);
$this->assertArrayNotHasKey('error', $res);
$opts = stream_context_get_options($res['body']);
$this->assertEquals(true, $opts['ssl']['verify_peer']);
$this->assertEquals($path, $opts['ssl']['cafile']);
$this->assertTrue(file_exists($opts['ssl']['cafile']));
}
public function testUsesSystemDefaultBundle()
{
$path = $path = ClientUtils::getDefaultCaBundle();
$res = $this->getSendResult(['verify' => true]);
$this->assertArrayNotHasKey('error', $res);
$opts = stream_context_get_options($res['body']);
if (PHP_VERSION_ID < 50600) {
$this->assertEquals($path, $opts['ssl']['cafile']);
}
}
public function testEnsuresVerifyOptionIsValid()
{
$res = $this->getSendResult(['verify' => 10]);
$this->assertContains(
'Invalid verify request option',
(string) $res['error']
);
}
public function testCanSetPasswordWhenSettingCert()
{
$path = __FILE__;
$res = $this->getSendResult(['cert' => [$path, 'foo']]);
$opts = stream_context_get_options($res['body']);
$this->assertEquals($path, $opts['ssl']['local_cert']);
$this->assertEquals('foo', $opts['ssl']['passphrase']);
}
public function testDebugAttributeWritesToStream()
{
$this->queueRes();
$f = fopen('php://temp', 'w+');
$this->getSendResult(['debug' => $f]);
fseek($f, 0);
$contents = stream_get_contents($f);
$this->assertContains('<GET http://127.0.0.1:8125/> [CONNECT]', $contents);
$this->assertContains('<GET http://127.0.0.1:8125/> [FILE_SIZE_IS]', $contents);
$this->assertContains('<GET http://127.0.0.1:8125/> [PROGRESS]', $contents);
}
public function testDebugAttributeWritesStreamInfoToBuffer()
{
$called = false;
$this->queueRes();
$buffer = fopen('php://temp', 'r+');
$this->getSendResult([
'progress' => function () use (&$called) { $called = true; },
'debug' => $buffer,
]);
fseek($buffer, 0);
$contents = stream_get_contents($buffer);
$this->assertContains('<GET http://127.0.0.1:8125/> [CONNECT]', $contents);
$this->assertContains('<GET http://127.0.0.1:8125/> [FILE_SIZE_IS] message: "Content-Length: 8"', $contents);
$this->assertContains('<GET http://127.0.0.1:8125/> [PROGRESS] bytes_max: "8"', $contents);
$this->assertTrue($called);
}
public function testEmitsProgressInformation()
{
$called = [];
$this->queueRes();
$this->getSendResult([
'progress' => function () use (&$called) {
$called[] = func_get_args();
},
]);
$this->assertNotEmpty($called);
$this->assertEquals(8, $called[0][0]);
$this->assertEquals(0, $called[0][1]);
}
public function testEmitsProgressInformationAndDebugInformation()
{
$called = [];
$this->queueRes();
$buffer = fopen('php://memory', 'w+');
$this->getSendResult([
'debug' => $buffer,
'progress' => function () use (&$called) {
$called[] = func_get_args();
},
]);
$this->assertNotEmpty($called);
$this->assertEquals(8, $called[0][0]);
$this->assertEquals(0, $called[0][1]);
rewind($buffer);
$this->assertNotEmpty(stream_get_contents($buffer));
fclose($buffer);
}
public function testAddsProxyByProtocol()
{
$url = str_replace('http', 'tcp', Server::$url);
$res = $this->getSendResult(['proxy' => ['http' => $url]]);
$opts = stream_context_get_options($res['body']);
$this->assertEquals($url, $opts['http']['proxy']);
}
public function testPerformsShallowMergeOfCustomContextOptions()
{
$res = $this->getSendResult([
'stream_context' => [
'http' => [
'request_fulluri' => true,
'method' => 'HEAD',
],
'socket' => [
'bindto' => '127.0.0.1:0',
],
'ssl' => [
'verify_peer' => false,
],
],
]);
$opts = stream_context_get_options($res['body']);
$this->assertEquals('HEAD', $opts['http']['method']);
$this->assertTrue($opts['http']['request_fulluri']);
$this->assertFalse($opts['ssl']['verify_peer']);
$this->assertEquals('127.0.0.1:0', $opts['socket']['bindto']);
}
public function testEnsuresThatStreamContextIsAnArray()
{
$res = $this->getSendResult(['stream_context' => 'foo']);
$this->assertContains(
'stream_context must be an array',
(string) $res['error']
);
}
public function testDoesNotAddContentTypeByDefault()
{
$this->queueRes();
$handler = new StreamHandler();
$handler([
'http_method' => 'PUT',
'uri' => '/',
'headers' => ['host' => [Server::$host], 'content-length' => [3]],
'body' => 'foo',
]);
$req = Server::received()[0];
$this->assertEquals('', Core::header($req, 'Content-Type'));
$this->assertEquals(3, Core::header($req, 'Content-Length'));
}
private function queueRes()
{
Server::flush();
Server::enqueue([
[
'status' => 200,
'reason' => 'OK',
'headers' => [
'Foo' => ['Bar'],
'Content-Length' => [8],
],
'body' => 'hi there',
],
]);
}
public function testSupports100Continue()
{
Server::flush();
Server::enqueue([
[
'status' => '200',
'reason' => 'OK',
'headers' => [
'Test' => ['Hello'],
'Content-Length' => ['4'],
],
'body' => 'test',
],
]);
$request = [
'http_method' => 'PUT',
'headers' => [
'Host' => [Server::$host],
'Expect' => ['100-Continue'],
],
'body' => 'test',
];
$handler = new StreamHandler();
$response = $handler($request);
$this->assertEquals(200, $response['status']);
$this->assertEquals('OK', $response['reason']);
$this->assertEquals(['Hello'], $response['headers']['Test']);
$this->assertEquals(['4'], $response['headers']['Content-Length']);
$this->assertEquals('test', Core::body($response));
}
}

View file

@ -0,0 +1,241 @@
/**
* Guzzle node.js test server to return queued responses to HTTP requests and
* expose a RESTful API for enqueueing responses and retrieving the requests
* that have been received.
*
* - Delete all requests that have been received:
* > DELETE /guzzle-server/requests
* > Host: 127.0.0.1:8125
*
* - Enqueue responses
* > PUT /guzzle-server/responses
* > Host: 127.0.0.1:8125
* >
* > [{'status': 200, 'reason': 'OK', 'headers': {}, 'body': '' }]
*
* - Get the received requests
* > GET /guzzle-server/requests
* > Host: 127.0.0.1:8125
*
* < HTTP/1.1 200 OK
* <
* < [{'http_method': 'GET', 'uri': '/', 'headers': {}, 'body': 'string'}]
*
* - Attempt access to the secure area
* > GET /secure/by-digest/qop-auth/guzzle-server/requests
* > Host: 127.0.0.1:8125
*
* < HTTP/1.1 401 Unauthorized
* < WWW-Authenticate: Digest realm="Digest Test", qop="auth", nonce="0796e98e1aeef43141fab2a66bf4521a", algorithm="MD5", stale="false"
* <
* < 401 Unauthorized
*
* - Shutdown the server
* > DELETE /guzzle-server
* > Host: 127.0.0.1:8125
*
* @package Guzzle PHP <http://www.guzzlephp.org>
* @license See the LICENSE file that was distributed with this source code.
*/
var http = require('http');
var url = require('url');
/**
* Guzzle node.js server
* @class
*/
var GuzzleServer = function(port, log) {
this.port = port;
this.log = log;
this.responses = [];
this.requests = [];
var that = this;
var md5 = function(input) {
var crypto = require('crypto');
var hasher = crypto.createHash('md5');
hasher.update(input);
return hasher.digest('hex');
}
/**
* Node.js HTTP server authentication module.
*
* It is only initialized on demand (by loadAuthentifier). This avoids
* requiring the dependency to http-auth on standard operations, and the
* performance hit at startup.
*/
var auth;
/**
* Provides authentication handlers (Basic, Digest).
*/
var loadAuthentifier = function(type, options) {
var typeId = type;
if (type == 'digest') {
typeId += '.'+(options && options.qop ? options.qop : 'none');
}
if (!loadAuthentifier[typeId]) {
if (!auth) {
try {
auth = require('http-auth');
} catch (e) {
if (e.code == 'MODULE_NOT_FOUND') {
return;
}
}
}
switch (type) {
case 'digest':
var digestParams = {
realm: 'Digest Test',
login: 'me',
password: 'test'
};
if (options && options.qop) {
digestParams.qop = options.qop;
}
loadAuthentifier[typeId] = auth.digest(digestParams, function(username, callback) {
callback(md5(digestParams.login + ':' + digestParams.realm + ':' + digestParams.password));
});
break
}
}
return loadAuthentifier[typeId];
};
var firewallRequest = function(request, req, res, requestHandlerCallback) {
var securedAreaUriParts = request.uri.match(/^\/secure\/by-(digest)(\/qop-([^\/]*))?(\/.*)$/);
if (securedAreaUriParts) {
var authentifier = loadAuthentifier(securedAreaUriParts[1], { qop: securedAreaUriParts[2] });
if (!authentifier) {
res.writeHead(501, 'HTTP authentication not implemented', { 'Content-Length': 0 });
res.end();
return;
}
authentifier.check(req, res, function(req, res) {
req.url = securedAreaUriParts[4];
requestHandlerCallback(request, req, res);
});
} else {
requestHandlerCallback(request, req, res);
}
};
var controlRequest = function(request, req, res) {
if (req.url == '/guzzle-server/perf') {
res.writeHead(200, 'OK', {'Content-Length': 16});
res.end('Body of response');
} else if (req.method == 'DELETE') {
if (req.url == '/guzzle-server/requests') {
// Clear the received requests
that.requests = [];
res.writeHead(200, 'OK', { 'Content-Length': 0 });
res.end();
if (that.log) {
console.log('Flushing requests');
}
} else if (req.url == '/guzzle-server') {
// Shutdown the server
res.writeHead(200, 'OK', { 'Content-Length': 0, 'Connection': 'close' });
res.end();
if (that.log) {
console.log('Shutting down');
}
that.server.close();
}
} else if (req.method == 'GET') {
if (req.url === '/guzzle-server/requests') {
if (that.log) {
console.log('Sending received requests');
}
// Get received requests
var body = JSON.stringify(that.requests);
res.writeHead(200, 'OK', { 'Content-Length': body.length });
res.end(body);
}
} else if (req.method == 'PUT' && req.url == '/guzzle-server/responses') {
if (that.log) {
console.log('Adding responses...');
}
if (!request.body) {
if (that.log) {
console.log('No response data was provided');
}
res.writeHead(400, 'NO RESPONSES IN REQUEST', { 'Content-Length': 0 });
} else {
that.responses = eval('(' + request.body + ')');
for (var i = 0; i < that.responses.length; i++) {
if (that.responses[i].body) {
that.responses[i].body = new Buffer(that.responses[i].body, 'base64');
}
}
if (that.log) {
console.log(that.responses);
}
res.writeHead(200, 'OK', { 'Content-Length': 0 });
}
res.end();
}
};
var receivedRequest = function(request, req, res) {
if (req.url.indexOf('/guzzle-server') === 0) {
controlRequest(request, req, res);
} else if (req.url.indexOf('/guzzle-server') == -1 && !that.responses.length) {
res.writeHead(500);
res.end('No responses in queue');
} else {
if (that.log) {
console.log('Returning response from queue and adding request');
}
that.requests.push(request);
var response = that.responses.shift();
res.writeHead(response.status, response.reason, response.headers);
res.end(response.body);
}
};
this.start = function() {
that.server = http.createServer(function(req, res) {
var parts = url.parse(req.url, false);
var request = {
http_method: req.method,
scheme: parts.scheme,
uri: parts.pathname,
query_string: parts.query,
headers: req.headers,
version: req.httpVersion,
body: ''
};
// Receive each chunk of the request body
req.addListener('data', function(chunk) {
request.body += chunk;
});
// Called when the request completes
req.addListener('end', function() {
firewallRequest(request, req, res, receivedRequest);
});
});
that.server.listen(this.port, '127.0.0.1');
if (this.log) {
console.log('Server running at http://127.0.0.1:8125/');
}
};
};
// Get the port from the arguments
port = process.argv.length >= 3 ? process.argv[2] : 8125;
log = process.argv.length >= 4 ? process.argv[3] : false;
// Start the server
server = new GuzzleServer(port, log);
server.start();

View file

@ -0,0 +1,336 @@
<?php
namespace GuzzleHttp\Tests\Ring;
use GuzzleHttp\Ring\Core;
use GuzzleHttp\Ring\Future\CompletedFutureArray;
use GuzzleHttp\Ring\Future\FutureArray;
use GuzzleHttp\Stream\Stream;
use React\Promise\Deferred;
class CoreTest extends \PHPUnit_Framework_TestCase
{
public function testReturnsNullNoHeadersAreSet()
{
$this->assertNull(Core::header([], 'Foo'));
$this->assertNull(Core::firstHeader([], 'Foo'));
}
public function testChecksIfHasHeader()
{
$message = [
'headers' => [
'Foo' => ['Bar', 'Baz'],
'foo' => ['hello'],
'bar' => ['1']
]
];
$this->assertTrue(Core::hasHeader($message, 'Foo'));
$this->assertTrue(Core::hasHeader($message, 'foo'));
$this->assertTrue(Core::hasHeader($message, 'FoO'));
$this->assertTrue(Core::hasHeader($message, 'bar'));
$this->assertFalse(Core::hasHeader($message, 'barr'));
}
public function testReturnsFirstHeaderWhenSimple()
{
$this->assertEquals('Bar', Core::firstHeader([
'headers' => ['Foo' => ['Bar', 'Baz']],
], 'Foo'));
}
public function testReturnsFirstHeaderWhenMultiplePerLine()
{
$this->assertEquals('Bar', Core::firstHeader([
'headers' => ['Foo' => ['Bar, Baz']],
], 'Foo'));
}
public function testExtractsCaseInsensitiveHeader()
{
$this->assertEquals(
'hello',
Core::header(['headers' => ['foo' => ['hello']]], 'FoO')
);
}
public function testExtractsCaseInsensitiveHeaderLines()
{
$this->assertEquals(
['a', 'b', 'c', 'd'],
Core::headerLines([
'headers' => [
'foo' => ['a', 'b'],
'Foo' => ['c', 'd']
]
], 'foo')
);
}
public function testExtractsHeaderLines()
{
$this->assertEquals(
['bar', 'baz'],
Core::headerLines([
'headers' => [
'Foo' => ['bar', 'baz'],
],
], 'Foo')
);
}
public function testExtractsHeaderAsString()
{
$this->assertEquals(
'bar, baz',
Core::header([
'headers' => [
'Foo' => ['bar', 'baz'],
],
], 'Foo', true)
);
}
public function testReturnsNullWhenHeaderNotFound()
{
$this->assertNull(Core::header(['headers' => []], 'Foo'));
}
public function testRemovesHeaders()
{
$message = [
'headers' => [
'foo' => ['bar'],
'Foo' => ['bam'],
'baz' => ['123'],
],
];
$this->assertSame($message, Core::removeHeader($message, 'bam'));
$this->assertEquals([
'headers' => ['baz' => ['123']],
], Core::removeHeader($message, 'foo'));
}
public function testCreatesUrl()
{
$req = [
'scheme' => 'http',
'headers' => ['host' => ['foo.com']],
'uri' => '/',
];
$this->assertEquals('http://foo.com/', Core::url($req));
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage No Host header was provided
*/
public function testEnsuresHostIsAvailableWhenCreatingUrls()
{
Core::url([]);
}
public function testCreatesUrlWithQueryString()
{
$req = [
'scheme' => 'http',
'headers' => ['host' => ['foo.com']],
'uri' => '/',
'query_string' => 'foo=baz',
];
$this->assertEquals('http://foo.com/?foo=baz', Core::url($req));
}
public function testUsesUrlIfSet()
{
$req = ['url' => 'http://foo.com'];
$this->assertEquals('http://foo.com', Core::url($req));
}
public function testReturnsNullWhenNoBody()
{
$this->assertNull(Core::body([]));
}
public function testReturnsStreamAsString()
{
$this->assertEquals(
'foo',
Core::body(['body' => Stream::factory('foo')])
);
}
public function testReturnsString()
{
$this->assertEquals('foo', Core::body(['body' => 'foo']));
}
public function testReturnsResourceContent()
{
$r = fopen('php://memory', 'w+');
fwrite($r, 'foo');
rewind($r);
$this->assertEquals('foo', Core::body(['body' => $r]));
fclose($r);
}
public function testReturnsIteratorContent()
{
$a = new \ArrayIterator(['a', 'b', 'cd', '']);
$this->assertEquals('abcd', Core::body(['body' => $a]));
}
public function testReturnsObjectToString()
{
$this->assertEquals('foo', Core::body(['body' => new StrClass]));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresBodyIsValid()
{
Core::body(['body' => false]);
}
public function testParsesHeadersFromLines()
{
$lines = ['Foo: bar', 'Foo: baz', 'Abc: 123', 'Def: a, b'];
$this->assertEquals([
'Foo' => ['bar', 'baz'],
'Abc' => ['123'],
'Def' => ['a, b'],
], Core::headersFromLines($lines));
}
public function testParsesHeadersFromLinesWithMultipleLines()
{
$lines = ['Foo: bar', 'Foo: baz', 'Foo: 123'];
$this->assertEquals([
'Foo' => ['bar', 'baz', '123'],
], Core::headersFromLines($lines));
}
public function testCreatesArrayCallFunctions()
{
$called = [];
$a = function ($a, $b) use (&$called) {
$called['a'] = func_get_args();
};
$b = function ($a, $b) use (&$called) {
$called['b'] = func_get_args();
};
$c = Core::callArray([$a, $b]);
$c(1, 2);
$this->assertEquals([1, 2], $called['a']);
$this->assertEquals([1, 2], $called['b']);
}
public function testRewindsGuzzleStreams()
{
$str = Stream::factory('foo');
$this->assertTrue(Core::rewindBody(['body' => $str]));
}
public function testRewindsStreams()
{
$str = Stream::factory('foo')->detach();
$this->assertTrue(Core::rewindBody(['body' => $str]));
}
public function testRewindsIterators()
{
$iter = new \ArrayIterator(['foo']);
$this->assertTrue(Core::rewindBody(['body' => $iter]));
}
public function testRewindsStrings()
{
$this->assertTrue(Core::rewindBody(['body' => 'hi']));
}
public function testRewindsToStrings()
{
$this->assertTrue(Core::rewindBody(['body' => new StrClass()]));
}
public function typeProvider()
{
return [
['foo', 'string(3) "foo"'],
[true, 'bool(true)'],
[false, 'bool(false)'],
[10, 'int(10)'],
[1.0, 'float(1)'],
[new StrClass(), 'object(GuzzleHttp\Tests\Ring\StrClass)'],
[['foo'], 'array(1)']
];
}
/**
* @dataProvider typeProvider
*/
public function testDescribesType($input, $output)
{
$this->assertEquals($output, Core::describeType($input));
}
public function testDoesSleep()
{
$t = microtime(true);
$expected = $t + (100 / 1000);
Core::doSleep(['client' => ['delay' => 100]]);
$this->assertGreaterThanOrEqual($expected, microtime(true));
}
public function testProxiesFuture()
{
$f = new CompletedFutureArray(['status' => 200]);
$res = null;
$proxied = Core::proxy($f, function ($value) use (&$res) {
$value['foo'] = 'bar';
$res = $value;
return $value;
});
$this->assertNotSame($f, $proxied);
$this->assertEquals(200, $f->wait()['status']);
$this->assertArrayNotHasKey('foo', $f->wait());
$this->assertEquals('bar', $proxied->wait()['foo']);
$this->assertEquals(200, $proxied->wait()['status']);
}
public function testProxiesDeferredFuture()
{
$d = new Deferred();
$f = new FutureArray($d->promise());
$f2 = Core::proxy($f);
$d->resolve(['foo' => 'bar']);
$this->assertEquals('bar', $f['foo']);
$this->assertEquals('bar', $f2['foo']);
}
public function testProxiesDeferredFutureFailure()
{
$d = new Deferred();
$f = new FutureArray($d->promise());
$f2 = Core::proxy($f);
$d->reject(new \Exception('foo'));
try {
$f2['hello?'];
$this->fail('did not throw');
} catch (\Exception $e) {
$this->assertEquals('foo', $e->getMessage());
}
}
}
final class StrClass
{
public function __toString()
{
return 'foo';
}
}

View file

@ -0,0 +1,21 @@
<?php
namespace GuzzleHttp\Tests\Ring\Future;
use GuzzleHttp\Ring\Future\CompletedFutureArray;
class CompletedFutureArrayTest extends \PHPUnit_Framework_TestCase
{
public function testReturnsAsArray()
{
$f = new CompletedFutureArray(['foo' => 'bar']);
$this->assertEquals('bar', $f['foo']);
$this->assertFalse(isset($f['baz']));
$f['abc'] = '123';
$this->assertTrue(isset($f['abc']));
$this->assertEquals(['foo' => 'bar', 'abc' => '123'], iterator_to_array($f));
$this->assertEquals(2, count($f));
unset($f['abc']);
$this->assertEquals(1, count($f));
$this->assertEquals(['foo' => 'bar'], iterator_to_array($f));
}
}

View file

@ -0,0 +1,46 @@
<?php
namespace GuzzleHttp\Tests\Ring\Future;
use GuzzleHttp\Ring\Exception\CancelledFutureAccessException;
use GuzzleHttp\Ring\Future\CompletedFutureValue;
class CompletedFutureValueTest extends \PHPUnit_Framework_TestCase
{
public function testReturnsValue()
{
$f = new CompletedFutureValue('hi');
$this->assertEquals('hi', $f->wait());
$f->cancel();
$a = null;
$f->then(function ($v) use (&$a) {
$a = $v;
});
$this->assertSame('hi', $a);
}
public function testThrows()
{
$ex = new \Exception('foo');
$f = new CompletedFutureValue(null, $ex);
$f->cancel();
try {
$f->wait();
$this->fail('did not throw');
} catch (\Exception $e) {
$this->assertSame($e, $ex);
}
}
public function testMarksAsCancelled()
{
$ex = new CancelledFutureAccessException();
$f = new CompletedFutureValue(null, $ex);
try {
$f->wait();
$this->fail('did not throw');
} catch (\Exception $e) {
$this->assertSame($e, $ex);
}
}
}

View file

@ -0,0 +1,56 @@
<?php
namespace GuzzleHttp\Tests\Ring\Future;
use GuzzleHttp\Ring\Future\FutureArray;
use React\Promise\Deferred;
class FutureArrayTest extends \PHPUnit_Framework_TestCase
{
public function testLazilyCallsDeref()
{
$c = false;
$deferred = new Deferred();
$f = new FutureArray(
$deferred->promise(),
function () use (&$c, $deferred) {
$c = true;
$deferred->resolve(['status' => 200]);
}
);
$this->assertFalse($c);
$this->assertFalse($this->readAttribute($f, 'isRealized'));
$this->assertEquals(200, $f['status']);
$this->assertTrue($c);
}
public function testActsLikeArray()
{
$deferred = new Deferred();
$f = new FutureArray(
$deferred->promise(),
function () use (&$c, $deferred) {
$deferred->resolve(['status' => 200]);
}
);
$this->assertTrue(isset($f['status']));
$this->assertEquals(200, $f['status']);
$this->assertEquals(['status' => 200], $f->wait());
$this->assertEquals(1, count($f));
$f['baz'] = 10;
$this->assertEquals(10, $f['baz']);
unset($f['baz']);
$this->assertFalse(isset($f['baz']));
$this->assertEquals(['status' => 200], iterator_to_array($f));
}
/**
* @expectedException \RuntimeException
*/
public function testThrowsWhenAccessingInvalidProperty()
{
$deferred = new Deferred();
$f = new FutureArray($deferred->promise(), function () {});
$f->foo;
}
}

View file

@ -0,0 +1,109 @@
<?php
namespace GuzzleHttp\Tests\Ring\Future;
use GuzzleHttp\Ring\Exception\CancelledFutureAccessException;
use GuzzleHttp\Ring\Future\FutureValue;
use React\Promise\Deferred;
class FutureValueTest extends \PHPUnit_Framework_TestCase
{
public function testDerefReturnsValue()
{
$called = 0;
$deferred = new Deferred();
$f = new FutureValue(
$deferred->promise(),
function () use ($deferred, &$called) {
$called++;
$deferred->resolve('foo');
}
);
$this->assertEquals('foo', $f->wait());
$this->assertEquals(1, $called);
$this->assertEquals('foo', $f->wait());
$this->assertEquals(1, $called);
$f->cancel();
$this->assertTrue($this->readAttribute($f, 'isRealized'));
}
/**
* @expectedException \GuzzleHttp\Ring\Exception\CancelledFutureAccessException
*/
public function testThrowsWhenAccessingCancelled()
{
$f = new FutureValue(
(new Deferred())->promise(),
function () {},
function () { return true; }
);
$f->cancel();
$f->wait();
}
/**
* @expectedException \OutOfBoundsException
*/
public function testThrowsWhenDerefFailure()
{
$called = false;
$deferred = new Deferred();
$f = new FutureValue(
$deferred->promise(),
function () use(&$called) {
$called = true;
}
);
$deferred->reject(new \OutOfBoundsException());
$f->wait();
$this->assertFalse($called);
}
/**
* @expectedException \GuzzleHttp\Ring\Exception\RingException
* @expectedExceptionMessage Waiting did not resolve future
*/
public function testThrowsWhenDerefDoesNotResolve()
{
$deferred = new Deferred();
$f = new FutureValue(
$deferred->promise(),
function () use(&$called) {
$called = true;
}
);
$f->wait();
}
public function testThrowingCancelledFutureAccessExceptionCancels()
{
$deferred = new Deferred();
$f = new FutureValue(
$deferred->promise(),
function () use ($deferred) {
throw new CancelledFutureAccessException();
}
);
try {
$f->wait();
$this->fail('did not throw');
} catch (CancelledFutureAccessException $e) {}
}
/**
* @expectedException \Exception
* @expectedExceptionMessage foo
*/
public function testThrowingExceptionInDerefMarksAsFailed()
{
$deferred = new Deferred();
$f = new FutureValue(
$deferred->promise(),
function () {
throw new \Exception('foo');
}
);
$f->wait();
}
}

View file

@ -0,0 +1,11 @@
<?php
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/Client/Server.php';
use GuzzleHttp\Tests\Ring\Client\Server;
Server::start();
register_shutdown_function(function () {
Server::stop();
});