lib/idempo.rb in idempo-1.2.2 vs lib/idempo.rb in idempo-1.3.0
- old
+ new
@@ -53,18 +53,20 @@
if (stored_response = store.lookup)
Measurometer.increment_counter("idempo.responses_served_from", 1, from: "store")
return from_persisted_response(stored_response)
end
- status, headers, body = @app.call(env)
+ status, raw_headers, body = @app.call(env)
+ headers = downcase_keys(raw_headers)
- expires_in_seconds = (headers.delete("X-Idempo-Persist-For-Seconds") || @persist_for_seconds).to_i
+ expires_in_seconds = (headers.delete("x-idempo-persist-for-seconds") || @persist_for_seconds).to_i
- # In some cases `body` could respond to to_ary. In this case, we don't need to call .close on body afterwards.
+ # In some cases `body` could respond to to_ary. In this case, we don't need to
+ # call .close on the body afterwards, as it is supposed to self-close as per Rack 3.0 SPEC
#
# @see https://github.com/rack/rack/blob/main/SPEC.rdoc#the-body-
- body = body.to_ary if rack_v3? && body.respond_to?(:to_ary)
+ body = body.to_ary if body.respond_to?(:to_ary)
if response_may_be_persisted?(status, headers, body)
# Body is replaced with a cached version since a Rack response body is not rewindable
marshaled_response, body = serialize_response(status, headers, body)
store.store(data: marshaled_response, ttl: expires_in_seconds)
@@ -81,12 +83,14 @@
@concurrent_request_error_app.call(env)
end
private
- def rack_v3?
- Gem::Version.new(Rack.release) >= Gem::Version.new("3.0")
+ def downcase_keys(raw_headers)
+ raw_headers.each_with_object({}) do |(name, value), hh|
+ hh[name.to_s.downcase] = value
+ end
end
def from_persisted_response(marshaled_response)
if marshaled_response[-2..] != ":1"
raise Error, "Unknown serialization of the marshaled response"
@@ -118,20 +122,21 @@
# This will not be applied to response bodies of Array type.
rack_response_body.close if rack_response_body.respond_to?(:close)
end
def response_may_be_persisted?(status, headers, body)
- return false if headers.delete("X-Idempo-Policy") == "no-store"
+ return false if headers.delete("x-idempo-policy") == "no-store"
return false unless status_may_be_persisted?(status)
return false unless body_size_within_limit?(headers, body)
true
end
def body_size_within_limit?(response_headers, body)
- return response_headers["Content-Length"].to_i <= SAVED_RESPONSE_BODY_SIZE_LIMIT if response_headers["Content-Length"]
+ if response_headers["content-length"]
+ return response_headers["content-length"].to_i <= SAVED_RESPONSE_BODY_SIZE_LIMIT
+ end
return false unless body.is_a?(Array) # Arbitrary iterable of unknown size
-
sum_of_string_bytesizes(body) <= SAVED_RESPONSE_BODY_SIZE_LIMIT
end
def status_may_be_persisted?(status)
case status