updated plugin W3 Total Cache version 2.3.3

This commit is contained in:
2023-06-28 12:45:56 +00:00
committed by Gitium
parent 7d5eef77cf
commit aa3da0eb92
129 changed files with 17998 additions and 186 deletions

View File

@ -0,0 +1,811 @@
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Client;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Uri;
use GuzzleHttp\RequestOptions;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
class ClientTest extends TestCase
{
public function testUsesDefaultHandler()
{
$client = new Client();
Server::enqueue([new Response(200, ['Content-Length' => 0])]);
$response = $client->get(Server::$url);
self::assertSame(200, $response->getStatusCode());
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Magic request methods require a URI and optional options array
*/
public function testValidatesArgsForMagicMethods()
{
$client = new Client();
$client->get();
}
public function testCanSendMagicAsyncRequests()
{
$client = new Client();
Server::flush();
Server::enqueue([new Response(200, ['Content-Length' => 2], 'hi')]);
$p = $client->getAsync(Server::$url, ['query' => ['test' => 'foo']]);
self::assertInstanceOf(PromiseInterface::class, $p);
self::assertSame(200, $p->wait()->getStatusCode());
$received = Server::received(true);
self::assertCount(1, $received);
self::assertSame('test=foo', $received[0]->getUri()->getQuery());
}
public function testCanSendSynchronously()
{
$client = new Client(['handler' => new MockHandler([new Response()])]);
$request = new Request('GET', 'http://example.com');
$r = $client->send($request);
self::assertInstanceOf(ResponseInterface::class, $r);
self::assertSame(200, $r->getStatusCode());
}
public function testClientHasOptions()
{
$client = new Client([
'base_uri' => 'http://foo.com',
'timeout' => 2,
'headers' => ['bar' => 'baz'],
'handler' => new MockHandler()
]);
$base = $client->getConfig('base_uri');
self::assertSame('http://foo.com', (string) $base);
self::assertInstanceOf(Uri::class, $base);
self::assertNotNull($client->getConfig('handler'));
self::assertSame(2, $client->getConfig('timeout'));
self::assertArrayHasKey('timeout', $client->getConfig());
self::assertArrayHasKey('headers', $client->getConfig());
}
public function testCanMergeOnBaseUri()
{
$mock = new MockHandler([new Response()]);
$client = new Client([
'base_uri' => 'http://foo.com/bar/',
'handler' => $mock
]);
$client->get('baz');
self::assertSame(
'http://foo.com/bar/baz',
(string)$mock->getLastRequest()->getUri()
);
}
public function testCanMergeOnBaseUriWithRequest()
{
$mock = new MockHandler([new Response(), new Response()]);
$client = new Client([
'handler' => $mock,
'base_uri' => 'http://foo.com/bar/'
]);
$client->request('GET', new Uri('baz'));
self::assertSame(
'http://foo.com/bar/baz',
(string) $mock->getLastRequest()->getUri()
);
$client->request('GET', new Uri('baz'), ['base_uri' => 'http://example.com/foo/']);
self::assertSame(
'http://example.com/foo/baz',
(string) $mock->getLastRequest()->getUri(),
'Can overwrite the base_uri through the request options'
);
}
public function testCanUseRelativeUriWithSend()
{
$mock = new MockHandler([new Response()]);
$client = new Client([
'handler' => $mock,
'base_uri' => 'http://bar.com'
]);
self::assertSame('http://bar.com', (string) $client->getConfig('base_uri'));
$request = new Request('GET', '/baz');
$client->send($request);
self::assertSame(
'http://bar.com/baz',
(string) $mock->getLastRequest()->getUri()
);
}
public function testMergesDefaultOptionsAndDoesNotOverwriteUa()
{
$c = new Client(['headers' => ['User-agent' => 'foo']]);
self::assertSame(['User-agent' => 'foo'], $c->getConfig('headers'));
self::assertInternalType('array', $c->getConfig('allow_redirects'));
self::assertTrue($c->getConfig('http_errors'));
self::assertTrue($c->getConfig('decode_content'));
self::assertTrue($c->getConfig('verify'));
}
public function testDoesNotOverwriteHeaderWithDefault()
{
$mock = new MockHandler([new Response()]);
$c = new Client([
'headers' => ['User-agent' => 'foo'],
'handler' => $mock
]);
$c->get('http://example.com', ['headers' => ['User-Agent' => 'bar']]);
self::assertSame('bar', $mock->getLastRequest()->getHeaderLine('User-Agent'));
}
public function testDoesNotOverwriteHeaderWithDefaultInRequest()
{
$mock = new MockHandler([new Response()]);
$c = new Client([
'headers' => ['User-agent' => 'foo'],
'handler' => $mock
]);
$request = new Request('GET', Server::$url, ['User-Agent' => 'bar']);
$c->send($request);
self::assertSame('bar', $mock->getLastRequest()->getHeaderLine('User-Agent'));
}
public function testDoesOverwriteHeaderWithSetRequestOption()
{
$mock = new MockHandler([new Response()]);
$c = new Client([
'headers' => ['User-agent' => 'foo'],
'handler' => $mock
]);
$request = new Request('GET', Server::$url, ['User-Agent' => 'bar']);
$c->send($request, ['headers' => ['User-Agent' => 'YO']]);
self::assertSame('YO', $mock->getLastRequest()->getHeaderLine('User-Agent'));
}
public function testCanUnsetRequestOptionWithNull()
{
$mock = new MockHandler([new Response()]);
$c = new Client([
'headers' => ['foo' => 'bar'],
'handler' => $mock
]);
$c->get('http://example.com', ['headers' => null]);
self::assertFalse($mock->getLastRequest()->hasHeader('foo'));
}
public function testRewriteExceptionsToHttpErrors()
{
$client = new Client(['handler' => new MockHandler([new Response(404)])]);
$res = $client->get('http://foo.com', ['exceptions' => false]);
self::assertSame(404, $res->getStatusCode());
}
public function testRewriteSaveToToSink()
{
$r = Psr7\stream_for(fopen('php://temp', 'r+'));
$mock = new MockHandler([new Response(200, [], 'foo')]);
$client = new Client(['handler' => $mock]);
$client->get('http://foo.com', ['save_to' => $r]);
self::assertSame($r, $mock->getLastOptions()['sink']);
}
public function testAllowRedirectsCanBeTrue()
{
$mock = new MockHandler([new Response(200, [], 'foo')]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get('http://foo.com', ['allow_redirects' => true]);
self::assertInternalType('array', $mock->getLastOptions()['allow_redirects']);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage allow_redirects must be true, false, or array
*/
public function testValidatesAllowRedirects()
{
$mock = new MockHandler([new Response(200, [], 'foo')]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get('http://foo.com', ['allow_redirects' => 'foo']);
}
/**
* @expectedException \GuzzleHttp\Exception\ClientException
*/
public function testThrowsHttpErrorsByDefault()
{
$mock = new MockHandler([new Response(404)]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get('http://foo.com');
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage cookies must be an instance of GuzzleHttp\Cookie\CookieJarInterface
*/
public function testValidatesCookies()
{
$mock = new MockHandler([new Response(200, [], 'foo')]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get('http://foo.com', ['cookies' => 'foo']);
}
public function testSetCookieToTrueUsesSharedJar()
{
$mock = new MockHandler([
new Response(200, ['Set-Cookie' => 'foo=bar']),
new Response()
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler, 'cookies' => true]);
$client->get('http://foo.com');
$client->get('http://foo.com');
self::assertSame('foo=bar', $mock->getLastRequest()->getHeaderLine('Cookie'));
}
public function testSetCookieToJar()
{
$mock = new MockHandler([
new Response(200, ['Set-Cookie' => 'foo=bar']),
new Response()
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$jar = new CookieJar();
$client->get('http://foo.com', ['cookies' => $jar]);
$client->get('http://foo.com', ['cookies' => $jar]);
self::assertSame('foo=bar', $mock->getLastRequest()->getHeaderLine('Cookie'));
}
public function testCanDisableContentDecoding()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->get('http://foo.com', ['decode_content' => false]);
$last = $mock->getLastRequest();
self::assertFalse($last->hasHeader('Accept-Encoding'));
self::assertFalse($mock->getLastOptions()['decode_content']);
}
public function testCanSetContentDecodingToValue()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->get('http://foo.com', ['decode_content' => 'gzip']);
$last = $mock->getLastRequest();
self::assertSame('gzip', $last->getHeaderLine('Accept-Encoding'));
self::assertSame('gzip', $mock->getLastOptions()['decode_content']);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testValidatesHeaders()
{
$mock = new MockHandler();
$client = new Client(['handler' => $mock]);
$client->get('http://foo.com', ['headers' => 'foo']);
}
public function testAddsBody()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$request = new Request('PUT', 'http://foo.com');
$client->send($request, ['body' => 'foo']);
$last = $mock->getLastRequest();
self::assertSame('foo', (string) $last->getBody());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testValidatesQuery()
{
$mock = new MockHandler();
$client = new Client(['handler' => $mock]);
$request = new Request('PUT', 'http://foo.com');
$client->send($request, ['query' => false]);
}
public function testQueryCanBeString()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$request = new Request('PUT', 'http://foo.com');
$client->send($request, ['query' => 'foo']);
self::assertSame('foo', $mock->getLastRequest()->getUri()->getQuery());
}
public function testQueryCanBeArray()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$request = new Request('PUT', 'http://foo.com');
$client->send($request, ['query' => ['foo' => 'bar baz']]);
self::assertSame('foo=bar%20baz', $mock->getLastRequest()->getUri()->getQuery());
}
public function testCanAddJsonData()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$request = new Request('PUT', 'http://foo.com');
$client->send($request, ['json' => ['foo' => 'bar']]);
$last = $mock->getLastRequest();
self::assertSame('{"foo":"bar"}', (string) $mock->getLastRequest()->getBody());
self::assertSame('application/json', $last->getHeaderLine('Content-Type'));
}
public function testCanAddJsonDataWithoutOverwritingContentType()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$request = new Request('PUT', 'http://foo.com');
$client->send($request, [
'headers' => ['content-type' => 'foo'],
'json' => 'a'
]);
$last = $mock->getLastRequest();
self::assertSame('"a"', (string) $mock->getLastRequest()->getBody());
self::assertSame('foo', $last->getHeaderLine('Content-Type'));
}
public function testCanAddJsonDataWithNullHeader()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$request = new Request('PUT', 'http://foo.com');
$client->send($request, [
'headers' => null,
'json' => 'a'
]);
$last = $mock->getLastRequest();
self::assertSame('"a"', (string) $mock->getLastRequest()->getBody());
self::assertSame('application/json', $last->getHeaderLine('Content-Type'));
}
public function testAuthCanBeTrue()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->get('http://foo.com', ['auth' => false]);
$last = $mock->getLastRequest();
self::assertFalse($last->hasHeader('Authorization'));
}
public function testAuthCanBeArrayForBasicAuth()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->get('http://foo.com', ['auth' => ['a', 'b']]);
$last = $mock->getLastRequest();
self::assertSame('Basic YTpi', $last->getHeaderLine('Authorization'));
}
public function testAuthCanBeArrayForDigestAuth()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->get('http://foo.com', ['auth' => ['a', 'b', 'digest']]);
$last = $mock->getLastOptions();
self::assertSame([
CURLOPT_HTTPAUTH => 2,
CURLOPT_USERPWD => 'a:b'
], $last['curl']);
}
public function testAuthCanBeArrayForNtlmAuth()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->get('http://foo.com', ['auth' => ['a', 'b', 'ntlm']]);
$last = $mock->getLastOptions();
self::assertSame([
CURLOPT_HTTPAUTH => 8,
CURLOPT_USERPWD => 'a:b'
], $last['curl']);
}
public function testAuthCanBeCustomType()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->get('http://foo.com', ['auth' => 'foo']);
$last = $mock->getLastOptions();
self::assertSame('foo', $last['auth']);
}
public function testCanAddFormParams()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->post('http://foo.com', [
'form_params' => [
'foo' => 'bar bam',
'baz' => ['boo' => 'qux']
]
]);
$last = $mock->getLastRequest();
self::assertSame(
'application/x-www-form-urlencoded',
$last->getHeaderLine('Content-Type')
);
self::assertSame(
'foo=bar+bam&baz%5Bboo%5D=qux',
(string) $last->getBody()
);
}
public function testFormParamsEncodedProperly()
{
$separator = ini_get('arg_separator.output');
ini_set('arg_separator.output', '&amp;');
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->post('http://foo.com', [
'form_params' => [
'foo' => 'bar bam',
'baz' => ['boo' => 'qux']
]
]);
$last = $mock->getLastRequest();
self::assertSame(
'foo=bar+bam&baz%5Bboo%5D=qux',
(string) $last->getBody()
);
ini_set('arg_separator.output', $separator);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresThatFormParamsAndMultipartAreExclusive()
{
$client = new Client(['handler' => function () {
}]);
$client->post('http://foo.com', [
'form_params' => ['foo' => 'bar bam'],
'multipart' => []
]);
}
public function testCanSendMultipart()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->post('http://foo.com', [
'multipart' => [
[
'name' => 'foo',
'contents' => 'bar'
],
[
'name' => 'test',
'contents' => fopen(__FILE__, 'r')
]
]
]);
$last = $mock->getLastRequest();
self::assertContains(
'multipart/form-data; boundary=',
$last->getHeaderLine('Content-Type')
);
self::assertContains(
'Content-Disposition: form-data; name="foo"',
(string) $last->getBody()
);
self::assertContains('bar', (string) $last->getBody());
self::assertContains(
'Content-Disposition: form-data; name="foo"' . "\r\n",
(string) $last->getBody()
);
self::assertContains(
'Content-Disposition: form-data; name="test"; filename="ClientTest.php"',
(string) $last->getBody()
);
}
public function testCanSendMultipartWithExplicitBody()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->send(
new Request(
'POST',
'http://foo.com',
[],
new Psr7\MultipartStream(
[
[
'name' => 'foo',
'contents' => 'bar',
],
[
'name' => 'test',
'contents' => fopen(__FILE__, 'r'),
],
]
)
)
);
$last = $mock->getLastRequest();
self::assertContains(
'multipart/form-data; boundary=',
$last->getHeaderLine('Content-Type')
);
self::assertContains(
'Content-Disposition: form-data; name="foo"',
(string) $last->getBody()
);
self::assertContains('bar', (string) $last->getBody());
self::assertContains(
'Content-Disposition: form-data; name="foo"' . "\r\n",
(string) $last->getBody()
);
self::assertContains(
'Content-Disposition: form-data; name="test"; filename="ClientTest.php"',
(string) $last->getBody()
);
}
public function testUsesProxyEnvironmentVariables()
{
$http = getenv('HTTP_PROXY');
$https = getenv('HTTPS_PROXY');
$no = getenv('NO_PROXY');
$client = new Client();
self::assertNull($client->getConfig('proxy'));
putenv('HTTP_PROXY=127.0.0.1');
$client = new Client();
self::assertSame(
['http' => '127.0.0.1'],
$client->getConfig('proxy')
);
putenv('HTTPS_PROXY=127.0.0.2');
putenv('NO_PROXY=127.0.0.3, 127.0.0.4');
$client = new Client();
self::assertSame(
['http' => '127.0.0.1', 'https' => '127.0.0.2', 'no' => ['127.0.0.3','127.0.0.4']],
$client->getConfig('proxy')
);
putenv("HTTP_PROXY=$http");
putenv("HTTPS_PROXY=$https");
putenv("NO_PROXY=$no");
}
public function testRequestSendsWithSync()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->request('GET', 'http://foo.com');
self::assertTrue($mock->getLastOptions()['synchronous']);
}
public function testSendSendsWithSync()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$client->send(new Request('GET', 'http://foo.com'));
self::assertTrue($mock->getLastOptions()['synchronous']);
}
public function testCanSetCustomHandler()
{
$mock = new MockHandler([new Response(500)]);
$client = new Client(['handler' => $mock]);
$mock2 = new MockHandler([new Response(200)]);
self::assertSame(
200,
$client->send(new Request('GET', 'http://foo.com'), [
'handler' => $mock2
])->getStatusCode()
);
}
public function testProperlyBuildsQuery()
{
$mock = new MockHandler([new Response()]);
$client = new Client(['handler' => $mock]);
$request = new Request('PUT', 'http://foo.com');
$client->send($request, ['query' => ['foo' => 'bar', 'john' => 'doe']]);
self::assertSame('foo=bar&john=doe', $mock->getLastRequest()->getUri()->getQuery());
}
public function testSendSendsWithIpAddressAndPortAndHostHeaderInRequestTheHostShouldBePreserved()
{
$mockHandler = new MockHandler([new Response()]);
$client = new Client(['base_uri' => 'http://127.0.0.1:8585', 'handler' => $mockHandler]);
$request = new Request('GET', '/test', ['Host'=>'foo.com']);
$client->send($request);
self::assertSame('foo.com', $mockHandler->getLastRequest()->getHeader('Host')[0]);
}
public function testSendSendsWithDomainAndHostHeaderInRequestTheHostShouldBePreserved()
{
$mockHandler = new MockHandler([new Response()]);
$client = new Client(['base_uri' => 'http://foo2.com', 'handler' => $mockHandler]);
$request = new Request('GET', '/test', ['Host'=>'foo.com']);
$client->send($request);
self::assertSame('foo.com', $mockHandler->getLastRequest()->getHeader('Host')[0]);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testValidatesSink()
{
$mockHandler = new MockHandler([new Response()]);
$client = new Client(['handler' => $mockHandler]);
$client->get('http://test.com', ['sink' => true]);
}
public function testHttpDefaultSchemeIfUriHasNone()
{
$mockHandler = new MockHandler([new Response()]);
$client = new Client(['handler' => $mockHandler]);
$client->request('GET', '//example.org/test');
self::assertSame('http://example.org/test', (string) $mockHandler->getLastRequest()->getUri());
}
public function testOnlyAddSchemeWhenHostIsPresent()
{
$mockHandler = new MockHandler([new Response()]);
$client = new Client(['handler' => $mockHandler]);
$client->request('GET', 'baz');
self::assertSame(
'baz',
(string) $mockHandler->getLastRequest()->getUri()
);
}
/**
* @expectedException InvalidArgumentException
*/
public function testHandlerIsCallable()
{
new Client(['handler' => 'not_cllable']);
}
public function testResponseBodyAsString()
{
$responseBody = '{ "package": "guzzle" }';
$mock = new MockHandler([new Response(200, ['Content-Type' => 'application/json'], $responseBody)]);
$client = new Client(['handler' => $mock]);
$request = new Request('GET', 'http://foo.com');
$response = $client->send($request, ['json' => ['a' => 'b']]);
self::assertSame($responseBody, (string) $response->getBody());
}
public function testResponseContent()
{
$responseBody = '{ "package": "guzzle" }';
$mock = new MockHandler([new Response(200, ['Content-Type' => 'application/json'], $responseBody)]);
$client = new Client(['handler' => $mock]);
$request = new Request('POST', 'http://foo.com');
$response = $client->send($request, ['json' => ['a' => 'b']]);
self::assertSame($responseBody, $response->getBody()->getContents());
}
public function testIdnSupportDefaultValue()
{
$mockHandler = new MockHandler([new Response()]);
$client = new Client(['handler' => $mockHandler]);
$config = $client->getConfig();
self::assertTrue($config['idn_conversion']);
}
public function testIdnIsTranslatedToAsciiWhenConversionIsEnabled()
{
$mockHandler = new MockHandler([new Response()]);
$client = new Client(['handler' => $mockHandler]);
$client->request('GET', 'https://яндекс.рф/images', ['idn_conversion' => true]);
$request = $mockHandler->getLastRequest();
self::assertSame('https://xn--d1acpjx3f.xn--p1ai/images', (string) $request->getUri());
self::assertSame('xn--d1acpjx3f.xn--p1ai', (string) $request->getHeaderLine('Host'));
}
public function testIdnStaysTheSameWhenConversionIsDisabled()
{
$mockHandler = new MockHandler([new Response()]);
$client = new Client(['handler' => $mockHandler]);
$client->request('GET', 'https://яндекс.рф/images', ['idn_conversion' => false]);
$request = $mockHandler->getLastRequest();
self::assertSame('https://яндекс.рф/images', (string) $request->getUri());
self::assertSame('яндекс.рф', (string) $request->getHeaderLine('Host'));
}
/**
* @expectedException \GuzzleHttp\Exception\InvalidArgumentException
* @expectedExceptionMessage IDN conversion failed
*/
public function testExceptionOnInvalidIdn()
{
$mockHandler = new MockHandler([new Response()]);
$client = new Client(['handler' => $mockHandler]);
$client->request('GET', 'https://-яндекс.рф/images', ['idn_conversion' => true]);
}
/**
* @depends testCanUseRelativeUriWithSend
* @depends testIdnSupportDefaultValue
*/
public function testIdnBaseUri()
{
$mock = new MockHandler([new Response()]);
$client = new Client([
'handler' => $mock,
'base_uri' => 'http://яндекс.рф',
]);
self::assertSame('http://яндекс.рф', (string) $client->getConfig('base_uri'));
$request = new Request('GET', '/baz');
$client->send($request);
self::assertSame('http://xn--d1acpjx3f.xn--p1ai/baz', (string) $mock->getLastRequest()->getUri());
self::assertSame('xn--d1acpjx3f.xn--p1ai', (string) $mock->getLastRequest()->getHeaderLine('Host'));
}
public function testIdnWithRedirect()
{
$mockHandler = new MockHandler([
new Response(302, ['Location' => 'http://www.tést.com/whatever']),
new Response()
]);
$handler = HandlerStack::create($mockHandler);
$requests = [];
$handler->push(Middleware::history($requests));
$client = new Client(['handler' => $handler]);
$client->request('GET', 'https://яндекс.рф/images', [
RequestOptions::ALLOW_REDIRECTS => [
'referer' => true,
'track_redirects' => true
],
'idn_conversion' => true
]);
$request = $mockHandler->getLastRequest();
self::assertSame('http://www.xn--tst-bma.com/whatever', (string) $request->getUri());
self::assertSame('www.xn--tst-bma.com', (string) $request->getHeaderLine('Host'));
$request = $requests[0]['request'];
self::assertSame('https://xn--d1acpjx3f.xn--p1ai/images', (string) $request->getUri());
self::assertSame('xn--d1acpjx3f.xn--p1ai', (string) $request->getHeaderLine('Host'));
}
}

