unicorn 4.9.0 → 5.0.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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/Application_Timeouts +3 -3
  3. data/DESIGN +2 -4
  4. data/Documentation/unicorn.1.txt +8 -5
  5. data/Documentation/unicorn_rails.1.txt +2 -2
  6. data/FAQ +17 -8
  7. data/GIT-VERSION-GEN +1 -1
  8. data/GNUmakefile +6 -1
  9. data/ISSUES +20 -28
  10. data/KNOWN_ISSUES +9 -9
  11. data/Links +14 -17
  12. data/PHILOSOPHY +0 -6
  13. data/README +22 -17
  14. data/SIGNALS +1 -1
  15. data/Sandbox +4 -4
  16. data/TUNING +11 -8
  17. data/bin/unicorn +1 -1
  18. data/bin/unicorn_rails +1 -1
  19. data/examples/nginx.conf +10 -11
  20. data/examples/unicorn.conf.rb +1 -4
  21. data/ext/unicorn_http/extconf.rb +1 -0
  22. data/ext/unicorn_http/httpdate.c +1 -1
  23. data/ext/unicorn_http/unicorn_http.rl +89 -156
  24. data/lib/unicorn.rb +10 -18
  25. data/lib/unicorn/configurator.rb +17 -31
  26. data/lib/unicorn/const.rb +2 -25
  27. data/lib/unicorn/http_request.rb +22 -33
  28. data/lib/unicorn/http_response.rb +14 -32
  29. data/lib/unicorn/http_server.rb +129 -122
  30. data/lib/unicorn/socket_helper.rb +36 -72
  31. data/lib/unicorn/stream_input.rb +3 -3
  32. data/lib/unicorn/tmpio.rb +0 -5
  33. data/lib/unicorn/util.rb +2 -1
  34. data/lib/unicorn/worker.rb +3 -15
  35. data/t/hijack.ru +2 -1
  36. data/t/t0200-rack-hijack.sh +5 -2
  37. data/test/exec/test_exec.rb +52 -0
  38. data/test/test_helper.rb +3 -2
  39. data/test/unit/test_http_parser_ng.rb +16 -114
  40. data/test/unit/test_response.rb +19 -16
  41. data/test/unit/test_socket_helper.rb +1 -1
  42. data/unicorn.gemspec +10 -1
  43. metadata +10 -23
  44. data/examples/git.ru +0 -13
  45. data/lib/unicorn/app/exec_cgi.rb +0 -154
  46. data/lib/unicorn/app/inetd.rb +0 -109
  47. data/lib/unicorn/ssl_client.rb +0 -11
  48. data/lib/unicorn/ssl_configurator.rb +0 -104
  49. data/lib/unicorn/ssl_server.rb +0 -42
  50. data/t/t0016-trust-x-forwarded-false.sh +0 -30
  51. data/t/t0017-trust-x-forwarded-true.sh +0 -30
  52. data/test/unit/test_http_parser_xftrust.rb +0 -38
  53. data/test/unit/test_sni_hostnames.rb +0 -47
data/lib/unicorn.rb CHANGED
@@ -1,5 +1,4 @@
1
1
  # -*- encoding: binary -*-
2
- require 'fcntl'
3
2
  require 'etc'
4
3
  require 'stringio'
5
4
  require 'rack'
@@ -11,10 +10,10 @@ require 'kgio'
11
10
  # enough functionality to service web application requests fast as possible.
12
11
  # :startdoc:
13
12
 
14
- # \Unicorn exposes very little of an user-visible API and most of its
15
- # internals are subject to change. \Unicorn is designed to host Rack
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
16
15
  # applications, so applications should be written against the Rack SPEC
17
- # and not \Unicorn internals.
16
+ # and not unicorn internals.
18
17
  module Unicorn
19
18
 
20
19
  # Raised inside TeeInput when a client closes the socket inside the
@@ -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,8 +1,7 @@
1
1
  # -*- encoding: binary -*-
2
2
  require 'logger'
3
- require 'unicorn/ssl_configurator'
4
3
 
5
- # Implements a simple DSL for configuring a \Unicorn server.
4
+ # Implements a simple DSL for configuring a unicorn server.
6
5
  #
7
6
  # See http://unicorn.bogomips.org/examples/unicorn.conf.rb and
8
7
  # http://unicorn.bogomips.org/examples/unicorn.conf.minimal.rb
