Skip to content

Commit e7aa611

Browse files
fix: Invalidate the request URL even when the response has no headers (#153)
A response cut short by a timeout or a connection reset can reach the delete path with nil headers, and reading Location out of it raised NoMethodError inside on_complete. Fall back to an empty header set so the request URL is still invalidated. Fixes #142
1 parent d7e3e91 commit e7aa611

2 files changed

Lines changed: 32 additions & 3 deletions

File tree

‎lib/faraday/http_cache.rb‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -314,9 +314,11 @@ def authorization_bearing?
314314
end
315315

316316
def delete(request, response)
317-
headers = %w[Location Content-Location]
318-
headers.each do |header|
319-
url = response.headers[header]
317+
# A response cut short by a timeout or a reset can arrive without
318+
# headers; there is still an entry to invalidate for the request URL.
319+
headers = response.headers || {}
320+
%w[Location Content-Location].each do |header|
321+
url = headers[header]
320322
@strategy.delete(url) if url
321323
end
322324

‎spec/http_cache_spec.rb‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,33 @@
9696
client.get('broken')
9797
end
9898

99+
it 'still expires the request URL when the response has no headers' do
100+
store = Faraday::HttpCache::MemoryStore.new
101+
cached = Faraday.new(url: ENV['FARADAY_SERVER']) do |stack|
102+
stack.use Faraday::HttpCache, store: store
103+
stack.adapter ENV['FARADAY_ADAPTER'].to_sym
104+
end
105+
# Mimics an adapter that gives up mid-response: the env is completed
106+
# with no status worth the name and no response headers at all.
107+
headerless_adapter = Class.new(Faraday::Adapter) do
108+
def call(env)
109+
super
110+
env.status = 0
111+
env.response_headers = nil
112+
env.response.finish(env)
113+
end
114+
end
115+
broken = Faraday.new(url: ENV['FARADAY_SERVER']) do |stack|
116+
stack.use Faraday::HttpCache, store: store
117+
stack.adapter headerless_adapter
118+
end
119+
120+
cached.get('get')
121+
broken.post('get')
122+
123+
expect(cached.get('get').body).to eq('2')
124+
end
125+
99126
it 'expires entries for the "Location" header' do
100127
client.get('get')
101128
client.post('delete-with-location')

0 commit comments

Comments
 (0)