unicorn-shopify 4.8.2.5.25 → 5.2.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.olddoc.yml +6 -3
  3. data/Application_Timeouts +3 -3
  4. data/DESIGN +2 -4
  5. data/Documentation/unicorn.1.txt +10 -8
  6. data/Documentation/unicorn_rails.1.txt +5 -5
  7. data/FAQ +17 -8
  8. data/GIT-VERSION-GEN +1 -1
  9. data/GNUmakefile +4 -3
  10. data/HACKING +1 -1
  11. data/ISSUES +22 -32
  12. data/KNOWN_ISSUES +11 -11
  13. data/Links +16 -19
  14. data/PHILOSOPHY +0 -6
  15. data/README +26 -33
  16. data/SIGNALS +2 -2
  17. data/Sandbox +10 -9
  18. data/TODO +0 -2
  19. data/TUNING +24 -6
  20. data/examples/big_app_gc.rb +1 -1
  21. data/examples/init.sh +36 -8
  22. data/examples/logrotate.conf +16 -1
  23. data/examples/nginx.conf +11 -12
  24. data/examples/unicorn.conf.minimal.rb +2 -2
  25. data/examples/unicorn.conf.rb +3 -6
  26. data/ext/unicorn_http/common_field_optimization.h +2 -2
  27. data/ext/unicorn_http/ext_help.h +0 -20
  28. data/ext/unicorn_http/extconf.rb +2 -1
  29. data/ext/unicorn_http/global_variables.h +2 -2
  30. data/ext/unicorn_http/httpdate.c +2 -2
  31. data/ext/unicorn_http/unicorn_http.rl +73 -22
  32. data/ext/unicorn_http/unicorn_http_common.rl +1 -1
  33. data/lib/unicorn.rb +33 -22
  34. data/lib/unicorn/configurator.rb +64 -19
  35. data/lib/unicorn/http_request.rb +19 -36
  36. data/lib/unicorn/http_response.rb +15 -28
  37. data/lib/unicorn/http_server.rb +72 -56
  38. data/lib/unicorn/oob_gc.rb +4 -4
  39. data/lib/unicorn/socket_helper.rb +3 -5
  40. data/lib/unicorn/stream_input.rb +3 -3
  41. data/lib/unicorn/tee_input.rb +1 -3
  42. data/lib/unicorn/util.rb +1 -1
  43. data/lib/unicorn/worker.rb +12 -5
  44. data/t/hijack.ru +2 -1
  45. data/t/t0011-active-unix-socket.sh +1 -1
  46. data/t/t0012-reload-empty-config.sh +2 -1
  47. data/t/t0200-rack-hijack.sh +5 -2
  48. data/test/exec/test_exec.rb +52 -0
  49. data/test/test_helper.rb +3 -2
  50. data/test/unit/test_http_parser.rb +32 -0
  51. data/test/unit/test_http_parser_ng.rb +11 -0
  52. data/test/unit/test_response.rb +29 -11
  53. data/unicorn.gemspec +18 -9
  54. metadata +14 -15
@@ -4,7 +4,7 @@
4
4
 
5
5
  #### HTTP PROTOCOL GRAMMAR
6
6
  # line endings
7
- CRLF = "\r\n";
7
+   CRLF = ("\r\n" | "\n");
8
8
 
9
9
  # character types
10
10
  CTL = (cntrl | 127);
@@ -1,26 +1,33 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'etc'
3
3
  require 'stringio'
4
- require 'rack'
5
4
  require 'kgio'
6
5
 
6
+ begin
7
+ require 'rack'
8
+ rescue LoadError
9
+ warn 'rack not available, functionality reduced'
10
+ end
11
+
7
12
  # :stopdoc:
8
13
  # Unicorn module containing all of the classes (include C extensions) for
9
14
  # running a Unicorn web server. It contains a minimalist HTTP server with just
10
15
  # enough functionality to service web application requests fast as possible.
11
16
  # :startdoc:
12
17
 
13
- # \Unicorn exposes very little of an user-visible API and most of its
14
- # internals are subject to change. \Unicorn is designed to host Rack
18
+ # unicorn exposes very little of an user-visible API and most of its
19
+ # internals are subject to change. unicorn is designed to host Rack
15
20
  # applications, so applications should be written against the Rack SPEC
16
- # and not \Unicorn internals.
21
+ # and not unicorn internals.
17
22
  module Unicorn
18
23
 
19
24
  # Raised inside TeeInput when a client closes the socket inside the
20
25
  # application dispatch. This is always raised with an empty backtrace
21
26
  # since there is nothing in the application stack that is responsible
22
27
  # for client shutdowns/disconnects. This exception is visible to Rack
