unicorn.git  about / heads / tags
Rack HTTP server for Unix and fast clients
blob 7bbb940d3edf311a928ce596d006ca78fc7f75c4 2418 bytes (raw)
$ git show v0.1.0:lib/unicorn/http_response.rb	# shows this blob on the CLI

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
 
require 'time'

module Unicorn
  # 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 ])
  #
  # 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.

  class HttpResponse

    # headers we allow duplicates for
    ALLOWED_DUPLICATES = {
      'Set-Cookie' => true,
      'Set-Cookie2' => true,
      'Warning' => true,
      'WWW-Authenticate' => true,
    }.freeze

    # writes the rack_response to socket as an HTTP response
    def self.write(socket, rack_response)
      status, headers, body = rack_response

      # Rack does not set/require Date, but don't worry about Content-Length
      # since Rack applications that conform to Rack::Lint enforce that
      out = [ "#{Const::DATE}: #{Time.now.httpdate}" ]
      sent = { Const::CONNECTION => true, Const::DATE => true }

      headers.each do |key, value|
        if ! sent[key] || ALLOWED_DUPLICATES[key]
          sent[key] = true
          out << "#{key}: #{value}"
        end
      end

      socket_write(socket,
                   "HTTP/1.1 #{status} #{HTTP_STATUS_CODES[status]}\r\n" \
                   "Connection: close\r\n" \
                   "#{out.join("\r\n")}\r\n\r\n")
      body.each { |chunk| socket_write(socket, chunk) }
    end

    private

      # write(2) can return short on slow devices like sockets as well
      # as fail with EINTR if a signal was caught.
      def self.socket_write(socket, buffer)
        loop do
          begin
            written = socket.syswrite(buffer)
            return written if written == buffer.length
            buffer = buffer[written..-1]
          rescue Errno::EINTR
            retry
          end
        end
      end

  end
end

git clone https://yhbt.net/unicorn.git