Transfer-Encoding: chunked streaming input support
[unicorn.git] / test / unit / test_chunked_reader.rb
blobd9fc56fa45b1b2d276f26cbf4cebb16a02ff384e
1 require 'test/unit'
2 require 'unicorn'
3 require 'tempfile'
4 require 'io/nonblock'
5 require 'digest/sha1'
7 class TestChunkedReader < Test::Unit::TestCase
9   def setup
10     @cr = Unicorn::ChunkedReader.new
11     @rd, @wr = IO.pipe
12     @rd.binmode
13     @wr.binmode
14     @rd.sync = @wr.sync = true
15     @start_pid = $$
16   end
18   def teardown
19     return if $$ != @start_pid
20     @rd.close rescue nil
21     @wr.close rescue nil
22   end
24   def test_eof1
25     @cr.reopen(@rd, "0\r\n")
26     assert_raises(EOFError) { @cr.readpartial(8192) }
27   end
29   def test_eof2
30     @cr.reopen(@rd, "0\r\n\r\n")
31     assert_raises(EOFError) { @cr.readpartial(8192) }
32   end
34   def test_readpartial1
35     @cr.reopen(@rd, "4\r\nasdf\r\n0\r\n")
36     assert_equal 'asdf', @cr.readpartial(8192)
37     assert_raises(EOFError) { @cr.readpartial(8192) }
38   end
40   def test_gets1
41     @cr.reopen(@rd, "4\r\nasdf\r\n0\r\n")
42     STDOUT.sync = true
43     assert_equal 'asdf', @cr.gets
44     assert_raises(EOFError) { @cr.readpartial(8192) }
45   end
47   def test_gets2
48     @cr.reopen(@rd, "4\r\nasd\n\r\n0\r\n\r\n")
49     assert_equal "asd\n", @cr.gets
50     assert_nil @cr.gets
51   end
53   def test_gets3
54     max = Unicorn::Const::CHUNK_SIZE * 2
55     str = ('a' * max).freeze
56     first = 5
57     last = str.size - first
58     @cr.reopen(@rd,
59       "#{'%x' % first}\r\n#{str[0, first]}\r\n" \
60       "#{'%x' % last}\r\n#{str[-last, last]}\r\n" \
61       "0\r\n")
62     assert_equal str, @cr.gets
63     assert_nil @cr.gets
64   end
66   def test_readpartial_gets_mixed1
67     max = Unicorn::Const::CHUNK_SIZE * 2
68     str = ('a' * max).freeze
69     first = 5
70     last = str.size - first
71     @cr.reopen(@rd,
72       "#{'%x' % first}\r\n#{str[0, first]}\r\n" \
73       "#{'%x' % last}\r\n#{str[-last, last]}\r\n" \
74       "0\r\n")
75     partial = @cr.readpartial(16384)
76     assert String === partial
78     len = max - partial.size
79     assert_equal(str[-len, len], @cr.gets)
80     assert_raises(EOFError) { @cr.readpartial(1) }
81     assert_nil @cr.gets
82   end
84   def test_gets_mixed_readpartial
85     max = 10
86     str = ("z\n" * max).freeze
87     first = 5
88     last = str.size - first
89     @cr.reopen(@rd,
90       "#{'%x' % first}\r\n#{str[0, first]}\r\n" \
91       "#{'%x' % last}\r\n#{str[-last, last]}\r\n" \
92       "0\r\n")
93     assert_equal("z\n", @cr.gets)
94     assert_equal("z\n", @cr.gets)
95   end
97   def test_dd
98     @cr.reopen(@rd, "6\r\nhello\n\r\n")
99     tmp = Tempfile.new('test_dd')
100     tmp.sync = true
102     pid = fork {
103       crd, cwr = IO.pipe
104       crd.binmode
105       cwr.binmode
106       crd.sync = cwr.sync = true
108       pid = fork {
109         STDOUT.reopen(cwr)
110         crd.close
111         cwr.close
112         exec('dd', 'if=/dev/urandom', 'bs=93390', 'count=16')
113       }
114       cwr.close
115       begin
116         buf = crd.readpartial(16384)
117         tmp.write(buf)
118         @wr.write("#{'%x' % buf.size}\r\n#{buf}\r\n")
119       rescue EOFError
120         @wr.write("0\r\n\r\n")
121         Process.waitpid(pid)
122         exit 0
123       end while true
124     }
125     assert_equal "hello\n", @cr.gets
126     sha1 = Digest::SHA1.new
127     buf = ''
128     begin
129       @cr.readpartial(16384, buf)
130       sha1.update(buf)
131     rescue EOFError
132       break
133     end while true
135     assert_nothing_raised { Process.waitpid(pid) }
136     sha1_file = Digest::SHA1.new
137     File.open(tmp.path, 'rb') { |fp|
138       while fp.read(16384, buf)
139         sha1_file.update(buf)
140       end
141     }
142     assert_equal sha1_file.hexdigest, sha1.hexdigest
143   end