about summary refs log tree commit homepage
path: root/lib/unicorn/http_request.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/unicorn/http_request.rb')
-rw-r--r--lib/unicorn/http_request.rb112
1 files changed, 55 insertions, 57 deletions
diff --git a/lib/unicorn/http_request.rb b/lib/unicorn/http_request.rb
index 65870ed..2dcd839 100644
--- a/lib/unicorn/http_request.rb
+++ b/lib/unicorn/http_request.rb
@@ -2,70 +2,68 @@
 
 require 'unicorn_http'
 
-module Unicorn
-  class HttpRequest
+# TODO: remove redundant names
+Unicorn.const_set(:HttpRequest, Unicorn::HttpParser)
+class Unicorn::HttpParser
 
-    # default parameters we merge into the request env for Rack handlers
-    DEFAULTS = {
-      "rack.errors" => $stderr,
-      "rack.multiprocess" => true,
-      "rack.multithread" => false,
-      "rack.run_once" => false,
-      "rack.version" => [1, 1],
-      "SCRIPT_NAME" => "",
+  # default parameters we merge into the request env for Rack handlers
+  DEFAULTS = {
+    "rack.errors" => $stderr,
+    "rack.multiprocess" => true,
+    "rack.multithread" => false,
+    "rack.run_once" => false,
+    "rack.version" => [1, 1],
+    "SCRIPT_NAME" => "",
 
-      # this is not in the Rack spec, but some apps may rely on it
-      "SERVER_SOFTWARE" => "Unicorn #{Const::UNICORN_VERSION}"
-    }
+    # this is not in the Rack spec, but some apps may rely on it
+    "SERVER_SOFTWARE" => "Unicorn #{Unicorn::Const::UNICORN_VERSION}"
+  }
 
-    NULL_IO = StringIO.new("")
-    LOCALHOST = '127.0.0.1'
+  NULL_IO = StringIO.new("")
 
-    # Being explicitly single-threaded, we have certain advantages in
-    # not having to worry about variables being clobbered :)
-    BUF = ""
-    PARSER = HttpParser.new
-    REQ = {}
+  # :stopdoc:
+  # A frozen format for this is about 15% faster
+  REMOTE_ADDR = 'REMOTE_ADDR'.freeze
+  RACK_INPUT = 'rack.input'.freeze
+  TeeInput = Unicorn::TeeInput
+  # :startdoc:
 
-    # Does the majority of the IO processing.  It has been written in
-    # Ruby using about 8 different IO processing strategies.
-    #
-    # It is currently carefully constructed to make sure that it gets
-    # the best possible performance for the common case: GET requests
-    # that are fully complete after a single read(2)
-    #
-    # Anyone who thinks they can make it faster is more than welcome to
-    # take a crack at it.
-    #
-    # returns an environment hash suitable for Rack if successful
-    # This does minimal exception trapping and it is up to the caller
-    # to handle any socket errors (e.g. user aborted upload).
-    def read(socket)
-      REQ.clear
-      PARSER.reset
+  # Does the majority of the IO processing.  It has been written in
+  # Ruby using about 8 different IO processing strategies.
+  #
+  # It is currently carefully constructed to make sure that it gets
+  # the best possible performance for the common case: GET requests
+  # that are fully complete after a single read(2)
+  #
+  # Anyone who thinks they can make it faster is more than welcome to
+  # take a crack at it.
+  #
+  # returns an environment hash suitable for Rack if successful
+  # This does minimal exception trapping and it is up to the caller
+  # to handle any socket errors (e.g. user aborted upload).
+  def read(socket)
+    reset
+    e = env
 
-      # From http://www.ietf.org/rfc/rfc3875:
-      # "Script authors should be aware that the REMOTE_ADDR and
-      #  REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
-      #  may not identify the ultimate source of the request.  They
-      #  identify the client for the immediate request to the server;
-      #  that client may be a proxy, gateway, or other intermediary
-      #  acting on behalf of the actual source client."
-      REQ[Const::REMOTE_ADDR] =
-                    TCPSocket === socket ? socket.peeraddr[-1] : LOCALHOST
+    # From http://www.ietf.org/rfc/rfc3875:
+    # "Script authors should be aware that the REMOTE_ADDR and
+    #  REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
+    #  may not identify the ultimate source of the request.  They
+    #  identify the client for the immediate request to the server;
+    #  that client may be a proxy, gateway, or other intermediary
+    #  acting on behalf of the actual source client."
+    e[REMOTE_ADDR] = socket.kgio_addr
 
-      # short circuit the common case with small GET requests first
-      if PARSER.headers(REQ, socket.readpartial(Const::CHUNK_SIZE, BUF)).nil?
-        # Parser is not done, queue up more data to read and continue parsing
-        # an Exception thrown from the PARSER will throw us out of the loop
-        begin
-          BUF << socket.readpartial(Const::CHUNK_SIZE)
-        end while PARSER.headers(REQ, BUF).nil?
-      end
-      REQ[Const::RACK_INPUT] = 0 == PARSER.content_length ?
-                   NULL_IO : Unicorn::TeeInput.new(socket, REQ, PARSER, BUF)
-      REQ.update(DEFAULTS)
+    # short circuit the common case with small GET requests first
+    socket.kgio_read!(16384, buf)
+    if parse.nil?
+      # Parser is not done, queue up more data to read and continue parsing
+      # an Exception thrown from the parser will throw us out of the loop
+      begin
+        buf << socket.kgio_read!(16384)
+      end while parse.nil?
     end
-
+    e[RACK_INPUT] = 0 == content_length ? NULL_IO : TeeInput.new(socket, self)
+    e.merge!(DEFAULTS)
   end
 end