23
- # applications unless PrereadInput middleware is loaded.
28
+ # applications unless PrereadInput middleware is loaded. This
29
+ # is a subclass of the standard EOFError class and applications should
30
+ # not rescue it explicitly, but rescue EOFError instead.
24
31
  ClientShutdown = Class.new(EOFError)
25
32
 
26
33
  # :stopdoc:
@@ -32,6 +39,9 @@ module Unicorn
32
39
  def self.builder(ru, op)
33
40
  # allow Configurator to parse cli switches embedded in the ru file
34
41
  op = Unicorn::Configurator::RACKUP.merge!(:file => ru, :optparse => op)
42
+ if ru =~ /\.ru$/ && !defined?(Rack::Builder)
43
+ abort "rack and Rack::Builder must be available for processing #{ru}"
44
+ end
35
45
 
36
46
  # Op is going to get cleared before the returned lambda is called, so
37
47
  # save this value so that it's still there when we need it:
@@ -53,32 +63,33 @@ module Unicorn
53
63
 
54
64
  return inner_app if no_default_middleware
55
65
 
66
+ middleware = { # order matters
67
+ ContentLength: nil,
68
+ Chunked: nil,
69
+ CommonLogger: [ $stderr ],
70
+ ShowExceptions: nil,
71
+ Lint: nil,
72
+ TempfileReaper: nil,
73
+ }
74
+
56
75
  # return value, matches rackup defaults based on env
57
76
  # Unicorn does not support persistent connections, but Rainbows!
58
77
  # and Zbatery both do. Users accustomed to the Rack::Server default
59
78
  # middlewares will need ContentLength/Chunked middlewares.
60
79
  case ENV["RACK_ENV"]
61
80
  when "development"
62
- Rack::Builder.new do
63
- use Rack::ContentLength
64
- use Rack::Chunked
65
- use Rack::CommonLogger, $stderr
66
- use Rack::ShowExceptions
67
- use Rack::Lint
68
- use Rack::TempfileReaper if Rack.const_defined?(:TempfileReaper)
69
- run inner_app
70
- end.to_app
71
81
  when "deployment"
72
- Rack::Builder.new do
73
- use Rack::ContentLength
74
- use Rack::Chunked
75
- use Rack::CommonLogger, $stderr
76
- use Rack::TempfileReaper if Rack.const_defined?(:TempfileReaper)
77
- run inner_app
78
- end.to_app
82
+ middleware.delete(:ShowExceptions)
83
+ middleware.delete(:Lint)
79
84
  else
80
- inner_app
85
+ return inner_app
81
86
  end
87
+ Rack::Builder.new do
88
+ middleware.each do |m, args|
89
+ use(Rack.const_get(m), *args) if Rack.const_defined?(m)
90
+ end
91
+ run inner_app
92
+ end.to_app
82
93
  end
83
94
  end
84
95
 
@@ -1,13 +1,13 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'logger'
3
3
 
4
- # Implements a simple DSL for configuring a \Unicorn server.
4
+ # Implements a simple DSL for configuring a unicorn server.
5
5
  #
6
- # See http://unicorn.bogomips.org/examples/unicorn.conf.rb and
7
- # http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb
6
+ # See https://bogomips.org/unicorn/examples/unicorn.conf.rb and
7
+ # https://bogomips.org/unicorn/examples/unicorn.conf.minimal.rb
8
8
  # example configuration files. An example config file for use with
9
9
  # nginx is also available at
10
- # http://unicorn.bogomips.org/examples/nginx.conf
10
+ # https://bogomips.org/unicorn/examples/nginx.conf
11
11
  #
12
12
  # See the link:/TUNING.html document for more information on tuning unicorn.
13
13
  class Unicorn::Configurator
@@ -41,6 +41,17 @@ class Unicorn::Configurator
41
41
  :before_exec => lambda { |server|
42
42
  server.logger.info("forked child re-executing...")
43
43
  },
44
+ :after_worker_exit => lambda { |server, worker, status|
45
+ m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}"
46
+ if status.success?
47
+ server.logger.info(m)
48
+ else
49
+ server.logger.error(m)
50
+ end
51
+ },
52
+ :after_worker_ready => lambda { |server, worker|
53
+ server.logger.info("worker=#{worker.nr} ready")
54
+ },
44
55
  :before_murder => nil,
45
56
  :pid => nil,
46
57
  :preload_app => false,
@@ -152,6 +163,34 @@ class Unicorn::Configurator
152
163
  set_hook(:after_fork, block_given? ? block : args[0])
153
164
  end
154
165
 
