Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7432f9b

Browse filesBrowse files
author
Anthony MARTIN
committed
[HttpClient] Add a ConditionalHttpClient
1 parent 2278d4c commit 7432f9b
Copy full SHA for 7432f9b

File tree

3 files changed

+174
-0
lines changed
Filter options

3 files changed

+174
-0
lines changed
+74Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpClient;
13+
14+
use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
15+
use Symfony\Contracts\HttpClient\HttpClientInterface;
16+
use Symfony\Contracts\HttpClient\ResponseInterface;
17+
use Symfony\Contracts\HttpClient\ResponseStreamInterface;
18+
19+
/**
20+
* Auto-configure the default options based on the requested absolute URL.
21+
*
22+
* @author Anthony Martin <anthony.martin@sensiolabs.com>
23+
*
24+
* @experimental in 4.3
25+
*/
26+
class ConditionalHttpClient implements HttpClientInterface
27+
{
28+
use HttpClientTrait;
29+
30+
private $client;
31+
private $options;
32+
33+
/**
34+
* @param array[] $options the default options to use when the regexp provided as key matches the requested URL
35+
*/
36+
public function __construct(HttpClientInterface $client, array $options)
37+
{
38+
$this->client = $client;
39+
$this->options = $options;
40+
}
41+
42+
/**
43+
* {@inheritdoc}
44+
*/
45+
public function request(string $method, string $url, array $options = []): ResponseInterface
46+
{
47+
// Normalize $url
48+
$url = self::parseUrl($url);
49+
50+
if (null === $url['scheme'] || null === $url['authority']) {
51+
throw new InvalidArgumentException(sprintf('Unsupported URL: host or scheme is missing in "%s".', implode('', $url)));
52+
}
53+
54+
$url = implode('', $url);
55+
56+
foreach ($this->options as $regexp => $defaultOptions) {
57+
if (preg_match(sprintf('{%s}A', $regexp), $url)) {
58+
$options = self::mergeDefaultOptions($options, $defaultOptions, true);
59+
60+
return $this->client->request($method, $url, $options);
61+
}
62+
}
63+
64+
return $this->client->request($method, $url, $options);
65+
}
66+
67+
/**
68+
* {@inheritdoc}
69+
*/
70+
public function stream($responses, float $timeout = null): ResponseStreamInterface
71+
{
72+
return $this->client->stream($responses, $timeout);
73+
}
74+
}

‎src/Symfony/Component/HttpClient/Response/MockResponse.php

Copy file name to clipboardExpand all lines: src/Symfony/Component/HttpClient/Response/MockResponse.php
+8Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ class MockResponse implements ResponseInterface
2727
use ResponseTrait;
2828

2929
private $body;
30+
private $requestOptions;
3031

3132
private static $mainMulti;
3233
private static $idSequence = 0;
@@ -60,6 +61,11 @@ protected function close(): void
6061
$this->body = [];
6162
}
6263

64+
public function getRequestedOptions(): array
65+
{
66+
return $this->requestOptions;
67+
}
68+
6369
/**
6470
* @internal
6571
*/
@@ -87,6 +93,8 @@ public static function fromRequest(string $method, string $url, array $options,
8793
$response->info['user_data'] = $options['user_data'] ?? null;
8894
$response->info['url'] = $url;
8995

96+
$response->requestOptions = $options;
97+
9098
self::writeRequest($response, $options, $mock);
9199
$response->body[] = [$options, $mock];
92100

+92Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\HttpClient\Tests;
13+
14+
use PHPUnit\Framework\TestCase;
15+
use Symfony\Component\HttpClient\ConditionalHttpClient;
16+
use Symfony\Component\HttpClient\Exception\InvalidArgumentException;
17+
use Symfony\Component\HttpClient\MockHttpClient;
18+
use Symfony\Component\HttpClient\Response\MockResponse;
19+
20+
class ConditionalHttpClientTest extends TestCase
21+
{
22+
public function testInvalidUrl()
23+
{
24+
$mockClient = new MockHttpClient([]);
25+
$client = new ConditionalHttpClient($mockClient, []);
26+
27+
$this->expectException(InvalidArgumentException::class);
28+
$client->request('GET', '/foo');
29+
}
30+
31+
/**
32+
* @dataProvider provideMatchingUrls
33+
*/
34+
public function testMatchingUrls(string $regexp, string $url, array $options)
35+
{
36+
$mockClient = new MockHttpClient(new MockResponse());
37+
$client = new ConditionalHttpClient($mockClient, $options);
38+
39+
$response = $client->request('GET', $url);
40+
$reuestedOptions = $response->getRequestedOptions();
41+
42+
$this->assertEquals([], array_diff_assoc($reuestedOptions['options'], $options[$regexp]['options']));
43+
}
44+
45+
public function provideMatchingUrls()
46+
{
47+
$defaultOptions = [
48+
'.*/foo-bar' => [
49+
'options' => ['case' => 1],
50+
],
51+
'.*' => [
52+
'options' => ['case' => 2],
53+
],
54+
];
55+
56+
yield ['regexp' => '.*/foo-bar', 'url' => 'http://example.com/foo-bar', 'default_options' => $defaultOptions];
57+
yield ['regexp' => '.*', 'url' => 'http://example.com/bar-foo', 'default_options' => $defaultOptions];
58+
yield ['regexp' => '.*', 'url' => 'http://example.com/foobar', 'default_options' => $defaultOptions];
59+
}
60+
61+
public function testMatchingUrlsAndOptions()
62+
{
63+
$defaultOptions = [
64+
'.*/foo-bar' => ['headers' => ['x-app' => 'unit-test-foo-bar']],
65+
'.*' => ['headers' => ['content-type' => 'text/html']],
66+
];
67+
68+
$mockResponses = [
69+
new MockResponse(),
70+
new MockResponse(),
71+
new MockResponse(),
72+
];
73+
74+
$mockClient = new MockHttpClient($mockResponses);
75+
$client = new ConditionalHttpClient($mockClient, $defaultOptions);
76+
77+
$response = $client->request('GET', 'http://example.com/foo-bar', ['json' => ['url' => 'http://example.com']]);
78+
$requestOptions = $response->getRequestedOptions();
79+
$this->assertEquals($requestOptions['json']['url'], 'http://example.com');
80+
$this->assertEquals($requestOptions['headers']['x-app'][0], $defaultOptions['.*/foo-bar']['headers']['x-app']);
81+
82+
$response = $client->request('GET', 'http://example.com/bar-foo', ['headers' => ['x-app' => 'unit-test']]);
83+
$requestOptions = $response->getRequestedOptions();
84+
$this->assertEquals($requestOptions['headers']['x-app'][0], 'unit-test');
85+
$this->assertEquals($requestOptions['headers']['content-type'][0], 'text/html');
86+
87+
$response = $client->request('GET', 'http://example.com/foobar-foo', ['headers' => ['x-app' => 'unit-test']]);
88+
$requestOptions = $response->getRequestedOptions();
89+
$this->assertEquals($requestOptions['headers']['x-app'][0], 'unit-test');
90+
$this->assertEquals($requestOptions['headers']['content-type'][0], 'text/html');
91+
}
92+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.