@@ -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
 
@@ -257,6 +254,11 @@ class Unicorn::Configurator
257
254
  #
258
255
  # Default: 1024
259
256
  #
257
+ # Note: with the Linux kernel, the net.core.somaxconn sysctl defaults
258
+ # to 128, capping this value to 128. Raising the sysctl allows a
259
+ # larger backlog (which may not be desirable with multiple,
260
+ # load-balanced machines).
261
+ #
260
262
  # [:rcvbuf => bytes, :sndbuf => bytes]
261
263
  #
262
264
  # Maximum receive and send buffer sizes (in bytes) of sockets.
@@ -280,20 +282,19 @@ class Unicorn::Configurator
280
282
  # Setting this to +true+ can make streaming responses in Rails 3.1
281
283
  # appear more quickly at the cost of slightly higher bandwidth usage.
282
284
  # The effect of this option is most visible if nginx is not used,
283
- # but nginx remains highly recommended with \Unicorn.
285
+ # but nginx remains highly recommended with unicorn.
284
286
  #
285
287
  # This has no effect on UNIX sockets.
286
288
  #
287
- # Default: +true+ (Nagle's algorithm disabled) in \Unicorn,
288
- # +true+ in Rainbows! This defaulted to +false+ in \Unicorn
289
- # 3.x
289
+ # Default: +true+ (Nagle's algorithm disabled) in unicorn
290
+ # This defaulted to +false+ in unicorn 3.x
290
291
  #
291
292
  # [:tcp_nopush => true or false]
292
293
  #
293
294
  # Enables/disables TCP_CORK in Linux or TCP_NOPUSH in FreeBSD
294
295
  #
295
296
  # This prevents partial TCP frames from being sent out and reduces
296
- # wakeups in nginx if it is on a different machine. Since \Unicorn
297
+ # wakeups in nginx if it is on a different machine. Since unicorn
297
298
  # is only designed for applications that send the response body
298
299
  # quickly without keepalive, sockets will always be flushed on close
299
300
  # to prevent delays.
@@ -301,7 +302,7 @@ class Unicorn::Configurator
301
302
  # This has no effect on UNIX sockets.
302
303
  #
303
304
  # Default: +false+
304
- # This defaulted to +true+ in \Unicorn 3.4 - 3.7
305
+ # This defaulted to +true+ in unicorn 3.4 - 3.7
305
306
  #
306
307
  # [:ipv6only => true or false]
307
308
  #
@@ -385,12 +386,10 @@ class Unicorn::Configurator
385
386
  # and +false+ or +nil+ is synonymous for a value of zero.
386
387
  #
387
388
  # A value of +1+ is a good optimization for local networks
388
- # and trusted clients. For Rainbows! and Zbatery users, a higher
389
- # value (e.g. +60+) provides more protection against some
390
- # denial-of-service attacks. There is no good reason to ever
391
- # disable this with a +zero+ value when serving HTTP.
389
+ # and trusted clients. There is no good reason to ever
390
+ # disable this with a +zero+ value with unicorn.
392
391
  #
393
- # Default: 1 retransmit for \Unicorn, 60 for Rainbows! 0.95.0\+
392
+ # Default: 1
394
393
  #
395
394
  # [:accept_filter => String]
396
395
  #
@@ -399,8 +398,7 @@ class Unicorn::Configurator
399
398
  # This enables either the "dataready" or (default) "httpready"
400
399
  # accept() filter under FreeBSD. This is intended as an
401
400
  # optimization to reduce context switches with common GET/HEAD
402
- # requests. For Rainbows! and Zbatery users, this provides
403
- # some protection against certain denial-of-service attacks, too.
401
+ # requests.
404
402
  #
405
403
  # There is no good reason to change from the default.
406
404
  #
@@ -556,18 +554,6 @@ class Unicorn::Configurator
556
554
  set[:user] = [ user, group ]
557
555
  end
558
556
 
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
557
  # expands "unix:path/to/foo" to a socket relative to the current path
572
558
  # expands pathnames of sockets if relative to "~" or "~username"
573
559
  # expands "*:port and ":port" to "0.0.0.0:port"
@@ -601,7 +587,7 @@ private
601
587
  def canonicalize_tcp(addr, port)
602
588
  packed = Socket.pack_sockaddr_in(port, addr)
603
589
  port, addr = Socket.unpack_sockaddr_in(packed)
