about summary refs log tree commit homepage
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/benchmark/README55
-rw-r--r--test/benchmark/big_request.rb35
-rw-r--r--test/benchmark/dd.ru18
-rw-r--r--test/benchmark/previous.rb11
-rw-r--r--test/benchmark/request.rb47
-rw-r--r--test/benchmark/response.rb29
-rw-r--r--test/benchmark/simple.rb11
-rw-r--r--test/benchmark/utils.rb82
-rw-r--r--test/unit/test_http_parser.rb113
-rw-r--r--test/unit/test_socket_helper.rb159
10 files changed, 420 insertions, 140 deletions
diff --git a/test/benchmark/README b/test/benchmark/README
new file mode 100644
index 0000000..b63b8a3
--- /dev/null
+++ b/test/benchmark/README
@@ -0,0 +1,55 @@
+= Performance
+
+Unicorn is pretty fast, and we want it to get faster.  Unicorn strives
+to get HTTP requests to your application and write HTTP responses back
+as quickly as possible.  Unicorn does not do any background processing
+while your app runs, so your app will get all the CPU time provided to
+it by your OS kernel.
+
+A gentle reminder: Unicorn is NOT for serving clients over slow network
+connections.  Use nginx (or something similar) to complement Unicorn if
+you have slow clients.
+
+== dd.ru
+
+This is a pure I/O benchmark.  In the context of Unicorn, this is the
+only one that matters.  It is a standard rackup-compatible .ru file and
+may be used with other Rack-compatible servers.
+
+  unicorn -E none dd.ru
+
+You can change the size and number of chunks in the response with
+the "bs" and "count" environment variables.   The following command
+will cause dd.ru to return 4 chunks of 16384 bytes each, leading to
+65536 byte response:
+
+  bs=16384 count=4 unicorn -E none dd.ru
+
+Or if you want to add logging (small performance impact):
+
+  unicorn -E deployment dd.ru
+
+Eric runs then runs clients on a LAN it in several different ways:
+
+  client@host1 -> unicorn@host1(tcp)
+  client@host2 -> unicorn@host1(tcp)
+  client@host3 -> nginx@host1 -> unicorn@host1(tcp)
+  client@host3 -> nginx@host1 -> unicorn@host1(unix)
+  client@host3 -> nginx@host2 -> unicorn@host1(tcp)
+
+The benchmark client is usually httperf.
+
+Another gentle reminder: performance with slow networks/clients
+is NOT our problem.  That is the job of nginx (or similar).
+
+== request.rb, response.rb, big_request.rb
+
+These are micro-benchmarks designed to test internal components
+of Unicorn.  It assumes the internal Unicorn API is mostly stable.
+
+== Contributors
+
+This directory is maintained independently in the "benchmark" branch
+based against v0.1.0.  Only changes to this directory (test/benchmarks)
+are committed to this branch although the master branch may merge this
+branch occassionaly.
diff --git a/test/benchmark/big_request.rb b/test/benchmark/big_request.rb
new file mode 100644
index 0000000..5f2111b
--- /dev/null
+++ b/test/benchmark/big_request.rb
@@ -0,0 +1,35 @@
+require 'benchmark'
+require 'tempfile'
+require 'unicorn'
+nr = ENV['nr'] ? ENV['nr'].to_i : 100
+bs = ENV['bs'] ? ENV['bs'].to_i : (1024 * 1024)
+count = ENV['count'] ? ENV['count'].to_i : 4
+length = bs * count
+slice = (' ' * bs).freeze
+
+big = Tempfile.new('')
+def big.unicorn_peeraddr; '127.0.0.1'; end
+big.syswrite(
+"PUT /hello/world/puturl?abcd=efg&hi#anchor HTTP/1.0\r\n" \
+"Host: localhost\r\n" \
+"Accept: */*\r\n" \
+"Content-Length: #{length}\r\n" \
+"User-Agent: test-user-agent 0.1.0 (Mozilla compatible) 5.0 asdfadfasda\r\n" \
+"\r\n")
+count.times { big.syswrite(slice) }
+big.sysseek(0)
+big.fsync
+
+include Unicorn
+request = HttpRequest.new(Logger.new($stderr))
+
+Benchmark.bmbm do |x|
+  x.report("big") do
+    for i in 1..nr
+      request.read(big)
+      request.reset
+      big.sysseek(0)
+    end
+  end
+end
+
diff --git a/test/benchmark/dd.ru b/test/benchmark/dd.ru
new file mode 100644
index 0000000..111fa2e
--- /dev/null
+++ b/test/benchmark/dd.ru
@@ -0,0 +1,18 @@
+# This benchmark is the simplest test of the I/O facilities in
+# unicorn.  It is meant to return a fixed-sized blob to test
+# the performance of things in Unicorn, _NOT_ the app.
+#
+# Adjusting this benchmark is done via the "bs" (byte size) and "count"
+# environment variables.  "count" designates the count of elements of
+# "bs" length in the Rack response body.  The defaults are bs=4096, count=1
+# to return one 4096-byte chunk.
+bs = ENV['bs'] ? ENV['bs'].to_i : 4096
+count = ENV['count'] ? ENV['count'].to_i : 1
+slice = (' ' * bs).freeze
+body = (1..count).map { slice }.freeze
+hdr = {
+  'Content-Length' => (bs * count).to_s.freeze,
+  'Content-Type' => 'text/plain'.freeze
+}.freeze
+response = [ 200, hdr, body ].freeze
+run(lambda { |env| response })
diff --git a/test/benchmark/previous.rb b/test/benchmark/previous.rb
deleted file mode 100644
index 8b6182a..0000000
--- a/test/benchmark/previous.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-# Benchmark to compare Mongrel performance against
-# previous Mongrel version (the one installed as a gem).
-#
-# Run with:
-#
-#  ruby previous.rb [num of request]
-#
-
-require File.dirname(__FILE__) + '/utils'
-
-benchmark "print", %w(current gem), 1000, [1, 10, 100]
diff --git a/test/benchmark/request.rb b/test/benchmark/request.rb
new file mode 100644
index 0000000..67266cb
--- /dev/null
+++ b/test/benchmark/request.rb
@@ -0,0 +1,47 @@
+require 'benchmark'
+require 'unicorn'
+nr = ENV['nr'] ? ENV['nr'].to_i : 100000
+
+class TestClient
+  def initialize(response)
+    @response = (response.join("\r\n") << "\r\n\r\n").freeze
+  end
+  def sysread(len, buf)
+    buf.replace(@response)
+  end
+
+  def unicorn_peeraddr
+    '127.0.0.1'
+  end
+end
+
+small = TestClient.new([
+  'GET / HTTP/1.0',
+  'Host: localhost',
+  'Accept: */*',
+  'User-Agent: test-user-agent 0.1.0'
+])
+
+medium = TestClient.new([
+  'GET /hello/world/geturl?abcd=efg&hi#anchor HTTP/1.0',
+  'Host: localhost',
+  'Accept: */*',
+  'User-Agent: test-user-agent 0.1.0 (Mozilla compatible) 5.0 asdfadfasda'
+])
+
+include Unicorn
+request = HttpRequest.new(Logger.new($stderr))
+Benchmark.bmbm do |x|
+  x.report("small") do
+    for i in 1..nr
+      request.read(small)
+      request.reset
+    end
+  end
+  x.report("medium") do
+    for i in 1..nr
+      request.read(medium)
+      request.reset
+    end
+  end
+end
diff --git a/test/benchmark/response.rb b/test/benchmark/response.rb
new file mode 100644
index 0000000..0ff0ac2
--- /dev/null
+++ b/test/benchmark/response.rb
@@ -0,0 +1,29 @@
+require 'benchmark'
+require 'unicorn'
+
+class NullWriter
+  def syswrite(buf); buf.size; end
+  def close; end
+end
+
+include Unicorn
+
+socket = NullWriter.new
+bs = ENV['bs'] ? ENV['bs'].to_i : 4096
+count = ENV['count'] ? ENV['count'].to_i : 1
+slice = (' ' * bs).freeze
+body = (1..count).map { slice }.freeze
+hdr = {
+  'Content-Length' => (bs * count).to_s.freeze,
+  'Content-Type' => 'text/plain'.freeze
+}.freeze
+response = [ 200, hdr, body ].freeze
+
+nr = ENV['nr'] ? ENV['nr'].to_i : 100000
+Benchmark.bmbm do |x|
+  x.report do
+    for i in 1..nr
+      HttpResponse.write(socket.dup, response)
+    end
+  end
+end
diff --git a/test/benchmark/simple.rb b/test/benchmark/simple.rb
deleted file mode 100644
index 906f74c..0000000
--- a/test/benchmark/simple.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-#
-# Simple benchmark to compare Mongrel performance against
-# other webservers supported by Rack.
-#
-
-require File.dirname(__FILE__) + '/utils'
-
-libs = %w(current gem WEBrick EMongrel Thin)
-libs = ARGV if ARGV.any?
-
-benchmark "print", libs, 1000, [1, 10, 100]
diff --git a/test/benchmark/utils.rb b/test/benchmark/utils.rb
deleted file mode 100644
index feb22c1..0000000
--- a/test/benchmark/utils.rb
+++ /dev/null
@@ -1,82 +0,0 @@
-
-require 'rubygems'
-require 'rack'
-require 'rack/lobster'
-
-def run(handler_name, n=1000, c=1)
-  port = 7000
-  
-  server = fork do
-    [STDOUT, STDERR].each { |o| o.reopen "/dev/null" }
-      
-    case handler_name
-    when 'EMongrel'
-      require 'swiftcore/evented_mongrel'
-      handler_name = 'Mongrel'
-    
-    when 'Thin'
-      require 'thin'
-      hander_name = 'Thin'
-    
-    when 'gem' # Load the current Mongrel gem
-      require 'mongrel'
-      handler_name = 'Mongrel'
-    
-    when 'current' # Load the current Mongrel version under /lib
-      require File.dirname(__FILE__) + '/../lib/mongrel'
-      handler_name = 'Mongrel'
-      
-    end
-    
-    app = Rack::Lobster.new
-    
-    handler = Rack::Handler.const_get(handler_name)
-    handler.run app, :Host => '0.0.0.0', :Port => port
-  end
-
-  sleep 2
-
-  out = `nice -n20 ab -c #{c} -n #{n} http://127.0.0.1:#{port}/ 2> /dev/null`
-
-  Process.kill('SIGKILL', server)
-  Process.wait
-  
-  if requests = out.match(/^Requests.+?(\d+\.\d+)/)
-    requests[1].to_i
-  else
-    0
-  end
-end
-
-def benchmark(type, servers, request, concurrency_levels)
-  send "#{type}_benchmark", servers, request, concurrency_levels
-end
-
-def graph_benchmark(servers, request, concurrency_levels)
-  require '/usr/local/lib/ruby/gems/1.8/gems/gruff-0.2.9/lib/gruff'
-  g = Gruff::Area.new
-  g.title = "Server benchmark"
-  
-  servers.each do |server|
-    g.data(server, concurrency_levels.collect { |c| print '.'; run(server, request, c) })
-  end
-  puts
-  
-  g.x_axis_label = 'Concurrency'
-  g.y_axis_label = 'Requests / sec'
-  g.labels = {}
-  concurrency_levels.each_with_index { |c, i| g.labels[i] = c.to_s }
-  
-  g.write('bench.png')
-  `open bench.png`
-end
-
-def print_benchmark(servers, request, concurrency_levels)
-  puts 'server     request   concurrency   req/s'
-  puts '=' * 42
-  concurrency_levels.each do |c|
-    servers.each do |server|
-      puts "#{server.ljust(8)}   #{request}      #{c.to_s.ljust(4)}          #{run(server, request, c)}"
-    end
-  end
-end \ No newline at end of file
diff --git a/test/unit/test_http_parser.rb b/test/unit/test_http_parser.rb
index fc75990..1deeaa2 100644
--- a/test/unit/test_http_parser.rb
+++ b/test/unit/test_http_parser.rb
@@ -14,33 +14,40 @@ class HttpParserTest < Test::Unit::TestCase
     parser = HttpParser.new
     req = {}
     http = "GET / HTTP/1.1\r\n\r\n"
