unicorn 4.8.0 → 6.1.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 (123) hide show
  1. checksums.yaml +7 -0
  2. data/.document +0 -1
  3. data/.gitattributes +5 -0
  4. data/.gitignore +2 -2
  5. data/.manifest +17 -21
  6. data/.olddoc.yml +25 -0
  7. data/Application_Timeouts +7 -7
  8. data/CONTRIBUTORS +6 -2
  9. data/DESIGN +2 -4
  10. data/Documentation/.gitignore +1 -3
  11. data/Documentation/unicorn.1 +222 -0
  12. data/Documentation/unicorn_rails.1 +207 -0
  13. data/FAQ +23 -6
  14. data/GIT-VERSION-FILE +1 -1
  15. data/GIT-VERSION-GEN +1 -1
  16. data/GNUmakefile +138 -92
  17. data/HACKING +10 -32
  18. data/ISSUES +80 -19
  19. data/KNOWN_ISSUES +18 -18
  20. data/LATEST +23 -27
  21. data/LICENSE +2 -2
  22. data/Links +24 -22
  23. data/NEWS +823 -0
  24. data/PHILOSOPHY +0 -6
  25. data/README +49 -48
  26. data/Rakefile +0 -44
  27. data/SIGNALS +2 -2
  28. data/Sandbox +11 -10
  29. data/TODO +0 -2
  30. data/TUNING +30 -9
  31. data/archive/.gitignore +3 -0
  32. data/archive/slrnpull.conf +4 -0
  33. data/bin/unicorn +4 -2
  34. data/bin/unicorn_rails +3 -3
  35. data/examples/big_app_gc.rb +1 -1
  36. data/examples/init.sh +36 -8
  37. data/examples/logrotate.conf +17 -2
  38. data/examples/nginx.conf +14 -14
  39. data/examples/unicorn.conf.minimal.rb +2 -2
  40. data/examples/unicorn.conf.rb +14 -6
  41. data/examples/unicorn.socket +11 -0
  42. data/examples/unicorn@.service +40 -0
  43. data/ext/unicorn_http/c_util.h +5 -13
  44. data/ext/unicorn_http/common_field_optimization.h +22 -5
  45. data/ext/unicorn_http/epollexclusive.h +124 -0
  46. data/ext/unicorn_http/ext_help.h +0 -44
  47. data/ext/unicorn_http/extconf.rb +32 -5
  48. data/ext/unicorn_http/global_variables.h +2 -2
  49. data/ext/unicorn_http/httpdate.c +3 -2
  50. data/ext/unicorn_http/unicorn_http.c +926 -638
  51. data/ext/unicorn_http/unicorn_http.rl +159 -170
  52. data/ext/unicorn_http/unicorn_http_common.rl +1 -1
  53. data/lib/unicorn/configurator.rb +110 -46
  54. data/lib/unicorn/const.rb +2 -25
  55. data/lib/unicorn/http_request.rb +110 -31
  56. data/lib/unicorn/http_response.rb +17 -31
  57. data/lib/unicorn/http_server.rb +272 -180
  58. data/lib/unicorn/launcher.rb +1 -1
  59. data/lib/unicorn/oob_gc.rb +16 -6
  60. data/lib/unicorn/select_waiter.rb +6 -0
  61. data/lib/unicorn/socket_helper.rb +58 -78
  62. data/lib/unicorn/stream_input.rb +8 -7
  63. data/lib/unicorn/tee_input.rb +16 -11
  64. data/lib/unicorn/tmpio.rb +10 -6
  65. data/lib/unicorn/util.rb +5 -4
  66. data/lib/unicorn/version.rb +1 -1
  67. data/lib/unicorn/worker.rb +36 -23
  68. data/lib/unicorn.rb +64 -44
  69. data/man/man1/unicorn.1 +124 -122
  70. data/man/man1/unicorn_rails.1 +113 -127
  71. data/t/.gitignore +0 -1
  72. data/t/GNUmakefile +3 -80
  73. data/t/README +4 -4
  74. data/t/t0002-parser-error.sh +3 -3
  75. data/t/t0011-active-unix-socket.sh +1 -1
  76. data/t/t0012-reload-empty-config.sh +2 -1
  77. data/t/t0301-no-default-middleware-ignored-in-config.sh +25 -0
  78. data/t/t0301.ru +13 -0
  79. data/t/test-lib.sh +4 -3
  80. data/test/benchmark/README +14 -4
  81. data/test/benchmark/ddstream.ru +50 -0
  82. data/test/benchmark/readinput.ru +40 -0
  83. data/test/benchmark/uconnect.perl +66 -0
  84. data/test/exec/test_exec.rb +74 -20
  85. data/test/test_helper.rb +42 -33
  86. data/test/unit/test_ccc.rb +91 -0
  87. data/test/unit/test_droplet.rb +1 -1
  88. data/test/unit/test_http_parser.rb +49 -19
  89. data/test/unit/test_http_parser_ng.rb +98 -115
  90. data/test/unit/test_request.rb +11 -11
  91. data/test/unit/test_response.rb +31 -19
  92. data/test/unit/test_server.rb +89 -15
  93. data/test/unit/test_signals.rb +9 -9
  94. data/test/unit/test_socket_helper.rb +16 -12
  95. data/test/unit/test_tee_input.rb +10 -0
  96. data/test/unit/test_upload.rb +10 -15
  97. data/test/unit/test_util.rb +32 -6
  98. data/test/unit/test_waiter.rb +34 -0
  99. data/unicorn.gemspec +28 -23
  100. data/unicorn_1 +1 -0
  101. data/unicorn_rails_1 +1 -0
  102. metadata +68 -134
  103. data/.wrongdoc.yml +0 -10
  104. data/ChangeLog +0 -4861
  105. data/Documentation/GNUmakefile +0 -30
  106. data/Documentation/unicorn.1.txt +0 -178
  107. data/Documentation/unicorn_rails.1.txt +0 -175
  108. data/examples/git.ru +0 -13
  109. data/lib/unicorn/app/exec_cgi.rb +0 -154
  110. data/lib/unicorn/app/inetd.rb +0 -109
  111. data/lib/unicorn/ssl_client.rb +0 -11
  112. data/lib/unicorn/ssl_configurator.rb +0 -104
  113. data/lib/unicorn/ssl_server.rb +0 -42
  114. data/local.mk.sample +0 -59
  115. data/script/isolate_for_tests +0 -32
  116. data/t/hijack.ru +0 -42
  117. data/t/sslgen.sh +0 -71
  118. data/t/t0016-trust-x-forwarded-false.sh +0 -30
  119. data/t/t0017-trust-x-forwarded-true.sh +0 -30
  120. data/t/t0200-rack-hijack.sh +0 -27
  121. data/t/t0600-https-server-basic.sh +0 -48
  122. data/test/unit/test_http_parser_xftrust.rb +0 -38
  123. data/test/unit/test_sni_hostnames.rb +0 -47
