Skip to content

Commit

Permalink
Merge pull request #14 from php-http/plugin_client
Browse files Browse the repository at this point in the history
Add Plugin Client
  • Loading branch information
sagikazarmark committed May 3, 2016
2 parents 3e25b9f + 9c1c93b commit 556647f
Show file tree
Hide file tree
Showing 40 changed files with 2,837 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Add a flexible http client providing both contract, and only emulating what's necessary
- HTTP Client Router: route requests to underlying clients
- Plugin client and core plugins moved here from `php-http/plugins`

### Deprecated

Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@
"php": ">=5.4",
"php-http/httplug": "^1.0",
"php-http/message-factory": "^1.0",
"php-http/message": "^1.2"
"php-http/message": "^1.2",
"symfony/options-resolver": "^2.6|^3.0"
},
"require-dev": {
"phpspec/phpspec": "^2.4",
Expand Down
1 change: 1 addition & 0 deletions spec/FlexibleHttpClientSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ function it_does_not_emulate_a_client($client, RequestInterface $syncRequest, Re
{
$client->implement('Http\Client\HttpClient');
$client->implement('Http\Client\HttpAsyncClient');

$client->sendRequest($syncRequest)->shouldBeCalled();
$client->sendRequest($asyncRequest)->shouldNotBeCalled();
$client->sendAsyncRequest($asyncRequest)->shouldBeCalled();
Expand Down
84 changes: 84 additions & 0 deletions spec/Plugin/AddHostPluginSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

namespace spec\Http\Client\Common\Plugin;

use Http\Message\StreamFactory;
use Http\Message\UriFactory;
use Http\Promise\FulfilledPromise;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\StreamInterface;
use Psr\Http\Message\UriInterface;
use PhpSpec\ObjectBehavior;

class AddHostPluginSpec extends ObjectBehavior
{
function let(UriInterface $uri)
{
$this->beConstructedWith($uri);
}

function it_is_initializable(UriInterface $uri)
{
$uri->getHost()->shouldBeCalled()->willReturn('example.com');

$this->shouldHaveType('Http\Client\Common\Plugin\AddHostPlugin');
}

function it_is_a_plugin(UriInterface $uri)
{
$uri->getHost()->shouldBeCalled()->willReturn('example.com');

$this->shouldImplement('Http\Client\Common\Plugin');
}

function it_adds_domain(
RequestInterface $request,
UriInterface $host,
UriInterface $uri
) {
$host->getScheme()->shouldBeCalled()->willReturn('http://');
$host->getHost()->shouldBeCalled()->willReturn('example.com');

$request->getUri()->shouldBeCalled()->willReturn($uri);
$request->withUri($uri)->shouldBeCalled()->willReturn($request);

$uri->withScheme('http://')->shouldBeCalled()->willReturn($uri);
$uri->withHost('example.com')->shouldBeCalled()->willReturn($uri);
$uri->getHost()->shouldBeCalled()->willReturn('');

$this->beConstructedWith($host);
$this->handleRequest($request, function () {}, function () {});
}

function it_replaces_domain(
RequestInterface $request,
UriInterface $host,
UriInterface $uri
) {
$host->getScheme()->shouldBeCalled()->willReturn('http://');
$host->getHost()->shouldBeCalled()->willReturn('example.com');

$request->getUri()->shouldBeCalled()->willReturn($uri);
$request->withUri($uri)->shouldBeCalled()->willReturn($request);

$uri->withScheme('http://')->shouldBeCalled()->willReturn($uri);
$uri->withHost('example.com')->shouldBeCalled()->willReturn($uri);


$this->beConstructedWith($host, ['replace' => true]);
$this->handleRequest($request, function () {}, function () {});
}

function it_does_nothing_when_domain_exists(
RequestInterface $request,
UriInterface $host,
UriInterface $uri
) {
$request->getUri()->shouldBeCalled()->willReturn($uri);
$uri->getHost()->shouldBeCalled()->willReturn('default.com');

$this->beConstructedWith($host);
$this->handleRequest($request, function () {}, function () {});
}
}
40 changes: 40 additions & 0 deletions spec/Plugin/AuthenticationPluginSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace spec\Http\Client\Common\Plugin;

use Http\Message\Authentication;
use Http\Promise\Promise;
use Psr\Http\Message\RequestInterface;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class AuthenticationPluginSpec extends ObjectBehavior
{
function let(Authentication $authentication)
{
$this->beConstructedWith($authentication);
}

function it_is_initializable(Authentication $authentication)
{
$this->shouldHaveType('Http\Client\Common\Plugin\AuthenticationPlugin');
}

function it_is_a_plugin()
{
$this->shouldImplement('Http\Client\Common\Plugin');
}

function it_sends_an_authenticated_request(Authentication $authentication, RequestInterface $notAuthedRequest, RequestInterface $authedRequest, Promise $promise)
{
$authentication->authenticate($notAuthedRequest)->willReturn($authedRequest);

$next = function (RequestInterface $request) use($authedRequest, $promise) {
if (Argument::is($authedRequest->getWrappedObject())->scoreArgument($request)) {
return $promise->getWrappedObject();
}
};

$this->handleRequest($notAuthedRequest, $next, function () {})->shouldReturn($promise);
}
}
48 changes: 48 additions & 0 deletions spec/Plugin/ContentLengthPluginSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?php

namespace spec\Http\Client\Common\Plugin;

use PhpSpec\Exception\Example\SkippingException;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\StreamInterface;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class ContentLengthPluginSpec extends ObjectBehavior
{
function it_is_initializable()
{
$this->shouldHaveType('Http\Client\Common\Plugin\ContentLengthPlugin');
}

function it_is_a_plugin()
{
$this->shouldImplement('Http\Client\Common\Plugin');
}

function it_adds_content_length_header(RequestInterface $request, StreamInterface $stream)
{
$request->hasHeader('Content-Length')->shouldBeCalled()->willReturn(false);
$request->getBody()->shouldBeCalled()->willReturn($stream);
$stream->getSize()->shouldBeCalled()->willReturn(100);
$request->withHeader('Content-Length', 100)->shouldBeCalled()->willReturn($request);

$this->handleRequest($request, function () {}, function () {});
}

function it_streams_chunked_if_no_size(RequestInterface $request, StreamInterface $stream)
{
if(defined('HHVM_VERSION')) {
throw new SkippingException('Skipping test on hhvm, as there is no chunk encoding on hhvm');
}

$request->hasHeader('Content-Length')->shouldBeCalled()->willReturn(false);
$request->getBody()->shouldBeCalled()->willReturn($stream);

$stream->getSize()->shouldBeCalled()->willReturn(null);
$request->withBody(Argument::type('Http\Message\Encoding\ChunkStream'))->shouldBeCalled()->willReturn($request);
$request->withAddedHeader('Transfer-Encoding', 'chunked')->shouldBeCalled()->willReturn($request);

$this->handleRequest($request, function () {}, function () {});
}
}
183 changes: 183 additions & 0 deletions spec/Plugin/CookiePluginSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
<?php

namespace spec\Http\Client\Common\Plugin;

use Http\Promise\FulfilledPromise;
use Http\Message\Cookie;
use Http\Message\CookieJar;
use Http\Promise\Promise;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\UriInterface;
use PhpSpec\ObjectBehavior;
use Prophecy\Argument;

class CookiePluginSpec extends ObjectBehavior
{
private $cookieJar;

function let()
{
$this->cookieJar = new CookieJar();

$this->beConstructedWith($this->cookieJar);
}

function it_is_initializable()
{
$this->shouldHaveType('Http\Client\Common\Plugin\CookiePlugin');
}

function it_is_a_plugin()
{
$this->shouldImplement('Http\Client\Common\Plugin');
}

function it_loads_cookie(RequestInterface $request, UriInterface $uri, Promise $promise)
{
$cookie = new Cookie('name', 'value', 86400, 'test.com');
$this->cookieJar->addCookie($cookie);

$request->getUri()->willReturn($uri);
$uri->getHost()->willReturn('test.com');
$uri->getPath()->willReturn('/');

$request->withAddedHeader('Cookie', 'name=value')->willReturn($request);

$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
return $promise->getWrappedObject();
}
}, function () {});
}

function it_does_not_load_cookie_if_expired(RequestInterface $request, UriInterface $uri, Promise $promise)
{
$cookie = new Cookie('name', 'value', null, 'test.com', false, false, null, (new \DateTime())->modify('-1 day'));
$this->cookieJar->addCookie($cookie);

$request->withAddedHeader('Cookie', 'name=value')->shouldNotBeCalled();

$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
return $promise->getWrappedObject();
}
}, function () {});
}

