unicorn 4.9.0 → 5.0.0.pre1

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.
@@ -1,5 +1,4 @@
1
1
  # -*- encoding: binary -*-
2
- require 'fcntl'
3
2
  require 'etc'
4
3
  require 'stringio'
5
4
  require 'rack'
@@ -22,8 +21,7 @@ module Unicorn
22
21
  # since there is nothing in the application stack that is responsible
23
22
  # for client shutdowns/disconnects. This exception is visible to Rack
24
23
  # applications unless PrereadInput middleware is loaded.
25
- class ClientShutdown < EOFError
26
- end
24
+ ClientShutdown = Class.new(EOFError)
27
25
 
28
26
  # :stopdoc:
29
27
 
@@ -102,19 +100,13 @@ module Unicorn
102
100
 
103
101
  # remove this when we only support Ruby >= 2.0
104
102
  def self.pipe # :nodoc:
105
- Kgio::Pipe.new.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
103
+ Kgio::Pipe.new.each { |io| io.close_on_exec = true }
106
104
  end
107
105
  # :startdoc:
108
106
  end
109
107
  # :enddoc:
110
- require 'unicorn/const'
111
- require 'unicorn/socket_helper'
112
- require 'unicorn/stream_input'
113
- require 'unicorn/tee_input'
114
- require 'unicorn/http_request'
115
- require 'unicorn/configurator'
116
- require 'unicorn/tmpio'
117
- require 'unicorn/util'
118
- require 'unicorn/http_response'
119
- require 'unicorn/worker'
120
- require 'unicorn/http_server'
108
+
109
+ %w(const socket_helper stream_input tee_input http_request configurator
110
+ tmpio util http_response worker http_server).each do |s|
111
+ require_relative "unicorn/#{s}"
112
+ end
@@ -1,6 +1,5 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'logger'
3
- require 'unicorn/ssl_configurator'
4
3
 
5
4
  # Implements a simple DSL for configuring a \Unicorn server.
6
5
  #
@@ -13,7 +12,6 @@ require 'unicorn/ssl_configurator'
13
12
  # See the link:/TUNING.html document for more information on tuning unicorn.
14
13
  class Unicorn::Configurator
15
14
  include Unicorn
16
- include Unicorn::SSLConfigurator
17
15
 
18
16
  # :stopdoc:
19
17
  attr_accessor :set, :config_file, :after_reload
@@ -48,7 +46,6 @@ class Unicorn::Configurator
48
46
  :check_client_connection => false,
49
47
  :rewindable_input => true, # for Rack 2.x: (Rack::VERSION[0] <= 1),
50
48
  :client_body_buffer_size => Unicorn::Const::MAX_BODY,
51
- :trust_x_forwarded => true,
52
49
  }
53
50
  #:startdoc:
54
51
 
@@ -556,18 +553,6 @@ class Unicorn::Configurator
556
553
  set[:user] = [ user, group ]
557
554
  end
558
555
 
559
- # Sets whether or not the parser will trust X-Forwarded-Proto and
560
- # X-Forwarded-SSL headers and set "rack.url_scheme" to "https" accordingly.
561
- # Rainbows!/Zbatery installations facing untrusted clients directly
562
- # should set this to +false+. This is +true+ by default as Unicorn
563
- # is designed to only sit behind trusted nginx proxies.
564
- #
565
- # This has never been publically documented and is subject to removal
566
- # in future releases.
567
- def trust_x_forwarded(bool) # :nodoc:
568
- set_bool(:trust_x_forwarded, bool)
569
- end
570
-
571
556
  # expands "unix:path/to/foo" to a socket relative to the current path
572
557
  # expands pathnames of sockets if relative to "~" or "~username"
573
558
  # expands "*:port and ":port" to "0.0.0.0:port"
@@ -601,7 +586,7 @@ private
601
586
  def canonicalize_tcp(addr, port)
602
587
  packed = Socket.pack_sockaddr_in(port, addr)
603
588
  port, addr = Socket.unpack_sockaddr_in(packed)
604
- /:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
589
+ addr.include?(':') ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
605
590
  end
606
591
 
607
592
  def set_path(var, path) #:nodoc:
@@ -657,7 +642,7 @@ private
657
642
  raise ArgumentError, "rackup file (#{ru}) not readable"
658
643
 
659
644
  # it could be a .rb file, too, we don't parse those manually
660
- ru =~ /\.ru\z/ or return
645
+ ru.end_with?('.ru') or return
661
646
 
