My webzone

A place for John Bintz to ramble.

Closing the given body in Rack middleware

Here’s one that was biting my bum: rack-livereload started generating a bunch of thread deadlock errors left and right for users. One commenter on the bug thread mentioned that middleware need to close the body, which I had never heard of before. Turns out it’s not as well-documented a practice that I would have hoped.

It boils down to trying to close the body that you get after you call your app, in case for some reason it passes you an open file handle (which I guess is a thing?)

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

  def call(env)
    dup._call(env) # <= the thing I thought would fix the deadlocks
  end

  def _call(env)
    [ status, headers, body ] = @app.call(env)

    new_body = []
    body.each { |line| new_body << line }
    body.close if body.respond_to?(:close) # <= the thing I really had to do

    # ...process stuff...

    [ status, headers, new_body ]
  end
end

So this is mainly so I remember to do this from now on, and for anyone else who runs into thread deadlock issues with Rack middleware.