604
- /:/ =~ addr ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
590
+ addr.include?(':') ? "[#{addr}]:#{port}" : "#{addr}:#{port}"
605
591
  end
606
592
 
607
593
  def set_path(var, path) #:nodoc:
@@ -657,7 +643,7 @@ private
657
643
  raise ArgumentError, "rackup file (#{ru}) not readable"
658
644
 
659
645
  # it could be a .rb file, too, we don't parse those manually
660
- ru =~ /\.ru\z/ or return
646
+ ru.end_with?('.ru') or return
661
647
 
662
648
  /^#\\(.*)/ =~ File.read(ru) or return
663
649
  RACKUP[:optparse].parse!($1.split(/\s+/))
data/lib/unicorn/const.rb CHANGED
@@ -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,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,12 +23,11 @@ class Unicorn::HttpParser
22
23
 
23
24
  NULL_IO = StringIO.new("")
24
25
 
25
- attr_accessor :response_start_sent
26
-
27
26
  # :stopdoc:
28
27
  # A frozen format for this is about 15% faster
29
- REMOTE_ADDR = 'REMOTE_ADDR'.freeze
30
- RACK_INPUT = 'rack.input'.freeze
28
+ # Drop these frozen strings when Ruby 2.2 becomes more prevalent,
29
+ # 2.2+ optimizes hash assignments when used with literal string keys
30
+ HTTP_RESPONSE_START = [ 'HTTP', '/1.1 ']
31
31
  @@input_class = Unicorn::TeeInput
32
32
  @@check_client_connection = false
33
33
 
@@ -73,7 +73,7 @@ class Unicorn::HttpParser
73
73
  # identify the client for the immediate request to the server;
74
74
  # that client may be a proxy, gateway, or other intermediary
75
75
  # acting on behalf of the actual source client."
76
- e[REMOTE_ADDR] = socket.kgio_addr
76
+ e['REMOTE_ADDR'] = socket.kgio_addr
77
77
 
78
78
  # short circuit the common case with small GET requests first
79
79
  socket.kgio_read!(16384, buf)
@@ -85,38 +85,27 @@ class Unicorn::HttpParser
85
85
 
86
86
  # detect if the socket is valid by writing a partial response:
87
87
  if @@check_client_connection && headers?
88
- @response_start_sent = true
89
- Unicorn::Const::HTTP_RESPONSE_START.each { |c| socket.write(c) }
88
+ self.response_start_sent = true
89
+ HTTP_RESPONSE_START.each { |c| socket.write(c) }
90
90
  end
91
91
 
92
- e[RACK_INPUT] = 0 == content_length ?
93
- NULL_IO : @@input_class.new(socket, self)
94
- hijack_setup(e, socket)
95
- e.merge!(DEFAULTS)
96
- end
97
-
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]
92
+ e['rack.input'] = 0 == content_length ?
93
+ NULL_IO : @@input_class.new(socket, self)
102
94
 
103
- RACK_HIJACK = "rack.hijack".freeze
104
- RACK_HIJACK_IO = "rack.hijack_io".freeze
95
+ # for Rack hijacking in Rack 1.5 and later
96
+ e['unicorn.socket'] = socket
97
+ e['rack.hijack'] = self
105
98
 
106
- def hijacked?
107
- env.include?(RACK_HIJACK_IO)
108
- end
99
+ e.merge!(DEFAULTS)
100
+ end
109
101
 
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
102
+ # for rack.hijack, we respond to this method so no extra allocation
103
+ # of a proc object
104
+ def call
105
+ env['rack.hijack_io'] = env['unicorn.socket']
106
+ end
117
107
 
118
- def hijacked?
119
- false
120
- end
108
+ def hijacked?
109
+ env.include?('rack.hijack_io'.freeze)
121
110
  end
122
111
  end
@@ -10,66 +10,48 @@
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"
19
-
13
+ # internal API, code will always be common-enough-for-even-old-Rack
20
14
  def err_response(code, response_start_sent)
21
- "#{response_start_sent ? '' : 'HTTP/1.1 '}#{CODES[code]}\r\n\r\n"
15
+ "#{response_start_sent ? '' : 'HTTP/1.1 '}" \
16
+ "#{code} #{Rack::Utils::HTTP_STATUS_CODES[code]}\r\n\r\n"
22
17
  end
23
18
 
