From 5defbd745dc41577e408d9714b137b056c3bd807 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Mon, 11 Dec 2017 19:24:14 +0100 Subject: [PATCH 1/3] Add an HTTP cache middleware --- src/Illuminate/Http/Middleware/Cache.php | 49 ++++++++++++++++++++++++ tests/Http/Middleware/CacheTest.php | 44 +++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/Illuminate/Http/Middleware/Cache.php create mode 100644 tests/Http/Middleware/CacheTest.php diff --git a/src/Illuminate/Http/Middleware/Cache.php b/src/Illuminate/Http/Middleware/Cache.php new file mode 100644 index 000000000000..e9beaf798329 --- /dev/null +++ b/src/Illuminate/Http/Middleware/Cache.php @@ -0,0 +1,49 @@ +isMethodCacheable() || ! $response->getContent()) { + return $response; + } + + if (! $response->getContent()) { + return; + } + if ($etag) { + $response->setEtag(md5($response->getContent())); + } + if (null !== $maxAge) { + $response->setMaxAge($maxAge); + } + if (null !== $sharedMaxAge) { + $response->setSharedMaxAge($sharedMaxAge); + } + if (null !== $public) { + $public ? $response->setPublic() : $response->setPrivate(); + } + + return $response; + } +} diff --git a/tests/Http/Middleware/CacheTest.php b/tests/Http/Middleware/CacheTest.php new file mode 100644 index 000000000000..eb5268d75aac --- /dev/null +++ b/tests/Http/Middleware/CacheTest.php @@ -0,0 +1,44 @@ +setMethod('PUT'); + + $response = (new Cache())->handle($request, function () { + return new Response('Hello Laravel'); + }, 1, 2, true, true); + + $this->assertNull($response->getMaxAge()); + $this->assertNull($response->getEtag()); + } + + public function testDoNotSetHeaderWhenNoContent() + { + $response = (new Cache())->handle(new Request(), function () { + return new Response(); + }, 1, 2, true, true); + + $this->assertNull($response->getMaxAge()); + $this->assertNull($response->getEtag()); + } + + public function testAddHeaders() + { + $response = (new Cache())->handle(new Request(), function () { + return new Response('some content'); + }, 100, 200, true, true); + + $this->assertSame('"9893532233caff98cd083a116b013c0b"', $response->getEtag()); + $this->assertSame('max-age=100, public, s-maxage=200', $response->headers->get('Cache-Control')); + } +} From 6a1480adb29644036102f92e8a20e983f5b96f61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Tue, 12 Dec 2017 09:56:46 +0100 Subject: [PATCH 2/3] Rely on setCache --- src/Illuminate/Http/Middleware/Cache.php | 38 +++++++++--------- tests/Http/Middleware/CacheTest.php | 49 ++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 24 deletions(-) diff --git a/src/Illuminate/Http/Middleware/Cache.php b/src/Illuminate/Http/Middleware/Cache.php index e9beaf798329..9af851365656 100644 --- a/src/Illuminate/Http/Middleware/Cache.php +++ b/src/Illuminate/Http/Middleware/Cache.php @@ -11,39 +11,37 @@ class Cache * * @param \Illuminate\Http\Request $request * @param \Closure $next - * @param int|null $maxAge - * @param int|null $sharedMaxAge - * @param bool|null $public - * @param bool|null $etag + * @param string|array $options * @return \Symfony\Component\HttpFoundation\Response + * @throws \InvalidArgumentException */ - public function handle($request, Closure $next, int $maxAge = null, int $sharedMaxAge = null, bool $public = null, bool $etag = false) + public function handle($request, Closure $next, $options = []) { /** - * @var \Symfony\Component\HttpFoundation\Response + * @var $response \Symfony\Component\HttpFoundation\Response */ $response = $next($request); - if (! $request->isMethodCacheable() || ! $response->getContent()) { return $response; } - if (! $response->getContent()) { - return; - } - if ($etag) { - $response->setEtag(md5($response->getContent())); - } - if (null !== $maxAge) { - $response->setMaxAge($maxAge); - } - if (null !== $sharedMaxAge) { - $response->setSharedMaxAge($sharedMaxAge); + if (\is_string($options)) { + $parsedOptions = []; + foreach (explode(';', $options) as $opt) { + $data = explode('=', $opt, 2); + $parsedOptions[$data[0]] = $data[1] ?? true; + } + + $options = $parsedOptions; } - if (null !== $public) { - $public ? $response->setPublic() : $response->setPrivate(); + + if (true === ($options['etag'] ?? false)) { + $options['etag'] = md5($response->getContent()); } + $response->setCache($options); + $response->isNotModified($request); + return $response; } } diff --git a/tests/Http/Middleware/CacheTest.php b/tests/Http/Middleware/CacheTest.php index eb5268d75aac..6d865e973bdc 100644 --- a/tests/Http/Middleware/CacheTest.php +++ b/tests/Http/Middleware/CacheTest.php @@ -16,17 +16,16 @@ public function testDoNotSetHeaderWhenMethodNotCacheable() $response = (new Cache())->handle($request, function () { return new Response('Hello Laravel'); - }, 1, 2, true, true); + }, 'max_age=120;s_maxage=60'); $this->assertNull($response->getMaxAge()); - $this->assertNull($response->getEtag()); } public function testDoNotSetHeaderWhenNoContent() { $response = (new Cache())->handle(new Request(), function () { return new Response(); - }, 1, 2, true, true); + }, 'max_age=120;s_maxage=60'); $this->assertNull($response->getMaxAge()); $this->assertNull($response->getEtag()); @@ -36,9 +35,51 @@ public function testAddHeaders() { $response = (new Cache())->handle(new Request(), function () { return new Response('some content'); - }, 100, 200, true, true); + }, 'max_age=100;s_maxage=200;etag=ABC'); + + $this->assertSame('"ABC"', $response->getEtag()); + $this->assertSame('max-age=100, public, s-maxage=200', $response->headers->get('Cache-Control')); + } + + public function testAddHeadersUsingArray() + { + $response = (new Cache())->handle(new Request(), function () { + return new Response('some content'); + }, ['max_age' => 100, 's_maxage' => 200, 'etag' => 'ABC']); + + $this->assertSame('"ABC"', $response->getEtag()); + $this->assertSame('max-age=100, public, s-maxage=200', $response->headers->get('Cache-Control')); + } + + public function testGenerateEtag() + { + $response = (new Cache())->handle(new Request(), function () { + return new Response('some content'); + }, 'etag;max_age=100;s_maxage=200'); $this->assertSame('"9893532233caff98cd083a116b013c0b"', $response->getEtag()); $this->assertSame('max-age=100, public, s-maxage=200', $response->headers->get('Cache-Control')); } + + public function testIsNotModified() + { + $request = new Request(); + $request->headers->set('If-None-Match', '"9893532233caff98cd083a116b013c0b"'); + + $response = (new Cache())->handle($request, function () { + return new Response('some content'); + }, 'etag;max_age=100;s_maxage=200'); + + $this->assertSame(304, $response->getStatusCode()); + } + + /** + * @expectedException \InvalidArgumentException + */ + public function testInvalidOption() + { + (new Cache())->handle(new Request(), function () { + return new Response('some content'); + }, 'invalid'); + } } From 9d6001b8280bdfc7c191e1ac3d2b2a5ee7413c2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?K=C3=A9vin=20Dunglas?= Date: Tue, 12 Dec 2017 10:01:02 +0100 Subject: [PATCH 3/3] Fix bug --- src/Illuminate/Http/Middleware/Cache.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Illuminate/Http/Middleware/Cache.php b/src/Illuminate/Http/Middleware/Cache.php index 9af851365656..2910cdca5bbd 100644 --- a/src/Illuminate/Http/Middleware/Cache.php +++ b/src/Illuminate/Http/Middleware/Cache.php @@ -35,7 +35,7 @@ public function handle($request, Closure $next, $options = []) $options = $parsedOptions; } - if (true === ($options['etag'] ?? false)) { + if (isset($options['etag']) && true === $options['etag']) { $options['etag'] = md5($response->getContent()); }