diff options
Diffstat (limited to 'lib/unicorn/http_response.rb')
-rw-r--r-- | lib/unicorn/http_response.rb | 56 |
1 files changed, 16 insertions, 40 deletions
diff --git a/lib/unicorn/http_response.rb b/lib/unicorn/http_response.rb index f3b5a82..b781e20 100644 --- a/lib/unicorn/http_response.rb +++ b/lib/unicorn/http_response.rb @@ -1,23 +1,13 @@ # -*- encoding: binary -*- -require 'time' - +# :enddoc: # Writes a Rack response to your client using the HTTP/1.1 specification. # You use it by simply doing: # # status, headers, body = rack_app.call(env) -# HttpResponse.write(socket, [ status, headers, body ]) +# http_response_write(socket, status, headers, body) # # Most header correctness (including Content-Length and Content-Type) -# is the job of Rack, with the exception of the "Connection: close" -# and "Date" headers. -# -# A design decision was made to force the client to not pipeline or -# keepalive requests. HTTP/1.1 pipelining really kills the -# performance due to how it has to be handled and how unclear the -# standard is. To fix this the HttpResponse always gives a -# "Connection: close" header which forces the client to close right -# away. The bonus for this is that it gives a pretty nice speed boost -# to most clients since they can close their connection immediately. +# is the job of Rack, with the exception of the "Date" and "Status" header. module Unicorn::HttpResponse # Every standard HTTP code mapped to the appropriate message. @@ -25,41 +15,27 @@ module Unicorn::HttpResponse hash[code] = "#{code} #{msg}" hash } - - # Rack does not set/require a Date: header. We always override the - # Connection: and Date: headers no matter what (if anything) our - # Rack application sent us. - SKIP = { 'connection' => true, 'date' => true, 'status' => true } + CRLF = "\r\n" # writes the rack_response to socket as an HTTP response - def self.write(socket, rack_response, have_header = true) - status, headers, body = rack_response - - if have_header - status = CODES[status.to_i] || status - out = [] - - # Don't bother enforcing duplicate supression, it's a Hash most of - # the time anyways so just hope our app knows what it's doing + def http_response_write(socket, status, headers, body) + status = CODES[status.to_i] || status + + if headers + buf = "HTTP/1.1 #{status}\r\n" \ + "Date: #{httpdate}\r\n" \ + "Status: #{status}\r\n" \ + "Connection: close\r\n" headers.each do |key, value| - next if SKIP.include?(key.downcase) + next if %r{\A(?:Date\z|Connection\z)}i =~ key if value =~ /\n/ # avoiding blank, key-only cookies with /\n+/ - out.concat(value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }) + buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join else - out << "#{key}: #{value}\r\n" + buf << "#{key}: #{value}\r\n" end end - - # Rack should enforce Content-Length or chunked transfer encoding, - # so don't worry or care about them. - # Date is required by HTTP/1.1 as long as our clock can be trusted. - # Some broken clients require a "Status" header so we accomodate them - socket.write("HTTP/1.1 #{status}\r\n" \ - "Date: #{Time.now.httpdate}\r\n" \ - "Status: #{status}\r\n" \ - "Connection: close\r\n" \ - "#{out.join('')}\r\n") + socket.write(buf << CRLF) end body.each { |chunk| socket.write(chunk) } |