662
647
  /^#\\(.*)/ =~ File.read(ru) or return
663
648
  RACKUP[:optparse].parse!($1.split(/\s+/))
@@ -1,12 +1,6 @@
1
1
  # -*- encoding: binary -*-
2
2
 
3
- # :enddoc:
4
- # Frequently used constants when constructing requests or responses.
5
- # Many times the constant just refers to a string with the same
6
- # contents. Using these constants gave about a 3% to 10% performance
7
- # improvement over using the strings directly. Symbols did not really
8
- # improve things much compared to constants.
9
- module Unicorn::Const
3
+ module Unicorn::Const # :nodoc:
10
4
  # default TCP listen host address (0.0.0.0, all interfaces)
11
5
  DEFAULT_HOST = "0.0.0.0"
12
6
 
@@ -23,22 +17,5 @@ module Unicorn::Const
23
17
  # temporary file for reading (112 kilobytes). This is the default
24
18
  # value of client_body_buffer_size.
25
19
  MAX_BODY = 1024 * 112
26
-
27
- # :stopdoc:
28
- # common errors we'll send back
29
- # (N.B. these are not used by unicorn, but we won't drop them until
30
- # unicorn 5.x to avoid breaking Rainbows!).
31
- ERROR_400_RESPONSE = "HTTP/1.1 400 Bad Request\r\n\r\n"
32
- ERROR_414_RESPONSE = "HTTP/1.1 414 Request-URI Too Long\r\n\r\n"
33
- ERROR_413_RESPONSE = "HTTP/1.1 413 Request Entity Too Large\r\n\r\n"
34
- ERROR_500_RESPONSE = "HTTP/1.1 500 Internal Server Error\r\n\r\n"
35
-
36
- EXPECT_100_RESPONSE = "HTTP/1.1 100 Continue\r\n\r\n"
37
- EXPECT_100_RESPONSE_SUFFIXED = "100 Continue\r\n\r\nHTTP/1.1 "
38
-
39
- HTTP_RESPONSE_START = ['HTTP', '/1.1 ']
40
- HTTP_EXPECT = "HTTP_EXPECT"
41
-
42
- # :startdoc:
43
20
  end
44
- require 'unicorn/version'
21
+ require_relative 'version'
@@ -13,21 +13,26 @@ 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
20
21
  "SERVER_SOFTWARE" => "Unicorn #{Unicorn::Const::UNICORN_VERSION}"
21
22
  }
22
23
 
24
+ RACK_HIJACK = "rack.hijack".freeze
25
+ RACK_HIJACK_IO = "rack.hijack_io".freeze
23
26
  NULL_IO = StringIO.new("")
24
27
 
25
- attr_accessor :response_start_sent
26
-
27
28
  # :stopdoc:
28
29
  # A frozen format for this is about 15% faster
30
+ # Drop these frozen strings when Ruby 2.2 becomes more prevalent,
31
+ # 2.2+ optimizes hash assignments when used with literal string keys
29
32
  REMOTE_ADDR = 'REMOTE_ADDR'.freeze
30
33
  RACK_INPUT = 'rack.input'.freeze
34
+ UNICORN_SOCKET = 'unicorn.socket'.freeze
35
+ HTTP_RESPONSE_START = [ 'HTTP', '/1.1 ']
31
36
  @@input_class = Unicorn::TeeInput
32
37
  @@check_client_connection = false
33
38
 
@@ -85,38 +90,27 @@ class Unicorn::HttpParser
85
90
 
86
91
  # detect if the socket is valid by writing a partial response:
87
92
  if @@check_client_connection && headers?
88
- @response_start_sent = true
89
- Unicorn::Const::HTTP_RESPONSE_START.each { |c| socket.write(c) }
93
+ self.response_start_sent = true
94
+ HTTP_RESPONSE_START.each { |c| socket.write(c) }
90
95
  end
91
96
 
92
97
  e[RACK_INPUT] = 0 == content_length ?
93
98
  NULL_IO : @@input_class.new(socket, self)
94
- hijack_setup(e, socket)
95
- e.merge!(DEFAULTS)
96
- end
97
99
 
98
- # Rack 1.5.0 (protocol version 1.2) adds hijack request support
99
- if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
100
- DEFAULTS["rack.hijack?"] = true
101
- DEFAULTS["rack.version"] = [1, 2]
100
+ # for Rack hijacking in Rack 1.5 and later
101
+ e[UNICORN_SOCKET] = socket
102
+ e[RACK_HIJACK] = self
102
103
 