View File

@ -0,0 +1,449 @@
<?php
namespace GuzzleHttp\Tests\CookieJar;
use DateInterval;
use DateTime;
use DateTimeImmutable;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Cookie\SetCookie;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
/**
* @covers GuzzleHttp\Cookie\CookieJar
*/
class CookieJarTest extends TestCase
{
/** @var CookieJar */
private $jar;
public function setUp()
{
$this->jar = new CookieJar();
}
protected function getTestCookies()
{
return [
new SetCookie(['Name' => 'foo', 'Value' => 'bar', 'Domain' => 'foo.com', 'Path' => '/', 'Discard' => true]),
new SetCookie(['Name' => 'test', 'Value' => '123', 'Domain' => 'baz.com', 'Path' => '/foo', 'Expires' => 2]),
new SetCookie(['Name' => 'you', 'Value' => '123', 'Domain' => 'bar.com', 'Path' => '/boo', 'Expires' => time() + 1000])
];
}
public function testCreatesFromArray()
{
$jar = CookieJar::fromArray([
'foo' => 'bar',
'baz' => 'bam'
], 'example.com');
self::assertCount(2, $jar);
}
public function testEmptyJarIsCountable()
{
self::assertCount(0, new CookieJar());
}
public function testGetsCookiesByName()
{
$cookies = $this->getTestCookies();
foreach ($this->getTestCookies() as $cookie) {
$this->jar->setCookie($cookie);
}
$testCookie = $cookies[0];
self::assertEquals($testCookie, $this->jar->getCookieByName($testCookie->getName()));
self::assertNull($this->jar->getCookieByName("doesnotexist"));
self::assertNull($this->jar->getCookieByName(""));
}
/**
* Provides test data for cookie cookieJar retrieval
*/
public function getCookiesDataProvider()
{
return [
[['foo', 'baz', 'test', 'muppet', 'googoo'], '', '', '', false],
[['foo', 'baz', 'muppet', 'googoo'], '', '', '', true],
[['googoo'], 'www.example.com', '', '', false],
[['muppet', 'googoo'], 'test.y.example.com', '', '', false],
[['foo', 'baz'], 'example.com', '', '', false],
[['muppet'], 'x.y.example.com', '/acme/', '', false],
[['muppet'], 'x.y.example.com', '/acme/test/', '', false],
[['googoo'], 'x.y.example.com', '/test/acme/test/', '', false],
[['foo', 'baz'], 'example.com', '', '', false],
[['baz'], 'example.com', '', 'baz', false],
];
}
public function testStoresAndRetrievesCookies()
{
$cookies = $this->getTestCookies();
foreach ($cookies as $cookie) {
self::assertTrue($this->jar->setCookie($cookie));
}
self::assertCount(3, $this->jar);
self::assertCount(3, $this->jar->getIterator());
self::assertEquals($cookies, $this->jar->getIterator()->getArrayCopy());
}
public function testRemovesTemporaryCookies()
{
$cookies = $this->getTestCookies();
foreach ($this->getTestCookies() as $cookie) {
$this->jar->setCookie($cookie);
}
$this->jar->clearSessionCookies();
self::assertEquals(
[$cookies[1], $cookies[2]],
$this->jar->getIterator()->getArrayCopy()
);
}
public function testRemovesSelectively()
{
foreach ($this->getTestCookies() as $cookie) {
$this->jar->setCookie($cookie);
}
// Remove foo.com cookies
$this->jar->clear('foo.com');
self::assertCount(2, $this->jar);
// Try again, removing no further cookies
$this->jar->clear('foo.com');
self::assertCount(2, $this->jar);
// Remove bar.com cookies with path of /boo
$this->jar->clear('bar.com', '/boo');
self::assertCount(1, $this->jar);
// Remove cookie by name
$this->jar->clear(null, null, 'test');
self::assertCount(0, $this->jar);
}
public function testDoesNotAddIncompleteCookies()
{
self::assertFalse($this->jar->setCookie(new SetCookie()));
self::assertFalse($this->jar->setCookie(new SetCookie([
'Name' => 'foo'
])));
self::assertFalse($this->jar->setCookie(new SetCookie([
'Name' => false
])));
self::assertFalse($this->jar->setCookie(new SetCookie([
'Name' => true
])));
self::assertFalse($this->jar->setCookie(new SetCookie([
'Name' => 'foo',
'Domain' => 'foo.com'
])));
}
public function testDoesNotAddEmptyCookies()
{
self::assertFalse($this->jar->setCookie(new SetCookie([
'Name' => '',
'Domain' => 'foo.com',
'Value' => 0
])));
}
public function testDoesAddValidCookies()
{
self::assertTrue($this->jar->setCookie(new SetCookie([
'Name' => '0',
'Domain' => 'foo.com',
'Value' => 0
])));
self::assertTrue($this->jar->setCookie(new SetCookie([
'Name' => 'foo',
'Domain' => 'foo.com',
'Value' => 0
])));
self::assertTrue($this->jar->setCookie(new SetCookie([
'Name' => 'foo',
'Domain' => 'foo.com',
'Value' => 0.0
])));
self::assertTrue($this->jar->setCookie(new SetCookie([
'Name' => 'foo',
'Domain' => 'foo.com',
'Value' => '0'
])));
}
public function testOverwritesCookiesThatAreOlderOrDiscardable()
{
$t = time() + 1000;
$data = [
'Name' => 'foo',
'Value' => 'bar',
'Domain' => '.example.com',
'Path' => '/',
'Max-Age' => '86400',
'Secure' => true,
'Discard' => true,
'Expires' => $t
];
// Make sure that the discard cookie is overridden with the non-discard
self::assertTrue($this->jar->setCookie(new SetCookie($data)));
self::assertCount(1, $this->jar);
$data['Discard'] = false;
self::assertTrue($this->jar->setCookie(new SetCookie($data)));
self::assertCount(1, $this->jar);
$c = $this->jar->getIterator()->getArrayCopy();
self::assertFalse($c[0]->getDiscard());
// Make sure it doesn't duplicate the cookie
$this->jar->setCookie(new SetCookie($data));
self::assertCount(1, $this->jar);
// Make sure the more future-ful expiration date supersede the other
$data['Expires'] = time() + 2000;
self::assertTrue($this->jar->setCookie(new SetCookie($data)));
self::assertCount(1, $this->jar);
$c = $this->jar->getIterator()->getArrayCopy();
self::assertNotEquals($t, $c[0]->getExpires());
}
public function testOverwritesCookiesThatHaveChanged()
{
$t = time() + 1000;
$data = [
'Name' => 'foo',
'Value' => 'bar',
'Domain' => '.example.com',
'Path' => '/',
'Max-Age' => '86400',
'Secure' => true,
'Discard' => true,
'Expires' => $t
];
// Make sure that the discard cookie is overridden with the non-discard
self::assertTrue($this->jar->setCookie(new SetCookie($data)));
$data['Value'] = 'boo';
self::assertTrue($this->jar->setCookie(new SetCookie($data)));
self::assertCount(1, $this->jar);
// Changing the value plus a parameter also must overwrite the existing one
$data['Value'] = 'zoo';
$data['Secure'] = false;
self::assertTrue($this->jar->setCookie(new SetCookie($data)));
self::assertCount(1, $this->jar);
$c = $this->jar->getIterator()->getArrayCopy();
self::assertSame('zoo', $c[0]->getValue());
}
public function testAddsCookiesFromResponseWithRequest()
{
$response = new Response(200, [
'Set-Cookie' => "fpc=d=.Hm.yh4.1XmJWjJfs4orLQzKzPImxklQoxXSHOZATHUSEFciRueW_7704iYUtsXNEXq0M92Px2glMdWypmJ7HIQl6XIUvrZimWjQ3vIdeuRbI.FNQMAfcxu_XN1zSx7l.AcPdKL6guHc2V7hIQFhnjRW0rxm2oHY1P4bGQxFNz7f.tHm12ZD3DbdMDiDy7TBXsuP4DM-&v=2; expires=Fri, 02-Mar-2019 02:17:40 GMT;"
]);
$request = new Request('GET', 'http://www.example.com');
$this->jar->extractCookies($request, $response);
self::assertCount(1, $this->jar);
}
public function getMatchingCookiesDataProvider()
{
return [
['https://example.com', 'foo=bar; baz=foobar'],
['http://example.com', ''],
['https://example.com:8912', 'foo=bar; baz=foobar'],
['https://foo.example.com', 'foo=bar; baz=foobar'],
['http://foo.example.com/test/acme/', 'googoo=gaga']
];
}
/**
* @dataProvider getMatchingCookiesDataProvider
*/
public function testReturnsCookiesMatchingRequests($url, $cookies)
{
$bag = [
new SetCookie([
'Name' => 'foo',
'Value' => 'bar',
'Domain' => 'example.com',
'Path' => '/',
'Max-Age' => '86400',
'Secure' => true
]),
new SetCookie([
'Name' => 'baz',
'Value' => 'foobar',
'Domain' => 'example.com',
'Path' => '/',
'Max-Age' => '86400',
'Secure' => true
]),
new SetCookie([
'Name' => 'test',
'Value' => '123',
'Domain' => 'www.foobar.com',
'Path' => '/path/',
'Discard' => true
]),
new SetCookie([
'Name' => 'muppet',
'Value' => 'cookie_monster',
'Domain' => '.y.example.com',
'Path' => '/acme/',
'Expires' => time() + 86400
]),
new SetCookie([
'Name' => 'googoo',
'Value' => 'gaga',
'Domain' => '.example.com',
'Path' => '/test/acme/',
'Max-Age' => 1500
])
];
foreach ($bag as $cookie) {
$this->jar->setCookie($cookie);
}
$request = new Request('GET', $url);
$request = $this->jar->withCookieHeader($request);
self::assertSame($cookies, $request->getHeaderLine('Cookie'));
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Invalid cookie: Cookie name must not contain invalid characters: ASCII Control characters (0-31;127), space, tab and the following characters: ()<>@,;:\"/?={}
*/
public function testThrowsExceptionWithStrictMode()
{
$a = new CookieJar(true);
$a->setCookie(new SetCookie(['Name' => "abc\n", 'Value' => 'foo', 'Domain' => 'bar']));
}
public function testDeletesCookiesByName()
{
$cookies = $this->getTestCookies();
$cookies[] = new SetCookie([
'Name' => 'other',
'Value' => '123',
'Domain' => 'bar.com',
'Path' => '/boo',
'Expires' => time() + 1000
]);
$jar = new CookieJar();
foreach ($cookies as $cookie) {
$jar->setCookie($cookie);
}
self::assertCount(4, $jar);
$jar->clear('bar.com', '/boo', 'other');
self::assertCount(3, $jar);
$names = array_map(function (SetCookie $c) {
return $c->getName();
}, $jar->getIterator()->getArrayCopy());
self::assertSame(['foo', 'test', 'you'], $names);
}
public function testCanConvertToAndLoadFromArray()
{
$jar = new CookieJar(true);
foreach ($this->getTestCookies() as $cookie) {
$jar->setCookie($cookie);
}
self::assertCount(3, $jar);
$arr = $jar->toArray();
self::assertCount(3, $arr);
$newCookieJar = new CookieJar(false, $arr);
self::assertCount(3, $newCookieJar);
self::assertSame($jar->toArray(), $newCookieJar->toArray());
}
public function testAddsCookiesWithEmptyPathFromResponse()
{
$response = new Response(200, [
'Set-Cookie' => "fpc=foobar; expires={$this->futureExpirationDate()}; path=;"
]);
$request = new Request('GET', 'http://www.example.com');
$this->jar->extractCookies($request, $response);
$newRequest = $this->jar->withCookieHeader(new Request('GET', 'http://www.example.com/foo'));
self::assertTrue($newRequest->hasHeader('Cookie'));
}
public function getCookiePathsDataProvider()
{
return [
['', '/'],
['/', '/'],
['/foo', '/'],
['/foo/bar', '/foo'],
['/foo/bar/', '/foo/bar'],
];
}
/**
* @dataProvider getCookiePathsDataProvider
*/
public function testCookiePathWithEmptySetCookiePath($uriPath, $cookiePath)
{
$response = (new Response(200))
->withAddedHeader(
'Set-Cookie',
"foo=bar; expires={$this->futureExpirationDate()}; domain=www.example.com; path=;"
)
->withAddedHeader(
'Set-Cookie',
"bar=foo; expires={$this->futureExpirationDate()}; domain=www.example.com; path=foobar;"
)
;
$request = (new Request('GET', "https://www.example.com{$uriPath}"));
$this->jar->extractCookies($request, $response);
self::assertSame($cookiePath, $this->jar->toArray()[0]['Path']);
self::assertSame($cookiePath, $this->jar->toArray()[1]['Path']);
}
public function getDomainMatchesProvider()
{
return [
['www.example.com', 'www.example.com', true],
['www.example.com', 'www.EXAMPLE.com', true],
['www.example.com', 'www.example.net', false],
['www.example.com', 'ftp.example.com', false],
['www.example.com', 'example.com', true],
['www.example.com', 'EXAMPLE.com', true],
['fra.de.example.com', 'EXAMPLE.com', true],
['www.EXAMPLE.com', 'www.example.com', true],
['www.EXAMPLE.com', 'www.example.COM', true],
];
}
/**
* @dataProvider getDomainMatchesProvider
*/
public function testIgnoresCookiesForMismatchingDomains($requestHost, $domainAttribute, $matches)
{
$response = (new Response(200))
->withAddedHeader(
'Set-Cookie',
"foo=bar; expires={$this->futureExpirationDate()}; domain={$domainAttribute}; path=/;"
)
;
$request = (new Request('GET', "https://{$requestHost}/"));
$this->jar->extractCookies($request, $response);
self::assertCount($matches ? 1 : 0, $this->jar->toArray());
}
private function futureExpirationDate()
{
return (new DateTimeImmutable)->add(new DateInterval('P1D'))->format(DateTime::COOKIE);
}
}

View File

@ -0,0 +1,88 @@
<?php
namespace GuzzleHttp\Tests\CookieJar;
use GuzzleHttp\Cookie\FileCookieJar;
use GuzzleHttp\Cookie\SetCookie;
use PHPUnit\Framework\TestCase;
/**
* @covers GuzzleHttp\Cookie\FileCookieJar
*/
class FileCookieJarTest extends TestCase
{
private $file;
public function setUp()
{
$this->file = tempnam('/tmp', 'file-cookies');
}
/**
* @expectedException \RuntimeException
*/
public function testValidatesCookieFile()
{
file_put_contents($this->file, 'true');
new FileCookieJar($this->file);
}
public function testLoadsFromFile()
{
$jar = new FileCookieJar($this->file);
self::assertSame([], $jar->getIterator()->getArrayCopy());
unlink($this->file);
}
/**
* @dataProvider providerPersistsToFileFileParameters
*/
public function testPersistsToFile($testSaveSessionCookie = false)
{
$jar = new FileCookieJar($this->file, $testSaveSessionCookie);
$jar->setCookie(new SetCookie([
'Name' => 'foo',
'Value' => 'bar',
'Domain' => 'foo.com',
'Expires' => time() + 1000
]));
$jar->setCookie(new SetCookie([
'Name' => 'baz',
'Value' => 'bar',
'Domain' => 'foo.com',
'Expires' => time() + 1000
]));
$jar->setCookie(new SetCookie([
'Name' => 'boo',
'Value' => 'bar',
'Domain' => 'foo.com',
]));
self::assertCount(3, $jar);
unset($jar);
// Make sure it wrote to the file
$contents = file_get_contents($this->file);
self::assertNotEmpty($contents);
// Load the cookieJar from the file
$jar = new FileCookieJar($this->file);
if ($testSaveSessionCookie) {
self::assertCount(3, $jar);
} else {
// Weeds out temporary and session cookies
self::assertCount(2, $jar);
}
unset($jar);
unlink($this->file);
}
public function providerPersistsToFileFileParameters()
{
return [
[false],
[true]
];
}
}

View File

@ -0,0 +1,92 @@
<?php
namespace GuzzleHttp\Tests\CookieJar;
use GuzzleHttp\Cookie\SessionCookieJar;
use GuzzleHttp\Cookie\SetCookie;
use PHPUnit\Framework\TestCase;
/**
* @covers GuzzleHttp\Cookie\SessionCookieJar
*/
class SessionCookieJarTest extends TestCase
{
private $sessionVar;
public function setUp()
{
$this->sessionVar = 'sessionKey';
if (!isset($_SESSION)) {
$_SESSION = [];
}
}
/**
* @expectedException \RuntimeException
*/
public function testValidatesCookieSession()
{
$_SESSION[$this->sessionVar] = 'true';
new SessionCookieJar($this->sessionVar);
}
public function testLoadsFromSession()
{
$jar = new SessionCookieJar($this->sessionVar);
self::assertSame([], $jar->getIterator()->getArrayCopy());
unset($_SESSION[$this->sessionVar]);
}
/**
* @dataProvider providerPersistsToSessionParameters
*/
public function testPersistsToSession($testSaveSessionCookie = false)
{
$jar = new SessionCookieJar($this->sessionVar, $testSaveSessionCookie);
$jar->setCookie(new SetCookie([
'Name' => 'foo',
'Value' => 'bar',
'Domain' => 'foo.com',
'Expires' => time() + 1000
]));
$jar->setCookie(new SetCookie([
'Name' => 'baz',
'Value' => 'bar',
'Domain' => 'foo.com',
'Expires' => time() + 1000
]));
$jar->setCookie(new SetCookie([
'Name' => 'boo',
'Value' => 'bar',
'Domain' => 'foo.com',
]));
self::assertCount(3, $jar);
unset($jar);
// Make sure it wrote to the sessionVar in $_SESSION
$contents = $_SESSION[$this->sessionVar];
self::assertNotEmpty($contents);
// Load the cookieJar from the file
$jar = new SessionCookieJar($this->sessionVar);
if ($testSaveSessionCookie) {
self::assertCount(3, $jar);
} else {
// Weeds out temporary and session cookies
self::assertCount(2, $jar);
}
unset($jar);
unset($_SESSION[$this->sessionVar]);
}
public function providerPersistsToSessionParameters()
{
return [
[false],
[true]
];
}
}

View File

@ -0,0 +1,445 @@
<?php
namespace GuzzleHttp\Tests\CookieJar;
use GuzzleHttp\Cookie\SetCookie;
use PHPUnit\Framework\TestCase;
/**
* @covers GuzzleHttp\Cookie\SetCookie
*/
class SetCookieTest extends TestCase
{
public function testInitializesDefaultValues()
{
$cookie = new SetCookie();
self::assertSame('/', $cookie->getPath());
}
public function testConvertsDateTimeMaxAgeToUnixTimestamp()
{
$cookie = new SetCookie(['Expires' => 'November 20, 1984']);
self::assertInternalType('integer', $cookie->getExpires());
}
public function testAddsExpiresBasedOnMaxAge()
{
$t = time();
$cookie = new SetCookie(['Max-Age' => 100]);
self::assertEquals($t + 100, $cookie->getExpires());
}
public function testHoldsValues()
{
$t = time();
$data = [
'Name' => 'foo',
'Value' => 'baz',
'Path' => '/bar',
'Domain' => 'baz.com',
'Expires' => $t,
'Max-Age' => 100,
'Secure' => true,
'Discard' => true,
'HttpOnly' => true,
'foo' => 'baz',
'bar' => 'bam'
];
$cookie = new SetCookie($data);
self::assertEquals($data, $cookie->toArray());
self::assertSame('foo', $cookie->getName());
self::assertSame('baz', $cookie->getValue());
self::assertSame('baz.com', $cookie->getDomain());
self::assertSame('/bar', $cookie->getPath());
self::assertSame($t, $cookie->getExpires());
self::assertSame(100, $cookie->getMaxAge());
self::assertTrue($cookie->getSecure());
self::assertTrue($cookie->getDiscard());
self::assertTrue($cookie->getHttpOnly());
self::assertSame('baz', $cookie->toArray()['foo']);
self::assertSame('bam', $cookie->toArray()['bar']);
$cookie->setName('a');
$cookie->setValue('b');
$cookie->setPath('c');
$cookie->setDomain('bar.com');
$cookie->setExpires(10);
$cookie->setMaxAge(200);
$cookie->setSecure(false);
$cookie->setHttpOnly(false);
$cookie->setDiscard(false);
self::assertSame('a', $cookie->getName());
self::assertSame('b', $cookie->getValue());
self::assertSame('c', $cookie->getPath());
self::assertSame('bar.com', $cookie->getDomain());
self::assertSame(10, $cookie->getExpires());
self::assertSame(200, $cookie->getMaxAge());
self::assertFalse($cookie->getSecure());
self::assertFalse($cookie->getDiscard());
self::assertFalse($cookie->getHttpOnly());
}
public function testDeterminesIfExpired()
{
$c = new SetCookie();
$c->setExpires(10);
self::assertTrue($c->isExpired());
$c->setExpires(time() + 10000);
self::assertFalse($c->isExpired());
}
public function testMatchesDomain()
{
$cookie = new SetCookie();
self::assertTrue($cookie->matchesDomain('baz.com'));
$cookie->setDomain('baz.com');
self::assertTrue($cookie->matchesDomain('baz.com'));
self::assertFalse($cookie->matchesDomain('bar.com'));
$cookie->setDomain('.baz.com');
self::assertTrue($cookie->matchesDomain('.baz.com'));
self::assertTrue($cookie->matchesDomain('foo.baz.com'));
self::assertFalse($cookie->matchesDomain('baz.bar.com'));
self::assertTrue($cookie->matchesDomain('baz.com'));
$cookie->setDomain('.127.0.0.1');
self::assertTrue($cookie->matchesDomain('127.0.0.1'));
$cookie->setDomain('127.0.0.1');
self::assertTrue($cookie->matchesDomain('127.0.0.1'));
$cookie->setDomain('.com.');
self::assertFalse($cookie->matchesDomain('baz.com'));
$cookie->setDomain('.local');
self::assertTrue($cookie->matchesDomain('example.local'));
$cookie->setDomain('example.com/'); // malformed domain
self::assertFalse($cookie->matchesDomain('example.com'));
}
public function pathMatchProvider()
{
return [
['/foo', '/foo', true],
['/foo', '/Foo', false],
['/foo', '/fo', false],
['/foo', '/foo/bar', true],
['/foo', '/foo/bar/baz', true],
['/foo', '/foo/bar//baz', true],
['/foo', '/foobar', false],
['/foo/bar', '/foo', false],
['/foo/bar', '/foobar', false],
['/foo/bar', '/foo/bar', true],
['/foo/bar', '/foo/bar/', true],
['/foo/bar', '/foo/bar/baz', true],
['/foo/bar/', '/foo/bar', false],
['/foo/bar/', '/foo/bar/', true],
['/foo/bar/', '/foo/bar/baz', true],
];
}
/**
* @dataProvider pathMatchProvider
*/
public function testMatchesPath($cookiePath, $requestPath, $isMatch)
{
$cookie = new SetCookie();
$cookie->setPath($cookiePath);
self::assertSame($isMatch, $cookie->matchesPath($requestPath));
}
public function cookieValidateProvider()
{
return [
['foo', 'baz', 'bar', true],
['0', '0', '0', true],
['foo[bar]', 'baz', 'bar', true],
['', 'baz', 'bar', 'The cookie name must not be empty'],
['foo', '', 'bar', 'The cookie value must not be empty'],
['foo', 'baz', '', 'The cookie domain must not be empty'],
["foo\r", 'baz', '0', 'Cookie name must not contain invalid characters: ASCII Control characters (0-31;127), space, tab and the following characters: ()<>@,;:\"/?={}'],
];
}
/**
* @dataProvider cookieValidateProvider
*/
public function testValidatesCookies($name, $value, $domain, $result)
{
$cookie = new SetCookie([
'Name' => $name,
'Value' => $value,
'Domain' => $domain,
]);
self::assertSame($result, $cookie->validate());
}
public function testDoesNotMatchIp()
{
$cookie = new SetCookie(['Domain' => '192.168.16.']);
self::assertFalse($cookie->matchesDomain('192.168.16.121'));
}
public function testConvertsToString()
{
$t = 1382916008;
$cookie = new SetCookie([
'Name' => 'test',
'Value' => '123',
'Domain' => 'foo.com',
'Expires' => $t,
'Path' => '/abc',
'HttpOnly' => true,
'Secure' => true
]);
self::assertSame(
'test=123; Domain=foo.com; Path=/abc; Expires=Sun, 27 Oct 2013 23:20:08 GMT; Secure; HttpOnly',
(string) $cookie
);
}
/**
* Provides the parsed information from a cookie
*
* @return array
*/
public function cookieParserDataProvider()
{
return [
[
'ASIHTTPRequestTestCookie=This+is+the+value; expires=Sat, 26-Jul-2008 17:00:42 GMT; path=/tests; domain=allseeing-i.com; PHPSESSID=6c951590e7a9359bcedde25cda73e43c; path=/;',
[
'Domain' => 'allseeing-i.com',
'Path' => '/',
'PHPSESSID' => '6c951590e7a9359bcedde25cda73e43c',
'Max-Age' => null,
'Expires' => 'Sat, 26-Jul-2008 17:00:42 GMT',
'Secure' => null,
'Discard' => null,
'Name' => 'ASIHTTPRequestTestCookie',
'Value' => 'This+is+the+value',
'HttpOnly' => false
]
],
['', []],
['foo', []],
['; foo', []],
[
'foo="bar"',
[
'Name' => 'foo',
'Value' => '"bar"',
'Discard' => null,
'Domain' => null,
'Expires' => null,
'Max-Age' => null,
'Path' => '/',
'Secure' => null,
'HttpOnly' => false
]
],
// Test setting a blank value for a cookie
[[
'foo=', 'foo =', 'foo =;', 'foo= ;', 'foo =', 'foo= '],
[
'Name' => 'foo',
'Value' => '',
'Discard' => null,
'Domain' => null,
'Expires' => null,
'Max-Age' => null,
'Path' => '/',
'Secure' => null,
'HttpOnly' => false
]
],
// Test setting a value and removing quotes
[[
'foo=1', 'foo =1', 'foo =1;', 'foo=1 ;', 'foo =1', 'foo= 1', 'foo = 1 ;'],
[
'Name' => 'foo',
'Value' => '1',
'Discard' => null,
'Domain' => null,
'Expires' => null,
'Max-Age' => null,
'Path' => '/',
'Secure' => null,
'HttpOnly' => false
]
],
// Some of the following tests are based on http://framework.zend.com/svn/framework/standard/trunk/tests/Zend/Http/CookieTest.php
[
'justacookie=foo; domain=example.com',
[
'Name' => 'justacookie',
'Value' => 'foo',
'Domain' => 'example.com',
'Discard' => null,
'Expires' => null,
'Max-Age' => null,
'Path' => '/',
'Secure' => null,
'HttpOnly' => false
]
],
[
'expires=tomorrow; secure; path=/Space Out/; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=.example.com',
[
'Name' => 'expires',
'Value' => 'tomorrow',
'Domain' => '.example.com',
'Path' => '/Space Out/',
'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
'Discard' => null,
'Secure' => true,
'Max-Age' => null,
'HttpOnly' => false
]
],
[
'domain=unittests; expires=Tue, 21-Nov-2006 08:33:44 GMT; domain=example.com; path=/some value/',
[
'Name' => 'domain',
'Value' => 'unittests',
'Domain' => 'example.com',
'Path' => '/some value/',
'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
'Secure' => false,
'Discard' => null,
'Max-Age' => null,
'HttpOnly' => false
]
],
[
'path=indexAction; path=/; domain=.foo.com; expires=Tue, 21-Nov-2006 08:33:44 GMT',
[
'Name' => 'path',
'Value' => 'indexAction',
'Domain' => '.foo.com',
'Path' => '/',
'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
'Secure' => false,
'Discard' => null,
'Max-Age' => null,
'HttpOnly' => false
]
],
[
'secure=sha1; secure; SECURE; domain=some.really.deep.domain.com; version=1; Max-Age=86400',
[
'Name' => 'secure',
'Value' => 'sha1',
'Domain' => 'some.really.deep.domain.com',
'Path' => '/',
'Secure' => true,
'Discard' => null,
'Expires' => time() + 86400,
'Max-Age' => 86400,
'HttpOnly' => false,
'version' => '1'
]
],
[
'PHPSESSID=123456789+abcd%2Cef; secure; discard; domain=.localdomain; path=/foo/baz; expires=Tue, 21-Nov-2006 08:33:44 GMT;',
[
'Name' => 'PHPSESSID',
'Value' => '123456789+abcd%2Cef',
'Domain' => '.localdomain',
'Path' => '/foo/baz',
'Expires' => 'Tue, 21-Nov-2006 08:33:44 GMT',
'Secure' => true,
'Discard' => true,
'Max-Age' => null,
'HttpOnly' => false
]
],
];
}
/**
* @dataProvider cookieParserDataProvider
*/
public function testParseCookie($cookie, $parsed)
{
foreach ((array) $cookie as $v) {
$c = SetCookie::fromString($v);
$p = $c->toArray();
if (isset($p['Expires'])) {
// Remove expires values from the assertion if they are relatively equal
if (abs($p['Expires'] != strtotime($parsed['Expires'])) < 40) {
unset($p['Expires']);
unset($parsed['Expires']);
}
}
if (!empty($parsed)) {
foreach ($parsed as $key => $value) {
self::assertEquals($parsed[$key], $p[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true));
}
foreach ($p as $key => $value) {
self::assertEquals($p[$key], $parsed[$key], 'Comparing ' . $key . ' ' . var_export($value, true) . ' : ' . var_export($parsed, true) . ' | ' . var_export($p, true));
}
} else {
self::assertSame([
'Name' => null,
'Value' => null,
'Domain' => null,
'Path' => '/',
'Max-Age' => null,
'Expires' => null,
'Secure' => false,
'Discard' => false,
'HttpOnly' => false,
], $p);
}
}
}
/**
* Provides the data for testing isExpired
*
* @return array
*/
public function isExpiredProvider()
{
return [
[
'FOO=bar; expires=Thu, 01 Jan 1970 00:00:00 GMT;',
true,
],
[
'FOO=bar; expires=Thu, 01 Jan 1970 00:00:01 GMT;',
true,
],
[
'FOO=bar; expires=' . date(\DateTime::RFC1123, time()+10) . ';',
false,
],
[
'FOO=bar; expires=' . date(\DateTime::RFC1123, time()-10) . ';',
true,
],
[
'FOO=bar;',
false,
],
];
}
/**
* @dataProvider isExpiredProvider
*/
public function testIsExpired($cookie, $expired)
{
self::assertSame(
$expired,
SetCookie::fromString($cookie)->isExpired()
);
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace GuzzleHttp\Tests\Exception;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Psr7\Request;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Exception\ConnectException
*/
class ConnectExceptionTest extends TestCase
{
public function testHasNoResponse()
{
$req = new Request('GET', '/');
$prev = new \Exception();
$e = new ConnectException('foo', $req, $prev, ['foo' => 'bar']);
self::assertSame($req, $e->getRequest());
self::assertNull($e->getResponse());
self::assertFalse($e->hasResponse());
self::assertSame('foo', $e->getMessage());
self::assertSame('bar', $e->getHandlerContext()['foo']);
self::assertSame($prev, $e->getPrevious());
}
}

View File

@ -0,0 +1,195 @@
<?php
namespace GuzzleHttp\Tests\Exception;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\Stream;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Exception\RequestException
*/
class RequestExceptionTest extends TestCase
{
public function testHasRequestAndResponse()
{
$req = new Request('GET', '/');
$res = new Response(200);
$e = new RequestException('foo', $req, $res);
self::assertSame($req, $e->getRequest());
self::assertSame($res, $e->getResponse());
self::assertTrue($e->hasResponse());
self::assertSame('foo', $e->getMessage());
}
public function testCreatesGenerateException()
{
$e = RequestException::create(new Request('GET', '/'));
self::assertSame('Error completing request', $e->getMessage());
self::assertInstanceOf('GuzzleHttp\Exception\RequestException', $e);
}
public function testCreatesClientErrorResponseException()
{
$e = RequestException::create(new Request('GET', '/'), new Response(400));
self::assertContains(
'GET /',
$e->getMessage()
);
self::assertContains(
'400 Bad Request',
$e->getMessage()
);
self::assertInstanceOf('GuzzleHttp\Exception\ClientException', $e);
}
public function testCreatesServerErrorResponseException()
{
$e = RequestException::create(new Request('GET', '/'), new Response(500));
self::assertContains(
'GET /',
$e->getMessage()
);
self::assertContains(
'500 Internal Server Error',
$e->getMessage()
);
self::assertInstanceOf('GuzzleHttp\Exception\ServerException', $e);
}
public function testCreatesGenericErrorResponseException()
{
$e = RequestException::create(new Request('GET', '/'), new Response(300));
self::assertContains(
'GET /',
$e->getMessage()
);
self::assertContains(
'300 ',
$e->getMessage()
);
self::assertInstanceOf('GuzzleHttp\Exception\RequestException', $e);
}
/**
* @expectedException InvalidArgumentException
* @expectedExceptionMessage Status code must be an integer value between 1xx and 5xx.
*/
public function testThrowsInvalidArgumentExceptionOnOutOfBoundsResponseCode()
{
throw RequestException::create(new Request('GET', '/'), new Response(600));
}
public function dataPrintableResponses()
{
return [
['You broke the test!'],
['<h1>zlomený zkouška</h1>'],
['{"tester": "Philépe Gonzalez"}'],
["<xml>\n\t<text>Your friendly test</text>\n</xml>"],
['document.body.write("here comes a test");'],
["body:before {\n\tcontent: 'test style';\n}"],
];
}
/**
* @dataProvider dataPrintableResponses
*/
public function testCreatesExceptionWithPrintableBodySummary($content)
{
$response = new Response(
500,
[],
$content
);
$e = RequestException::create(new Request('GET', '/'), $response);
self::assertContains(
$content,
$e->getMessage()
);
self::assertInstanceOf('GuzzleHttp\Exception\RequestException', $e);
}
public function testCreatesExceptionWithTruncatedSummary()
{
$content = str_repeat('+', 121);
$response = new Response(500, [], $content);
$e = RequestException::create(new Request('GET', '/'), $response);
$expected = str_repeat('+', 120) . ' (truncated...)';
self::assertContains($expected, $e->getMessage());
}
public function testExceptionMessageIgnoresEmptyBody()
{
$e = RequestException::create(new Request('GET', '/'), new Response(500));
self::assertStringEndsWith('response', $e->getMessage());
}
public function testHasStatusCodeAsExceptionCode()
{
$e = RequestException::create(new Request('GET', '/'), new Response(442));
self::assertSame(442, $e->getCode());
}
public function testWrapsRequestExceptions()
{
$e = new \Exception('foo');
$r = new Request('GET', 'http://www.oo.com');
$ex = RequestException::wrapException($r, $e);
self::assertInstanceOf('GuzzleHttp\Exception\RequestException', $ex);
self::assertSame($e, $ex->getPrevious());
}
public function testDoesNotWrapExistingRequestExceptions()
{
$r = new Request('GET', 'http://www.oo.com');
$e = new RequestException('foo', $r);
$e2 = RequestException::wrapException($r, $e);
self::assertSame($e, $e2);
}
public function testCanProvideHandlerContext()
{
$r = new Request('GET', 'http://www.oo.com');
$e = new RequestException('foo', $r, null, null, ['bar' => 'baz']);
self::assertSame(['bar' => 'baz'], $e->getHandlerContext());
}
public function testObfuscateUrlWithUsername()
{
$r = new Request('GET', 'http://username@www.oo.com');
$e = RequestException::create($r, new Response(500));
self::assertContains('http://username@www.oo.com', $e->getMessage());
}
public function testObfuscateUrlWithUsernameAndPassword()
{
$r = new Request('GET', 'http://user:password@www.oo.com');
$e = RequestException::create($r, new Response(500));
self::assertContains('http://user:***@www.oo.com', $e->getMessage());
}
public function testGetResponseBodySummaryOfNonReadableStream()
{
self::assertNull(RequestException::getResponseBodySummary(new Response(500, [], new ReadSeekOnlyStream())));
}
}
final class ReadSeekOnlyStream extends Stream
{
public function __construct()
{
parent::__construct(fopen('php://memory', 'wb'));
}
public function isSeekable()
{
return true;
}
public function isReadable()
{
return false;
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace GuzzleHttp\Tests\Exception;
use GuzzleHttp\Exception\SeekException;
use GuzzleHttp\Psr7;
use PHPUnit\Framework\TestCase;
class SeekExceptionTest extends TestCase
{
public function testHasStream()
{
$s = Psr7\stream_for('foo');
$e = new SeekException($s, 10);
self::assertSame($s, $e->getStream());
self::assertContains('10', $e->getMessage());
}
}

View File

@ -0,0 +1,770 @@
<?php
namespace GuzzleHttp\Test\Handler;
use GuzzleHttp\Handler;
use GuzzleHttp\Handler\CurlFactory;
use GuzzleHttp\Handler\EasyHandle;
use GuzzleHttp\Psr7;
use GuzzleHttp\Tests\Server;
use GuzzleHttp\TransferStats;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
/**
* @covers \GuzzleHttp\Handler\CurlFactory
*/
class CurlFactoryTest extends 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([
new Psr7\Response(200, [
'Foo' => 'Bar',
'Baz' => 'bam',
'Content-Length' => 2,
], 'hi')
]);
$stream = Psr7\stream_for();
$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);
self::assertInternalType('resource', $result->handle);
self::assertInternalType('array', $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::assertEquals(true, $_SERVER['_curl'][CURLOPT_NOBODY]);
$checks = [CURLOPT_WRITEFUNCTION, CURLOPT_READFUNCTION, 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]);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage SSL CA bundle not found: /does/not/exist
*/
public function testValidatesVerify()
{
$f = new Handler\CurlFactory(3);
$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::assertEquals(true, $_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::assertEquals(true, $_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::assertEquals(true, $_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::assertEquals(false, $_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']);
}
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage SSL private key not found: /does/not/exist
*/
public function testValidatesSslKey()
{
$f = new Handler\CurlFactory(3);
$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]);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage SSL certificate not found: /does/not/exist
*/
public function testValidatesCert()
{
$f = new Handler\CurlFactory(3);
$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]);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage progress client option must be callable
*/
public function testValidatesProgress()
{
$f = new Handler\CurlFactory(3);
$f->create(new Psr7\Request('GET', Server::$url), ['progress' => 'foo']);
}
public function testEmitsDebugInfoToStream()
{
$res = fopen('php://memory', '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::assertContains("> HEAD / HTTP/1.1", $output);
self::assertContains("< 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' => function () use (&$called) {
$called[] = func_get_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()
);
}
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\stream_for();
$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'));
}
/**
* @expectedException \GuzzleHttp\Exception\RequestException
* @expectedExceptionMessage but attempting to rewind the request body failed
*/
public function testFailsWhenCannotRewindRetryAfterNoResponse()
{
$factory = new Handler\CurlFactory(1);
$stream = Psr7\stream_for('abc');
$stream->read(1);
$stream = new Psr7\NoSeekStream($stream);
$request = new Psr7\Request('PUT', Server::$url, [], $stream);
$fn = function ($request, $options) use (&$fn, $factory) {
$easy = $factory->create($request, $options);
return Handler\CurlFactory::finish($fn, $easy, $factory);
};
$fn($request, [])->wait();
}
public function testRetriesWhenBodyCanBeRewound()
{
$callHandler = $called = false;
$fn = function ($r, $options) use (&$callHandler) {
$callHandler = true;
return \GuzzleHttp\Promise\promise_for(new Psr7\Response());
};
$bd = Psr7\FnStream::decorate(Psr7\stream_for('test'), [
'tell' => function () {
return 1;
},
'rewind' => 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());
}
/**
* @expectedException \GuzzleHttp\Exception\RequestException
* @expectedExceptionMessage The cURL request was retried 3 times
*/
public function testFailsWhenRetryMoreThanThreeTimes()
{
$factory = new Handler\CurlFactory(1);
$call = 0;
$fn = 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);
$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());
}
/**
* @expectedException \GuzzleHttp\Exception\ConnectException
*/
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,
function () {
},
$easy,
$factory
);
$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\stream_for('foo'), [
'getSize' => function () {
return null;
}
]);
$request = new Psr7\Request('PUT', Server::$url, [], $bd);
$f->create($request, []);
self::assertEquals(1, $_SERVER['_curl'][CURLOPT_UPLOAD]);
self::assertInternalType('callable', $_SERVER['_curl'][CURLOPT_READFUNCTION]);
}
/**
* @expectedException \RuntimeException
* @expectedExceptionMessage Directory /does/not/exist/so does not exist for sink value of /does/not/exist/so/error.txt
*/
public function testEnsuresDirExistsBeforeThrowingWarning()
{
$f = new Handler\CurlFactory(3);
$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, self::readAttribute($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, self::readAttribute($f, 'handles'));
$f->release($easy2);
self::assertCount(2, self::readAttribute($f, 'handles'));
$f->release($easy3);
self::assertCount(3, self::readAttribute($f, 'handles'));
$f->release($easy4);
self::assertCount(3, self::readAttribute($f, 'handles'));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresOnHeadersIsCallable()
{
$req = new Psr7\Request('GET', Server::$url);
$handler = new Handler\CurlHandler();
$handler($req, ['on_headers' => 'error!']);
}
/**
* @expectedException \GuzzleHttp\Exception\RequestException
* @expectedExceptionMessage An error was encountered during the on_headers event
* @expectedExceptionMessage test
*/
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' => function () {
throw new \Exception('test');
}
]);
$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\stream_for();
$stream = Psr7\FnStream::decorate($stream, [
'write' => function ($data) use ($stream, &$got) {
self::assertNotNull($got);
return $stream->write($data);
}
]);
$handler = new Handler\CurlHandler();
$promise = $handler($req, [
'sink' => $stream,
'on_headers' => 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' => 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' => 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::assertInternalType('float', $gotStats->getTransferTime());
self::assertInternalType('int', $gotStats->getHandlerErrorData());
self::assertArrayHasKey('appconnect_time', $gotStats->getHandlerStats());
}
public function testRewindsBodyIfPossible()
{
$body = Psr7\stream_for(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\stream_for(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));
}
}