24
19
  # writes the rack_response to socket as an HTTP response
25
20
  def http_response_write(socket, status, headers, body,
26
21
  response_start_sent=false)
27
- status = CODES[status.to_i] || status
28
22
  hijack = nil
29
23
 
30
- http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
31
24
  if headers
32
- buf = "#{http_response_start}#{status}\r\n" \
25
+ code = status.to_i
26
+ msg = Rack::Utils::HTTP_STATUS_CODES[code]
27
+ start = response_start_sent ? ''.freeze : 'HTTP/1.1 '.freeze
28
+ buf = "#{start}#{msg ? %Q(#{code} #{msg}) : status}\r\n" \
33
29
  "Date: #{httpdate}\r\n" \
34
- "Status: #{status}\r\n" \
35
30
  "Connection: close\r\n"
36
31
  headers.each do |key, value|
37
32
  case key
38
- when %r{\A(?:Date\z|Connection\z)}i
33
+ when %r{\A(?:Date|Connection)\z}i
39
34
  next
40
35
  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)
36
+ # This should only be hit under Rack >= 1.5, as this was an illegal
37
+ # key in Rack < 1.5
38
+ hijack = value
44
39
  else
45
- if value =~ /\n/
40
+ if value.include?("\n".freeze)
46
41
  # avoiding blank, key-only cookies with /\n+/
47
- buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
42
+ value.split(/\n+/).each { |v| buf << "#{key}: #{v}\r\n" }
48
43
  else
49
44
  buf << "#{key}: #{value}\r\n"
50
45
  end
51
46
  end
52
47
  end
53
- socket.write(buf << CRLF)
48
+ socket.write(buf << "\r\n".freeze)
54
49
  end
55
50
 
56
51
  if hijack
57
- body = nil # ensure we do not close body
58
52
  hijack.call(socket)
59
53
  else
60
54
  body.each { |chunk| socket.write(chunk) }
61
55
  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
56
  end
75
57
  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.
@@ -8,50 +7,27 @@ require "unicorn/ssl_server"
8
7
  #
9
8
  # Users do not need to know the internals of this class, but reading the
10
9
  # {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
+ # 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
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,12 +481,13 @@ 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
510
488
 
511
- srand # http://redmine.ruby-lang.org/issues/4338
489
+ # http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/36450
490
+ srand # remove in unicorn 6
512
491
 
513
492
  # The OpenSSL PRNG is seeded with only the pid, and apps with frequently
514
493
  # dying workers can recycle pids
@@ -518,11 +497,11 @@ class Unicorn::HttpServer
518
497
  def spawn_missing_workers
519
498
  worker_nr = -1
520
499
  until (worker_nr += 1) == @worker_processes
521
- WORKERS.value?(worker_nr) and next
522
- worker = Worker.new(worker_nr)
500
+ @workers.value?(worker_nr) and next
501
+ worker = Unicorn::Worker.new(worker_nr)
523
502
  before_fork.call(self, worker)
524
503
  if pid = fork
525
- WORKERS[pid] = worker
504
+ @workers[pid] = worker
526
505
  worker.atfork_parent
527
506
  else
528
507
  after_fork_internal
@@ -536,9 +515,9 @@ class Unicorn::HttpServer
536
515
  end
537
516
 
538
517
  def maintain_worker_count
539
- (off = WORKERS.size - worker_processes) == 0 and return
518
+ (off = @workers.size - worker_processes) == 0 and return
540
519
  off < 0 and return spawn_missing_workers
541
- WORKERS.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
520
+ @workers.each_value { |w| w.nr >= worker_processes and w.soft_kill(:QUIT) }
542
521
  end
543
522
 
544
523
  # if we get any error, try to write something back to the client
@@ -566,29 +545,37 @@ class Unicorn::HttpServer
566
545
  rescue
567
546
  end
568
547
 
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
548
+ def e100_response_write(client, env)
549
+ # We use String#freeze to avoid allocations under Ruby 2.1+
550
+ # Not many users hit this code path, so it's better to reduce the
551
+ # constant table sizes even for 1.9.3-2.0 users who'll hit extra
552
+ # allocations here.
553
+ client.write(@request.response_start_sent ?
554
+ "100 Continue\r\n\r\nHTTP/1.1 ".freeze :
555
+ "HTTP/1.1 100 Continue\r\n\r\n".freeze)
556
+ env.delete('HTTP_EXPECT'.freeze)
575
557
  end