@@ -1,5 +1,4 @@
1
1
  # -*- encoding: binary -*-
2
- require "unicorn/ssl_server"
3
2
 
4
3
  # This is the process manager of Unicorn. This manages worker
5
4
  # processes which in turn handle the I/O and application process.
@@ -7,51 +6,30 @@ require "unicorn/ssl_server"
7
6
  # forked worker children.
8
7
  #
9
8
  # Users do not need to know the internals of this class, but reading the
10
- # {source}[http://bogomips.org/unicorn.git/tree/lib/unicorn/http_server.rb]
11
- # is education for programmers wishing to learn how \Unicorn works.
12
- # See Unicorn::Configurator for information on how to configure \Unicorn.
9
+ # {source}[https://yhbt.net/unicorn.git/tree/lib/unicorn/http_server.rb]
10
+ # is education for programmers wishing to learn how unicorn works.
11
+ # See Unicorn::Configurator for information on how to configure unicorn.
13
12
  class Unicorn::HttpServer
14
13
  # :stopdoc:
15
- attr_accessor :app, :request, :timeout, :worker_processes,
14
+ attr_accessor :app, :timeout, :worker_processes,
16
15
  :before_fork, :after_fork, :before_exec,
17
16
  :listener_opts, :preload_app,
18
- :reexec_pid, :orig_app, :init_listeners,
19
- :master_pid, :config, :ready_pipe, :user
17
+ :orig_app, :config, :ready_pipe, :user,
18
+ :default_middleware, :early_hints
19
+ attr_writer :after_worker_exit, :after_worker_ready, :worker_exec
20
20
 
21
21
  attr_reader :pid, :logger
22
22
  include Unicorn::SocketHelper
23
23
  include Unicorn::HttpResponse
24
- include Unicorn::SSLServer
25
-
26
- # backwards compatibility with 1.x
27
- Worker = Unicorn::Worker
28
24
 
29
25
  # all bound listener sockets
26
+ # note: this is public used by raindrops, but not recommended for use
27
+ # in new projects
30
28
  LISTENERS = []
31
29
 
32
30
  # listeners we have yet to bind
33
31
  NEW_LISTENERS = []
34
32
 
35
- # This hash maps PIDs to Workers
36
- WORKERS = {}
37
-
38
- # We use SELF_PIPE differently in the master and worker processes:
39
- #
40
- # * The master process never closes or reinitializes this once
41
- # initialized. Signal handlers in the master process will write to
42
- # it to wake up the master from IO.select in exactly the same manner
43
- # djb describes in http://cr.yp.to/docs/selfpipe.html
44
- #
45
- # * The workers immediately close the pipe they inherit. See the
46
- # Unicorn::Worker class for the pipe workers use.
47
- SELF_PIPE = []
48
-
49
- # signal queue used for self-piping
50
- SIG_QUEUE = []
51
-
52
- # list of signals we care about and trap in master.
53
- QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
54
-
55
33
  # :startdoc:
56
34
  # We populate this at startup so we can figure out how to reexecute
57
35
  # and upgrade the currently running instance of Unicorn
@@ -60,7 +38,7 @@ class Unicorn::HttpServer
60
38
  # or even different installations of the same applications without
61
39
  # downtime. Keys of this constant Hash are described as follows:
62
40
  #
63
- # * 0 - the path to the unicorn/unicorn_rails executable
41
+ # * 0 - the path to the unicorn executable
64
42
  # * :argv - a deep copy of the ARGV array the executable originally saw
65
43
  # * :cwd - the working directory of the application, this is where
66
44
  # you originally started Unicorn.
@@ -69,9 +47,9 @@ class Unicorn::HttpServer
69
47
  # you can set the following in your Unicorn config file, HUP and then
70
48
  # continue with the traditional USR2 + QUIT upgrade steps:
71
49
  #
