From c94a00757dac150b17d9272b72288217c66f0a2d Mon Sep 17 00:00:00 2001 From: Daniel Schierbeck Date: Wed, 13 Jun 2018 18:08:24 +0200 Subject: Add support for more HTTP cache controls From : > The stale-if-error HTTP Cache-Control extension allows a cache to > return a stale response when an error -- e.g., a 500 Internal Server > Error, a network segment, or DNS failure -- is encountered, rather > than returning a "hard" error. This improves availability. > > The stale-while-revalidate HTTP Cache-Control extension allows a > cache to immediately return a stale response while it revalidates it > in the background, thereby hiding latency (both in the network and on > the server) from clients. These are useful, fully standardized parts of the HTTP protocol with widespread support among CDN vendors. Supporting them will make it easier to utilize reverse proxies and CDNs from Rails. --- .../lib/action_controller/metal/conditional_get.rb | 4 +++- actionpack/lib/action_dispatch/http/cache.rb | 6 +++++- actionpack/test/controller/render_test.rb | 20 ++++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index 06b6a95ff8..4be4557e2c 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -235,7 +235,9 @@ module ActionController response.cache_control.merge!( max_age: seconds, public: options.delete(:public), - must_revalidate: options.delete(:must_revalidate) + must_revalidate: options.delete(:must_revalidate), + stale_while_revalidate: options.delete(:stale_while_revalidate), + stale_if_error: options.delete(:stale_if_error), ) options.delete(:private) diff --git a/actionpack/lib/action_dispatch/http/cache.rb b/actionpack/lib/action_dispatch/http/cache.rb index a8febc32b3..a7c7cfc1e5 100644 --- a/actionpack/lib/action_dispatch/http/cache.rb +++ b/actionpack/lib/action_dispatch/http/cache.rb @@ -202,13 +202,17 @@ module ActionDispatch self._cache_control = _cache_control + ", #{control[:extras].join(', ')}" end else - extras = control[:extras] + extras = control[:extras] max_age = control[:max_age] + stale_while_revalidate = control[:stale_while_revalidate] + stale_if_error = control[:stale_if_error] options = [] options << "max-age=#{max_age.to_i}" if max_age options << (control[:public] ? PUBLIC : PRIVATE) options << MUST_REVALIDATE if control[:must_revalidate] + options << "stale-while-revalidate=#{stale_while_revalidate.to_i}" if stale_while_revalidate + options << "stale-if-error=#{stale_if_error.to_i}" if stale_if_error options.concat(extras) if extras self._cache_control = options.join(", ") diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 24c5761e41..6e3bd0596b 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -141,6 +141,16 @@ class TestController < ActionController::Base render action: "hello_world" end + def conditional_hello_with_expires_in_with_stale_while_revalidate + expires_in 1.minute, public: true, stale_while_revalidate: 5.minutes + render action: "hello_world" + end + + def conditional_hello_with_expires_in_with_stale_if_error + expires_in 1.minute, public: true, stale_if_error: 5.minutes + render action: "hello_world" + end + def conditional_hello_with_expires_in_with_public_with_more_keys expires_in 1.minute, :public => true, "s-maxage" => 5.hours render action: "hello_world" @@ -358,6 +368,16 @@ class ExpiresInRenderTest < ActionController::TestCase assert_equal "max-age=60, public, must-revalidate", @response.headers["Cache-Control"] end + def test_expires_in_header_with_stale_while_revalidate + get :conditional_hello_with_expires_in_with_stale_while_revalidate + assert_equal "max-age=60, public, stale-while-revalidate=300", @response.headers["Cache-Control"] + end + + def test_expires_in_header_with_stale_if_error + get :conditional_hello_with_expires_in_with_stale_if_error + assert_equal "max-age=60, public, stale-if-error=300", @response.headers["Cache-Control"] + end + def test_expires_in_header_with_additional_headers get :conditional_hello_with_expires_in_with_public_with_more_keys assert_equal "max-age=60, public, s-maxage=18000", @response.headers["Cache-Control"] -- cgit v1.2.3