166
+ # sets after_worker_exit hook to a given block. This block will be called
167
+ # by the master process after a worker exits:
168
+ #
169
+ # after_worker_exit do |server,worker,status|
170
+ # # status is a Process::Status instance for the exited worker process
171
+ # unless status.success?
172
+ # server.logger.error("worker process failure: #{status.inspect}")
173
+ # end
174
+ # end
175
+ def after_worker_exit(*args, &block)
176
+ set_hook(:after_worker_exit, block_given? ? block : args[0], 3)
177
+ end
178
+
179
+ # sets after_worker_ready hook to a given block. This block will be called
180
+ # by a worker process after it has been fully loaded, directly before it
181
+ # starts responding to requests:
182
+ #
183
+ # after_worker_ready do |server,worker|
184
+ # server.logger.info("worker #{worker.nr} ready, dropping privileges")
185
+ # worker.user('username', 'groupname')
186
+ # end
187
+ #
188
+ # Do not use Configurator#user if you rely on changing users in the
189
+ # after_worker_ready hook.
190
+ def after_worker_ready(*args, &block)
191
+ set_hook(:after_worker_ready, block_given? ? block : args[0])
192
+ end
193
+
155
194
  # sets before_fork got be a given Proc object. This Proc
156
195
  # object will be called by the master process before forking
157
196
  # each worker.
@@ -205,8 +244,6 @@ class Unicorn::Configurator
205
244
  # to have nginx always retry backends that may have had workers
206
245
  # SIGKILL-ed due to timeouts.
207
246
  #
208
- # # See http://wiki.nginx.org/NginxHttpUpstreamModule for more details
209
- # # on nginx upstream configuration:
210
247
  # upstream unicorn_backend {
211
248
  # # for UNIX domain socket setups:
212
249
  # server unix:/path/to/.unicorn.sock fail_timeout=0;
@@ -216,6 +253,9 @@ class Unicorn::Configurator
216
253
  # server 192.168.0.8:8080 fail_timeout=0;
217
254
  # server 192.168.0.9:8080 fail_timeout=0;
218
255
  # }
256
+ #
257
+ # See http://nginx.org/en/docs/http/ngx_http_upstream_module.html
258
+ # for more details on nginx upstream configuration.
219
259
  def timeout(seconds)
220
260
  set_int(:timeout, seconds, 3)
221
261
  # POSIX says 31 days is the smallest allowed maximum timeout for select()
@@ -278,6 +318,11 @@ class Unicorn::Configurator
278
318
  #
279
319
  # Default: 1024
280
320
  #
321
+ # Note: with the Linux kernel, the net.core.somaxconn sysctl defaults
322
+ # to 128, capping this value to 128. Raising the sysctl allows a
323
+ # larger backlog (which may not be desirable with multiple,
324
+ # load-balanced machines).
325
+ #
281
326
  # [:rcvbuf => bytes, :sndbuf => bytes]
282
327
  #
283
328
  # Maximum receive and send buffer sizes (in bytes) of sockets.
@@ -301,20 +346,19 @@ class Unicorn::Configurator
301
346
  # Setting this to +true+ can make streaming responses in Rails 3.1
302
347
  # appear more quickly at the cost of slightly higher bandwidth usage.
303
348
  # The effect of this option is most visible if nginx is not used,
304
- # but nginx remains highly recommended with \Unicorn.
349
+ # but nginx remains highly recommended with unicorn.
305
350
  #
306
351
  # This has no effect on UNIX sockets.
307
352
  #
308
- # Default: +true+ (Nagle's algorithm disabled) in \Unicorn,
309
- # +true+ in Rainbows! This defaulted to +false+ in \Unicorn
310
- # 3.x
353
+ # Default: +true+ (Nagle's algorithm disabled) in unicorn
354
+ # This defaulted to +false+ in unicorn 3.x
311
355
  #
312
356
  # [:tcp_nopush => true or false]
313
357
  #
314
358
  # Enables/disables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
315
359
  #
316
360
  # This prevents partial TCP frames from being sent out and reduces
317
- # wakeups in nginx if it is on a different machine. Since \Unicorn
361
+ # wakeups in nginx if it is on a different machine. Since unicorn
318
362
  # is only designed for applications that send the response body
319
363
  # quickly without keepalive, sockets will always be flushed on close
320
364
  # to prevent delays.
@@ -322,7 +366,7 @@ class Unicorn::Configurator
322
366
  # This has no effect on UNIX sockets.
323
367
  #
324
368
  # Default: +false+
325
- # This defaulted to +true+ in \Unicorn 3.4 - 3.7
369
+ # This defaulted to +true+ in unicorn 3.4 - 3.7
326
370
  #
327
371
  # [:ipv6only => true or false]
328
372
  #
@@ -406,12 +450,10 @@ class Unicorn::Configurator
406
450
  # and +false+ or +nil+ is synonymous for a value of zero.
407
451
  #
408
452
  # A value of +1+ is a good optimization for local networks