72
- # Unicorn::HttpServer::START_CTX[0] = "/home/bofh/1.9.2/bin/unicorn"
50
+ # Unicorn::HttpServer::START_CTX[0] = "/home/bofh/2.3.0/bin/unicorn"
73
51
  START_CTX = {
74
- :argv => ARGV.map { |arg| arg.dup },
52
+ :argv => ARGV.map(&:dup),
75
53
  0 => $0.dup,
76
54
  }
77
55
  # We favor ENV['PWD'] since it is (usually) symlink aware for Capistrano
@@ -91,8 +69,8 @@ class Unicorn::HttpServer
91
69
  # incoming requests on the socket.
92
70
  def initialize(app, options = {})
93
71
  @app = app
94
- @request = Unicorn::HttpRequest.new
95
- self.reexec_pid = 0
72
+ @reexec_pid = 0
73
+ @default_middleware = true
96
74
  options = options.dup
97
75
  @ready_pipe = options.delete(:ready_pipe)
98
76
  @init_listeners = options[:listeners] ? options[:listeners].dup : []
@@ -100,6 +78,20 @@ class Unicorn::HttpServer
100
78
  self.config = Unicorn::Configurator.new(options)
101
79
  self.listener_opts = {}
102
80
 
81
+ # We use @self_pipe differently in the master and worker processes:
82
+ #
83
+ # * The master process never closes or reinitializes this once
84
+ # initialized. Signal handlers in the master process will write to
85
+ # it to wake up the master from IO.select in exactly the same manner
86
+ # djb describes in https://cr.yp.to/docs/selfpipe.html
87
+ #
88
+ # * The workers immediately close the pipe they inherit. See the
89
+ # Unicorn::Worker class for the pipe workers use.
90
+ @self_pipe = []
91
+ @workers = {} # hash maps PIDs to Workers
92
+ @sig_queue = [] # signal queue used for self-piping
93
+ @pid = nil
94
+
103
95
  # we try inheriting listeners first, so we bind them later.
104
96
  # we don't write the pid file until we've bound listeners in case
105
97
  # unicorn was started twice by mistake. Even though our #pid= method
@@ -111,7 +103,18 @@ class Unicorn::HttpServer
111
103
  # monitoring tools may also rely on pid files existing before we
112
104
  # attempt to connect to the listener(s)
113
105
  config.commit!(self, :skip => [:listeners, :pid])
