Skip to content

Commit 4fe307b

Browse files
committed
Move plugins from php-http/plugins to common
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
1 parent 3e25b9f commit 4fe307b

40 files changed

+2837
-1
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

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

1011
### Deprecated
1112

composer.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
"php": ">=5.4",
1515
"php-http/httplug": "^1.0",
1616
"php-http/message-factory": "^1.0",
17-
"php-http/message": "^1.2"
17+
"php-http/message": "^1.2",
18+
"symfony/options-resolver": "^2.6|^3.0"
1819
},
1920
"require-dev": {
2021
"phpspec/phpspec": "^2.4",

spec/FlexibleHttpClientSpec.php

+1
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ function it_does_not_emulate_a_client($client, RequestInterface $syncRequest, Re
7979
{
8080
$client->implement('Http\Client\HttpClient');
8181
$client->implement('Http\Client\HttpAsyncClient');
82+
8283
$client->sendRequest($syncRequest)->shouldBeCalled();
8384
$client->sendRequest($asyncRequest)->shouldNotBeCalled();
8485
$client->sendAsyncRequest($asyncRequest)->shouldBeCalled();

spec/Plugin/AddHostPluginSpec.php

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
<?php
2+
3+
namespace spec\Http\Client\Common\Plugin;
4+
5+
use Http\Message\StreamFactory;
6+
use Http\Message\UriFactory;
7+
use Http\Promise\FulfilledPromise;
8+
use Psr\Http\Message\RequestInterface;
9+
use Psr\Http\Message\ResponseInterface;
10+
use Psr\Http\Message\StreamInterface;
11+
use Psr\Http\Message\UriInterface;
12+
use PhpSpec\ObjectBehavior;
13+
14+
class AddHostPluginSpec extends ObjectBehavior
15+
{
16+
function let(UriInterface $uri)
17+
{
18+
$this->beConstructedWith($uri);
19+
}
20+
21+
function it_is_initializable(UriInterface $uri)
22+
{
23+
$uri->getHost()->shouldBeCalled()->willReturn('example.com');
24+
25+
$this->shouldHaveType('Http\Client\Common\Plugin\AddHostPlugin');
26+
}
27+
28+
function it_is_a_plugin(UriInterface $uri)
29+
{
30+
$uri->getHost()->shouldBeCalled()->willReturn('example.com');
31+
32+
$this->shouldImplement('Http\Client\Common\Plugin');
33+
}
34+
35+
function it_adds_domain(
36+
RequestInterface $request,
37+
UriInterface $host,
38+
UriInterface $uri
39+
) {
40+
$host->getScheme()->shouldBeCalled()->willReturn('http://');
41+
$host->getHost()->shouldBeCalled()->willReturn('example.com');
42+
43+
$request->getUri()->shouldBeCalled()->willReturn($uri);
44+
$request->withUri($uri)->shouldBeCalled()->willReturn($request);
45+
46+
$uri->withScheme('http://')->shouldBeCalled()->willReturn($uri);
47+
$uri->withHost('example.com')->shouldBeCalled()->willReturn($uri);
48+
$uri->getHost()->shouldBeCalled()->willReturn('');
49+
50+
$this->beConstructedWith($host);
51+
$this->handleRequest($request, function () {}, function () {});
52+
}
53+
54+
function it_replaces_domain(
55+
RequestInterface $request,
56+
UriInterface $host,
57+
UriInterface $uri
58+
) {
59+
$host->getScheme()->shouldBeCalled()->willReturn('http://');
60+
$host->getHost()->shouldBeCalled()->willReturn('example.com');
61+
62+
$request->getUri()->shouldBeCalled()->willReturn($uri);
63+
$request->withUri($uri)->shouldBeCalled()->willReturn($request);
64+
65+
$uri->withScheme('http://')->shouldBeCalled()->willReturn($uri);
66+
$uri->withHost('example.com')->shouldBeCalled()->willReturn($uri);
67+
68+
69+
$this->beConstructedWith($host, ['replace' => true]);
70+
$this->handleRequest($request, function () {}, function () {});
71+
}
72+
73+
function it_does_nothing_when_domain_exists(
74+
RequestInterface $request,
75+
UriInterface $host,
76+
UriInterface $uri
77+
) {
78+
$request->getUri()->shouldBeCalled()->willReturn($uri);
79+
$uri->getHost()->shouldBeCalled()->willReturn('default.com');
80+
81+
$this->beConstructedWith($host);
82+
$this->handleRequest($request, function () {}, function () {});
83+
}
84+
}
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<?php
2+
3+
namespace spec\Http\Client\Common\Plugin;
4+
5+
use Http\Message\Authentication;
6+
use Http\Promise\Promise;
7+
use Psr\Http\Message\RequestInterface;
8+
use PhpSpec\ObjectBehavior;
9+
use Prophecy\Argument;
10+
11+
class AuthenticationPluginSpec extends ObjectBehavior
12+
{
13+
function let(Authentication $authentication)
14+
{
15+
$this->beConstructedWith($authentication);
16+
}
17+
18+
function it_is_initializable(Authentication $authentication)
19+
{
20+
$this->shouldHaveType('Http\Client\Common\Plugin\AuthenticationPlugin');
21+
}
22+
23+
function it_is_a_plugin()
24+
{
25+
$this->shouldImplement('Http\Client\Common\Plugin');
26+
}
27+
28+
function it_sends_an_authenticated_request(Authentication $authentication, RequestInterface $notAuthedRequest, RequestInterface $authedRequest, Promise $promise)
29+
{
30+
$authentication->authenticate($notAuthedRequest)->willReturn($authedRequest);
31+
32+
$next = function (RequestInterface $request) use($authedRequest, $promise) {
33+
if (Argument::is($authedRequest->getWrappedObject())->scoreArgument($request)) {
34+
return $promise->getWrappedObject();
35+
}
36+
};
37+
38+
$this->handleRequest($notAuthedRequest, $next, function () {})->shouldReturn($promise);
39+
}
40+
}
+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?php
2+
3+
namespace spec\Http\Client\Common\Plugin;
4+
5+
use PhpSpec\Exception\Example\SkippingException;
6+
use Psr\Http\Message\RequestInterface;
7+
use Psr\Http\Message\StreamInterface;
8+
use PhpSpec\ObjectBehavior;
9+
use Prophecy\Argument;
10+
11+
class ContentLengthPluginSpec extends ObjectBehavior
12+
{
13+
function it_is_initializable()
14+
{
15+
$this->shouldHaveType('Http\Client\Common\Plugin\ContentLengthPlugin');
16+
}
17+
18+
function it_is_a_plugin()
19+
{
20+
$this->shouldImplement('Http\Client\Common\Plugin');
21+
}
22+
23+
function it_adds_content_length_header(RequestInterface $request, StreamInterface $stream)
24+
{
25+
$request->hasHeader('Content-Length')->shouldBeCalled()->willReturn(false);
26+
$request->getBody()->shouldBeCalled()->willReturn($stream);
27+
$stream->getSize()->shouldBeCalled()->willReturn(100);
28+
$request->withHeader('Content-Length', 100)->shouldBeCalled()->willReturn($request);
29+
30+
$this->handleRequest($request, function () {}, function () {});
31+
}
32+
33+
function it_streams_chunked_if_no_size(RequestInterface $request, StreamInterface $stream)
34+
{
35+
if(defined('HHVM_VERSION')) {
36+
throw new SkippingException('Skipping test on hhvm, as there is no chunk encoding on hhvm');
37+
}
38+
39+
$request->hasHeader('Content-Length')->shouldBeCalled()->willReturn(false);
40+
$request->getBody()->shouldBeCalled()->willReturn($stream);
41+
42+
$stream->getSize()->shouldBeCalled()->willReturn(null);
43+
$request->withBody(Argument::type('Http\Message\Encoding\ChunkStream'))->shouldBeCalled()->willReturn($request);
44+
$request->withAddedHeader('Transfer-Encoding', 'chunked')->shouldBeCalled()->willReturn($request);
45+
46+
$this->handleRequest($request, function () {}, function () {});
47+
}
48+
}

spec/Plugin/CookiePluginSpec.php

+183
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<?php
2+
3+
namespace spec\Http\Client\Common\Plugin;
4+
5+
use Http\Promise\FulfilledPromise;
6+
use Http\Message\Cookie;
7+
use Http\Message\CookieJar;
8+
use Http\Promise\Promise;
9+
use Psr\Http\Message\RequestInterface;
10+
use Psr\Http\Message\ResponseInterface;
11+
use Psr\Http\Message\UriInterface;
12+
use PhpSpec\ObjectBehavior;
13+
use Prophecy\Argument;
14+
15+
class CookiePluginSpec extends ObjectBehavior
16+
{
17+
private $cookieJar;
18+
19+
function let()
20+
{
21+
$this->cookieJar = new CookieJar();
22+
23+
$this->beConstructedWith($this->cookieJar);
24+
}
25+
26+
function it_is_initializable()
27+
{
28+
$this->shouldHaveType('Http\Client\Common\Plugin\CookiePlugin');
29+
}
30+
31+
function it_is_a_plugin()
32+
{
33+
$this->shouldImplement('Http\Client\Common\Plugin');
34+
}
35+
36+
function it_loads_cookie(RequestInterface $request, UriInterface $uri, Promise $promise)
37+
{
38+
$cookie = new Cookie('name', 'value', 86400, 'test.com');
39+
$this->cookieJar->addCookie($cookie);
40+
41+
$request->getUri()->willReturn($uri);
42+
$uri->getHost()->willReturn('test.com');
43+
$uri->getPath()->willReturn('/');
44+
45+
$request->withAddedHeader('Cookie', 'name=value')->willReturn($request);
46+
47+
$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
48+
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
49+
return $promise->getWrappedObject();
50+
}
51+
}, function () {});
52+
}
53+
54+
function it_does_not_load_cookie_if_expired(RequestInterface $request, UriInterface $uri, Promise $promise)
55+
{
56+
$cookie = new Cookie('name', 'value', null, 'test.com', false, false, null, (new \DateTime())->modify('-1 day'));
57+
$this->cookieJar->addCookie($cookie);
58+
59+
$request->withAddedHeader('Cookie', 'name=value')->shouldNotBeCalled();
60+
61+
$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
62+
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
63+
return $promise->getWrappedObject();
64+
}
65+
}, function () {});
66+
}
67+
68+
function it_does_not_load_cookie_if_domain_does_not_match(RequestInterface $request, UriInterface $uri, Promise $promise)
69+
{
70+
$cookie = new Cookie('name', 'value', 86400, 'test2.com');
71+
$this->cookieJar->addCookie($cookie);
72+
73+
$request->getUri()->willReturn($uri);
74+
$uri->getHost()->willReturn('test.com');
75+
76+
$request->withAddedHeader('Cookie', 'name=value')->shouldNotBeCalled();
77+
78+
$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
79+
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
80+
return $promise->getWrappedObject();
81+
}
82+
}, function () {});
83+
}
84+
85+
function it_does_not_load_cookie_if_path_does_not_match(RequestInterface $request, UriInterface $uri, Promise $promise)
86+
{
87+
$cookie = new Cookie('name', 'value', 86400, 'test.com', '/sub');
88+
$this->cookieJar->addCookie($cookie);
89+
90+
$request->getUri()->willReturn($uri);
91+
$uri->getHost()->willReturn('test.com');
92+
$uri->getPath()->willReturn('/');
93+
94+
$request->withAddedHeader('Cookie', 'name=value')->shouldNotBeCalled();
95+
96+
$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
97+
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
98+
return $promise->getWrappedObject();
99+
}
100+
}, function () {});
101+
}
102+
103+
function it_does_not_load_cookie_when_cookie_is_secure(RequestInterface $request, UriInterface $uri, Promise $promise)
104+
{
105+
$cookie = new Cookie('name', 'value', 86400, 'test.com', null, true);
106+
$this->cookieJar->addCookie($cookie);
107+
108+
$request->getUri()->willReturn($uri);
109+
$uri->getHost()->willReturn('test.com');
110+
$uri->getPath()->willReturn('/');
111+
$uri->getScheme()->willReturn('http');
112+
113+
$request->withAddedHeader('Cookie', 'name=value')->shouldNotBeCalled();
114+
115+
$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
116+
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
117+
return $promise->getWrappedObject();
118+
}
119+
}, function () {});
120+
}
121+
122+
function it_loads_cookie_when_cookie_is_secure(RequestInterface $request, UriInterface $uri, Promise $promise)
123+
{
124+
$cookie = new Cookie('name', 'value', 86400, 'test.com', null, true);
125+
$this->cookieJar->addCookie($cookie);
126+
127+
$request->getUri()->willReturn($uri);
128+
$uri->getHost()->willReturn('test.com');
129+
$uri->getPath()->willReturn('/');
130+
$uri->getScheme()->willReturn('https');
131+
132+
$request->withAddedHeader('Cookie', 'name=value')->willReturn($request);
133+
134+
$this->handleRequest($request, function (RequestInterface $requestReceived) use ($request, $promise) {
135+
if (Argument::is($requestReceived)->scoreArgument($request->getWrappedObject())) {
136+
return $promise->getWrappedObject();
137+
}
138+
}, function () {});
139+
}
140+
141+
function it_saves_cookie(RequestInterface $request, ResponseInterface $response, UriInterface $uri)
142+
{
143+
$next = function () use ($response) {
144+
return new FulfilledPromise($response->getWrappedObject());
145+
};
146+
147+
$response->hasHeader('Set-Cookie')->willReturn(true);
148+
$response->getHeader('Set-Cookie')->willReturn([
149+
'cookie=value; expires=Tuesday, 31-Mar-99 07:42:12 GMT; Max-Age=60; path=/; domain=test.com; secure; HttpOnly'
150+
]);
151+
152+
$request->getUri()->willReturn($uri);
153+
$uri->getHost()->willReturn('test.com');
154+
$uri->getPath()->willReturn('/');
155+
156+
$promise = $this->handleRequest($request, $next, function () {});
157+
$promise->shouldHaveType('Http\Promise\Promise');
158+
$promise->wait()->shouldReturnAnInstanceOf('Psr\Http\Message\ResponseInterface');
159+
}
160+
161+
function it_throws_exception_on_invalid_expires_date(
162+
RequestInterface $request,
163+
ResponseInterface $response,
164+
UriInterface $uri
165+
) {
166+
$next = function () use ($response) {
167+
return new FulfilledPromise($response->getWrappedObject());
168+
};
169+
170+
$response->hasHeader('Set-Cookie')->willReturn(true);
171+
$response->getHeader('Set-Cookie')->willReturn([
172+
'cookie=value; expires=i-am-an-invalid-date;'
173+
]);
174+
175+
$request->getUri()->willReturn($uri);
176+
$uri->getHost()->willReturn('test.com');
177+
$uri->getPath()->willReturn('/');
178+
179+
$promise = $this->handleRequest($request, $next, function () {});
180+
$promise->shouldReturnAnInstanceOf('Http\Promise\RejectedPromise');
181+
$promise->shouldThrow('Http\Client\Exception\TransferException')->duringWait();
182+
}
183+
}

0 commit comments

Comments
 (0)