103
- RACK_HIJACK = "rack.hijack".freeze
104
- RACK_HIJACK_IO = "rack.hijack_io".freeze
105
-
106
- def hijacked?
107
- env.include?(RACK_HIJACK_IO)
108
- end
104
+ e.merge!(DEFAULTS)
105
+ end
109
106
 
110
- def hijack_setup(e, socket)
111
- e[RACK_HIJACK] = proc { e[RACK_HIJACK_IO] = socket }
112
- end
113
- else
114
- # old Rack, do nothing.
115
- def hijack_setup(e, _)
116
- end
107
+ # for rack.hijack, we respond to this method so no extra allocation
108
+ # of a proc object
109
+ def call
110
+ env[RACK_HIJACK_IO] = env[UNICORN_SOCKET]
111
+ end
117
112
 
118
- def hijacked?
119
- false
120
- end
113
+ def hijacked?
114
+ env.include?(RACK_HIJACK_IO)
121
115
  end
122
116
  end
@@ -24,23 +24,21 @@ module Unicorn::HttpResponse
24
24
  # writes the rack_response to socket as an HTTP response
25
25
  def http_response_write(socket, status, headers, body,
26
26
  response_start_sent=false)
27
- status = CODES[status.to_i] || status
28
27
  hijack = nil
29
28
 
30
29
  http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
31
30
  if headers
32
- buf = "#{http_response_start}#{status}\r\n" \
31
+ buf = "#{http_response_start}#{CODES[status.to_i] || status}\r\n" \
33
32
  "Date: #{httpdate}\r\n" \
34
- "Status: #{status}\r\n" \
35
33
  "Connection: close\r\n"
36
34
  headers.each do |key, value|
37
35
  case key
38
- when %r{\A(?:Date\z|Connection\z)}i
36
+ when %r{\A(?:Date|Connection)\z}i
39
37
  next
40
38
  when "rack.hijack"
41
- # this was an illegal key in Rack < 1.5, so it should be
42
- # OK to silently discard it for those older versions
43
- 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
44
42
  else
45
43
  if value =~ /\n/
46
44
  # avoiding blank, key-only cookies with /\n+/
@@ -54,22 +52,9 @@ module Unicorn::HttpResponse
54
52
  end
55
53
 
56
54
  if hijack
57
- body = nil # ensure we do not close body
58
55
  hijack.call(socket)
59
56
  else
60
57
  body.each { |chunk| socket.write(chunk) }
61
58
  end
62
- ensure
63
- body.respond_to?(:close) and body.close
64
- end
65
-
66
- # Rack 1.5.0 (protocol version 1.2) adds response hijacking support
67
- if ((Rack::VERSION[0] << 8) | Rack::VERSION[1]) >= 0x0102
68
- def hijack_prepare(value)
69
- value
70
- end
71
- else
72
- def hijack_prepare(_)
73
- end
74
59
  end
75
60
  end
@@ -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.
@@ -12,46 +11,23 @@ require "unicorn/ssl_server"
12
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
20
18
 
21
19
  attr_reader :pid, :logger
22
20
  include Unicorn::SocketHelper
23
21
  include Unicorn::HttpResponse
24
- include Unicorn::SSLServer
25
-
26
- # backwards compatibility with 1.x
27
- Worker = Unicorn::Worker
28
22
 
29
23
  # all bound listener sockets
24
+ # note: this is public used by raindrops, but not recommended for use
25
+ # in new projects
30
26
  LISTENERS = []
31
27
 
32
28
  # listeners we have yet to bind
33
29
  NEW_LISTENERS = []
34
30
 
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
31
  # :startdoc:
56
32
  # We populate this at startup so we can figure out how to reexecute
57
33
  # and upgrade the currently running instance of Unicorn
@@ -71,7 +47,7 @@ class Unicorn::HttpServer
71
47
  #
72
48
  # Unicorn::HttpServer::START_CTX[0] = "/home/bofh/2.2.0/bin/unicorn"
73
49
  START_CTX = {
74
- :argv => ARGV.map { |arg| arg.dup },
50
+ :argv => ARGV.map(&:dup),
75
51
  0 => $0.dup,
76
52
  }
77
53
  # We favor ENV['PWD'] since it is (usually) symlink aware for Capistrano
@@ -92,7 +68,7 @@ class Unicorn::HttpServer
92
68
  def initialize(app, options = {})
93
69
  @app = app
94
70
  @request = Unicorn::HttpRequest.new
