about summary refs log tree commit homepage
path: root/lib
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2009-02-06 14:52:10 -0800
committerEric Wong <normalperson@yhbt.net>2009-02-09 19:50:54 -0800
commit25bbb88bcc16c1c0a10efc99472b05e9f6b45861 (patch)
tree4921a360284b9ccdda99fd9e0593160b0432649b /lib
parent33cf16e950d32db97ef03fa304eb2b73e9b3cc87 (diff)
downloadunicorn-25bbb88bcc16c1c0a10efc99472b05e9f6b45861.tar.gz
Just stuff what little logic we had for it into HttpResponse
since Rack takes care of the rest for us.

Put the HTTP_STATUS_HEADERS hash in HttpResponse since we're the
only user of it.   Also, change HttpResponse.send to
HttpResponse.write to avoid overriding the default method.
Diffstat (limited to 'lib')
-rw-r--r--lib/unicorn.rb25
-rw-r--r--lib/unicorn/const.rb6
-rw-r--r--lib/unicorn/header_out.rb47
-rw-r--r--lib/unicorn/http_response.rb44
4 files changed, 44 insertions, 78 deletions
diff --git a/lib/unicorn.rb b/lib/unicorn.rb
index a301df6..af99474 100644
--- a/lib/unicorn.rb
+++ b/lib/unicorn.rb
@@ -18,7 +18,6 @@ require 'rack'
 require 'unicorn/socket'
 require 'unicorn/const'
 require 'unicorn/http_request'
-require 'unicorn/header_out'
 require 'unicorn/http_response'
 
 # Unicorn module containing all of the classes (include C extensions) for running
@@ -66,9 +65,11 @@ module Unicorn
     # HttpServer.workers.join to join the thread that's processing
     # incoming requests on the socket.
     def initialize(app, options = {})
-      @app = app      
+      @app = app
       @workers = WorkerTable.new
-      
+      @parser = HttpParser.new
+      @params = Hash.new
+
       (DEFAULTS.to_a + options.to_a).each do |key, value|
         instance_variable_set("@#{key.to_s.downcase}", value)
       end
@@ -83,9 +84,9 @@ module Unicorn
     # thinks they can make it faster is more than welcome to take a crack at it.
     def process_client(client)
       begin
-        parser = HttpParser.new
-        params = Hash.new
-        request = nil
+        parser, params = @parser, @params
+        parser.reset
+        params.clear
         data = client.readpartial(Const::CHUNK_SIZE)
         nparsed = 0
 
@@ -117,10 +118,9 @@ module Unicorn
             params[Const::REMOTE_ADDR] = client.unicorn_peeraddr.last
 
             # Select handlers that want more detailed request notification
-            request = $http_request ||= HttpRequest.new(logger)
-            env = request.consume(params, client) or break
+            env = @request.consume(params, client) or break
             app_response = @app.call(env)
-            HttpResponse.send(client, app_response)
+            HttpResponse.write(client, app_response)
           break #done
           else
             # Parser is not done, queue up more data to read and continue parsing
@@ -152,7 +152,7 @@ module Unicorn
           logger.error "Client error: #{e.inspect}"
           logger.error e.backtrace.join("\n")
         end
-        request.reset! if request
+        @request.reset!
       end
     end
 
@@ -167,7 +167,10 @@ module Unicorn
 
       (1..@nr_workers).each do |worker_nr|
         pid = fork do
-          nr, alive, listeners = 0, true, @listeners
+          nr = 0
+          alive = true
+          listeners = @listeners
+          @request = HttpRequest.new(logger)
           trap('TERM') { exit 0 }
           trap('QUIT') do
             alive = false
diff --git a/lib/unicorn/const.rb b/lib/unicorn/const.rb
index 7c236c6..73c334c 100644
--- a/lib/unicorn/const.rb
+++ b/lib/unicorn/const.rb
@@ -44,12 +44,6 @@ module Unicorn
     505  => 'HTTP Version not supported'
   }
 
-  HTTP_STATUS_HEADERS = HTTP_STATUS_CODES.inject({}) do |hash, (code, text)|
-    text.freeze
-    hash[code] = "HTTP/1.1 #{code} #{text}\r\nConnection: close\r\n".freeze
-    hash
-  end
-
   # Frequently used constants when constructing requests or responses.  Many times
   # the constant just refers to a string with the same contents.  Using these constants
   # gave about a 3% to 10% performance improvement over using the strings directly.
