rainbows.git  about / heads / tags
Unicorn for sleepy apps and slow clients
blob e418fd1bbeee0f6cd216eac74a8d73860d9f0597 2793 bytes (raw)
$ git show v0.94.0:lib/rainbows/sendfile.rb	# shows this blob on the CLI

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
 
# -*- encoding: binary -*-
module Rainbows

# This middleware handles X-\Sendfile headers generated by applications
# or middlewares down the stack.  It should be placed at the top
# (outermost layer) of the middleware stack to avoid having its
# +to_path+ method clobbered by another middleware.
#
# This converts X-\Sendfile responses to bodies which respond to the
# +to_path+ method which allows certain concurrency models to serve
# efficiently using sendfile() or similar.  With multithreaded models
# under Ruby 1.9, IO.copy_stream will be used.
#
# This middleware is the opposite of Rack::Contrib::Sendfile as it
# reverses the effect of Rack::Contrib::Sendfile.  Unlike many Ruby
# web servers, some configurations of \Rainbows! are capable of
# serving static files efficiently.
#
# === Compatibility (via IO.copy_stream in Ruby 1.9):
# * ThreadSpawn
# * ThreadPool
# * WriterThreadPool
# * WriterThreadSpawn
#
# === Compatibility (Ruby 1.8 and 1.9)
# * EventMachine
# * NeverBlock (using EventMachine)
#
# DO NOT use this middleware if you're proxying to \Rainbows! with a
# server that understands X-\Sendfile (e.g. Apache, Lighttpd) natively.
#
# This does NOT understand X-Accel-Redirect headers intended for nginx.
# X-Accel-Redirect requires the application to be highly coupled with
# the corresponding nginx configuration, and is thus too complicated to
# be worth supporting.
#
# Example config.ru:
#
#    use Rainbows::Sendfile
#    run lambda { |env|
#      path = "#{Dir.pwd}/random_blob"
#      [ 200,
#        {
#          'X-Sendfile' => path,
#          'Content-Type' => 'application/octet-stream'
#        },
#        []
#      ]
#    }

class Sendfile < Struct.new(:app)

  # :stopdoc:
  HH = Rack::Utils::HeaderHash
  # :startdoc:

  # Body wrapper, this allows us to fall back gracefully to
  # +each+ in case a given concurrency model does not optimize
  # +to_path+ calls.
  class Body < Struct.new(:to_io)

    def initialize(path, headers)
      # Rainbows! will try #to_io if #to_path exists to avoid unnecessary
      # open() calls.
      self.to_io = File.open(path, 'rb')

      unless headers['Content-Length']
        stat = to_io.stat
        headers['Content-Length'] = stat.size.to_s if stat.file?
      end
    end

    def to_path
      to_io.path
    end

    # fallback in case our +to_path+ doesn't get handled for whatever reason
    def each(&block)
      buf = ''
      while to_io.read(0x4000, buf)
        yield buf
      end
    end

    def close
      to_io.close
    end
  end

  def call(env)
    status, headers, body = app.call(env)
    headers = HH.new(headers)
    if path = headers.delete('X-Sendfile')
      body = Body.new(path, headers) unless body.respond_to?(:to_path)
    end
    [ status, headers, body ]
  end
end

end

git clone https://yhbt.net/rainbows.git