95
- self.reexec_pid = 0
71
+ @reexec_pid = 0
96
72
  options = options.dup
97
73
  @ready_pipe = options.delete(:ready_pipe)
98
74
  @init_listeners = options[:listeners] ? options[:listeners].dup : []
@@ -100,6 +76,19 @@ class Unicorn::HttpServer
100
76
  self.config = Unicorn::Configurator.new(options)
101
77
  self.listener_opts = {}
102
78
 
79
+ # We use @self_pipe differently in the master and worker processes:
80
+ #
81
+ # * The master process never closes or reinitializes this once
82
+ # initialized. Signal handlers in the master process will write to
83
+ # it to wake up the master from IO.select in exactly the same manner
84
+ # djb describes in http://cr.yp.to/docs/selfpipe.html
85
+ #
86
+ # * The workers immediately close the pipe they inherit. See the
87
+ # Unicorn::Worker class for the pipe workers use.
88
+ @self_pipe = []
89
+ @workers = {} # hash maps PIDs to Workers
90
+ @sig_queue = [] # signal queue used for self-piping
91
+
103
92
  # we try inheriting listeners first, so we bind them later.
104
93
  # we don't write the pid file until we've bound listeners in case
105
94
  # unicorn was started twice by mistake. Even though our #pid= method
@@ -111,7 +100,10 @@ class Unicorn::HttpServer
111
100
  # monitoring tools may also rely on pid files existing before we
112
101
  # attempt to connect to the listener(s)
113
102
  config.commit!(self, :skip => [:listeners, :pid])