-    nread = parser.execute(req, http, 0)
-
-    assert nread == http.length, "Failed to parse the full HTTP request"
-    assert parser.finished?, "Parser didn't finish"
-    assert !parser.error?, "Parser had error"
-    assert nread == parser.nread, "Number read returned from execute does not match"
+    assert parser.execute(req, http)
 
     assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
     assert_equal '/', req['REQUEST_PATH']
     assert_equal 'HTTP/1.1', req['HTTP_VERSION']
     assert_equal '/', req['REQUEST_URI']
-    assert_equal 'GET', req['REQUEST_METHOD']    
+    assert_equal 'GET', req['REQUEST_METHOD']
     assert_nil req['FRAGMENT']
     assert_nil req['QUERY_STRING']
 
     parser.reset
-    assert parser.nread == 0, "Number read after reset should be 0"
+    req.clear
+
+    assert ! parser.execute(req, "G")
+    assert req.empty?
+
+    # try parsing again to ensure we were reset correctly
+    http = "GET /hello-world HTTP/1.1\r\n\r\n"
+    assert parser.execute(req, http)
+
+    assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
+    assert_equal '/hello-world', req['REQUEST_PATH']
+    assert_equal 'HTTP/1.1', req['HTTP_VERSION']
+    assert_equal '/hello-world', req['REQUEST_URI']
+    assert_equal 'GET', req['REQUEST_METHOD']
+    assert_nil req['FRAGMENT']
+    assert_nil req['QUERY_STRING']
   end
