Skip to content
This repository

Infinite loop resulting in stack level too deep in Sinatra::Response#finish #612

Closed
wants to merge 1 commit into from

3 participants

Gheorghita Catalin Bordianu Konstantin Haase Topper Bowers
Gheorghita Catalin Bordianu

If the call to super on line 86 returns a Rack::BodyProxy instance inside result, the equality comparison on line 87 fails. The end result will be an infinite loop when calling #each on body, which will cause a stack overflow.

Rather than add || Rack::BodyProxy === result on line 86, I suggest we overload the "==" operator in Sinatra::Response, to perform compare instances based solely on the status, headers, body and length attribute (of course, the comparison on line 86 needs to be flipped to self == result).

Konstantin Haase rkh commented on the diff January 13, 2013
lib/sinatra/base.rb
((6 lines not shown))
88 88
       [status, headers, result]
89 89
     end
  90
+    
  91
+    def ==(other)
  92
+      [:status, :headers, :body, :length].each do |attribute|
  93
+        return false unless other.respond_to?(attribute)
  94
+        return false unless self.__send__(attribute) == other.__send__(attribute)
  95
+      end
  96
+      true
3
Konstantin Haase Owner
rkh added a note January 13, 2013

seems bad

Gheorghita Catalin Bordianu
omikronn added a note January 14, 2013

Could you please elaborate a bit more?

It's one of the first the first times I'm submitting a pull request instead of packaging/releasing a hotfixed gem on my own gem server in prod, so I want to get a hang of this and turn contributing into a habit.

Are there any other attributes I should be checking equality for? Is there anything wrong with this style of coding (am I breaking any guidelines)? Is there something conceptually wrong with this snippet/approach that I'm not seeing?

Thanks in advance for an answer.

Konstantin Haase Owner
rkh added a note January 14, 2013

Ah, sorry. It does not seem like good practice to me to 1. return true for == if the other is a body proxy and 2. call private methods on the body proxy in order to do so.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Konstantin Haase
Owner
rkh commented January 14, 2013

Do you have a test case or example code for me to reproduce this?

Topper Bowers

We (@bglusman and I) have a small fix that worked for us: #615

Konstantin Haase rkh closed this January 26, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 1 unique commit by 1 author.

Jan 12, 2013
Gheorghita Catalin Bordianu Sinatra::Response#== overloaded to perform comparisons based only on …
…the status, headers, body and length attributes
c618e88
This page is out of date. Refresh to see the latest.
11  lib/sinatra/base.rb
@@ -84,9 +84,18 @@ def finish
84 84
 
85 85
       # Rack::Response#finish sometimes returns self as response body. We don't want that.
86 86
       status, headers, result = super
87  
-      result = body if result == self || Rack::BodyProxy === result
  87
+      result = body if self == result
88 88
       [status, headers, result]
89 89
     end
  90
+    
  91
+    def ==(other)
  92
+      [:status, :headers, :body, :length].each do |attribute|
  93
+        return false unless other.respond_to?(attribute)
  94
+        return false unless self.__send__(attribute) == other.__send__(attribute)
  95
+      end
  96
+      true
  97
+    end
  98
+    
90 99
   end
91 100
 
92 101
   # Some Rack handlers (Thin, Rainbows!) implement an extended body object protocol, however,
16  test/response_test.rb
@@ -61,4 +61,20 @@ def object.each(*) end
61 61
     @response.body = Rack::Response.new ["foo"]
62 62
     assert_same_body @response.body, ["foo"]
63 63
   end
  64
+
  65
+  it 'perform equality comparison based on the status, headers, body and length' do
  66
+    assert_equal @response, Sinatra::Response.new
  67
+    assert_equal @response, Rack::Response.new
  68
+    assert_not_equal @response, Sinatra::Response.new("foo")
  69
+    assert_not_equal @response, Rack::Response.new("foo")
  70
+    assert_not_equal @response, Object
  71
+    
  72
+    test_response = Sinatra::Response.new
  73
+    test_response.headers["Content-Type"] = "application/json"
  74
+    assert_not_equal @response, test_response
  75
+    
  76
+    test_response = Sinatra::Response.new
  77
+    test_response.length = 3
  78
+    assert_not_equal @response, test_response
  79
+  end
64 80
 end
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.