Skip to content

Commit 715d06d

Browse files
authored
Add cloudflare support (#587)
1 parent be22ba1 commit 715d06d

File tree

11 files changed

+212
-5
lines changed

11 files changed

+212
-5
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
Changelog
22
=========
33

4+
2.15.0
5+
------
6+
7+
* Added Cloudflare proxy client.
8+
49
2.14.0
510
------
611

Resources/doc/reference/configuration/proxy-client.rst

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ The proxy client is also directly available as a service. The default client
1212
can be autowired with the ``FOS\HttpCache\ProxyClient\ProxyClient`` type
1313
declaration or the service ``fos_http_cache.default_proxy_client``. Specific
1414
clients, if configured, are available as ``fos_http_cache.proxy_client.varnish``
15-
, ``fos_http_cache.proxy_client.nginx`` or ``fos_http_cache.proxy_client.symfony``).
15+
, ``fos_http_cache.proxy_client.nginx``, ``fos_http_cache.proxy_client.symfony``
16+
or ``fos_http_cache.proxy_client.cloudflare``).
1617

1718
If you need to adjust the proxy client, you can also configure the ``CacheManager``
1819
with a :ref:`custom proxy client <custom_proxy_client>` that you defined as a
@@ -236,6 +237,43 @@ HTTP method for sending purge requests to the Symfony HttpCache. Make sure to
236237
configure the purge plugin for your HttpCache with the matching header if you
237238
change this.
238239