114
- self.orig_app = app
103
+ @orig_app = app
104
+ # list of signals we care about and trap in master.
105
+ @queue_sigs = [
106
+ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
115
107
  end
116
108
 
117
109
  # Runs the thing. Returns self so you can run join on it
@@ -119,13 +111,13 @@ class Unicorn::HttpServer
119
111
  inherit_listeners!
120
112
  # this pipe is used to wake us up from select(2) in #join when signals
121
113
  # are trapped. See trap_deferred.
122
- SELF_PIPE.replace(Unicorn.pipe)
114
+ @self_pipe.replace(Unicorn.pipe)
123
115
  @master_pid = $$
124
116
 
125
117
  # setup signal handlers before writing pid file in case people get
126
118
  # trigger happy and send signals as soon as the pid file exists.
127
119
  # Note that signals don't actually get handled until the #join method
128
- QUEUE_SIGS.each { |sig| trap(sig) { SIG_QUEUE << sig; awaken_master } }
120
+ @queue_sigs.each { |sig| trap(sig) { @sig_queue << sig; awaken_master } }
129
121
  trap(:CHLD) { awaken_master }
130
122
 
131
123
  # write pid early for Mongrel compatibility if we're not inheriting sockets
@@ -158,9 +150,6 @@ class Unicorn::HttpServer
158
150
 
159
151
  LISTENERS.delete_if do |io|
160
152
  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
153
  (io.close rescue nil).nil? # true
165
154
  else
166
155
  set_server_sockopt(io, listener_opts[sock_name(io)])
@@ -198,7 +187,7 @@ class Unicorn::HttpServer
198
187
  if path
199
188
  if x = valid_pid?(path)
200
189
  return path if pid && path == pid && x == $$
201
- if x == reexec_pid && pid =~ /\.oldbin\z/
190
+ if x == @reexec_pid && pid.end_with?('.oldbin')
202
191
  logger.warn("will not set pid=#{path} while reexec-ed "\
203
192
  "child is running PID:#{x}")
204
193
  return
@@ -241,7 +230,7 @@ class Unicorn::HttpServer
241
230
  begin
242
231
  io = bind_listen(address, opt)
243
232
  unless Kgio::TCPServer === io || Kgio::UNIXServer === io
244
- prevent_autoclose(io)
233
+ io.autoclose = false
245
234
  io = server_cast(io)
246
235
  end
247
236
  logger.info "listening on addr=#{sock_name(io)} fd=#{io.fileno}"
@@ -267,7 +256,7 @@ class Unicorn::HttpServer
267
256
  # is signalling us too often.
268
257
  def join
269
258
  respawn = true
270
- last_check = Time.now
259
+ last_check = time_now
271
260
 
272
261
  proc_name 'master'
273
262
  logger.info "master process ready" # test_exec.rb relies on this message
@@ -281,11 +270,11 @@ class Unicorn::HttpServer
281
270
  end
282
271
  begin
283
272
  reap_all_workers
284
- case SIG_QUEUE.shift
273
+ case @sig_queue.shift
285
274
  when nil
286
275
  # avoid murdering workers after our master process (or the
287
276
  # machine) comes out of suspend/hibernation
288
- if (last_check + @timeout) >= (last_check = Time.now)
277
+ if (last_check + @timeout) >= (last_check = time_now)
289
278
  sleep_time = murder_lazy_workers
290
279
  else
291
280
  sleep_time = @timeout/2.0 + 1
@@ -306,13 +295,13 @@ class Unicorn::HttpServer
306
295
  when :USR2 # exec binary, stay alive in case something went wrong
307
296
  reexec
308
297
  when :WINCH
309
- if Unicorn::Configurator::RACKUP[:daemonized]
298
+ if $stdin.tty?
299
+ logger.info "SIGWINCH ignored because we're not daemonized"
300
+ else
310
301
  respawn = false
311
302
  logger.info "gracefully stopping all workers"
312
303
  soft_kill_each_worker(:QUIT)
313
304
  self.worker_processes = 0
314
- else
315
- logger.info "SIGWINCH ignored because we're not daemonized"
316
305
  end
317
306
  when :TTIN
318
307
  respawn = true
@@ -339,8 +328,8 @@ class Unicorn::HttpServer
339
328
  # Terminates all workers, but does not exit master process
340
329
  def stop(graceful = true)
341
330
  self.listeners = []
342
- limit = Time.now + timeout
343
- until WORKERS.empty? || Time.now > limit
331
+ limit = time_now + timeout
332
+ until @workers.empty? || time_now > limit
344
333
  if graceful
345
334
  soft_kill_each_worker(:QUIT)
346
335
  else
@@ -369,14 +358,6 @@ class Unicorn::HttpServer
369
358
  Unicorn::TeeInput.client_body_buffer_size = bytes
370
359
  end
371
360
 
372
- def trust_x_forwarded
373
- Unicorn::HttpParser.trust_x_forwarded?
374
- end
375
-
376
- def trust_x_forwarded=(bool)
377
- Unicorn::HttpParser.trust_x_forwarded = bool
378
- end
379
-
380
361
  def check_client_connection
381
362
  Unicorn::HttpRequest.check_client_connection
382
363
  end
@@ -389,17 +370,17 @@ class Unicorn::HttpServer
389
370
 
390
371
  # wait for a signal hander to wake us up and then consume the pipe
391
372
  def master_sleep(sec)
373
+ @self_pipe[0].kgio_wait_readable(sec) or return
392
374
  # 11 bytes is the maximum string length which can be embedded within
393
375
  # the Ruby itself and not require a separate malloc (on 32-bit MRI 1.9+).
394
376
  # Most reads are only one byte here and uncommon, so it's not worth a
395
377
  # persistent buffer, either:
396
- IO.select([ SELF_PIPE[0] ], nil, nil, sec) or return
397
- SELF_PIPE[0].kgio_tryread(11)
378
+ @self_pipe[0].kgio_tryread(11)
398
379
  end
399
380
 
400
381
  def awaken_master
401
382
  return if $$ != @master_pid
402
- SELF_PIPE[1].kgio_trywrite('.') # wakeup master process from select
383
+ @self_pipe[1].kgio_trywrite('.') # wakeup master process from select
403
384
  end
404
385
 
405
386
  # reaps all unreaped workers
@@ -407,13 +388,13 @@ class Unicorn::HttpServer
407
388
  begin
408
389
  wpid, status = Process.waitpid2(-1, Process::WNOHANG)
409
390
  wpid or return
410
- if reexec_pid == wpid
391
+ if @reexec_pid == wpid
411
392
  logger.error "reaped #{status.inspect} exec()-ed"
412
- self.reexec_pid = 0
393
+ @reexec_pid = 0
413
394
  self.pid = pid.chomp('.oldbin') if pid
414
395
  proc_name 'master'
415
396
  else
416
- worker = WORKERS.delete(wpid) and worker.close rescue nil
397
+ worker = @workers.delete(wpid) and worker.close rescue nil
417
398
  m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}"
418
399
  status.success? ? logger.info(m) : logger.error(m)
419
400
  end
@@ -424,13 +405,13 @@ class Unicorn::HttpServer
424
405
 
425
406
  # reexecutes the START_CTX with a new binary
426
407
  def reexec
427
- if reexec_pid > 0
408
+ if @reexec_pid > 0
428
409
  begin
