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) }
44 assert_equal(4096 * 4 * 3 + 5 + $/.size, line.size)
45 assert_equal("hello" << ("ffff" * 4096 * 3) << "#$/", line)
47 assert_equal "foo#$/", line
49 pid, status = Process.waitpid2(pid)
50 assert status.success?
54 r = init_request("hello", 5 + "#$/foo".size)
55 ti = TeeInput.new(@rd, r)
64 assert_equal("hello#$/", line)
66 assert_equal "foo", line
68 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)
149 pid, status = Process.waitpid2(pid)
150 assert status.success?
154 @parser = Unicorn::HttpParser.new
155 @parser.buf << "POST / HTTP/1.1\r\n" \
156 "Host: localhost\r\n" \
157 "Transfer-Encoding: chunked\r\n" \
160 assert_equal "", @parser.buf
164 5.times { @wr.write("5\r\nabcde\r\n") }
165 @wr.write("0\r\n\r\n")
168 ti = TeeInput.new(@rd, @parser)
169 assert_nil @parser.content_length
171 assert ! @parser.body_eof?
172 assert_equal 25, ti.size
173 assert @parser.body_eof?
174 assert_equal 25, ti.len
175 assert_equal 0, ti.tmp.pos
177 assert_equal 0, ti.tmp.pos
178 assert_equal 'abcdeabcdeabcdeabcde', ti.read(20)
179 assert_equal 20, ti.tmp.pos
181 assert_equal 0, ti.tmp.pos
182 assert_kind_of File, ti.tmp
184 pid, status = Process.waitpid2(pid)
185 assert status.success?
188 def test_chunked_ping_pong
189 @parser = Unicorn::HttpParser.new
191 buf << "POST / HTTP/1.1\r\n" \
192 "Host: localhost\r\n" \
193 "Transfer-Encoding: chunked\r\n" \
197 chunks = %w(aa bbb cccc dddd eeee)
201 chunks.each do |chunk|
202 rd.read(1) == "." and
203 @wr.write("#{'%x' % [ chunk.size]}\r\n#{chunk}\r\n")
205 @wr.write("0\r\n\r\n")
207 ti = TeeInput.new(@rd, @parser)
208 assert_nil @parser.content_length
210 assert ! @parser.body_eof?
211 chunks.each do |chunk|
213 assert_equal chunk, ti.read(16384)
215 _, status = Process.waitpid2(pid)
216 assert status.success?
219 def test_chunked_with_trailer
220 @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" \
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", @parser.env['HTTP_HELLO']
243 pid, status = Process.waitpid2(pid)
244 assert status.success?
247 def test_chunked_and_size_slow
248 @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" \
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", @parser.env['HTTP_HELLO']
270 def test_gets_read_mix
271 r = init_request("hello\nasdfasdf")
272 ti = Unicorn::TeeInput.new(@rd, r)
273 assert_equal "hello\n", ti.gets
274 assert_equal "asdfasdf", ti.read(9)
275 assert_nil ti.read(9)
280 def init_request(body, size = nil)
281 @parser = Unicorn::HttpParser.new
282 body = body.to_s.freeze
284 buf << "POST / HTTP/1.1\r\n" \
285 "Host: localhost\r\n" \
286 "Content-Length: #{size || body.size}\r\n" \
289 assert_equal body, buf