unicorn-maintained 6.2.0

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 (151) hide show
  1. checksums.yaml +7 -0
  2. data/.CHANGELOG.old +25 -0
  3. data/.document +28 -0
  4. data/.gitattributes +5 -0
  5. data/.gitignore +25 -0
  6. data/.mailmap +26 -0
  7. data/.manifest +149 -0
  8. data/.olddoc.yml +25 -0
  9. data/Application_Timeouts +77 -0
  10. data/CONTRIBUTORS +39 -0
  11. data/COPYING +674 -0
  12. data/DESIGN +99 -0
  13. data/Documentation/.gitignore +3 -0
  14. data/Documentation/unicorn.1 +222 -0
  15. data/Documentation/unicorn_rails.1 +207 -0
  16. data/FAQ +70 -0
  17. data/GIT-VERSION-FILE +1 -0
  18. data/GIT-VERSION-GEN +39 -0
  19. data/GNUmakefile +317 -0
  20. data/HACKING +112 -0
  21. data/ISSUES +102 -0
  22. data/KNOWN_ISSUES +79 -0
  23. data/LATEST +1 -0
  24. data/LICENSE +67 -0
  25. data/Links +58 -0
  26. data/NEWS +1 -0
  27. data/PHILOSOPHY +139 -0
  28. data/README +156 -0
  29. data/Rakefile +16 -0
  30. data/SIGNALS +123 -0
  31. data/Sandbox +104 -0
  32. data/TODO +3 -0
  33. data/TUNING +119 -0
  34. data/archive/.gitignore +3 -0
  35. data/archive/slrnpull.conf +4 -0
  36. data/bin/unicorn +128 -0
  37. data/bin/unicorn_rails +209 -0
  38. data/examples/big_app_gc.rb +2 -0
  39. data/examples/echo.ru +26 -0
  40. data/examples/init.sh +102 -0
  41. data/examples/logger_mp_safe.rb +25 -0
  42. data/examples/logrotate.conf +44 -0
  43. data/examples/nginx.conf +156 -0
  44. data/examples/unicorn.conf.minimal.rb +13 -0
  45. data/examples/unicorn.conf.rb +110 -0
  46. data/examples/unicorn.socket +11 -0
  47. data/examples/unicorn@.service +40 -0
  48. data/ext/unicorn_http/CFLAGS +13 -0
  49. data/ext/unicorn_http/c_util.h +116 -0
  50. data/ext/unicorn_http/common_field_optimization.h +128 -0
  51. data/ext/unicorn_http/epollexclusive.h +128 -0
  52. data/ext/unicorn_http/ext_help.h +38 -0
  53. data/ext/unicorn_http/extconf.rb +39 -0
  54. data/ext/unicorn_http/global_variables.h +97 -0
  55. data/ext/unicorn_http/httpdate.c +91 -0
  56. data/ext/unicorn_http/unicorn_http.c +4334 -0
  57. data/ext/unicorn_http/unicorn_http.rl +1040 -0
  58. data/ext/unicorn_http/unicorn_http_common.rl +76 -0
  59. data/lib/unicorn/app/old_rails/static.rb +59 -0
  60. data/lib/unicorn/app/old_rails.rb +35 -0
  61. data/lib/unicorn/cgi_wrapper.rb +147 -0
  62. data/lib/unicorn/configurator.rb +748 -0
  63. data/lib/unicorn/const.rb +21 -0
  64. data/lib/unicorn/http_request.rb +201 -0
  65. data/lib/unicorn/http_response.rb +93 -0
  66. data/lib/unicorn/http_server.rb +859 -0
  67. data/lib/unicorn/launcher.rb +62 -0
  68. data/lib/unicorn/oob_gc.rb +81 -0
  69. data/lib/unicorn/preread_input.rb +33 -0
  70. data/lib/unicorn/select_waiter.rb +6 -0
  71. data/lib/unicorn/socket_helper.rb +185 -0
  72. data/lib/unicorn/stream_input.rb +151 -0
  73. data/lib/unicorn/tee_input.rb +131 -0
  74. data/lib/unicorn/tmpio.rb +33 -0
  75. data/lib/unicorn/util.rb +90 -0
  76. data/lib/unicorn/version.rb +1 -0
  77. data/lib/unicorn/worker.rb +165 -0
  78. data/lib/unicorn.rb +136 -0
  79. data/man/man1/unicorn.1 +222 -0
  80. data/man/man1/unicorn_rails.1 +207 -0
  81. data/setup.rb +1586 -0
  82. data/t/.gitignore +4 -0
  83. data/t/GNUmakefile +5 -0
  84. data/t/README +49 -0
  85. data/t/active-unix-socket.t +117 -0
  86. data/t/bin/unused_listen +40 -0
  87. data/t/broken-app.ru +12 -0
  88. data/t/client_body_buffer_size.ru +14 -0
  89. data/t/client_body_buffer_size.t +80 -0
  90. data/t/detach.ru +11 -0
  91. data/t/env.ru +3 -0
  92. data/t/fails-rack-lint.ru +5 -0
  93. data/t/heartbeat-timeout.ru +12 -0
  94. data/t/heartbeat-timeout.t +62 -0
  95. data/t/integration.ru +115 -0
  96. data/t/integration.t +356 -0
  97. data/t/lib.perl +258 -0
  98. data/t/listener_names.ru +4 -0
  99. data/t/my-tap-lib.sh +201 -0
  100. data/t/oob_gc.ru +17 -0
  101. data/t/oob_gc_path.ru +17 -0
  102. data/t/pid.ru +3 -0
  103. data/t/preread_input.ru +22 -0
  104. data/t/reload-bad-config.t +54 -0
  105. data/t/reopen-logs.ru +13 -0
  106. data/t/reopen-logs.t +39 -0
  107. data/t/t0008-back_out_of_upgrade.sh +110 -0
  108. data/t/t0009-broken-app.sh +56 -0
  109. data/t/t0010-reap-logging.sh +55 -0
  110. data/t/t0012-reload-empty-config.sh +86 -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/t0020-at_exit-handler.sh +49 -0
  117. data/t/t0021-process_detach.sh +29 -0
  118. data/t/t0022-listener_names-preload_app.sh +32 -0
  119. data/t/t0300-no-default-middleware.sh +20 -0
  120. data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  121. data/t/t0301.ru +13 -0
  122. data/t/t9001-oob_gc.sh +47 -0
  123. data/t/t9002-oob_gc-path.sh +75 -0
  124. data/t/test-lib.sh +125 -0
  125. data/t/winch_ttin.t +67 -0
  126. data/t/working_directory.t +94 -0
  127. data/test/aggregate.rb +15 -0
  128. data/test/benchmark/README +60 -0
  129. data/test/benchmark/dd.ru +18 -0
  130. data/test/benchmark/ddstream.ru +50 -0
  131. data/test/benchmark/readinput.ru +40 -0
  132. data/test/benchmark/stack.ru +8 -0
  133. data/test/benchmark/uconnect.perl +66 -0
  134. data/test/exec/README +5 -0
  135. data/test/exec/test_exec.rb +1029 -0
  136. data/test/test_helper.rb +306 -0
  137. data/test/unit/test_ccc.rb +91 -0
  138. data/test/unit/test_configurator.rb +175 -0
  139. data/test/unit/test_droplet.rb +28 -0
  140. data/test/unit/test_http_parser.rb +884 -0
  141. data/test/unit/test_http_parser_ng.rb +714 -0
  142. data/test/unit/test_request.rb +169 -0
  143. data/test/unit/test_server.rb +244 -0
  144. data/test/unit/test_signals.rb +188 -0
  145. data/test/unit/test_socket_helper.rb +159 -0
  146. data/test/unit/test_stream_input.rb +210 -0
  147. data/test/unit/test_tee_input.rb +303 -0
  148. data/test/unit/test_util.rb +131 -0
  149. data/test/unit/test_waiter.rb +34 -0
  150. data/unicorn.gemspec +48 -0
  151. metadata +275 -0