114
- self.orig_app = app
106
+ @orig_app = app
107
+ # list of signals we care about and trap in master.
108
+ @queue_sigs = [
109
+ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
110
+
111
+ @worker_data = if worker_data = ENV['UNICORN_WORKER']
112
+ worker_data = worker_data.split(',').map!(&:to_i)
113
+ worker_data[1] = worker_data.slice!(1..2).map do |i|
114
+ Kgio::Pipe.for_fd(i)
115
+ end
116
+ worker_data
117
+ end
115
118
  end
116
119
 
117
120
  # Runs the thing. Returns self so you can run join on it
@@ -119,12 +122,13 @@ class Unicorn::HttpServer
119
122
  inherit_listeners!
120
123
  # this pipe is used to wake us up from select(2) in #join when signals
121
124
  # are trapped. See trap_deferred.
122
- SELF_PIPE.replace(Unicorn.pipe)
125
+ @self_pipe.replace(Unicorn.pipe)
126
+ @master_pid = @worker_data ? Process.ppid : $$
123
127
 
124
128
  # setup signal handlers before writing pid file in case people get
125
129
  # trigger happy and send signals as soon as the pid file exists.
126
130
  # Note that signals don't actually get handled until the #join method
127
- QUEUE_SIGS.each { |sig| trap(sig) { SIG_QUEUE << sig; awaken_master } }
131
+ @queue_sigs.each { |sig| trap(sig) { @sig_queue << sig; awaken_master } }
128
132
  trap(:CHLD) { awaken_master }
129
133
 
130
134
  # write pid early for Mongrel compatibility if we're not inheriting sockets
@@ -133,7 +137,6 @@ class Unicorn::HttpServer
133
137
  # we upgrade and the upgrade breaks during preload_app==true && build_app!
134
138
  self.pid = config[:pid]
135
139
 
136
- self.master_pid = $$
137
140
  build_app! if preload_app
138
141
  bind_new_listeners!
139
142
 
@@ -146,7 +149,7 @@ class Unicorn::HttpServer
146
149
  def listeners=(listeners)
147
150
  cur_names, dead_names = [], []
148
151
  listener_names.each do |name|
149
- if ?/ == name[0]
152
+ if name.start_with?('/')
150
153
  # mark unlinked sockets as dead so we can rebind them
151
154
  (File.socket?(name) ? cur_names : dead_names) << name
152
155
  else
@@ -158,9 +161,6 @@ class Unicorn::HttpServer
158
161
 
159
162
  LISTENERS.delete_if do |io|
160
163
  if dead_names.include?(sock_name(io))
161
- IO_PURGATORY.delete_if do |pio|
162
- pio.fileno == io.fileno && (pio.close rescue nil).nil? # true
163
- end
164
164
  (io.close rescue nil).nil? # true
165
165
  else
166
166
  set_server_sockopt(io, listener_opts[sock_name(io)])
@@ -198,7 +198,7 @@ class Unicorn::HttpServer
198
198
  if path
199
199
  if x = valid_pid?(path)
200
200
  return path if pid && path == pid && x == $$
201
- if x == reexec_pid && pid =~ /\.oldbin\z/
201
+ if x == @reexec_pid && pid.end_with?('.oldbin')
202
202
  logger.warn("will not set pid=#{path} while reexec-ed "\
203
203
  "child is running PID:#{x}")
204
204
  return
@@ -241,7 +241,7 @@ class Unicorn::HttpServer
241
241
  begin
242
242
  io = bind_listen(address, opt)
243
243
  unless Kgio::TCPServer === io || Kgio::UNIXServer === io
244
- prevent_autoclose(io)
244
+ io.autoclose = false
245
245
  io = server_cast(io)
246
246
  end
247
247
  logger.info "listening on addr=#{sock_name(io)} fd=#{io.fileno}"
@@ -267,21 +267,25 @@ class Unicorn::HttpServer
267
267
  # is signalling us too often.
268
268
  def join
269
269
  respawn = true
270
- last_check = Time.now
270
+ last_check = time_now
271
271
 
272
272
  proc_name 'master'
273
273
  logger.info "master process ready" # test_exec.rb relies on this message
274
274
  if @ready_pipe
275
- @ready_pipe.syswrite($$.to_s)
275
+ begin
276
+ @ready_pipe.syswrite($$.to_s)
277
+ rescue => e
278
+ logger.warn("grandparent died too soon?: #{e.message} (#{e.class})")
279
+ end
276
280
  @ready_pipe = @ready_pipe.close rescue nil
277
281
  end
278
282
  begin
279
283
  reap_all_workers
280
- case SIG_QUEUE.shift
284
+ case @sig_queue.shift
281
285
  when nil
282
286
  # avoid murdering workers after our master process (or the
283
287
  # machine) comes out of suspend/hibernation
284
- if (last_check + @timeout) >= (last_check = Time.now)
288
+ if (last_check + @timeout) >= (last_check = time_now)
285
289
  sleep_time = murder_lazy_workers
286
290
  else
287
291
  sleep_time = @timeout/2.0 + 1
@@ -302,13 +306,13 @@ class Unicorn::HttpServer
302
306
  when :USR2 # exec binary, stay alive in case something went wrong
303
307
  reexec
304
308
  when :WINCH
305
- if Unicorn::Configurator::RACKUP[:daemonized]
309
+ if $stdin.tty?
310
+ logger.info "SIGWINCH ignored because we're not daemonized"
311
+ else
306
312
  respawn = false
307
313
  logger.info "gracefully stopping all workers"
308
314
  soft_kill_each_worker(:QUIT)
309
315
  self.worker_processes = 0
310
- else
311
- logger.info "SIGWINCH ignored because we're not daemonized"
312
316
  end
313
317
  when :TTIN
314
318
  respawn = true
@@ -335,8 +339,8 @@ class Unicorn::HttpServer
335
339
  # Terminates all workers, but does not exit master process
336
340
  def stop(graceful = true)
337
341
  self.listeners = []
338
- limit = Time.now + timeout
339
- until WORKERS.empty? || Time.now > limit
342
+ limit = time_now + timeout
343
+ until @workers.empty? || time_now > limit
340
344
  if graceful
341
345
  soft_kill_each_worker(:QUIT)
342
346
  else
@@ -365,14 +369,6 @@ class Unicorn::HttpServer
365
369
  Unicorn::TeeInput.client_body_buffer_size = bytes
366
370
  end
367
371
 
368
- def trust_x_forwarded
369
- Unicorn::HttpParser.trust_x_forwarded?
370
- end
371
-
372
- def trust_x_forwarded=(bool)
373
- Unicorn::HttpParser.trust_x_forwarded = bool
374
- end
375
-
376
372
  def check_client_connection
377
373
  Unicorn::HttpRequest.check_client_connection
378
374
  end
@@ -385,12 +381,17 @@ class Unicorn::HttpServer
385
381
 
386
382
  # wait for a signal hander to wake us up and then consume the pipe
387
383
  def master_sleep(sec)
388
- IO.select([ SELF_PIPE[0] ], nil, nil, sec) or return
389
- SELF_PIPE[0].kgio_tryread(11)
384
+ @self_pipe[0].wait(sec) or return
385
+ # 11 bytes is the maximum string length which can be embedded within
386
+ # the Ruby itself and not require a separate malloc (on 32-bit MRI 1.9+).
387
+ # Most reads are only one byte here and uncommon, so it's not worth a
388
+ # persistent buffer, either:
389
+ @self_pipe[0].kgio_tryread(11)
390
390
  end
391
391
 
392
392
  def awaken_master
393
- SELF_PIPE[1].kgio_trywrite('.') # wakeup master process from select
393
+ return if $$ != @master_pid
394
+ @self_pipe[1].kgio_trywrite('.') # wakeup master process from select
394
395
  end
395
396
 
396
397
  # reaps all unreaped workers
@@ -398,15 +399,14 @@ class Unicorn::HttpServer
398
399
  begin
399
400
  wpid, status = Process.waitpid2(-1, Process::WNOHANG)
400
401
  wpid or return
401
- if reexec_pid == wpid
402
+ if @reexec_pid == wpid
402
403
  logger.error "reaped #{status.inspect} exec()-ed"
403
- self.reexec_pid = 0
404
+ @reexec_pid = 0
404
405
  self.pid = pid.chomp('.oldbin') if pid
405
406
  proc_name 'master'
406
407
  else
407
- worker = WORKERS.delete(wpid) and worker.close rescue nil
408
- m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}"
409
- status.success? ? logger.info(m) : logger.error(m)
408
+ worker = @workers.delete(wpid) and worker.close rescue nil
409
+ @after_worker_exit.call(self, worker, status)
410
410
  end
