1 # -*- encoding: binary -*-
7 class TeeInput < Unicorn::TeeInput
8 attr_accessor :tmp, :len
11 class TestTeeInput < Test::Unit::TestCase
15 @rd, @wr = Kgio::UNIXSocket.pair
16 @rd.sync = @wr.sync = true
21 return if $$ != @start_pid
33 r = init_request("hello", 5 + (4096 * 4 * 3) + "#$/foo#$/".size)
34 ti = TeeInput.new(@rd, r)
38 3.times { @wr.write("ffff" * 4096) }
43 assert_nothing_raised { line = ti.gets }
44 assert_equal(4096 * 4 * 3 + 5 + $/.size, line.size)
45 assert_equal("hello" << ("ffff" * 4096 * 3) << "#$/", line)
46 assert_nothing_raised { line = ti.gets }
47 assert_equal "foo#$/", line
49 assert_nothing_raised { pid, status = Process.waitpid2(pid) }
50 assert status.success?
54 r = init_request("hello", 5 + "#$/foo".size)
55 ti = TeeInput.new(@rd, r)
63 assert_nothing_raised { line = ti.gets }
64 assert_equal("hello#$/", line)
65 assert_nothing_raised { line = ti.gets }
66 assert_equal "foo", line
68 assert_nothing_raised { pid, status = Process.waitpid2(pid) }
69 assert status.success?
73 r = init_request('hello')
74 ti = TeeInput.new(@rd, r)
75 assert_equal 0, @parser.content_length
76 assert @parser.body_eof?
77 assert_equal StringIO, ti.tmp.class
78 assert_equal 0, ti.tmp.pos
79 assert_equal 5, ti.size
80 assert_equal 'hello', ti.read
81 assert_equal '', ti.read
82 assert_nil ti.read(4096)
83 assert_equal 5, ti.size
86 def test_read_with_buffer
87 r = init_request('hello')
88 ti = TeeInput.new(@rd, r)
91 assert_equal 'hell', rv
92 assert_equal 'hell', buf
93 assert_equal rv.object_id, buf.object_id
94 assert_equal 'o', ti.read
95 assert_equal nil, ti.read(5, buf)
96 assert_equal 0, ti.rewind
97 assert_equal 'hello', ti.read(5, buf)
98 assert_equal 'hello', buf
102 r = init_request('.' * Unicorn::Const::MAX_BODY << 'a')
103 ti = TeeInput.new(@rd, r)
104 assert_equal 0, @parser.content_length
105 assert @parser.body_eof?
106 assert_kind_of File, ti.tmp
107 assert_equal 0, ti.tmp.pos
108 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
111 def test_read_in_full_if_content_length
113 r = init_request('.' * b, 300)
114 assert_equal 300, @parser.content_length
115 ti = TeeInput.new(@rd, r)
118 sleep 1 # still a *potential* race here that would make the test moot...
121 assert_equal a, ti.read(a).size
122 _, status = Process.waitpid2(pid)
123 assert status.success?
127 def test_big_body_multi
128 r = init_request('.', Unicorn::Const::MAX_BODY + 1)
129 ti = TeeInput.new(@rd, r)
130 assert_equal Unicorn::Const::MAX_BODY, @parser.content_length
131 assert ! @parser.body_eof?
132 assert_kind_of File, ti.tmp
133 assert_equal 0, ti.tmp.pos
134 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
135 nr = Unicorn::Const::MAX_BODY / 4
138 nr.times { @wr.write('....') }
142 assert_equal '.', ti.read(1)
143 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
145 assert_equal '....', ti.read(4), "nr=#{x}"
146 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
148 assert_nil ti.read(1)
150 assert_nothing_raised { pid, status = Process.waitpid2(pid) }
151 assert status.success?
155 @parser = Unicorn::HttpParser.new
156 @parser.buf << "POST / HTTP/1.1\r\n" \
157 "Host: localhost\r\n" \
158 "Transfer-Encoding: chunked\r\n" \
161 assert_equal "", @parser.buf
165 5.times { @wr.write("5\r\nabcde\r\n") }
166 @wr.write("0\r\n\r\n")
169 ti = TeeInput.new(@rd, @parser)
170 assert_nil @parser.content_length
172 assert ! @parser.body_eof?
173 assert_equal 25, ti.size
174 assert @parser.body_eof?
175 assert_equal 25, ti.len
176 assert_equal 0, ti.tmp.pos
177 assert_nothing_raised { ti.rewind }
178 assert_equal 0, ti.tmp.pos
179 assert_equal 'abcdeabcdeabcdeabcde', ti.read(20)
180 assert_equal 20, ti.tmp.pos
181 assert_nothing_raised { ti.rewind }
182 assert_equal 0, ti.tmp.pos
183 assert_kind_of File, ti.tmp
185 assert_nothing_raised { pid, status = Process.waitpid2(pid) }
186 assert status.success?
189 def test_chunked_ping_pong
190 @parser = Unicorn::HttpParser.new
192 buf << "POST / HTTP/1.1\r\n" \
193 "Host: localhost\r\n" \
194 "Transfer-Encoding: chunked\r\n" \
198 chunks = %w(aa bbb cccc dddd eeee)
202 chunks.each do |chunk|
203 rd.read(1) == "." and
204 @wr.write("#{'%x' % [ chunk.size]}\r\n#{chunk}\r\n")
206 @wr.write("0\r\n\r\n")
208 ti = TeeInput.new(@rd, @parser)
209 assert_nil @parser.content_length
211 assert ! @parser.body_eof?
212 chunks.each do |chunk|
214 assert_equal chunk, ti.read(16384)
216 _, status = Process.waitpid2(pid)
217 assert status.success?
220 def test_chunked_with_trailer
221 @parser = Unicorn::HttpParser.new
223 buf << "POST / HTTP/1.1\r\n" \
224 "Host: localhost\r\n" \
225 "Trailer: Hello\r\n" \
226 "Transfer-Encoding: chunked\r\n" \
233 5.times { @wr.write("5\r\nabcde\r\n") }
235 @wr.write("Hello: World\r\n\r\n")
238 ti = TeeInput.new(@rd, @parser)
239 assert_nil @parser.content_length
241 assert ! @parser.body_eof?
242 assert_equal 25, ti.size
243 assert_equal "World", @parser.env['HTTP_HELLO']
245 assert_nothing_raised { pid, status = Process.waitpid2(pid) }
246 assert status.success?
249 def test_chunked_and_size_slow
250 @parser = Unicorn::HttpParser.new
252 buf << "POST / HTTP/1.1\r\n" \
253 "Host: localhost\r\n" \
254 "Trailer: Hello\r\n" \
255 "Transfer-Encoding: chunked\r\n" \
260 @wr.write("9\r\nabcde")
261 ti = TeeInput.new(@rd, @parser)
262 assert_nil @parser.content_length
263 assert_equal "abcde", ti.read(9)
264 assert ! @parser.body_eof?
265 @wr.write("fghi\r\n0\r\nHello: World\r\n\r\n")
266 assert_equal 9, ti.size
267 assert_equal "fghi", ti.read(9)
268 assert_equal nil, ti.read(9)
269 assert_equal "World", @parser.env['HTTP_HELLO']
272 def test_gets_read_mix
273 r = init_request("hello\nasdfasdf")
274 ti = Unicorn::TeeInput.new(@rd, r)
275 assert_equal "hello\n", ti.gets
276 assert_equal "asdfasdf", ti.read(9)
277 assert_nil ti.read(9)
282 def init_request(body, size = nil)
283 @parser = Unicorn::HttpParser.new
284 body = body.to_s.freeze
286 buf << "POST / HTTP/1.1\r\n" \
287 "Host: localhost\r\n" \
288 "Content-Length: #{size || body.size}\r\n" \
291 assert_equal body, buf