diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2c7e1b82..a666ee45 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,8 @@
### Added
+- You can now also configure client specific plugins in the `plugins` option of a client.
+ Some plugins that you previously had to define in your own services can now be configured on the client.
- Support for BatchClient
- The stopwatch plugin in included by default when using profiling.
diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php
index 8bea9e7c..2eed8a3b 100644
--- a/DependencyInjection/Configuration.php
+++ b/DependencyInjection/Configuration.php
@@ -2,8 +2,8 @@
namespace Http\HttplugBundle\DependencyInjection;
-use Symfony\Component\Config\Definition\ArrayNode;
use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition;
+use Symfony\Component\Config\Definition\Builder\NodeDefinition;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
use Symfony\Component\Config\Definition\Exception\InvalidConfigurationException;
@@ -45,7 +45,7 @@ public function getConfigTreeBuilder()
$rootNode = $treeBuilder->root('httplug');
$this->configureClients($rootNode);
- $this->configurePlugins($rootNode);
+ $this->configureSharedPlugins($rootNode);
$rootNode
->validate()
@@ -167,6 +167,7 @@ private function configureClients(ArrayNodeDefinition $root)
->thenInvalid('A http client can\'t be decorated with both FlexibleHttpClient and HttpMethodsClient. Only one of the following options can be true. ("flexible_client", "http_methods_client")')->end()
->useAttributeAsKey('name')
->prototype('array')
+ ->fixXmlConfig('plugin')
->children()
->scalarNode('factory')
->isRequired()
@@ -185,136 +186,289 @@ private function configureClients(ArrayNodeDefinition $root)
->defaultFalse()
->info('Set to true to get the client wrapped in a BatchClient which allows you to send multiple request at the same time.')
->end()
- ->arrayNode('plugins')
- ->info('A list of service ids of plugins. The order is important.')
- ->prototype('scalar')->end()
- ->end()
->variableNode('config')->defaultValue([])->end()
+ ->append($this->createClientPluginNode())
->end()
- ->end();
+ ->end()
+ ->end();
}
/**
* @param ArrayNodeDefinition $root
*/
- private function configurePlugins(ArrayNodeDefinition $root)
+ private function configureSharedPlugins(ArrayNodeDefinition $root)
{
- $root->children()
- ->arrayNode('plugins')
+ $pluginsNode = $root
+ ->children()
+ ->arrayNode('plugins')
+ ->info('Global plugin configuration. Plugins need to be explicitly added to clients.')
->addDefaultsIfNotSet()
- ->children()
- ->append($this->addAuthenticationPluiginNode())
+ // don't call end to get the plugins node
+ ;
+ $this->addSharedPluginNodes($pluginsNode);
+ }
- ->arrayNode('cache')
+ /**
+ * Createplugins node of a client.
+ *
+ * @return ArrayNodeDefinition The plugin node
+ */
+ private function createClientPluginNode()
+ {
+ $builder = new TreeBuilder();
+ $node = $builder->root('plugins');
+
+ /** @var ArrayNodeDefinition $pluginList */
+ $pluginList = $node
+ ->info('A list of plugin service ids and client specific plugin definitions. The order is important.')
+ ->prototype('array')
+ ;
+ $pluginList
+ // support having just a service id in the list
+ ->beforeNormalization()
+ ->always(function ($plugin) {
+ if (is_string($plugin)) {
+ return [
+ 'reference' => [
+ 'enabled' => true,
+ 'id' => $plugin,
+ ],
+ ];
+ }
+
+ return $plugin;
+ })
+ ->end()
+
+ ->validate()
+ ->always(function ($plugins) {
+ foreach ($plugins as $name => $definition) {
+ if ('authentication' === $name) {
+ if (!count($definition)) {
+ unset($plugins['authentication']);
+ }
+ } elseif (!$definition['enabled']) {
+ unset($plugins[$name]);
+ }
+ }
+
+ return $plugins;
+ })
+ ->end()
+ ;
+ $this->addSharedPluginNodes($pluginList, true);
+
+ $pluginList
+ ->children()
+ ->arrayNode('reference')
+ ->canBeEnabled()
+ ->info('Reference to a plugin service')
+ ->children()
+ ->scalarNode('id')
+ ->info('Service id of a plugin')
+ ->isRequired()
+ ->cannotBeEmpty()
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('add_host')
->canBeEnabled()
->addDefaultsIfNotSet()
- ->children()
- ->scalarNode('cache_pool')
- ->info('This must be a service id to a service implementing Psr\Cache\CacheItemPoolInterface')
- ->isRequired()
- ->cannotBeEmpty()
- ->end()
- ->scalarNode('stream_factory')
- ->info('This must be a service id to a service implementing Http\Message\StreamFactory')
- ->defaultValue('httplug.stream_factory')
- ->cannotBeEmpty()
- ->end()
- ->arrayNode('config')
- ->addDefaultsIfNotSet()
- ->children()
- ->scalarNode('default_ttl')->defaultNull()->end()
- ->scalarNode('respect_cache_headers')->defaultTrue()->end()
- ->end()
- ->end()
+ ->info('Set scheme, host and port in the request URI.')
+ ->children()
+ ->scalarNode('host')
+ ->info('Host name including protocol and optionally the port number, e.g. https://api.local:8000')
+ ->isRequired()
+ ->cannotBeEmpty()
->end()
- ->end() // End cache plugin
-
- ->arrayNode('cookie')
+ ->scalarNode('replace')
+ ->info('Whether to replace the host if request already specifies one')
+ ->defaultValue(false)
+ ->end()
+ ->end()
+ ->end()
+ ->arrayNode('header_append')
->canBeEnabled()
- ->children()
- ->scalarNode('cookie_jar')
- ->info('This must be a service id to a service implementing Http\Message\CookieJar')
- ->isRequired()
- ->cannotBeEmpty()
- ->end()
+ ->info('Append headers to the request. If the header already exists the value will be appended to the current value.')
+ ->fixXmlConfig('header')
+ ->children()
+ ->arrayNode('headers')
+ ->info('Keys are the header names, values the header values')
+ ->normalizeKeys(false)
+ ->useAttributeAsKey('name')
+ ->prototype('scalar')->end()
->end()
- ->end() // End cookie plugin
-
- ->arrayNode('decoder')
- ->canBeDisabled()
- ->addDefaultsIfNotSet()
- ->children()
- ->scalarNode('use_content_encoding')->defaultTrue()->end()
+ ->end()
+ ->end()
+ ->arrayNode('header_defaults')
+ ->canBeEnabled()
+ ->info('Set header to default value if it does not exist.')
+ ->fixXmlConfig('header')
+ ->children()
+ ->arrayNode('headers')
+ ->info('Keys are the header names, values the header values')
+ ->normalizeKeys(false)
+ ->useAttributeAsKey('name')
+ ->prototype('scalar')->end()
->end()
- ->end() // End decoder plugin
-
- ->arrayNode('history')
+ ->end()
+ ->end()
+ ->arrayNode('header_set')
->canBeEnabled()
- ->children()
- ->scalarNode('journal')
- ->info('This must be a service id to a service implementing Http\Client\Plugin\Journal')
- ->isRequired()
- ->cannotBeEmpty()
- ->end()
+ ->info('Set headers to requests. If the header does not exist it wil be set, if the header already exists it will be replaced.')
+ ->fixXmlConfig('header')
+ ->children()
+ ->arrayNode('headers')
+ ->info('Keys are the header names, values the header values')
+ ->normalizeKeys(false)
+ ->useAttributeAsKey('name')
+ ->prototype('scalar')->end()
->end()
- ->end() // End history plugin
-
- ->arrayNode('logger')
- ->canBeDisabled()
- ->addDefaultsIfNotSet()
- ->children()
- ->scalarNode('logger')
- ->info('This must be a service id to a service implementing Psr\Log\LoggerInterface')
- ->defaultValue('logger')
- ->cannotBeEmpty()
- ->end()
- ->scalarNode('formatter')
- ->info('This must be a service id to a service implementing Http\Message\Formatter')
- ->defaultNull()
- ->end()
+ ->end()
+ ->end()
+ ->arrayNode('header_remove')
+ ->canBeEnabled()
+ ->info('Remove headers from requests.')
+ ->fixXmlConfig('header')
+ ->children()
+ ->arrayNode('headers')
+ ->info('List of header names to remove')
+ ->prototype('scalar')->end()
->end()
- ->end() // End logger plugin
+ ->end()
+ ->end()
+ ->end()
+ ->end();
- ->arrayNode('redirect')
- ->canBeDisabled()
- ->addDefaultsIfNotSet()
- ->children()
- ->scalarNode('preserve_header')->defaultTrue()->end()
- ->scalarNode('use_default_for_multiple')->defaultTrue()->end()
- ->end()
- ->end() // End redirect plugin
+ return $node;
+ }
- ->arrayNode('retry')
- ->canBeDisabled()
- ->addDefaultsIfNotSet()
- ->children()
- ->scalarNode('retry')->defaultValue(1)->end()
- ->end()
- ->end() // End retry plugin
+ /**
+ * Add the definitions for shared plugin configurations.
+ *
+ * @param ArrayNodeDefinition $pluginNode The node to add to.
+ * @param bool $disableAll Some shared plugins are enabled by default. On the client, all are disabled by default.
+ */
+ private function addSharedPluginNodes(ArrayNodeDefinition $pluginNode, $disableAll = false)
+ {
+ $children = $pluginNode->children();
- ->arrayNode('stopwatch')
- ->canBeDisabled()
- ->addDefaultsIfNotSet()
+ $children->append($this->createAuthenticationPluginNode());
+
+ $children->arrayNode('cache')
+ ->canBeEnabled()
+ ->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('cache_pool')
+ ->info('This must be a service id to a service implementing Psr\Cache\CacheItemPoolInterface')
+ ->isRequired()
+ ->cannotBeEmpty()
+ ->end()
+ ->scalarNode('stream_factory')
+ ->info('This must be a service id to a service implementing Http\Message\StreamFactory')
+ ->defaultValue('httplug.stream_factory')
+ ->cannotBeEmpty()
+ ->end()
+ ->arrayNode('config')
+ ->addDefaultsIfNotSet()
->children()
- ->scalarNode('stopwatch')
- ->info('This must be a service id to a service extending Symfony\Component\Stopwatch\Stopwatch')
- ->defaultValue('debug.stopwatch')
- ->cannotBeEmpty()
- ->end()
+ ->scalarNode('default_ttl')->defaultNull()->end()
+ ->scalarNode('respect_cache_headers')->defaultTrue()->end()
->end()
- ->end() // End stopwatch plugin
+ ->end()
+ ->end()
+ ->end();
+ // End cache plugin
+
+ $children->arrayNode('cookie')
+ ->canBeEnabled()
+ ->children()
+ ->scalarNode('cookie_jar')
+ ->info('This must be a service id to a service implementing Http\Message\CookieJar')
+ ->isRequired()
+ ->cannotBeEmpty()
+ ->end()
+ ->end()
+ ->end();
+ // End cookie plugin
+
+ $decoder = $children->arrayNode('decoder');
+ $disableAll ? $decoder->canBeEnabled() : $decoder->canBeDisabled();
+ $decoder->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('use_content_encoding')->defaultTrue()->end()
+ ->end()
+ ->end();
+ // End decoder plugin
+
+ $children->arrayNode('history')
+ ->canBeEnabled()
+ ->children()
+ ->scalarNode('journal')
+ ->info('This must be a service id to a service implementing Http\Client\Plugin\Journal')
+ ->isRequired()
+ ->cannotBeEmpty()
+ ->end()
+ ->end()
+ ->end();
+ // End history plugin
+ $logger = $children->arrayNode('logger');
+ $disableAll ? $logger->canBeEnabled() : $logger->canBeDisabled();
+ $logger->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('logger')
+ ->info('This must be a service id to a service implementing Psr\Log\LoggerInterface')
+ ->defaultValue('logger')
+ ->cannotBeEmpty()
+ ->end()
+ ->scalarNode('formatter')
+ ->info('This must be a service id to a service implementing Http\Message\Formatter')
+ ->defaultNull()
+ ->end()
+ ->end()
+ ->end();
+ // End logger plugin
+
+ $redirect = $children->arrayNode('redirect');
+ $disableAll ? $redirect->canBeEnabled() : $redirect->canBeDisabled();
+ $redirect->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('preserve_header')->defaultTrue()->end()
+ ->scalarNode('use_default_for_multiple')->defaultTrue()->end()
+ ->end()
+ ->end();
+ // End redirect plugin
+
+ $retry = $children->arrayNode('retry');
+ $disableAll ? $retry->canBeEnabled() : $retry->canBeDisabled();
+ $retry->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('retry')->defaultValue(1)->end() // TODO: should be called retries for consistency with the class
+ ->end()
+ ->end();
+ // End retry plugin
+
+ $stopwatch = $children->arrayNode('stopwatch');
+ $disableAll ? $stopwatch->canBeEnabled() : $stopwatch->canBeDisabled();
+ $stopwatch->addDefaultsIfNotSet()
+ ->children()
+ ->scalarNode('stopwatch')
+ ->info('This must be a service id to a service extending Symfony\Component\Stopwatch\Stopwatch')
+ ->defaultValue('debug.stopwatch')
+ ->cannotBeEmpty()
->end()
->end()
->end();
+ // End stopwatch plugin
}
/**
- * Add configuration for authentication plugin.
+ * Create configuration for authentication plugin.
*
- * @return ArrayNodeDefinition|\Symfony\Component\Config\Definition\Builder\NodeDefinition
+ * @return NodeDefinition Definition for the authentication node in the plugins list.
*/
- private function addAuthenticationPluiginNode()
+ private function createAuthenticationPluginNode()
{
$builder = new TreeBuilder();
$node = $builder->root('authentication');
diff --git a/DependencyInjection/HttplugExtension.php b/DependencyInjection/HttplugExtension.php
index 9ec64a3d..e5035c3b 100644
--- a/DependencyInjection/HttplugExtension.php
+++ b/DependencyInjection/HttplugExtension.php
@@ -14,8 +14,10 @@
use Http\Message\Authentication\BasicAuth;
use Http\Message\Authentication\Bearer;
use Http\Message\Authentication\Wsse;
+use Psr\Http\Message\UriInterface;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;
@@ -53,7 +55,7 @@ public function load(array $configs, ContainerBuilder $container)
}
// Configure toolbar
- if ($config['profiling']['enabled']) {
+ if ($this->isConfigEnabled($container, $config['profiling'])) {
$loader->load('data-collector.xml');
if (!empty($config['profiling']['formatter'])) {
@@ -70,8 +72,8 @@ public function load(array $configs, ContainerBuilder $container)
;
}
- $this->configurePlugins($container, $config['plugins']);
$this->configureClients($container, $config);
+ $this->configureSharedPlugins($container, $config['plugins']); // must be after clients, as clients.X.plugins might use plugins as templates that will be removed
$this->configureAutoDiscoveryClients($container, $config);
}
@@ -91,7 +93,7 @@ private function configureClients(ContainerBuilder $container, array $config)
$first = $name;
}
- $this->configureClient($container, $name, $arguments, $config['profiling']['enabled']);
+ $this->configureClient($container, $name, $arguments, $this->isConfigEnabled($container, $config['profiling']));
}
// If we have clients configured
@@ -108,7 +110,7 @@ private function configureClients(ContainerBuilder $container, array $config)
* @param ContainerBuilder $container
* @param array $config
*/
- private function configurePlugins(ContainerBuilder $container, array $config)
+ private function configureSharedPlugins(ContainerBuilder $container, array $config)
{
if (!empty($config['authentication'])) {
$this->configureAuthentication($container, $config['authentication']);
@@ -118,9 +120,9 @@ private function configurePlugins(ContainerBuilder $container, array $config)
foreach ($config as $name => $pluginConfig) {
$pluginId = 'httplug.plugin.'.$name;
- if ($pluginConfig['enabled']) {
+ if ($this->isConfigEnabled($container, $pluginConfig)) {
$def = $container->getDefinition($pluginId);
- $this->configurePluginByName($name, $def, $pluginConfig);
+ $this->configurePluginByName($name, $def, $pluginConfig, $container, $pluginId);
} else {
$container->removeDefinition($pluginId);
}
@@ -128,11 +130,13 @@ private function configurePlugins(ContainerBuilder $container, array $config)
}
/**
- * @param string $name
- * @param Definition $definition
- * @param array $config
+ * @param string $name
+ * @param Definition $definition
+ * @param array $config
+ * @param ContainerBuilder $container In case we need to add additional services for this plugin
+ * @param string $serviceId Service id of the plugin, in case we need to add additional services for this plugin.
*/
- private function configurePluginByName($name, Definition $definition, array $config)
+ private function configurePluginByName($name, Definition $definition, array $config, ContainerInterface $container, $serviceId)
{
switch ($name) {
case 'cache':
@@ -173,6 +177,23 @@ private function configurePluginByName($name, Definition $definition, array $con
$definition->replaceArgument(0, new Reference($config['stopwatch']));
break;
+ /* client specific plugins */
+
+ case 'add_host':
+ $uriService = $serviceId.'.host_uri';
+ $this->createUri($container, $uriService, $config['host']);
+ $definition->replaceArgument(0, new Reference($uriService));
+ $definition->replaceArgument(1, [
+ 'replace' => $config['replace'],
+ ]);
+ break;
+ case 'header_append':
+ case 'header_defaults':
+ case 'header_set':
+ case 'header_remove':
+ $definition->replaceArgument(0, $config['headers']);
+ break;
+
default:
throw new \InvalidArgumentException(sprintf('Internal exception: Plugin %s is not handled', $name));
}
@@ -181,11 +202,15 @@ private function configurePluginByName($name, Definition $definition, array $con
/**
* @param ContainerBuilder $container
* @param array $config
+ *
+ * @return array List of service ids for the authentication plugins.
*/
- private function configureAuthentication(ContainerBuilder $container, array $config)
+ private function configureAuthentication(ContainerBuilder $container, array $config, $servicePrefix = 'httplug.plugin.authentication')
{
+ $pluginServices = [];
+
foreach ($config as $name => $values) {
- $authServiceKey = sprintf('httplug.plugin.authentication.%s.auth', $name);
+ $authServiceKey = sprintf($servicePrefix.'.%s.auth', $name);
switch ($values['type']) {
case 'bearer':
$container->register($authServiceKey, Bearer::class)
@@ -208,33 +233,54 @@ private function configureAuthentication(ContainerBuilder $container, array $con
throw new \LogicException(sprintf('Unknown authentication type: "%s"', $values['type']));
}
- $container->register('httplug.plugin.authentication.'.$name, AuthenticationPlugin::class)
- ->addArgument(new Reference($authServiceKey));
+ $pluginServiceKey = $servicePrefix.'.'.$name;
+ $container->register($pluginServiceKey, AuthenticationPlugin::class)
+ ->addArgument(new Reference($authServiceKey))
+ ;
+ $pluginServices[] = $pluginServiceKey;
}
+
+ return $pluginServices;
}
/**
* @param ContainerBuilder $container
- * @param string $name
+ * @param string $clientName
* @param array $arguments
* @param bool $profiling
*/
- private function configureClient(ContainerBuilder $container, $name, array $arguments, $profiling)
+ private function configureClient(ContainerBuilder $container, $clientName, array $arguments, $profiling)
{
- $serviceId = 'httplug.client.'.$name;
+ $serviceId = 'httplug.client.'.$clientName;
+
+ $plugins = [];
+ foreach ($arguments['plugins'] as $plugin) {
+ list($pluginName, $pluginConfig) = each($plugin);
+ if ('reference' === $pluginName) {
+ $plugins[] = $pluginConfig['id'];
+ } elseif ('authentication' === $pluginName) {
+ $plugins = array_merge($plugins, $this->configureAuthentication($container, $pluginConfig, $serviceId.'.authentication'));
+ } else {
+ $pluginServiceId = $serviceId.'.plugin.'.$pluginName;
+ $def = clone $container->getDefinition('httplug.plugin'.'.'.$pluginName);
+ $def->setAbstract(false);
+ $this->configurePluginByName($pluginName, $def, $pluginConfig, $container, $pluginServiceId);
+ $container->setDefinition($pluginServiceId, $def);
+ $plugins[] = $pluginServiceId;
+ }
+ }
$pluginClientOptions = [];
-
if ($profiling) {
+ // Add the stopwatch plugin
if (!in_array('httplug.plugin.stopwatch', $arguments['plugins'])) {
- // Add the stopwatch plugin
- array_unshift($arguments['plugins'], 'httplug.plugin.stopwatch');
+ array_unshift($plugins, 'httplug.plugin.stopwatch');
}
// Tell the plugin journal what plugins we used
$container
->getDefinition('httplug.collector.plugin_journal')
- ->addMethodCall('setPlugins', [$name, $arguments['plugins']])
+ ->addMethodCall('setPlugins', [$clientName, $plugins])
;
$debugPluginServiceId = $this->registerDebugPlugin($container, $serviceId);
@@ -250,7 +296,7 @@ private function configureClient(ContainerBuilder $container, $name, array $argu
function ($id) {
return new Reference($id);
},
- $arguments['plugins']
+ $plugins
)
)
->addArgument(new Reference($arguments['factory']))
@@ -290,6 +336,23 @@ function ($id) {
}
}
+ /**
+ * Create a URI object with the default URI factory.
+ *
+ * @param ContainerBuilder $container
+ * @param string $serviceId Name of the private service to create
+ * @param string $uri String representation of the URI
+ */
+ private function createUri(ContainerBuilder $container, $serviceId, $uri)
+ {
+ $container
+ ->register($serviceId, UriInterface::class)
+ ->setPublic(false)
+ ->setFactory([new Reference('httplug.uri_factory'), 'createUri'])
+ ->addArgument($uri)
+ ;
+ }
+
/**
* Make the user can select what client is used for auto discovery. If none is provided, a service will be created
* by finding a client using auto discovery.
@@ -307,7 +370,7 @@ private function configureAutoDiscoveryClients(ContainerBuilder $container, arra
$container,
'auto_discovered_client',
[HttpClientDiscovery::class, 'find'],
- $config['profiling']['enabled']
+ $this->isConfigEnabled($container, $config['profiling'])
);
}
@@ -322,7 +385,7 @@ private function configureAutoDiscoveryClients(ContainerBuilder $container, arra
$container,
'auto_discovered_async',
[HttpAsyncClientDiscovery::class, 'find'],
- $config['profiling']['enabled']
+ $this->isConfigEnabled($container, $config['profiling'])
);
}
diff --git a/Resources/config/plugins.xml b/Resources/config/plugins.xml
index 7980222a..d921f910 100644
--- a/Resources/config/plugins.xml
+++ b/Resources/config/plugins.xml
@@ -27,5 +27,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/Functional/ServiceInstantiationTest.php b/Tests/Functional/ServiceInstantiationTest.php
index 7cefc810..8d07db67 100644
--- a/Tests/Functional/ServiceInstantiationTest.php
+++ b/Tests/Functional/ServiceInstantiationTest.php
@@ -43,7 +43,10 @@ public function testDebugToolbar()
$plugins = $journal->getPlugins('acme');
$this->assertEquals([
'httplug.plugin.stopwatch',
+ 'httplug.client.acme.plugin.decoder',
'httplug.plugin.redirect',
+ 'httplug.client.acme.plugin.add_host',
+ 'httplug.client.acme.authentication.my_basic',
], $plugins);
}
}
diff --git a/Tests/Resources/Fixtures/config/full.php b/Tests/Resources/Fixtures/config/full.php
index 0a8ab554..0dd24a6f 100644
--- a/Tests/Resources/Fixtures/config/full.php
+++ b/Tests/Resources/Fixtures/config/full.php
@@ -13,6 +13,43 @@
'uri_factory' => 'Http\Message\UriFactory\GuzzleUriFactory',
'stream_factory' => 'Http\Message\StreamFactory\GuzzleStreamFactory',
],
+ 'clients' => [
+ 'test' => [
+ 'factory' => 'httplug.factory.guzzle6',
+ 'http_methods_client' => true,
+ 'plugins' => [
+ 'httplug.plugin.redirect',
+ [
+ 'add_host' => [
+ 'host' => 'http://localhost',
+ ],
+ ],
+ [
+ 'header_set' => [
+ 'headers' => [
+ 'X-FOO' => 'bar',
+ ],
+ ],
+ ],
+ [
+ 'header_remove' => [
+ 'headers' => [
+ 'X-FOO',
+ ],
+ ],
+ ],
+ [
+ 'authentication' => [
+ 'my_basic' => [
+ 'type' => 'basic',
+ 'username' => 'foo',
+ 'password' => 'bar',
+ ],
+ ],
+ ]
+ ],
+ ],
+ ],
'profiling' => [
'enabled' => true,
'formatter' => 'my_toolbar_formatter',
diff --git a/Tests/Resources/Fixtures/config/full.xml b/Tests/Resources/Fixtures/config/full.xml
index e0ded54b..11a2e2a4 100644
--- a/Tests/Resources/Fixtures/config/full.xml
+++ b/Tests/Resources/Fixtures/config/full.xml
@@ -14,6 +14,27 @@
Http\Message\UriFactory\GuzzleUriFactory
Http\Message\StreamFactory\GuzzleStreamFactory
+
+ httplug.plugin.redirect
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Tests/Resources/Fixtures/config/full.yml b/Tests/Resources/Fixtures/config/full.yml
index 72341e8e..445c3b68 100644
--- a/Tests/Resources/Fixtures/config/full.yml
+++ b/Tests/Resources/Fixtures/config/full.yml
@@ -9,6 +9,28 @@ httplug:
message_factory: Http\Message\MessageFactory\GuzzleMessageFactory
uri_factory: Http\Message\UriFactory\GuzzleUriFactory
stream_factory: Http\Message\StreamFactory\GuzzleStreamFactory
+ clients:
+ test:
+ factory: httplug.factory.guzzle6
+ http_methods_client: true
+ plugins:
+ - 'httplug.plugin.redirect'
+ -
+ add_host:
+ host: http://localhost
+ -
+ header_set:
+ headers:
+ X-FOO: bar
+ -
+ header_remove:
+ headers: [X-FOO]
+ -
+ authentication:
+ my_basic:
+ type: basic
+ username: foo
+ password: bar
profiling:
enabled: true
formatter: my_toolbar_formatter
diff --git a/Tests/Resources/Fixtures/config/invalid_plugin.yml b/Tests/Resources/Fixtures/config/invalid_plugin.yml
new file mode 100644
index 00000000..f05c987f
--- /dev/null
+++ b/Tests/Resources/Fixtures/config/invalid_plugin.yml
@@ -0,0 +1,6 @@
+httplug:
+ clients:
+ acme:
+ plugins:
+ - foobar:
+ baz: ~
diff --git a/Tests/Resources/app/config/config.yml b/Tests/Resources/app/config/config.yml
index c97ad0fe..9856992b 100644
--- a/Tests/Resources/app/config/config.yml
+++ b/Tests/Resources/app/config/config.yml
@@ -7,4 +7,16 @@ httplug:
acme:
factory: httplug.factory.curl
plugins:
+ -
+ decoder:
+ use_content_encoding: false
- httplug.plugin.redirect
+ -
+ add_host:
+ host: "http://localhost:8000"
+ -
+ authentication:
+ my_basic:
+ type: basic
+ username: foo
+ password: bar
diff --git a/Tests/Unit/DependencyInjection/ConfigurationTest.php b/Tests/Unit/DependencyInjection/ConfigurationTest.php
index df931ea3..ecf09fcc 100644
--- a/Tests/Unit/DependencyInjection/ConfigurationTest.php
+++ b/Tests/Unit/DependencyInjection/ConfigurationTest.php
@@ -115,7 +115,55 @@ public function testSupportsAllConfigFormats()
'uri_factory' => 'Http\Message\UriFactory\GuzzleUriFactory',
'stream_factory' => 'Http\Message\StreamFactory\GuzzleStreamFactory',
],
- 'clients' => [],
+ 'clients' => [
+ 'test' => [
+ 'factory' => 'httplug.factory.guzzle6',
+ 'http_methods_client' => true,
+ 'flexible_client' => false,
+ 'batch_client' => false,
+ 'plugins' => [
+ [
+ 'reference' => [
+ 'enabled' => true,
+ 'id' => 'httplug.plugin.redirect',
+ ],
+ ],
+ [
+ 'add_host' => [
+ 'enabled' => true,
+ 'host' => 'http://localhost',
+ 'replace' => false,
+ ],
+ ],
+ [
+ 'header_set' => [
+ 'enabled' => true,
+ 'headers' => [
+ 'X-FOO' => 'bar',
+ ],
+ ],
+ ],
+ [
+ 'header_remove' => [
+ 'enabled' => true,
+ 'headers' => [
+ 'X-FOO',
+ ],
+ ],
+ ],
+ [
+ 'authentication' => [
+ 'my_basic' => [
+ 'type' => 'basic',
+ 'username' => 'foo',
+ 'password' => 'bar',
+ ],
+ ],
+ ],
+ ],
+ 'config' => [],
+ ],
+ ],
'profiling' => [
'enabled' => true,
'formatter' => 'my_toolbar_formatter',
@@ -211,6 +259,16 @@ public function testMissingClass()
$this->assertProcessedConfigurationEquals([], [$file]);
}
+ /**
+ * @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
+ * @expectedExceptionMessage Unrecognized option "foobar" under "httplug.clients.acme.plugins.0"
+ */
+ public function testInvalidPlugin()
+ {
+ $file = __DIR__.'/../../Resources/Fixtures/config/invalid_plugin.yml';
+ $this->assertProcessedConfigurationEquals([], [$file]);
+ }
+
/**
* @expectedException \Symfony\Component\Config\Definition\Exception\InvalidConfigurationException
* @expectedExceptionMessage password, service, username
diff --git a/Tests/Unit/DependencyInjection/HttplugExtensionTest.php b/Tests/Unit/DependencyInjection/HttplugExtensionTest.php
index 2e0484d5..598a8e07 100644
--- a/Tests/Unit/DependencyInjection/HttplugExtensionTest.php
+++ b/Tests/Unit/DependencyInjection/HttplugExtensionTest.php
@@ -2,8 +2,10 @@
namespace Http\HttplugBundle\Tests\Unit\DependencyInjection;
+use Http\Client\Common\PluginClient;
use Http\HttplugBundle\DependencyInjection\HttplugExtension;
use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase;
+use Symfony\Component\DependencyInjection\Reference;
/**
* @author David Buchmann
@@ -61,6 +63,80 @@ public function testConfigLoadService()
}
}
+ public function testClientPlugins()
+ {
+ $this->load([
+ 'clients' => [
+ 'acme' => [
+ 'factory' => 'httplug.factory.curl',
+ 'plugins' => [
+ [
+ 'decoder' => [
+ 'use_content_encoding' => false,
+ ],
+ ],
+ 'httplug.plugin.redirect',
+ [
+ 'add_host' => [
+ 'host' => 'http://localhost:8000',
+ ],
+ ],
+ [
+ 'header_append' => [
+ 'headers' => ['X-FOO' => 'bar'],
+ ],
+ ],
+ [
+ 'header_defaults' => [
+ 'headers' => ['X-FOO' => 'bar'],
+ ],
+ ],
+ [
+ 'header_set' => [
+ 'headers' => ['X-FOO' => 'bar'],
+ ],
+ ],
+ [
+ 'header_remove' => [
+ 'headers' => ['X-FOO'],
+ ],
+ ],
+ [
+ 'authentication' => [
+ 'my_basic' => [
+ 'type' => 'basic',
+ 'username' => 'foo',
+ 'password' => 'bar',
+ ],
+ ],
+ ],
+ ],
+ ],
+ ],
+ ]);
+
+ $plugins = [
+ 'httplug.plugin.stopwatch',
+ 'httplug.client.acme.plugin.decoder',
+ 'httplug.plugin.redirect',
+ 'httplug.client.acme.plugin.add_host',
+ 'httplug.client.acme.plugin.header_append',
+ 'httplug.client.acme.plugin.header_defaults',
+ 'httplug.client.acme.plugin.header_set',
+ 'httplug.client.acme.plugin.header_remove',
+ 'httplug.client.acme.authentication.my_basic',
+ ];
+ $pluginReferences = array_map(function ($id) {
+ return new Reference($id);
+ }, $plugins);
+
+ $this->assertContainerBuilderHasService('httplug.client.acme');
+ foreach ($plugins as $id) {
+ $this->assertContainerBuilderHasService($id);
+ }
+ $this->assertContainerBuilderHasServiceDefinitionWithArgument('httplug.client.acme', 0, $pluginReferences);
+ }
+
public function testNoProfilingWhenToolbarIsDisabled()
{
$this->load(