restore Rainbows::HttpResponse.write for Cramp
[rainbows.git] / lib / rainbows / response / body.rb
blob8d8ec278079fa0e58215eda05da3021b37f84f11
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
12 # objects, though.
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:
23 #         write_body
24 #         `- write_body_each
25 #         `- write_body_path
26 #            `- write_body_file
27 #            `- write_body_stream
29 module Rainbows::Response::Body # :nodoc:
30   ALIASES = {}
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.
36   def body_to_io(body)
37     if body.respond_to?(:to_io)
38       body.to_io
39     else
40       # try to take advantage of Rainbows::DevFdResponse, calling File.open
41       # is a last resort
42       path = body.to_path
43       path =~ %r{\A/dev/fd/(\d+)\z} ? IO.new($1.to_i) : File.open(path, 'rb')
44     end
45   end
47   if IO.method_defined?(:sendfile_nonblock)
48     def write_body_file(sock, body)
49       sock.sendfile(body, 0)
50     end
51   end
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)
58       end
59     end
61     # only used when body is a pipe or socket that can't handle
62     # pread() semantics
63     def write_body_stream(sock, body)
64       IO.copy_stream(body, sock)
65       ensure
66         body.respond_to?(:close) and body.close
67     end
68   else
69     # fall back to body#each, which is a Rack standard
70     ALIASES[:write_body_stream] = :write_body_each
71   end
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)
78       if inp.stat.file?
79         begin
80           write_body_file(sock, inp)
81         ensure
82           inp.close if inp != body
83         end
84       else
85         write_body_stream(sock, inp)
86       end
87       ensure
88         body.respond_to?(:close) && inp != body and body.close
89     end
90   else
91     def write_body_path(sock, body)
92       write_body_stream(sock, body_to_io(body))
93     end
94   end
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)
101     end
102   else
103     ALIASES[:write_body] = :write_body_each
104   end
106   # generic body writer, used for most dynamically generated responses
107   def write_body_each(socket, body)
108     body.each { |chunk| socket.write(chunk) }
109     ensure
110       body.respond_to?(:close) and body.close
111   end
113   def self.included(klass)
114     ALIASES.each do |new_method, orig_method|
115       klass.__send__(:alias_method, new_method, orig_method)
116     end
117   end