@@ -0,0 +1,306 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ # Copyright (c) 2005 Zed A. Shaw
4
+ # You can redistribute it and/or modify it under the same terms as Ruby 1.8 or
5
+ # the GPLv2+ (GPLv3+ preferred)
6
+ #
7
+ # Additional work donated by contributors. See git history
8
+ # for more information.
9
+
10
+ STDIN.sync = STDOUT.sync = STDERR.sync = true # buffering makes debugging hard
11
+
12
+ # FIXME: move curl-dependent tests into t/
13
+ ENV['NO_PROXY'] ||= ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
14
+
15
+ # Some tests watch a log file or a pid file to spring up to check state
16
+ # Can't rely on inotify on non-Linux and logging to a pipe makes things
17
+ # more complicated
18
+ DEFAULT_TRIES = 1000
19
+ DEFAULT_RES = 0.2
20
+
21
+ require 'test/unit'
22
+ require 'net/http'
23
+ require 'digest/sha1'
24
+ require 'uri'
25
+ require 'stringio'
26
+ require 'pathname'
27
+ require 'tempfile'
28
+ require 'fileutils'
29
+ require 'logger'
30
+ require 'unicorn'
31
+ require 'io/nonblock'
32
+
33
+ if ENV['DEBUG']
34
+ require 'ruby-debug'
35
+ Debugger.start
36
+ end
37
+
38
+ unless RUBY_VERSION < '3.1'
39
+ warn "Unicorn was only tested against MRI up to 3.0.\n" \
40
+ "It might not properly work with #{RUBY_VERSION}"
41
+ end
42
+
43
+ def redirect_test_io
44
+ orig_err = STDERR.dup
45
+ orig_out = STDOUT.dup
46
+ rdr_pid = $$
47
+ new_out = File.open("test_stdout.#$$.log", "a")
48
+ new_err = File.open("test_stderr.#$$.log", "a")
49
+ new_out.sync = new_err.sync = true
50
+
51
+ if tail = ENV['TAIL'] # "tail -F" if GNU, "tail -f" otherwise
52
+ require 'shellwords'
53
+ cmd = tail.shellsplit
54
+ cmd << new_out.path
55
+ cmd << new_err.path
56
+ pid = Process.spawn(*cmd, { 1 => 2, :pgroup => true })
57
+ sleep 0.1 # wait for tail(1) to startup
58
+ end
59
+ STDERR.reopen(new_err)
60
+ STDOUT.reopen(new_out)
61
+ STDERR.sync = STDOUT.sync = true
62
+
63
+ at_exit do
64
+ if rdr_pid == $$
65
+ File.unlink(new_out.path) rescue nil
66
+ File.unlink(new_err.path) rescue nil
67
+ end
68
+ end
69
+
70
+ begin
71
+ yield
72
+ ensure
73
+ STDERR.reopen(orig_err)
74
+ STDOUT.reopen(orig_out)
75
+ Process.kill(:TERM, pid) if pid
76
+ end
77
+ end
78
+
79
+ # which(1) exit codes cannot be trusted on some systems
80
+ # We use UNIX shell utilities in some tests because we don't trust
81
+ # ourselves to write Ruby 100% correctly :)
82
+ def which(bin)
83
+ ex = ENV['PATH'].split(/:/).detect do |x|
84
+ x << "/#{bin}"
85
+ File.executable?(x)
86
+ end or warn "`#{bin}' not found in PATH=#{ENV['PATH']}"
87
+ ex
88
+ end
89
+
90
+ # Either takes a string to do a get request against, or a tuple of [URI, HTTP] where
91
+ # HTTP is some kind of Net::HTTP request object (POST, HEAD, etc.)
92
+ def hit(uris)
93
+ results = []
94
+ uris.each do |u|
95
+ res = nil
96
+
97
+ if u.kind_of? String
98
+ u = 'http://127.0.0.1:8080/' if u == 'http://0.0.0.0:8080/'
99
+ res = Net::HTTP.get(URI.parse(u))
100
+ else
101
+ url = URI.parse(u[0])
102
+ res = Net::HTTP.new(url.host, url.port).start {|h| h.request(u[1]) }
103
+ end
104
+
105
+ assert res != nil, "Didn't get a response: #{u}"
106
+ results << res
107
+ end
108
+
109
+ return results
110
+ end
111
+
112
+ # unused_port provides an unused port on +addr+ usable for TCP that is
113
+ # guaranteed to be unused across all unicorn builds on that system. It
114
+ # prevents race conditions by using a lock file other unicorn builds
115
+ # will see. This is required if you perform several builds in parallel
116
+ # with a continuous integration system or run tests in parallel via
117
+ # gmake. This is NOT guaranteed to be race-free if you run other
118
+ # processes that bind to random ports for testing (but the window
119
+ # for a race condition is very small). You may also set UNICORN_TEST_ADDR
120
+ # to override the default test address (127.0.0.1).
121
+ def unused_port(addr = '127.0.0.1')
122
+ retries = 100
123
+ base = 5000
124
+ port = sock = nil
125
+ begin
126
+ begin
127
+ port = base + rand(32768 - base)
128
+ while port == Unicorn::Const::DEFAULT_PORT
129
+ port = base + rand(32768 - base)
130
+ end
131
+
132
+ sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
133
+ sock.bind(Socket.pack_sockaddr_in(port, addr))
134
+ sock.listen(5)
135
+ rescue Errno::EADDRINUSE, Errno::EACCES
136
+ sock.close rescue nil
137
+ retry if (retries -= 1) >= 0
138
+ end
139
+
140
+ # since we'll end up closing the random port we just got, there's a race
141
+ # condition could allow the random port we just chose to reselect itself
142
+ # when running tests in parallel with gmake. Create a lock file while
143
+ # we have the port here to ensure that does not happen .
144
+ lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
145
+ File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600).close
146
+ at_exit { File.unlink(lock_path) rescue nil }
147
+ rescue Errno::EEXIST
148
+ sock.close rescue nil
149
+ retry
150
+ end
151
+ sock.close rescue nil
152
+ port
153
+ end
154
+
155
+ def try_require(lib)
156
+ begin
157
+ require lib
158
+ true
159
+ rescue LoadError
160
+ false
161
+ end
162
+ end
163
+
164
+ # sometimes the server may not come up right away
165
+ def retry_hit(uris = [])
166
+ tries = DEFAULT_TRIES
167
+ begin
168
+ hit(uris)
169
+ rescue Errno::EINVAL, Errno::ECONNREFUSED => err
170
+ if (tries -= 1) > 0
171
+ sleep DEFAULT_RES
172
+ retry
173
+ end
174
+ raise err
175
+ end
176
+ end
177
+
178
+ def assert_shutdown(pid)
179
+ wait_master_ready("test_stderr.#{pid}.log")
180
+ Process.kill(:QUIT, pid)
181
+ pid, status = Process.waitpid2(pid)
182
+ assert status.success?, "exited successfully"
183
+ end
184
+
185
+ def wait_workers_ready(path, nr_workers)
186
+ tries = DEFAULT_TRIES
187
+ lines = []
188
+ while (tries -= 1) > 0
189
+ begin
190
+ lines = File.readlines(path).grep(/worker=\d+ ready/)
191
+ lines.size == nr_workers and return
192
+ rescue Errno::ENOENT
193
+ end
194
+ sleep DEFAULT_RES
195
+ end
196
+ raise "#{nr_workers} workers never became ready:" \
197
+ "\n\t#{lines.join("\n\t")}\n"
198
+ end
199
+
200
+ def wait_master_ready(master_log)
201
+ tries = DEFAULT_TRIES
202
+ while (tries -= 1) > 0
203
+ begin
204
+ File.readlines(master_log).grep(/master process ready/)[0] and return
205
+ rescue Errno::ENOENT
206
+ end
207
+ sleep DEFAULT_RES
208
+ end
209
+ raise "master process never became ready"
210
+ end
211
+
212
+ def reexec_usr2_quit_test(pid, pid_file)
213
+ assert File.exist?(pid_file), "pid file OK"
214
+ assert ! File.exist?("#{pid_file}.oldbin"), "oldbin pid file"
215
+ Process.kill(:USR2, pid)
216
+ retry_hit(["http://#{@addr}:#{@port}/"])
217
+ wait_for_file("#{pid_file}.oldbin")
218
+ wait_for_file(pid_file)
219
+
220
+ old_pid = File.read("#{pid_file}.oldbin").to_i
221
+ new_pid = File.read(pid_file).to_i
222
+
223
+ # kill old master process
224
+ assert_not_equal pid, new_pid
225
+ assert_equal pid, old_pid
226
+ Process.kill(:QUIT, old_pid)
227
+ retry_hit(["http://#{@addr}:#{@port}/"])
228
+ wait_for_death(old_pid)
229
+ assert_equal new_pid, File.read(pid_file).to_i
230
+ retry_hit(["http://#{@addr}:#{@port}/"])
231
+ Process.kill(:QUIT, new_pid)
232
+ end
233
+
234
+ def reexec_basic_test(pid, pid_file)
235
+ results = retry_hit(["http://#{@addr}:#{@port}/"])
236
+ assert_equal String, results[0].class
237
+ Process.kill(0, pid)
238
+ master_log = "#{@tmpdir}/test_stderr.#{pid}.log"
239
+ wait_master_ready(master_log)
240
+ File.truncate(master_log, 0)
241
+ nr = 50
242
+ kill_point = 2
243
+ nr.times do |i|
244
+ hit(["http://#{@addr}:#{@port}/#{i}"])
245
+ i == kill_point and Process.kill(:HUP, pid)
246
+ end
247
+ wait_master_ready(master_log)
248
+ assert File.exist?(pid_file), "pid=#{pid_file} exists"
249
+ new_pid = File.read(pid_file).to_i
250
+ assert_not_equal pid, new_pid
251
+ Process.kill(0, new_pid)
252
+ Process.kill(:QUIT, new_pid)
253
+ end
254
+
255
+ def wait_for_file(path)
256
+ tries = DEFAULT_TRIES
257
+ while (tries -= 1) > 0 && ! File.exist?(path)
258
+ sleep DEFAULT_RES
259
+ end
260
+ assert File.exist?(path), "path=#{path} exists #{caller.inspect}"
261
+ end
262
+
263
+ def xfork(&block)
264
+ fork do
265
+ ObjectSpace.each_object(Tempfile) do |tmp|
266
+ ObjectSpace.undefine_finalizer(tmp)
267
+ end
268
+ yield
269
+ end
270
+ end
271
+
272
+ # can't waitpid on detached processes
273
+ def wait_for_death(pid)
274
+ tries = DEFAULT_TRIES
275
+ while (tries -= 1) > 0
276
+ begin
277
+ Process.kill(0, pid)
278
+ begin
279
+ Process.waitpid(pid, Process::WNOHANG)
280
+ rescue Errno::ECHILD
281
+ end
282
+ sleep(DEFAULT_RES)
283
+ rescue Errno::ESRCH
284
+ return
285
+ end
286
+ end
287
+ raise "PID:#{pid} never died!"
288
+ end
289
+
290
+ def reset_sig_handlers
291
+ %w(WINCH QUIT INT TERM USR1 USR2 HUP TTIN TTOU CHLD).each do |sig|
292
+ trap(sig, "DEFAULT")
293
+ end
294
+ end
295
+
296
+ def tcp_socket(*args)
297
+ sock = TCPSocket.new(*args)
298
+ sock.nonblock = false
299
+ sock
300
+ end
301
+
302
+ def unix_socket(*args)
303
+ sock = UNIXSocket.new(*args)
304
+ sock.nonblock = false
305
+ sock
306
+ end
@@ -0,0 +1,91 @@
1
+ require 'socket'
2
+ require 'unicorn'
3
+ require 'io/wait'
4
+ require 'tempfile'
5
+ require 'test/unit'
6
+ require './test/test_helper'
7
+
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
28
+
29
+ # will wake up when writer closes
30
+ sleep_pipe[0].read if env['PATH_INFO'] == '/sleep'
31
+
32
+ [ 200, {'content-length'=>'0', '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
44
+
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
53
+
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")
57
+
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
@@ -0,0 +1,175 @@
1
+ # -*- encoding: binary -*-
2
+
3
+ require 'test/unit'
4
+ require 'tempfile'
5
+ require 'unicorn'
6
+
7
+ TestStruct = Struct.new(
8
+ *(Unicorn::Configurator::DEFAULTS.keys + %w(listener_opts listeners)))
9
+ class TestConfigurator < Test::Unit::TestCase
10
+
11
+ def test_config_init
12
+ Unicorn::Configurator.new {}
13
+ end
14
+
15
+ def test_expand_addr
16
+ meth = Unicorn::Configurator.new.method(:expand_addr)
17
+
18
+ assert_equal "/var/run/unicorn.sock", meth.call("/var/run/unicorn.sock")
19
+ assert_equal "#{Dir.pwd}/foo/bar.sock", meth.call("unix:foo/bar.sock")
20
+
21
+ path = meth.call("~/foo/bar.sock")
22
+ assert_equal "/", path[0..0]
23
+ assert_match %r{/foo/bar\.sock\z}, path
24
+
25
+ path = meth.call("~root/foo/bar.sock")
26
+ assert_equal "/", path[0..0]
27
+ assert_match %r{/foo/bar\.sock\z}, path
28
+
29
+ assert_equal "1.2.3.4:2007", meth.call('1.2.3.4:2007')
30
+ assert_equal "0.0.0.0:2007", meth.call('0.0.0.0:2007')
31
+ assert_equal "0.0.0.0:2007", meth.call(':2007')
32
+ assert_equal "0.0.0.0:2007", meth.call('*:2007')
33
+ assert_equal "0.0.0.0:2007", meth.call('2007')
34
+ assert_equal "0.0.0.0:2007", meth.call(2007)
35
+
36
+ %w([::1]:2007 [::]:2007).each do |addr|
37
+ assert_equal addr, meth.call(addr.dup)
38
+ end
39
+
40
+ # for Rainbows! users only
41
+ assert_equal "[::]:80", meth.call("[::]")
42
+ assert_equal "127.6.6.6:80", meth.call("127.6.6.6")
43
+
44
+ # the next two aren't portable, consider them unsupported for now
45
+ # assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('1:2007')
46
+ # assert_match %r{\A\d+\.\d+\.\d+\.\d+:2007\z}, meth.call('2:2007')
47
+ end
48
+
49
+ def test_config_invalid
50
+ tmp = Tempfile.new('unicorn_config')
51
+ tmp.syswrite(%q(asdfasdf "hello-world"))
52
+ assert_raises(NoMethodError) do
53
+ Unicorn::Configurator.new(:config_file => tmp.path)
54
+ end
55
+ end
56
+
57
+ def test_config_non_existent
58
+ tmp = Tempfile.new('unicorn_config')
59
+ path = tmp.path
60
+ tmp.close!
61
+ assert_raises(Errno::ENOENT) do
62
+ Unicorn::Configurator.new(:config_file => path)
63
+ end
64
+ end
65
+
66
+ def test_config_defaults
67
+ cfg = Unicorn::Configurator.new(:use_defaults => true)
68
+ test_struct = TestStruct.new
69
+ cfg.commit!(test_struct)
70
+ Unicorn::Configurator::DEFAULTS.each do |key,value|
71
+ assert_equal value, test_struct.__send__(key)
72
+ end
73
+ end
74
+
75
+ def test_config_defaults_skip
76
+ cfg = Unicorn::Configurator.new(:use_defaults => true)
77
+ skip = [ :logger ]
78
+ test_struct = TestStruct.new
79
+ cfg.commit!(test_struct, :skip => skip)
80
+ Unicorn::Configurator::DEFAULTS.each do |key,value|
81
+ next if skip.include?(key)
82
+ assert_equal value, test_struct.__send__(key)
83
+ end
84
+ assert_nil test_struct.logger
85
+ end
86
+
87
+ def test_listen_options
88
+ tmp = Tempfile.new('unicorn_config')
89
+ expect = { :sndbuf => 1, :rcvbuf => 2, :backlog => 10 }.freeze
90
+ listener = "127.0.0.1:12345"
91
+ tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
92
+ cfg = Unicorn::Configurator.new(:config_file => tmp.path)
93
+ test_struct = TestStruct.new
94
+ cfg.commit!(test_struct)
95
+ assert(listener_opts = test_struct.listener_opts)
96
+ assert_equal expect, listener_opts[listener]
97
+ end
98
+
99
+ def test_listen_option_bad
100
+ tmp = Tempfile.new('unicorn_config')
101
+ expect = { :sndbuf => "five" }
102
+ listener = "127.0.0.1:12345"
103
+ tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
104
+ assert_raises(ArgumentError) do
105
+ Unicorn::Configurator.new(:config_file => tmp.path)
106
+ end
107
+ end
108
+
109
+ def test_listen_option_bad_delay
110
+ tmp = Tempfile.new('unicorn_config')
111
+ expect = { :delay => "five" }
112
+ listener = "127.0.0.1:12345"
113
+ tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
114
+ assert_raises(ArgumentError) do
115
+ Unicorn::Configurator.new(:config_file => tmp.path)
116
+ end
117
+ end
118
+
119
+ def test_listen_option_float_delay
120
+ tmp = Tempfile.new('unicorn_config')
121
+ expect = { :delay => 0.5 }
122
+ listener = "127.0.0.1:12345"
123
+ tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
124
+ Unicorn::Configurator.new(:config_file => tmp.path)
125
+ end
126
+
127
+ def test_listen_option_int_delay
128
+ tmp = Tempfile.new('unicorn_config')
129
+ expect = { :delay => 5 }
130
+ listener = "127.0.0.1:12345"
131
+ tmp.syswrite("listen '#{listener}', #{expect.inspect}\n")
132
+ Unicorn::Configurator.new(:config_file => tmp.path)
133
+ end
134
+
135
+ def test_check_client_connection
136
+ tmp = Tempfile.new('unicorn_config')
137
+ test_struct = TestStruct.new
138
+ tmp.syswrite("check_client_connection true\n")
139
+
140
+ assert_nothing_raised do
141
+ Unicorn::Configurator.new(:config_file => tmp.path).commit!(test_struct)
142
+ end
143
+
144
+ assert test_struct.check_client_connection
145
+ end
146
+
147
+ def test_check_client_connection_with_tcp_bad
148
+ tmp = Tempfile.new('unicorn_config')
149
+ test_struct = TestStruct.new
150
+ listener = "127.0.0.1:12345"
151
+ tmp.syswrite("check_client_connection true\n")
152
+ tmp.syswrite("listen '#{listener}', :tcp_nopush => true\n")
153
+
154
+ assert_raises(ArgumentError) do
155
+ Unicorn::Configurator.new(:config_file => tmp.path).commit!(test_struct)
156
+ end
157
+ end
158
+
159
+ def test_after_fork_proc
160
+ test_struct = TestStruct.new
161
+ [ proc { |a,b| }, Proc.new { |a,b| }, lambda { |a,b| } ].each do |my_proc|
162
+ Unicorn::Configurator.new(:after_fork => my_proc).commit!(test_struct)
163
+ assert_equal my_proc, test_struct.after_fork
164
+ end
165
+ end
166
+
167
+ def test_after_fork_wrong_arity
168
+ [ proc { |a| }, Proc.new { }, lambda { |a,b,c| } ].each do |my_proc|
169
+ assert_raises(ArgumentError) do
170
+ Unicorn::Configurator.new(:after_fork => my_proc)
171
+ end
172
+ end
173
+ end
174
+
175
+ end
@@ -0,0 +1,28 @@
1
+ require 'test/unit'
2
+ require 'unicorn'
3
+
4
+ class TestDroplet < Test::Unit::TestCase
5
+ def test_create_many_droplets
6
+ now = Time.now.to_i
7
+ (0..1024).each do |i|
8
+ droplet = Unicorn::Worker.new(i)
9
+ assert droplet.respond_to?(:tick)
10
+ assert_equal 0, droplet.tick
11
+ assert_equal(now, droplet.tick = now)
12
+ assert_equal now, droplet.tick
13
+ assert_equal(0, droplet.tick = 0)
14
+ assert_equal 0, droplet.tick
15
+ end
16
+ end
17
+
18
+ def test_shared_process
19
+ droplet = Unicorn::Worker.new(0)
20
+ _, status = Process.waitpid2(fork { droplet.tick += 1; exit!(0) })
21
+ assert status.success?, status.inspect
22
+ assert_equal 1, droplet.tick
23
+
24
+ _, status = Process.waitpid2(fork { droplet.tick += 1; exit!(0) })
25
+ assert status.success?, status.inspect
26
+ assert_equal 2, droplet.tick
27
+ end
28
+ end