about summary refs log tree commit homepage
path: root/test/unit/test_chunked_reader.rb
diff options
context:
space:
mode:
Diffstat (limited to 'test/unit/test_chunked_reader.rb')
-rw-r--r--test/unit/test_chunked_reader.rb180
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