lib/idempo.rb in idempo-1.2.1 vs lib/idempo.rb in idempo-1.2.2

- old
+ new

@@ -5,10 +5,11 @@ require "json" require "measurometer" require "msgpack" require "zlib" require "set" +require "rack" require "idempo/version" class Idempo autoload :ConcurrentRequestErrorApp, "idempo/concurrent_request_error_app" @@ -55,10 +56,16 @@ end status, headers, body = @app.call(env) 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. + # + # @see https://github.com/rack/rack/blob/main/SPEC.rdoc#the-body- + body = body.to_ary if rack_v3? && 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) end @@ -74,10 +81,14 @@ @concurrent_request_error_app.call(env) end private + def rack_v3? + Gem::Version.new(Rack.release) >= Gem::Version.new("3.0") + end + def from_persisted_response(marshaled_response) if marshaled_response[-2..] != ":1" raise Error, "Unknown serialization of the marshaled response" else MessagePack.unpack(Zlib.inflate(marshaled_response[0..-3])) @@ -102,9 +113,10 @@ # Add the version specifier at the end, because slicing a string in Ruby at the end # (when we unserialize our response again) does a realloc, while slicing at the start # does not [deflated_message_packed_str, body_chunks] ensure + # 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"