Module: Unicorn::HttpResponse

Included in:
HttpServer
Defined in:
lib/unicorn/http_response.rb

Overview

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)
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 "Date" and "Status" header.

TODO: allow keepalive

Constant Summary collapse

CODES =

Every standard HTTP code mapped to the appropriate message.

Rack::Utils::HTTP_STATUS_CODES.inject({}) { |hash,(code,msg)|
  hash[code] = "#{code} #{msg}"
  hash
}
CRLF =
"\r\n"

Instance Method Summary collapse

Instance Method Details

#http_response_write(socket, rack_response) ⇒ Object

writes the rack_response to socket as an HTTP response



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/unicorn/http_response.rb', line 24

def http_response_write(socket, rack_response)
  status, headers, body = rack_response
  status = CODES[status.to_i] || status

  if headers
    buf = "HTTP/1.1 #{status}\r\n" \
          "Date: #{Time.now.httpdate}\r\n" \
          "Status: #{status}\r\n" \
          "Connection: close\r\n"
    headers.each do |key, value|
      next if %r{\A(?:Date\z|Status\z|Connection\z)}i =~ key
      if value =~ /\n/
        # avoiding blank, key-only cookies with /\n+/
        buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join('')
      else
        buf << "#{key}: #{value}\r\n"
      end
    end
    socket.write(buf << CRLF)
  end

  body.each { |chunk| socket.write(chunk) }
  socket.close # flushes and uncorks the socket immediately
  ensure
    body.respond_to?(:close) and body.close
end