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
|
module Thin
# A response sent to the client.
class Response
CONNECTION = 'Connection'.freeze
CLOSE = 'close'.freeze
KEEP_ALIVE = 'keep-alive'.freeze
SERVER = 'Server'.freeze
CONTENT_LENGTH = 'Content-Length'.freeze
# Status code
attr_accessor :status
# Response body, must respond to +each+.
attr_accessor :body
# Headers key-value hash
attr_reader :headers
def initialize
@headers = Headers.new
@status = 200
@persistent = false
end
# String representation of the headers
# to be sent in the response.
def headers_output
# Set default headers
@headers[CONNECTION] = persistent? ? KEEP_ALIVE : CLOSE
@headers[SERVER] = Thin::SERVER
@headers.to_s
end
# Top header of the response,
# containing the status code and response headers.
def head
"HTTP/1.1 #{@status} #{HTTP_STATUS_CODES[@status.to_i]}\r\n#{headers_output}\r\n"
end
if Thin.ruby_18?
# Ruby 1.8 implementation.
# Respects Rack specs.
#
# See http://rack.rubyforge.org/doc/files/SPEC.html
def headers=(key_value_pairs)
key_value_pairs.each do |k, vs|
vs.each { |v| @headers[k] = v.chomp } if vs
end if key_value_pairs
end
else
# Ruby 1.9 doesn't have a String#each anymore.
# Rack spec doesn't take care of that yet, for now we just use
# +each+ but fallback to +each_line+ on strings.
# I wish we could remove that condition.
# To be reviewed when a new Rack spec comes out.
def headers=(key_value_pairs)
key_value_pairs.each do |k, vs|
next unless vs
if vs.is_a?(String)
vs.each_line { |v| @headers[k] = v.chomp }
else
vs.each { |v| @headers[k] = v.chomp }
end
end if key_value_pairs
end
end
# Close any resource used by the response
def close
@body.close if @body.respond_to?(:close)
end
# Yields each chunk of the response.
# To control the size of each chunk
# define your own +each+ method on +body+.
def each
yield head
if @body.is_a?(String)
yield @body
else
@body.each { |chunk| yield chunk }
end
end
# Tell the client the connection should stay open
def persistent!
@persistent = true
end
# Persistent connection must be requested as keep-alive
# from the server and have a Content-Length.
def persistent?
@persistent && @headers.has_key?(CONTENT_LENGTH)
end
end
end
|