test_response: fix redefined method
[kcar.git] / test / test_response.rb
blobc5739c419afc5ab3e1b6d13924928b1ddf132454
1 # -*- encoding: binary -*-
2 require 'test/unit'
3 require 'pp'
4 require 'socket'
5 require 'kcar'
6 require 'digest/sha1'
7 $stderr.sync = true
9 class TestSession < Test::Unit::TestCase
10   def setup
11     @s, @c = UNIXSocket.pair
12   end
14   def test_http_status_only_pipelined
15     resp = "HTTP/1.1 404 Not Found\r\n\r\n" \
16            "HTTP/1.1 404 Not Found\r\n\r\n"
17     pid = fork do
18       @s << resp
19       @s.close
20     end
22     @s.close
23     @response = Kcar::Response.new(@c)
24     status, headers, body = @response.rack
25     assert_equal status, "404 Not Found"
26     assert_equal({}, headers)
27     tmp = []
28     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
29     assert_equal [], tmp
30     assert @response.parser.keepalive?
31     assert @response.parser.body_eof?
32     body.close
34     status, headers, body = @response.rack
35     assert_equal status, "404 Not Found"
36     assert_equal({},headers)
37     tmp = []
38     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
39     assert_equal [], tmp
41     _, status = Process.waitpid2(pid)
42     assert status.success?
43     body.close
44   end
46   def test_http_small_pipelined_identity
47     resp = "HTTP/1.1 200 OK\r\nContent-Length: 12\r\n\r\nhello world\n" \
48            "HTTP/1.1 200 OK\r\nContent-Length: 14\r\n\r\ngoodbye world\n"
49     pid = fork do
50       @s << resp
51       @s.close
52     end
53     @s.close
54     @response = Kcar::Response.new(@c)
55     status, headers, body = @response.rack
56     assert_equal status, "200 OK"
57     assert_equal({'Content-Length'=>'12'},headers)
58     tmp = []
59     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
60     assert_equal [ "hello world\n" ], tmp
61     body.close
62     assert ! @c.closed?
64     status, headers, body = @response.rack
65     assert_equal status, "200 OK"
66     assert_equal({'Content-Length'=>'14'},headers)
67     tmp = []
68     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
69     assert_equal [ "goodbye world\n" ], tmp
71     _, status = Process.waitpid2(pid)
72     assert status.success?
73     body.close
74   end
76   def test_http_big_pipelined_identity
77     nr = 1024 * 512
78     width = 80
79     length = nr * width
80     template = "%0#{width - 1}x\n"
81     expect = Digest::SHA1.new
82     nr.times { |i| expect << sprintf(template, i) }
83     pid = fork do
84       @s.sync = false
85       @s << "HTTP/1.1 200 OK\r\nContent-Length: #{length}\r\n\r\n"
86       nr.times { |i| @s.printf(template, i) }
87       @s << "HTTP/1.1 200 OK\r\nContent-Length: #{length}\r\n\r\n"
88       nr.times { |i| @s.printf(template, i) }
89       @s.close
90     end
91     @s.close
92     @response = Kcar::Response.new(@c)
94     2.times do |i|
95       status, headers, body = @response.rack
96       assert_equal status, "200 OK"
97       assert_equal({'Content-Length'=>length.to_s}, headers)
98       sha1 = Digest::SHA1.new
99       assert_nothing_raised { body.each { |chunk| sha1 << chunk } }
100       assert_equal expect, sha1, "#{expect.hexdigest} != #{sha1.hexdigest}"
101       body.close
102       assert ! @c.closed?
103     end
104     _, status = Process.waitpid2(pid)
105     assert status.success?
106   end
108   def test_http_one_zero
109     pid = fork do
110       @s << "HTTP/1.0 200 OK\r\n\r\nHI"
111       @s.close
112     end
113     @s.close
114     @response = Kcar::Response.new(@c)
115     status, headers, body = @response.read
116     assert_equal status, "200 OK"
117     assert headers.empty?
118     tmp = []
119     assert ! body.parser.keepalive?
120     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
121     assert_equal [ "HI" ], tmp
122     _, status = Process.waitpid2(pid)
123     assert status.success?
124     body.close
125     assert @c.closed?
126   end
128   def test_http_keepalive
129     pid = fork do
130       @s << "HTTP/1.1 200 OK\r\nContent-Length: 2\r\n\r\nHI"
131     end
132     @response = Kcar::Response.new(@c)
133     status, headers, body = @response.read
134     assert_equal status, "200 OK"
135     assert_equal({"Content-Length" => "2" }, headers)
136     tmp = []
137     assert body.parser.keepalive?
138     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
139     assert body.parser.body_eof?
140     assert body.parser.keepalive?
141     assert_equal [ "HI" ], tmp
142     _, status = Process.waitpid2(pid)
143     assert status.success?
144     body.close
145     assert ! @c.closed?
147     pid = fork do
148       @s << "HTTP/1.1 200 OK\r\nContent-Length: 3\r\n"
149       @s << "Connection: close\r\n\r\nBYE"
150     end
151     status, headers, body = @response.read
152     assert_equal status, "200 OK"
153     assert_equal({ "Content-Length" => "3", "Connection" => "close" }, headers)
154     tmp = []
155     assert ! body.parser.keepalive?
156     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
157     assert_equal [ "BYE" ], tmp
158     _, status = Process.waitpid2(pid)
159     assert status.success?
160     body.close
161     assert @c.closed?
162   end
164   def test_http_keepalive_chunky
165     @response = Kcar::Response.new(@c)
166     pid = fork do
167       @s << "HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n"
168       @s << "5\r\nabcde\r\n"
169       @s << "0\r\n\r\nHTTP/1.1 " # partial response
170     end
171     status, headers, body = @response.read
172     assert_equal status, "200 OK"
173     assert_equal({"Transfer-Encoding" => "chunked" }, headers)
174     tmp = []
175     assert body.parser.keepalive?
176     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
177     assert body.parser.keepalive?
178     assert body.parser.body_eof?
179     assert_equal [ "abcde" ], tmp
180     _, status = Process.waitpid2(pid)
181     assert status.success?
182     body.close
183     assert ! @c.closed?
184     assert_equal "HTTP/1.1 ", @response.buf
186     pid = fork do
187       @s << "200 OK\r\nContent-Length: 3\r\n"
188       @s << "Connection: close\r\n\r\nBYE"
189     end
190     status, headers, body = @response.read
191     assert_equal status, "200 OK"
192     assert_equal({ "Content-Length" => "3", "Connection" => "close" }, headers)
193     tmp = []
194     assert ! body.parser.keepalive?
195     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
196     assert_equal [ "BYE" ], tmp
197     _, status = Process.waitpid2(pid)
198     assert status.success?
199     body.close
200     assert @c.closed?
201   end
203   def test_http_no_body_keepalive
204     pid = fork { @s << "HTTP/1.1 100 Continue\r\n\r\n" }
205     @response = Kcar::Response.new(@c)
206     status, headers, body = @response.read
207     assert_equal status, "100 Continue"
208     assert_equal({}, headers)
209     tmp = []
210     assert body.parser.keepalive?
211     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
212     assert tmp.empty?
213     _, status = Process.waitpid2(pid)
214     assert status.success?
215     body.close
216     assert ! @c.closed?
218     pid = fork { @s << "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nhello" }
219     @s.close
220     status, headers, body = @response.read
221     assert_equal status, "200 OK"
222     assert_equal({'Connection' => 'close'}, headers)
223     tmp = []
224     assert ! body.parser.keepalive?
225     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
226     assert_equal(%w(hello), tmp)
227     _, status = Process.waitpid2(pid)
228     assert status.success?
229     body.close
230     assert @c.closed?
231   end
233   def test_trailers
234     pid = fork do
235       @s << "HTTP/1.1 200 OK\r\nTrailer: Foo\r\n"
236       @s << "Transfer-Encoding: chunked\r\n\r\n"
237     end
238     @response = Kcar::Response.new(@c)
239     status, headers, body = @response.read
240     assert_equal status, "200 OK"
241     expect = {
242       "Trailer" => "Foo",
243       "Transfer-Encoding" => "chunked",
244     }
245     assert_equal(expect, headers)
246     assert body.parser.keepalive?
247     _, status = Process.waitpid2(pid)
248     assert status.success?
249     tmp = []
250     pid = fork { @s << "5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n" }
251     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
252     assert_equal %w(hello), tmp
253     expect['Foo'] = 'bar'
254     assert_equal(expect, headers)
255     _, status = Process.waitpid2(pid)
256     assert status.success?
257     body.close
258     assert ! @c.closed?
259   end
261   def test_trailers_pass_through
262     pid = fork do
263       @s << "HTTP/1.1 200 OK\r\nTrailer: Foo\r\n"
264       @s << "Transfer-Encoding: chunked\r\n\r\n"
265     end
266     @response = Kcar::Response.new(@c, {}, false)
267     status, headers, body = @response.read
268     assert_equal status, "200 OK"
269     expect = {
270       "Trailer" => "Foo",
271       "Transfer-Encoding" => "chunked",
272     }
273     assert_equal(expect, headers)
274     assert body.parser.keepalive?
275     _, status = Process.waitpid2(pid)
276     assert status.success?
277     tmp = []
278     pid = fork { @s << "5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n" }
279     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
280     assert_equal ["5\r\n", "hello\r\n", "0\r\n", "Foo: bar\r\n\r\n"], tmp
281     expect['Foo'] = 'bar'
282     assert_equal(expect, headers)
283     _, status = Process.waitpid2(pid)
284     assert status.success?
285     body.close
286     assert ! @c.closed?
287   end
289   def test_pass_through_one_oh
290     pid = fork do
291       @s << "HTTP/1.0 200 OK\r\n"
292       @s << "Content-Type: text/plain\r\n\r\n"
293     end
294     @response = Kcar::Response.new(@c, {}, false)
295     status, headers, body = @response.read
296     assert_equal status, "200 OK"
297     expect = { "Content-Type" => "text/plain", }
298     assert_equal(expect, headers)
299     assert ! body.parser.keepalive?
300     _, status = Process.waitpid2(pid)
301     assert status.success?
302     tmp = []
303     pid = fork { @s << "hello" }
304     @s.close
305     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
306     assert_equal %w(hello), tmp
307     assert_equal(expect, headers)
308     _, status = Process.waitpid2(pid)
309     assert status.success?
310     body.close
311     assert @c.closed?
312   end
314   def test_trailers_burpy
315     pid = fork do
316       @s << "HTTP/1.1 200 OK\r\nTrailer: Foo\r\n"
317       @s << "Transfer-Encoding: chunked\r\n\r\n"
318     end
319     @response = Kcar::Response.new(@c)
320     status, headers, body = @response.read
321     assert_equal status, "200 OK"
322     expect = {
323       "Trailer" => "Foo",
324       "Transfer-Encoding" => "chunked",
325     }
326     assert_equal(expect, headers)
327     assert body.parser.keepalive?
328     _, status = Process.waitpid2(pid)
329     assert status.success?
330     tmp = []
331     pid = fork { @s << "5\r\nhello\r\n0\r\nFoo: bar" }
332     rd, wr = IO.pipe
333     crlf_pid = fork do
334       wr.close
335       @s << rd.sysread(4)
336     end
337     rd.close
338     assert_nothing_raised do
339       first = true
340       body.each do |chunk|
341         tmp << chunk.dup
342         if first
343           first = false
344           wr.syswrite "\r\n\r\n"
345         end
346       end
347     end
348     assert_equal %w(hello), tmp
349     _, status = Process.waitpid2(pid)
350     assert status.success?
351     _, status = Process.waitpid2(crlf_pid)
352     assert status.success?
353     expect['Foo'] = 'bar'
354     assert_equal(expect, headers)
355     body.close
356     assert ! @c.closed?
357   end
359   def test_pass_through_trailers_burpy
360     pid = fork do
361       @s << "HTTP/1.1 200 OK\r\nTrailer: Foo\r\n"
362       @s << "Transfer-Encoding: chunked\r\n\r\n"
363     end
364     @response = Kcar::Response.new(@c, {}, false)
365     status, headers, body = @response.read
366     assert_equal status, "200 OK"
367     expect = {
368       "Trailer" => "Foo",
369       "Transfer-Encoding" => "chunked",
370     }
371     assert_equal(expect, headers)
372     assert body.parser.keepalive?
373     _, status = Process.waitpid2(pid)
374     assert status.success?
375     tmp = []
376     pid = fork { @s << "5\r\nhello\r\n0\r\nFoo: bar" }
377     rd, wr = IO.pipe
378     crlf_pid = fork do
379       wr.close
380       @s << rd.sysread(4)
381     end
382     rd.close
383     assert_nothing_raised do
384       first = true
385       body.each do |chunk|
386         tmp << chunk.dup
387         if first
388           first = false
389           wr.syswrite "\r\n\r\n"
390         end
391       end
392     end
393     assert_equal ["5\r\n", "hello\r\n", "0\r\n", "Foo: bar\r\n\r\n"], tmp
394     _, status = Process.waitpid2(pid)
395     assert status.success?
396     _, status = Process.waitpid2(crlf_pid)
397     assert status.success?
398     expect['Foo'] = 'bar'
399     assert_equal(expect, headers)
400     body.close
401     assert ! @c.closed?
402   end
404   def test_identity_burpy
405     pid = fork { @s << "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\n" }
406     @response = Kcar::Response.new(@c)
407     status, headers, body = @response.read
408     assert_equal status, "200 OK"
409     expect = { "Content-Length" => '5' }
410     assert_equal(expect, headers)
411     assert body.parser.keepalive?
412     _, status = Process.waitpid2(pid)
413     assert status.success?
414     tmp = []
415     pid = fork { @s << "h" }
416     rd, wr = IO.pipe
417     crlf_pid = fork do
418       wr.close
419       @s << rd.sysread(4)
420     end
421     rd.close
422     assert_nothing_raised do
423       first = true
424       body.each do |chunk|
425         tmp << chunk.dup
426         if first
427           first = false
428           wr.syswrite "ello"
429         end
430       end
431     end
432     assert_equal %w(h ello), tmp
433     _, status = Process.waitpid2(pid)
434     assert status.success?
435     _, status = Process.waitpid2(crlf_pid)
436     assert status.success?
437     assert_equal(expect, headers)
438     body.close
439     assert ! @c.closed?
440   end
442   def test_rack_preserve_chunk_hash
443     pid = fork do
444       @s << "HTTP/1.1 200 OK\r\n"
445       @s << "Trailer: Foo\r\n"
446       @s << "Transfer-Encoding: chunked\r\n\r\n"
447       @s << "5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n"
448     end
449     @response = Kcar::Response.new(@c)
450     status, headers, body = @response.rack
451     assert_equal status, "200 OK"
452     expect = {
453       "Trailer" => "Foo",
454       "Transfer-Encoding" => "chunked",
455     }
456     assert_equal expect, headers
457     tmp = []
458     assert body.parser.keepalive?
459     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
460     assert_equal ["5\r\n", "hello\r\n", "0\r\n", "Foo: bar\r\n\r\n"], tmp
461     expect["Foo"] = "bar"
462     assert_equal expect, headers
463     _, status = Process.waitpid2(pid)
464     assert status.success?
465     body.close
466     assert ! @c.closed?
467   end
469   def test_rack_preserve_chunk_ary
470     pid = fork do
471       @s << "HTTP/1.1 200 OK\r\n"
472       @s << "Trailer: Foo\r\n"
473       @s << "Transfer-Encoding: chunked\r\n\r\n"
474       @s << "5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n"
475     end
476     @response = Kcar::Response.new(@c, [])
477     status, headers, body = @response.rack
478     assert_equal status, "200 OK"
479     expect = [ %w(Trailer Foo), %w(Transfer-Encoding chunked) ]
480     assert_equal expect, headers
481     tmp = []
482     assert body.parser.keepalive?
483     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
484     assert_equal ["5\r\n", "hello\r\n", "0\r\n", "Foo: bar\r\n\r\n"], tmp
485     expect << %w(Foo bar)
486     assert_equal expect, headers
487     _, status = Process.waitpid2(pid)
488     assert status.success?
489     body.close
490     assert ! @c.closed?
491   end
493   def test_rack_preserve_chunk_no_keepalive
494     pid = fork do
495       @s << "HTTP/1.1 200 OK\r\n"
496       @s << "Connection: close\r\n"
497       @s << "Trailer: Foo\r\n"
498       @s << "Transfer-Encoding: chunked\r\n\r\n"
499       @s << "5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n"
500     end
501     @s.close
502     @response = Kcar::Response.new(@c, [])
503     status, headers, body = @response.rack
504     assert_kind_of Array, headers
505     assert_equal status, "200 OK"
506     tmp = []
507     assert ! body.parser.keepalive?
508     assert_nothing_raised { body.each { |chunk| tmp << chunk.dup } }
509     assert_equal "5\r\nhello\r\n0\r\nFoo: bar\r\n\r\n", tmp.join
510     _, status = Process.waitpid2(pid)
511     assert status.success?
512     body.close
513     assert @c.closed?
514   end
516   def test_rack_preserve_chunk_no_keepalive_2
517     s = "HTTP/1.1 200 OK\r\n"
518     s << "Connection: close\r\n"
519     s << "Content-Length: 666\r\n"
520     s << "\r\n"
521     s << "hello"
522     @s.write(s)
523     @response = Kcar::Response.new(@c, [])
524     status, headers, body = @response.rack
525     assert_kind_of Array, headers
526     assert_equal status, "200 OK"
527     tmp = []
528     assert ! body.parser.keepalive?
530     closer = Thread.new do
531       Thread.pass until tmp[0]
532       @s.close
533     end
534     assert_raises(EOFError) {
535       body.each { |chunk| tmp << chunk.dup }
536     }
537     assert_nil @c.close
538     assert_equal "hello", tmp[0]
539     assert_nil closer.value
540   end