test_ccc: use a pipe to synchronize test
[unicorn.git] / test / unit / test_ccc.rb
blob0db0c381eed11707ccaf4d9154bbf9e9df8cfc87
1 require 'socket'
2 require 'unicorn'
3 require 'io/wait'
4 require 'tempfile'
5 require 'test/unit'
7 class TestCccTCPI < Test::Unit::TestCase
8   def test_ccc_tcpi
9     start_pid = $$
10     host = '127.0.0.1'
11     srv = TCPServer.new(host, 0)
12     port = srv.addr[1]
13     err = Tempfile.new('unicorn_ccc')
14     rd, wr = IO.pipe
15     sleep_pipe = IO.pipe
16     pid = fork do
17       sleep_pipe[1].close
18       reqs = 0
19       rd.close
20       worker_pid = nil
21       app = lambda do |env|
22         worker_pid ||= begin
23           at_exit { wr.write(reqs.to_s) if worker_pid == $$ }
24           $$
25         end
26         reqs += 1
28         # will wake up when writer closes
29         sleep_pipe[0].read if env['PATH_INFO'] == '/sleep'
31         [ 200, [ %w(Content-Length 0),  %w(Content-Type text/plain) ], [] ]
32       end
33       ENV['UNICORN_FD'] = srv.fileno.to_s
34       opts = {
35         listeners: [ "#{host}:#{port}" ],
36         stderr_path: err.path,
37         check_client_connection: true,
38       }
39       uni = Unicorn::HttpServer.new(app, opts)
40       uni.start.join
41     end
42     wr.close
44     # make sure the server is running, at least
45     client = TCPSocket.new(host, port)
46     client.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
47     assert client.wait_readable(10), 'never got response from server'
48     res = client.read
49     assert_match %r{\AHTTP/1\.1 200}, res, 'got part of first response'
50     assert_match %r{\r\n\r\n\z}, res, 'got end of response, server is ready'
51     client.close
53     # start a slow request...
54     sleeper = TCPSocket.new(host, port)
55     sleeper.write("GET /sleep HTTP/1.1\r\nHost: example.com\r\n\r\n")
57     # and a bunch of aborted ones
58     nr = 100
59     nr.times do |i|
60       client = TCPSocket.new(host, port)
61       client.write("GET /collections/#{rand(10000)} HTTP/1.1\r\n" \
62                    "Host: example.com\r\n\r\n")
63       client.close
64     end
65     sleep_pipe[1].close # wake up the reader in the worker
66     res = sleeper.read
67     assert_match %r{\AHTTP/1\.1 200}, res, 'got part of first sleeper response'
68     assert_match %r{\r\n\r\n\z}, res, 'got end of sleeper response'
69     sleeper.close
70     kpid = pid
71     pid = nil
72     Process.kill(:QUIT, kpid)
73     _, status = Process.waitpid2(kpid)
74     assert status.success?
75     reqs = rd.read.to_i
76     warn "server got #{reqs} requests with #{nr} CCC aborted\n" if $DEBUG
77     assert_operator reqs, :<, nr
78     assert_operator reqs, :>=, 2, 'first 2 requests got through, at least'
79   ensure
80     return if start_pid != $$
81     srv.close if srv
82     if pid
83       Process.kill(:QUIT, pid)
84       _, status = Process.waitpid2(pid)
85       assert status.success?
86     end
87     err.close! if err
88     rd.close if rd
89   end
90 end