diff options
Diffstat (limited to 'test')
-rw-r--r-- | test/benchmark/README | 55 | ||||
-rw-r--r-- | test/benchmark/big_request.rb | 35 | ||||
-rw-r--r-- | test/benchmark/dd.ru | 18 | ||||
-rw-r--r-- | test/benchmark/previous.rb | 11 | ||||
-rw-r--r-- | test/benchmark/request.rb | 47 | ||||
-rw-r--r-- | test/benchmark/response.rb | 29 | ||||
-rw-r--r-- | test/benchmark/simple.rb | 11 | ||||
-rw-r--r-- | test/benchmark/utils.rb | 82 | ||||
-rw-r--r-- | test/unit/test_http_parser.rb | 113 | ||||
-rw-r--r-- | test/unit/test_socket_helper.rb | 159 |
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 |