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 PARSER = HttpParser.new
33 def initialize(logger = Configurator::DEFAULT_LOGGER)
37 # Does the majority of the IO processing. It has been written in
38 # Ruby using about 8 different IO processing strategies.
40 # It is currently carefully constructed to make sure that it gets
41 # the best possible performance for the common case: GET requests
42 # that are fully complete after a single read(2)
44 # Anyone who thinks they can make it faster is more than welcome to
47 # returns an environment hash suitable for Rack if successful
48 # This does minimal exception trapping and it is up to the caller
49 # to handle any socket errors (e.g. user aborted upload).
54 # From http://www.ietf.org/rfc/rfc3875:
55 # "Script authors should be aware that the REMOTE_ADDR and
56 # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
57 # may not identify the ultimate source of the request. They
58 # identify the client for the immediate request to the server;
59 # that client may be a proxy, gateway, or other intermediary
60 # acting on behalf of the actual source client."
61 PARAMS[Const::REMOTE_ADDR] =
62 TCPSocket === socket ? socket.peeraddr.last : LOCALHOST
64 # short circuit the common case with small GET requests first
65 PARSER.execute(PARAMS, socket.readpartial(Const::CHUNK_SIZE, BUFFER)) and
66 return handle_body(socket)
68 data = BUFFER.dup # socket.readpartial will clobber BUFFER
70 # Parser is not done, queue up more data to read and continue parsing
71 # an Exception thrown from the PARSER will throw us out of the loop
73 data << socket.readpartial(Const::CHUNK_SIZE, BUFFER)
74 PARSER.execute(PARAMS, data) and return handle_body(socket)
76 rescue HttpParserError => e
77 @logger.error "HTTP parse error, malformed request " \
78 "(#{PARAMS[Const::HTTP_X_FORWARDED_FOR] ||
79 PARAMS[Const::REMOTE_ADDR]}): #{e.inspect}"
80 @logger.error "REQUEST DATA: #{data.inspect}\n---\n" \
81 "PARAMS: #{PARAMS.inspect}\n---\n"
87 # Handles dealing with the rest of the request
88 # returns a Rack environment if successful
89 def handle_body(socket)
90 PARAMS[Const::RACK_INPUT] = if (body = PARAMS.delete(:http_body))
91 length = PARAMS[Const::CONTENT_LENGTH].to_i
93 if te = PARAMS[Const::HTTP_TRANSFER_ENCODING]
95 socket = ChunkedReader.new(socket, body)
100 inp = TeeInput.new(socket, length, body)
101 DEFAULTS[Const::STREAM_INPUT] ? inp : inp.consume
103 NULL_IO.closed? ? NULL_IO.reopen(Z) : NULL_IO
106 PARAMS.update(DEFAULTS)