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 # Rack 1.5.0 (protocol version 1.2) adds response hijacking support
23 if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
24 RACK_HIJACK = "rack.hijack"
26 def hijack_prepare(value)
31 @hp.env[RACK_HIJACK].call
38 # returns the original body on success
39 # returns nil if the headers hijacked the response body
40 def write_headers(status, headers, alive, body)
41 @hp.headers? or return body
43 status = CODES[status.to_i] || status
44 buf = "HTTP/1.1 #{status}\r\n" \
45 "Date: #{httpdate}\r\n" \
46 "Status: #{status}\r\n"
47 headers.each do |key, value|
49 when %r{\A(?:Date\z|Connection\z)}i
52 # this was an illegal key in Rack < 1.5, so it should be
53 # OK to silently discard it for those older versions
54 hijack = hijack_prepare(value)
55 alive = false # No persistent connections for hijacking
58 # avoiding blank, key-only cookies with /\n+/
59 buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
61 buf << "#{key}: #{value}\r\n"
65 write(buf << "Connection: #{alive ? KeepAlive : Close}\r\n\r\n")
68 body = nil # ensure caller does not close body
69 hijack.call(hijack_socket)
74 def close_if_private(io)
79 Rainbows::FD_MAP.delete(fd) || F.for_fd(fd)
82 # to_io is not part of the Rack spec, but make an exception here
83 # since we can conserve path lookups and file descriptors.
84 # \Rainbows! will never get here without checking for the existence
85 # of body.to_path first.
87 if body.respond_to?(:to_io)
90 # try to take advantage of Rainbows::DevFdResponse, calling F.open
93 %r{\A/dev/fd/(\d+)\z} =~ path ? io_for_fd($1.to_i) : F.open(path)
98 # generic body writer, used for most dynamically-generated responses
99 def write_body_each(body)
100 body.each { |chunk| write(chunk) }
103 # generic response writer, used for most dynamically-generated responses
104 # and also when copy_stream and/or IO#trysendfile is unavailable
105 def write_response(status, headers, body, alive)
106 body = write_headers(status, headers, alive, body)
107 write_body_each(body) if body
110 body.close if body.respond_to?(:close)
115 if IO.method_defined?(:trysendfile)
117 def write_body_file(body, range)
118 io = body_to_io(body)
119 range ? sendfile(io, range[0], range[1]) : sendfile(io, 0)
128 unless IO.method_defined?(:trysendfile)
130 def write_body_file(body, range)
131 range ? COPY_STREAM.copy_stream(body, self, range[1], range[0]) :
132 COPY_STREAM.copy_stream(body, self)
138 # write_body_stream is an alias for write_body_each if copy_stream
139 # isn't used or available.
140 def write_body_stream(body)
141 COPY_STREAM.copy_stream(io = body_to_io(body), self)
146 alias write_body_stream write_body_each
149 if IO.method_defined?(:trysendfile) || COPY_STREAM
150 HTTP_RANGE = 'HTTP_RANGE'
151 Content_Range = 'Content-Range'.freeze
153 # This does not support multipart responses (does anybody actually
155 def sendfile_range(status, headers)
158 if %r{\Abytes (\d+)-(\d+)/\d+\z} =~ headers[Content_Range]
159 a, b = $1.to_i, $2.to_i
160 return 206, headers, [ a, b - a + 1 ]
165 /\Abytes=(\d+-\d*|\d*-\d+)\z/ =~ @hp.env[HTTP_RANGE] or
169 # HeaderHash is quite expensive, and Rack::File currently
170 # uses a regular Ruby Hash with properly-cased headers the
171 # same way they're presented in rfc2616.
172 headers = Rack::Utils::HeaderHash.new(headers) unless Hash === headers
173 clen = headers[Content_Length] or return
178 count = size - offset
179 elsif a.empty? # bytes=-N
180 offset = size - b.to_i
181 count = size - offset
184 count = b.to_i + 1 - offset
187 if 0 > count || offset >= size
188 headers[Content_Length] = "0"
189 headers[Content_Range] = "bytes */#{clen}"
190 return 416, headers, nil
192 count = size if count > size
193 headers[Content_Length] = count.to_s
194 headers[Content_Range] = "bytes #{offset}-#{offset+count-1}/#{clen}"
195 return 206, headers, [ offset, count ]
199 def write_response_path(status, headers, body, alive)
200 if File.file?(body.to_path)
201 if r = sendfile_range(status, headers)
202 status, headers, range = r
203 body = write_headers(status, headers, alive, body)
204 write_body_file(body, range) if body && range
206 body = write_headers(status, headers, alive, body)
207 write_body_file(body, nil) if body
210 body = write_headers(status, headers, alive, body)
211 write_body_stream(body) if body
215 body.close if body.respond_to?(:close)
219 # returns nil if hijacked
220 def write_response(status, headers, body, alive)
221 if body.respond_to?(:to_path)
222 write_response_path(status, headers, body, alive)
229 end # COPY_STREAM || IO.method_defined?(:trysendfile)