411
411
  rescue Errno::ECHILD
412
412
  break
@@ -415,13 +415,13 @@ class Unicorn::HttpServer
415
415
 
416
416
  # reexecutes the START_CTX with a new binary
417
417
  def reexec
418
- if reexec_pid > 0
418
+ if @reexec_pid > 0
419
419
  begin
420
- Process.kill(0, reexec_pid)
421
- logger.error "reexec-ed child already running PID:#{reexec_pid}"
420
+ Process.kill(0, @reexec_pid)
421
+ logger.error "reexec-ed child already running PID:#@reexec_pid"
422
422
  return
423
423
  rescue Errno::ESRCH
424
- self.reexec_pid = 0
424
+ @reexec_pid = 0
425
425
  end
426
426
  end
427
427
 
@@ -439,15 +439,8 @@ class Unicorn::HttpServer
439
439
  end
440
440
  end
441
441
 
442
- self.reexec_pid = fork do
443
- listener_fds = {}
444
- LISTENERS.each do |sock|
445
- # IO#close_on_exec= will be available on any future version of
446
- # Ruby that sets FD_CLOEXEC by default on new file descriptors
447
- # ref: http://redmine.ruby-lang.org/issues/5041
448
- sock.close_on_exec = false if sock.respond_to?(:close_on_exec=)
449
- listener_fds[sock.fileno] = sock
450
- end
442
+ @reexec_pid = fork do
443
+ listener_fds = listener_sockets
451
444
  ENV['UNICORN_FD'] = listener_fds.keys.join(',')
452
445
  Dir.chdir(START_CTX[:cwd])
453
446
  cmd = [ START_CTX[0] ].concat(START_CTX[:argv])
@@ -455,16 +448,11 @@ class Unicorn::HttpServer
455
448
  # avoid leaking FDs we don't know about, but let before_exec
456
449
  # unset FD_CLOEXEC, if anything else in the app eventually
457
450
  # relies on FD inheritence.
458
- (3..1024).each do |io|
459
- next if listener_fds.include?(io)
460
- io = IO.for_fd(io) rescue next
461
- prevent_autoclose(io)
462
- io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
463
- end
451
+ close_sockets_on_exec(listener_fds)
464
452
 
465
453
  # exec(command, hash) works in at least 1.9.1+, but will only be
466
454
  # required in 1.9.4/2.0.0 at earliest.
467
- cmd << listener_fds if RUBY_VERSION >= "1.9.1"
455
+ cmd << listener_fds
468
456
  logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
469
457
  before_exec.call(self)
470
458
  exec(*cmd)
@@ -472,11 +460,45 @@ class Unicorn::HttpServer
472
460
  proc_name 'master (old)'
473
461
  end
474
462
 
463
+ def worker_spawn(worker)
464
+ listener_fds = listener_sockets
465
+ env = {}
466
+ env['UNICORN_FD'] = listener_fds.keys.join(',')
467
+
468
+ listener_fds[worker.to_io.fileno] = worker.to_io
469
+ listener_fds[worker.master.fileno] = worker.master
470
+
471
+ worker_info = [worker.nr, worker.to_io.fileno, worker.master.fileno]
472
+ env['UNICORN_WORKER'] = worker_info.join(',')
473
+
474
+ close_sockets_on_exec(listener_fds)
475
+
476
+ Process.spawn(env, START_CTX[0], *START_CTX[:argv], listener_fds)
477
+ end
478
+
479
+ def listener_sockets
480
+ listener_fds = {}
481
+ LISTENERS.each do |sock|
482
+ sock.close_on_exec = false
483
+ listener_fds[sock.fileno] = sock
484
+ end
485
+ listener_fds
486
+ end
487
+
488
+ def close_sockets_on_exec(sockets)
489
+ (3..1024).each do |io|
490
+ next if sockets.include?(io)
491
+ io = IO.for_fd(io) rescue next
492
+ io.autoclose = false
493
+ io.close_on_exec = true
494
+ end
495
+ end
496
+
475
497
  # forcibly terminate all workers that haven't checked in in timeout seconds. The timeout is implemented using an unlinked File
476
498
  def murder_lazy_workers
477
499
  next_sleep = @timeout - 1
478
- now = Time.now.to_i
479
- WORKERS.dup.each_pair do |wpid, worker|
500
+ now = time_now.to_i
501
+ @workers.dup.each_pair do |wpid, worker|
480
502
  tick = worker.tick
481
503
  0 == tick and next # skip workers that haven't processed any clients
482
504
  diff = now - tick
@@ -494,42 +516,50 @@ class Unicorn::HttpServer
494
516
  end
495
517
 
496
518
  def after_fork_internal
497
- SELF_PIPE.each { |io| io.close }.clear # this is master-only, now
519
+ @self_pipe.each(&:close).clear # this is master-only, now
498
520
  @ready_pipe.close if @ready_pipe
499
521
  Unicorn::Configurator::RACKUP.clear
500
522
  @ready_pipe = @init_listeners = @before_exec = @before_fork = nil
501
523
 
502
- srand # http://redmine.ruby-lang.org/issues/4338
503
-
504
524
  # The OpenSSL PRNG is seeded with only the pid, and apps with frequently
505
525
  # dying workers can recycle pids
506
526
  OpenSSL::Random.seed(rand.to_s) if defined?(OpenSSL::Random)
507
527
  end
508
528
 
509
529
  def spawn_missing_workers