function it_does_not_load_cookie_if_domain_does_not_match(RequestInterface $request, UriInterface $uri, Promise $promise)
{
$cookie = new Cookie('name', 'value', 86400, 'test2.com');
$this->cookieJar->addCookie($cookie);

$request->getUri()->willReturn($uri);
$uri->getHost()->willReturn('test.com');

$request->withAddedHeader('Cookie', 'name=value')->shouldNotBeCalled();

$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
return $promise->getWrappedObject();
}
}, function () {});
}

function it_does_not_load_cookie_if_path_does_not_match(RequestInterface $request, UriInterface $uri, Promise $promise)
{
$cookie = new Cookie('name', 'value', 86400, 'test.com', '/sub');
$this->cookieJar->addCookie($cookie);

$request->getUri()->willReturn($uri);
$uri->getHost()->willReturn('test.com');
$uri->getPath()->willReturn('/');

$request->withAddedHeader('Cookie', 'name=value')->shouldNotBeCalled();

$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
return $promise->getWrappedObject();
}
}, function () {});
}

function it_does_not_load_cookie_when_cookie_is_secure(RequestInterface $request, UriInterface $uri, Promise $promise)
{
$cookie = new Cookie('name', 'value', 86400, 'test.com', null, true);
$this->cookieJar->addCookie($cookie);

$request->getUri()->willReturn($uri);
$uri->getHost()->willReturn('test.com');
$uri->getPath()->willReturn('/');
$uri->getScheme()->willReturn('http');

$request->withAddedHeader('Cookie', 'name=value')->shouldNotBeCalled();

$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
return $promise->getWrappedObject();
}
}, function () {});
}

