diff --git a/docker/caddy/Dockerfile b/docker/caddy/Dockerfile index cb15332..367e521 100644 --- a/docker/caddy/Dockerfile +++ b/docker/caddy/Dockerfile @@ -1,7 +1,7 @@ FROM caddy:2.5.1-builder-alpine RUN xcaddy build --with github.com/darkweak/souin/plugins/caddy \ - --with github.com/darkweak/souin@v1.6.16 + --with github.com/darkweak/souin@v1.6.17 # See https://caddyserver.com/docs/conventions#file-locations for details diff --git a/src/Caddy.php b/src/Caddy.php index dcec18a..bcdc055 100644 --- a/src/Caddy.php +++ b/src/Caddy.php @@ -5,7 +5,6 @@ use GuzzleHttp\Client; use GuzzleHttp\Exception\ClientException; use mattvb91\CaddyPhp\Config\Admin; -use mattvb91\CaddyPhp\Config\Apps\Http; use mattvb91\CaddyPhp\Config\Logging; use mattvb91\CaddyPhp\Exceptions\CaddyClientException; use mattvb91\caddyPhp\Interfaces\App; @@ -13,12 +12,6 @@ class Caddy implements Arrayable { - /** - * We need a reference to ourselves for adding domains and hosts - * dynamically later. - */ - private static self $_instance; - /** * This is the collection of hosts with the associated paths to where they are * in the config. @@ -41,10 +34,14 @@ class Caddy implements Arrayable /** @var App[] */ private array $_apps; + private $_hostname; + public function __construct(?string $hostname = 'caddy', ?Admin $admin = new Admin(), ?Client $client = null) { $this->setAdmin($admin); + $this->_hostname = $hostname; + $this->_client = $client ?? new Client([ 'base_uri' => $hostname . $this->getAdmin()->getListen() . '/config', 'headers' => [ @@ -52,14 +49,6 @@ public function __construct(?string $hostname = 'caddy', ?Admin $admin = new Adm ], ] ); - - //Reference ourselves - self::$_instance = &$this; - } - - public static function getInstance(): self - { - return self::$_instance; } /** @@ -113,6 +102,31 @@ public function removeHostname(string $hostIdentifier, string $hostname): bool return false; } + /** + * Get the current config of the caddy server. + * + * TODO we should be able to build our $caddy object back up from this. + * So instead of toArray we should be able to do fromArray() or something + */ + public function getRemoteConfig(): object + { + return json_decode($this->_client->get('/config')->getBody(), false, 512, JSON_THROW_ON_ERROR); + } + + /** + * This is responsible for flushing the individual caches of items on the caddy server. + */ + public function flushSurrogates(array $surrogates): bool + { + //TODO this is missing the fact that you could customize your cache paths. + + return $this->_client->request('PURGE', 'http://' . $this->_hostname . '/cache/souin', [ + 'headers' => [ + 'Surrogate-Key' => implode(', ', $surrogates), + ], + ])->getStatusCode() === 204; + } + /** * @throws \GuzzleHttp\Exception\GuzzleException * @throws \Exception diff --git a/src/Traits/IterableProps.php b/src/Traits/IterableProps.php index 413a275..45ed9c3 100644 --- a/src/Traits/IterableProps.php +++ b/src/Traits/IterableProps.php @@ -6,12 +6,7 @@ trait IterableProps { public function iterateAllProperties(): array { - $props = []; - foreach ($this as $key => $value) { - $props[$key] = &$value; - } - - return $props; + return get_object_vars($this); } } \ No newline at end of file diff --git a/tests/Integration/Apps/CacheTest.php b/tests/Integration/Apps/CacheTest.php index 7ef1000..8a79ea6 100644 --- a/tests/Integration/Apps/CacheTest.php +++ b/tests/Integration/Apps/CacheTest.php @@ -61,4 +61,61 @@ public function test_keys() $caddy->addApp($cache); $this->assertCaddyConfigLoaded($caddy); } + + /** + * @covers \mattvb91\CaddyPhp\Caddy::flushSurrogates + */ + public function test_surrogate_key_flush() + { + $caddy = new Caddy(); + + $http = new Http(); + $server = new Http\Server(); + $route = new Http\Server\Route(); + + $route->addHandle(new Http\Server\Routes\Handle\Cache()) + ->addHandle((new Http\Server\Routes\Handle\StaticResponse('cache test')) + ->setHeaders([ + 'Surrogate-Key' => [ + 'test_cache_key', + ], + ] + ) + ); + + $server->addRoute($route); + $http->addServer(key: 'cacheServer', server: $server); + + $caddy->addApp(new Cache()); + $caddy->addApp($http); + $this->assertCaddyConfigLoaded($caddy); + + $caddy->flushSurrogates(['test_cache_key']); + + $client = new Client([ + 'base_uri' => 'caddy', + ]); + + $client->request('PURGE', 'caddy/cache/souin/GET-caddy-/'); + $client->request('PURGE', 'caddy/cache/souin/GET-localhost-/'); + sleep(1); + + $request = $client->get(''); + $this->assertEquals(200, $request->getStatusCode()); + + $this->assertEquals('cache test', $request->getBody()->getContents()); + $this->assertStringContainsString('Souin; fwd=uri-miss; stored', $request->getHeader('cache-status')[0]); + + $request = $client->get(''); + $this->assertEquals(200, $request->getStatusCode()); + $this->assertStringContainsString('Souin; hit;', $request->getHeader('cache-status')[0]); + + $caddy->flushSurrogates(['test_cache_key']); + sleep(1); + + $request = $client->get(''); + $this->assertEquals(200, $request->getStatusCode()); + $this->assertStringContainsString('Souin; fwd=uri-miss; stored', $request->getHeader('cache-status')[0]); + + } } \ No newline at end of file diff --git a/tests/Integration/CaddyTest.php b/tests/Integration/CaddyTest.php index c64a9fb..f119d8b 100644 --- a/tests/Integration/CaddyTest.php +++ b/tests/Integration/CaddyTest.php @@ -274,4 +274,20 @@ public function test_http_basic_auth() // // $this->assertTrue($caddy->load()); // } + + /** + * @covers \mattvb91\CaddyPhp\Caddy::getRemoteConfig + */ + public function test_caddy_get_config() + { + $caddy = new Caddy(); + $caddy->addApp((new Http()) + ->addServer('test', (new Http\Server()) + ->addRoute((new Route()) + ->addHandle((new StaticResponse('test')))))); + $caddy->load(); + + $this->assertEquals(json_decode(json_encode($caddy->toArray())), $caddy->getRemoteConfig()); + } + } \ No newline at end of file diff --git a/tests/Unit/MiscTest.php b/tests/Unit/MiscTest.php new file mode 100644 index 0000000..96c6de8 --- /dev/null +++ b/tests/Unit/MiscTest.php @@ -0,0 +1,37 @@ +setListen([":80"]); + + + $host = (new Http\Server\Routes\Match\Host('shops')) + ->setHosts([ + "{http.request.host}.localhost", + ]); + + $shopDeploymentsRoute = new Http\Server\Route(); + $shopDeploymentsRoute->addMatch($host) + ->setTerminal(true); + + + $server->addRoute($shopDeploymentsRoute); + $http->addServer('platform', $server); + + $this->assertEquals($host, findHost($http, 'shops')['host']); + } + +} \ No newline at end of file