576
558
 
577
559
  # once a client is accepted, it is processed in its entirety here
578
560
  # in 3 easy steps: read request, call app, write app response
579
561
  def process_client(client)
580
562
  status, headers, body = @app.call(env = @request.read(client))
581
- return if @request.hijacked?
582
563
 
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)
564
+ begin
587
565
  return if @request.hijacked?
566
+
567
+ if 100 == status.to_i
568
+ e100_response_write(client, env)
569
+ status, headers, body = @app.call(env)
570
+ return if @request.hijacked?
571
+ end
572
+ @request.headers? or headers = nil
573
+ http_response_write(client, status, headers, body,
574
+ @request.response_start_sent)
575
+ ensure
576
+ body.respond_to?(:close) and body.close
588
577
  end
589
- @request.headers? or headers = nil
590
- http_response_write(client, status, headers, body,
591
- @request.response_start_sent)
578
+
592
579
  unless client.closed? # rack.hijack may've close this for us
593
580
  client.shutdown # in case of fork() in Rack app
594
581
  client.close # flush and uncork socket immediately, no keepalive
@@ -597,9 +584,6 @@ class Unicorn::HttpServer
597
584
  handle_error(client, e)
598
585
  end
599
586
 
600
- EXIT_SIGS = [ :QUIT, :TERM, :INT ]
601
- WORKER_QUEUE_SIGS = QUEUE_SIGS - EXIT_SIGS
602
-
603
587
  def nuke_listeners!(readers)
604
588
  # only called from the worker, ordering is important here
605
589
  tmp = readers.dup
@@ -614,23 +598,23 @@ class Unicorn::HttpServer
614
598
  def init_worker_process(worker)
615
599
  worker.atfork_child
616
600
  # 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) }
601
+ exit_sigs = [ :QUIT, :TERM, :INT ]
602
+ exit_sigs.each { |sig| trap(sig) { exit!(0) } }
603
+ exit!(0) if (@sig_queue & exit_sigs)[0]
604
+ (@queue_sigs - exit_sigs).each { |sig| trap(sig, nil) }
620
605
  trap(:CHLD, 'DEFAULT')
621
- SIG_QUEUE.clear
606
+ @sig_queue.clear
622
607
  proc_name "worker[#{worker.nr}]"
623
608
  START_CTX.clear
624
- WORKERS.clear
609
+ @workers.clear
625
610
 
626
611
  after_fork.call(self, worker) # can drop perms and create listeners
