1 # -*- encoding: binary -*-
3 module Rainbows::Response
4 include Unicorn::HttpResponse
6 KeepAlive = "keep-alive"
7 Content_Length = "Content-Length".freeze
8 Transfer_Encoding = "Transfer-Encoding".freeze
9 Rainbows.config!(self, :copy_stream)
11 # private file class for IO objects opened by Rainbows! itself (and not
12 # the app or middleware)
15 # called after forking
17 Kgio.accept_class = Rainbows::Client
18 0 == Rainbows.server.keepalive_timeout and
19 Rainbows::HttpParser.keepalive_requests = 0
22 def write_headers(status, headers, alive)
23 @hp.headers? or return
24 status = CODES[status.to_i] || status
25 buf = "HTTP/1.1 #{status}\r\n" \
26 "Date: #{httpdate}\r\n" \
27 "Status: #{status}\r\n" \
28 "Connection: #{alive ? KeepAlive : Close}\r\n"
29 headers.each do |key, value|
30 next if %r{\A(?:Date\z|Connection\z)}i =~ key
32 # avoiding blank, key-only cookies with /\n+/
33 buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
35 buf << "#{key}: #{value}\r\n"
41 def close_if_private(io)
46 Rainbows::FD_MAP.delete(fd) || F.for_fd(fd)
49 # to_io is not part of the Rack spec, but make an exception here
50 # since we can conserve path lookups and file descriptors.
51 # \Rainbows! will never get here without checking for the existence
52 # of body.to_path first.
54 if body.respond_to?(:to_io)
57 # try to take advantage of Rainbows::DevFdResponse, calling F.open
60 %r{\A/dev/fd/(\d+)\z} =~ path ? io_for_fd($1.to_i) : F.open(path)
65 # generic body writer, used for most dynamically-generated responses
66 def write_body_each(body)
67 body.each { |chunk| write(chunk) }
70 # generic response writer, used for most dynamically-generated responses
71 # and also when copy_stream and/or IO#trysendfile is unavailable
72 def write_response(status, headers, body, alive)
73 write_headers(status, headers, alive)
76 body.close if body.respond_to?(:close)
81 if IO.method_defined?(:trysendfile)
83 def write_body_file(body, range)
85 range ? sendfile(io, range[0], range[1]) : sendfile(io, 0)
94 unless IO.method_defined?(:trysendfile)
96 def write_body_file(body, range)
97 range ? COPY_STREAM.copy_stream(body, self, range[1], range[0]) :
98 COPY_STREAM.copy_stream(body, self, nil, 0)
104 # write_body_stream is an alias for write_body_each if copy_stream
105 # isn't used or available.
106 def write_body_stream(body)
107 COPY_STREAM.copy_stream(io = body_to_io(body), self)
112 alias write_body_stream write_body_each
115 if IO.method_defined?(:trysendfile) || COPY_STREAM
116 HTTP_RANGE = 'HTTP_RANGE'
117 Content_Range = 'Content-Range'.freeze
119 # This does not support multipart responses (does anybody actually
121 def sendfile_range(status, headers)
122 200 == status.to_i &&
123 /\Abytes=(\d+-\d*|\d*-\d+)\z/ =~ @hp.env[HTTP_RANGE] or
127 # HeaderHash is quite expensive, and Rack::File currently
128 # uses a regular Ruby Hash with properly-cased headers the
129 # same way they're presented in rfc2616.
130 headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
131 clen = headers[Content_Length] or return
136 count = size - offset
137 elsif a.empty? # bytes=-N
138 offset = size - b.to_i
139 count = size - offset
142 count = b.to_i + 1 - offset
145 if 0 > count || offset >= size
146 headers[Content_Length] = "0"
147 headers[Content_Range] = "bytes */#{clen}"
148 return 416, headers, nil
150 count = size if count > size
151 headers[Content_Length] = count.to_s
152 headers[Content_Range] = "bytes #{offset}-#{offset+count-1}/#{clen}"
153 return 206, headers, [ offset, count ]
157 def write_response_path(status, headers, body, alive)
158 if File.file?(body.to_path)
159 if r = sendfile_range(status, headers)
160 status, headers, range = r
161 write_headers(status, headers, alive)
162 write_body_file(body, range) if range
164 write_headers(status, headers, alive)
165 write_body_file(body, nil)
168 write_headers(status, headers, alive)
169 write_body_stream(body)
172 body.close if body.respond_to?(:close)
176 def write_response(status, headers, body, alive)
177 if body.respond_to?(:to_path)
178 write_response_path(status, headers, body, alive)
185 end # COPY_STREAM || IO.method_defined?(:trysendfile)