diff --git a/lib/faraday/http_cache.rb b/lib/faraday/http_cache.rb index 3e4f070..ffa7448 100644 --- a/lib/faraday/http_cache.rb +++ b/lib/faraday/http_cache.rb @@ -314,9 +314,11 @@ def authorization_bearing? end def delete(request, response) - headers = %w[Location Content-Location] - headers.each do |header| - url = response.headers[header] + # A response cut short by a timeout or a reset can arrive without + # headers; there is still an entry to invalidate for the request URL. + headers = response.headers || {} + %w[Location Content-Location].each do |header| + url = headers[header] @strategy.delete(url) if url end diff --git a/spec/http_cache_spec.rb b/spec/http_cache_spec.rb index 1a12270..6e69218 100644 --- a/spec/http_cache_spec.rb +++ b/spec/http_cache_spec.rb @@ -96,6 +96,33 @@ client.get('broken') end + it 'still expires the request URL when the response has no headers' do + store = Faraday::HttpCache::MemoryStore.new + cached = Faraday.new(url: ENV['FARADAY_SERVER']) do |stack| + stack.use Faraday::HttpCache, store: store + stack.adapter ENV['FARADAY_ADAPTER'].to_sym + end + # Mimics an adapter that gives up mid-response: the env is completed + # with no status worth the name and no response headers at all. + headerless_adapter = Class.new(Faraday::Adapter) do + def call(env) + super + env.status = 0 + env.response_headers = nil + env.response.finish(env) + end + end + broken = Faraday.new(url: ENV['FARADAY_SERVER']) do |stack| + stack.use Faraday::HttpCache, store: store + stack.adapter headerless_adapter + end + + cached.get('get') + broken.post('get') + + expect(cached.get('get').body).to eq('2') + end + it 'expires entries for the "Location" header' do client.get('get') client.post('delete-with-location')