4 # Writes a Rack response to your client using the HTTP/1.1 specification.
5 # You use it by simply doing:
7 # status, headers, body = rack_app.call(env)
8 # HttpResponse.write(socket, [ status, headers, body ])
10 # Most header correctness (including Content-Length and Content-Type)
11 # is the job of Rack, with the exception of the "Connection: close"
14 # A design decision was made to force the client to not pipeline or
15 # keepalive requests. HTTP/1.1 pipelining really kills the
16 # performance due to how it has to be handled and how unclear the
17 # standard is. To fix this the HttpResponse always gives a
18 # "Connection: close" header which forces the client to close right
19 # away. The bonus for this is that it gives a pretty nice speed boost
20 # to most clients since they can close their connection immediately.
24 # Rack does not set/require a Date: header. We always override the
25 # Connection: and Date: headers no matter what (if anything) our
26 # Rack application sent us.
27 SKIP = { 'connection' => true, 'date' => true, 'status' => true }.freeze
28 CONNECTION_CLOSE = "Connection: close".freeze #:nodoc
30 # writes the rack_response to socket as an HTTP response
31 def self.write(socket, rack_response)
32 status, headers, body = rack_response
33 status = "#{status} #{HTTP_STATUS_CODES[status]}"
34 out = [ CONNECTION_CLOSE ]
36 # Don't bother enforcing duplicate supression, it's a Hash most of
37 # the time anyways so just hope our app knows what it's doing
38 headers.each do |key, value|
39 next if SKIP.include?(key.downcase)
41 value.split(/\n/).each { |v| out << "#{key}: #{v}" }
43 out << "#{key}: #{value}"
47 # Rack should enforce Content-Length or chunked transfer encoding,
48 # so don't worry or care about them.
49 # Date is required by HTTP/1.1 as long as our clock can be trusted.
50 # Some broken clients require a "Status" header so we accomodate them
52 "HTTP/1.1 #{status}\r\n" \
53 "Date: #{Time.now.httpdate}\r\n" \
54 "Status: #{status}\r\n" \
55 "#{out.join("\r\n")}\r\n\r\n")
56 body.each { |chunk| socket_write(socket, chunk) }
57 socket.close # uncorks the socket immediately
59 body.respond_to?(:close) and body.close rescue nil
64 # write(2) can return short on slow devices like sockets as well
65 # as fail with EINTR if a signal was caught.
66 def self.socket_write(socket, buffer)
68 written = socket.syswrite(buffer)
69 return written if written == buffer.length
70 buffer = buffer[written..-1]