409
- # and trusted clients. For Rainbows! and Zbatery users, a higher
410
- # value (e.g. +60+) provides more protection against some
411
- # denial-of-service attacks. There is no good reason to ever
412
- # disable this with a +zero+ value when serving HTTP.
453
+ # and trusted clients. There is no good reason to ever
454
+ # disable this with a +zero+ value with unicorn.
413
455
  #
414
- # Default: 1 retransmit for \Unicorn, 60 for Rainbows! 0.95.0\+
456
+ # Default: 1
415
457
  #
416
458
  # [:accept_filter => String]
417
459
  #
@@ -420,8 +462,7 @@ class Unicorn::Configurator
420
462
  # This enables either the "dataready" or (default) "httpready"
421
463
  # accept() filter under FreeBSD. This is intended as an
422
464
  # optimization to reduce context switches with common GET/HEAD
423
- # requests. For Rainbows! and Zbatery users, this provides
424
- # some protection against certain denial-of-service attacks, too.
465
+ # requests.
425
466
  #
426
467
  # There is no good reason to change from the default.
427
468
  #
@@ -570,6 +611,10 @@ class Unicorn::Configurator
570
611
  # This switch will occur after calling the after_fork hook, and only
571
612
  # if the Worker#user method is not called in the after_fork hook
572
613
  # +group+ is optional and will not change if unspecified.
614
+ #
615
+ # Do not use Configurator#user if you rely on changing users in the
616
+ # after_worker_ready hook. Instead, you need to call Worker#user
617
+ # directly in after_worker_ready.
573
618
  def user(user, group = nil)
574
619
  # raises ArgumentError on invalid user/group
575
620
  Etc.getpwnam(user)
@@ -13,7 +13,8 @@ class Unicorn::HttpParser
13
13
  "rack.multiprocess" => true,
14
14
  "rack.multithread" => false,
15
15
  "rack.run_once" => false,
16
- "rack.version" => [1, 1],
16
+ "rack.version" => [1, 2],
17
+ "rack.hijack?" => true,
17
18
  "SCRIPT_NAME" => "",
18
19
 
19
20
  # this is not in the Rack spec, but some apps may rely on it
@@ -22,15 +23,8 @@ class Unicorn::HttpParser
22
23
 
23
24
  NULL_IO = StringIO.new("")
24
25
 
25
- attr_accessor :response_start_sent
26
-
27
26
  # :stopdoc:
28
- # A frozen format for this is about 15% faster
29
- # Drop these frozen strings when Ruby 2.2 becomes more prevalent,
30
- # 2.2+ optimizes hash assignments when used with literal string keys
31
- REMOTE_ADDR = 'REMOTE_ADDR'.freeze
32
- RACK_INPUT = 'rack.input'.freeze
33
- HTTP_RESPONSE_START = [ 'HTTP', '/1.1 ']
27
+ HTTP_RESPONSE_START = [ 'HTTP'.freeze, '/1.1 '.freeze ]
34
28
  @@input_class = Unicorn::TeeInput
35
29
  @@check_client_connection = false
36
30
 
@@ -76,7 +70,7 @@ class Unicorn::HttpParser
76
70
  # identify the client for the immediate request to the server;
77
71
  # that client may be a proxy, gateway, or other intermediary
78
72
  # acting on behalf of the actual source client."
79
- e[REMOTE_ADDR] = socket.kgio_addr
73
+ e['REMOTE_ADDR'] = socket.kgio_addr
80
74
 
81
75
  # short circuit the common case with small GET requests first
82
76
  socket.kgio_read!(16384, buf)
@@ -88,38 +82,27 @@ class Unicorn::HttpParser
88
82
 
89
83
  # detect if the socket is valid by writing a partial response:
90
84
  if @@check_client_connection && headers?
91
- @response_start_sent = true
85
+ self.response_start_sent = true
92
86
  HTTP_RESPONSE_START.each { |c| socket.write(c) }
93
87
  end
94
88
 
95
- e[RACK_INPUT] = 0 == content_length ?
96
- NULL_IO : @@input_class.new(socket, self)
97
- hijack_setup(e, socket)
98
- e.merge!(DEFAULTS)
99
- end
100
-
101
- # Rack 1.5.0 (protocol version 1.2) adds hijack request support
102
- if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
103
- DEFAULTS["rack.hijack?"] = true
104
- DEFAULTS["rack.version"] = [1, 2]
89
+ e['rack.input'] = 0 == content_length ?
90
+ NULL_IO : @@input_class.new(socket, self)
105
91
 
106
- RACK_HIJACK = "rack.hijack".freeze
107
- RACK_HIJACK_IO = "rack.hijack_io".freeze
92
+ # for Rack hijacking in Rack 1.5 and later
93
+ e['unicorn.socket'] = socket
94
+ e['rack.hijack'] = self
108
95
 
