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