kubo39 / bossan

high performance asynchronous rack web server
Other
28 stars 5 forks source link

Rack::Lint Error when yield response body #7

Closed kubo39 closed 11 years ago

kubo39 commented 11 years ago

Log is here.

$ rackup examples/config.ru -s bossan
* Listening on tcp://0.0.0.0:9292
/usr/local/lib/ruby/gems/2.0.0/gems/rack-1.5.2/lib/rack/lint.rb:653:in `block in each': no block given (yield) (LocalJumpError)
    from /usr/local/lib/ruby/gems/2.0.0/gems/rack-1.5.2/lib/rack/lint.rb:647:in `each'
    from /usr/local/lib/ruby/gems/2.0.0/gems/rack-1.5.2/lib/rack/lint.rb:647:in `each'
    from /usr/local/lib/ruby/gems/2.0.0/gems/rack-1.5.2/lib/rack/body_proxy.rb:31:in `each'
    from /usr/local/lib/ruby/gems/2.0.0/gems/bossan-0.4.0dev/lib/rack/handler/bossan.rb:19:in `run'
    from /usr/local/lib/ruby/gems/2.0.0/gems/bossan-0.4.0dev/lib/rack/handler/bossan.rb:19:in `run'
    from /usr/local/lib/ruby/gems/2.0.0/gems/rack-1.5.2/lib/rack/server.rb:264:in `start'
    from /usr/local/lib/ruby/gems/2.0.0/gems/rack-1.5.2/lib/rack/server.rb:141:in `start'
    from /usr/local/lib/ruby/gems/2.0.0/gems/rack-1.5.2/bin/rackup:4:in `<top (required)>'
    from /usr/local/bin/rackup:23:in `load'
    from /usr/local/bin/rackup:23:in `<main>'
kubo39 commented 11 years ago

The reason is Rack::Lint#each doesn't return Enumerator object.

 $ pry
[1] pry(main)> app = ->(env){ [200, {"foo"=> "bar"}, ["baz"]] }
=> #<Proc:0xb8c96a7c@(pry):1 (lambda)>
[2] pry(main)> require 'rack'
=> true
[3] pry(main)> lint = Rack::Lint.new(app)
=> #<Rack::Lint:0xb905ea9c
 @app=#<Proc:0xb8c96a7c@(pry):1 (lambda)>,
 @content_length=nil>
[4] pry(main)> lint.each
Rack::Lint::LintError: Response body must respond to each
from /usr/local/lib/ruby/gems/2.0.0/gems/rack-1.5.2/lib/rack/lint.rb:20:in `assert'

How to avoid this error is like:

module Rack
  class Lint
    def each
      @closed = false
      bytes = 0

      ## The Body must respond to +each+
      assert("Response body must respond to each") do
        @body.respond_to?(:each)
      end

      @body.each { |part|
        ## and must only yield String values.
        assert("Body yielded non-string value #{part.inspect}") {
          part.kind_of? String
        }
        bytes += Rack::Utils.bytesize(part)
        yield part if block_given?
      }
      verify_content_length(bytes)

      ##
      ## The Body itself should not be an instance of String, as this will
      ## break in Ruby 1.9.
      ##
      ## If the Body responds to +close+, it will be called after iteration. If
      ## the body is replaced by a middleware after action, the original body
      ## must be closed first, if it responds to close.
      # XXX howto: assert("Body has not been closed") { @closed }

      ##
      ## If the Body responds to +to_path+, it must return a String
      ## identifying the location of a file whose contents are identical
      ## to that produced by calling +each+; this may be used by the
      ## server as an alternative, possibly more efficient way to
      ## transport the response.

      if @body.respond_to?(:to_path)
        assert("The file identified by body.to_path does not exist") {
          ::File.exist? @body.to_path
        }
      end

      ##
      ## The Body commonly is an Array of Strings, the application
      ## instance itself, or a File-like object.

      unless block_given?
        assert("Without block, we expect @body.each returns Enumerator") {
          @body.each.kind_of? Enumerator
        }
        return @body.each
      end
    end
  end
end

Do you think about this?

kubo39 commented 11 years ago

fixed using Object#to_enum from response-body to Enumerator, not Object#each.