109
- def hijacked?
110
- env.include?(RACK_HIJACK_IO)
111
- end
96
+ e.merge!(DEFAULTS)
97
+ end
112
98
 
113
- def hijack_setup(e, socket)
114
- e[RACK_HIJACK] = proc { e[RACK_HIJACK_IO] = socket }
115
- end
116
- else
117
- # old Rack, do nothing.
118
- def hijack_setup(e, _)
119
- end
99
+ # for rack.hijack, we respond to this method so no extra allocation
100
+ # of a proc object
101
+ def call
102
+ env['rack.hijack_io'] = env['unicorn.socket']
103
+ end
120
104
 
121
- def hijacked?
122
- false
123
- end
105
+ def hijacked?
106
+ env.include?('rack.hijack_io'.freeze)
124
107
  end
125
108
  end
@@ -10,15 +10,13 @@
10
10
  # is the job of Rack, with the exception of the "Date" and "Status" header.
11
11
  module Unicorn::HttpResponse
12
12
 
13
- # Every standard HTTP code mapped to the appropriate message.
14
- CODES = Rack::Utils::HTTP_STATUS_CODES.inject({}) { |hash,(code,msg)|
15
- hash[code] = "#{code} #{msg}"
16
- hash
17
- }
18
- CRLF = "\r\n"
13
+ STATUS_CODES = defined?(Rack::Utils::HTTP_STATUS_CODES) ?
14
+ Rack::Utils::HTTP_STATUS_CODES : {}
19
15
 
16
+ # internal API, code will always be common-enough-for-even-old-Rack
20
17
  def err_response(code, response_start_sent)
21
- "#{response_start_sent ? '' : 'HTTP/1.1 '}#{CODES[code]}\r\n\r\n"
18
+ "#{response_start_sent ? '' : 'HTTP/1.1 '}" \
19
+ "#{code} #{STATUS_CODES[code]}\r\n\r\n"
22
20
  end
23
21
 
24
22
  # writes the rack_response to socket as an HTTP response
@@ -26,48 +24,37 @@ module Unicorn::HttpResponse
26
24
  response_start_sent=false)
27
25
  hijack = nil
28
26
 
29
- http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
30
27
  if headers
31
- buf = "#{http_response_start}#{CODES[status.to_i] || status}\r\n" \
28
+ code = status.to_i
29
+ msg = STATUS_CODES[code]
30
+ start = response_start_sent ? ''.freeze : 'HTTP/1.1 '.freeze
31
+ buf = "#{start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \
32
32
  "Date: #{httpdate}\r\n" \
33
33
  "Connection: close\r\n"
34
34
  headers.each do |key, value|
35
35
  case key
36
- when %r{\A(?:Date\z|Connection\z)}i
36
+ when %r{\A(?:Date|Connection)\z}i
37
37
  next
38
38
  when "rack.hijack"
39
- # this was an illegal key in Rack < 1.5, so it should be
40
- # OK to silently discard it for those older versions
41
- hijack = hijack_prepare(value)
39
+ # This should only be hit under Rack >= 1.5, as this was an illegal
40
+ # key in Rack < 1.5
41
+ hijack = value
42
42
  else
43
43
  if value =~ /\n/
44
44
  # avoiding blank, key-only cookies with /\n+/
45
- buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
45
+ value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
46
46
  else
47
47
  buf << "#{key}: #{value}\r\n"
48
48
  end
49
49
  end
50
50
  end
51
- socket.write(buf << CRLF)
51
+ socket.write(buf << "\r\n".freeze)
52
52
  end
53
53
 
54
54
  if hijack
55
- body = nil # ensure we do not close body
56
55
  hijack.call(socket)
57
56
  else
58
57
  body.each { |chunk| socket.write(chunk) }
59
58
  end
60
- ensure
61
- body.respond_to?(:close) and body.close
62
- end
63
-
64
- # Rack 1.5.0 (protocol version 1.2) adds response hijacking support
65
- if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
66
- def hijack_prepare(value)
67
- value
68
- end
69
- else
70
- def hijack_prepare(_)
71
- end
72
59
  end
73
60
  end
@@ -7,16 +7,17 @@ require 'set'
7
7
  # forked worker children.
8
8
  #
9
9
  # 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.
10
+ # {source}[https://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.
13
13
  class Unicorn::HttpServer
14
14
  # :stopdoc:
15
- attr_accessor :app, :request, :timeout, :worker_processes,
15
+ attr_accessor :app, :timeout, :worker_processes,
16
16
  :before_fork, :after_fork, :before_exec,
17
17
  :listener_opts, :preload_app,
18
- :reexec_pid, :orig_app, :init_listeners,
19
- :master_pid, :config, :ready_pipe, :user,
18
+ :orig_app, :config, :ready_pipe, :user
19
+
20
+ attr_writer :after_worker_exit, :after_worker_ready
20
21
  :before_murder
