httpx/test/support/response_cache_store_tests.rb
HoneyryderChuck 89fcd2eea4 fix: ensure that cached gzip-encoded responses don't try to unzip again
when the response is rebuilt from cache, bypass decoding logic, as responses are cached in plaintext

Fixes https://github.com/HoneyryderChuck/httpx/issues/109
2025-10-17 15:29:05 +01:00

199 lines
7.3 KiB
Ruby

# frozen_string_literal: true
module ResponseCacheStoreTests
include HTTPX
def test_store_cache
request = make_request("GET", "http://store-cache/")
response = cached_response(request)
cached_response = store.get(request)
assert cached_response.headers == response.headers
assert cached_response.body == response.body
assert store.get(request)
request2 = make_request("GET", "http://store-cache/")
cached_response2 = store.get(request2)
assert cached_response2
assert cached_response2.headers == response.headers
assert cached_response2.body == response.body
request3 = make_request("POST", "http://store-cache/")
assert store.get(request3).nil?
request4 = make_request("GET", "http://store-cache/", params: { "foo" => "bar" })
assert store.get(request4).nil?
end
def test_store_cache_compressed
body = "a" * 8012
gzip_body = Zlib.gzip(body)
request = make_request("GET", "http://store-gzip/", headers: { "accept-encoding" => "gzip" })
response = cached_response(
request,
extra_headers: { "content-encoding" => "gzip", "content-length" => gzip_body.size.to_s },
body: gzip_body
)
cached_response = store.get(request)
assert cached_response.headers == response.headers
assert cached_response.body == body
assert store.get(request)
end
def test_store_prepare_maxage
request = make_request("GET", "http://prepare-maxage/")
response = cached_response(request, extra_headers: { "cache-control" => "max-age=2" })
assert request.response.nil?
prepare(request)
assert request.response
assert request.response.headers == response.headers
assert request.response.body == response.body
assert request.cached_response.nil?
request.instance_variable_set(:@response, nil)
sleep(3)
prepare(request)
assert request.cached_response
assert request.cached_response.headers == response.headers
assert request.cached_response.body == response.body
assert request.response.nil?
request2 = make_request("GET", "http://prepare-cache-2.com/")
cached_response(request2, extra_headers: { "cache-control" => "no-cache, max-age=2" })
prepare(request2)
assert request2.response.nil?
end
def test_store_prepare_expires
request = make_request("GET", "http://prepare-expires/")
response = cached_response(request, extra_headers: { "expires" => (Time.now + 5).httpdate })
assert request.response.nil?
prepare(request)
assert request.response
assert request.response.headers == response.headers
assert request.response.body == response.body
assert request.cached_response.nil?
request.instance_variable_set(:@response, nil)
sleep(6)
prepare(request)
assert request.cached_response
assert request.cached_response.headers == response.headers
assert request.cached_response.body == response.body
assert request.response.nil?
request2 = make_request("GET", "http://prepare-expires-2/")
cached_response(request2, extra_headers: { "cache-control" => "no-cache", "expires" => (Time.now + 2).httpdate })
prepare(request2)
assert request2.response.nil?
request_invalid_expires = make_request("GET", "http://prepare-expires-3/")
_invalid_expires_response = cached_response(request_invalid_expires, extra_headers: { "expires" => "smthsmth" })
prepare(request_invalid_expires)
assert request_invalid_expires.response.nil?
end
def test_store_prepare_invalid_date
request_invalid_age = make_request("GET", "http://prepare-expires-4/")
response_invalid_age = cached_response(request_invalid_age, extra_headers: { "cache-control" => "max-age=2", "date" => "smthsmth" })
prepare(request_invalid_age)
assert request_invalid_age.response
assert request_invalid_age.response.headers == response_invalid_age.headers
assert request_invalid_age.response.body == response_invalid_age.body
end
def test_prepare_vary
request = make_request("GET", "http://prepare-vary/", headers: { "accept" => "text/plain" })
response = cached_response(request, extra_headers: { "vary" => "Accept" })
request2 = make_request("GET", "http://prepare-vary/", headers: { "accept" => "text/html" })
prepare(request2)
assert !request2.headers.key?("if-none-match")
assert request2.cached_response.nil?
request3 = make_request("GET", "http://prepare-vary/", headers: { "accept" => "text/plain" })
prepare(request3)
assert request3.cached_response
assert request3.cached_response.headers == response.headers
assert request3.cached_response.body == response.body
assert request3.headers.key?("if-none-match")
request4 = make_request("GET", "http://prepare-vary/", headers: { "accept" => "text/plain", "user-agent" => "Linux Bowser" })
prepare(request4)
assert request4.cached_response
assert request4.cached_response.headers == response.headers
assert request4.cached_response.body == response.body
assert request4.headers.key?("if-none-match")
end
def test_prepare_vary_asterisk
request = make_request("GET", "http://prepare-vary-asterisk/", headers: { "accept" => "text/plain" })
response = cached_response(request, extra_headers: { "vary" => "*" })
request2 = make_request("GET", "http://prepare-vary-asterisk/", headers: { "accept" => "text/html" })
prepare(request2)
assert request.cached_response.nil?
assert !request2.headers.key?("if-none-match")
request3 = make_request("GET", "http://prepare-vary-asterisk/", headers: { "accept" => "text/plain" })
prepare(request3)
assert request3.cached_response
assert request3.cached_response.headers == response.headers
assert request3.cached_response.body == response.body
assert request3.headers.key?("if-none-match")
request4 = make_request("GET", "http://prepare-vary-asterisk/", headers: { "accept" => "text/plain", "accept-language" => "en" })
prepare(request4)
assert request4.cached_response.nil?
assert !request4.headers.key?("if-none-match")
end
private
def teardown
response_cache_session.clear_response_cache
end
def request_class
@request_class ||= response_cache_session_options.request_class
end
def response_class
@response_class ||= response_cache_session_options.response_class
end
def options_class
@options_class ||= response_cache_session_optionsoptions_class
end
def response_cache_session_options
@response_cache_session_options ||= response_cache_session.class.default_options
end
def response_cache_session
@response_cache_session ||= HTTPX.plugin(:response_cache, response_cache_store: init_store)
end
def make_request(meth, uri, *args)
response_cache_session.build_request(meth, uri, *args)
end
def init_store
raise NotImplementedError, "must define a `init_store` method returning the cache store to use"
end
def store
response_cache_session.class.default_options.response_cache_store
end
def cached_response(request, status: 200, extra_headers: {}, body: "test")
response = response_class.new(request, status, "2.0", { "date" => Time.now.httpdate, "etag" => "ETAG" }.merge(extra_headers))
response.body.write(body)
store.set(request, response)
response
end
def prepare(request)
response_cache_session.send(:prepare_cache, request)
end
end