1 # -*- encoding: binary -*-
3 # non-portable body response stuff goes here
5 # The sendfile 1.0.0 RubyGem includes IO#sendfile and
6 # IO#sendfile_nonblock. Previous versions of "sendfile" didn't have
7 # IO#sendfile_nonblock, and IO#sendfile in previous versions could
8 # block other threads under 1.8 with large files
10 # IO#sendfile currently (June 2010) beats 1.9 IO.copy_stream with
11 # non-Linux support and large files on 32-bit. We still fall back to
12 # IO.copy_stream (if available) if we're dealing with DevFdResponse
15 # Linux-only splice(2) support via the "io_splice" gem will eventually
16 # be added for streaming sockets/pipes, too.
18 # * write_body_file - regular files (sendfile or pread+write)
19 # * write_body_stream - socket/pipes (read+write, splice later)
20 # * write_body_each - generic fallback
22 # callgraph is as follows:
28 # `- write_body_stream
30 module Rainbows::Response::Body # :nodoc:
33 FD_MAP = Rainbows::FD_MAP
37 def close_if_private(io)
42 FD_MAP.delete(fd) || F.for_fd(fd)
45 # to_io is not part of the Rack spec, but make an exception here
46 # since we can conserve path lookups and file descriptors.
47 # \Rainbows! will never get here without checking for the existence
48 # of body.to_path first.
50 if body.respond_to?(:to_io)
53 # try to take advantage of Rainbows::DevFdResponse, calling File.open
56 path =~ %r{\A/dev/fd/(\d+)\z} ? io_for_fd($1.to_i) : F.open(path)
60 if IO.method_defined?(:sendfile_nonblock)
61 def write_body_file(sock, body, range)
63 range ? sock.sendfile(io, range[0], range[1]) : sock.sendfile(io, 0)
69 if IO.respond_to?(:copy_stream)
70 unless method_defined?(:write_body_file)
71 # try to use sendfile() via IO.copy_stream, otherwise pread()+write()
72 def write_body_file(sock, body, range)
73 range ? IO.copy_stream(body, sock, range[1], range[0]) :
74 IO.copy_stream(body, sock, nil, 0)
78 # only used when body is a pipe or socket that can't handle
80 def write_body_stream(sock, body, range)
81 IO.copy_stream(body, sock)
84 # fall back to body#each, which is a Rack standard
85 ALIASES[:write_body_stream] = :write_body_each
88 if method_defined?(:write_body_file)
89 # middlewares/apps may return with a body that responds to +to_path+
90 def write_body_path(sock, body, range)
91 stat = File.stat(body.to_path)
92 stat.file? ? write_body_file(sock, body, range) :
93 write_body_stream(sock, body, range)
95 body.respond_to?(:close) and body.close
97 elsif method_defined?(:write_body_stream)
98 def write_body_path(sock, body, range)
99 write_body_stream(sock, body, range)
101 body.respond_to?(:close) and body.close
105 if method_defined?(:write_body_path)
106 def write_body(client, body, range)
107 body.respond_to?(:to_path) ?
108 write_body_path(client, body, range) :
109 write_body_each(client, body, range)
112 ALIASES[:write_body] = :write_body_each
115 # generic body writer, used for most dynamically generated responses
116 def write_body_each(socket, body, range = nil)
117 body.each { |chunk| socket.write(chunk) }
119 body.respond_to?(:close) and body.close
122 def self.included(klass)
123 ALIASES.each do |new_method, orig_method|
124 klass.__send__(:alias_method, new_method, orig_method)