21
22
 
22
23
  attr_reader :pid, :logger
@@ -24,14 +25,13 @@ class Unicorn::HttpServer
24
25
  include Unicorn::HttpResponse
25
26
 
26
27
  # all bound listener sockets
28
+ # note: this is public used by raindrops, but not recommended for use
29
+ # in new projects
27
30
  LISTENERS = []
28
31
 
29
32
  # listeners we have yet to bind
30
33
  NEW_LISTENERS = []
31
34
 
32
- # list of signals we care about and trap in master.
33
- QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
34
-
35
35
  # :startdoc:
36
36
  # We populate this at startup so we can figure out how to reexecute
37
37
  # and upgrade the currently running instance of Unicorn
@@ -40,7 +40,7 @@ class Unicorn::HttpServer
40
40
  # or even different installations of the same applications without
41
41
  # downtime. Keys of this constant Hash are described as follows:
42
42
  #
43
- # * 0 - the path to the unicorn/unicorn_rails executable
43
+ # * 0 - the path to the unicorn executable
44
44
  # * :argv - a deep copy of the ARGV array the executable originally saw
45
45
  # * :cwd - the working directory of the application, this is where
46
46
  # you originally started Unicorn.
@@ -49,7 +49,7 @@ class Unicorn::HttpServer
49
49
  # you can set the following in your Unicorn config file, HUP and then
50
50
  # continue with the traditional USR2 + QUIT upgrade steps:
51
51
  #