530
+ if @worker_data
531
+ worker = Unicorn::Worker.new(*@worker_data)
532
+ after_fork_internal
533
+ worker_loop(worker)
534
+ exit
535
+ end
536
+
510
537
  worker_nr = -1
511
538
  until (worker_nr += 1) == @worker_processes
512
- WORKERS.value?(worker_nr) and next
513
- worker = Worker.new(worker_nr)
539
+ @workers.value?(worker_nr) and next
540
+ worker = Unicorn::Worker.new(worker_nr)
514
541
  before_fork.call(self, worker)
515
- if pid = fork
516
- WORKERS[pid] = worker
517
- worker.atfork_parent
518
- else
542
+
543
+ pid = @worker_exec ? worker_spawn(worker) : fork
544
+
545
+ unless pid
519
546
  after_fork_internal
520
547
  worker_loop(worker)
521
548
  exit
522
549
  end
550
+
551
+ @workers[pid] = worker
552
+ worker.atfork_parent
523
553
  end
524
- rescue => e
525
- @logger.error(e) rescue nil
526
- exit!
554
+ rescue => e
555
+ @logger.error(e) rescue nil
556
+ exit!
527
557
  end
528
558
 
529
559
  def maintain_worker_count
530
- (off = WORKERS.size - worker_processes) == 0 and return
560
+ (off = @workers.size - worker_processes) == 0 and return
531
561
  off < 0 and return spawn_missing_workers
532
- WORKERS.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
562
+ @workers.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
533
563
  end
534
564
 
535
565
  # if we get any error, try to write something back to the client
@@ -554,42 +584,85 @@ class Unicorn::HttpServer
554
584
  client.kgio_trywrite(err_response(code, @request.response_start_sent))
555
585
  end
556
586
  client.close
557
- rescue
587
+ rescue
558
588
  end
559
589
 
560
- def expect_100_response
561
- if @request.response_start_sent
562
- Unicorn::Const::EXPECT_100_RESPONSE_SUFFIXED
590
+ def e103_response_write(client, headers)
591
+ response = if @request.response_start_sent
592
+ "103 Early Hints\r\n"
563
593
  else
564
- Unicorn::Const::EXPECT_100_RESPONSE
594
+ "HTTP/1.1 103 Early Hints\r\n"
595
+ end
596
+
597
+ headers.each_pair do |k, vs|
598
+ next if !vs || vs.empty?
599
+ values = vs.to_s.split("\n".freeze)
600
+ values.each do |v|
601
+ response << "#{k}: #{v}\r\n"
602
+ end
565
603
  end
604
+ response << "\r\n".freeze
605
+ response << "HTTP/1.1 ".freeze if @request.response_start_sent
606
+ client.write(response)
607
+ end
608
+
609
+ def e100_response_write(client, env)
610
+ # We use String#freeze to avoid allocations under Ruby 2.1+
611
+ # Not many users hit this code path, so it's better to reduce the
612
+ # constant table sizes even for Ruby 2.0 users who'll hit extra
613
+ # allocations here.
614
+ client.write(@request.response_start_sent ?
615
+ "100 Continue\r\n\r\nHTTP/1.1 ".freeze :
616
+ "HTTP/1.1 100 Continue\r\n\r\n".freeze)
617
+ env.delete('HTTP_EXPECT'.freeze)
566
618
  end
567
619
 
568
620
  # once a client is accepted, it is processed in its entirety here
569
621
  # in 3 easy steps: read request, call app, write app response
570
622
  def process_client(client)
571
- status, headers, body = @app.call(env = @request.read(client))
572
- return if @request.hijacked?
623
+ @request = Unicorn::HttpRequest.new
624
+ env = @request.read(client)
625
+
626
+ if early_hints
627
+ env["rack.early_hints"] = lambda do |headers|
628
+ e103_response_write(client, headers)
629
+ end
630
+ end
631
+
632
+ env["rack.after_reply"] = []
633
+
634
+ status, headers, body = @app.call(env)
573
635
 
574
- if 100 == status.to_i
575
- client.write(expect_100_response)
576
- env.delete(Unicorn::Const::HTTP_EXPECT)
577
- status, headers, body = @app.call(env)
636
+ begin
578
637
  return if @request.hijacked?
638
+
639
+ if 100 == status.to_i
640
+ e100_response_write(client, env)
641
+ status, headers, body = @app.call(env)
642
+ return if @request.hijacked?
643
+ end
644
+ @request.headers? or headers = nil
645
+ http_response_write(client, status, headers, body, @request)
646
+ ensure
647
+ body.respond_to?(:close) and body.close
579
648
  end
580
- @request.headers? or headers = nil
581
- http_response_write(client, status, headers, body,
582
- @request.response_start_sent)
649
+
583
650
  unless client.closed? # rack.hijack may've close this for us
584
651
  client.shutdown # in case of fork() in Rack app
585
652
  client.close # flush and uncork socket immediately, no keepalive
586
653
  end
587
654
  rescue => e
588
655
  handle_error(client, e)
656
+ ensure
657
+ env["rack.after_reply"].each(&:call) if env
589
658
  end
590
659
 
591
- EXIT_SIGS = [ :QUIT, :TERM, :INT ]
592
- WORKER_QUEUE_SIGS = QUEUE_SIGS - EXIT_SIGS
660
+ def nuke_listeners!(readers)
661
+ # only called from the worker, ordering is important here
662
+ tmp = readers.dup
663
+ readers.replace([false]) # ensure worker does not continue ASAP
664
+ tmp.each { |io| io.close rescue nil } # break out of IO.select
665
+ end
593
666
 