-
+
   def test_parse_strange_headers
     parser = HttpParser.new
     req = {}
     should_be_good = "GET / HTTP/1.1\r\naaaaaaaaaaaaa:++++++++++\r\n\r\n"
-    nread = parser.execute(req, should_be_good, 0)
-    assert_equal should_be_good.length, nread
-    assert parser.finished?
-    assert !parser.error?
+    assert parser.execute(req, should_be_good)
 
     # ref: http://thread.gmane.org/gmane.comp.lang.ruby.Unicorn.devel/37/focus=45
     # (note we got 'pen' mixed up with 'pound' in that thread,
@@ -49,10 +56,7 @@ class HttpParserTest < Test::Unit::TestCase
     # nasty_pound_header = "GET / HTTP/1.1\r\nX-SSL-Bullshit:   -----BEGIN CERTIFICATE-----\r\n\tMIIFbTCCBFWgAwIBAgICH4cwDQYJKoZIhvcNAQEFBQAwcDELMAkGA1UEBhMCVUsx\r\n\tETAPBgNVBAoTCGVTY2llbmNlMRIwEAYDVQQLEwlBdXRob3JpdHkxCzAJBgNVBAMT\r\n\tAkNBMS0wKwYJKoZIhvcNAQkBFh5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMu\r\n\tdWswHhcNMDYwNzI3MTQxMzI4WhcNMDcwNzI3MTQxMzI4WjBbMQswCQYDVQQGEwJV\r\n\tSzERMA8GA1UEChMIZVNjaWVuY2UxEzARBgNVBAsTCk1hbmNoZXN0ZXIxCzAJBgNV\r\n\tBAcTmrsogriqMWLAk1DMRcwFQYDVQQDEw5taWNoYWVsIHBhcmQYJKoZIhvcNAQEB\r\n\tBQADggEPADCCAQoCggEBANPEQBgl1IaKdSS1TbhF3hEXSl72G9J+WC/1R64fAcEF\r\n\tW51rEyFYiIeZGx/BVzwXbeBoNUK41OK65sxGuflMo5gLflbwJtHBRIEKAfVVp3YR\r\n\tgW7cMA/s/XKgL1GEC7rQw8lIZT8RApukCGqOVHSi/F1SiFlPDxuDfmdiNzL31+sL\r\n\t0iwHDdNkGjy5pyBSB8Y79dsSJtCW/iaLB0/n8Sj7HgvvZJ7x0fr+RQjYOUUfrePP\r\n\tu2MSpFyf+9BbC/aXgaZuiCvSR+8Snv3xApQY+fULK/xY8h8Ua51iXoQ5jrgu2SqR\r\n\twgA7BUi3G8LFzMBl8FRCDYGUDy7M6QaHXx1ZWIPWNKsCAwEAAaOCAiQwggIgMAwG\r\n\tA1UdEwEB/wQCMAAwEQYJYIZIAYb4QgEBBAQDAgWgMA4GA1UdDwEB/wQEAwID6DAs\r\n\tBglghkgBhvhCAQ0EHxYdVUsgZS1TY2llbmNlIFVzZXIgQ2VydGlmaWNhdGUwHQYD\r\n\tVR0OBBYEFDTt/sf9PeMaZDHkUIldrDYMNTBZMIGaBgNVHSMEgZIwgY+AFAI4qxGj\r\n\tloCLDdMVKwiljjDastqooXSkcjBwMQswCQYDVQQGEwJVSzERMA8GA1UEChMIZVNj\r\n\taWVuY2UxEjAQBgNVBAsTCUF1dGhvcml0eTELMAkGA1UEAxMCQ0ExLTArBgkqhkiG\r\n\t9w0BCQEWHmNhLW9wZXJhdG9yQGdyaWQtc3VwcG9ydC5hYy51a4IBADApBgNVHRIE\r\n\tIjAggR5jYS1vcGVyYXRvckBncmlkLXN1cHBvcnQuYWMudWswGQYDVR0gBBIwEDAO\r\n\tBgwrBgEEAdkvAQEBAQYwPQYJYIZIAYb4QgEEBDAWLmh0dHA6Ly9jYS5ncmlkLXN1\r\n\tcHBvcnQuYWMudmT4sopwqlBWsvcHViL2NybC9jYWNybC5jcmwwPQYJYIZIAYb4QgEDBDAWLmh0\r\n\tdHA6Ly9jYS5ncmlkLXN1cHBvcnQuYWMudWsvcHViL2NybC9jYWNybC5jcmwwPwYD\r\n\tVR0fBDgwNjA0oDKgMIYuaHR0cDovL2NhLmdyaWQt5hYy51ay9wdWIv\r\n\tY3JsL2NhY3JsLmNybDANBgkqhkiG9w0BAQUFAAOCAQEAS/U4iiooBENGW/Hwmmd3\r\n\tXCy6Zrt08YjKCzGNjorT98g8uGsqYjSxv/hmi0qlnlHs+k/3Iobc3LjS5AMYr5L8\r\n\tUO7OSkgFFlLHQyC9JzPfmLCAugvzEbyv4Olnsr8hbxF1MbKZoQxUZtMVu29wjfXk\r\n\thTeApBv7eaKCWpSp7MCbvgzm74izKhu3vlDk9w6qVrxePfGgpKPqfHiOoGhFnbTK\r\n\twTC6o2xq5y0qZ03JonF7OJspEd3I5zKY3E+ov7/ZhW6DqT8UFvsAdjvQbXyhV8Eu\r\n\tYhixw1aKEPzNjNowuIseVogKOLXxWI5vAi5HgXdS0/ES5gDGsABo4fqovUKlgop3\r\n\tRA==\r\n\t-----END CERTIFICATE-----\r\n\r\n"
     # parser = HttpParser.new
     # req = {}