240+
cloudflare
241+
-------
242+
243+
.. code-block:: yaml
244+
245+
# config/packages/fos_http_cache.yaml
246+
fos_http_cache:
247+
proxy_client:
248+
cloudflare:
249+
zone_identifier: '<my-zone-identifier>'
250+
authentication_token: '<user-authentication-token>'
251+
http:
252+
servers:
253+
- 'https://api.cloudflare.com'
254+
255+
``authentication_token``
256+
"""""""""""""""""""""""
257+
258+
**type**: ``string``
259+
260+
User API token for authentication against Cloudflare APIs, requires ``Zone.Cache`` Purge permissions.
261+
262+
``zone_identifier``
263+
"""""""""""""""""
264+
265+
**type**: ``string``
266+
267+
Identifier for the Cloudflare zone you want to purge the cache for.
268+
269+
``http.servers``
270+
""""""""""""""""
271+
272+
**type**: ``array`` **default**: ``['https://api.cloudflare.com']``
273+
274+
List of Cloudflare API endpoints to use for purging the cache. You can use this to specify a different
275+
endpoint for testing purposes.
276+
239277
.. _configuration_noop_proxy_client:
240278

241279
noop

Resources/doc/spelling_word_list.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ autoconfigure
55
autoconfigured
66
backend
77
cacheable
8+
cloudflare
89
ETag
910
friendsofsymfony
1011
github

src/DependencyInjection/Configuration.php

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,7 @@ private function addProxyClientSection(ArrayNodeDefinition $rootNode)
412412
->arrayNode('proxy_client')
413413
->children()
414414
->enumNode('default')
415-
->values(['varnish', 'nginx', 'symfony', 'noop'])
415+
->values(['varnish', 'nginx', 'symfony', 'cloudflare', 'noop'])
416416
->info('If you configure more than one proxy client, you need to specify which client is the default.')
417417
->end()
418418
->arrayNode('varnish')
@@ -482,6 +482,18 @@ private function addProxyClientSection(ArrayNodeDefinition $rootNode)
482482
->end()
483483
->end()
484484

485+
->arrayNode('cloudflare')
486+
->children()
487+
->scalarNode('authentication_token')
488+
->info('API authorization token, requires Zone.Cache Purge permissions')
489+
->end()
490+
->scalarNode('zone_identifier')
491+
->info('Identifier for your Cloudflare zone you want to purge the cache for')
492+
->end()
493+
->append($this->getCloudflareHttpDispatcherNode())
494+
->end()
495+
->end()
496+
485497
->booleanNode('noop')->end()
486498
->end()
487499
->validate()
@@ -500,7 +512,7 @@ private function addProxyClientSection(ArrayNodeDefinition $rootNode)
500512
throw new InvalidConfigurationException(sprintf('You can only set one of "http.servers" or "http.servers_from_jsonenv" but not both to avoid ambiguity for the proxy "%s"', $proxyName));
501513
}
502514

503-
if (!\in_array($proxyName, ['noop', 'default', 'symfony'])) {
515+
if (!\in_array($proxyName, ['noop', 'default', 'symfony', 'cloudflare'])) {
504516
if (!$arrayServersConfigured && !$jsonServersConfigured) {
505517
throw new InvalidConfigurationException(sprintf('The "http.servers" or "http.servers_from_jsonenv" section must be defined for the proxy "%s"', $proxyName));
506518
}
@@ -564,6 +576,36 @@ private function getHttpDispatcherNode()
564576
return $node;
565577
}
566578

579+
private function getCloudflareHttpDispatcherNode()
580+
{
581+
$treeBuilder = new TreeBuilder('http');
582+
583+
// Keep compatibility with symfony/config < 4.2
584+
if (!method_exists($treeBuilder, 'getRootNode')) {
585+
$node = $treeBuilder->root('http');
586+
} else {
587+
$node = $treeBuilder->getRootNode();
588+
}
589+
590+
$node
591+
->addDefaultsIfNotSet()
592+
->children()
593+
->arrayNode('servers')
594+
->info('Addresses of the hosts the caching proxy is running on. The values may be hostnames or ips, and with :port if not the default port 80.')
595+
->useAttributeAsKey('name')
596+
->requiresAtLeastOneElement()
597+
->defaultValue(['https://api.cloudflare.com'])
598+
->prototype('scalar')->end()
599+
->end()
600+
->scalarNode('http_client')
601+
->defaultNull()
602+
->info('Httplug async client service name to use for sending the requests.')
603+
->end()
604+
->end();
605+
606+
return $node;
607+
}
608+
567609
private function addTestSection(ArrayNodeDefinition $rootNode)
568610
{
569611
$rootNode

src/DependencyInjection/FOSHttpCacheExtension.php

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,9 @@ private function loadProxyClient(ContainerBuilder $container, XmlFileLoader $loa
337337
if (isset($config['symfony'])) {
338338
$this->loadSymfony($container, $loader, $config['symfony']);
339339
}
340+
if (isset($config['cloudflare'])) {
341+
$this->loadCloudflare($container, $loader, $config['cloudflare']);
342+
}
340343
if (isset($config['noop'])) {
341344
$loader->load('noop.xml');
342345
}
@@ -455,19 +458,32 @@ private function loadSymfony(ContainerBuilder $container, XmlFileLoader $loader,
455458
$loader->load('symfony.xml');
456459
}
457460

461+
private function loadCloudflare(ContainerBuilder $container, XmlFileLoader $loader, array $config)
462+
{
463+
$this->createHttpDispatcherDefinition($container, $config['http'], 'fos_http_cache.proxy_client.cloudflare.http_dispatcher');
464+
$options = [
465+
'authentication_token' => $config['authentication_token'],
466+
'zone_identifier' => $config['zone_identifier'],
467+
];
468+
469+
$container->setParameter('fos_http_cache.proxy_client.cloudflare.options', $options);
470+
471+
$loader->load('cloudflare.xml');
472+
}
473+
458474
/**
459475
* @param array $config Configuration section for the tags node
460476
* @param string $client Name of the client used with the cache manager,
461477
* "custom" when a custom client is used
462478
*/
463479
private function loadCacheTagging(ContainerBuilder $container, XmlFileLoader $loader, array $config, $client)
464480
{
465-
if ('auto' === $config['enabled'] && !in_array($client, ['varnish', 'symfony'])) {
481+
if ('auto' === $config['enabled'] && !in_array($client, ['varnish', 'symfony', 'cloudflare'])) {
466482
$container->setParameter('fos_http_cache.compiler_pass.tag_annotations', false);
467483

468484
return;
469485
}
470-
if (!in_array($client, ['varnish', 'symfony', 'custom', 'noop'])) {
486+
if (!in_array($client, ['varnish', 'symfony', 'cloudflare', 'custom', 'noop'])) {
471487
throw new InvalidConfigurationException(sprintf('You can not enable cache tagging with the %s client', $client));
472488
}
473489

@@ -609,6 +625,10 @@ private function getDefaultProxyClient(array $config)
609625
return 'symfony';
610626
}
611627

628+
if (isset($config['cloudflare'])) {
629+
return 'cloudflare';
630+
}
631+
612632
if (isset($config['noop'])) {
613633
return 'noop';
614634
}

src/Resources/config/cloudflare.xml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?xml version="1.0" ?>
2+
3+
<container xmlns="http://symfony.com/schema/dic/services"
4+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
5+
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
6+
7+
<services>
8+
<service id="fos_http_cache.proxy_client.cloudflare"
9+
class="FOS\HttpCache\ProxyClient\Cloudflare"
10+
public="true">
11+
<argument type="service" id="fos_http_cache.proxy_client.cloudflare.http_dispatcher"/>
12+
<argument>%fos_http_cache.proxy_client.cloudflare.options%</argument>
13+
</service>
14+
</services>
15+
16+
</container>
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the FOSHttpCacheBundle package.
5+
*
6+
* (c) FriendsOfSymfony <http://friendsofsymfony.github.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+
$container->loadFromExtension('fos_http_cache', [
13+
'proxy_client' => [
14+
'cloudflare' => [
15+
'authentication_token' => 'mytoken',
16+
'zone_identifier' => 'myzone',
17+
],
18+
],
19+
]);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<container xmlns="http://symfony.com/schema/dic/services">
3+
4+
<config xmlns="http://example.org/schema/dic/fos_http_cache">
5+
<proxy-client>
6+
<cloudflare authentication-token="mytoken" zone-identifier="myzone">
7+
</cloudflare>
8+
</proxy-client>
9+
10+
</config>
11+
</container>
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
fos_http_cache:
2+
3+
proxy_client:
4+
cloudflare:
5+
authentication_token: mytoken
6+
zone_identifier: myzone

tests/Unit/DependencyInjection/ConfigurationTest.php

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,35 @@ public function testSupportsSymfony()
298298
}
299299
}
300300

301+
public function testSupportsCloudflare()
302+
{
303+
$expectedConfiguration = $this->getEmptyConfig();
304+
$expectedConfiguration['proxy_client'] = [
305+
'cloudflare' => [
306+
'authentication_token' => 'mytoken',
307+
'zone_identifier' => 'myzone',
308+
'http' => ['servers' => ['https://api.cloudflare.com'], 'http_client' => null],
309+
],
310+
];
311+
$expectedConfiguration['cache_manager']['enabled'] = 'auto';
312+
$expectedConfiguration['cache_manager']['generate_url_type'] = 'auto';
313+
$expectedConfiguration['tags']['enabled'] = 'auto';
314+
$expectedConfiguration['invalidation']['enabled'] = 'auto';
315+
$expectedConfiguration['user_context']['logout_handler']['enabled'] = false;
316+
317+
$formats = array_map(function ($path) {
318+
return __DIR__.'/../../Resources/Fixtures/'.$path;
319+
}, [
320+
'config/cloudflare.yml',
321+
'config/cloudflare.xml',
322+
'config/cloudflare.php',
323+
]);
324+
325+
foreach ($formats as $format) {
326+
$this->assertProcessedConfigurationEquals($expectedConfiguration, [$format]);
327+
}
328+
}
329+
301330
public function testEmptyServerConfigurationIsNotAllowed()
302331
{
303332
$this->expectException(InvalidConfigurationException::class);

0 commit comments

Comments
 (0)