View File

@ -0,0 +1,87 @@
<?php
namespace GuzzleHttp\Test\Handler;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Handler\CurlHandler;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Tests\Server;
use GuzzleHttp\Utils;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Handler\CurlHandler
*/
class CurlHandlerTest extends TestCase
{
protected function getHandler($options = [])
{
return new CurlHandler($options);
}
/**
* @expectedException \GuzzleHttp\Exception\ConnectException
* @expectedExceptionMessage cURL
*/
public function testCreatesCurlErrors()
{
$handler = new CurlHandler();
$request = new Request('GET', 'http://localhost:123');
$handler($request, ['timeout' => 0.001, 'connect_timeout' => 0.001])->wait();
}
public function testReusesHandles()
{
Server::flush();
$response = new response(200);
Server::enqueue([$response, $response]);
$a = new CurlHandler();
$request = new Request('GET', Server::$url);
self::assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $a($request, []));
self::assertInstanceOf('GuzzleHttp\Promise\FulfilledPromise', $a($request, []));
}
public function testDoesSleep()
{
$response = new response(200);
Server::enqueue([$response]);
$a = new CurlHandler();
$request = new Request('GET', Server::$url);
$s = Utils::currentTime();
$a($request, ['delay' => 0.1])->wait();
self::assertGreaterThan(0.0001, Utils::currentTime() - $s);
}
public function testCreatesCurlErrorsWithContext()
{
$handler = new CurlHandler();
$request = new Request('GET', 'http://localhost:123');
$called = false;
$p = $handler($request, ['timeout' => 0.001, 'connect_timeout' => 0.001])
->otherwise(function (ConnectException $e) use (&$called) {
$called = true;
self::assertArrayHasKey('errno', $e->getHandlerContext());
});
$p->wait();
self::assertTrue($called);
}
public function testUsesContentLengthWhenOverInMemorySize()
{
Server::flush();
Server::enqueue([new Response()]);
$stream = Psr7\stream_for(str_repeat('.', 1000000));
$handler = new CurlHandler();
$request = new Request(
'PUT',
Server::$url,
['Content-Length' => 1000000],
$stream
);
$handler($request, [])->wait();
$received = Server::received()[0];
self::assertEquals(1000000, $received->getHeaderLine('Content-Length'));
self::assertFalse($received->hasHeader('Transfer-Encoding'));
}
}