594
667
  # gets rid of stuff the worker has no business keeping track of
595
668
  # to free some resources and drops all sig handlers.
@@ -598,27 +671,26 @@ class Unicorn::HttpServer
598
671
  def init_worker_process(worker)
599
672
  worker.atfork_child
600
673
  # we'll re-trap :QUIT later for graceful shutdown iff we accept clients
601
- EXIT_SIGS.each { |sig| trap(sig) { exit!(0) } }
602
- exit!(0) if (SIG_QUEUE & EXIT_SIGS)[0]
603
- WORKER_QUEUE_SIGS.each { |sig| trap(sig, nil) }
674
+ exit_sigs = [ :QUIT, :TERM, :INT ]
675
+ exit_sigs.each { |sig| trap(sig) { exit!(0) } }
676
+ exit!(0) if (@sig_queue & exit_sigs)[0]
677
+ (@queue_sigs - exit_sigs).each { |sig| trap(sig, nil) }
604
678
  trap(:CHLD, 'DEFAULT')
605
- SIG_QUEUE.clear
679
+ @sig_queue.clear
606
680
  proc_name "worker[#{worker.nr}]"
607
681
  START_CTX.clear
608
- WORKERS.clear
682
+ @workers.clear
609
683
 
610
684
  after_fork.call(self, worker) # can drop perms and create listeners
