From df6c63f2635e380f35a93103e26d719e293dbc59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?George=20Guimar=C3=A3es?= Date: Tue, 15 Sep 2026 18:28:50 -0300 Subject: [PATCH] fix: Ignore max-age and s-maxage directives without a value A bare max-age or s-maxage parses as true and calling to_i on it raised NoMethodError, the same crash #144 fixed for stale-while-revalidate. Read all three integer directives through one helper that treats a valueless directive as absent, including in normalize_max_ages. --- lib/faraday/http_cache/cache_control.rb | 31 +++++++++++++++---------- spec/cache_control_spec.rb | 17 ++++++++++++++ 2 files changed, 36 insertions(+), 12 deletions(-) diff --git a/lib/faraday/http_cache/cache_control.rb b/lib/faraday/http_cache/cache_control.rb index 1b05de7..4d514bb 100644 --- a/lib/faraday/http_cache/cache_control.rb +++ b/lib/faraday/http_cache/cache_control.rb @@ -34,9 +34,9 @@ def no_store? # Internal: Gets the 'max-age' directive as an Integer. # - # Returns nil if the 'max-age' directive isn't present. + # Returns nil if the 'max-age' directive isn't present or has no value. def max_age - @directives['max-age'].to_i if @directives.key?('max-age') + integer_directive('max-age') end # Internal: Gets the 'max-age' directive as an Integer. @@ -45,16 +45,16 @@ def max_age # if present to account for having to remove static age header when caching responses def normalize_max_ages(age) if age > 0 - @directives['max-age'] = @directives['max-age'].to_i - age if @directives.key?('max-age') - @directives['s-maxage'] = @directives['s-maxage'].to_i - age if @directives.key?('s-maxage') + @directives['max-age'] = max_age - age if max_age + @directives['s-maxage'] = shared_max_age - age if shared_max_age end end # Internal: Gets the 's-maxage' directive as an Integer. # - # Returns nil if the 's-maxage' directive isn't present. + # Returns nil if the 's-maxage' directive isn't present or has no value. def shared_max_age - @directives['s-maxage'].to_i if @directives.key?('s-maxage') + integer_directive('s-maxage') end alias s_maxage shared_max_age @@ -70,13 +70,10 @@ def proxy_revalidate? # Internal: Gets the 'stale-while-revalidate' directive as an Integer. # - # Returns nil if the 'stale-while-revalidate' directive isn't present, or - # assigned as boolean. + # Returns nil if the 'stale-while-revalidate' directive isn't present or + # has no value. def stale_while_revalidate - return unless @directives.key?('stale-while-revalidate') - return if @directives['stale-while-revalidate'] == true - - @directives['stale-while-revalidate'].to_i + integer_directive('stale-while-revalidate') end # Internal: Gets the String representation for the cache directives. @@ -102,6 +99,16 @@ def to_s private + # Internal: Reads a directive whose value must be an integer. + # A directive given without a value (a bare 'max-age') is parsed as + # true; treat it as absent instead of calling to_i on it. + # + # Returns the Integer value, or nil. + def integer_directive(name) + value = @directives[name] + value.to_i unless value.nil? || value == true + end + # Internal: Parses the Cache Control string to a Hash. # Existing whitespace will be removed and the string is split on commas. # For each part everything before a '=' will be treated as the key diff --git a/spec/cache_control_spec.rb b/spec/cache_control_spec.rb index a6fa284..6029729 100644 --- a/spec/cache_control_spec.rb +++ b/spec/cache_control_spec.rb @@ -52,6 +52,23 @@ expect(cache_control.shared_max_age).to eq(600) end + it 'responds to #max_age with nil when the max-age directive has no value' do + cache_control = Faraday::HttpCache::CacheControl.new('public, max-age') + expect(cache_control.max_age).to be_nil + end + + it 'responds to #shared_max_age with nil when the s-maxage directive has no value' do + cache_control = Faraday::HttpCache::CacheControl.new('public, s-maxage') + expect(cache_control.shared_max_age).to be_nil + end + + it 'normalizes max ages without raising when a directive has no value' do + cache_control = Faraday::HttpCache::CacheControl.new('max-age, s-maxage=600') + cache_control.normalize_max_ages(100) + expect(cache_control.max_age).to be_nil + expect(cache_control.shared_max_age).to eq(500) + end + it 'responds to #shared_max_age with nil when no s-maxage directive present' do cache_control = Faraday::HttpCache::CacheControl.new('public') expect(cache_control.shared_max_age).to be_nil