rainbows.git  about / heads / tags
Unicorn for sleepy apps and slow clients
blob 2535374b14bf728704544544f4acf221a1f36b98 3928 bytes (raw)
$ git show v0.97.0:lib/rainbows/response/body.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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
 
# -*- encoding: binary -*-
# :enddoc:
# non-portable body response stuff goes here
#
# The sendfile 1.0.0 RubyGem includes IO#sendfile and
# IO#sendfile_nonblock.   Previous versions of "sendfile" didn't have
# IO#sendfile_nonblock, and IO#sendfile in previous versions could
# block other threads under 1.8 with large files
#
# IO#sendfile currently (June 2010) beats 1.9 IO.copy_stream with
# non-Linux support and large files on 32-bit.  We still fall back to
# IO.copy_stream (if available) if we're dealing with DevFdResponse
# objects, though.
#
# Linux-only splice(2) support via the "io_splice" gem will eventually
# be added for streaming sockets/pipes, too.
#
# * write_body_file - regular files (sendfile or pread+write)
# * write_body_stream - socket/pipes (read+write, splice later)
# * write_body_each - generic fallback
#
# callgraph is as follows:
#
#         write_body
#         `- write_body_each
#         `- write_body_path
#            `- write_body_file
#            `- write_body_stream
#
module Rainbows::Response::Body # :nodoc:
  ALIASES = {}

  FD_MAP = Rainbows::FD_MAP

  def io_for_fd(fd)
    FD_MAP.delete(fd) || IO.new(fd)
  end

  # to_io is not part of the Rack spec, but make an exception here
  # since we can conserve path lookups and file descriptors.
  # \Rainbows! will never get here without checking for the existence
  # of body.to_path first.
  def body_to_io(body)
    if body.respond_to?(:to_io)
      body.to_io
    else
      # try to take advantage of Rainbows::DevFdResponse, calling File.open
      # is a last resort
      path = body.to_path
      path =~ %r{\A/dev/fd/(\d+)\z} ? io_for_fd($1.to_i) : File.open(path)
    end
  end

  if IO.method_defined?(:sendfile_nonblock)
    def write_body_file(sock, body, range)
      range ? sock.sendfile(body, range[0], range[1]) : sock.sendfile(body, 0)
    end
  end

  if IO.respond_to?(:copy_stream)
    unless method_defined?(:write_body_file)
      # try to use sendfile() via IO.copy_stream, otherwise pread()+write()
      def write_body_file(sock, body, range)
        range ? IO.copy_stream(body, sock, range[1], range[0]) :
                IO.copy_stream(body, sock, nil, 0)
      end
    end

    # only used when body is a pipe or socket that can't handle
    # pread() semantics
    def write_body_stream(sock, body, range)
      IO.copy_stream(body, sock)
      ensure
        body.respond_to?(:close) and body.close
    end
  else
    # fall back to body#each, which is a Rack standard
    ALIASES[:write_body_stream] = :write_body_each
  end

  if method_defined?(:write_body_file)

    # middlewares/apps may return with a body that responds to +to_path+
    def write_body_path(sock, body, range)
      inp = body_to_io(body)
      if inp.stat.file?
        begin
          write_body_file(sock, inp, range)
        ensure
          inp.close if inp != body
        end
      else
        write_body_stream(sock, inp, range)
      end
      ensure
        body.respond_to?(:close) && inp != body and body.close
    end
  elsif method_defined?(:write_body_stream)
    def write_body_path(sock, body, range)
      write_body_stream(sock, inp = body_to_io(body), range)
      ensure
        body.respond_to?(:close) && inp != body and body.close
    end
  end

  if method_defined?(:write_body_path)
    def write_body(client, body, range)
      body.respond_to?(:to_path) ?
        write_body_path(client, body, range) :
        write_body_each(client, body, range)
    end
  else
    ALIASES[:write_body] = :write_body_each
  end

  # generic body writer, used for most dynamically generated responses
  def write_body_each(socket, body, range = nil)
    body.each { |chunk| socket.write(chunk) }
    ensure
      body.respond_to?(:close) and body.close
  end

  def self.included(klass)
    ALIASES.each do |new_method, orig_method|
      klass.__send__(:alias_method, new_method, orig_method)
    end
  end
end

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