diff options
author | Jeremy Evans <code@jeremyevans.net> | 2023-03-20 18:09:07 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2023-03-20 18:09:07 -0700 |
commit | 8fad7b1f51cfe2985f7d875cdd21c72d1ccb536a (patch) | |
tree | 35badbad22a04bc0e41e91906fdb467a641b16c7 | |
parent | 8483b1034de7c9b18201f60fe8735fc83d32ba21 (diff) | |
download | rack-8fad7b1f51cfe2985f7d875cdd21c72d1ccb536a.tar.gz |
Do not allow BodyProxy to respond to to_str, make to_ary call close
See https://github.com/rack/rack-test/issues/335 for an example
where allowing BodyProxy to respond to to_str (when provided an
invalid rack body) complicated debugging.
Call BodyProxy#close if BodyProxy#to_ary is called, as not doing so
violates SPEC.
-rw-r--r-- | lib/rack/body_proxy.rb | 20 | ||||
-rw-r--r-- | test/spec_body_proxy.rb | 36 |
2 files changed, 48 insertions, 8 deletions
diff --git a/lib/rack/body_proxy.rb b/lib/rack/body_proxy.rb index fbb344b8..72915798 100644 --- a/lib/rack/body_proxy.rb +++ b/lib/rack/body_proxy.rb @@ -15,7 +15,12 @@ module Rack # Return whether the wrapped body responds to the method. def respond_to_missing?(method_name, include_all = false) - super or @body.respond_to?(method_name, include_all) + case method_name + when :to_str + false + else + super or @body.respond_to?(method_name, include_all) + end end # If not already closed, close the wrapped body and @@ -38,7 +43,18 @@ module Rack # Delegate missing methods to the wrapped body. def method_missing(method_name, *args, &block) - @body.__send__(method_name, *args, &block) + case method_name + when :to_str + super + when :to_ary + begin + @body.__send__(method_name, *args, &block) + ensure + close + end + else + @body.__send__(method_name, *args, &block) + end end # :nocov: ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true) diff --git a/test/spec_body_proxy.rb b/test/spec_body_proxy.rb index 91fcbd74..c003713a 100644 --- a/test/spec_body_proxy.rb +++ b/test/spec_body_proxy.rb @@ -74,13 +74,37 @@ describe Rack::BodyProxy do proxy.banana(foo: 1).must_equal 1 end - it 'not respond to :to_ary' do - body = Object.new.tap { |o| def o.to_ary() end } - body.respond_to?(:to_ary).must_equal true + it 'respond to :to_ary if body does responds to it, and have to_ary call close' do + proxy_closed = false + proxy = Rack::BodyProxy.new([]) { proxy_closed = true } + proxy.respond_to?(:to_ary).must_equal true + proxy_closed.must_equal false + proxy.to_ary.must_equal [] + proxy_closed.must_equal true + end - proxy = Rack::BodyProxy.new(body) { } - x = [proxy] - assert_equal x, x.flatten + it 'not respond to :to_ary if body does not respond to it' do + proxy = Rack::BodyProxy.new([].map) { } + proxy.respond_to?(:to_ary).must_equal false + proc do + proxy.to_ary + end.must_raise NoMethodError + end + + it 'not respond to :to_str' do + proxy = Rack::BodyProxy.new("string body") { } + proxy.respond_to?(:to_str).must_equal false + proc do + proxy.to_str + end.must_raise NoMethodError + end + + it 'not respond to :to_path if body does not respond to it' do + proxy = Rack::BodyProxy.new("string body") { } + proxy.respond_to?(:to_path).must_equal false + proc do + proxy.to_path + end.must_raise NoMethodError end it 'not close more than one time' do |