about summary refs log tree commit homepage
path: root/lib/unicorn/http_response.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/unicorn/http_response.rb')
-rw-r--r--lib/unicorn/http_response.rb53
1 files changed, 29 insertions, 24 deletions
diff --git a/lib/unicorn/http_response.rb b/lib/unicorn/http_response.rb
index 3bf9347..96e484b 100644
--- a/lib/unicorn/http_response.rb
+++ b/lib/unicorn/http_response.rb
@@ -1,3 +1,5 @@
+# -*- encoding: binary -*-
+
 require 'time'
 
 module Unicorn
@@ -30,40 +32,43 @@ 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, 'status' => true }.freeze
-    EMPTY = ''.freeze # :nodoc
-    OUT = [] # :nodoc
+    SKIP = { 'connection' => true, 'date' => true, 'status' => true }
 
     # writes the rack_response to socket as an HTTP response
-    def self.write(socket, rack_response)
+    def self.write(socket, rack_response, have_header = true)
       status, headers, body = rack_response
-      status = CODES[status.to_i] || status
-      OUT.clear
 
-      # 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
-      headers.each do |key, value|
-        next if SKIP.include?(key.downcase)
-        if value =~ /\n/
-          value.split(/\n/).each { |v| OUT << "#{key}: #{v}\r\n" }
-        else
-          OUT << "#{key}: #{value}\r\n"
+      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
+        headers.each do |key, value|
+          next if SKIP.include?(key.downcase)
+          if value =~ /\n/
+            # avoiding blank, key-only cookies with /\n+/
+            out.concat(value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" })
+          else
+            out << "#{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")
       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(EMPTY)}\r\n")
       body.each { |chunk| socket.write(chunk) }
       socket.close # flushes and uncorks the socket immediately
       ensure
-        body.respond_to?(:close) and body.close rescue nil
+        body.respond_to?(:close) and body.close
     end
 
   end