diff options
Diffstat (limited to 'test/unit/test_server.rb')
-rw-r--r-- | test/unit/test_server.rb | 132 |
1 files changed, 129 insertions, 3 deletions
diff --git a/test/unit/test_server.rb b/test/unit/test_server.rb index 742b240..00705d0 100644 --- a/test/unit/test_server.rb +++ b/test/unit/test_server.rb @@ -1,3 +1,5 @@ +# -*- encoding: binary -*- + # Copyright (c) 2005 Zed A. Shaw # You can redistribute it and/or modify it under the same terms as Ruby. # @@ -10,9 +12,13 @@ include Unicorn class TestHandler - def call(env) - # response.socket.write("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\n\r\nhello!\n") + def call(env) + while env['rack.input'].read(4096) + end [200, { 'Content-Type' => 'text/plain' }, ['hello!\n']] + rescue Unicorn::ClientShutdown, Unicorn::HttpParserError => e + $stderr.syswrite("#{e.class}: #{e.message} #{e.backtrace.empty?}\n") + raise e end end @@ -31,6 +37,8 @@ class WebServerTest < Test::Unit::TestCase def teardown redirect_test_io do + wait_workers_ready("test_stderr.#$$.log", 1) + File.truncate("test_stderr.#$$.log", 0) @server.stop(true) end end @@ -51,8 +59,10 @@ class WebServerTest < Test::Unit::TestCase end results = hit(["http://localhost:#@port/"]) worker_pid = results[0].to_i + assert worker_pid != 0 tmp.sysseek(0) loader_pid = tmp.sysread(4096).to_i + assert loader_pid != 0 assert_equal worker_pid, loader_pid teardown @@ -63,6 +73,7 @@ class WebServerTest < Test::Unit::TestCase end results = hit(["http://localhost:#@port/"]) worker_pid = results[0].to_i + assert worker_pid != 0 tmp.sysseek(0) loader_pid = tmp.sysread(4096).to_i assert_equal $$, loader_pid @@ -94,6 +105,92 @@ class WebServerTest < Test::Unit::TestCase assert_equal 'hello!\n', results[0], "Handler didn't really run" end + def test_client_shutdown_writes + sock = nil + buf = nil + bs = 15609315 * rand + assert_nothing_raised do + sock = TCPSocket.new('127.0.0.1', @port) + sock.syswrite("PUT /hello HTTP/1.1\r\n") + sock.syswrite("Host: example.com\r\n") + sock.syswrite("Transfer-Encoding: chunked\r\n") + sock.syswrite("Trailer: X-Foo\r\n") + sock.syswrite("\r\n") + sock.syswrite("%x\r\n" % [ bs ]) + sock.syswrite("F" * bs) + sock.syswrite("\r\n0\r\nX-") + "Foo: bar\r\n\r\n".each_byte do |x| + sock.syswrite x.chr + sleep 0.05 + end + # we wrote the entire request before shutting down, server should + # continue to process our request and never hit EOFError on our sock + sock.shutdown(Socket::SHUT_WR) + buf = sock.read + end + assert_equal 'hello!\n', buf.split(/\r\n\r\n/).last + next_client = Net::HTTP.get(URI.parse("http://127.0.0.1:#@port/")) + assert_equal 'hello!\n', next_client + lines = File.readlines("test_stderr.#$$.log") + assert lines.grep(/^Unicorn::ClientShutdown: /).empty? + assert_nothing_raised { sock.close } + end + + def test_client_shutdown_write_truncates + sock = nil + buf = nil + bs = 15609315 * rand + assert_nothing_raised do + sock = TCPSocket.new('127.0.0.1', @port) + sock.syswrite("PUT /hello HTTP/1.1\r\n") + sock.syswrite("Host: example.com\r\n") + sock.syswrite("Transfer-Encoding: chunked\r\n") + sock.syswrite("Trailer: X-Foo\r\n") + sock.syswrite("\r\n") + sock.syswrite("%x\r\n" % [ bs ]) + sock.syswrite("F" * (bs / 2.0)) + + # shutdown prematurely, this will force the server to abort + # processing on us even during app dispatch + sock.shutdown(Socket::SHUT_WR) + IO.select([sock], nil, nil, 60) or raise "Timed out" + buf = sock.read + end + assert_equal "", buf + next_client = Net::HTTP.get(URI.parse("http://127.0.0.1:#@port/")) + assert_equal 'hello!\n', next_client + lines = File.readlines("test_stderr.#$$.log") + lines = lines.grep(/^Unicorn::ClientShutdown: bytes_read=\d+/) + assert_equal 1, lines.size + assert_match %r{\AUnicorn::ClientShutdown: bytes_read=\d+ true$}, lines[0] + assert_nothing_raised { sock.close } + end + + def test_client_malformed_body + sock = nil + buf = nil + bs = 15653984 + assert_nothing_raised do + sock = TCPSocket.new('127.0.0.1', @port) + sock.syswrite("PUT /hello HTTP/1.1\r\n") + sock.syswrite("Host: example.com\r\n") + sock.syswrite("Transfer-Encoding: chunked\r\n") + sock.syswrite("Trailer: X-Foo\r\n") + sock.syswrite("\r\n") + sock.syswrite("%x\r\n" % [ bs ]) + sock.syswrite("F" * bs) + end + begin + File.open("/dev/urandom", "rb") { |fp| sock.syswrite(fp.sysread(16384)) } + rescue + end + assert_nothing_raised { sock.close } + next_client = Net::HTTP.get(URI.parse("http://127.0.0.1:#@port/")) + assert_equal 'hello!\n', next_client + lines = File.readlines("test_stderr.#$$.log") + lines = lines.grep(/^Unicorn::HttpParserError: .* true$/) + assert_equal 1, lines.size + end def do_test(string, chunk, close_after=nil, shutdown_delay=0) # Do not use instance variables here, because it needs to be thread safe @@ -131,6 +228,16 @@ class WebServerTest < Test::Unit::TestCase end end + def test_logger_set + assert_equal @server.logger, Unicorn::HttpRequest::DEFAULTS["rack.logger"] + end + + def test_logger_changed + tmp = Logger.new($stdout) + @server.logger = tmp + assert_equal tmp, Unicorn::HttpRequest::DEFAULTS["rack.logger"] + end + def test_bad_client_400 sock = nil assert_nothing_raised do @@ -141,6 +248,16 @@ class WebServerTest < Test::Unit::TestCase assert_nothing_raised { sock.close } end + def test_http_0_9 + sock = nil + assert_nothing_raised do + sock = TCPSocket.new('127.0.0.1', @port) + sock.syswrite("GET /hello\r\n") + end + assert_match 'hello!\n', sock.sysread(4096) + assert_nothing_raised { sock.close } + end + def test_header_is_too_long redirect_test_io do long = "GET /test HTTP/1.1\r\n" + ("X-Big: stuff\r\n" * 15000) + "\r\n" @@ -152,9 +269,18 @@ class WebServerTest < Test::Unit::TestCase def test_file_streamed_request body = "a" * (Unicorn::Const::MAX_BODY * 2) - long = "GET /test HTTP/1.1\r\nContent-length: #{body.length}\r\n\r\n" + body + long = "PUT /test HTTP/1.1\r\nContent-length: #{body.length}\r\n\r\n" + body do_test(long, Unicorn::Const::CHUNK_SIZE * 2 -400) end + def test_file_streamed_request_bad_body + body = "a" * (Unicorn::Const::MAX_BODY * 2) + long = "GET /test HTTP/1.1\r\nContent-ength: #{body.length}\r\n\r\n" + body + assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL, + Errno::EBADF) { + do_test(long, Unicorn::Const::CHUNK_SIZE * 2 -400) + } + end + end |