negative timeout in Rainbows::Fiber::Base

Eric Wong normalperson at yhbt.net
Fri Sep 28 19:24:49 UTC 2012


Eric Wong <normalperson at yhbt.net> wrote:
> So the _actual_ Content-Length that's sent is zero?
> 
> Rainbows! should drop a connection if an exception is raised while
> sending the response body, perhaps the heroku router is confused
> by that?

Instead of trying "keepalive_timeout 0", you can also try using the
following middleware.  This only works for Content-Length responses
(which seems to be your case)

Of course, like all middleware, this makes your stack deeper, so
maybe it's not good, either...

Furthermore, this code is totally untested, it may not even compile,
but I hope you get the idea and fix trivial errors :)

class PadOnError
  def initialize(app)
    @app = app
  end

  def call(env)
    status, headers, body = @app.call(env)
    headers = Rack::Utils::HeaderHash.new(headers)
    clen = headers["Content-Length"]
    if clen
      [ status, headers, Padder.new(clen.to_i, body) ]
    else
      # TODO: write your own Transfer-Encoding: chunked padder :)
      # It would need to parse the chunked encoding and keep track
      # of each chunk length, pad any unfinished chunk, and ensure
      # termination with "0\r\n\r\n"
      [ status, headers, body ]
    end
  end

  class Padder
    def initialize(clen, body)
      @clen = clen
      @body = body
      @bytes_sent = 0
    end

    def each
      @body.each do |chunk|
        @bytes_sent += chunk.bytesize

        # if this raises due to a socket error... we'll just end up
        # raising (again) below
        yield chunk
      end
    ensure
      # pad the response in case @body.each raised an error while
      # generating.
      left_to_send = clen - @bytes_sent

      # XXX assuming you're sending HTTP responses that fit comfortably in
      # memory, this can OOM if you're sending multi-mega/gigabyte files
      # like most of my applications :P
      if left_to_send > 0
        padding = " " * left_to_send
        yield padding
      end
    end

    def close
      @body.close if @body.respond_to?(:close)
    end
  end
end

----------------------- config.ru ------------------------
use PadOnError
run YourApp.new
---------------------------------------------------------


More information about the rainbows-talk mailing list