From 781f76d90af690ebcf2ae73081d00b8b4370325b Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Thu, 13 Jun 2024 18:30:32 +0200 Subject: [PATCH 1/9] cache 404s --- .../Concerns/RendersHttpExceptions.php | 24 +++++++++++++++++++ .../Cachers/ApplicationCacher.php | 3 ++- src/StaticCaching/Middleware/Cache.php | 2 +- src/StaticCaching/Page.php | 9 ++++--- 4 files changed, 33 insertions(+), 5 deletions(-) diff --git a/src/Exceptions/Concerns/RendersHttpExceptions.php b/src/Exceptions/Concerns/RendersHttpExceptions.php index 884711863f..8e90bade44 100644 --- a/src/Exceptions/Concerns/RendersHttpExceptions.php +++ b/src/Exceptions/Concerns/RendersHttpExceptions.php @@ -2,8 +2,10 @@ namespace Statamic\Exceptions\Concerns; +use Illuminate\Http\Response; use Statamic\Facades\Cascade; use Statamic\Statamic; +use Statamic\StaticCaching\Cacher; use Statamic\View\View; trait RendersHttpExceptions @@ -18,6 +20,10 @@ public function render() return response()->json(['message' => $this->getApiMessage()], $this->getStatusCode()); } + if ($cached = $this->getCached404()) { + return $cached; + } + if (view()->exists('errors.'.$this->getStatusCode())) { return response($this->contents(), $this->getStatusCode()); } @@ -53,4 +59,22 @@ public function getApiMessage() { return $this->getMessage(); } + + private function getCached404(): ?Response + { + if ($this->getStatusCode() !== 404) { + return null; + } + + $cacher = app(Cacher::class); + + $request = request(); + // $request->setUrl('404'); + + if (! $cacher->hasCachedPage($request)) { + return null; + } + + return $cacher->getCachedPage($request)->toResponse($request); + } } diff --git a/src/StaticCaching/Cachers/ApplicationCacher.php b/src/StaticCaching/Cachers/ApplicationCacher.php index da05b7ab01..0486ea20de 100644 --- a/src/StaticCaching/Cachers/ApplicationCacher.php +++ b/src/StaticCaching/Cachers/ApplicationCacher.php @@ -52,6 +52,7 @@ public function cachePage(Request $request, $content) $cacheValue = [ 'content' => $value, 'headers' => $headers, + 'status' => $event->response->getStatusCode(), ]; $this->getDefaultExpiration() @@ -79,7 +80,7 @@ public function getCachedPage(Request $request) { $cachedPage = $this->cached ?? $this->getFromCache($request); - return new Page($cachedPage['content'], $cachedPage['headers']); + return new Page($cachedPage['content'], $cachedPage['headers'], $cachedPage['status']); } private function getFromCache(Request $request) diff --git a/src/StaticCaching/Middleware/Cache.php b/src/StaticCaching/Middleware/Cache.php index 4f74c7de34..5e99a3af2a 100644 --- a/src/StaticCaching/Middleware/Cache.php +++ b/src/StaticCaching/Middleware/Cache.php @@ -139,7 +139,7 @@ private function shouldBeCached($request, $response) return false; } - if ($response->getStatusCode() !== 200 || $response->getContent() == '') { + if (! in_array($response->getStatusCode(), [200, 404]) || $response->getContent() == '') { return false; } diff --git a/src/StaticCaching/Page.php b/src/StaticCaching/Page.php index ca804540f8..03473dc1f8 100644 --- a/src/StaticCaching/Page.php +++ b/src/StaticCaching/Page.php @@ -7,12 +7,15 @@ class Page implements Responsable { - public function __construct(public string $content, public array $headers = []) - { + public function __construct( + public string $content, + public array $headers = [], + public int $status = 200 + ) { } public function toResponse($request): Response { - return new Response($this->content, 200, $this->headers); + return new Response($this->content, $this->status, $this->headers); } } From fa8381d593cae5e41eafc0f43e8ee901d9a52154 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 17 Jun 2024 13:29:34 -0400 Subject: [PATCH 2/9] wip --- config/static_caching.php | 14 ++++++++++++++ .../Concerns/RendersHttpExceptions.php | 16 +++++++++------- src/StaticCaching/Middleware/Cache.php | 19 +++++++++++++++++++ src/StaticCaching/ServiceProvider.php | 9 +++++++++ 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/config/static_caching.php b/config/static_caching.php index ec0ceba8fa..5bf1d08016 100644 --- a/config/static_caching.php +++ b/config/static_caching.php @@ -129,4 +129,18 @@ 'warm_queue' => null, + /* + |-------------------------------------------------------------------------- + | Errors + |-------------------------------------------------------------------------- + | + | Here you may define the URLs that should be used for error pages. This + | will make all instances of a response code refer to the same page. + | + */ + + 'errors' => [ + // 404 => '/404', + ], + ]; diff --git a/src/Exceptions/Concerns/RendersHttpExceptions.php b/src/Exceptions/Concerns/RendersHttpExceptions.php index 8e90bade44..d4e4fd335b 100644 --- a/src/Exceptions/Concerns/RendersHttpExceptions.php +++ b/src/Exceptions/Concerns/RendersHttpExceptions.php @@ -2,6 +2,7 @@ namespace Statamic\Exceptions\Concerns; +use Illuminate\Http\Request; use Illuminate\Http\Response; use Statamic\Facades\Cascade; use Statamic\Statamic; @@ -66,15 +67,16 @@ private function getCached404(): ?Response return null; } - $cacher = app(Cacher::class); - - $request = request(); - // $request->setUrl('404'); - - if (! $cacher->hasCachedPage($request)) { + if (! config('statamic.static_caching.errors.404')) { return null; } - return $cacher->getCachedPage($request)->toResponse($request); + $request = Request::createFrom(request())->fakeStaticCache404(); + + $cacher = app(Cacher::class); + + if ($cacher->hasCachedPage($request)) { + return $cacher->getCachedPage($request)->toResponse($request); + } } } diff --git a/src/StaticCaching/Middleware/Cache.php b/src/StaticCaching/Middleware/Cache.php index 5e99a3af2a..52b7297ffd 100644 --- a/src/StaticCaching/Middleware/Cache.php +++ b/src/StaticCaching/Middleware/Cache.php @@ -63,6 +63,8 @@ public function handle($request, Closure $next) if ($this->shouldBeCached($request, $response)) { $lock->acquire(true); + $this->copy404($request, $response); + $this->makeReplacementsAndCacheResponse($request, $response); $this->nocache->write(); @@ -73,6 +75,23 @@ public function handle($request, Closure $next) return $response; } + private function copy404($request, $response) + { + if ($response->getStatusCode() !== 404) { + return; + } + + if (! config('statamic.static_caching.errors.404')) { + return; + } + + $request = Request::createFrom($request)->fakeStaticCache404(); + + if ($this->cacher->hasCachedPage($request)) { + $this->cacher->cachePage($request, $response); + } + } + private function attemptToGetCachedResponse($request) { if ($this->canBeCached($request) && $this->cacher->hasCachedPage($request)) { diff --git a/src/StaticCaching/ServiceProvider.php b/src/StaticCaching/ServiceProvider.php index fec06fad69..21bef1d3dc 100644 --- a/src/StaticCaching/ServiceProvider.php +++ b/src/StaticCaching/ServiceProvider.php @@ -2,6 +2,7 @@ namespace Statamic\StaticCaching; +use Illuminate\Http\Request; use Illuminate\Support\Facades\Blade; use Illuminate\Support\Facades\Event; use Illuminate\Support\ServiceProvider as LaravelServiceProvider; @@ -78,5 +79,13 @@ public function boot() Blade::directive('nocache', function ($exp) { return 'handle('.$exp.', \Illuminate\Support\Arr::except(get_defined_vars(), [\'__data\', \'__path\'])); ?>'; }); + + Request::macro('fakeStaticCache404', function () { + $url = str(config('statamic.static_caching.errors.404'))->start('/')->toString(); + $this->pathInfo = $url; + $this->requestUri = $url; + + return $this; + }); } } From f2763e3cb2609273e896f22be2fa58d982ab88e6 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 17 Jun 2024 14:25:06 -0400 Subject: [PATCH 3/9] future proof --- src/Exceptions/Concerns/RendersHttpExceptions.php | 2 +- src/StaticCaching/Middleware/Cache.php | 2 +- src/StaticCaching/ServiceProvider.php | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Exceptions/Concerns/RendersHttpExceptions.php b/src/Exceptions/Concerns/RendersHttpExceptions.php index d4e4fd335b..984f3a7c84 100644 --- a/src/Exceptions/Concerns/RendersHttpExceptions.php +++ b/src/Exceptions/Concerns/RendersHttpExceptions.php @@ -71,7 +71,7 @@ private function getCached404(): ?Response return null; } - $request = Request::createFrom(request())->fakeStaticCache404(); + $request = Request::createFrom(request())->fakeStaticCacheStatus(404); $cacher = app(Cacher::class); diff --git a/src/StaticCaching/Middleware/Cache.php b/src/StaticCaching/Middleware/Cache.php index 52b7297ffd..ad4e066184 100644 --- a/src/StaticCaching/Middleware/Cache.php +++ b/src/StaticCaching/Middleware/Cache.php @@ -85,7 +85,7 @@ private function copy404($request, $response) return; } - $request = Request::createFrom($request)->fakeStaticCache404(); + $request = Request::createFrom($request)->fakeStaticCacheStatus(404); if ($this->cacher->hasCachedPage($request)) { $this->cacher->cachePage($request, $response); diff --git a/src/StaticCaching/ServiceProvider.php b/src/StaticCaching/ServiceProvider.php index 21bef1d3dc..35a4978d96 100644 --- a/src/StaticCaching/ServiceProvider.php +++ b/src/StaticCaching/ServiceProvider.php @@ -80,8 +80,8 @@ public function boot() return 'handle('.$exp.', \Illuminate\Support\Arr::except(get_defined_vars(), [\'__data\', \'__path\'])); ?>'; }); - Request::macro('fakeStaticCache404', function () { - $url = str(config('statamic.static_caching.errors.404'))->start('/')->toString(); + Request::macro('fakeStaticCacheStatus', function (int $status) { + $url = str(config('statamic.static_caching.errors.'.$status))->start('/')->toString(); $this->pathInfo = $url; $this->requestUri = $url; From b1cab5c77f9e67ac8c3f3fc0ee1250bf40f7b8e0 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 17 Jun 2024 14:28:58 -0400 Subject: [PATCH 4/9] gotta return something --- src/Exceptions/Concerns/RendersHttpExceptions.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Exceptions/Concerns/RendersHttpExceptions.php b/src/Exceptions/Concerns/RendersHttpExceptions.php index 984f3a7c84..4fb3cf4551 100644 --- a/src/Exceptions/Concerns/RendersHttpExceptions.php +++ b/src/Exceptions/Concerns/RendersHttpExceptions.php @@ -75,8 +75,8 @@ private function getCached404(): ?Response $cacher = app(Cacher::class); - if ($cacher->hasCachedPage($request)) { - return $cacher->getCachedPage($request)->toResponse($request); - } + return $cacher->hasCachedPage($request) + ? $cacher->getCachedPage($request)->toResponse($request) + : null; } } From bb4a09e7bd3f47d6dd41f4ce8af746fb3a4f2662 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 17 Jun 2024 14:32:02 -0400 Subject: [PATCH 5/9] fix derpy refactor --- src/StaticCaching/Middleware/Cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/StaticCaching/Middleware/Cache.php b/src/StaticCaching/Middleware/Cache.php index ad4e066184..d73b6671bc 100644 --- a/src/StaticCaching/Middleware/Cache.php +++ b/src/StaticCaching/Middleware/Cache.php @@ -87,7 +87,7 @@ private function copy404($request, $response) $request = Request::createFrom($request)->fakeStaticCacheStatus(404); - if ($this->cacher->hasCachedPage($request)) { + if (! $this->cacher->hasCachedPage($request)) { $this->cacher->cachePage($request, $response); } } From b80711aef3324e9ef17b3232c214cddbe31dddfd Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 17 Jun 2024 14:36:23 -0400 Subject: [PATCH 6/9] wip --- src/Exceptions/Concerns/RendersHttpExceptions.php | 12 +++++------- src/StaticCaching/Middleware/Cache.php | 12 +++++------- 2 files changed, 10 insertions(+), 14 deletions(-) diff --git a/src/Exceptions/Concerns/RendersHttpExceptions.php b/src/Exceptions/Concerns/RendersHttpExceptions.php index 4fb3cf4551..e7fa461737 100644 --- a/src/Exceptions/Concerns/RendersHttpExceptions.php +++ b/src/Exceptions/Concerns/RendersHttpExceptions.php @@ -21,7 +21,7 @@ public function render() return response()->json(['message' => $this->getApiMessage()], $this->getStatusCode()); } - if ($cached = $this->getCached404()) { + if ($cached = $this->getCachedError()) { return $cached; } @@ -61,17 +61,15 @@ public function getApiMessage() return $this->getMessage(); } - private function getCached404(): ?Response + private function getCachedError(): ?Response { - if ($this->getStatusCode() !== 404) { - return null; - } + $status = $this->getStatusCode(); - if (! config('statamic.static_caching.errors.404')) { + if (! config('statamic.static_caching.errors.'.$status)) { return null; } - $request = Request::createFrom(request())->fakeStaticCacheStatus(404); + $request = Request::createFrom(request())->fakeStaticCacheStatus($status); $cacher = app(Cacher::class); diff --git a/src/StaticCaching/Middleware/Cache.php b/src/StaticCaching/Middleware/Cache.php index d73b6671bc..24229a1ca3 100644 --- a/src/StaticCaching/Middleware/Cache.php +++ b/src/StaticCaching/Middleware/Cache.php @@ -63,7 +63,7 @@ public function handle($request, Closure $next) if ($this->shouldBeCached($request, $response)) { $lock->acquire(true); - $this->copy404($request, $response); + $this->copyError($request, $response); $this->makeReplacementsAndCacheResponse($request, $response); @@ -75,17 +75,15 @@ public function handle($request, Closure $next) return $response; } - private function copy404($request, $response) + private function copyError($request, $response) { - if ($response->getStatusCode() !== 404) { - return; - } + $status = $response->getStatusCode(); - if (! config('statamic.static_caching.errors.404')) { + if (! config('statamic.static_caching.errors.'.$status)) { return; } - $request = Request::createFrom($request)->fakeStaticCacheStatus(404); + $request = Request::createFrom($request)->fakeStaticCacheStatus($status); if (! $this->cacher->hasCachedPage($request)) { $this->cacher->cachePage($request, $response); From 7e2baafd5cf4a3eadb8f902f3d0d0adcd92204f7 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 17 Jun 2024 14:43:19 -0400 Subject: [PATCH 7/9] switch to boolean --- config/static_caching.php | 11 +++++------ src/Exceptions/Concerns/RendersHttpExceptions.php | 2 +- src/StaticCaching/Middleware/Cache.php | 2 +- src/StaticCaching/ServiceProvider.php | 2 +- 4 files changed, 8 insertions(+), 9 deletions(-) diff --git a/config/static_caching.php b/config/static_caching.php index 5bf1d08016..fe09d946db 100644 --- a/config/static_caching.php +++ b/config/static_caching.php @@ -131,16 +131,15 @@ /* |-------------------------------------------------------------------------- - | Errors + | Shared Error Pages |-------------------------------------------------------------------------- | - | Here you may define the URLs that should be used for error pages. This - | will make all instances of a response code refer to the same page. + | You may choose to share the same statically generated error page across + | all errors. For example, the first time a 404 is encountered it will + | be generated and cached, and then served for all subsequent 404s. | */ - 'errors' => [ - // 404 => '/404', - ], + 'share_errors' => false, ]; diff --git a/src/Exceptions/Concerns/RendersHttpExceptions.php b/src/Exceptions/Concerns/RendersHttpExceptions.php index e7fa461737..9efee59a6e 100644 --- a/src/Exceptions/Concerns/RendersHttpExceptions.php +++ b/src/Exceptions/Concerns/RendersHttpExceptions.php @@ -65,7 +65,7 @@ private function getCachedError(): ?Response { $status = $this->getStatusCode(); - if (! config('statamic.static_caching.errors.'.$status)) { + if (! config('statamic.static_caching.share_errors')) { return null; } diff --git a/src/StaticCaching/Middleware/Cache.php b/src/StaticCaching/Middleware/Cache.php index 24229a1ca3..c41e7309b5 100644 --- a/src/StaticCaching/Middleware/Cache.php +++ b/src/StaticCaching/Middleware/Cache.php @@ -79,7 +79,7 @@ private function copyError($request, $response) { $status = $response->getStatusCode(); - if (! config('statamic.static_caching.errors.'.$status)) { + if (! config('statamic.static_caching.share_errors')) { return; } diff --git a/src/StaticCaching/ServiceProvider.php b/src/StaticCaching/ServiceProvider.php index 35a4978d96..6fad890af0 100644 --- a/src/StaticCaching/ServiceProvider.php +++ b/src/StaticCaching/ServiceProvider.php @@ -81,7 +81,7 @@ public function boot() }); Request::macro('fakeStaticCacheStatus', function (int $status) { - $url = str(config('statamic.static_caching.errors.'.$status))->start('/')->toString(); + $url = '/__shared-errors/'.$status; $this->pathInfo = $url; $this->requestUri = $url; From 133e7e87995b3c5c5a04696eb15db2e6a8f2883b Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 17 Jun 2024 14:48:47 -0400 Subject: [PATCH 8/9] only supported on half measure --- config/static_caching.php | 2 ++ src/Exceptions/Concerns/RendersHttpExceptions.php | 9 +++++++-- src/StaticCaching/Middleware/Cache.php | 5 ++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/config/static_caching.php b/config/static_caching.php index fe09d946db..e4a2fdd13b 100644 --- a/config/static_caching.php +++ b/config/static_caching.php @@ -138,6 +138,8 @@ | all errors. For example, the first time a 404 is encountered it will | be generated and cached, and then served for all subsequent 404s. | + | This is only supported for half measure. + | */ 'share_errors' => false, diff --git a/src/Exceptions/Concerns/RendersHttpExceptions.php b/src/Exceptions/Concerns/RendersHttpExceptions.php index 9efee59a6e..aa4528b5d3 100644 --- a/src/Exceptions/Concerns/RendersHttpExceptions.php +++ b/src/Exceptions/Concerns/RendersHttpExceptions.php @@ -7,6 +7,7 @@ use Statamic\Facades\Cascade; use Statamic\Statamic; use Statamic\StaticCaching\Cacher; +use Statamic\StaticCaching\Cachers\ApplicationCacher; use Statamic\View\View; trait RendersHttpExceptions @@ -69,10 +70,14 @@ private function getCachedError(): ?Response return null; } - $request = Request::createFrom(request())->fakeStaticCacheStatus($status); - $cacher = app(Cacher::class); + if (! $cacher instanceof ApplicationCacher) { + return null; + } + + $request = Request::createFrom(request())->fakeStaticCacheStatus($status); + return $cacher->hasCachedPage($request) ? $cacher->getCachedPage($request)->toResponse($request) : null; diff --git a/src/StaticCaching/Middleware/Cache.php b/src/StaticCaching/Middleware/Cache.php index c41e7309b5..705e6ded4f 100644 --- a/src/StaticCaching/Middleware/Cache.php +++ b/src/StaticCaching/Middleware/Cache.php @@ -10,6 +10,7 @@ use Statamic\Facades\File; use Statamic\Statamic; use Statamic\StaticCaching\Cacher; +use Statamic\StaticCaching\Cachers\ApplicationCacher; use Statamic\StaticCaching\Cachers\NullCacher; use Statamic\StaticCaching\NoCache\RegionNotFound; use Statamic\StaticCaching\NoCache\Session; @@ -156,7 +157,9 @@ private function shouldBeCached($request, $response) return false; } - if (! in_array($response->getStatusCode(), [200, 404]) || $response->getContent() == '') { + $statuses = $this->cacher instanceof ApplicationCacher ? [200, 404] : [200]; + + if (! in_array($response->getStatusCode(), $statuses) || $response->getContent() == '') { return false; } From e78bc2449cd13f032babf398b20372e3f51a59bf Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Mon, 17 Jun 2024 14:57:25 -0400 Subject: [PATCH 9/9] add test and fix bc --- src/StaticCaching/Cachers/ApplicationCacher.php | 2 +- tests/StaticCaching/ApplicationCacherTest.php | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/StaticCaching/Cachers/ApplicationCacher.php b/src/StaticCaching/Cachers/ApplicationCacher.php index 0486ea20de..a583f57770 100644 --- a/src/StaticCaching/Cachers/ApplicationCacher.php +++ b/src/StaticCaching/Cachers/ApplicationCacher.php @@ -80,7 +80,7 @@ public function getCachedPage(Request $request) { $cachedPage = $this->cached ?? $this->getFromCache($request); - return new Page($cachedPage['content'], $cachedPage['headers'], $cachedPage['status']); + return new Page($cachedPage['content'], $cachedPage['headers'], $cachedPage['status'] ?? 200); } private function getFromCache(Request $request) diff --git a/tests/StaticCaching/ApplicationCacherTest.php b/tests/StaticCaching/ApplicationCacherTest.php index 7ea579cdac..6be2e434ba 100644 --- a/tests/StaticCaching/ApplicationCacherTest.php +++ b/tests/StaticCaching/ApplicationCacherTest.php @@ -42,6 +42,7 @@ public function gets_cached_page() $cachedPage = $cacher->getCachedPage($request); $this->assertEquals('html content', $cachedPage->content); $this->assertEquals('application/html', $cachedPage->headers['Content-Type']); + $this->assertEquals(200, $cachedPage->status); } /** @test */