remove DATE constant
[unicorn.git] / lib / unicorn / http_response.rb
blobc0b10812eb308a7f3f4b162d200514f2613db3ba
1 require 'time'
3 module Unicorn
4   # Writes a Rack response to your client using the HTTP/1.1 specification.
5   # You use it by simply doing:
6   #
7   #   status, headers, body = rack_app.call(env)
8   #   HttpResponse.write(socket, [ status, headers, body ])
9   #
10   # Most header correctness (including Content-Length and Content-Type)
11   # is the job of Rack, with the exception of the "Connection: close"
12   # and "Date" headers.
13   #
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.
22   class HttpResponse
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
29     # writes the rack_response to socket as an HTTP response
30     def self.write(socket, rack_response)
31       status, headers, body = rack_response
32       status = "#{status} #{HTTP_STATUS_CODES[status]}"
34       # Date is required by HTTP/1.1 as long as our clock can be trusted.
35       # Some broken clients require a "Status" header so we accomodate them
36       out = [ "Date: #{Time.now.httpdate}", "Status: #{status}" ]
38       # Don't bother enforcing duplicate supression, it's a Hash most of
39       # the time anyways so just hope our app knows what it's doing
40       headers.each do |key, value|
41         next if SKIP.include?(key.downcase)
42         if value =~ /\n/
43           value.split(/\n/).each { |v| out << "#{key}: #{v}" }
44         else
45           out << "#{key}: #{value}"
46         end
47       end
49       # Rack should enforce Content-Length or chunked transfer encoding,
50       # so don't worry or care about them.
51       socket_write(socket,
52                    "HTTP/1.1 #{status}\r\n" \
53                    "Connection: close\r\n" \
54                    "#{out.join("\r\n")}\r\n\r\n")
55       body.each { |chunk| socket_write(socket, chunk) }
56       socket.close # uncorks the socket immediately
57       ensure
58         body.respond_to?(:close) and body.close rescue nil
59     end
61     private
63       # write(2) can return short on slow devices like sockets as well
64       # as fail with EINTR if a signal was caught.
65       def self.socket_write(socket, buffer)
66         loop do
67           begin
68             written = socket.syswrite(buffer)
69             return written if written == buffer.length
70             buffer = buffer[written..-1]
71           rescue Errno::EINTR
72             retry
73           end
74         end
75       end
77   end
78 end