boourns-unicorn 4.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. data/.CHANGELOG.old +25 -0
  2. data/.document +29 -0
  3. data/.gitignore +24 -0
  4. data/.mailmap +26 -0
  5. data/.wrongdoc.yml +10 -0
  6. data/Application_Timeouts +77 -0
  7. data/CONTRIBUTORS +35 -0
  8. data/COPYING +674 -0
  9. data/DESIGN +97 -0
  10. data/Documentation/.gitignore +5 -0
  11. data/Documentation/GNUmakefile +30 -0
  12. data/Documentation/unicorn.1.txt +174 -0
  13. data/Documentation/unicorn_rails.1.txt +175 -0
  14. data/FAQ +53 -0
  15. data/GIT-VERSION-GEN +40 -0
  16. data/GNUmakefile +267 -0
  17. data/HACKING +134 -0
  18. data/ISSUES +36 -0
  19. data/KNOWN_ISSUES +79 -0
  20. data/LICENSE +64 -0
  21. data/Links +56 -0
  22. data/PHILOSOPHY +145 -0
  23. data/README +149 -0
  24. data/Rakefile +97 -0
  25. data/SIGNALS +114 -0
  26. data/Sandbox +96 -0
  27. data/TODO +5 -0
  28. data/TUNING +98 -0
  29. data/bin/unicorn +121 -0
  30. data/bin/unicorn_rails +209 -0
  31. data/examples/big_app_gc.rb +2 -0
  32. data/examples/echo.ru +27 -0
  33. data/examples/git.ru +13 -0
  34. data/examples/init.sh +74 -0
  35. data/examples/logger_mp_safe.rb +25 -0
  36. data/examples/logrotate.conf +29 -0
  37. data/examples/nginx.conf +156 -0
  38. data/examples/unicorn.conf.minimal.rb +13 -0
  39. data/examples/unicorn.conf.rb +94 -0
  40. data/ext/unicorn_http/CFLAGS +13 -0
  41. data/ext/unicorn_http/c_util.h +124 -0
  42. data/ext/unicorn_http/common_field_optimization.h +111 -0
  43. data/ext/unicorn_http/ext_help.h +86 -0
  44. data/ext/unicorn_http/extconf.rb +10 -0
  45. data/ext/unicorn_http/global_variables.h +97 -0
  46. data/ext/unicorn_http/httpdate.c +82 -0
  47. data/ext/unicorn_http/unicorn_http.rl +1036 -0
  48. data/ext/unicorn_http/unicorn_http_common.rl +76 -0
  49. data/lib/unicorn.rb +107 -0
  50. data/lib/unicorn/app/exec_cgi.rb +154 -0
  51. data/lib/unicorn/app/inetd.rb +109 -0
  52. data/lib/unicorn/app/old_rails.rb +35 -0
  53. data/lib/unicorn/app/old_rails/static.rb +59 -0
  54. data/lib/unicorn/cgi_wrapper.rb +147 -0
  55. data/lib/unicorn/configurator.rb +630 -0
  56. data/lib/unicorn/const.rb +40 -0
  57. data/lib/unicorn/http_request.rb +83 -0
  58. data/lib/unicorn/http_response.rb +45 -0
  59. data/lib/unicorn/http_server.rb +755 -0
  60. data/lib/unicorn/launcher.rb +62 -0
  61. data/lib/unicorn/oob_gc.rb +71 -0
  62. data/lib/unicorn/preread_input.rb +33 -0
  63. data/lib/unicorn/socket_helper.rb +208 -0
  64. data/lib/unicorn/ssl_client.rb +11 -0
  65. data/lib/unicorn/ssl_configurator.rb +104 -0
  66. data/lib/unicorn/ssl_server.rb +42 -0
  67. data/lib/unicorn/stream_input.rb +149 -0
  68. data/lib/unicorn/tee_input.rb +126 -0
  69. data/lib/unicorn/tmpio.rb +29 -0
  70. data/lib/unicorn/util.rb +69 -0
  71. data/lib/unicorn/worker.rb +88 -0
  72. data/local.mk.sample +59 -0
  73. data/script/isolate_for_tests +32 -0
  74. data/setup.rb +1586 -0
  75. data/t/.gitignore +5 -0
  76. data/t/GNUmakefile +82 -0
  77. data/t/README +42 -0
  78. data/t/bin/content-md5-put +36 -0
  79. data/t/bin/sha1sum.rb +17 -0
  80. data/t/bin/unused_listen +40 -0
  81. data/t/bin/utee +12 -0
  82. data/t/broken-app.ru +12 -0
  83. data/t/detach.ru +11 -0
  84. data/t/env.ru +3 -0
  85. data/t/heartbeat-timeout.ru +12 -0
  86. data/t/listener_names.ru +4 -0
  87. data/t/my-tap-lib.sh +201 -0
  88. data/t/oob_gc.ru +21 -0
  89. data/t/oob_gc_path.ru +21 -0
  90. data/t/pid.ru +3 -0
  91. data/t/preread_input.ru +17 -0
  92. data/t/rack-input-tests.ru +21 -0
  93. data/t/sslgen.sh +71 -0
  94. data/t/t0000-http-basic.sh +50 -0
  95. data/t/t0001-reload-bad-config.sh +53 -0
  96. data/t/t0002-config-conflict.sh +49 -0
  97. data/t/t0002-parser-error.sh +94 -0
  98. data/t/t0003-working_directory.sh +51 -0
  99. data/t/t0004-heartbeat-timeout.sh +69 -0
  100. data/t/t0004-working_directory_broken.sh +24 -0
  101. data/t/t0005-working_directory_app.rb.sh +37 -0
  102. data/t/t0006-reopen-logs.sh +83 -0
  103. data/t/t0006.ru +13 -0
  104. data/t/t0007-working_directory_no_embed_cli.sh +44 -0
  105. data/t/t0008-back_out_of_upgrade.sh +110 -0
  106. data/t/t0009-broken-app.sh +56 -0
  107. data/t/t0009-winch_ttin.sh +59 -0
  108. data/t/t0010-reap-logging.sh +55 -0
  109. data/t/t0011-active-unix-socket.sh +79 -0
  110. data/t/t0012-reload-empty-config.sh +85 -0
  111. data/t/t0013-rewindable-input-false.sh +24 -0
  112. data/t/t0013.ru +12 -0
  113. data/t/t0014-rewindable-input-true.sh +24 -0
  114. data/t/t0014.ru +12 -0
  115. data/t/t0015-configurator-internals.sh +25 -0
  116. data/t/t0016-trust-x-forwarded-false.sh +30 -0
  117. data/t/t0017-trust-x-forwarded-true.sh +30 -0
  118. data/t/t0018-write-on-close.sh +23 -0
  119. data/t/t0019-max_header_len.sh +49 -0
  120. data/t/t0020-at_exit-handler.sh +49 -0
  121. data/t/t0021-process_detach.sh +29 -0
  122. data/t/t0022-listener_names-preload_app.sh +32 -0
  123. data/t/t0100-rack-input-tests.sh +124 -0
  124. data/t/t0116-client_body_buffer_size.sh +80 -0
  125. data/t/t0116.ru +16 -0
  126. data/t/t0600-https-server-basic.sh +48 -0
  127. data/t/t9000-preread-input.sh +48 -0
  128. data/t/t9001-oob_gc.sh +47 -0
  129. data/t/t9002-oob_gc-path.sh +75 -0
  130. data/t/test-lib.sh +113 -0
  131. data/t/write-on-close.ru +11 -0
  132. data/test/aggregate.rb +15 -0
  133. data/test/benchmark/README +50 -0
  134. data/test/benchmark/dd.ru +18 -0
  135. data/test/benchmark/stack.ru +8 -0
  136. data/test/exec/README +5 -0
  137. data/test/exec/test_exec.rb +1041 -0
  138. data/test/test_helper.rb +300 -0
  139. data/test/unit/test_configurator.rb +158 -0
  140. data/test/unit/test_droplet.rb +28 -0
  141. data/test/unit/test_http_parser.rb +860 -0
  142. data/test/unit/test_http_parser_ng.rb +716 -0
  143. data/test/unit/test_http_parser_xftrust.rb +38 -0
  144. data/test/unit/test_request.rb +197 -0
  145. data/test/unit/test_response.rb +99 -0
  146. data/test/unit/test_server.rb +289 -0
  147. data/test/unit/test_signals.rb +207 -0
  148. data/test/unit/test_sni_hostnames.rb +47 -0
  149. data/test/unit/test_socket_helper.rb +192 -0
  150. data/test/unit/test_stream_input.rb +204 -0
  151. data/test/unit/test_tee_input.rb +296 -0
  152. data/test/unit/test_upload.rb +306 -0
  153. data/test/unit/test_util.rb +99 -0
  154. data/unicorn.gemspec +44 -0
  155. metadata +333 -0
