1 # -*- encoding: binary -*-
2 # frozen_string_literal: false
8 class TeeInput < Unicorn::TeeInput
9 attr_accessor :tmp, :len
12 class TestTeeInput < Test::Unit::TestCase
14 @rd, @wr = UNIXSocket.pair
15 @rd.sync = @wr.sync = true
18 $/ == "\n" or abort %q{test broken if \$/ != "\\n"}
22 return if $$ != @start_pid
33 tmp = @parser.env["rack.tempfiles"]
34 assert_instance_of Array, tmp
35 assert_operator tmp.size, :>=, 1
36 assert_instance_of Unicorn::TmpIO, tmp[0]
40 r = init_request("hello", 5 + (4096 * 4 * 3) + "#{@rs}foo#{@rs}".size)
41 ti = TeeInput.new(@rd, r)
45 3.times { @wr.write("ffff" * 4096) }
46 @wr.write "#{@rs}foo#{@rs}"
51 assert_equal(4096 * 4 * 3 + 5 + $/.size, line.size)
52 assert_equal("hello" << ("ffff" * 4096 * 3) << "#{@rs}", line)
54 assert_equal "foo#{@rs}", line
56 pid, status = Process.waitpid2(pid)
57 assert status.success?
61 r = init_request("hello", 5 + "#{@rs}foo".size)
62 ti = TeeInput.new(@rd, r)
71 assert_equal("hello#{@rs}", line)
73 assert_equal "foo", line
75 pid, status = Process.waitpid2(pid)
76 assert status.success?
80 r = init_request('hello')
81 ti = TeeInput.new(@rd, r)
82 assert_equal 0, @parser.content_length
83 assert @parser.body_eof?
84 assert_equal StringIO, ti.tmp.class
85 assert_equal 0, ti.tmp.pos
86 assert_equal 5, ti.size
87 assert_equal 'hello', ti.read
88 assert_equal '', ti.read
89 assert_nil ti.read(4096)
90 assert_equal 5, ti.size
93 def test_read_with_buffer
94 r = init_request('hello')
95 ti = TeeInput.new(@rd, r)
98 assert_equal 'hell', rv
99 assert_equal 'hell', buf
100 assert_equal rv.object_id, buf.object_id
101 assert_equal 'o', ti.read
102 assert_equal nil, ti.read(5, buf)
103 assert_equal 0, ti.rewind
104 assert_equal 'hello', ti.read(5, buf)
105 assert_equal 'hello', buf
109 r = init_request('.' * Unicorn::Const::MAX_BODY << 'a')
110 ti = TeeInput.new(@rd, r)
111 assert_equal 0, @parser.content_length
112 assert @parser.body_eof?
113 assert_kind_of File, ti.tmp
114 assert_equal 0, ti.tmp.pos
115 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
119 def test_read_in_full_if_content_length
121 r = init_request('.' * b, 300)
122 assert_equal 300, @parser.content_length
123 ti = TeeInput.new(@rd, r)
126 sleep 1 # still a *potential* race here that would make the test moot...
129 assert_equal a, ti.read(a).size
130 _, status = Process.waitpid2(pid)
131 assert status.success?
135 def test_big_body_multi
136 r = init_request('.', Unicorn::Const::MAX_BODY + 1)
137 ti = TeeInput.new(@rd, r)
138 assert_equal Unicorn::Const::MAX_BODY, @parser.content_length
139 assert ! @parser.body_eof?
140 assert_kind_of File, ti.tmp
141 assert_equal 0, ti.tmp.pos
142 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
143 nr = Unicorn::Const::MAX_BODY / 4
146 nr.times { @wr.write('....') }
150 assert_equal '.', ti.read(1)
151 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
153 assert_equal '....', ti.read(4), "nr=#{x}"
154 assert_equal Unicorn::Const::MAX_BODY + 1, ti.size
156 assert_nil ti.read(1)
157 pid, status = Process.waitpid2(pid)
158 assert status.success?
163 @parser = Unicorn::HttpParser.new
164 @parser.buf << "POST / HTTP/1.1\r\n" \
165 "Host: localhost\r\n" \
166 "Transfer-Encoding: chunked\r\n" \
169 assert_equal "", @parser.buf
173 5.times { @wr.write("5\r\nabcde\r\n") }
174 @wr.write("0\r\n\r\n")
177 ti = TeeInput.new(@rd, @parser)
178 assert_nil @parser.content_length
180 assert ! @parser.body_eof?
181 assert_equal 25, ti.size
182 assert @parser.body_eof?
183 assert_equal 25, ti.len
184 assert_equal 0, ti.tmp.pos
186 assert_equal 0, ti.tmp.pos
187 assert_equal 'abcdeabcdeabcdeabcde', ti.read(20)
188 assert_equal 20, ti.tmp.pos
190 assert_equal 0, ti.tmp.pos
191 assert_kind_of File, ti.tmp
193 pid, status = Process.waitpid2(pid)
194 assert status.success?
198 def test_chunked_ping_pong
199 @parser = Unicorn::HttpParser.new
201 buf << "POST / HTTP/1.1\r\n" \
202 "Host: localhost\r\n" \
203 "Transfer-Encoding: chunked\r\n" \
207 chunks = %w(aa bbb cccc dddd eeee)
211 chunks.each do |chunk|
212 rd.read(1) == "." and
213 @wr.write("#{'%x' % [ chunk.size]}\r\n#{chunk}\r\n")
215 @wr.write("0\r\n\r\n")
217 ti = TeeInput.new(@rd, @parser)
218 assert_nil @parser.content_length
220 assert ! @parser.body_eof?
221 chunks.each do |chunk|
223 assert_equal chunk, ti.read(16384)
225 _, status = Process.waitpid2(pid)
226 assert status.success?
229 def test_chunked_with_trailer
230 @parser = Unicorn::HttpParser.new
232 buf << "POST / HTTP/1.1\r\n" \
233 "Host: localhost\r\n" \
234 "Trailer: Hello\r\n" \
235 "Transfer-Encoding: chunked\r\n" \
242 5.times { @wr.write("5\r\nabcde\r\n") }
244 @wr.write("Hello: World\r\n\r\n")
247 ti = TeeInput.new(@rd, @parser)
248 assert_nil @parser.content_length
250 assert ! @parser.body_eof?
251 assert_equal 25, ti.size
252 assert_equal "World", @parser.env['HTTP_HELLO']
253 pid, status = Process.waitpid2(pid)
254 assert status.success?
257 def test_chunked_and_size_slow
258 @parser = Unicorn::HttpParser.new
260 buf << "POST / HTTP/1.1\r\n" \
261 "Host: localhost\r\n" \
262 "Trailer: Hello\r\n" \
263 "Transfer-Encoding: chunked\r\n" \
268 @wr.write("9\r\nabcde")
269 ti = TeeInput.new(@rd, @parser)
270 assert_nil @parser.content_length
271 assert_equal "abcde", ti.read(9)
272 assert ! @parser.body_eof?
273 @wr.write("fghi\r\n0\r\nHello: World\r\n\r\n")
274 assert_equal 9, ti.size
275 assert_equal "fghi", ti.read(9)
276 assert_equal nil, ti.read(9)
277 assert_equal "World", @parser.env['HTTP_HELLO']
280 def test_gets_read_mix
281 r = init_request("hello\nasdfasdf")
282 ti = Unicorn::TeeInput.new(@rd, r)
283 assert_equal "hello\n", ti.gets
284 assert_equal "asdfasdf", ti.read(9)
285 assert_nil ti.read(9)
290 def init_request(body, size = nil)
291 @parser = Unicorn::HttpParser.new
292 body = body.to_s.freeze
294 buf << "POST / HTTP/1.1\r\n" \
295 "Host: localhost\r\n" \
296 "Content-Length: #{size || body.size}\r\n" \
299 assert_equal body, buf