4 require 'unicorn/http11'
11 # default parameters we merge into the request env for Rack handlers
13 "rack.errors" => $stderr,
14 "rack.multiprocess" => true,
15 "rack.multithread" => false,
16 "rack.run_once" => false,
17 "rack.version" => [1, 0].freeze,
18 "SCRIPT_NAME" => "".freeze,
20 # this is not in the Rack spec, but some apps may rely on it
21 "SERVER_SOFTWARE" => "Unicorn #{Const::UNICORN_VERSION}".freeze
24 NULL_IO = StringIO.new(Z)
25 LOCALHOST = '127.0.0.1'.freeze
27 # Being explicitly single-threaded, we have certain advantages in
28 # not having to worry about variables being clobbered :)
29 BUFFER = ' ' * Const::CHUNK_SIZE # initial size, may grow
30 BUFFER.force_encoding(Encoding::BINARY) if Z.respond_to?(:force_encoding)
31 PARSER = HttpParser.new
34 def initialize(logger = Configurator::DEFAULT_LOGGER)
38 # Does the majority of the IO processing. It has been written in
39 # Ruby using about 8 different IO processing strategies.
41 # It is currently carefully constructed to make sure that it gets
42 # the best possible performance for the common case: GET requests
43 # that are fully complete after a single read(2)
45 # Anyone who thinks they can make it faster is more than welcome to
48 # returns an environment hash suitable for Rack if successful
49 # This does minimal exception trapping and it is up to the caller
50 # to handle any socket errors (e.g. user aborted upload).
55 # From http://www.ietf.org/rfc/rfc3875:
56 # "Script authors should be aware that the REMOTE_ADDR and
57 # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
58 # may not identify the ultimate source of the request. They
59 # identify the client for the immediate request to the server;
60 # that client may be a proxy, gateway, or other intermediary
61 # acting on behalf of the actual source client."
62 PARAMS[Const::REMOTE_ADDR] =
63 TCPSocket === socket ? socket.peeraddr.last : LOCALHOST
65 # short circuit the common case with small GET requests first
66 PARSER.execute(PARAMS, socket.readpartial(Const::CHUNK_SIZE, BUFFER)) and
67 return handle_body(socket)
69 data = BUFFER.dup # socket.readpartial will clobber BUFFER
71 # Parser is not done, queue up more data to read and continue parsing
72 # an Exception thrown from the PARSER will throw us out of the loop
74 data << socket.readpartial(Const::CHUNK_SIZE, BUFFER)
75 PARSER.execute(PARAMS, data) and return handle_body(socket)
77 rescue HttpParserError => e
78 @logger.error "HTTP parse error, malformed request " \
79 "(#{PARAMS[Const::HTTP_X_FORWARDED_FOR] ||
80 PARAMS[Const::REMOTE_ADDR]}): #{e.inspect}"
81 @logger.error "REQUEST DATA: #{data.inspect}\n---\n" \
82 "PARAMS: #{PARAMS.inspect}\n---\n"
88 # Handles dealing with the rest of the request
89 # returns a Rack environment if successful
90 def handle_body(socket)
91 PARAMS[Const::RACK_INPUT] = if (body = PARAMS.delete(:http_body))
92 length = PARAMS[Const::CONTENT_LENGTH].to_i
94 if te = PARAMS[Const::HTTP_TRANSFER_ENCODING]
95 if /\Achunked\z/i =~ te
96 socket = ChunkedReader.new(PARAMS, socket, body)
101 TeeInput.new(socket, length, body)
103 NULL_IO.closed? ? NULL_IO.reopen(Z) : NULL_IO
106 PARAMS.update(DEFAULTS)