429
- Process.kill(0, reexec_pid)
430
- logger.error "reexec-ed child already running PID:#{reexec_pid}"
410
+ Process.kill(0, @reexec_pid)
411
+ logger.error "reexec-ed child already running PID:#@reexec_pid"
431
412
  return
432
413
  rescue Errno::ESRCH
433
- self.reexec_pid = 0
414
+ @reexec_pid = 0
434
415
  end
435
416
  end
436
417
 
@@ -448,13 +429,10 @@ class Unicorn::HttpServer
448
429
  end
449
430
  end
450
431
 
451
- self.reexec_pid = fork do
432
+ @reexec_pid = fork do
452
433
  listener_fds = {}
453
434
  LISTENERS.each do |sock|
454
- # IO#close_on_exec= will be available on any future version of
455
- # Ruby that sets FD_CLOEXEC by default on new file descriptors
456
- # ref: http://redmine.ruby-lang.org/issues/5041
457
- sock.close_on_exec = false if sock.respond_to?(:close_on_exec=)
435
+ sock.close_on_exec = false
458
436
  listener_fds[sock.fileno] = sock
459
437
  end
460
438
  ENV['UNICORN_FD'] = listener_fds.keys.join(',')
@@ -467,13 +445,13 @@ class Unicorn::HttpServer
467
445
  (3..1024).each do |io|
468
446
  next if listener_fds.include?(io)
469
447
  io = IO.for_fd(io) rescue next
470
- prevent_autoclose(io)
471
- io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
448
+ io.autoclose = false
449
+ io.close_on_exec = true
472
450
  end
473
451
 
474
452
  # exec(command, hash) works in at least 1.9.1+, but will only be
475
453
  # required in 1.9.4/2.0.0 at earliest.
476
- cmd << listener_fds if RUBY_VERSION >= "1.9.1"
454
+ cmd << listener_fds
477
455
  logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
478
456
  before_exec.call(self)
479
457
  exec(*cmd)
@@ -484,8 +462,8 @@ class Unicorn::HttpServer
484
462
  # forcibly terminate all workers that haven't checked in in timeout seconds. The timeout is implemented using an unlinked File
485
463
  def murder_lazy_workers
486
464
  next_sleep = @timeout - 1
487
- now = Time.now.to_i
488
- WORKERS.dup.each_pair do |wpid, worker|
465
+ now = time_now.to_i
466
+ @workers.dup.each_pair do |wpid, worker|
489
467
  tick = worker.tick
490
468
  0 == tick and next # skip workers that haven't processed any clients
491
469
  diff = now - tick
@@ -503,7 +481,7 @@ class Unicorn::HttpServer
503
481
  end
504
482
 
505
483
  def after_fork_internal
506
- SELF_PIPE.each { |io| io.close }.clear # this is master-only, now
484
+ @self_pipe.each(&:close).clear # this is master-only, now
507
485
  @ready_pipe.close if @ready_pipe
508
486
  Unicorn::Configurator::RACKUP.clear
509
487
  @ready_pipe = @init_listeners = @before_exec = @before_fork = nil
@@ -518,11 +496,11 @@ class Unicorn::HttpServer
518
496
  def spawn_missing_workers
519
497
  worker_nr = -1
520
498
  until (worker_nr += 1) == @worker_processes
521
- WORKERS.value?(worker_nr) and next
522
- worker = Worker.new(worker_nr)
499
+ @workers.value?(worker_nr) and next
500
+ worker = Unicorn::Worker.new(worker_nr)
523
501
  before_fork.call(self, worker)
524
502
  if pid = fork
525
- WORKERS[pid] = worker
503
+ @workers[pid] = worker
526
504
  worker.atfork_parent
527
505
  else
528
506
  after_fork_internal
@@ -536,9 +514,9 @@ class Unicorn::HttpServer
536
514
  end
537
515
 
538
516
  def maintain_worker_count
539
- (off = WORKERS.size - worker_processes) == 0 and return
517
+ (off = @workers.size - worker_processes) == 0 and return
540
518
  off < 0 and return spawn_missing_workers
541
- WORKERS.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
519
+ @workers.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
542
520
  end
543
521
 
544
522
  # if we get any error, try to write something back to the client
@@ -566,29 +544,37 @@ class Unicorn::HttpServer
566
544
  rescue
567
545
  end
568
546
 
