diff options
-rw-r--r-- | lib/unicorn/http_response.rb | 10 | ||||
-rw-r--r-- | test/unit/test_response.rb | 21 |
2 files changed, 28 insertions, 3 deletions
diff --git a/lib/unicorn/http_response.rb b/lib/unicorn/http_response.rb index 98759f1..c0b1081 100644 --- a/lib/unicorn/http_response.rb +++ b/lib/unicorn/http_response.rb @@ -24,12 +24,16 @@ module Unicorn # 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 }.freeze + SKIP = { 'connection' => true, 'date' => true, 'status' => true }.freeze # writes the rack_response to socket as an HTTP response def self.write(socket, rack_response) status, headers, body = rack_response - out = [ "Date: #{Time.now.httpdate}" ] + status = "#{status} #{HTTP_STATUS_CODES[status]}" + + # 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 + out = [ "Date: #{Time.now.httpdate}", "Status: #{status}" ] # 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 @@ -45,7 +49,7 @@ module Unicorn # Rack should enforce Content-Length or chunked transfer encoding, # so don't worry or care about them. socket_write(socket, - "HTTP/1.1 #{status} #{HTTP_STATUS_CODES[status]}\r\n" \ + "HTTP/1.1 #{status}\r\n" \ "Connection: close\r\n" \ "#{out.join("\r\n")}\r\n\r\n") body.each { |chunk| socket_write(socket, chunk) } diff --git a/test/unit/test_response.rb b/test/unit/test_response.rb index 203ae4d..8abc86c 100644 --- a/test/unit/test_response.rb +++ b/test/unit/test_response.rb @@ -54,6 +54,27 @@ class ResponseTest < Test::Unit::TestCase assert_match(/^X-Whatever: stuff\r\nX-Whatever: bleh\r\n/, out.string) end + # Even though Rack explicitly forbids "Status" in the header hash, + # some broken clients still rely on it + def test_status_header_added + out = StringIO.new + HttpResponse.write(out,[200, {"X-Whatever" => "stuff"}, []]) + assert out.closed? + assert_match(/^Status: 200 OK\r\nX-Whatever: stuff\r\n/, out.string) + end + + # we always favor the code returned by the application, since "Status" + # in the header hash is not allowed by Rack (but not every app is + # fully Rack-compliant). + def test_status_header_ignores_app_hash + out = StringIO.new + header_hash = {"X-Whatever" => "stuff", 'StaTus' => "666" } + HttpResponse.write(out,[200, header_hash, []]) + assert out.closed? + assert_match(/^Status: 200 OK\r\nX-Whatever: stuff\r\n/, out.string) + assert_equal 1, out.string.split(/\r\n/).grep(/^Status:/i).size + end + def test_body_closed expect_body = %w(1 2 3 4).join("\n") body = StringIO.new(expect_body) |