Skip to content

Commit

Permalink
Move plugins from php-http/plugins to common
Browse files Browse the repository at this point in the history
Update todo

Remove options resolver dependency

Revert: Remove options resolver dependency

Add AddHostPlugin

Add Authentication plugins

Add content length plugin

Add final warning to plugins

Add cookie plugin

Add decoder plugin

Add error plugin

Add header plugins

Add history plugin

Fix namespace import order

Add redirect plugin

Add retry plugin

Make plugin classes final, related #18

Fix throw keyword

Manually apply php-http/plugins#68

Manually apply and close php-http/plugins#65
  • Loading branch information
sagikazarmark committed May 1, 2016
1 parent 8fc9328 commit ca9ca1c
Show file tree
Hide file tree
Showing 40 changed files with 2,840 additions and 2 deletions.
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 implementations moved here from `php-http/plugins`


## 1.0.0 - 2016-01-27
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
5 changes: 4 additions & 1 deletion spec/FlexibleHttpClientSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,11 @@ function it_emulates_a_client(
$this->sendRequest($syncRequest)->shouldReturn($syncResponse);
}

function it_does_not_emulate_a_client(FlexibleHttpClient $client, RequestInterface $syncRequest, RequestInterface $asyncRequest)
function it_does_not_emulate_a_client($client, RequestInterface $syncRequest, RequestInterface $asyncRequest)
{
$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 ca9ca1c

Please sign in to comment.