diff --git a/lib/unicorn/header_out.rb b/lib/unicorn/header_out.rb
deleted file mode 100644
index 95f2633..0000000
--- a/lib/unicorn/header_out.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-module Unicorn
-  # This class implements a simple way of constructing the HTTP headers dynamically
-  # via a Hash syntax.  Think of it as a write-only Hash.  Refer to HttpResponse for
-  # information on how this is used.
-  #
-  # One consequence of this write-only nature is that you can write multiple headers
-  # by just doing them twice (which is sometimes needed in HTTP), but that the normal
-  # semantics for Hash (where doing an insert replaces) is not there.
-  class HeaderOut
-    ALLOWED_DUPLICATES = {
-      'Set-Cookie' => true,
-      'Set-Cookie2' => true,
-      'Warning' => true,
-      'WWW-Authenticate' => true,
-    }.freeze
-
-    def initialize
-      @sent = { Const::CONNECTION => true }
-      @out = []
-    end
-
-    def reset!
-      @sent.clear
-      @out.clear
-      @sent[Const::CONNECTION] = true
-    end
-
-    def merge!(hash)
-      hash.each do |key, value|
-        self[key] = value
-      end
-    end
-
-    # Simply writes "#{key}: #{value}" to an output buffer.
-    def[]=(key,value)
-      if not @sent.has_key?(key) or ALLOWED_DUPLICATES.has_key?(key)
-        @sent[key] = true
-        @out << "#{key}: #{value}\r\n"
-      end
-    end
-
-    def to_s
-      @out.join
-    end
-
-  end
-end
diff --git a/lib/unicorn/http_response.rb b/lib/unicorn/http_response.rb
index 4ffe64b..e2a4e2f 100644
--- a/lib/unicorn/http_response.rb
+++ b/lib/unicorn/http_response.rb
@@ -1,14 +1,13 @@
 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.send(socket, [ status, headers, body ])
+  #   HttpResponse.write(socket, [ status, headers, body ])
   #
-  # Most header correctness (including Content-Length) is the job of
-  # Rack, with the exception of the "Connection: close" and "Date"
-  # headers.
+  # 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
@@ -20,19 +19,36 @@ module Unicorn
 
   class HttpResponse
 
-    # we'll have one of these per-process
-    HEADERS = HeaderOut.new unless defined?(HEADERS)
+    # enforce "Connection: close" usage on all our responses
+    HTTP_STATUS_HEADERS = HTTP_STATUS_CODES.inject({}) do |hash, (code, text)|
+      hash[code] = "HTTP/1.1 #{code} #{text}\r\nConnection: close".freeze
+      hash
+    end.freeze
+
+    # headers we allow duplicates for
+    ALLOWED_DUPLICATES = {
+      'Set-Cookie' => true,
+      'Set-Cookie2' => true,
+      'Warning' => true,
+      'WWW-Authenticate' => true,
+    }.freeze
 
-    def self.send(socket, rack_response)
+    def self.write(socket, rack_response)
       status, headers, body = rack_response
-      HEADERS.reset!
 
-      # Rack does not set Date, but don't worry about Content-Length,
-      # since Rack enforces that in Rack::Lint
-      HEADERS[Const::DATE] = Time.now.httpdate
-      HEADERS.merge!(headers)
+      # Rack does not set/require Date, but don't worry about Content-Length
+      # since Rack enforces that in Rack::Lint.
+      out = [ "#{Const::DATE}: #{Time.now.httpdate}\r\n" ]
+      sent = { Const::CONNECTION => true, Const::DATE => true }
+
+      headers.each_pair do |key, value|
+        if ! sent[key] || ALLOWED_DUPLICATES[key]
+          sent[key] = true
+          out << "#{key}: #{value}\r\n"
+        end
+      end
 
-      socket.write("#{HTTP_STATUS_HEADERS[status]}#{HEADERS.to_s}\r\n")
+      socket.write("#{HTTP_STATUS_HEADERS[status]}\r\n#{out.join}\r\n")
       body.each { |chunk| socket.write(chunk) }
     end