function it_loads_cookie_when_cookie_is_secure(RequestInterface $request, UriInterface $uri, Promise $promise)
{
$cookie = new Cookie('name', 'value', 86400, 'test.com', null, true);
$this->cookieJar->addCookie($cookie);

$request->getUri()->willReturn($uri);
$uri->getHost()->willReturn('test.com');
$uri->getPath()->willReturn('/');
$uri->getScheme()->willReturn('https');

$request->withAddedHeader('Cookie', 'name=value')->willReturn($request);

$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
return $promise->getWrappedObject();
}
}, function () {});
}

function it_saves_cookie(RequestInterface $request, ResponseInterface $response, UriInterface $uri)
{
$next = function () use ($response) {
return new FulfilledPromise($response->getWrappedObject());
};

$response->hasHeader('Set-Cookie')->willReturn(true);
$response->getHeader('Set-Cookie')->willReturn([
'cookie=value; expires=Tuesday, 31-Mar-99 07:42:12 GMT; Max-Age=60; path=/; domain=test.com; secure; HttpOnly'
]);

$request->getUri()->willReturn($uri);
$uri->getHost()->willReturn('test.com');
$uri->getPath()->willReturn('/');

$promise = $this->handleRequest($request, $next, function () {});
$promise->shouldHaveType('Http\Promise\Promise');
$promise->wait()->shouldReturnAnInstanceOf('Psr\Http\Message\ResponseInterface');
}

function it_throws_exception_on_invalid_expires_date(
RequestInterface $request,
ResponseInterface $response,
UriInterface $uri
) {
$next = function () use ($response) {
return new FulfilledPromise($response->getWrappedObject());
};

$response->hasHeader('Set-Cookie')->willReturn(true);
$response->getHeader('Set-Cookie')->willReturn([
'cookie=value; expires=i-am-an-invalid-date;'
]);

$request->getUri()->willReturn($uri);
$uri->getHost()->willReturn('test.com');
$uri->getPath()->willReturn('/');

$promise = $this->handleRequest($request, $next, function () {});
$promise->shouldReturnAnInstanceOf('Http\Promise\RejectedPromise');
$promise->shouldThrow('Http\Client\Exception\TransferException')->duringWait();
}
}

0 comments on commit 556647f

Please sign in to comment.