unicorn 0.8.4
[unicorn.git] / lib / unicorn / http_response.rb
blob3bf934776e2e79b8c437cebae1880068dbc41841
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     # Every standard HTTP code mapped to the appropriate message.
25     CODES = Rack::Utils::HTTP_STATUS_CODES.inject({}) { |hash,(code,msg)|
26       hash[code] = "#{code} #{msg}"
27       hash
28     }
30     # Rack does not set/require a Date: header.  We always override the
31     # Connection: and Date: headers no matter what (if anything) our
32     # Rack application sent us.
33     SKIP = { 'connection' => true, 'date' => true, 'status' => true }.freeze
34     EMPTY = ''.freeze # :nodoc
35     OUT = [] # :nodoc
37     # writes the rack_response to socket as an HTTP response
38     def self.write(socket, rack_response)
39       status, headers, body = rack_response
40       status = CODES[status.to_i] || status
41       OUT.clear
43       # Don't bother enforcing duplicate supression, it's a Hash most of
44       # the time anyways so just hope our app knows what it's doing
45       headers.each do |key, value|
46         next if SKIP.include?(key.downcase)
47         if value =~ /\n/
48           value.split(/\n/).each { |v| OUT << "#{key}: #{v}\r\n" }
49         else
50           OUT << "#{key}: #{value}\r\n"
51         end
52       end
54       # Rack should enforce Content-Length or chunked transfer encoding,
55       # so don't worry or care about them.
56       # Date is required by HTTP/1.1 as long as our clock can be trusted.
57       # Some broken clients require a "Status" header so we accomodate them
58       socket.write("HTTP/1.1 #{status}\r\n" \
59                    "Date: #{Time.now.httpdate}\r\n" \
60                    "Status: #{status}\r\n" \
61                    "Connection: close\r\n" \
62                    "#{OUT.join(EMPTY)}\r\n")
63       body.each { |chunk| socket.write(chunk) }
64       socket.close # flushes and uncorks the socket immediately
65       ensure
66         body.respond_to?(:close) and body.close rescue nil
67     end
69   end
70 end