@@ -0,0 +1,207 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ # Copyright (c) 2009 Eric Wong
4
+ # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
5
+ # the GPLv3
6
+ #
7
+ # Ensure we stay sane in the face of signals being sent to us
8
+
9
+ require 'test/test_helper'
10
+
11
+ include Unicorn
12
+
13
+ class Dd
14
+ def initialize(bs, count)
15
+ @count = count
16
+ @buf = ' ' * bs
17
+ end
18
+
19
+ def each(&block)
20
+ @count.times { yield @buf }
21
+ end
22
+ end
23
+
24
+ class SignalsTest < Test::Unit::TestCase
25
+
26
+ def setup
27
+ @bs = 1 * 1024 * 1024
28
+ @count = 100
29
+ @port = unused_port
30
+ @sock = Tempfile.new('unicorn.sock')
31
+ @tmp = Tempfile.new('unicorn.write')
32
+ @tmp.sync = true
33
+ File.unlink(@sock.path)
34
+ File.unlink(@tmp.path)
35
+ @server_opts = {
36
+ :listeners => [ "127.0.0.1:#@port", @sock.path ],
37
+ :after_fork => lambda { |server,worker|
38
+ trap(:HUP) { @tmp.syswrite('.') }
39
+ },
40
+ }
41
+ @server = nil
42
+ end
43
+
44
+ def teardown
45
+ reset_sig_handlers
46
+ end
47
+
48
+ def test_worker_dies_on_dead_master
49
+ pid = fork {
50
+ app = lambda { |env| [ 200, {'X-Pid' => "#$$" }, [] ] }
51
+ opts = @server_opts.merge(:timeout => 3)
52
+ redirect_test_io { HttpServer.new(app, opts).start.join }
53
+ }
54
+ child = sock = buf = t0 = nil
55
+ assert_nothing_raised do
56
+ wait_workers_ready("test_stderr.#{pid}.log", 1)
57
+ sock = TCPSocket.new('127.0.0.1', @port)
58
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
59
+ buf = sock.readpartial(4096)
60
+ sock.close
61
+ buf =~ /\bX-Pid: (\d+)\b/ or raise Exception
62
+ child = $1.to_i
63
+ wait_master_ready("test_stderr.#{pid}.log")
64
+ wait_workers_ready("test_stderr.#{pid}.log", 1)
65
+ Process.kill(:KILL, pid)
66
+ Process.waitpid(pid)
67
+ File.unlink("test_stderr.#{pid}.log", "test_stdout.#{pid}.log")
68
+ t0 = Time.now
69
+ end
70
+ assert child
71
+ assert t0
72
+ assert_raises(Errno::ESRCH) { loop { Process.kill(0, child); sleep 0.2 } }
73
+ assert((Time.now - t0) < 60)
74
+ end
75
+
76
+ def test_sleepy_kill
77
+ rd, wr = IO.pipe
78
+ pid = fork {
79
+ rd.close
80
+ app = lambda { |env| wr.syswrite('.'); sleep; [ 200, {}, [] ] }
81
+ redirect_test_io { HttpServer.new(app, @server_opts).start.join }
82
+ }
83
+ sock = buf = nil
84
+ wr.close
85
+ assert_nothing_raised do
86
+ wait_workers_ready("test_stderr.#{pid}.log", 1)
87
+ sock = TCPSocket.new('127.0.0.1', @port)
88
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
89
+ buf = rd.readpartial(1)
90
+ wait_master_ready("test_stderr.#{pid}.log")
91
+ Process.kill(:INT, pid)
92
+ Process.waitpid(pid)
93
+ end
94
+ assert_equal '.', buf
95
+ buf = nil
96
+ assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
97
+ Errno::EBADF) do
98
+ buf = sock.sysread(4096)
99
+ end
100
+ assert_nil buf
101
+ ensure
102
+ end
103
+
104
+ def test_timeout_slow_response
105
+ pid = fork {
106
+ app = lambda { |env| sleep }
107
+ opts = @server_opts.merge(:timeout => 3)
108
+ redirect_test_io { HttpServer.new(app, opts).start.join }
109
+ }
110
+ t0 = Time.now
111
+ sock = nil
112
+ assert_nothing_raised do
113
+ wait_workers_ready("test_stderr.#{pid}.log", 1)
114
+ sock = TCPSocket.new('127.0.0.1', @port)
115
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
116
+ end
117
+
118
+ buf = nil
119
+ assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
120
+ Errno::EBADF) do
121
+ buf = sock.sysread(4096)
122
+ end
123
+ diff = Time.now - t0
124
+ assert_nil buf
125
+ assert diff > 1.0, "diff was #{diff.inspect}"
126
+ assert diff < 60.0
127
+ ensure
128
+ Process.kill(:TERM, pid) rescue nil
129
+ end
130
+
131
+ def test_response_write
132
+ app = lambda { |env|
133
+ [ 200, { 'Content-Type' => 'text/plain', 'X-Pid' => Process.pid.to_s },
134
+ Dd.new(@bs, @count) ]
135
+ }
136
+ redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
137
+ sock = nil
138
+ assert_nothing_raised do
139
+ wait_workers_ready("test_stderr.#{$$}.log", 1)
140
+ sock = TCPSocket.new('127.0.0.1', @port)
141
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
142
+ end
143
+ buf = ''
144
+ header_len = pid = nil
145
+ assert_nothing_raised do
146
+ buf = sock.sysread(16384, buf)
147
+ pid = buf[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
148
+ header_len = buf[/\A(.+?\r\n\r\n)/m, 1].size
149
+ end
150
+ assert pid > 0, "pid not positive: #{pid.inspect}"
151
+ read = buf.size
152
+ size_before = @tmp.stat.size
153
+ assert_raises(EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::EINVAL,
154
+ Errno::EBADF) do
155
+ loop do
156
+ 3.times { Process.kill(:HUP, pid) }
157
+ sock.sysread(16384, buf)
158
+ read += buf.size
159
+ 3.times { Process.kill(:HUP, pid) }
160
+ end
161
+ end
162
+
163
+ redirect_test_io { @server.stop(true) }
164
+ # can't check for == since pending signals get merged
165
+ assert size_before < @tmp.stat.size
166
+ got = read - header_len
167
+ expect = @bs * @count
168
+ assert_equal(expect, got, "expect=#{expect} got=#{got}")
169
+ assert_nothing_raised { sock.close }
170
+ end
171
+
172
+ def test_request_read
173
+ app = lambda { |env|
174
+ while env['rack.input'].read(4096)
175
+ end
176
+ [ 200, {'Content-Type'=>'text/plain', 'X-Pid'=>Process.pid.to_s}, [] ]
177
+ }
178
+ redirect_test_io { @server = HttpServer.new(app, @server_opts).start }
179
+ pid = nil
180
+
181
+ assert_nothing_raised do
182
+ wait_workers_ready("test_stderr.#{$$}.log", 1)
183
+ sock = TCPSocket.new('127.0.0.1', @port)
184
+ sock.syswrite("GET / HTTP/1.0\r\n\r\n")
185
+ pid = sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
186
+ sock.close
187
+ end
188
+
189
+ assert pid > 0, "pid not positive: #{pid.inspect}"
190
+ sock = TCPSocket.new('127.0.0.1', @port)
191
+ sock.syswrite("PUT / HTTP/1.0\r\n")
192
+ sock.syswrite("Content-Length: #{@bs * @count}\r\n\r\n")
193
+ 1000.times { Process.kill(:HUP, pid) }
194
+ size_before = @tmp.stat.size
195
+ killer = fork { loop { Process.kill(:HUP, pid); sleep(0.0001) } }
196
+ buf = ' ' * @bs
197
+ @count.times { sock.syswrite(buf) }
198
+ Process.kill(:KILL, killer)
199
+ Process.waitpid2(killer)
200
+ redirect_test_io { @server.stop(true) }
201
+ # can't check for == since pending signals get merged
202
+ assert size_before < @tmp.stat.size
203
+ assert_equal pid, sock.sysread(4096)[/\r\nX-Pid: (\d+)\r\n/, 1].to_i
204
+ sock.close
205
+ end
206
+
207
+ end
@@ -0,0 +1,47 @@
1
+ # -*- encoding: binary -*-
2
+ require "test/unit"
3
+ require "unicorn"
4
+
5
+ # this tests an implementation detail, it may change so this test
6
+ # can be removed later.
7
+ class TestSniHostnames < Test::Unit::TestCase
8
+ include Unicorn::SSLServer
9
+
10
+ def setup
11
+ GC.start
12
+ end
13
+
14
+ def teardown
15
+ GC.start
16
+ end
17
+
18
+ def test_host_name_detect_one
19
+ app = Rack::Builder.new do
20
+ map "http://sni1.example.com/" do
21
+ use Rack::ContentLength
22
+ use Rack::ContentType, "text/plain"
23
+ run lambda { |env| [ 200, {}, [] ] }
24
+ end
25
+ end.to_app
26
+ hostnames = rack_sni_hostnames(app)
27
+ assert hostnames.include?("sni1.example.com")
28
+ end
29
+
30
+ def test_host_name_detect_multiple
31
+ app = Rack::Builder.new do
32
+ map "http://sni2.example.com/" do
33
+ use Rack::ContentLength
34
+ use Rack::ContentType, "text/plain"
35
+ run lambda { |env| [ 200, {}, [] ] }
36
+ end
37
+ map "http://sni3.example.com/" do
38
+ use Rack::ContentLength
39
+ use Rack::ContentType, "text/plain"
40
+ run lambda { |env| [ 200, {}, [] ] }
41
+ end
42
+ end.to_app
43
+ hostnames = rack_sni_hostnames(app)
44
+ assert hostnames.include?("sni2.example.com")
45
+ assert hostnames.include?("sni3.example.com")
46
+ end
47
+ end
@@ -0,0 +1,192 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ require 'test/test_helper'
4
+ require 'tempfile'
5
+
6
+ class TestSocketHelper < Test::Unit::TestCase
7
+ include Unicorn::SocketHelper
8
+ attr_reader :logger
9
+ GET_SLASH = "GET / HTTP/1.0\r\n\r\n".freeze
10
+
11
+ def setup
12
+ @log_tmp = Tempfile.new 'logger'
13
+ @logger = Logger.new(@log_tmp.path)
14
+ @test_addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
15
+ @test6_addr = ENV['UNICORN_TEST6_ADDR'] || '::1'
16
+ GC.disable
17
+ end
18
+
19
+ def teardown
20
+ GC.enable
21
+ end
22
+
23
+ def test_bind_listen_tcp
24
+ port = unused_port @test_addr
25
+ @tcp_listener_name = "#@test_addr:#{port}"
26
+ @tcp_listener = bind_listen(@tcp_listener_name)
27
+ assert TCPServer === @tcp_listener
28
+ assert_equal @tcp_listener_name, sock_name(@tcp_listener)
29
+ end
30
+
31
+ def test_bind_listen_options
32
+ port = unused_port @test_addr
33
+ tcp_listener_name = "#@test_addr:#{port}"
34
+ tmp = Tempfile.new 'unix.sock'
35
+ unix_listener_name = tmp.path
36
+ File.unlink(tmp.path)
37
+ [ { :backlog => 5 }, { :sndbuf => 4096 }, { :rcvbuf => 4096 },
38
+ { :backlog => 16, :rcvbuf => 4096, :sndbuf => 4096 }
39
+ ].each do |opts|
40
+ assert_nothing_raised do
41
+ tcp_listener = bind_listen(tcp_listener_name, opts)
42
+ assert TCPServer === tcp_listener
43
+ tcp_listener.close
44
+ unix_listener = bind_listen(unix_listener_name, opts)
45
+ assert UNIXServer === unix_listener
46
+ unix_listener.close
47
+ end
48
+ end
49
+ #system('cat', @log_tmp.path)
50
+ end
51
+
52
+ def test_bind_listen_unix
53
+ old_umask = File.umask(0777)
54
+ tmp = Tempfile.new 'unix.sock'
55
+ @unix_listener_path = tmp.path
56
+ File.unlink(@unix_listener_path)
57
+ @unix_listener = bind_listen(@unix_listener_path)
58
+ assert UNIXServer === @unix_listener
59
+ assert_equal @unix_listener_path, sock_name(@unix_listener)
60
+ assert File.readable?(@unix_listener_path), "not readable"
61
+ assert File.writable?(@unix_listener_path), "not writable"
62
+ assert_equal 0777, File.umask
63
+ ensure
64
+ File.umask(old_umask)
65
+ end
66
+
67
+ def test_bind_listen_unix_umask
68
+ old_umask = File.umask(0777)
69
+ tmp = Tempfile.new 'unix.sock'
70
+ @unix_listener_path = tmp.path
71
+ File.unlink(@unix_listener_path)
72
+ @unix_listener = bind_listen(@unix_listener_path, :umask => 077)
73
+ assert UNIXServer === @unix_listener
74
+ assert_equal @unix_listener_path, sock_name(@unix_listener)
75
+ assert_equal 0140700, File.stat(@unix_listener_path).mode
76
+ assert_equal 0777, File.umask
77
+ ensure
78
+ File.umask(old_umask)
79
+ end
80
+
81
+ def test_bind_listen_unix_idempotent
82
+ test_bind_listen_unix
83
+ a = bind_listen(@unix_listener)
84
+ assert_equal a.fileno, @unix_listener.fileno
85
+ unix_server = server_cast(@unix_listener)
86
+ assert UNIXServer === unix_server
87
+ a = bind_listen(unix_server)
88
+ assert_equal a.fileno, unix_server.fileno
89
+ assert_equal a.fileno, @unix_listener.fileno
90
+ end
91
+
92
+ def test_bind_listen_tcp_idempotent
93
+ test_bind_listen_tcp
94
+ a = bind_listen(@tcp_listener)
95
+ assert_equal a.fileno, @tcp_listener.fileno
96
+ tcp_server = server_cast(@tcp_listener)
97
+ assert TCPServer === tcp_server
98
+ a = bind_listen(tcp_server)
99
+ assert_equal a.fileno, tcp_server.fileno
100
+ assert_equal a.fileno, @tcp_listener.fileno
101
+ end
102
+
103
+ def test_bind_listen_unix_rebind
104
+ test_bind_listen_unix
105
+ new_listener = nil
106
+ assert_raises(Errno::EADDRINUSE) do
107
+ new_listener = bind_listen(@unix_listener_path)
108
+ end
109
+ assert_nothing_raised do
110
+ File.unlink(@unix_listener_path)
111
+ new_listener = bind_listen(@unix_listener_path)
112
+ end
113
+ assert UNIXServer === new_listener
114
+ assert new_listener.fileno != @unix_listener.fileno
115
+ assert_equal sock_name(new_listener), sock_name(@unix_listener)
116
+ assert_equal @unix_listener_path, sock_name(new_listener)
117
+ pid = fork do
118
+ client = server_cast(new_listener).accept
119
+ client.syswrite('abcde')
120
+ exit 0
121
+ end
122
+ s = UNIXSocket.new(@unix_listener_path)
123
+ IO.select([s])
124
+ assert_equal 'abcde', s.sysread(5)
125
+ pid, status = Process.waitpid2(pid)
126
+ assert status.success?
127
+ end
128
+
129
+ def test_server_cast
130
+ assert_nothing_raised do
131
+ test_bind_listen_unix
132
+ test_bind_listen_tcp
133
+ end
134
+ unix_listener_socket = Socket.for_fd(@unix_listener.fileno)
135
+ assert Socket === unix_listener_socket
136
+ @unix_server = server_cast(unix_listener_socket)
137
+ assert_equal @unix_listener.fileno, @unix_server.fileno
138
+ assert UNIXServer === @unix_server
139
+ assert_equal(@unix_server.path, @unix_listener.path,
140
+ "##{@unix_server.path} != #{@unix_listener.path}")
141
+ assert File.socket?(@unix_server.path)
142
+ assert_equal @unix_listener_path, sock_name(@unix_server)
143
+
144
+ tcp_listener_socket = Socket.for_fd(@tcp_listener.fileno)
145
+ assert Socket === tcp_listener_socket
146
+ @tcp_server = server_cast(tcp_listener_socket)
147
+ assert_equal @tcp_listener.fileno, @tcp_server.fileno
148
+ assert TCPServer === @tcp_server
149
+ assert_equal @tcp_listener_name, sock_name(@tcp_server)
150
+ end
151
+
152
+ def test_sock_name
153
+ test_server_cast
154
+ sock_name(@unix_server)
155
+ end
156
+
157
+ def test_tcp_defer_accept_default
158
+ port = unused_port @test_addr
159
+ name = "#@test_addr:#{port}"
160
+ sock = bind_listen(name)
161
+ cur = sock.getsockopt(Socket::SOL_TCP, TCP_DEFER_ACCEPT).unpack('i')[0]
162
+ assert cur >= 1
163
+ end if defined?(TCP_DEFER_ACCEPT)
164
+
165
+ def test_tcp_defer_accept_disable
166
+ port = unused_port @test_addr
167
+ name = "#@test_addr:#{port}"
168
+ sock = bind_listen(name, :tcp_defer_accept => false)
169
+ cur = sock.getsockopt(Socket::SOL_TCP, TCP_DEFER_ACCEPT).unpack('i')[0]
170
+ assert_equal 0, cur
171
+ end if defined?(TCP_DEFER_ACCEPT)
172
+
173
+ def test_tcp_defer_accept_nr
174
+ port = unused_port @test_addr
175
+ name = "#@test_addr:#{port}"
176
+ sock = bind_listen(name, :tcp_defer_accept => 60)
177
+ cur = sock.getsockopt(Socket::SOL_TCP, TCP_DEFER_ACCEPT).unpack('i')[0]
178
+ assert cur > 1
179
+ end if defined?(TCP_DEFER_ACCEPT)
180
+
181
+ def test_ipv6only
182
+ port = begin
183
+ unused_port "#@test6_addr"
184
+ rescue Errno::EINVAL
185
+ return
186
+ end
187
+ sock = bind_listen "[#@test6_addr]:#{port}", :ipv6only => true
188
+ cur = sock.getsockopt(:IPPROTO_IPV6, :IPV6_V6ONLY).unpack('i')[0]
189
+ assert_equal 1, cur
190
+ rescue Errno::EAFNOSUPPORT
191
+ end if RUBY_VERSION >= "1.9.2"
192
+ end
@@ -0,0 +1,204 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ require 'test/unit'
4
+ require 'digest/sha1'
5
+ require 'unicorn'
6
+
7
+ class TestStreamInput < Test::Unit::TestCase
8
+ def setup
9
+ @rs = $/
10
+ @env = {}
11
+ @rd, @wr = Kgio::UNIXSocket.pair
12
+ @rd.sync = @wr.sync = true
13
+ @start_pid = $$
14
+ end
15
+
16
+ def teardown
17
+ return if $$ != @start_pid
18
+ $/ = @rs
19
+ @rd.close rescue nil
20
+ @wr.close rescue nil
21
+ Process.waitall
22
+ end
23
+
24
+ def test_read_negative
25
+ r = init_request('hello')
26
+ si = Unicorn::StreamInput.new(@rd, r)
27
+ assert_raises(ArgumentError) { si.read(-1) }
28
+ assert_equal 'hello', si.read
29
+ end
30
+
31
+ def test_read_small
32
+ r = init_request('hello')
33
+ si = Unicorn::StreamInput.new(@rd, r)
34
+ assert_equal 'hello', si.read
35
+ assert_equal '', si.read
36
+ assert_nil si.read(5)
37
+ assert_nil si.gets
38
+ end
39
+
40
+ def test_gets_oneliner
41
+ r = init_request('hello')
42
+ si = Unicorn::StreamInput.new(@rd, r)
43
+ assert_equal 'hello', si.gets
44
+ assert_nil si.gets
45
+ end
46
+
47
+ def test_gets_multiline
48
+ r = init_request("a\nb\n\n")
49
+ si = Unicorn::StreamInput.new(@rd, r)
50
+ assert_equal "a\n", si.gets
51
+ assert_equal "b\n", si.gets
52
+ assert_equal "\n", si.gets
53
+ assert_nil si.gets
54
+ end
55
+
56
+ def test_gets_empty_rs
57
+ $/ = nil
58
+ r = init_request("a\nb\n\n")
59
+ si = Unicorn::StreamInput.new(@rd, r)
60
+ assert_equal "a\nb\n\n", si.gets
61
+ assert_nil si.gets
62
+ end
63
+
64
+ def test_read_with_equal_len
65
+ r = init_request("abcde")
66
+ si = Unicorn::StreamInput.new(@rd, r)
67
+ assert_equal "abcde", si.read(5)
68
+ assert_nil si.read(5)
69
+ end
70
+
71
+ def test_big_body_multi
72
+ r = init_request('.', Unicorn::Const::MAX_BODY + 1)
73
+ si = Unicorn::StreamInput.new(@rd, r)
74
+ assert_equal Unicorn::Const::MAX_BODY, @parser.content_length
75
+ assert ! @parser.body_eof?
76
+ nr = Unicorn::Const::MAX_BODY / 4
77
+ pid = fork {
78
+ @rd.close
79
+ nr.times { @wr.write('....') }
80
+ @wr.close
81
+ }
82
+ @wr.close
83
+ assert_equal '.', si.read(1)
84
+ nr.times { |x|
85
+ assert_equal '....', si.read(4), "nr=#{x}"
86
+ }
87
+ assert_nil si.read(1)
88
+ status = nil
89
+ assert_nothing_raised { pid, status = Process.waitpid2(pid) }
90
+ assert status.success?
91
+ end
92
+
93
+ def test_gets_long
94
+ r = init_request("hello", 5 + (4096 * 4 * 3) + "#$/foo#$/".size)
95
+ si = Unicorn::StreamInput.new(@rd, r)
96
+ status = line = nil
97
+ pid = fork {
98
+ @rd.close
99
+ 3.times { @wr.write("ffff" * 4096) }
100
+ @wr.write "#$/foo#$/"
101
+ @wr.close
102
+ }
103
+ @wr.close
104
+ assert_nothing_raised { line = si.gets }
105
+ assert_equal(4096 * 4 * 3 + 5 + $/.size, line.size)
106
+ assert_equal("hello" << ("ffff" * 4096 * 3) << "#$/", line)
107
+ assert_nothing_raised { line = si.gets }
108
+ assert_equal "foo#$/", line
109
+ assert_nil si.gets
110
+ assert_nothing_raised { pid, status = Process.waitpid2(pid) }
111
+ assert status.success?
112
+ end
113
+
114
+ def test_read_with_buffer
115
+ r = init_request('hello')
116
+ si = Unicorn::StreamInput.new(@rd, r)
117
+ buf = ''
118
+ rv = si.read(4, buf)
119
+ assert_equal 'hell', rv
120
+ assert_equal 'hell', buf
121
+ assert_equal rv.object_id, buf.object_id
122
+ assert_equal 'o', si.read
123
+ assert_equal nil, si.read(5, buf)
124
+ end
125
+
126
+ def test_read_with_buffer_clobbers
127
+ r = init_request('hello')
128
+ si = Unicorn::StreamInput.new(@rd, r)
129
+ buf = 'foo'
130
+ assert_equal 'hello', si.read(nil, buf)
131
+ assert_equal 'hello', buf
132
+ assert_equal '', si.read(nil, buf)
133
+ assert_equal '', buf
134
+ buf = 'asdf'
135
+ assert_nil si.read(5, buf)
136
+ assert_equal '', buf
137
+ end
138
+
139
+ def test_read_zero
140
+ r = init_request('hello')
141
+ si = Unicorn::StreamInput.new(@rd, r)
142
+ assert_equal '', si.read(0)
143
+ buf = 'asdf'
144
+ rv = si.read(0, buf)
145
+ assert_equal rv.object_id, buf.object_id
146
+ assert_equal '', buf
147
+ assert_equal 'hello', si.read
148
+ assert_nil si.read(5)
149
+ assert_equal '', si.read(0)
150
+ buf = 'hello'
151
+ rv = si.read(0, buf)
152
+ assert_equal rv.object_id, buf.object_id
153
+ assert_equal '', rv
154
+ end
155
+
156
+ def test_gets_read_mix
157
+ r = init_request("hello\nasdfasdf")
158
+ si = Unicorn::StreamInput.new(@rd, r)
159
+ assert_equal "hello\n", si.gets
160
+ assert_equal "asdfasdf", si.read(9)
161
+ assert_nil si.read(9)
162
+ end
163
+
164
+ def test_gets_read_mix_chunked
165
+ r = @parser = Unicorn::HttpParser.new
166
+ body = "6\r\nhello"
167
+ @buf = "POST / HTTP/1.1\r\n" \
168
+ "Host: localhost\r\n" \
169
+ "Transfer-Encoding: chunked\r\n" \
170
+ "\r\n#{body}"
171
+ assert_equal @env, @parser.headers(@env, @buf)
172
+ assert_equal body, @buf
173
+ si = Unicorn::StreamInput.new(@rd, r)
174
+ @wr.syswrite "\n\r\n"
175
+ assert_equal "hello\n", si.gets
176
+ @wr.syswrite "8\r\nasdfasdf\r\n"
177
+ assert_equal"asdfasdf", si.read(9) + si.read(9)
178
+ @wr.syswrite "0\r\n\r\n"
179
+ assert_nil si.read(9)
180
+ end
181
+
182
+ def test_gets_read_mix_big
183
+ r = init_request("hello\n#{'.' * 65536}")
184
+ si = Unicorn::StreamInput.new(@rd, r)
185
+ assert_equal "hello\n", si.gets
186
+ assert_equal '.' * 16384, si.read(16384)
187
+ assert_equal '.' * 16383, si.read(16383)
188
+ assert_equal '.' * 16384, si.read(16384)
189
+ assert_equal '.' * 16385, si.read(16385)
190
+ assert_nil si.gets
191
+ end
192
+
193
+ def init_request(body, size = nil)
194
+ @parser = Unicorn::HttpParser.new
195
+ body = body.to_s.freeze
196
+ @buf = "POST / HTTP/1.1\r\n" \
197
+ "Host: localhost\r\n" \
198
+ "Content-Length: #{size || body.size}\r\n" \
199
+ "\r\n#{body}"
200
+ assert_equal @env, @parser.headers(@env, @buf)
201
+ assert_equal body, @buf
202
+ @parser
203
+ end
204
+ end