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