627
- LISTENERS.each { |sock| sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
612
+ LISTENERS.each { |sock| sock.close_on_exec = true }
628
613
 
629
614
  worker.user(*user) if user.kind_of?(Array) && ! worker.switched
630
615
  self.timeout /= 2.0 # halve it for select()
631
616
  @config = nil
632
617
  build_app! unless preload_app
633
- ssl_enable!
634
618
  @after_fork = @listener_opts = @orig_app = nil
635
619
  readers = LISTENERS.dup
636
620
  readers << worker
@@ -651,7 +635,7 @@ class Unicorn::HttpServer
651
635
  # for connections and doesn't die until the parent dies (or is
652
636
  # given a INT, QUIT, or TERM signal)
653
637
  def worker_loop(worker)
654
- ppid = master_pid
638
+ ppid = @master_pid
655
639
  readers = init_worker_process(worker)
656
640
  nr = 0 # this becomes negative if we need to reopen logs
657
641
 
@@ -665,7 +649,7 @@ class Unicorn::HttpServer
665
649
  begin
666
650
  nr < 0 and reopen_worker_logs(worker.nr)
667
651
  nr = 0
668
- worker.tick = Time.now.to_i
652
+ worker.tick = time_now.to_i
669
653
  tmp = ready.dup
670
654
  while sock = tmp.shift
671
655
  # Unicorn::Worker#kgio_tryaccept is not like accept(2) at all,
@@ -673,7 +657,7 @@ class Unicorn::HttpServer
673
657
  if client = sock.kgio_tryaccept
674
658
  process_client(client)
675
659
  nr += 1
676
- worker.tick = Time.now.to_i
660
+ worker.tick = time_now.to_i
677
661
  end
678
662
  break if nr < 0
679
663
  end
@@ -690,7 +674,7 @@ class Unicorn::HttpServer
690
674
  ppid == Process.ppid or return
691
675
 
692
676
  # timeout used so we can detect parent death:
693
- worker.tick = Time.now.to_i
677
+ worker.tick = time_now.to_i
694
678
  ret = IO.select(readers, nil, nil, @timeout) and ready = ret[0]
695
679
  rescue => e
696
680
  redo if nr < 0 && readers[0]
@@ -702,17 +686,17 @@ class Unicorn::HttpServer
702
686
  # is no longer running.
703
687
  def kill_worker(signal, wpid)
704
688
  Process.kill(signal, wpid)
705
- rescue Errno::ESRCH
706
- worker = WORKERS.delete(wpid) and worker.close rescue nil
689
+ rescue Errno::ESRCH
690
+ worker = @workers.delete(wpid) and worker.close rescue nil
707
691
  end
708
692
 
709
693
  # delivers a signal to each worker
710
694
  def kill_each_worker(signal)
711
- WORKERS.keys.each { |wpid| kill_worker(signal, wpid) }
695
+ @workers.keys.each { |wpid| kill_worker(signal, wpid) }
712
696
  end
713
697
 
714
698
  def soft_kill_each_worker(signal)
715
- WORKERS.each_value { |worker| worker.soft_kill(signal) }
699
+ @workers.each_value { |worker| worker.soft_kill(signal) }
716
700
  end
717
701
 
718
702
  # unlinks a PID file at given +path+ if it contains the current PID
@@ -745,7 +729,7 @@ class Unicorn::HttpServer
745
729
  config.commit!(self)
746
730
  soft_kill_each_worker(:QUIT)
747
731
  Unicorn::Util.reopen_logs
748
- self.app = orig_app
732
+ self.app = @orig_app
749
733
  build_app! if preload_app
750
734
  logger.info "done reloading config_file=#{config.config_file}"
751
735
  rescue StandardError, LoadError, SyntaxError => e
@@ -782,12 +766,23 @@ class Unicorn::HttpServer
782
766
  def inherit_listeners!
783
767
  # inherit sockets from parents, they need to be plain Socket objects
784
768
  # before they become Kgio::UNIXServer or Kgio::TCPServer
785
- inherited = ENV['UNICORN_FD'].to_s.split(/,/).map do |fd|
769
+ inherited = ENV['UNICORN_FD'].to_s.split(',')
770
+
771
+ # emulate sd_listen_fds() for systemd
772
+ sd_pid, sd_fds = ENV.values_at('LISTEN_PID', 'LISTEN_FDS')
773
+ if sd_pid.to_i == $$ # n.b. $$ can never be zero
774
+ # 3 = SD_LISTEN_FDS_START
775
+ inherited.concat((3...(3 + sd_fds.to_i)).to_a)
776
+ end
777
+ # to ease debugging, we will not unset LISTEN_PID and LISTEN_FDS
778
+
779
+ inherited.map! do |fd|
786
780
  io = Socket.for_fd(fd.to_i)
781
+ io.autoclose = false
782
+ io = server_cast(io)
787
783
  set_server_sockopt(io, listener_opts[sock_name(io)])
788
- prevent_autoclose(io)
789
- logger.info "inherited addr=#{sock_name(io)} fd=#{fd}"
790
- server_cast(io)
784
+ logger.info "inherited addr=#{sock_name(io)} fd=#{io.fileno}"
785
+ io
791
786
  end
792
787
 
793
788
  config_listeners = config[:listeners].dup
@@ -810,8 +805,20 @@ class Unicorn::HttpServer
810
805
  # call only after calling inherit_listeners!
811
806
  # This binds any listeners we did NOT inherit from the parent
812
807
  def bind_new_listeners!
813
- NEW_LISTENERS.each { |addr| listen(addr) }
808
+ NEW_LISTENERS.each { |addr| listen(addr) }.clear
814
809
  raise ArgumentError, "no listeners" if LISTENERS.empty?
815
- NEW_LISTENERS.clear
810
+ end
811
+
812
+ # try to use the monotonic clock in Ruby >= 2.1, it is immune to clock
813
+ # offset adjustments and generates less garbage (Float vs Time object)
814
+ begin
815
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
816
+ def time_now
817
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
818
+ end
819
+ rescue NameError, NoMethodError
820
+ def time_now # Ruby <= 2.0
821
+ Time.now
822
+ end
816
823
  end
817
824
  end