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"