View File

@ -0,0 +1,123 @@
<?php
namespace GuzzleHttp\Tests\Handler;
use GuzzleHttp\Handler\CurlMultiHandler;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Tests\Server;
use GuzzleHttp\Utils;
use PHPUnit\Framework\TestCase;
class CurlMultiHandlerTest extends TestCase
{
public function setUp()
{
$_SERVER['curl_test'] = true;
unset($_SERVER['_curl_multi']);
}
public function tearDown()
{
unset($_SERVER['_curl_multi'], $_SERVER['curl_test']);
}
public function testCanAddCustomCurlOptions()
{
Server::flush();
Server::enqueue([new Response()]);
$a = new CurlMultiHandler(['options' => [
CURLMOPT_MAXCONNECTS => 5,
]]);
$request = new Request('GET', Server::$url);
$a($request, []);
self::assertEquals(5, $_SERVER['_curl_multi'][CURLMOPT_MAXCONNECTS]);
}
public function testSendsRequest()
{
Server::enqueue([new Response()]);
$a = new CurlMultiHandler();
$request = new Request('GET', Server::$url);
$response = $a($request, [])->wait();
self::assertSame(200, $response->getStatusCode());
}
/**
* @expectedException \GuzzleHttp\Exception\ConnectException
* @expectedExceptionMessage cURL error
*/
public function testCreatesExceptions()
{
$a = new CurlMultiHandler();
$a(new Request('GET', 'http://localhost:123'), [])->wait();
}
public function testCanSetSelectTimeout()
{
$a = new CurlMultiHandler(['select_timeout' => 2]);
self::assertEquals(2, self::readAttribute($a, 'selectTimeout'));
}
public function testCanCancel()
{
Server::flush();
$response = new Response(200);
Server::enqueue(array_fill_keys(range(0, 10), $response));
$a = new CurlMultiHandler();
$responses = [];
for ($i = 0; $i < 10; $i++) {
$response = $a(new Request('GET', Server::$url), []);
$response->cancel();
$responses[] = $response;
}
foreach ($responses as $r) {
self::assertSame('rejected', $response->getState());
}
}
public function testCannotCancelFinished()
{
Server::flush();
Server::enqueue([new Response(200)]);
$a = new CurlMultiHandler();
$response = $a(new Request('GET', Server::$url), []);
$response->wait();
$response->cancel();
self::assertSame('fulfilled', $response->getState());
}
public function testDelaysConcurrently()
{
Server::flush();
Server::enqueue([new Response()]);
$a = new CurlMultiHandler();
$expected = Utils::currentTime() + (100 / 1000);
$response = $a(new Request('GET', Server::$url), ['delay' => 100]);
$response->wait();
self::assertGreaterThanOrEqual($expected, Utils::currentTime());
}
public function testUsesTimeoutEnvironmentVariables()
{
$a = new CurlMultiHandler();
//default if no options are given and no environment variable is set
self::assertEquals(1, self::readAttribute($a, 'selectTimeout'));
putenv("GUZZLE_CURL_SELECT_TIMEOUT=3");
$a = new CurlMultiHandler();
$selectTimeout = getenv('GUZZLE_CURL_SELECT_TIMEOUT');
//Handler reads from the environment if no options are given
self::assertEquals($selectTimeout, self::readAttribute($a, 'selectTimeout'));
}
/**
* @expectedException \BadMethodCallException
*/
public function throwsWhenAccessingInvalidProperty()
{
$h = new CurlMultiHandler();
$h->foo;
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace GuzzleHttp\Test\Handler;
use GuzzleHttp\Handler\EasyHandle;
use GuzzleHttp\Psr7;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Handler\EasyHandle
*/
class EasyHandleTest extends TestCase
{
/**
* @expectedException \BadMethodCallException
* @expectedExceptionMessage The EasyHandle has been released
*/
public function testEnsuresHandleExists()
{
$easy = new EasyHandle;
unset($easy->handle);
$easy->handle;
}
}

View File

@ -0,0 +1,261 @@
<?php
namespace GuzzleHttp\Test\Handler;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\TransferStats;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Handler\MockHandler
*/
class MockHandlerTest extends TestCase
{
public function testReturnsMockResponse()
{
$res = new Response();
$mock = new MockHandler([$res]);
$request = new Request('GET', 'http://example.com');
$p = $mock($request, []);
self::assertSame($res, $p->wait());
}
public function testIsCountable()
{
$res = new Response();
$mock = new MockHandler([$res, $res]);
self::assertCount(2, $mock);
}
public function testEmptyHandlerIsCountable()
{
self::assertCount(0, new MockHandler());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresEachAppendIsValid()
{
$mock = new MockHandler(['a']);
$request = new Request('GET', 'http://example.com');
$mock($request, []);
}
public function testCanQueueExceptions()
{
$e = new \Exception('a');
$mock = new MockHandler([$e]);
$request = new Request('GET', 'http://example.com');
$p = $mock($request, []);
try {
$p->wait();
self::fail();
} catch (\Exception $e2) {
self::assertSame($e, $e2);
}
}
public function testCanGetLastRequestAndOptions()
{
$res = new Response();
$mock = new MockHandler([$res]);
$request = new Request('GET', 'http://example.com');
$mock($request, ['foo' => 'bar']);
self::assertSame($request, $mock->getLastRequest());
self::assertSame(['foo' => 'bar'], $mock->getLastOptions());
}
public function testSinkFilename()
{
$filename = sys_get_temp_dir() . '/mock_test_' . uniqid();
$res = new Response(200, [], 'TEST CONTENT');
$mock = new MockHandler([$res]);
$request = new Request('GET', '/');
$p = $mock($request, ['sink' => $filename]);
$p->wait();
self::assertFileExists($filename);
self::assertStringEqualsFile($filename, 'TEST CONTENT');
unlink($filename);
}
public function testSinkResource()
{
$file = tmpfile();
$meta = stream_get_meta_data($file);
$res = new Response(200, [], 'TEST CONTENT');
$mock = new MockHandler([$res]);
$request = new Request('GET', '/');
$p = $mock($request, ['sink' => $file]);
$p->wait();
self::assertFileExists($meta['uri']);
self::assertStringEqualsFile($meta['uri'], 'TEST CONTENT');
}
public function testSinkStream()
{
$stream = new \GuzzleHttp\Psr7\Stream(tmpfile());
$res = new Response(200, [], 'TEST CONTENT');
$mock = new MockHandler([$res]);
$request = new Request('GET', '/');
$p = $mock($request, ['sink' => $stream]);
$p->wait();
self::assertFileExists($stream->getMetadata('uri'));
self::assertStringEqualsFile($stream->getMetadata('uri'), 'TEST CONTENT');
}
public function testCanEnqueueCallables()
{
$r = new Response();
$fn = function ($req, $o) use ($r) {
return $r;
};
$mock = new MockHandler([$fn]);
$request = new Request('GET', 'http://example.com');
$p = $mock($request, ['foo' => 'bar']);
self::assertSame($r, $p->wait());
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresOnHeadersIsCallable()
{
$res = new Response();
$mock = new MockHandler([$res]);
$request = new Request('GET', 'http://example.com');
$mock($request, ['on_headers' => 'error!']);
}
/**
* @expectedException \GuzzleHttp\Exception\RequestException
* @expectedExceptionMessage An error was encountered during the on_headers event
* @expectedExceptionMessage test
*/
public function testRejectsPromiseWhenOnHeadersFails()
{
$res = new Response();
$mock = new MockHandler([$res]);
$request = new Request('GET', 'http://example.com');
$promise = $mock($request, [
'on_headers' => function () {
throw new \Exception('test');
}
]);
$promise->wait();
}
public function testInvokesOnFulfilled()
{
$res = new Response();
$mock = new MockHandler([$res], function ($v) use (&$c) {
$c = $v;
});
$request = new Request('GET', 'http://example.com');
$mock($request, [])->wait();
self::assertSame($res, $c);
}
public function testInvokesOnRejected()
{
$e = new \Exception('a');
$c = null;
$mock = new MockHandler([$e], null, function ($v) use (&$c) {
$c = $v;
});
$request = new Request('GET', 'http://example.com');
$mock($request, [])->wait(false);
self::assertSame($e, $c);
}
/**
* @expectedException \OutOfBoundsException
*/
public function testThrowsWhenNoMoreResponses()
{
$mock = new MockHandler();
$request = new Request('GET', 'http://example.com');
$mock($request, []);
}
/**
* @expectedException \GuzzleHttp\Exception\BadResponseException
*/
public function testCanCreateWithDefaultMiddleware()
{
$r = new Response(500);
$mock = MockHandler::createWithMiddleware([$r]);
$request = new Request('GET', 'http://example.com');
$mock($request, ['http_errors' => true])->wait();
}
public function testInvokesOnStatsFunctionForResponse()
{
$res = new Response();
$mock = new MockHandler([$res]);
$request = new Request('GET', 'http://example.com');
/** @var TransferStats|null $stats */
$stats = null;
$onStats = function (TransferStats $s) use (&$stats) {
$stats = $s;
};
$p = $mock($request, ['on_stats' => $onStats]);
$p->wait();
self::assertSame($res, $stats->getResponse());
self::assertSame($request, $stats->getRequest());
}
public function testInvokesOnStatsFunctionForError()
{
$e = new \Exception('a');
$c = null;
$mock = new MockHandler([$e], null, function ($v) use (&$c) {
$c = $v;
});
$request = new Request('GET', 'http://example.com');
/** @var TransferStats|null $stats */
$stats = null;
$onStats = function (TransferStats $s) use (&$stats) {
$stats = $s;
};
$mock($request, ['on_stats' => $onStats])->wait(false);
self::assertSame($e, $stats->getHandlerErrorData());
self::assertNull($stats->getResponse());
self::assertSame($request, $stats->getRequest());
}
public function testTransferTime()
{
$e = new \Exception('a');
$c = null;
$mock = new MockHandler([$e], null, function ($v) use (&$c) {
$c = $v;
});
$request = new Request('GET', 'http://example.com');
$stats = null;
$onStats = function (TransferStats $s) use (&$stats) {
$stats = $s;
};
$mock($request, [ 'on_stats' => $onStats, 'transfer_time' => 0.4 ])->wait(false);
self::assertEquals(0.4, $stats->getTransferTime());
}
public function testResetQueue()
{
$mock = new MockHandler([new Response(200), new Response(204)]);
self::assertCount(2, $mock);
$mock->reset();
self::assertEmpty($mock);
$mock->append(new Response(500));
self::assertCount(1, $mock);
}
}

View File

@ -0,0 +1,74 @@
<?php
namespace GuzzleHttp\Test\Handler;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Handler\Proxy;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
use PHPUnit\Framework\TestCase;
/**
* @covers \GuzzleHttp\Handler\Proxy
*/
class ProxyTest extends TestCase
{
public function testSendsToNonSync()
{
$a = $b = null;
$m1 = new MockHandler([function ($v) use (&$a) {
$a = $v;
}]);
$m2 = new MockHandler([function ($v) use (&$b) {
$b = $v;
}]);
$h = Proxy::wrapSync($m1, $m2);
$h(new Request('GET', 'http://foo.com'), []);
self::assertNotNull($a);
self::assertNull($b);
}
public function testSendsToSync()
{
$a = $b = null;
$m1 = new MockHandler([function ($v) use (&$a) {
$a = $v;
}]);
$m2 = new MockHandler([function ($v) use (&$b) {
$b = $v;
}]);
$h = Proxy::wrapSync($m1, $m2);
$h(new Request('GET', 'http://foo.com'), [RequestOptions::SYNCHRONOUS => true]);
self::assertNull($a);
self::assertNotNull($b);
}
public function testSendsToStreaming()
{
$a = $b = null;
$m1 = new MockHandler([function ($v) use (&$a) {
$a = $v;
}]);
$m2 = new MockHandler([function ($v) use (&$b) {
$b = $v;
}]);
$h = Proxy::wrapStreaming($m1, $m2);
$h(new Request('GET', 'http://foo.com'), []);
self::assertNotNull($a);
self::assertNull($b);
}
public function testSendsToNonStreaming()
{
$a = $b = null;
$m1 = new MockHandler([function ($v) use (&$a) {
$a = $v;
}]);
$m2 = new MockHandler([function ($v) use (&$b) {
$b = $v;
}]);
$h = Proxy::wrapStreaming($m1, $m2);
$h(new Request('GET', 'http://foo.com'), ['stream' => true]);
self::assertNull($a);
self::assertNotNull($b);
}
}

View File

@ -0,0 +1,689 @@
<?php
namespace GuzzleHttp\Test\Handler;
use GuzzleHttp\Exception\ConnectException;
use GuzzleHttp\Handler\StreamHandler;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\FnStream;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\RequestOptions;
use GuzzleHttp\Tests\Server;
use GuzzleHttp\TransferStats;
use GuzzleHttp\Utils;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\ResponseInterface;
/**
* @covers \GuzzleHttp\Handler\StreamHandler
*/
class StreamHandlerTest extends TestCase
{
private function queueRes()
{
Server::flush();
Server::enqueue([
new Response(200, [
'Foo' => 'Bar',
'Content-Length' => 8,
], 'hi there')
]);
}
public function testReturnsResponseForSuccessfulRequest()
{
$this->queueRes();
$handler = new StreamHandler();
$response = $handler(
new Request('GET', Server::$url, ['Foo' => 'Bar']),
[]
)->wait();
self::assertSame(200, $response->getStatusCode());
self::assertSame('OK', $response->getReasonPhrase());
self::assertSame('Bar', $response->getHeaderLine('Foo'));
self::assertSame('8', $response->getHeaderLine('Content-Length'));
self::assertSame('hi there', (string) $response->getBody());
$sent = Server::received()[0];
self::assertSame('GET', $sent->getMethod());
self::assertSame('/', $sent->getUri()->getPath());
self::assertSame('127.0.0.1:8126', $sent->getHeaderLine('Host'));
self::assertSame('Bar', $sent->getHeaderLine('foo'));
}
/**
* @expectedException \GuzzleHttp\Exception\RequestException
*/
public function testAddsErrorToResponse()
{
$handler = new StreamHandler();
$handler(
new Request('GET', 'http://localhost:123'),
['timeout' => 0.01]
)->wait();
}
public function testStreamAttributeKeepsStreamOpen()
{
$this->queueRes();
$handler = new StreamHandler();
$request = new Request(
'PUT',
Server::$url . 'foo?baz=bar',
['Foo' => 'Bar'],
'test'
);
$response = $handler($request, ['stream' => true])->wait();
self::assertSame(200, $response->getStatusCode());
self::assertSame('OK', $response->getReasonPhrase());
self::assertSame('8', $response->getHeaderLine('Content-Length'));
$body = $response->getBody();
$stream = $body->detach();
self::assertInternalType('resource', $stream);
self::assertSame('http', stream_get_meta_data($stream)['wrapper_type']);
self::assertSame('hi there', stream_get_contents($stream));
fclose($stream);
$sent = Server::received()[0];
self::assertSame('PUT', $sent->getMethod());
self::assertSame('http://127.0.0.1:8126/foo?baz=bar', (string) $sent->getUri());
self::assertSame('Bar', $sent->getHeaderLine('Foo'));
self::assertSame('test', (string) $sent->getBody());
}
public function testDrainsResponseIntoTempStream()
{
$this->queueRes();
$handler = new StreamHandler();
$request = new Request('GET', Server::$url);
$response = $handler($request, [])->wait();
$body = $response->getBody();
$stream = $body->detach();
self::assertSame('php://temp', stream_get_meta_data($stream)['uri']);
self::assertSame('hi', fread($stream, 2));
fclose($stream);
}
public function testDrainsResponseIntoSaveToBody()
{
$r = fopen('php://temp', 'r+');
$this->queueRes();
$handler = new StreamHandler();
$request = new Request('GET', Server::$url);
$response = $handler($request, ['sink' => $r])->wait();
$body = $response->getBody()->detach();
self::assertSame('php://temp', stream_get_meta_data($body)['uri']);
self::assertSame('hi', fread($body, 2));
self::assertSame(' there', stream_get_contents($r));
fclose($r);
}
public function testDrainsResponseIntoSaveToBodyAtPath()
{
$tmpfname = tempnam('/tmp', 'save_to_path');
$this->queueRes();
$handler = new StreamHandler();
$request = new Request('GET', Server::$url);
$response = $handler($request, ['sink' => $tmpfname])->wait();
$body = $response->getBody();
self::assertSame($tmpfname, $body->getMetadata('uri'));
self::assertSame('hi', $body->read(2));
$body->close();
unlink($tmpfname);
}
public function testDrainsResponseIntoSaveToBodyAtNonExistentPath()
{
$tmpfname = tempnam('/tmp', 'save_to_path');
unlink($tmpfname);
$this->queueRes();
$handler = new StreamHandler();
$request = new Request('GET', Server::$url);
$response = $handler($request, ['sink' => $tmpfname])->wait();
$body = $response->getBody();
self::assertSame($tmpfname, $body->getMetadata('uri'));
self::assertSame('hi', $body->read(2));
$body->close();
unlink($tmpfname);
}
public function testDrainsResponseAndReadsOnlyContentLengthBytes()
{
Server::flush();
Server::enqueue([
new Response(200, [
'Foo' => 'Bar',
'Content-Length' => 8,
], 'hi there... This has way too much data!')
]);
$handler = new StreamHandler();
$request = new Request('GET', Server::$url);
$response = $handler($request, [])->wait();
$body = $response->getBody();
$stream = $body->detach();
self::assertSame('hi there', stream_get_contents($stream));
fclose($stream);
}
public function testDoesNotDrainWhenHeadRequest()
{
Server::flush();
// Say the content-length is 8, but return no response.
Server::enqueue([
new Response(200, [
'Foo' => 'Bar',
'Content-Length' => 8,
], '')
]);
$handler = new StreamHandler();
$request = new Request('HEAD', Server::$url);
$response = $handler($request, [])->wait();
$body = $response->getBody();
$stream = $body->detach();
self::assertSame('', stream_get_contents($stream));
fclose($stream);
}
public function testAutomaticallyDecompressGzip()
{
Server::flush();
$content = gzencode('test');
Server::enqueue([
new Response(200, [
'Content-Encoding' => 'gzip',
'Content-Length' => strlen($content),
], $content)
]);
$handler = new StreamHandler();
$request = new Request('GET', Server::$url);
$response = $handler($request, ['decode_content' => true])->wait();
self::assertSame('test', (string) $response->getBody());
self::assertFalse($response->hasHeader('content-encoding'));
self::assertTrue(!$response->hasHeader('content-length') || $response->getHeaderLine('content-length') == $response->getBody()->getSize());
}
public function testReportsOriginalSizeAndContentEncodingAfterDecoding()
{
Server::flush();
$content = gzencode('test');
Server::enqueue([
new Response(200, [
'Content-Encoding' => 'gzip',
'Content-Length' => strlen($content),
], $content)
]);
$handler = new StreamHandler();
$request = new Request('GET', Server::$url);
$response = $handler($request, ['decode_content' => true])->wait();
self::assertSame(
'gzip',
$response->getHeaderLine('x-encoded-content-encoding')
);
self::assertSame(
strlen($content),
(int) $response->getHeaderLine('x-encoded-content-length')
);
}
public function testDoesNotForceGzipDecode()
{
Server::flush();
$content = gzencode('test');
Server::enqueue([
new Response(200, [
'Content-Encoding' => 'gzip',
'Content-Length' => strlen($content),
], $content)
]);
$handler = new StreamHandler();
$request = new Request('GET', Server::$url);
$response = $handler($request, ['decode_content' => false])->wait();
self::assertSame($content, (string) $response->getBody());
self::assertSame('gzip', $response->getHeaderLine('content-encoding'));
self::assertEquals(strlen($content), $response->getHeaderLine('content-length'));
}
public function testProtocolVersion()
{
$this->queueRes();
$handler = new StreamHandler();
$request = new Request('GET', Server::$url, [], null, '1.0');
$handler($request, []);
self::assertSame('1.0', Server::received()[0]->getProtocolVersion());
}
protected function getSendResult(array $opts)
{
$this->queueRes();
$handler = new StreamHandler();
$opts['stream'] = true;
$request = new Request('GET', Server::$url);
return $handler($request, $opts)->wait();
}
/**
* @expectedException \GuzzleHttp\Exception\ConnectException
* @expectedExceptionMessage Connection refused
*/
public function testAddsProxy()
{
$this->getSendResult(['proxy' => '127.0.0.1:8125']);
}
public function testAddsProxyByProtocol()
{
$url = str_replace('http', 'tcp', Server::$url);
// Workaround until #1823 is fixed properly
$url = rtrim($url, '/');
$res = $this->getSendResult(['proxy' => ['http' => $url]]);
$opts = stream_context_get_options($res->getBody()->detach());
self::assertSame($url, $opts['http']['proxy']);
}
public function testAddsProxyButHonorsNoProxy()
{
$url = str_replace('http', 'tcp', Server::$url);
$res = $this->getSendResult(['proxy' => [
'http' => $url,
'no' => ['*']
]]);
$opts = stream_context_get_options($res->getBody()->detach());
self::assertArrayNotHasKey('proxy', $opts['http']);
}
public function testAddsTimeout()
{
$res = $this->getSendResult(['stream' => true, 'timeout' => 200]);
$opts = stream_context_get_options($res->getBody()->detach());
self::assertEquals(200, $opts['http']['timeout']);
}
/**
* @expectedException \GuzzleHttp\Exception\RequestException
* @expectedExceptionMessage SSL CA bundle not found: /does/not/exist
*/
public function testVerifiesVerifyIsValidIfPath()
{
$this->getSendResult(['verify' => '/does/not/exist']);
}
public function testVerifyCanBeDisabled()
{
$handler = $this->getSendResult(['verify' => false]);
self::assertInstanceOf('GuzzleHttp\Psr7\Response', $handler);
}
/**
* @expectedException \GuzzleHttp\Exception\RequestException
* @expectedExceptionMessage SSL certificate not found: /does/not/exist
*/
public function testVerifiesCertIfValidPath()
{
$this->getSendResult(['cert' => '/does/not/exist']);
}
public function testVerifyCanBeSetToPath()
{
$path = $path = \GuzzleHttp\default_ca_bundle();
$res = $this->getSendResult(['verify' => $path]);
$opts = stream_context_get_options($res->getBody()->detach());
self::assertTrue($opts['ssl']['verify_peer']);
self::assertTrue($opts['ssl']['verify_peer_name']);
self::assertSame($path, $opts['ssl']['cafile']);
self::assertFileExists($opts['ssl']['cafile']);
}
public function testUsesSystemDefaultBundle()
{
$path = $path = \GuzzleHttp\default_ca_bundle();
$res = $this->getSendResult(['verify' => true]);
$opts = stream_context_get_options($res->getBody()->detach());
if (PHP_VERSION_ID < 50600) {
self::assertSame($path, $opts['ssl']['cafile']);
} else {
self::assertArrayNotHasKey('cafile', $opts['ssl']);
}
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage Invalid verify request option
*/
public function testEnsuresVerifyOptionIsValid()
{
$this->getSendResult(['verify' => 10]);
}
public function testCanSetPasswordWhenSettingCert()
{
$path = __FILE__;
$res = $this->getSendResult(['cert' => [$path, 'foo']]);
$opts = stream_context_get_options($res->getBody()->detach());
self::assertSame($path, $opts['ssl']['local_cert']);
self::assertSame('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);
self::assertContains('<GET http://127.0.0.1:8126/> [CONNECT]', $contents);
self::assertContains('<GET http://127.0.0.1:8126/> [FILE_SIZE_IS]', $contents);
self::assertContains('<GET http://127.0.0.1:8126/> [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);
self::assertContains('<GET http://127.0.0.1:8126/> [CONNECT]', $contents);
self::assertContains('<GET http://127.0.0.1:8126/> [FILE_SIZE_IS] message: "Content-Length: 8"', $contents);
self::assertContains('<GET http://127.0.0.1:8126/> [PROGRESS] bytes_max: "8"', $contents);
self::assertTrue($called);
}
public function testEmitsProgressInformation()
{
$called = [];
$this->queueRes();
$this->getSendResult([
'progress' => function () use (&$called) {
$called[] = func_get_args();
},
]);
self::assertNotEmpty($called);
self::assertEquals(8, $called[0][0]);
self::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();
},
]);
self::assertNotEmpty($called);
self::assertEquals(8, $called[0][0]);
self::assertEquals(0, $called[0][1]);
rewind($buffer);
self::assertNotEmpty(stream_get_contents($buffer));
fclose($buffer);
}
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->getBody()->detach());
self::assertSame('HEAD', $opts['http']['method']);
self::assertTrue($opts['http']['request_fulluri']);
self::assertSame('127.0.0.1:0', $opts['socket']['bindto']);
self::assertFalse($opts['ssl']['verify_peer']);
}
/**
* @expectedException \InvalidArgumentException
* @expectedExceptionMessage stream_context must be an array
*/
public function testEnsuresThatStreamContextIsAnArray()
{
$this->getSendResult(['stream_context' => 'foo']);
}
public function testDoesNotAddContentTypeByDefault()
{
$this->queueRes();
$handler = new StreamHandler();
$request = new Request('PUT', Server::$url, ['Content-Length' => 3], 'foo');
$handler($request, []);
$req = Server::received()[0];
self::assertEquals('', $req->getHeaderLine('Content-Type'));
self::assertEquals(3, $req->getHeaderLine('Content-Length'));
}
public function testAddsContentLengthByDefault()
{
$this->queueRes();
$handler = new StreamHandler();
$request = new Request('PUT', Server::$url, [], 'foo');
$handler($request, []);
$req = Server::received()[0];
self::assertEquals(3, $req->getHeaderLine('Content-Length'));
}
public function testAddsContentLengthEvenWhenEmpty()
{
$this->queueRes();
$handler = new StreamHandler();
$request = new Request('PUT', Server::$url, [], '');
$handler($request, []);
$req = Server::received()[0];
self::assertEquals(0, $req->getHeaderLine('Content-Length'));
}
public function testSupports100Continue()
{
Server::flush();
$response = new Response(200, ['Test' => 'Hello', 'Content-Length' => '4'], 'test');
Server::enqueue([$response]);
$request = new Request('PUT', Server::$url, ['Expect' => '100-Continue'], 'test');
$handler = new StreamHandler();
$response = $handler($request, [])->wait();
self::assertSame(200, $response->getStatusCode());
self::assertSame('Hello', $response->getHeaderLine('Test'));
self::assertSame('4', $response->getHeaderLine('Content-Length'));
self::assertSame('test', (string) $response->getBody());
}
public function testDoesSleep()
{
$response = new response(200);
Server::enqueue([$response]);
$a = new StreamHandler();
$request = new Request('GET', Server::$url);
$s = Utils::currentTime();
$a($request, ['delay' => 0.1])->wait();
self::assertGreaterThan(0.0001, Utils::currentTime() - $s);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresOnHeadersIsCallable()
{
$req = new Request('GET', Server::$url);
$handler = new StreamHandler();
$handler($req, ['on_headers' => 'error!']);
}
/**
* @expectedException \GuzzleHttp\Exception\RequestException
* @expectedExceptionMessage An error was encountered during the on_headers event
* @expectedExceptionMessage test
*/
public function testRejectsPromiseWhenOnHeadersFails()
{
Server::flush();
Server::enqueue([
new Response(200, ['X-Foo' => 'bar'], 'abc 123')
]);
$req = new Request('GET', Server::$url);
$handler = new StreamHandler();
$promise = $handler($req, [
'on_headers' => function () {
throw new \Exception('test');
}
]);
$promise->wait();
}
public function testSuccessfullyCallsOnHeadersBeforeWritingToSink()
{
Server::flush();
Server::enqueue([
new Response(200, ['X-Foo' => 'bar'], 'abc 123')
]);
$req = new Request('GET', Server::$url);
$got = null;
$stream = Psr7\stream_for();
$stream = FnStream::decorate($stream, [
'write' => function ($data) use ($stream, &$got) {
self::assertNotNull($got);
return $stream->write($data);
}
]);
$handler = new StreamHandler();
$promise = $handler($req, [
'sink' => $stream,
'on_headers' => function (ResponseInterface $res) use (&$got) {
$got = $res;
self::assertSame('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 StreamHandler();
$promise = $handler($req, [
'on_stats' => 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());
}
public function testInvokesOnStatsOnError()
{
$req = new Psr7\Request('GET', 'http://127.0.0.1:123');
$gotStats = null;
$handler = new StreamHandler();
$promise = $handler($req, [
'connect_timeout' => 0.001,
'timeout' => 0.001,
'on_stats' => 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::assertInternalType('float', $gotStats->getTransferTime());
self::assertInstanceOf(
ConnectException::class,
$gotStats->getHandlerErrorData()
);
}
public function testStreamIgnoresZeroTimeout()
{
Server::flush();
Server::enqueue([new Psr7\Response(200)]);
$req = new Psr7\Request('GET', Server::$url);
$gotStats = null;
$handler = new StreamHandler();
$promise = $handler($req, [
'connect_timeout' => 10,
'timeout' => 0
]);
$response = $promise->wait();
self::assertSame(200, $response->getStatusCode());
}
public function testDrainsResponseAndReadsAllContentWhenContentLengthIsZero()
{
Server::flush();
Server::enqueue([
new Response(200, [
'Foo' => 'Bar',
'Content-Length' => '0',
], 'hi there... This has a lot of data!')
]);
$handler = new StreamHandler();
$request = new Request('GET', Server::$url);
$response = $handler($request, [])->wait();
$body = $response->getBody();
$stream = $body->detach();
self::assertSame('hi there... This has a lot of data!', stream_get_contents($stream));
fclose($stream);
}
public function testHonorsReadTimeout()
{
Server::flush();
$handler = new StreamHandler();
$response = $handler(
new Request('GET', Server::$url . 'guzzle-server/read-timeout'),
[
RequestOptions::READ_TIMEOUT => 1,
RequestOptions::STREAM => true,
]
)->wait();
self::assertSame(200, $response->getStatusCode());
self::assertSame('OK', $response->getReasonPhrase());
$body = $response->getBody()->detach();
$line = fgets($body);
self::assertSame("sleeping 60 seconds ...\n", $line);
$line = fgets($body);
self::assertFalse($line);
self::assertTrue(stream_get_meta_data($body)['timed_out']);
self::assertFalse(feof($body));
}
}

View File

@ -0,0 +1,214 @@
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
class HandlerStackTest extends TestCase
{
public function testSetsHandlerInCtor()
{
$f = function () {
};
$m1 = function () {
};
$h = new HandlerStack($f, [$m1]);
self::assertTrue($h->hasHandler());
}
/**
* @doesNotPerformAssertions
*/
public function testCanSetDifferentHandlerAfterConstruction()
{
$f = function () {
};
$h = new HandlerStack();
$h->setHandler($f);
$h->resolve();
}
/**
* @expectedException \LogicException
*/
public function testEnsuresHandlerIsSet()
{
$h = new HandlerStack();
$h->resolve();
}
public function testPushInOrder()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->push($meths[2]);
$builder->push($meths[3]);
$builder->push($meths[4]);
$composed = $builder->resolve();
self::assertSame('Hello - test123', $composed('test'));
self::assertSame(
[['a', 'test'], ['b', 'test1'], ['c', 'test12']],
$meths[0]
);
}
public function testUnshiftsInReverseOrder()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->unshift($meths[2]);
$builder->unshift($meths[3]);
$builder->unshift($meths[4]);
$composed = $builder->resolve();
self::assertSame('Hello - test321', $composed('test'));
self::assertSame(
[['c', 'test'], ['b', 'test3'], ['a', 'test32']],
$meths[0]
);
}
public function testCanRemoveMiddlewareByInstance()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->push($meths[2]);
$builder->push($meths[2]);
$builder->push($meths[3]);
$builder->push($meths[4]);
$builder->push($meths[2]);
$builder->remove($meths[3]);
$composed = $builder->resolve();
self::assertSame('Hello - test1131', $composed('test'));
}
public function testCanPrintMiddleware()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->push($meths[2], 'a');
$builder->push([__CLASS__, 'foo']);
$builder->push([$this, 'bar']);
$builder->push(__CLASS__ . '::' . 'foo');
$lines = explode("\n", (string) $builder);
self::assertContains("> 4) Name: 'a', Function: callable(", $lines[0]);
self::assertContains("> 3) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[1]);
self::assertContains("> 2) Name: '', Function: callable(['GuzzleHttp\\Tests\\HandlerStackTest', 'bar'])", $lines[2]);
self::assertContains("> 1) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[3]);
self::assertContains("< 0) Handler: callable(", $lines[4]);
self::assertContains("< 1) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[5]);
self::assertContains("< 2) Name: '', Function: callable(['GuzzleHttp\\Tests\\HandlerStackTest', 'bar'])", $lines[6]);
self::assertContains("< 3) Name: '', Function: callable(GuzzleHttp\\Tests\\HandlerStackTest::foo)", $lines[7]);
self::assertContains("< 4) Name: 'a', Function: callable(", $lines[8]);
}
public function testCanAddBeforeByName()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->push($meths[2], 'foo');
$builder->before('foo', $meths[3], 'baz');
$builder->before('baz', $meths[4], 'bar');
$builder->before('baz', $meths[4], 'qux');
$lines = explode("\n", (string) $builder);
self::assertContains('> 4) Name: \'bar\'', $lines[0]);
self::assertContains('> 3) Name: \'qux\'', $lines[1]);
self::assertContains('> 2) Name: \'baz\'', $lines[2]);
self::assertContains('> 1) Name: \'foo\'', $lines[3]);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresHandlerExistsByName()
{
$builder = new HandlerStack();
$builder->before('foo', function () {
});
}
public function testCanAddAfterByName()
{
$meths = $this->getFunctions();
$builder = new HandlerStack();
$builder->setHandler($meths[1]);
$builder->push($meths[2], 'a');
$builder->push($meths[3], 'b');
$builder->after('a', $meths[4], 'c');
$builder->after('b', $meths[4], 'd');
$lines = explode("\n", (string) $builder);
self::assertContains('4) Name: \'a\'', $lines[0]);
self::assertContains('3) Name: \'c\'', $lines[1]);
self::assertContains('2) Name: \'b\'', $lines[2]);
self::assertContains('1) Name: \'d\'', $lines[3]);
}
public function testPicksUpCookiesFromRedirects()
{
$mock = new MockHandler([
new Response(301, [
'Location' => 'http://foo.com/baz',
'Set-Cookie' => 'foo=bar; Domain=foo.com'
]),
new Response(200)
]);
$handler = HandlerStack::create($mock);
$request = new Request('GET', 'http://foo.com/bar');
$jar = new CookieJar();
$response = $handler($request, [
'allow_redirects' => true,
'cookies' => $jar
])->wait();
self::assertSame(200, $response->getStatusCode());
$lastRequest = $mock->getLastRequest();
self::assertSame('http://foo.com/baz', (string) $lastRequest->getUri());
self::assertSame('foo=bar', $lastRequest->getHeaderLine('Cookie'));
}
private function getFunctions()
{
$calls = [];
$a = function (callable $next) use (&$calls) {
return function ($v) use ($next, &$calls) {
$calls[] = ['a', $v];
return $next($v . '1');
};
};
$b = function (callable $next) use (&$calls) {
return function ($v) use ($next, &$calls) {
$calls[] = ['b', $v];
return $next($v . '2');
};
};
$c = function (callable $next) use (&$calls) {
return function ($v) use ($next, &$calls) {
$calls[] = ['c', $v];
return $next($v . '3');
};
};
$handler = function ($v) {
return 'Hello - ' . $v;
};
return [&$calls, $handler, $a, $b, $c];
}
public static function foo()
{
}
public function bar()
{
}
}

View File

@ -0,0 +1,21 @@
<?php
namespace GuzzleHttp\Test;
use GuzzleHttp\Psr7;
use GuzzleHttp\Utils;
use PHPUnit\Framework\TestCase;
class InternalUtilsTest extends TestCase
{
public function testCurrentTime()
{
self::assertGreaterThan(0, Utils::currentTime());
}
public function testIdnConvert()
{
$uri = Psr7\uri_for('https://яндекс.рф/images');
$uri = Utils::idnUriConvert($uri);
self::assertSame('xn--d1acpjx3f.xn--p1ai', $uri->getHost());
}
}

View File

@ -0,0 +1,93 @@
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
/**
* @covers GuzzleHttp\MessageFormatter
*/
class MessageFormatterTest extends TestCase
{
public function testCreatesWithClfByDefault()
{
$f = new MessageFormatter();
self::assertEquals(MessageFormatter::CLF, self::readAttribute($f, 'template'));
$f = new MessageFormatter(null);
self::assertEquals(MessageFormatter::CLF, self::readAttribute($f, 'template'));
}
public function dateProvider()
{
return [
['{ts}', '/^[0-9]{4}\-[0-9]{2}\-[0-9]{2}/'],
['{date_iso_8601}', '/^[0-9]{4}\-[0-9]{2}\-[0-9]{2}/'],
['{date_common_log}', '/^\d\d\/[A-Z][a-z]{2}\/[0-9]{4}/']
];
}
/**
* @dataProvider dateProvider
*/
public function testFormatsTimestamps($format, $pattern)
{
$f = new MessageFormatter($format);
$request = new Request('GET', '/');
$result = $f->format($request);
self::assertRegExp($pattern, $result);
}
public function formatProvider()
{
$request = new Request('PUT', '/', ['x-test' => 'abc'], Psr7\stream_for('foo'));
$response = new Response(200, ['X-Baz' => 'Bar'], Psr7\stream_for('baz'));
$err = new RequestException('Test', $request, $response);
return [
['{request}', [$request], Psr7\str($request)],
['{response}', [$request, $response], Psr7\str($response)],
['{request} {response}', [$request, $response], Psr7\str($request) . ' ' . Psr7\str($response)],
// Empty response yields no value
['{request} {response}', [$request], Psr7\str($request) . ' '],
['{req_headers}', [$request], "PUT / HTTP/1.1\r\nx-test: abc"],
['{res_headers}', [$request, $response], "HTTP/1.1 200 OK\r\nX-Baz: Bar"],
['{res_headers}', [$request], 'NULL'],
['{req_body}', [$request], 'foo'],
['{res_body}', [$request, $response], 'baz'],
['{res_body}', [$request], 'NULL'],
['{method}', [$request], $request->getMethod()],
['{url}', [$request], $request->getUri()],
['{target}', [$request], $request->getRequestTarget()],
['{req_version}', [$request], $request->getProtocolVersion()],
['{res_version}', [$request, $response], $response->getProtocolVersion()],
['{res_version}', [$request], 'NULL'],
['{host}', [$request], $request->getHeaderLine('Host')],
['{hostname}', [$request, $response], gethostname()],
['{hostname}{hostname}', [$request, $response], gethostname() . gethostname()],
['{code}', [$request, $response], $response->getStatusCode()],
['{code}', [$request], 'NULL'],
['{phrase}', [$request, $response], $response->getReasonPhrase()],
['{phrase}', [$request], 'NULL'],
['{error}', [$request, $response, $err], 'Test'],
['{error}', [$request], 'NULL'],
['{req_header_x-test}', [$request], 'abc'],
['{req_header_x-not}', [$request], ''],
['{res_header_X-Baz}', [$request, $response], 'Bar'],
['{res_header_x-not}', [$request, $response], ''],
['{res_header_X-Baz}', [$request], 'NULL'],
];
}
/**
* @dataProvider formatProvider
*/
public function testFormatsMessages($template, $args, $result)
{
$f = new MessageFormatter($template);
self::assertSame((string) $result, call_user_func_array([$f, 'format'], $args));
}
}

View File

@ -0,0 +1,217 @@
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Cookie\CookieJar;
use GuzzleHttp\Cookie\SetCookie;
use GuzzleHttp\Exception\RequestException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\MessageFormatter;
use GuzzleHttp\Middleware;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\Test\TestLogger;
class MiddlewareTest extends TestCase
{
public function testAddsCookiesToRequests()
{
$jar = new CookieJar();
$m = Middleware::cookies($jar);
$h = new MockHandler(
[
function (RequestInterface $request) {
return new Response(200, [
'Set-Cookie' => (string) new SetCookie([
'Name' => 'name',
'Value' => 'value',
'Domain' => 'foo.com'
])
]);
}
]
);
$f = $m($h);
$f(new Request('GET', 'http://foo.com'), ['cookies' => $jar])->wait();
self::assertCount(1, $jar);
}
/**
* @expectedException \GuzzleHttp\Exception\ClientException
*/
public function testThrowsExceptionOnHttpClientError()
{
$m = Middleware::httpErrors();
$h = new MockHandler([new Response(404)]);
$f = $m($h);
$p = $f(new Request('GET', 'http://foo.com'), ['http_errors' => true]);
self::assertSame('pending', $p->getState());
$p->wait();
self::assertSame('rejected', $p->getState());
}
/**
* @expectedException \GuzzleHttp\Exception\ServerException
*/
public function testThrowsExceptionOnHttpServerError()
{
$m = Middleware::httpErrors();
$h = new MockHandler([new Response(500)]);
$f = $m($h);
$p = $f(new Request('GET', 'http://foo.com'), ['http_errors' => true]);
self::assertSame('pending', $p->getState());
$p->wait();
self::assertSame('rejected', $p->getState());
}
/**
* @dataProvider getHistoryUseCases
*/
public function testTracksHistory($container)
{
$m = Middleware::history($container);
$h = new MockHandler([new Response(200), new Response(201)]);
$f = $m($h);
$p1 = $f(new Request('GET', 'http://foo.com'), ['headers' => ['foo' => 'bar']]);
$p2 = $f(new Request('HEAD', 'http://foo.com'), ['headers' => ['foo' => 'baz']]);
$p1->wait();
$p2->wait();
self::assertCount(2, $container);
self::assertSame(200, $container[0]['response']->getStatusCode());
self::assertSame(201, $container[1]['response']->getStatusCode());
self::assertSame('GET', $container[0]['request']->getMethod());
self::assertSame('HEAD', $container[1]['request']->getMethod());
self::assertSame('bar', $container[0]['options']['headers']['foo']);
self::assertSame('baz', $container[1]['options']['headers']['foo']);
}
public function getHistoryUseCases()
{
return [
[[]], // 1. Container is an array
[new \ArrayObject()] // 2. Container is an ArrayObject
];
}
public function testTracksHistoryForFailures()
{
$container = [];
$m = Middleware::history($container);
$request = new Request('GET', 'http://foo.com');
$h = new MockHandler([new RequestException('error', $request)]);
$f = $m($h);
$f($request, [])->wait(false);
self::assertCount(1, $container);
self::assertSame('GET', $container[0]['request']->getMethod());
self::assertInstanceOf(RequestException::class, $container[0]['error']);
}
public function testTapsBeforeAndAfter()
{
$calls = [];
$m = function ($handler) use (&$calls) {
return function ($request, $options) use ($handler, &$calls) {
$calls[] = '2';
return $handler($request, $options);
};
};
$m2 = Middleware::tap(
function (RequestInterface $request, array $options) use (&$calls) {
$calls[] = '1';
},
function (RequestInterface $request, array $options, PromiseInterface $p) use (&$calls) {
$calls[] = '3';
}
);
$h = new MockHandler([new Response()]);
$b = new HandlerStack($h);
$b->push($m2);
$b->push($m);
$comp = $b->resolve();
$p = $comp(new Request('GET', 'http://foo.com'), []);
self::assertSame('123', implode('', $calls));
self::assertInstanceOf(PromiseInterface::class, $p);
self::assertSame(200, $p->wait()->getStatusCode());
}
public function testMapsRequest()
{
$h = new MockHandler([
function (RequestInterface $request, array $options) {
self::assertSame('foo', $request->getHeaderLine('Bar'));
return new Response(200);
}
]);
$stack = new HandlerStack($h);
$stack->push(Middleware::mapRequest(function (RequestInterface $request) {
return $request->withHeader('Bar', 'foo');
}));
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com'), []);
self::assertInstanceOf(PromiseInterface::class, $p);
}
public function testMapsResponse()
{
$h = new MockHandler([new Response(200)]);
$stack = new HandlerStack($h);
$stack->push(Middleware::mapResponse(function (ResponseInterface $response) {
return $response->withHeader('Bar', 'foo');
}));
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com'), []);
$p->wait();
self::assertSame('foo', $p->wait()->getHeaderLine('Bar'));
}
public function testLogsRequestsAndResponses()
{
$h = new MockHandler([new Response(200)]);
$stack = new HandlerStack($h);
$logger = new TestLogger();
$formatter = new MessageFormatter();
$stack->push(Middleware::log($logger, $formatter));
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com'), []);
$p->wait();
self::assertCount(1, $logger->records);
self::assertContains('"PUT / HTTP/1.1" 200', $logger->records[0]['message']);
}
public function testLogsRequestsAndResponsesCustomLevel()
{
$h = new MockHandler([new Response(200)]);
$stack = new HandlerStack($h);
$logger = new TestLogger();
$formatter = new MessageFormatter();
$stack->push(Middleware::log($logger, $formatter, 'debug'));
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com'), []);
$p->wait();
self::assertCount(1, $logger->records);
self::assertContains('"PUT / HTTP/1.1" 200', $logger->records[0]['message']);
self::assertSame('debug', $logger->records[0]['level']);
}
public function testLogsRequestsAndErrors()
{
$h = new MockHandler([new Response(404)]);
$stack = new HandlerStack($h);
$logger = new TestLogger();
$formatter = new MessageFormatter('{code} {error}');
$stack->push(Middleware::log($logger, $formatter));
$stack->push(Middleware::httpErrors());
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com'), ['http_errors' => true]);
$p->wait(false);
self::assertCount(1, $logger->records);
self::assertContains('PUT http://www.google.com', $logger->records[0]['message']);
self::assertContains('404 Not Found', $logger->records[0]['message']);
}
}

View File

@ -0,0 +1,193 @@
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Pool;
use GuzzleHttp\Promise\Promise;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
class PoolTest extends TestCase
{
/**
* @expectedException \InvalidArgumentException
*/
public function testValidatesIterable()
{
$p = new Pool(new Client(), 'foo');
$p->promise()->wait();
}
/**
* @expectedException \InvalidArgumentException
*/
public function testValidatesEachElement()
{
$c = new Client();
$requests = ['foo'];
$p = new Pool($c, new \ArrayIterator($requests));
$p->promise()->wait();
}
/**
* @doesNotPerformAssertions
*/
public function testSendsAndRealizesFuture()
{
$c = $this->getClient();
$p = new Pool($c, [new Request('GET', 'http://example.com')]);
$p->promise()->wait();
}
/**
* @doesNotPerformAssertions
*/
public function testExecutesPendingWhenWaiting()
{
$r1 = new Promise(function () use (&$r1) {
$r1->resolve(new Response());
});
$r2 = new Promise(function () use (&$r2) {
$r2->resolve(new Response());
});
$r3 = new Promise(function () use (&$r3) {
$r3->resolve(new Response());
});
$handler = new MockHandler([$r1, $r2, $r3]);
$c = new Client(['handler' => $handler]);
$p = new Pool($c, [
new Request('GET', 'http://example.com'),
new Request('GET', 'http://example.com'),
new Request('GET', 'http://example.com'),
], ['pool_size' => 2]);
$p->promise()->wait();
}
public function testUsesRequestOptions()
{
$h = [];
$handler = new MockHandler([
function (RequestInterface $request) use (&$h) {
$h[] = $request;
return new Response();
}
]);
$c = new Client(['handler' => $handler]);
$opts = ['options' => ['headers' => ['x-foo' => 'bar']]];
$p = new Pool($c, [new Request('GET', 'http://example.com')], $opts);
$p->promise()->wait();
self::assertCount(1, $h);
self::assertTrue($h[0]->hasHeader('x-foo'));
}
public function testCanProvideCallablesThatReturnResponses()
{
$h = [];
$handler = new MockHandler([
function (RequestInterface $request) use (&$h) {
$h[] = $request;
return new Response();
}
]);
$c = new Client(['handler' => $handler]);
$optHistory = [];
$fn = function (array $opts) use (&$optHistory, $c) {
$optHistory = $opts;
return $c->request('GET', 'http://example.com', $opts);
};
$opts = ['options' => ['headers' => ['x-foo' => 'bar']]];
$p = new Pool($c, [$fn], $opts);
$p->promise()->wait();
self::assertCount(1, $h);
self::assertTrue($h[0]->hasHeader('x-foo'));
}
public function testBatchesResults()
{
$requests = [
new Request('GET', 'http://foo.com/200'),
new Request('GET', 'http://foo.com/201'),
new Request('GET', 'http://foo.com/202'),
new Request('GET', 'http://foo.com/404'),
];
$fn = function (RequestInterface $request) {
return new Response(substr($request->getUri()->getPath(), 1));
};
$mock = new MockHandler([$fn, $fn, $fn, $fn]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$results = Pool::batch($client, $requests);
self::assertCount(4, $results);
self::assertSame([0, 1, 2, 3], array_keys($results));
self::assertSame(200, $results[0]->getStatusCode());
self::assertSame(201, $results[1]->getStatusCode());
self::assertSame(202, $results[2]->getStatusCode());
self::assertInstanceOf(ClientException::class, $results[3]);
}
public function testBatchesResultsWithCallbacks()
{
$requests = [
new Request('GET', 'http://foo.com/200'),
new Request('GET', 'http://foo.com/201')
];
$mock = new MockHandler([
function (RequestInterface $request) {
return new Response(substr($request->getUri()->getPath(), 1));
}
]);
$client = new Client(['handler' => $mock]);
$results = Pool::batch($client, $requests, [
'fulfilled' => function ($value) use (&$called) {
$called = true;
}
]);
self::assertCount(2, $results);
self::assertTrue($called);
}
public function testUsesYieldedKeyInFulfilledCallback()
{
$r1 = new Promise(function () use (&$r1) {
$r1->resolve(new Response());
});
$r2 = new Promise(function () use (&$r2) {
$r2->resolve(new Response());
});
$r3 = new Promise(function () use (&$r3) {
$r3->resolve(new Response());
});
$handler = new MockHandler([$r1, $r2, $r3]);
$c = new Client(['handler' => $handler]);
$keys = [];
$requests = [
'request_1' => new Request('GET', 'http://example.com'),
'request_2' => new Request('GET', 'http://example.com'),
'request_3' => new Request('GET', 'http://example.com'),
];
$p = new Pool($c, $requests, [
'pool_size' => 2,
'fulfilled' => function ($res, $index) use (&$keys) {
$keys[] = $index;
}
]);
$p->promise()->wait();
self::assertCount(3, $keys);
self::assertSame($keys, array_keys($requests));
}
private function getClient($total = 1)
{
$queue = [];
for ($i = 0; $i < $total; $i++) {
$queue[] = new Response();
}
$handler = new MockHandler($queue);
return new Client(['handler' => $handler]);
}
}

View File

@ -0,0 +1,155 @@
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Promise\PromiseInterface;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\FnStream;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
class PrepareBodyMiddlewareTest extends TestCase
{
public function methodProvider()
{
$methods = ['GET', 'PUT', 'POST'];
$bodies = ['Test', ''];
foreach ($methods as $method) {
foreach ($bodies as $body) {
yield [$method, $body];
}
}
}
/**
* @dataProvider methodProvider
*/
public function testAddsContentLengthWhenMissingAndPossible($method, $body)
{
$h = new MockHandler([
function (RequestInterface $request) use ($body) {
$length = strlen($body);
if ($length > 0) {
self::assertEquals($length, $request->getHeaderLine('Content-Length'));
} else {
self::assertFalse($request->hasHeader('Content-Length'));
}
return new Response(200);
}
]);
$m = Middleware::prepareBody();
$stack = new HandlerStack($h);
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(new Request($method, 'http://www.google.com', [], $body), []);
self::assertInstanceOf(PromiseInterface::class, $p);
$response = $p->wait();
self::assertSame(200, $response->getStatusCode());
}
public function testAddsTransferEncodingWhenNoContentLength()
{
$body = FnStream::decorate(Psr7\stream_for('foo'), [
'getSize' => function () {
return null;
}
]);
$h = new MockHandler([
function (RequestInterface $request) {
self::assertFalse($request->hasHeader('Content-Length'));
self::assertSame('chunked', $request->getHeaderLine('Transfer-Encoding'));
return new Response(200);
}
]);
$m = Middleware::prepareBody();
$stack = new HandlerStack($h);
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com', [], $body), []);
self::assertInstanceOf(PromiseInterface::class, $p);
$response = $p->wait();
self::assertSame(200, $response->getStatusCode());
}
public function testAddsContentTypeWhenMissingAndPossible()
{
$bd = Psr7\stream_for(fopen(__DIR__ . '/../composer.json', 'r'));
$h = new MockHandler([
function (RequestInterface $request) {
self::assertSame('application/json', $request->getHeaderLine('Content-Type'));
self::assertTrue($request->hasHeader('Content-Length'));
return new Response(200);
}
]);
$m = Middleware::prepareBody();
$stack = new HandlerStack($h);
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com', [], $bd), []);
self::assertInstanceOf(PromiseInterface::class, $p);
$response = $p->wait();
self::assertSame(200, $response->getStatusCode());
}
public function expectProvider()
{
return [
[true, ['100-Continue']],
[false, []],
[10, ['100-Continue']],
[500000, []]
];
}
/**
* @dataProvider expectProvider
*/
public function testAddsExpect($value, $result)
{
$bd = Psr7\stream_for(fopen(__DIR__ . '/../composer.json', 'r'));
$h = new MockHandler([
function (RequestInterface $request) use ($result) {
self::assertSame($result, $request->getHeader('Expect'));
return new Response(200);
}
]);
$m = Middleware::prepareBody();
$stack = new HandlerStack($h);
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(new Request('PUT', 'http://www.google.com', [], $bd), [
'expect' => $value
]);
self::assertInstanceOf(PromiseInterface::class, $p);
$response = $p->wait();
self::assertSame(200, $response->getStatusCode());
}
public function testIgnoresIfExpectIsPresent()
{
$bd = Psr7\stream_for(fopen(__DIR__ . '/../composer.json', 'r'));
$h = new MockHandler([
function (RequestInterface $request) {
self::assertSame(['Foo'], $request->getHeader('Expect'));
return new Response(200);
}
]);
$m = Middleware::prepareBody();
$stack = new HandlerStack($h);
$stack->push($m);
$comp = $stack->resolve();
$p = $comp(
new Request('PUT', 'http://www.google.com', ['Expect' => 'Foo'], $bd),
['expect' => true]
);
self::assertInstanceOf(PromiseInterface::class, $p);
$response = $p->wait();
self::assertSame(200, $response->getStatusCode());
}
}

View File

@ -0,0 +1,439 @@
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\RedirectMiddleware;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
/**
* @covers GuzzleHttp\RedirectMiddleware
*/
class RedirectMiddlewareTest extends TestCase
{
public function testIgnoresNonRedirects()
{
$response = new Response(200);
$stack = new HandlerStack(new MockHandler([$response]));
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'http://example.com');
$promise = $handler($request, []);
$response = $promise->wait();
self::assertSame(200, $response->getStatusCode());
}
public function testIgnoresWhenNoLocation()
{
$response = new Response(304);
$stack = new HandlerStack(new MockHandler([$response]));
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'http://example.com');
$promise = $handler($request, []);
$response = $promise->wait();
self::assertSame(304, $response->getStatusCode());
}
public function testRedirectsWithAbsoluteUri()
{
$mock = new MockHandler([
new Response(302, ['Location' => 'http://test.com']),
new Response(200)
]);
$stack = new HandlerStack($mock);
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'http://example.com?a=b');
$promise = $handler($request, [
'allow_redirects' => ['max' => 2]
]);
$response = $promise->wait();
self::assertSame(200, $response->getStatusCode());
self::assertSame('http://test.com', (string)$mock->getLastRequest()->getUri());
}
public function testRedirectsWithRelativeUri()
{
$mock = new MockHandler([
new Response(302, ['Location' => '/foo']),
new Response(200)
]);
$stack = new HandlerStack($mock);
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'http://example.com?a=b');
$promise = $handler($request, [
'allow_redirects' => ['max' => 2]
]);
$response = $promise->wait();
self::assertSame(200, $response->getStatusCode());
self::assertSame('http://example.com/foo', (string)$mock->getLastRequest()->getUri());
}
/**
* @expectedException \GuzzleHttp\Exception\TooManyRedirectsException
* @expectedExceptionMessage Will not follow more than 3 redirects
*/
public function testLimitsToMaxRedirects()
{
$mock = new MockHandler([
new Response(301, ['Location' => 'http://test.com']),
new Response(302, ['Location' => 'http://test.com']),
new Response(303, ['Location' => 'http://test.com']),
new Response(304, ['Location' => 'http://test.com'])
]);
$stack = new HandlerStack($mock);
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'http://example.com');
$promise = $handler($request, ['allow_redirects' => ['max' => 3]]);
$promise->wait();
}
/**
* @expectedException \GuzzleHttp\Exception\BadResponseException
* @expectedExceptionMessage Redirect URI,
*/
public function testEnsuresProtocolIsValid()
{
$mock = new MockHandler([
new Response(301, ['Location' => 'ftp://test.com'])
]);
$stack = new HandlerStack($mock);
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'http://example.com');
$handler($request, ['allow_redirects' => ['max' => 3]])->wait();
}
public function testAddsRefererHeader()
{
$mock = new MockHandler([
new Response(302, ['Location' => 'http://test.com']),
new Response(200)
]);
$stack = new HandlerStack($mock);
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'http://example.com?a=b');
$promise = $handler($request, [
'allow_redirects' => ['max' => 2, 'referer' => true]
]);
$promise->wait();
self::assertSame(
'http://example.com?a=b',
$mock->getLastRequest()->getHeaderLine('Referer')
);
}
public function testAddsRefererHeaderButClearsUserInfo()
{
$mock = new MockHandler([
new Response(302, ['Location' => 'http://test.com']),
new Response(200)
]);
$stack = new HandlerStack($mock);
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'http://foo:bar@example.com?a=b');
$promise = $handler($request, [
'allow_redirects' => ['max' => 2, 'referer' => true]
]);
$promise->wait();
self::assertSame(
'http://example.com?a=b',
$mock->getLastRequest()->getHeaderLine('Referer')
);
}
public function testAddsGuzzleRedirectHeader()
{
$mock = new MockHandler([
new Response(302, ['Location' => 'http://example.com']),
new Response(302, ['Location' => 'http://example.com/foo']),
new Response(302, ['Location' => 'http://example.com/bar']),
new Response(200)
]);
$stack = new HandlerStack($mock);
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'http://example.com?a=b');
$promise = $handler($request, [
'allow_redirects' => ['track_redirects' => true]
]);
$response = $promise->wait(true);
self::assertSame(
[
'http://example.com',
'http://example.com/foo',
'http://example.com/bar',
],
$response->getHeader(RedirectMiddleware::HISTORY_HEADER)
);
}
public function testAddsGuzzleRedirectStatusHeader()
{
$mock = new MockHandler([
new Response(301, ['Location' => 'http://example.com']),
new Response(302, ['Location' => 'http://example.com/foo']),
new Response(301, ['Location' => 'http://example.com/bar']),
new Response(302, ['Location' => 'http://example.com/baz']),
new Response(200)
]);
$stack = new HandlerStack($mock);
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'http://example.com?a=b');
$promise = $handler($request, [
'allow_redirects' => ['track_redirects' => true]
]);
$response = $promise->wait(true);
self::assertSame(
[
'301',
'302',
'301',
'302',
],
$response->getHeader(RedirectMiddleware::STATUS_HISTORY_HEADER)
);
}
public function testDoesNotAddRefererWhenGoingFromHttpsToHttp()
{
$mock = new MockHandler([
new Response(302, ['Location' => 'http://test.com']),
new Response(200)
]);
$stack = new HandlerStack($mock);
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'https://example.com?a=b');
$promise = $handler($request, [
'allow_redirects' => ['max' => 2, 'referer' => true]
]);
$promise->wait();
self::assertFalse($mock->getLastRequest()->hasHeader('Referer'));
}
public function testInvokesOnRedirectForRedirects()
{
$mock = new MockHandler([
new Response(302, ['Location' => 'http://test.com']),
new Response(200)
]);
$stack = new HandlerStack($mock);
$stack->push(Middleware::redirect());
$handler = $stack->resolve();
$request = new Request('GET', 'http://example.com?a=b');
$call = false;
$promise = $handler($request, [
'allow_redirects' => [
'max' => 2,
'on_redirect' => function ($request, $response, $uri) use (&$call) {
self::assertSame(302, $response->getStatusCode());
self::assertSame('GET', $request->getMethod());
self::assertSame('http://test.com', (string) $uri);
$call = true;
}
]
]);
$promise->wait();
self::assertTrue($call);
}
/**
* @testWith ["digest"]
* ["ntlm"]
*/
public function testRemoveCurlAuthorizationOptionsOnRedirectCrossHost($auth)
{
if (!defined('\CURLOPT_HTTPAUTH')) {
self::markTestSkipped('ext-curl is required for this test');
}
$mock = new MockHandler([
new Response(302, ['Location' => 'http://test.com']),
static function (RequestInterface $request, $options) {
self::assertFalse(
isset($options['curl'][\CURLOPT_HTTPAUTH]),
'curl options still contain CURLOPT_HTTPAUTH entry'
);
self::assertFalse(
isset($options['curl'][\CURLOPT_USERPWD]),
'curl options still contain CURLOPT_USERPWD entry'
);
return new Response(200);
}
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get('http://example.com?a=b', ['auth' => ['testuser', 'testpass', $auth]]);
}
/**
* @testWith ["digest"]
* ["ntlm"]
*/
public function testRemoveCurlAuthorizationOptionsOnRedirectCrossPort($auth)
{
if (!defined('\CURLOPT_HTTPAUTH')) {
self::markTestSkipped('ext-curl is required for this test');
}
$mock = new MockHandler([
new Response(302, ['Location' => 'http://example.com:81/']),
static function (RequestInterface $request, $options) {
self::assertFalse(
isset($options['curl'][\CURLOPT_HTTPAUTH]),
'curl options still contain CURLOPT_HTTPAUTH entry'
);
self::assertFalse(
isset($options['curl'][\CURLOPT_USERPWD]),
'curl options still contain CURLOPT_USERPWD entry'
);
return new Response(200);
}
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get('http://example.com?a=b', ['auth' => ['testuser', 'testpass', $auth]]);
}
/**
* @testWith ["digest"]
* ["ntlm"]
*/
public function testRemoveCurlAuthorizationOptionsOnRedirectCrossScheme($auth)
{
if (!defined('\CURLOPT_HTTPAUTH')) {
self::markTestSkipped('ext-curl is required for this test');
}
$mock = new MockHandler([
new Response(302, ['Location' => 'http://example.com?a=b']),
static function (RequestInterface $request, $options) {
self::assertFalse(
isset($options['curl'][\CURLOPT_HTTPAUTH]),
'curl options still contain CURLOPT_HTTPAUTH entry'
);
self::assertFalse(
isset($options['curl'][\CURLOPT_USERPWD]),
'curl options still contain CURLOPT_USERPWD entry'
);
return new Response(200);
}
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get('https://example.com?a=b', ['auth' => ['testuser', 'testpass', $auth]]);
}
/**
* @testWith ["digest"]
* ["ntlm"]
*/
public function testRemoveCurlAuthorizationOptionsOnRedirectCrossSchemeSamePort($auth)
{
if (!defined('\CURLOPT_HTTPAUTH')) {
self::markTestSkipped('ext-curl is required for this test');
}
$mock = new MockHandler([
new Response(302, ['Location' => 'http://example.com:80?a=b']),
static function (RequestInterface $request, $options) {
self::assertFalse(
isset($options['curl'][\CURLOPT_HTTPAUTH]),
'curl options still contain CURLOPT_HTTPAUTH entry'
);
self::assertFalse(
isset($options['curl'][\CURLOPT_USERPWD]),
'curl options still contain CURLOPT_USERPWD entry'
);
return new Response(200);
}
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get('https://example.com?a=b', ['auth' => ['testuser', 'testpass', $auth]]);
}
/**
* @testWith ["digest"]
* ["ntlm"]
*/
public function testNotRemoveCurlAuthorizationOptionsOnRedirect($auth)
{
if (!defined('\CURLOPT_HTTPAUTH') || !defined('\CURLOPT_USERPWD')) {
self::markTestSkipped('ext-curl is required for this test');
}
$mock = new MockHandler([
new Response(302, ['Location' => 'http://example.com/2']),
static function (RequestInterface $request, $options) {
self::assertTrue(
isset($options['curl'][\CURLOPT_HTTPAUTH]),
'curl options does not contain expected CURLOPT_HTTPAUTH entry'
);
self::assertTrue(
isset($options['curl'][\CURLOPT_USERPWD]),
'curl options does not contain expected CURLOPT_USERPWD entry'
);
return new Response(200);
}
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get('http://example.com?a=b', ['auth' => ['testuser', 'testpass', $auth]]);
}
public function crossOriginRedirectProvider()
{
return [
['http://example.com/123', 'http://example.com/', false],
['http://example.com/123', 'http://example.com:80/', false],
['http://example.com:80/123', 'http://example.com/', false],
['http://example.com:80/123', 'http://example.com:80/', false],
['http://example.com/123', 'https://example.com/', true],
['http://example.com/123', 'http://www.example.com/', true],
['http://example.com/123', 'http://example.com:81/', true],
['http://example.com:80/123', 'http://example.com:81/', true],
['https://example.com/123', 'https://example.com/', false],
['https://example.com/123', 'https://example.com:443/', false],
['https://example.com:443/123', 'https://example.com/', false],
['https://example.com:443/123', 'https://example.com:443/', false],
['https://example.com/123', 'http://example.com/', true],
['https://example.com/123', 'https://www.example.com/', true],
['https://example.com/123', 'https://example.com:444/', true],
['https://example.com:443/123', 'https://example.com:444/', true],
];
}
/**
* @dataProvider crossOriginRedirectProvider
*/
public function testHeadersTreatmentOnRedirect($originalUri, $targetUri, $isCrossOrigin)
{
$mock = new MockHandler([
new Response(302, ['Location' => $targetUri]),
function (RequestInterface $request) use ($isCrossOrigin) {
self::assertSame(!$isCrossOrigin, $request->hasHeader('Authorization'));
self::assertSame(!$isCrossOrigin, $request->hasHeader('Cookie'));
return new Response(200);
}
]);
$handler = HandlerStack::create($mock);
$client = new Client(['handler' => $handler]);
$client->get($originalUri, ['auth' => ['testuser', 'testpass'], 'headers' => ['Cookie' => 'foo=bar']]);
}
}

View File

@ -0,0 +1,81 @@
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7;
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\RetryMiddleware;
use PHPUnit\Framework\TestCase;
class RetryMiddlewareTest extends TestCase
{
public function testRetriesWhenDeciderReturnsTrue()
{
$delayCalls = 0;
$calls = [];
$decider = function ($retries, $request, $response, $error) use (&$calls) {
$calls[] = func_get_args();
return count($calls) < 3;
};
$delay = function ($retries, $response) use (&$delayCalls) {
$delayCalls++;
self::assertSame($retries, $delayCalls);
self::assertInstanceOf(Response::class, $response);
return 1;
};
$m = Middleware::retry($decider, $delay);
$h = new MockHandler([new Response(200), new Response(201), new Response(202)]);
$f = $m($h);
$c = new Client(['handler' => $f]);
$p = $c->sendAsync(new Request('GET', 'http://test.com'), []);
$p->wait();
self::assertCount(3, $calls);
self::assertSame(2, $delayCalls);
self::assertSame(202, $p->wait()->getStatusCode());
}
public function testDoesNotRetryWhenDeciderReturnsFalse()
{
$decider = function () {
return false;
};
$m = Middleware::retry($decider);
$h = new MockHandler([new Response(200)]);
$c = new Client(['handler' => $m($h)]);
$p = $c->sendAsync(new Request('GET', 'http://test.com'), []);
self::assertSame(200, $p->wait()->getStatusCode());
}
public function testCanRetryExceptions()
{
$calls = [];
$decider = function ($retries, $request, $response, $error) use (&$calls) {
$calls[] = func_get_args();
return $error instanceof \Exception;
};
$m = Middleware::retry($decider);
$h = new MockHandler([new \Exception(), new Response(201)]);
$c = new Client(['handler' => $m($h)]);
$p = $c->sendAsync(new Request('GET', 'http://test.com'), []);
self::assertSame(201, $p->wait()->getStatusCode());
self::assertCount(2, $calls);
self::assertSame(0, $calls[0][0]);
self::assertNull($calls[0][2]);
self::assertInstanceOf('Exception', $calls[0][3]);
self::assertSame(1, $calls[1][0]);
self::assertInstanceOf(Response::class, $calls[1][2]);
self::assertNull($calls[1][3]);
}
public function testBackoffCalculateDelay()
{
self::assertSame(0, RetryMiddleware::exponentialDelay(0));
self::assertSame(1000, RetryMiddleware::exponentialDelay(1));
self::assertSame(2000, RetryMiddleware::exponentialDelay(2));
self::assertSame(4000, RetryMiddleware::exponentialDelay(3));
self::assertSame(8000, RetryMiddleware::exponentialDelay(4));
}
}

View File

@ -0,0 +1,174 @@
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7;
use Psr\Http\Message\ResponseInterface;
/**
* The Server class is used to control a scripted webserver using node.js that
* will respond to HTTP requests with queued responses.
*
* 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
{
/** @var Client */
private static $client;
private static $started = false;
public static $url = 'http://127.0.0.1:8126/';
public static $port = 8126;
/**
* Flush the received requests from the server
* @throws \RuntimeException
*/
public static function flush()
{
return self::getClient()->request('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|ResponseInterface $responses A single or array of Responses
* to queue.
* @throws \Exception
*/
public static function enqueue($responses)
{
$data = [];
foreach ((array) $responses as $response) {
if (!($response instanceof ResponseInterface)) {
throw new \Exception('Invalid response given.');
}
$headers = array_map(function ($h) {
return implode(' ,', $h);
}, $response->getHeaders());
$data[] = [
'status' => (string) $response->getStatusCode(),
'reason' => $response->getReasonPhrase(),
'headers' => $headers,
'body' => base64_encode((string) $response->getBody())
];
}
self::getClient()->request('PUT', 'guzzle-server/responses', [
'json' => $data
]);
}
/**
* Get all of the received requests
*
* @return ResponseInterface[]
* @throws \RuntimeException
*/
public static function received()
{
if (!self::$started) {
return [];
}
$response = self::getClient()->request('GET', 'guzzle-server/requests');
$data = json_decode($response->getBody(), true);
return array_map(
function ($message) {
$uri = $message['uri'];
if (isset($message['query_string'])) {
$uri .= '?' . $message['query_string'];
}
$response = new Psr7\Request(
$message['http_method'],
$uri,
$message['headers'],
$message['body'],
$message['version']
);
return $response->withUri(
$response->getUri()
->withScheme('http')
->withHost($response->getHeaderLine('host'))
);
},
$data
);
}
/**
* Stop running the node.js server
*/
public static function stop()
{
if (self::$started) {
self::getClient()->request('DELETE', 'guzzle-server');
}
self::$started = false;
}
public static function wait($maxTries = 5)
{
$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;
}
if (!self::isListening()) {
exec('node ' . __DIR__ . '/server.js '
. self::$port . ' >> /tmp/server.log 2>&1 &');
self::wait();
}
self::$started = true;
}
private static function isListening()
{
try {
self::getClient()->request('GET', 'guzzle-server/perf', [
'connect_timeout' => 5,
'timeout' => 5
]);
return true;
} catch (\Exception $e) {
return false;
}
}
private static function getClient()
{
if (!self::$client) {
self::$client = new Client([
'base_uri' => self::$url,
'sync' => true,
]);
}
return self::$client;
}
}

View File

@ -0,0 +1,30 @@
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\Psr7;
use GuzzleHttp\TransferStats;
use PHPUnit\Framework\TestCase;
class TransferStatsTest extends TestCase
{
public function testHasData()
{
$request = new Psr7\Request('GET', 'http://foo.com');
$response = new Psr7\Response();
$stats = new TransferStats(
$request,
$response,
10.5,
null,
['foo' => 'bar']
);
self::assertSame($request, $stats->getRequest());
self::assertSame($response, $stats->getResponse());
self::assertTrue($stats->hasResponse());
self::assertSame(['foo' => 'bar'], $stats->getHandlerStats());
self::assertSame('bar', $stats->getHandlerStat('foo'));
self::assertSame($request->getUri(), $stats->getEffectiveUri());
self::assertEquals(10.5, $stats->getTransferTime());
self::assertNull($stats->getHandlerErrorData());
}
}

View File

@ -0,0 +1,202 @@
<?php
namespace GuzzleHttp\Tests;
use GuzzleHttp\UriTemplate;
use PHPUnit\Framework\TestCase;
/**
* @covers GuzzleHttp\UriTemplate
*/
class UriTemplateTest extends TestCase
{
/**
* @return array
*/
public function templateProvider()
{
$params = [
'var' => 'value',
'hello' => 'Hello World!',
'empty' => '',
'path' => '/foo/bar',
'x' => '1024',
'y' => '768',
'null' => null,
'list' => ['red', 'green', 'blue'],
'keys' => [
"semi" => ';',
"dot" => '.',
"comma" => ','
],
'empty_keys' => [],
];
return array_map(function ($t) use ($params) {
$t[] = $params;
return $t;
}, [
['foo', 'foo'],
['{var}', 'value'],
['{hello}', 'Hello%20World%21'],
['{+var}', 'value'],
['{+hello}', 'Hello%20World!'],
['{+path}/here', '/foo/bar/here'],
['here?ref={+path}', 'here?ref=/foo/bar'],
['X{#var}', 'X#value'],
['X{#hello}', 'X#Hello%20World!'],
['map?{x,y}', 'map?1024,768'],
['{x,hello,y}', '1024,Hello%20World%21,768'],
['{+x,hello,y}', '1024,Hello%20World!,768'],
['{+path,x}/here', '/foo/bar,1024/here'],
['{#x,hello,y}', '#1024,Hello%20World!,768'],
['{#path,x}/here', '#/foo/bar,1024/here'],
['X{.var}', 'X.value'],
['X{.x,y}', 'X.1024.768'],
['{/var}', '/value'],
['{/var,x}/here', '/value/1024/here'],
['{;x,y}', ';x=1024;y=768'],
['{;x,y,empty}', ';x=1024;y=768;empty'],
['{?x,y}', '?x=1024&y=768'],
['{?x,y,empty}', '?x=1024&y=768&empty='],
['?fixed=yes{&x}', '?fixed=yes&x=1024'],
['{&x,y,empty}', '&x=1024&y=768&empty='],
['{var:3}', 'val'],
['{var:30}', 'value'],
['{list}', 'red,green,blue'],
['{list*}', 'red,green,blue'],
['{keys}', 'semi,%3B,dot,.,comma,%2C'],
['{keys*}', 'semi=%3B,dot=.,comma=%2C'],
['{+path:6}/here', '/foo/b/here'],
['{+list}', 'red,green,blue'],
['{+list*}', 'red,green,blue'],
['{+keys}', 'semi,;,dot,.,comma,,'],
['{+keys*}', 'semi=;,dot=.,comma=,'],
['{#path:6}/here', '#/foo/b/here'],
['{#list}', '#red,green,blue'],
['{#list*}', '#red,green,blue'],
['{#keys}', '#semi,;,dot,.,comma,,'],
['{#keys*}', '#semi=;,dot=.,comma=,'],
['X{.var:3}', 'X.val'],
['X{.list}', 'X.red,green,blue'],
['X{.list*}', 'X.red.green.blue'],
['X{.keys}', 'X.semi,%3B,dot,.,comma,%2C'],
['X{.keys*}', 'X.semi=%3B.dot=..comma=%2C'],
['{/var:1,var}', '/v/value'],
['{/list}', '/red,green,blue'],
['{/list*}', '/red/green/blue'],
['{/list*,path:4}', '/red/green/blue/%2Ffoo'],
['{/keys}', '/semi,%3B,dot,.,comma,%2C'],
['{/keys*}', '/semi=%3B/dot=./comma=%2C'],
['{;hello:5}', ';hello=Hello'],
['{;list}', ';list=red,green,blue'],
['{;list*}', ';list=red;list=green;list=blue'],
['{;keys}', ';keys=semi,%3B,dot,.,comma,%2C'],
['{;keys*}', ';semi=%3B;dot=.;comma=%2C'],
['{?var:3}', '?var=val'],
['{?list}', '?list=red,green,blue'],
['{?list*}', '?list=red&list=green&list=blue'],
['{?keys}', '?keys=semi,%3B,dot,.,comma,%2C'],
['{?keys*}', '?semi=%3B&dot=.&comma=%2C'],
['{&var:3}', '&var=val'],
['{&list}', '&list=red,green,blue'],
['{&list*}', '&list=red&list=green&list=blue'],
['{&keys}', '&keys=semi,%3B,dot,.,comma,%2C'],
['{&keys*}', '&semi=%3B&dot=.&comma=%2C'],
['{.null}', ''],
['{.null,var}', '.value'],
['X{.empty_keys*}', 'X'],
['X{.empty_keys}', 'X'],
// Test that missing expansions are skipped
['test{&missing*}', 'test'],
// Test that multiple expansions can be set
['http://{var}/{var:2}{?keys*}', 'http://value/va?semi=%3B&dot=.&comma=%2C'],
// Test more complex query string stuff
['http://www.test.com{+path}{?var,keys*}', 'http://www.test.com/foo/bar?var=value&semi=%3B&dot=.&comma=%2C']
]);
}
/**
* @dataProvider templateProvider
*/
public function testExpandsUriTemplates($template, $expansion, $params)
{
$uri = new UriTemplate();
self::assertSame($expansion, $uri->expand($template, $params));
}
public function expressionProvider()
{
return [
[
'{+var*}', [
'operator' => '+',
'values' => [
['modifier' => '*', 'value' => 'var']
]
],
],
[
'{?keys,var,val}', [
'operator' => '?',
'values' => [
['value' => 'keys', 'modifier' => ''],
['value' => 'var', 'modifier' => ''],
['value' => 'val', 'modifier' => '']
]
],
],
[
'{+x,hello,y}', [
'operator' => '+',
'values' => [
['value' => 'x', 'modifier' => ''],
['value' => 'hello', 'modifier' => ''],
['value' => 'y', 'modifier' => '']
]
]
]
];
}
/**
* @dataProvider expressionProvider
*/
public function testParsesExpressions($exp, $data)
{
$template = new UriTemplate();
// Access the config object
$class = new \ReflectionClass($template);
$method = $class->getMethod('parseExpression');
$method->setAccessible(true);
$exp = substr($exp, 1, -1);
self::assertSame($data, $method->invokeArgs($template, [$exp]));
}
/**
* @ticket https://github.com/guzzle/guzzle/issues/90
*/
public function testAllowsNestedArrayExpansion()
{
$template = new UriTemplate();
$result = $template->expand('http://example.com{+path}{/segments}{?query,data*,foo*}', [
'path' => '/foo/bar',
'segments' => ['one', 'two'],
'query' => 'test',
'data' => [
'more' => ['fun', 'ice cream']
],
'foo' => [
'baz' => [
'bar' => 'fizz',
'test' => 'buzz'
],
'bam' => 'boo'
]
]);
self::assertSame('http://example.com/foo/bar/one,two?query=test&more%5B0%5D=fun&more%5B1%5D=ice%20cream&baz%5Bbar%5D=fizz&baz%5Btest%5D=buzz&bam=boo', $result);
}
}

View File

@ -0,0 +1,39 @@
<?php
namespace {
setlocale(LC_ALL, 'C');
}
namespace GuzzleHttp\Test {
require __DIR__ . '/../vendor/autoload.php';
require __DIR__ . '/Server.php';
use GuzzleHttp\Tests\Server;
Server::start();
register_shutdown_function(function () {
Server::stop();
});
}
// Override curl_setopt_array() and curl_multi_setopt() to get the last set curl options
namespace GuzzleHttp\Handler {
function curl_setopt_array($handle, array $options)
{
if (!empty($_SERVER['curl_test'])) {
$_SERVER['_curl'] = $options;
} else {
unset($_SERVER['_curl']);
}
return \curl_setopt_array($handle, $options);
}
function curl_multi_setopt($handle, $option, $value)
{
if (!empty($_SERVER['curl_test'])) {
$_SERVER['_curl_multi'][$option] = $value;
} else {
unset($_SERVER['_curl_multi']);
}
return \curl_multi_setopt($handle, $option, $value);
}
}

View File

@ -0,0 +1,139 @@
<?php
namespace GuzzleHttp\Test;
use GuzzleHttp;
use PHPUnit\Framework\TestCase;
class FunctionsTest extends TestCase
{
public function testExpandsTemplate()
{
self::assertSame(
'foo/123',
GuzzleHttp\uri_template('foo/{bar}', ['bar' => '123'])
);
}
public function noBodyProvider()
{
return [['get'], ['head'], ['delete']];
}
public function testProvidesDefaultUserAgent()
{
$ua = GuzzleHttp\default_user_agent();
self::assertRegExp('#^GuzzleHttp/.+ curl/.+ PHP/.+$#', $ua);
}
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\Test\StrClass)'],
[['foo'], 'array(1)']
];
}
/**
* @dataProvider typeProvider
*/
public function testDescribesType($input, $output)
{
self::assertSame($output, GuzzleHttp\describe_type($input));
}
public function testParsesHeadersFromLines()
{
$lines = ['Foo: bar', 'Foo: baz', 'Abc: 123', 'Def: a, b'];
self::assertSame([
'Foo' => ['bar', 'baz'],
'Abc' => ['123'],
'Def' => ['a, b'],
], GuzzleHttp\headers_from_lines($lines));
}
public function testParsesHeadersFromLinesWithMultipleLines()
{
$lines = ['Foo: bar', 'Foo: baz', 'Foo: 123'];
self::assertSame([
'Foo' => ['bar', 'baz', '123'],
], GuzzleHttp\headers_from_lines($lines));
}
public function testReturnsDebugResource()
{
self::assertInternalType('resource', GuzzleHttp\debug_resource());
}
public function testProvidesDefaultCaBundler()
{
self::assertFileExists(GuzzleHttp\default_ca_bundle());
}
public function noProxyProvider()
{
return [
['mit.edu', ['.mit.edu'], false],
['foo.mit.edu', ['.mit.edu'], true],
['mit.edu', ['mit.edu'], true],
['mit.edu', ['baz', 'mit.edu'], true],
['mit.edu', ['', '', 'mit.edu'], true],
['mit.edu', ['baz', '*'], true],
];
}
/**
* @dataProvider noproxyProvider
*/
public function testChecksNoProxyList($host, $list, $result)
{
self::assertSame(
$result,
\GuzzleHttp\is_host_in_noproxy($host, $list)
);
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEnsuresNoProxyCheckHostIsSet()
{
\GuzzleHttp\is_host_in_noproxy('', []);
}
public function testEncodesJson()
{
self::assertSame('true', \GuzzleHttp\json_encode(true));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testEncodesJsonAndThrowsOnError()
{
\GuzzleHttp\json_encode("\x99");
}
public function testDecodesJson()
{
self::assertTrue(\GuzzleHttp\json_decode('true'));
}
/**
* @expectedException \InvalidArgumentException
*/
public function testDecodesJsonAndThrowsOnError()
{
\GuzzleHttp\json_decode('{{]]');
}
}
final class StrClass
{
public function __toString()
{
return 'foo';
}
}

View File

@ -0,0 +1,250 @@
/**
* 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:8126
*
* - Enqueue responses
* > PUT /guzzle-server/responses
* > Host: 127.0.0.1:8126
* >
* > [{'status': 200, 'reason': 'OK', 'headers': {}, 'body': '' }]
*
* - Get the received requests
* > GET /guzzle-server/requests
* > Host: 127.0.0.1:8126
*
* < 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:8126
*
* < 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:8126
*
* @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.url == '/guzzle-server/read-timeout') {
if (that.log) {
console.log('Sleeping');
}
res.writeHead(200, 'OK');
res.write("sleeping 60 seconds ...\n");
setTimeout(function () {
res.end("slept 60 seconds\n");
}, 60*1000);
}
} 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 = JSON.parse(request.body);
for (var i = 0; i < that.responses.length; i++) {
if (that.responses[i].body) {
that.responses[i].body = Buffer.from(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:8126/');
}
};
};
// Get the port from the arguments
port = process.argv.length >= 3 ? process.argv[2] : 8126;
log = process.argv.length >= 4 ? process.argv[3] : false;
// Start the server
server = new GuzzleServer(port, log);
server.start();