569
- def expect_100_response
570
- if @request.response_start_sent
571
- Unicorn::Const::EXPECT_100_RESPONSE_SUFFIXED
572
- else
573
- Unicorn::Const::EXPECT_100_RESPONSE
574
- end
547
+ def e100_response_write(client, env)
548
+ # We use String#freeze to avoid allocations under Ruby 2.1+
549
+ # Not many users hit this code path, so it's better to reduce the
550
+ # constant table sizes even for 1.9.3-2.0 users who'll hit extra
551
+ # allocations here.
552
+ client.write(@request.response_start_sent ?
553
+ "100 Continue\r\n\r\nHTTP/1.1 ".freeze :
554
+ "HTTP/1.1 100 Continue\r\n\r\n".freeze)
555
+ env.delete('HTTP_EXPECT'.freeze)
575
556
  end
576
557
 
577
558
  # once a client is accepted, it is processed in its entirety here
578
559
  # in 3 easy steps: read request, call app, write app response
579
560
  def process_client(client)
580
561
  status, headers, body = @app.call(env = @request.read(client))
581
- return if @request.hijacked?
582
562
 
583
- if 100 == status.to_i
584
- client.write(expect_100_response)
585
- env.delete(Unicorn::Const::HTTP_EXPECT)
586
- status, headers, body = @app.call(env)
563
+ begin
587
564
  return if @request.hijacked?
565
+
566
+ if 100 == status.to_i
567
+ e100_response_write(client, env)
568
+ status, headers, body = @app.call(env)
569
+ return if @request.hijacked?
570
+ end
571
+ @request.headers? or headers = nil
572
+ http_response_write(client, status, headers, body,
573
+ @request.response_start_sent)
574
+ ensure
575
+ body.respond_to?(:close) and body.close
588
576
  end
589
- @request.headers? or headers = nil
590
- http_response_write(client, status, headers, body,
591
- @request.response_start_sent)
577
+
592
578
  unless client.closed? # rack.hijack may've close this for us
593
579
  client.shutdown # in case of fork() in Rack app
594
580
  client.close # flush and uncork socket immediately, no keepalive
@@ -597,9 +583,6 @@ class Unicorn::HttpServer
597
583
  handle_error(client, e)
598
584
  end
599
585
 
600
- EXIT_SIGS = [ :QUIT, :TERM, :INT ]
601
- WORKER_QUEUE_SIGS = QUEUE_SIGS - EXIT_SIGS
602
-
603
586
  def nuke_listeners!(readers)
604
587
  # only called from the worker, ordering is important here
605
588
  tmp = readers.dup
@@ -614,23 +597,23 @@ class Unicorn::HttpServer
614
597
  def init_worker_process(worker)
615
598
  worker.atfork_child
616
599
  # we'll re-trap :QUIT later for graceful shutdown iff we accept clients
617
- EXIT_SIGS.each { |sig| trap(sig) { exit!(0) } }
618
- exit!(0) if (SIG_QUEUE & EXIT_SIGS)[0]
619
- WORKER_QUEUE_SIGS.each { |sig| trap(sig, nil) }
600
+ exit_sigs = [ :QUIT, :TERM, :INT ]
601
+ exit_sigs.each { |sig| trap(sig) { exit!(0) } }
602
+ exit!(0) if (@sig_queue & exit_sigs)[0]
603
+ (@queue_sigs - exit_sigs).each { |sig| trap(sig, nil) }
620
604
  trap(:CHLD, 'DEFAULT')
621
- SIG_QUEUE.clear
605
+ @sig_queue.clear
622
606
  proc_name "worker[#{worker.nr}]"
623
607
  START_CTX.clear
624
- WORKERS.clear
608
+ @workers.clear
625
609
 
626
610
  after_fork.call(self, worker) # can drop perms and create listeners
