diff options
Diffstat (limited to 'test/unit/test_chunked_reader.rb')
-rw-r--r-- | test/unit/test_chunked_reader.rb | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/test/unit/test_chunked_reader.rb b/test/unit/test_chunked_reader.rb new file mode 100644 index 0000000..67fe43b --- /dev/null +++ b/test/unit/test_chunked_reader.rb @@ -0,0 +1,180 @@ +require 'test/unit' +require 'unicorn' +require 'unicorn/http11' +require 'tempfile' +require 'io/nonblock' +require 'digest/sha1' + +class TestChunkedReader < Test::Unit::TestCase + + def setup + @env = {} + @rd, @wr = IO.pipe + @rd.binmode + @wr.binmode + @rd.sync = @wr.sync = true + @start_pid = $$ + end + + def teardown + return if $$ != @start_pid + @rd.close rescue nil + @wr.close rescue nil + begin + Process.wait + rescue Errno::ECHILD + break + end while true + end + + def test_error + cr = bin_reader("8\r\nasdfasdf\r\n8\r\nasdfasdfa#{'a' * 1024}") + a = nil + assert_nothing_raised { a = cr.readpartial(8192) } + assert_equal 'asdfasdf', a + assert_nothing_raised { a = cr.readpartial(8192) } + assert_equal 'asdfasdf', a + assert_raises(Unicorn::HttpParserError) { cr.readpartial(8192) } + end + + def test_eof1 + cr = bin_reader("0\r\n") + assert_raises(EOFError) { cr.readpartial(8192) } + end + + def test_eof2 + cr = bin_reader("0\r\n\r\n") + assert_raises(EOFError) { cr.readpartial(8192) } + end + + def test_readpartial1 + cr = bin_reader("4\r\nasdf\r\n0\r\n") + assert_equal 'asdf', cr.readpartial(8192) + assert_raises(EOFError) { cr.readpartial(8192) } + end + + def test_gets1 + cr = bin_reader("4\r\nasdf\r\n0\r\n") + STDOUT.sync = true + assert_equal 'asdf', cr.gets + assert_raises(EOFError) { cr.readpartial(8192) } + end + + def test_gets2 + cr = bin_reader("4\r\nasd\n\r\n0\r\n\r\n") + assert_equal "asd\n", cr.gets + assert_nil cr.gets + end + + def test_gets3 + max = Unicorn::Const::CHUNK_SIZE * 2 + str = ('a' * max).freeze + first = 5 + last = str.size - first + cr = bin_reader( + "#{'%x' % first}\r\n#{str[0, first]}\r\n" \ + "#{'%x' % last}\r\n#{str[-last, last]}\r\n" \ + "0\r\n") + assert_equal str, cr.gets + assert_nil cr.gets + end + + def test_readpartial_gets_mixed1 + max = Unicorn::Const::CHUNK_SIZE * 2 + str = ('a' * max).freeze + first = 5 + last = str.size - first + cr = bin_reader( + "#{'%x' % first}\r\n#{str[0, first]}\r\n" \ + "#{'%x' % last}\r\n#{str[-last, last]}\r\n" \ + "0\r\n") + partial = cr.readpartial(16384) + assert String === partial + + len = max - partial.size + assert_equal(str[-len, len], cr.gets) + assert_raises(EOFError) { cr.readpartial(1) } + assert_nil cr.gets + end + + def test_gets_mixed_readpartial + max = 10 + str = ("z\n" * max).freeze + first = 5 + last = str.size - first + cr = bin_reader( + "#{'%x' % first}\r\n#{str[0, first]}\r\n" \ + "#{'%x' % last}\r\n#{str[-last, last]}\r\n" \ + "0\r\n") + assert_equal("z\n", cr.gets) + assert_equal("z\n", cr.gets) + end + + def test_dd + cr = bin_reader("6\r\nhello\n\r\n") + tmp = Tempfile.new('test_dd') + tmp.sync = true + + pid = fork { + crd, cwr = IO.pipe + crd.binmode + cwr.binmode + crd.sync = cwr.sync = true + + pid = fork { + STDOUT.reopen(cwr) + crd.close + cwr.close + exec('dd', 'if=/dev/urandom', 'bs=93390', 'count=16') + } + cwr.close + begin + buf = crd.readpartial(16384) + tmp.write(buf) + @wr.write("#{'%x' % buf.size}\r\n#{buf}\r\n") + rescue EOFError + @wr.write("0\r\n\r\n") + Process.waitpid(pid) + exit 0 + end while true + } + assert_equal "hello\n", cr.gets + sha1 = Digest::SHA1.new + buf = Unicorn::Z.dup + begin + cr.readpartial(16384, buf) + sha1.update(buf) + rescue EOFError + break + end while true + + assert_nothing_raised { Process.waitpid(pid) } + sha1_file = Digest::SHA1.new + File.open(tmp.path, 'rb') { |fp| + while fp.read(16384, buf) + sha1_file.update(buf) + end + } + assert_equal sha1_file.hexdigest, sha1.hexdigest + end + + def test_trailer + @env['HTTP_TRAILER'] = 'Content-MD5' + pid = fork { @wr.syswrite("Content-MD5: asdf\r\n") } + cr = bin_reader("8\r\nasdfasdf\r\n8\r\nasdfasdf\r\n0\r\n") + assert_equal 'asdfasdf', cr.readpartial(4096) + assert_equal 'asdfasdf', cr.readpartial(4096) + assert_raises(EOFError) { cr.readpartial(4096) } + pid, status = Process.waitpid2(pid) + assert status.success? + assert_equal 'asdf', @env['HTTP_CONTENT_MD5'] + end + +private + + def bin_reader(buf) + buf.force_encoding(Encoding::BINARY) if buf.respond_to?(:force_encoding) + Unicorn::ChunkedReader.new(@env, @rd, buf) + end + +end |