52
- # Unicorn::HttpServer::START_CTX[0] = "/home/bofh/2.2.0/bin/unicorn"
52
+ # Unicorn::HttpServer::START_CTX[0] = "/home/bofh/2.3.0/bin/unicorn"
53
53
  START_CTX = {
54
54
  :argv => ARGV.map(&:dup),
55
55
  0 => $0.dup,
@@ -72,7 +72,7 @@ class Unicorn::HttpServer
72
72
  def initialize(app, options = {})
73
73
  @app = app
74
74
  @request = Unicorn::HttpRequest.new
75
- self.reexec_pid = 0
75
+ @reexec_pid = 0
76
76
  options = options.dup
77
77
  @ready_pipe = options.delete(:ready_pipe)
78
78
  @init_listeners = options[:listeners] ? options[:listeners].dup : []
@@ -105,7 +105,10 @@ class Unicorn::HttpServer
105
105
  # monitoring tools may also rely on pid files existing before we
106
106
  # attempt to connect to the listener(s)
107
107
  config.commit!(self, :skip => [:listeners, :pid])
108
- self.orig_app = app
108
+ @orig_app = app
109
+ # list of signals we care about and trap in master.
110
+ @queue_sigs = [
111
+ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
109
112
  end
110
113
 
111
114
  # Runs the thing. Returns self so you can run join on it
@@ -119,7 +122,7 @@ class Unicorn::HttpServer
119
122
  # setup signal handlers before writing pid file in case people get
120
123
  # trigger happy and send signals as soon as the pid file exists.
121
124
  # Note that signals don't actually get handled until the #join method
122
- QUEUE_SIGS.each { |sig| trap(sig) { @sig_queue << sig; awaken_master } }
125
+ @queue_sigs.each { |sig| trap(sig) { @sig_queue << sig; awaken_master } }
123
126
  trap(:CHLD) { awaken_master }
124
127
 
125
128
  # write pid early for Mongrel compatibility if we're not inheriting sockets
@@ -189,7 +192,7 @@ class Unicorn::HttpServer
189
192
  if path
190
193
  if x = valid_pid?(path)
191
194
  return path if pid && path == pid && x == $$
192
- if x == reexec_pid && pid.end_with?('.oldbin')
195
+ if x == @reexec_pid && pid.end_with?('.oldbin')
193
196
  logger.warn("will not set pid=#{path} while reexec-ed "\
194
197
  "child is running PID:#{x}")
195
198
  return
@@ -297,13 +300,13 @@ class Unicorn::HttpServer
297
300
  when :USR2 # exec binary, stay alive in case something went wrong
298
301
  reexec
299
302
  when :WINCH
300
- if Unicorn::Configurator::RACKUP[:daemonized]
303
+ if $stdin.tty?
304
+ logger.info "SIGWINCH ignored because we're not daemonized"
305
+ else
301
306
  respawn = false
302
307
  logger.info "gracefully stopping all workers"
303
308
  soft_kill_each_worker(:QUIT)
304
309
  self.worker_processes = 0
305
- else
306
- logger.info "SIGWINCH ignored because we're not daemonized"
307
310
  end
308
311
  when :TTIN
309
312
  respawn = true
@@ -390,16 +393,15 @@ class Unicorn::HttpServer
390
393
  begin
391
394
  wpid, status = Process.waitpid2(-1, Process::WNOHANG)
392
395
  wpid or return
393
- if reexec_pid == wpid
396
+ if @reexec_pid == wpid
394
397
  logger.error "reaped #{status.inspect} exec()-ed"
395
- self.reexec_pid = 0
398
+ @reexec_pid = 0
396
399
  self.pid = pid.chomp('.oldbin') if pid
397
400
  proc_name 'master'
398
401
  else
399
402
  @before_murder_called.delete(wpid)
400
403
  worker = @workers.delete(wpid) and worker.close rescue nil
401
- m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}"
402
- status.success? ? logger.info(m) : logger.error(m)
404
+ @after_worker_exit.call(self, worker, status)
403
405
  end
404
406
  rescue Errno::ECHILD
405
407
  break
@@ -408,13 +410,13 @@ class Unicorn::HttpServer
408
410
 
409
411
  # reexecutes the START_CTX with a new binary
410
412
  def reexec
411
- if reexec_pid > 0
413
+ if @reexec_pid > 0
412
414
  begin
413
- Process.kill(0, reexec_pid)
414
- logger.error "reexec-ed child already running PID:#{reexec_pid}"
415
+ Process.kill(0, @reexec_pid)
416
+ logger.error "reexec-ed child already running PID:#@reexec_pid"
415
417
  return
416
418
  rescue Errno::ESRCH
417
- self.reexec_pid = 0
419
+ @reexec_pid = 0
418
420
  end
419
421
  end
420
422
 
@@ -432,7 +434,7 @@ class Unicorn::HttpServer
432
434
  end
433
435
  end
434
436
 
435
- self.reexec_pid = fork do
437
+ @reexec_pid = fork do
436
438
  listener_fds = {}
437
439
  LISTENERS.each do |sock|
438
440
  sock.close_on_exec = false
@@ -491,7 +493,8 @@ class Unicorn::HttpServer
491
493
  Unicorn::Configurator::RACKUP.clear
492
494
  @ready_pipe = @init_listeners = @before_exec = @before_fork = nil
493
495
 
494
- srand # http://redmine.ruby-lang.org/issues/4338
496
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/36450
497
+ srand # remove in unicorn 6
495
498
 
496
499
  # The OpenSSL PRNG is seeded with only the pid, and apps with frequently
497
500
  # dying workers can recycle pids
@@ -528,10 +531,9 @@ class Unicorn::HttpServer
528
531
  # assuming we haven't closed the socket, but don't get hung up
529
532
  # if the socket is already closed or broken. We'll always ensure
530
533
  # the socket is closed at the end of this function
531
- def handle_error(client, exception, env)
532
- code = case exception
534
+ def handle_error(client, e)
535
+ code = case e
533
536
  when EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::ENOTCONN
534
- Unicorn.log_error(@logger, "client disconnected", exception)
535
537
  # client disconnected on us and there's nothing we can do
536
538
  when Unicorn::RequestURITooLongError
537
539
  414
@@ -540,7 +542,7 @@ class Unicorn::HttpServer
540
542
  when Unicorn::HttpParserError # try to tell the client they're bad
541
543
  400
542
544
  else
543
- Unicorn.log_error(@logger, "app error", exception)
545
+ Unicorn.log_error(@logger, "app error", e)
544
546
  500
545
547
  end
546
548
  if code
@@ -565,27 +567,30 @@ class Unicorn::HttpServer
565
567
  # in 3 easy steps: read request, call app, write app response
566
568
  def process_client(client)
567
569
  status, headers, body = @app.call(env = @request.read(client))
568
- return if @request.hijacked?
569
570
 
570
- if 100 == status.to_i
571
- e100_response_write(client, env)
572
- status, headers, body = @app.call(env)
571
+ begin
573
572
  return if @request.hijacked?
573
+
574
+ if 100 == status.to_i
575
+ e100_response_write(client, env)
576
+ status, headers, body = @app.call(env)
577
+ return if @request.hijacked?
578
+ end
579
+ @request.headers? or headers = nil
580
+ http_response_write(client, status, headers, body,
581
+ @request.response_start_sent)
582
+ ensure
583
+ body.respond_to?(:close) and body.close
574
584
  end
575
- @request.headers? or headers = nil
576
- http_response_write(client, status, headers, body,
577
- @request.response_start_sent)
585
+
578
586
  unless client.closed? # rack.hijack may've close this for us
579
587
  client.shutdown # in case of fork() in Rack app
580
588
  client.close # flush and uncork socket immediately, no keepalive
581
589
  end
582
- rescue => exception
583
- handle_error(client, exception, env)
590
+ rescue => e
591
+ handle_error(client, e)
584
592
  end
585
593
 
586
- EXIT_SIGS = [ :QUIT, :TERM, :INT ]
587
- WORKER_QUEUE_SIGS = QUEUE_SIGS - EXIT_SIGS
588
-
589
594
  def nuke_listeners!(readers)
590
595
  # only called from the worker, ordering is important here
591
596
  tmp = readers.dup
@@ -600,9 +605,10 @@ class Unicorn::HttpServer
600
605
  def init_worker_process(worker)
601
606
  worker.atfork_child
602
607
  # we'll re-trap :QUIT later for graceful shutdown iff we accept clients
603
- EXIT_SIGS.each { |sig| trap(sig) { exit!(0) } }
604
- exit!(0) if (@sig_queue & EXIT_SIGS)[0]
605
- WORKER_QUEUE_SIGS.each { |sig| trap(sig, nil) }
608
+ exit_sigs = [ :QUIT, :TERM, :INT ]
609
+ exit_sigs.each { |sig| trap(sig) { exit!(0) } }
610
+ exit!(0) if (@sig_queue & exit_sigs)[0]
611
+ (@queue_sigs - exit_sigs).each { |sig| trap(sig, nil) }
606
612
  trap(:CHLD, 'DEFAULT')
607
613
  @sig_queue.clear
608
614
  proc_name "worker[#{worker.nr}]"
@@ -636,7 +642,7 @@ class Unicorn::HttpServer
636
642
  # for connections and doesn't die until the parent dies (or is
637
643
  # given a INT, QUIT, or TERM signal)
638
644
  def worker_loop(worker)
639
- ppid = master_pid
645
+ ppid = @master_pid
640
646
  readers = init_worker_process(worker)
641
647
  nr = 0 # this becomes negative if we need to reopen logs
642
648
 
@@ -645,7 +651,7 @@ class Unicorn::HttpServer
645
651
  trap(:USR1) { nr = -65536 }
646
652
 
647
653
  ready = readers.dup
648
- @logger.info "worker=#{worker.nr} ready"
654
+ @after_worker_ready.call(self, worker)
649
655
 
650
656
  begin
651
657
  nr < 0 and reopen_worker_logs(worker.nr)
@@ -731,7 +737,7 @@ class Unicorn::HttpServer
731
737
  config.commit!(self)
732
738
  soft_kill_each_worker(:QUIT)
733
739
  Unicorn::Util.reopen_logs
734
- self.app = orig_app
740
+ self.app = @orig_app
735
741
  build_app! if preload_app
736
742
  logger.info "done reloading config_file=#{config.config_file}"
737
743
  rescue StandardError, LoadError, SyntaxError => e
@@ -768,12 +774,23 @@ class Unicorn::HttpServer
768
774
  def inherit_listeners!
769
775
  # inherit sockets from parents, they need to be plain Socket objects
770
776
  # before they become Kgio::UNIXServer or Kgio::TCPServer
771
- inherited = ENV['UNICORN_FD'].to_s.split(',').map do |fd|
777
+ inherited = ENV['UNICORN_FD'].to_s.split(',')
778
+
779
+ # emulate sd_listen_fds() for systemd
780
+ sd_pid, sd_fds = ENV.values_at('LISTEN_PID', 'LISTEN_FDS')
781
+ if sd_pid.to_i == $$ # n.b. $$ can never be zero
782
+ # 3 = SD_LISTEN_FDS_START
783
+ inherited.concat((3...(3 + sd_fds.to_i)).to_a)
784
+ end
785
+ # to ease debugging, we will not unset LISTEN_PID and LISTEN_FDS
786
+
787
+ inherited.map! do |fd|
772
788
  io = Socket.for_fd(fd.to_i)
773
- set_server_sockopt(io, listener_opts[sock_name(io)])
774
789
  io.autoclose = false
775
- logger.info "inherited addr=#{sock_name(io)} fd=#{fd}"
776
- server_cast(io)
790
+ io = server_cast(io)
791
+ set_server_sockopt(io, listener_opts[sock_name(io)])
792
+ logger.info "inherited addr=#{sock_name(io)} fd=#{io.fileno}"
793
+ io
777
794
  end
778
795
 
779
796
  config_listeners = config[:listeners].dup
@@ -796,9 +813,8 @@ class Unicorn::HttpServer
796
813
  # call only after calling inherit_listeners!
797
814
  # This binds any listeners we did NOT inherit from the parent
798
815
  def bind_new_listeners!
799
- NEW_LISTENERS.each { |addr| listen(addr) }
816
+ NEW_LISTENERS.each { |addr| listen(addr) }.clear
800
817
  raise ArgumentError, "no listeners" if LISTENERS.empty?
801
- NEW_LISTENERS.clear
802
818
  end
803
819
 
804
820
  # try to use the monotonic clock in Ruby >= 2.1, it is immune to clock