1 # -*- encoding: binary -*-
7 class TeeInput < Unicorn::TeeInput
8 attr_accessor :tmp, :len
11 class TestTeeInput < Test::Unit::TestCase
16 @rd, @wr = Kgio::UNIXSocket.pair
17 @rd.sync = @wr.sync = true
22 return if $$ != @start_pid
34 r = init_request("hello", 5 + (4096 * 4 * 3) + "#$/foo#$/".size)
35 ti = TeeInput.new(@rd, r)
39 3.times { @wr.write("ffff" * 4096) }
44 assert_nothing_raised { line = ti.gets }
45 assert_equal(4096 * 4 * 3 + 5 + $/.size, line.size)
46 assert_equal("hello" << ("ffff" * 4096 * 3) << "#$/", line)
47 assert_nothing_raised { line = ti.gets }
48 assert_equal "foo#$/", line
50 assert_nothing_raised { pid, status = Process.waitpid2(pid) }
51 assert status.success?
55 r = init_request("hello", 5 + "#$/foo".size)
56 ti = TeeInput.new(@rd, r)
64 assert_nothing_raised { line = ti.gets }
65 assert_equal("hello#$/", line)
66 assert_nothing_raised { line = ti.gets }
67 assert_equal "foo", line
69 assert_nothing_raised { pid, status = Process.waitpid2(pid) }
70 assert status.success?
74 r = init_request('hello')
75 ti = TeeInput.new(@rd, r)
76 assert_equal 0, @parser.content_length
77 assert @parser.body_eof?
78 assert_equal StringIO, ti.tmp.class
79 assert_equal 0, ti.tmp.pos
80 assert_equal 5, ti.size
81 assert_equal 'hello', ti.read
82 assert_equal '', ti.read
83 assert_nil ti.read(4096)
84 assert_equal 5, ti.size
87 def test_read_with_buffer
88 r = init_request('hello')
89 ti = TeeInput.new(@rd, r)
92 assert_equal 'hell', rv
93 assert_equal 'hell', buf
94 assert_equal rv.object_id, buf.object_id
95 assert_equal 'o', ti.read
96 assert_equal nil, ti.read(5, buf)
97 assert_equal 0, ti.rewind
98 assert_equal 'hello', ti.read(5, buf)
99 assert_equal 'hello', buf
103 r = init_request('.' * Unicorn::Const::MAX_BODY << 'a')
104 ti = TeeInput.new(@rd, r)
105 assert_equal 0, @parser.content_length
106 assert @parser.body_eof?
107 assert_kind_of File, ti.tmp
108 assert_equal 0, ti.tmp.pos
109 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
112 def test_read_in_full_if_content_length
114 r = init_request('.' * b, 300)
115 assert_equal 300, @parser.content_length
116 ti = TeeInput.new(@rd, r)
119 sleep 1 # still a *potential* race here that would make the test moot...
122 assert_equal a, ti.read(a).size
123 _, status = Process.waitpid2(pid)
124 assert status.success?
128 def test_big_body_multi
129 r = init_request('.', Unicorn::Const::MAX_BODY + 1)
130 ti = TeeInput.new(@rd, r)
131 assert_equal Unicorn::Const::MAX_BODY, @parser.content_length
132 assert ! @parser.body_eof?
133 assert_kind_of File, ti.tmp
134 assert_equal 0, ti.tmp.pos
135 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
136 nr = Unicorn::Const::MAX_BODY / 4
139 nr.times { @wr.write('....') }
143 assert_equal '.', ti.read(1)
144 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
146 assert_equal '....', ti.read(4), "nr=#{x}"
147 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
149 assert_nil ti.read(1)
151 assert_nothing_raised { pid, status = Process.waitpid2(pid) }
152 assert status.success?
156 @parser = Unicorn::HttpParser.new
157 @buf = "POST / HTTP/1.1\r\n" \
158 "Host: localhost\r\n" \
159 "Transfer-Encoding: chunked\r\n" \
161 assert_equal @env, @parser.headers(@env, @buf)
162 assert_equal "", @buf
166 5.times { @wr.write("5\r\nabcde\r\n") }
167 @wr.write("0\r\n\r\n")
170 ti = TeeInput.new(@rd, @parser)
171 assert_nil @parser.content_length
173 assert ! @parser.body_eof?
174 assert_equal 25, ti.size
175 assert @parser.body_eof?
176 assert_equal 25, ti.len
177 assert_equal 0, ti.tmp.pos
178 assert_nothing_raised { ti.rewind }
179 assert_equal 0, ti.tmp.pos
180 assert_equal 'abcdeabcdeabcdeabcde', ti.read(20)
181 assert_equal 20, ti.tmp.pos
182 assert_nothing_raised { ti.rewind }
183 assert_equal 0, ti.tmp.pos
184 assert_kind_of File, ti.tmp
186 assert_nothing_raised { pid, status = Process.waitpid2(pid) }
187 assert status.success?
190 def test_chunked_ping_pong
191 @parser = Unicorn::HttpParser.new
192 @buf = "POST / HTTP/1.1\r\n" \
193 "Host: localhost\r\n" \
194 "Transfer-Encoding: chunked\r\n" \
196 assert_equal @env, @parser.headers(@env, @buf)
197 assert_equal "", @buf
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
222 @buf = "POST / HTTP/1.1\r\n" \
223 "Host: localhost\r\n" \
224 "Trailer: Hello\r\n" \
225 "Transfer-Encoding: chunked\r\n" \
227 assert_equal @env, @parser.headers(@env, @buf)
228 assert_equal "", @buf
232 5.times { @wr.write("5\r\nabcde\r\n") }
234 @wr.write("Hello: World\r\n\r\n")
237 ti = TeeInput.new(@rd, @parser)
238 assert_nil @parser.content_length
240 assert ! @parser.body_eof?
241 assert_equal 25, ti.size
242 assert_equal "World", @env['HTTP_HELLO']
244 assert_nothing_raised { pid, status = Process.waitpid2(pid) }
245 assert status.success?
248 def test_chunked_and_size_slow
249 @parser = Unicorn::HttpParser.new
250 @buf = "POST / HTTP/1.1\r\n" \
251 "Host: localhost\r\n" \
252 "Trailer: Hello\r\n" \
253 "Transfer-Encoding: chunked\r\n" \
255 assert_equal @env, @parser.headers(@env, @buf)
256 assert_equal "", @buf
258 @wr.write("9\r\nabcde")
259 ti = TeeInput.new(@rd, @parser)
260 assert_nil @parser.content_length
261 assert_equal "abcde", ti.read(9)
262 assert ! @parser.body_eof?
263 @wr.write("fghi\r\n0\r\nHello: World\r\n\r\n")
264 assert_equal 9, ti.size
265 assert_equal "fghi", ti.read(9)
266 assert_equal nil, ti.read(9)
267 assert_equal "World", @env['HTTP_HELLO']
272 def init_request(body, size = nil)
273 @parser = Unicorn::HttpParser.new
274 body = body.to_s.freeze
275 @buf = "POST / HTTP/1.1\r\n" \
276 "Host: localhost\r\n" \
277 "Content-Length: #{size || body.size}\r\n" \
279 assert_equal @env, @parser.headers(@env, @buf)
280 assert_equal body, @buf