627
- LISTENERS.each { |sock| sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
611
+ LISTENERS.each { |sock| sock.close_on_exec = true }
628
612
 
629
613
  worker.user(*user) if user.kind_of?(Array) && ! worker.switched
630
614
  self.timeout /= 2.0 # halve it for select()
631
615
  @config = nil
632
616
  build_app! unless preload_app
633
- ssl_enable!
634
617
  @after_fork = @listener_opts = @orig_app = nil
635
618
  readers = LISTENERS.dup
636
619
  readers << worker
@@ -651,7 +634,7 @@ class Unicorn::HttpServer
651
634
  # for connections and doesn't die until the parent dies (or is
652
635
  # given a INT, QUIT, or TERM signal)
653
636
  def worker_loop(worker)
654
- ppid = master_pid
637
+ ppid = @master_pid
655
638
  readers = init_worker_process(worker)
656
639
  nr = 0 # this becomes negative if we need to reopen logs
657
640
 
@@ -665,7 +648,7 @@ class Unicorn::HttpServer
665
648
  begin
666
649
  nr < 0 and reopen_worker_logs(worker.nr)
667
650
  nr = 0
668
- worker.tick = Time.now.to_i
651
+ worker.tick = time_now.to_i
669
652
  tmp = ready.dup
670
653
  while sock = tmp.shift
671
654
  # Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
@@ -673,7 +656,7 @@ class Unicorn::HttpServer
673
656
  if client = sock.kgio_tryaccept
674
657
  process_client(client)
675
658
  nr += 1
676
- worker.tick = Time.now.to_i
659
+ worker.tick = time_now.to_i
677
660
  end
678
661
  break if nr < 0
679
662
  end
@@ -690,7 +673,7 @@ class Unicorn::HttpServer
690
673
  ppid == Process.ppid or return
691
674
 
692
675
  # timeout used so we can detect parent death:
693
- worker.tick = Time.now.to_i
676
+ worker.tick = time_now.to_i
694
677
  ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
695
678
  rescue => e
696
679
  redo if nr < 0 && readers[0]
@@ -702,17 +685,17 @@ class Unicorn::HttpServer
702
685
  # is no longer running.
703
686
  def kill_worker(signal, wpid)
704
687
  Process.kill(signal, wpid)
705
- rescue Errno::ESRCH
706
- worker = WORKERS.delete(wpid) and worker.close rescue nil
688
+ rescue Errno::ESRCH
689
+ worker = @workers.delete(wpid) and worker.close rescue nil
707
690
  end
708
691
 
709
692
  # delivers a signal to each worker
710
693
  def kill_each_worker(signal)
711
- WORKERS.keys.each { |wpid| kill_worker(signal, wpid) }
694
+ @workers.keys.each { |wpid| kill_worker(signal, wpid) }
712
695
  end
713
696
 
714
697
  def soft_kill_each_worker(signal)
715
- WORKERS.each_value { |worker| worker.soft_kill(signal) }
698
+ @workers.each_value { |worker| worker.soft_kill(signal) }
716
699
  end
717
700
 
718
701
  # unlinks a PID file at given +path+ if it contains the current PID
@@ -745,7 +728,7 @@ class Unicorn::HttpServer
745
728
  config.commit!(self)
746
729
  soft_kill_each_worker(:QUIT)
747
730
  Unicorn::Util.reopen_logs
748
- self.app = orig_app
731
+ self.app = @orig_app
749
732
  build_app! if preload_app
750
733
  logger.info "done reloading config_file=#{config.config_file}"
751
734
  rescue StandardError, LoadError, SyntaxError => e
@@ -782,10 +765,10 @@ class Unicorn::HttpServer
782
765
  def inherit_listeners!
783
766
  # inherit sockets from parents, they need to be plain Socket objects
784
767
  # before they become Kgio::UNIXServer or Kgio::TCPServer
785
- inherited = ENV['UNICORN_FD'].to_s.split(/,/).map do |fd|
768
+ inherited = ENV['UNICORN_FD'].to_s.split(',').map do |fd|
786
769
  io = Socket.for_fd(fd.to_i)
787
770
  set_server_sockopt(io, listener_opts[sock_name(io)])
788
- prevent_autoclose(io)
771
+ io.autoclose = false
789
772
  logger.info "inherited addr=#{sock_name(io)} fd=#{fd}"
790
773
  server_cast(io)
791
774
  end
@@ -810,8 +793,20 @@ class Unicorn::HttpServer
810
793
  # call only after calling inherit_listeners!
811
794
  # This binds any listeners we did NOT inherit from the parent
812
795
  def bind_new_listeners!
813
- NEW_LISTENERS.each { |addr| listen(addr) }
796
+ NEW_LISTENERS.each { |addr| listen(addr) }.clear
814
797
  raise ArgumentError, "no listeners" if LISTENERS.empty?
815
- NEW_LISTENERS.clear
798
+ end
799
+
800
+ # try to use the monotonic clock in Ruby >= 2.1, it is immune to clock
801
+ # offset adjustments and generates less garbage (Float vs Time object)
802
+ begin
803
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
804
+ def time_now
805
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
806
+ end
807
+ rescue NameError, NoMethodError
808
+ def time_now # Ruby <= 2.0
809
+ Time.now
810
+ end
816
811
  end
817
812
  end