1 # -*- encoding: binary -*-
2 # non-portable body response stuff goes here
4 # The sendfile 1.0.0 RubyGem includes IO#sendfile and
5 # IO#sendfile_nonblock. Previous versions of "sendfile" didn't have
6 # IO#sendfile_nonblock, and IO#sendfile in previous versions could
7 # block other threads under 1.8 with large files
9 # IO#sendfile currently (June 2010) beats 1.9 IO.copy_stream with
10 # non-Linux support and large files on 32-bit. We still fall back to
11 # IO.copy_stream (if available) if we're dealing with DevFdResponse
14 # Linux-only splice(2) support via the "io_splice" gem will eventually
15 # be added for streaming sockets/pipes, too.
17 # * write_body_file - regular files (sendfile or pread+write)
18 # * write_body_stream - socket/pipes (read+write, splice later)
19 # * write_body_each - generic fallback
21 # callgraph is as follows:
27 # `- write_body_stream
29 module Rainbows::Response::Body # :nodoc:
32 # to_io is not part of the Rack spec, but make an exception here
33 # since we can conserve path lookups and file descriptors.
34 # \Rainbows! will never get here without checking for the existence
35 # of body.to_path first.
37 if body.respond_to?(:to_io)
40 # try to take advantage of Rainbows::DevFdResponse, calling File.open
43 path =~ %r{\A/dev/fd/(\d+)\z} ? IO.new($1.to_i) : File.open(path, 'rb')
47 if IO.method_defined?(:sendfile_nonblock)
48 def write_body_file(sock, body)
49 sock.sendfile(body, 0)
53 if IO.respond_to?(:copy_stream)
54 unless method_defined?(:write_body_file)
55 # try to use sendfile() via IO.copy_stream, otherwise pread()+write()
56 def write_body_file(sock, body)
57 IO.copy_stream(body, sock, nil, 0)
61 # only used when body is a pipe or socket that can't handle
63 def write_body_stream(sock, body)
64 IO.copy_stream(body, sock)
66 body.respond_to?(:close) and body.close
69 # fall back to body#each, which is a Rack standard
70 ALIASES[:write_body_stream] = :write_body_each
73 if method_defined?(:write_body_file)
75 # middlewares/apps may return with a body that responds to +to_path+
76 def write_body_path(sock, body)
77 inp = body_to_io(body)
80 write_body_file(sock, inp)
82 inp.close if inp != body
85 write_body_stream(sock, inp)
88 body.respond_to?(:close) && inp != body and body.close
91 def write_body_path(sock, body)
92 write_body_stream(sock, body_to_io(body))
96 if method_defined?(:write_body_path)
97 def write_body(client, body)
98 body.respond_to?(:to_path) ?
99 write_body_path(client, body) :
100 write_body_each(client, body)
103 ALIASES[:write_body] = :write_body_each
106 # generic body writer, used for most dynamically generated responses
107 def write_body_each(socket, body)
108 body.each { |chunk| socket.write(chunk) }
110 body.respond_to?(:close) and body.close
113 def self.included(klass)
114 ALIASES.each do |new_method, orig_method|
115 klass.__send__(:alias_method, new_method, orig_method)