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
| | # -*- 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 = {}
# 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.new($1.to_i) : File.open(path, 'rb')
end
end
if IO.method_defined?(:sendfile_nonblock)
def write_body_file(sock, body)
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)
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)
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)
inp = body_to_io(body)
if inp.stat.file?
begin
write_body_file(sock, inp)
ensure
inp.close if inp != body
end
else
write_body_stream(sock, inp)
end
ensure
body.respond_to?(:close) && inp != body and body.close
end
else
def write_body_path(sock, body)
write_body_stream(sock, body_to_io(body))
end
end
if method_defined?(:write_body_path)
def write_body(client, body)
body.respond_to?(:to_path) ?
write_body_path(client, body) :
write_body_each(client, body)
end
else
ALIASES[:write_body] = :write_body_each
end
# generic body writer, used for most dynamically generated responses
def write_body_each(socket, body)
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
|