diff --git a/lib/protocol/http/header/cache_control.rb b/lib/protocol/http/header/cache_control.rb index c3f1579..7555328 100644 --- a/lib/protocol/http/header/cache_control.rb +++ b/lib/protocol/http/header/cache_control.rb @@ -2,6 +2,7 @@ # Released under the MIT License. # Copyright, 2020-2023, by Samuel Williams. +# Copyright, 2023, by Thomas Morgan. require_relative 'split' @@ -14,11 +15,15 @@ class CacheControl < Split NO_CACHE = 'no-cache' NO_STORE = 'no-store' MAX_AGE = 'max-age' + S_MAXAGE = 's-maxage' STATIC = 'static' DYNAMIC = 'dynamic' STREAMING = 'streaming' + MUST_REVALIDATE = 'must-revalidate' + PROXY_REVALIDATE = 'proxy-revalidate' + def initialize(value = nil) super(value&.downcase) end @@ -55,11 +60,40 @@ def no_store? self.include?(NO_STORE) end + # Indicates that a response must not be used once it is stale. + # See https://www.rfc-editor.org/rfc/rfc9111.html#name-must-revalidate + def must_revalidate? + self.include?(MUST_REVALIDATE) + end + + # Like must-revalidate, but for shared caches only. + # See https://www.rfc-editor.org/rfc/rfc9111.html#name-proxy-revalidate + def proxy_revalidate? + self.include?(PROXY_REVALIDATE) + end + + # The maximum time, in seconds, a response should be considered fresh. + # See https://www.rfc-editor.org/rfc/rfc9111.html#name-max-age-2 def max_age - if value = self.find{|value| value.start_with?(MAX_AGE)} + find_integer_value(MAX_AGE) + end + + # Like max-age, but for shared caches only, which should use it before + # max-age when present. + # See https://www.rfc-editor.org/rfc/rfc9111.html#name-s-maxage + def s_maxage + find_integer_value(S_MAXAGE) + end + + private + + def find_integer_value(value_name) + if value = self.find{|value| value.start_with?(value_name)} _, age = value.split('=', 2) - return Integer(age) + if age =~ /\A[0-9]+\z/ + return Integer(age) + end end end end diff --git a/test/protocol/http/header/cache_control.rb b/test/protocol/http/header/cache_control.rb index 0997bc0..ebe3bd3 100644 --- a/test/protocol/http/header/cache_control.rb +++ b/test/protocol/http/header/cache_control.rb @@ -2,18 +2,29 @@ # Released under the MIT License. # Copyright, 2023, by Samuel Williams. +# Copyright, 2023, by Thomas Morgan. require 'protocol/http/header/cache_control' describe Protocol::HTTP::Header::CacheControl do let(:header) {subject.new(description)} - with "max-age=60, public" do + with "max-age=60, s-maxage=30, public" do it "correctly parses cache header" do expect(header).to have_attributes( public?: be == true, private?: be == false, max_age: be == 60, + s_maxage: be == 30, + ) + end + end + + with "max-age=-10, s-maxage=0x22" do + it "gracefully handles invalid values" do + expect(header).to have_attributes( + max_age: be == nil, + s_maxage: be == nil, ) end end @@ -51,6 +62,22 @@ end end + with "must-revalidate" do + it "correctly parses cache header" do + expect(header).to have_attributes( + must_revalidate?: be == true, + ) + end + end + + with "proxy-revalidate" do + it "correctly parses cache header" do + expect(header).to have_attributes( + proxy_revalidate?: be == true, + ) + end + end + with "#<<" do let(:header) {subject.new}