coolio: rename deferred_response => response_pipe
[rainbows.git] / lib / rainbows / response / body.rb
bloba5d04dd2a9d3a5796566d7bab6b3f4e2627cf014
1 # -*- encoding: binary -*-
2 # :enddoc:
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
13 # objects, though.
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:
24 #         write_body
25 #         `- write_body_each
26 #         `- write_body_path
27 #            `- write_body_file
28 #            `- write_body_stream
30 module Rainbows::Response::Body # :nodoc:
31   ALIASES = {}
33   FD_MAP = Rainbows::FD_MAP
35   class F < File; end
37   def close_if_private(io)
38     io.close if F === io
39   end
41   def io_for_fd(fd)
42     FD_MAP.delete(fd) || F.for_fd(fd)
43   end
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.
49   def body_to_io(body)
50     if body.respond_to?(:to_io)
51       body.to_io
52     else
53       # try to take advantage of Rainbows::DevFdResponse, calling File.open
54       # is a last resort
55       path = body.to_path
56       path =~ %r{\A/dev/fd/(\d+)\z} ? io_for_fd($1.to_i) : F.open(path)
57     end
58   end
60   if IO.method_defined?(:sendfile_nonblock)
61     def write_body_file_sendfile(sock, body, range)
62       io = body_to_io(body)
63       range ? sock.sendfile(io, range[0], range[1]) : sock.sendfile(io, 0)
64       ensure
65         close_if_private(io)
66     end
67     ALIASES[:write_body_file] = :write_body_file_sendfile
68   end
70   if IO.respond_to?(:copy_stream)
71     unless method_defined?(:write_body_file_sendfile)
72       # try to use sendfile() via IO.copy_stream, otherwise pread()+write()
73       def write_body_file_copy_stream(sock, body, range)
74         range ? IO.copy_stream(body, sock, range[1], range[0]) :
75                 IO.copy_stream(body, sock, nil, 0)
76       end
77       ALIASES[:write_body_file] = :write_body_file_copy_stream
78     end
80     # only used when body is a pipe or socket that can't handle
81     # pread() semantics
82     def write_body_stream(sock, body)
83       IO.copy_stream(body, sock)
84     end
85   else
86     # fall back to body#each, which is a Rack standard
87     ALIASES[:write_body_stream] = :write_body_each
88   end
90   if ALIASES[:write_body_file]
91     # middlewares/apps may return with a body that responds to +to_path+
92     def write_body_path(sock, body, range)
93       File.file?(body.to_path) ? write_body_file(sock, body, range) :
94                                  write_body_stream(sock, body)
95       ensure
96         body.respond_to?(:close) and body.close
97     end
98   end
100   if method_defined?(:write_body_path)
101     def write_body(client, body, range)
102       body.respond_to?(:to_path) ?
103         write_body_path(client, body, range) :
104         write_body_each(client, body)
105     end
106   else
107     ALIASES[:write_body] = :write_body_each
108   end
110   # generic body writer, used for most dynamically generated responses
111   def write_body_each(socket, body, range = nil)
112     body.each { |chunk| socket.write(chunk) }
113     ensure
114       body.respond_to?(:close) and body.close
115   end
117   def self.included(klass)
118     ALIASES.each do |new_method, orig_method|
119       klass.__send__(:alias_method, new_method, orig_method)
120     end
121   end