-    # nread = parser.execute(req, nasty_pound_header, 0)
-    # assert_equal nasty_pound_header.length, nread
-    # assert parser.finished?
-    # assert !parser.error?
+    # assert parser.execute(req, nasty_pound_header, 0)
   end
 
   def test_parse_ie6_urls
@@ -66,10 +70,7 @@ class HttpParserTest < Test::Unit::TestCase
       parser = HttpParser.new
       req = {}
       sorta_safe = %(GET #{path} HTTP/1.1\r\n\r\n)
-      nread = parser.execute(req, sorta_safe, 0)
-      assert_equal sorta_safe.length, nread
-      assert parser.finished?
-      assert !parser.error?
+      assert parser.execute(req, sorta_safe)
     end
   end
   
@@ -78,28 +79,68 @@ class HttpParserTest < Test::Unit::TestCase
     req = {}
     bad_http = "GET / SsUTF/1.1"
 
-    error = false
-    begin
-      nread = parser.execute(req, bad_http, 0)
-    rescue => details
-      error = true
-    end
+    assert_raises(HttpParserError) { parser.execute(req, bad_http) }
+    parser.reset
+    assert(parser.execute({}, "GET / HTTP/1.0\r\n\r\n"))
+  end
 
-    assert error, "failed to throw exception"
-    assert !parser.finished?, "Parser shouldn't be finished"
-    assert parser.error?, "Parser SHOULD have error"
+  def test_piecemeal
+    parser = HttpParser.new
+    req = {}
+    http = "GET"
+    assert ! parser.execute(req, http)
+    assert_raises(HttpParserError) { parser.execute(req, http) }
+    assert ! parser.execute(req, http << " / HTTP/1.0")
+    assert_equal '/', req['REQUEST_PATH']
+    assert_equal '/', req['REQUEST_URI']
+    assert_equal 'GET', req['REQUEST_METHOD']
+    assert ! parser.execute(req, http << "\r\n")
+    assert_equal 'HTTP/1.0', req['HTTP_VERSION']
+    assert ! parser.execute(req, http << "\r")
+    assert parser.execute(req, http << "\n")
+    assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
+    assert_nil req['FRAGMENT']
+    assert_nil req['QUERY_STRING']
+  end
+
+  def test_put_body_oneshot
+    parser = HttpParser.new
+    req = {}
+    http = "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\nabcde"
+    assert parser.execute(req, http)
+    assert_equal '/', req['REQUEST_PATH']
+    assert_equal '/', req['REQUEST_URI']
+    assert_equal 'PUT', req['REQUEST_METHOD']
+    assert_equal 'HTTP/1.0', req['HTTP_VERSION']
+    assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
+    assert_equal "abcde", req['HTTP_BODY']
+  end
+
+  def test_put_body_later
+    parser = HttpParser.new
+    req = {}
+    http = "PUT /l HTTP/1.0\r\nContent-Length: 5\r\n\r\n"
+    assert parser.execute(req, http)
+    assert_equal '/l', req['REQUEST_PATH']
+    assert_equal '/l', req['REQUEST_URI']
+    assert_equal 'PUT', req['REQUEST_METHOD']
+    assert_equal 'HTTP/1.0', req['HTTP_VERSION']
+    assert_equal 'HTTP/1.1', req['SERVER_PROTOCOL']
+    assert_equal "", req['HTTP_BODY']
   end
 
   def test_fragment_in_uri
     parser = HttpParser.new
     req = {}
     get = "GET /forums/1/topics/2375?page=1#posts-17408 HTTP/1.1\r\n\r\n"
+    ok = false
     assert_nothing_raised do
-      parser.execute(req, get, 0)
+      ok = parser.execute(req, get)
     end
-    assert parser.finished?
+    assert ok
     assert_equal '/forums/1/topics/2375?page=1', req['REQUEST_URI']
     assert_equal 'posts-17408', req['FRAGMENT']
+    assert_equal 'page=1', req['QUERY_STRING']
   end
 
   # lame random garbage maker
@@ -124,7 +165,7 @@ class HttpParserTest < Test::Unit::TestCase
     10.times do |c|
       get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-#{rand_data(1024, 1024+(c*1024))}: Test\r\n\r\n"
       assert_raises Unicorn::HttpParserError do
-        parser.execute({}, get, 0)
+        parser.execute({}, get)
         parser.reset
       end
     end
@@ -133,7 +174,7 @@ class HttpParserTest < Test::Unit::TestCase
     10.times do |c|
       get = "GET /#{rand_data(10,120)} HTTP/1.1\r\nX-Test: #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
       assert_raises Unicorn::HttpParserError do
-        parser.execute({}, get, 0)
+        parser.execute({}, get)
         parser.reset
       end
     end
@@ -142,7 +183,7 @@ class HttpParserTest < Test::Unit::TestCase
     get = "GET /#{rand_data(10,120)} HTTP/1.1\r\n"
     get << "X-Test: test\r\n" * (80 * 1024)
     assert_raises Unicorn::HttpParserError do
-      parser.execute({}, get, 0)
+      parser.execute({}, get)
       parser.reset
     end
 
@@ -150,7 +191,7 @@ class HttpParserTest < Test::Unit::TestCase
     10.times do |c|
       get = "GET #{rand_data(1024, 1024+(c*1024), false)} #{rand_data(1024, 1024+(c*1024), false)}\r\n\r\n"
       assert_raises Unicorn::HttpParserError do
-        parser.execute({}, get, 0)
+        parser.execute({}, get)
         parser.reset
       end
     end
diff --git a/test/unit/test_socket_helper.rb b/test/unit/test_socket_helper.rb
new file mode 100644
index 0000000..23fa44c
--- /dev/null
+++ b/test/unit/test_socket_helper.rb
@@ -0,0 +1,159 @@
+require 'test/test_helper'
+require 'tempfile'
+
+class TestSocketHelper < Test::Unit::TestCase
+  include Unicorn::SocketHelper
+  attr_reader :logger
+  GET_SLASH = "GET / HTTP/1.0\r\n\r\n".freeze
+
+  def setup
+    @log_tmp = Tempfile.new 'logger'
+    @logger = Logger.new(@log_tmp.path)
+    @test_addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
+  end
+
+  def test_bind_listen_tcp
+    port = unused_port @test_addr
+    @tcp_listener_name = "#@test_addr:#{port}"
+    @tcp_listener = bind_listen(@tcp_listener_name)
+    assert Socket === @tcp_listener
+    assert_equal @tcp_listener_name, sock_name(@tcp_listener)
+  end
+
+  def test_bind_listen_options
+    port = unused_port @test_addr
+    tcp_listener_name = "#@test_addr:#{port}"
+    tmp = Tempfile.new 'unix.sock'
+    unix_listener_name = tmp.path
+    File.unlink(tmp.path)
+    [ { :backlog => 5 }, { :sndbuf => 4096 }, { :rcvbuf => 4096 },
+      { :backlog => 16, :rcvbuf => 4096, :sndbuf => 4096 }
+    ].each do |opts|
+      assert_nothing_raised do
+        tcp_listener = bind_listen(tcp_listener_name, opts)
+        assert Socket === tcp_listener
+        tcp_listener.close
+        unix_listener = bind_listen(unix_listener_name, opts)
+        assert Socket === unix_listener
+        unix_listener.close
+      end
+    end
+    #system('cat', @log_tmp.path)
+  end
+
+  def test_bind_listen_unix
+    tmp = Tempfile.new 'unix.sock'
+    @unix_listener_path = tmp.path
+    File.unlink(@unix_listener_path)
+    @unix_listener = bind_listen(@unix_listener_path)
+    assert Socket === @unix_listener
+    assert_equal @unix_listener_path, sock_name(@unix_listener)
+  end
+
+  def test_bind_listen_unix_idempotent
+    test_bind_listen_unix
+    a = bind_listen(@unix_listener)
+    assert_equal a.fileno, @unix_listener.fileno
+    unix_server = server_cast(@unix_listener)
+    a = bind_listen(unix_server)
+    assert_equal a.fileno, unix_server.fileno
+    assert_equal a.fileno, @unix_listener.fileno
+  end
+
+  def test_bind_listen_tcp_idempotent
+    test_bind_listen_tcp
+    a = bind_listen(@tcp_listener)
+    assert_equal a.fileno, @tcp_listener.fileno
+    tcp_server = server_cast(@tcp_listener)
+    a = bind_listen(tcp_server)
+    assert_equal a.fileno, tcp_server.fileno
+    assert_equal a.fileno, @tcp_listener.fileno
+  end
+
+  def test_bind_listen_unix_rebind
+    test_bind_listen_unix
+    new_listener = bind_listen(@unix_listener_path)
+    assert Socket === new_listener
+    assert new_listener.fileno != @unix_listener.fileno
+    assert_equal sock_name(new_listener), sock_name(@unix_listener)
+    assert_equal @unix_listener_path, sock_name(new_listener)
+    pid = fork do
+      client = server_cast(new_listener).accept
+      client.syswrite('abcde')
+      exit 0
+    end
+    s = UNIXSocket.new(@unix_listener_path)
+    IO.select([s])
+    assert_equal 'abcde', s.sysread(5)
+    pid, status = Process.waitpid2(pid)
+    assert status.success?
+  end
+
+  def test_server_cast
+    assert_nothing_raised do
+      test_bind_listen_unix
+      test_bind_listen_tcp
+    end
+    @unix_server = server_cast(@unix_listener)
+    assert_equal @unix_listener.fileno, @unix_server.fileno
+    assert UNIXServer === @unix_server
+    assert File.socket?(@unix_server.path)
+    assert_equal @unix_listener_path, sock_name(@unix_server)
+
+    @tcp_server = server_cast(@tcp_listener)
+    assert_equal @tcp_listener.fileno, @tcp_server.fileno
+    assert TCPServer === @tcp_server
+    assert_equal @tcp_listener_name, sock_name(@tcp_server)
+  end
+
+  def test_sock_name
+    test_server_cast
+    sock_name(@unix_server)
+  end
+
+  def test_tcp_unicorn_peeraddr
+    test_bind_listen_tcp
+    @tcp_server = server_cast(@tcp_listener)
+    tmp = Tempfile.new 'shared'
+    pid = fork do
+      client = @tcp_server.accept
+      IO.select([client])
+      assert_equal GET_SLASH, client.sysread(GET_SLASH.size)
+      tmp.syswrite "#{client.unicorn_peeraddr}"
+      exit 0
+    end
+    host, port = sock_name(@tcp_server).split(/:/)
+    client = TCPSocket.new(host, port.to_i)
+    client.syswrite(GET_SLASH)
+
+    pid, status = Process.waitpid2(pid)
+    assert_nothing_raised { client.close }
+    assert status.success?
+    tmp.sysseek 0
+    assert_equal @test_addr, tmp.sysread(4096)
+    tmp.sysseek 0
+  end
+
+  def test_unix_unicorn_peeraddr
+    test_bind_listen_unix
+    @unix_server = server_cast(@unix_listener)
+    tmp = Tempfile.new 'shared'
+    pid = fork do
+      client = @unix_server.accept
+      IO.select([client])
+      assert_equal GET_SLASH, client.sysread(4096)
+      tmp.syswrite "#{client.unicorn_peeraddr}"
+      exit 0
+    end
+    client = UNIXSocket.new(@unix_listener_path)
+    client.syswrite(GET_SLASH)
+
+    pid, status = Process.waitpid2(pid)
+    assert_nothing_raised { client.close }
+    assert status.success?
+    tmp.sysseek 0
+    assert_equal '127.0.0.1', tmp.sysread(4096)
+    tmp.sysseek 0
+  end
+
+end