summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Evans <code@jeremyevans.net>2023-03-20 18:09:07 -0700
committerGitHub <noreply@github.com>2023-03-20 18:09:07 -0700
commit8fad7b1f51cfe2985f7d875cdd21c72d1ccb536a (patch)
tree35badbad22a04bc0e41e91906fdb467a641b16c7
parent8483b1034de7c9b18201f60fe8735fc83d32ba21 (diff)
downloadrack-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.rb20
-rw-r--r--test/spec_body_proxy.rb36
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