611
- LISTENERS.each { |sock| sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
685
+ LISTENERS.each { |sock| sock.close_on_exec = true }
612
686
 
613
687
  worker.user(*user) if user.kind_of?(Array) && ! worker.switched
614
- self.timeout /= 2.0 # halve it for select()
615
688
  @config = nil
616
689
  build_app! unless preload_app
617
- ssl_enable!
618
690
  @after_fork = @listener_opts = @orig_app = nil
619
691
  readers = LISTENERS.dup
620
692
  readers << worker
621
- trap(:QUIT) { readers.each { |io| io.close }.replace([false]) }
693
+ trap(:QUIT) { nuke_listeners!(readers) }
622
694
  readers
623
695
  end
624
696
 
@@ -626,58 +698,55 @@ class Unicorn::HttpServer
626
698
  logger.info "worker=#{worker_nr} reopening logs..."
627
699
  Unicorn::Util.reopen_logs
628
700
  logger.info "worker=#{worker_nr} done reopening logs"
629
- rescue => e
630
- logger.error(e) rescue nil
631
- exit!(77) # EX_NOPERM in sysexits.h
701
+ false
702
+ rescue => e
703
+ logger.error(e) rescue nil
704
+ exit!(77) # EX_NOPERM in sysexits.h
705
+ end
706
+
707
+ def prep_readers(readers)
708
+ wtr = Unicorn::Waiter.prep_readers(readers)
709
+ @timeout *= 500 # to milliseconds for epoll, but halved
710
+ wtr
711
+ rescue
712
+ require_relative 'select_waiter'
713
+ @timeout /= 2.0 # halved for IO.select
714
+ Unicorn::SelectWaiter.new
632
715
  end
633
716
 
634
717
  # runs inside each forked worker, this sits around and waits
635
718
  # for connections and doesn't die until the parent dies (or is
636
719
  # given a INT, QUIT, or TERM signal)
637
720
  def worker_loop(worker)
638
- ppid = master_pid
639
721
  readers = init_worker_process(worker)
640
- nr = 0 # this becomes negative if we need to reopen logs
722
+ waiter = prep_readers(readers)
723
+ reopen = false
641
724
 
642
725
  # this only works immediately if the master sent us the signal
643
726
  # (which is the normal case)
644
- trap(:USR1) { nr = -65536 }
727
+ trap(:USR1) { reopen = true }
645
728
 
646
729
  ready = readers.dup
647
- @logger.info "worker=#{worker.nr} ready"
730
+ @after_worker_ready.call(self, worker)
648
731
 
649
732
  begin
650
- nr < 0 and reopen_worker_logs(worker.nr)
651
- nr = 0
652
- worker.tick = Time.now.to_i
653
- tmp = ready.dup
654
- while sock = tmp.shift
733
+ reopen = reopen_worker_logs(worker.nr) if reopen
734
+ worker.tick = time_now.to_i
735
+ while sock = ready.shift
655
736
  # Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
656
737
  # but that will return false
657
738
  if client = sock.kgio_tryaccept
658
739
  process_client(client)
659
- nr += 1
660
- worker.tick = Time.now.to_i
740
+ worker.tick = time_now.to_i
661
741
  end
662
- break if nr < 0
663
- end
664
-
665
- # make the following bet: if we accepted clients this round,
666
- # we're probably reasonably busy, so avoid calling select()
667
- # and do a speculative non-blocking accept() on ready listeners
668
- # before we sleep again in select().
669
- unless nr == 0
670
- tmp = ready.dup
671
- redo
742
+ break if reopen
672
743
  end
673
744
 
674
- ppid == Process.ppid or return
675
-
676
- # timeout used so we can detect parent death:
677
- worker.tick = Time.now.to_i
678
- ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
745
+ # timeout so we can .tick and keep parent from SIGKILL-ing us
746
+ worker.tick = time_now.to_i
747
+ waiter.get_readers(ready, readers, @timeout)
679
748
  rescue => e
680
- redo if nr < 0
749
+ redo if reopen && readers[0]
681
750
  Unicorn.log_error(@logger, "listen loop error", e) if readers[0]
682
751
  end while readers[0]
683
752
  end
@@ -686,17 +755,17 @@ class Unicorn::HttpServer
686
755
  # is no longer running.
687
756
  def kill_worker(signal, wpid)
688
757
  Process.kill(signal, wpid)
689
- rescue Errno::ESRCH
690
- worker = WORKERS.delete(wpid) and worker.close rescue nil
758
+ rescue Errno::ESRCH
759
+ worker = @workers.delete(wpid) and worker.close rescue nil
691
760
  end
692
761
 
693
762
  # delivers a signal to each worker
694
763
  def kill_each_worker(signal)
695
- WORKERS.keys.each { |wpid| kill_worker(signal, wpid) }
764
+ @workers.keys.each { |wpid| kill_worker(signal, wpid) }
696
765
  end
697
766
 
698
767
  def soft_kill_each_worker(signal)
699
- WORKERS.each_value { |worker| worker.soft_kill(signal) }
768
+ @workers.each_value { |worker| worker.soft_kill(signal) }
700
769
  end
701
770
 
702
771
  # unlinks a PID file at given +path+ if it contains the current PID
@@ -714,11 +783,11 @@ class Unicorn::HttpServer
714
783
  wpid <= 0 and return
715
784
  Process.kill(0, wpid)
716
785
  wpid
717
- rescue Errno::EPERM
718
- logger.info "pid=#{path} possibly stale, got EPERM signalling PID:#{wpid}"
719
- nil
720
- rescue Errno::ESRCH, Errno::ENOENT
721
- # don't unlink stale pid files, racy without non-portable locking...
786
+ rescue Errno::EPERM
787
+ logger.info "pid=#{path} possibly stale, got EPERM signalling PID:#{wpid}"
788
+ nil
789
+ rescue Errno::ESRCH, Errno::ENOENT
790
+ # don't unlink stale pid files, racy without non-portable locking...
722
791
  end
723
792
 
724
793
  def load_config!
@@ -729,7 +798,7 @@ class Unicorn::HttpServer
729
798
  config.commit!(self)
730
799
  soft_kill_each_worker(:QUIT)
731
800
  Unicorn::Util.reopen_logs
732
- self.app = orig_app
801
+ self.app = @orig_app
733
802
  build_app! if preload_app
734
803
  logger.info "done reloading config_file=#{config.config_file}"
735
804
  rescue StandardError, LoadError, SyntaxError => e
@@ -744,12 +813,12 @@ class Unicorn::HttpServer
744
813
  end
745
814
 
746
815
  def build_app!
747
- if app.respond_to?(:arity) && app.arity == 0
816
+ if app.respond_to?(:arity) && (app.arity == 0 || app.arity == 2)
748
817
  if defined?(Gem) && Gem.respond_to?(:refresh)
749
818
  logger.info "Refreshing Gem list"
750
819
  Gem.refresh
751
820
  end
752
- self.app = app.call
821
+ self.app = app.arity == 0 ? app.call : app.call(nil, self)
753
822
  end
754
823
  end
755
824
 
@@ -766,12 +835,23 @@ class Unicorn::HttpServer
766
835
  def inherit_listeners!
767
836
  # inherit sockets from parents, they need to be plain Socket objects
768
837
  # before they become Kgio::UNIXServer or Kgio::TCPServer
769
- inherited = ENV['UNICORN_FD'].to_s.split(/,/).map do |fd|
838
+ inherited = ENV['UNICORN_FD'].to_s.split(',')
839
+
840
+ # emulate sd_listen_fds() for systemd
841
+ sd_pid, sd_fds = ENV.values_at('LISTEN_PID', 'LISTEN_FDS')
842
+ if sd_pid.to_i == $$ # n.b. $$ can never be zero
843
+ # 3 = SD_LISTEN_FDS_START
844
+ inherited.concat((3...(3 + sd_fds.to_i)).to_a)
845
+ end
846
+ # to ease debugging, we will not unset LISTEN_PID and LISTEN_FDS
847
+
848
+ inherited.map! do |fd|
770
849
  io = Socket.for_fd(fd.to_i)
850
+ io.autoclose = false
851
+ io = server_cast(io)
771
852
  set_server_sockopt(io, listener_opts[sock_name(io)])
772
- prevent_autoclose(io)
773
- logger.info "inherited addr=#{sock_name(io)} fd=#{fd}"
774
- server_cast(io)
853
+ logger.info "inherited addr=#{sock_name(io)} fd=#{io.fileno}"
854
+ io
775
855
  end
776
856
 
777
857
  config_listeners = config[:listeners].dup
@@ -794,8 +874,20 @@ class Unicorn::HttpServer
794
874
  # call only after calling inherit_listeners!
795
875
  # This binds any listeners we did NOT inherit from the parent
796
876
  def bind_new_listeners!
797
- NEW_LISTENERS.each { |addr| listen(addr) }
877
+ NEW_LISTENERS.each { |addr| listen(addr) }.clear
798
878
  raise ArgumentError, "no listeners" if LISTENERS.empty?
799
- NEW_LISTENERS.clear
879
+ end
880
+
881
+ # try to use the monotonic clock in Ruby >= 2.1, it is immune to clock
882
+ # offset adjustments and generates less garbage (Float vs Time object)
883
+ begin
884
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
885
+ def time_now
886
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
887
+ end
888
+ rescue NameError, NoMethodError
889
+ def time_now # Ruby <= 2.0
890
+ Time.now
891
+ end
800
892
  end
801
893
  end