unicorn-academia 4.7.0.1

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 (159) hide show
  1. checksums.yaml +7 -0
  2. data/.CHANGELOG.old +25 -0
  3. data/.document +29 -0
  4. data/.gitignore +25 -0
  5. data/.mailmap +26 -0
  6. data/.wrongdoc.yml +10 -0
  7. data/Application_Timeouts +77 -0
  8. data/CONTRIBUTORS +35 -0
  9. data/COPYING +674 -0
  10. data/DESIGN +97 -0
  11. data/Documentation/.gitignore +5 -0
  12. data/Documentation/GNUmakefile +30 -0
  13. data/Documentation/unicorn.1.txt +178 -0
  14. data/Documentation/unicorn_rails.1.txt +175 -0
  15. data/FAQ +53 -0
  16. data/GIT-VERSION-GEN +39 -0
  17. data/GNUmakefile +266 -0
  18. data/HACKING +134 -0
  19. data/ISSUES +36 -0
  20. data/KNOWN_ISSUES +79 -0
  21. data/LICENSE +67 -0
  22. data/Links +56 -0
  23. data/PHILOSOPHY +145 -0
  24. data/README +150 -0
  25. data/Rakefile +60 -0
  26. data/SIGNALS +114 -0
  27. data/Sandbox +103 -0
  28. data/TODO +5 -0
  29. data/TUNING +98 -0
  30. data/bin/unicorn +126 -0
  31. data/bin/unicorn_rails +209 -0
  32. data/examples/big_app_gc.rb +2 -0
  33. data/examples/echo.ru +27 -0
  34. data/examples/git.ru +13 -0
  35. data/examples/init.sh +74 -0
  36. data/examples/logger_mp_safe.rb +25 -0
  37. data/examples/logrotate.conf +29 -0
  38. data/examples/nginx.conf +156 -0
  39. data/examples/unicorn.conf.minimal.rb +13 -0
  40. data/examples/unicorn.conf.rb +102 -0
  41. data/ext/unicorn_http/CFLAGS +13 -0
  42. data/ext/unicorn_http/c_util.h +124 -0
  43. data/ext/unicorn_http/common_field_optimization.h +111 -0
  44. data/ext/unicorn_http/ext_help.h +82 -0
  45. data/ext/unicorn_http/extconf.rb +10 -0
  46. data/ext/unicorn_http/global_variables.h +97 -0
  47. data/ext/unicorn_http/httpdate.c +78 -0
  48. data/ext/unicorn_http/unicorn_http.rl +1036 -0
  49. data/ext/unicorn_http/unicorn_http_common.rl +76 -0
  50. data/lib/unicorn.rb +113 -0
  51. data/lib/unicorn/app/exec_cgi.rb +154 -0
  52. data/lib/unicorn/app/inetd.rb +109 -0
  53. data/lib/unicorn/app/old_rails.rb +35 -0
  54. data/lib/unicorn/app/old_rails/static.rb +59 -0
  55. data/lib/unicorn/cgi_wrapper.rb +147 -0
  56. data/lib/unicorn/configurator.rb +679 -0
  57. data/lib/unicorn/const.rb +44 -0
  58. data/lib/unicorn/http_request.rb +122 -0
  59. data/lib/unicorn/http_response.rb +75 -0
  60. data/lib/unicorn/http_server.rb +808 -0
  61. data/lib/unicorn/launcher.rb +62 -0
  62. data/lib/unicorn/oob_gc.rb +71 -0
  63. data/lib/unicorn/preread_input.rb +33 -0
  64. data/lib/unicorn/socket_helper.rb +231 -0
  65. data/lib/unicorn/ssl_client.rb +11 -0
  66. data/lib/unicorn/ssl_configurator.rb +104 -0
  67. data/lib/unicorn/ssl_server.rb +42 -0
  68. data/lib/unicorn/stream_input.rb +149 -0
  69. data/lib/unicorn/tee_input.rb +126 -0
  70. data/lib/unicorn/tmpio.rb +29 -0
  71. data/lib/unicorn/util.rb +89 -0
  72. data/lib/unicorn/worker.rb +88 -0
  73. data/local.mk.sample +59 -0
  74. data/script/isolate_for_tests +32 -0
  75. data/setup.rb +1586 -0
  76. data/t/.gitignore +5 -0
  77. data/t/GNUmakefile +82 -0
  78. data/t/README +42 -0
  79. data/t/bin/content-md5-put +36 -0
  80. data/t/bin/sha1sum.rb +17 -0
  81. data/t/bin/unused_listen +40 -0
  82. data/t/broken-app.ru +12 -0
  83. data/t/detach.ru +11 -0
  84. data/t/env.ru +3 -0
  85. data/t/fails-rack-lint.ru +5 -0
  86. data/t/heartbeat-timeout.ru +12 -0
  87. data/t/hijack.ru +42 -0
  88. data/t/listener_names.ru +4 -0
  89. data/t/my-tap-lib.sh +201 -0
  90. data/t/oob_gc.ru +20 -0
  91. data/t/oob_gc_path.ru +20 -0
  92. data/t/pid.ru +3 -0
  93. data/t/preread_input.ru +17 -0
  94. data/t/rack-input-tests.ru +21 -0
  95. data/t/sslgen.sh +71 -0
  96. data/t/t0000-http-basic.sh +50 -0
  97. data/t/t0001-reload-bad-config.sh +53 -0
  98. data/t/t0002-config-conflict.sh +49 -0
  99. data/t/t0002-parser-error.sh +94 -0
  100. data/t/t0003-working_directory.sh +51 -0
  101. data/t/t0004-heartbeat-timeout.sh +69 -0
  102. data/t/t0004-working_directory_broken.sh +24 -0
  103. data/t/t0005-working_directory_app.rb.sh +40 -0
  104. data/t/t0006-reopen-logs.sh +83 -0
  105. data/t/t0006.ru +13 -0
  106. data/t/t0007-working_directory_no_embed_cli.sh +44 -0
  107. data/t/t0008-back_out_of_upgrade.sh +110 -0
  108. data/t/t0009-broken-app.sh +56 -0
  109. data/t/t0009-winch_ttin.sh +59 -0
  110. data/t/t0010-reap-logging.sh +55 -0
  111. data/t/t0011-active-unix-socket.sh +79 -0
  112. data/t/t0012-reload-empty-config.sh +85 -0
  113. data/t/t0013-rewindable-input-false.sh +24 -0
  114. data/t/t0013.ru +12 -0
  115. data/t/t0014-rewindable-input-true.sh +24 -0
  116. data/t/t0014.ru +12 -0
  117. data/t/t0015-configurator-internals.sh +25 -0
  118. data/t/t0016-trust-x-forwarded-false.sh +30 -0
  119. data/t/t0017-trust-x-forwarded-true.sh +30 -0
  120. data/t/t0018-write-on-close.sh +23 -0
  121. data/t/t0019-max_header_len.sh +49 -0
  122. data/t/t0020-at_exit-handler.sh +49 -0
  123. data/t/t0021-process_detach.sh +29 -0
  124. data/t/t0022-listener_names-preload_app.sh +32 -0
  125. data/t/t0100-rack-input-tests.sh +124 -0
  126. data/t/t0116-client_body_buffer_size.sh +80 -0
  127. data/t/t0116.ru +16 -0
  128. data/t/t0200-rack-hijack.sh +27 -0
  129. data/t/t0300-no-default-middleware.sh +15 -0
  130. data/t/t0600-https-server-basic.sh +48 -0
  131. data/t/t9000-preread-input.sh +48 -0
  132. data/t/t9001-oob_gc.sh +47 -0
  133. data/t/t9002-oob_gc-path.sh +75 -0
  134. data/t/test-lib.sh +128 -0
  135. data/t/write-on-close.ru +11 -0
  136. data/test/aggregate.rb +15 -0
  137. data/test/benchmark/README +50 -0
  138. data/test/benchmark/dd.ru +18 -0
  139. data/test/benchmark/stack.ru +8 -0
  140. data/test/exec/README +5 -0
  141. data/test/exec/test_exec.rb +1047 -0
  142. data/test/test_helper.rb +297 -0
  143. data/test/unit/test_configurator.rb +175 -0
  144. data/test/unit/test_droplet.rb +28 -0
  145. data/test/unit/test_http_parser.rb +854 -0
  146. data/test/unit/test_http_parser_ng.rb +731 -0
  147. data/test/unit/test_http_parser_xftrust.rb +38 -0
  148. data/test/unit/test_request.rb +182 -0
  149. data/test/unit/test_response.rb +99 -0
  150. data/test/unit/test_server.rb +268 -0
  151. data/test/unit/test_signals.rb +188 -0
  152. data/test/unit/test_sni_hostnames.rb +47 -0
  153. data/test/unit/test_socket_helper.rb +195 -0
  154. data/test/unit/test_stream_input.rb +203 -0
  155. data/test/unit/test_tee_input.rb +294 -0
  156. data/test/unit/test_upload.rb +306 -0
  157. data/test/unit/test_util.rb +105 -0
  158. data/unicorn-academia.gemspec +44 -0
  159. metadata +328 -0
@@ -0,0 +1,44 @@
1
+ # -*- encoding: binary -*-
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
10
+ # default TCP listen host address (0.0.0.0, all interfaces)
11
+ DEFAULT_HOST = "0.0.0.0"
12
+
13
+ # default TCP listen port (8080)
14
+ DEFAULT_PORT = 8080
15
+
16
+ # default TCP listen address and port (0.0.0.0:8080)
17
+ DEFAULT_LISTEN = "#{DEFAULT_HOST}:#{DEFAULT_PORT}"
18
+
19
+ # The basic request body size we'll try to read at once (16 kilobytes).
20
+ CHUNK_SIZE = 16 * 1024
21
+
22
+ # Maximum request body size before it is moved out of memory and into a
23
+ # temporary file for reading (112 kilobytes). This is the default
24
+ # value of client_body_buffer_size.
25
+ 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
+ end
44
+ require 'unicorn/version'
@@ -0,0 +1,122 @@
1
+ # -*- encoding: binary -*-
2
+ # :enddoc:
3
+ # no stable API here
4
+ require 'unicorn_http'
5
+
6
+ # TODO: remove redundant names
7
+ Unicorn.const_set(:HttpRequest, Unicorn::HttpParser)
8
+ class Unicorn::HttpParser
9
+
10
+ # default parameters we merge into the request env for Rack handlers
11
+ DEFAULTS = {
12
+ "rack.errors" => $stderr,
13
+ "rack.multiprocess" => true,
14
+ "rack.multithread" => false,
15
+ "rack.run_once" => false,
16
+ "rack.version" => [1, 1],
17
+ "SCRIPT_NAME" => "",
18
+
19
+ # this is not in the Rack spec, but some apps may rely on it
20
+ "SERVER_SOFTWARE" => "Unicorn #{Unicorn::Const::UNICORN_VERSION}"
21
+ }
22
+
23
+ NULL_IO = StringIO.new("")
24
+
25
+ attr_accessor :response_start_sent
26
+
27
+ # :stopdoc:
28
+ # A frozen format for this is about 15% faster
29
+ REMOTE_ADDR = 'REMOTE_ADDR'.freeze
30
+ RACK_INPUT = 'rack.input'.freeze
31
+ @@input_class = Unicorn::TeeInput
32
+ @@check_client_connection = false
33
+
34
+ def self.input_class
35
+ @@input_class
36
+ end
37
+
38
+ def self.input_class=(klass)
39
+ @@input_class = klass
40
+ end
41
+
42
+ def self.check_client_connection
43
+ @@check_client_connection
44
+ end
45
+
46
+ def self.check_client_connection=(bool)
47
+ @@check_client_connection = bool
48
+ end
49
+
50
+ # :startdoc:
51
+
52
+ # Does the majority of the IO processing. It has been written in
53
+ # Ruby using about 8 different IO processing strategies.
54
+ #
55
+ # It is currently carefully constructed to make sure that it gets
56
+ # the best possible performance for the common case: GET requests
57
+ # that are fully complete after a single read(2)
58
+ #
59
+ # Anyone who thinks they can make it faster is more than welcome to
60
+ # take a crack at it.
61
+ #
62
+ # returns an environment hash suitable for Rack if successful
63
+ # This does minimal exception trapping and it is up to the caller
64
+ # to handle any socket errors (e.g. user aborted upload).
65
+ def read(socket)
66
+ clear
67
+ e = env
68
+
69
+ # From http://www.ietf.org/rfc/rfc3875:
70
+ # "Script authors should be aware that the REMOTE_ADDR and
71
+ # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
72
+ # may not identify the ultimate source of the request. They
73
+ # identify the client for the immediate request to the server;
74
+ # that client may be a proxy, gateway, or other intermediary
75
+ # acting on behalf of the actual source client."
76
+ e[REMOTE_ADDR] = socket.kgio_addr
77
+
78
+ # short circuit the common case with small GET requests first
79
+ socket.kgio_read!(16384, buf)
80
+ if parse.nil?
81
+ # Parser is not done, queue up more data to read and continue parsing
82
+ # an Exception thrown from the parser will throw us out of the loop
83
+ false until add_parse(socket.kgio_read!(16384))
84
+ end
85
+
86
+ # detect if the socket is valid by writing a partial response:
87
+ if @@check_client_connection && headers?
88
+ @response_start_sent = true
89
+ Unicorn::Const::HTTP_RESPONSE_START.each { |c| socket.write(c) }
90
+ end
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]
102
+
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
109
+
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
117
+
118
+ def hijacked?
119
+ false
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,75 @@
1
+ # -*- encoding: binary -*-
2
+ # :enddoc:
3
+ # Writes a Rack response to your client using the HTTP/1.1 specification.
4
+ # You use it by simply doing:
5
+ #
6
+ # status, headers, body = rack_app.call(env)
7
+ # http_response_write(socket, status, headers, body)
8
+ #
9
+ # Most header correctness (including Content-Length and Content-Type)
10
+ # is the job of Rack, with the exception of the "Date" and "Status" header.
11
+ module Unicorn::HttpResponse
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
+
20
+ def err_response(code, response_start_sent)
21
+ "#{response_start_sent ? '' : 'HTTP/1.1 '}#{CODES[code]}\r\n\r\n"
22
+ end
23
+
24
+ # writes the rack_response to socket as an HTTP response
25
+ def http_response_write(socket, status, headers, body,
26
+ response_start_sent=false)
27
+ status = CODES[status.to_i] || status
28
+ hijack = nil
29
+
30
+ http_response_start = response_start_sent ? '' : 'HTTP/1.1 '
31
+ if headers
32
+ buf = "#{http_response_start}#{status}\r\n" \
33
+ "Date: #{httpdate}\r\n" \
34
+ "Status: #{status}\r\n" \
35
+ "Connection: close\r\n"
36
+ headers.each do |key, value|
37
+ case key
38
+ when %r{\A(?:Date\z|Connection\z)}i
39
+ next
40
+ 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)
44
+ else
45
+ if value =~ /\n/
46
+ # avoiding blank, key-only cookies with /\n+/
47
+ buf << value.split(/\n+/).map! { |v| "#{key}: #{v}\r\n" }.join
48
+ else
49
+ buf << "#{key}: #{value}\r\n"
50
+ end
51
+ end
52
+ end
53
+ socket.write(buf << CRLF)
54
+ end
55
+
56
+ if hijack
57
+ body = nil # ensure we do not close body
58
+ hijack.call(socket)
59
+ else
60
+ body.each { |chunk| socket.write(chunk) }
61
+ 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
+ end
75
+ end
@@ -0,0 +1,808 @@
1
+ # -*- encoding: binary -*-
2
+ require "unicorn/ssl_server"
3
+
4
+ # This is the process manager of Unicorn. This manages worker
5
+ # processes which in turn handle the I/O and application process.
6
+ # Listener sockets are started in the master process and shared with
7
+ # forked worker children.
8
+ #
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.
13
+ class Unicorn::HttpServer
14
+ # :stopdoc:
15
+ attr_accessor :app, :request, :timeout, :worker_processes,
16
+ :before_fork, :after_fork, :before_exec,
17
+ :listener_opts, :preload_app,
18
+ :reexec_pid, :orig_app, :init_listeners,
19
+ :master_pid, :config, :ready_pipe, :user
20
+
21
+ attr_reader :pid, :logger
22
+ include Unicorn::SocketHelper
23
+ include Unicorn::HttpResponse
24
+ include Unicorn::SSLServer
25
+
26
+ # backwards compatibility with 1.x
27
+ Worker = Unicorn::Worker
28
+
29
+ # all bound listener sockets
30
+ LISTENERS = []
31
+
32
+ # listeners we have yet to bind
33
+ NEW_LISTENERS = []
34
+
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 from the
46
+ # master and replace it with a new pipe after forking. This new
47
+ # pipe is also used to wakeup from IO.select from inside (worker)
48
+ # signal handlers. However, workers *close* the pipe descriptors in
49
+ # the signal handlers to raise EBADF in IO.select instead of writing
50
+ # like we do in the master. We cannot easily use the reader set for
51
+ # IO.select because LISTENERS is already that set, and it's extra
52
+ # work (and cycles) to distinguish the pipe FD from the reader set
53
+ # once IO.select returns. So we're lazy and just close the pipe when
54
+ # a (rare) signal arrives in the worker and reinitialize the pipe later.
55
+ SELF_PIPE = []
56
+
57
+ # signal queue used for self-piping
58
+ SIG_QUEUE = []
59
+
60
+ # list of signals we care about and trap in master.
61
+ QUEUE_SIGS = [ :WINCH, :QUIT, :INT, :TERM, :USR1, :USR2, :HUP, :TTIN, :TTOU ]
62
+
63
+ # :startdoc:
64
+ # We populate this at startup so we can figure out how to reexecute
65
+ # and upgrade the currently running instance of Unicorn
66
+ # This Hash is considered a stable interface and changing its contents
67
+ # will allow you to switch between different installations of Unicorn
68
+ # or even different installations of the same applications without
69
+ # downtime. Keys of this constant Hash are described as follows:
70
+ #
71
+ # * 0 - the path to the unicorn/unicorn_rails executable
72
+ # * :argv - a deep copy of the ARGV array the executable originally saw
73
+ # * :cwd - the working directory of the application, this is where
74
+ # you originally started Unicorn.
75
+ #
76
+ # To change your unicorn executable to a different path without downtime,
77
+ # you can set the following in your Unicorn config file, HUP and then
78
+ # continue with the traditional USR2 + QUIT upgrade steps:
79
+ #
80
+ # Unicorn::HttpServer::START_CTX[0] = "/home/bofh/1.9.2/bin/unicorn"
81
+ START_CTX = {
82
+ :argv => ARGV.map { |arg| arg.dup },
83
+ 0 => $0.dup,
84
+ }
85
+ # We favor ENV['PWD'] since it is (usually) symlink aware for Capistrano
86
+ # and like systems
87
+ START_CTX[:cwd] = begin
88
+ a = File.stat(pwd = ENV['PWD'])
89
+ b = File.stat(Dir.pwd)
90
+ a.ino == b.ino && a.dev == b.dev ? pwd : Dir.pwd
91
+ rescue
92
+ Dir.pwd
93
+ end
94
+ # :stopdoc:
95
+
96
+ # Creates a working server on host:port (strange things happen if
97
+ # port isn't a Number). Use HttpServer::run to start the server and
98
+ # HttpServer.run.join to join the thread that's processing
99
+ # incoming requests on the socket.
100
+ def initialize(app, options = {})
101
+ @app = app
102
+ @request = Unicorn::HttpRequest.new
103
+ self.reexec_pid = 0
104
+ options = options.dup
105
+ @ready_pipe = options.delete(:ready_pipe)
106
+ @init_listeners = options[:listeners] ? options[:listeners].dup : []
107
+ options[:use_defaults] = true
108
+ self.config = Unicorn::Configurator.new(options)
109
+ self.listener_opts = {}
110
+
111
+ # we try inheriting listeners first, so we bind them later.
112
+ # we don't write the pid file until we've bound listeners in case
113
+ # unicorn was started twice by mistake. Even though our #pid= method
114
+ # checks for stale/existing pid files, race conditions are still
115
+ # possible (and difficult/non-portable to avoid) and can be likely
116
+ # to clobber the pid if the second start was in quick succession
117
+ # after the first, so we rely on the listener binding to fail in
118
+ # that case. Some tests (in and outside of this source tree) and
119
+ # monitoring tools may also rely on pid files existing before we
120
+ # attempt to connect to the listener(s)
121
+ config.commit!(self, :skip => [:listeners, :pid])
122
+ self.orig_app = app
123
+ end
124
+
125
+ # Runs the thing. Returns self so you can run join on it
126
+ def start
127
+ inherit_listeners!
128
+ # this pipe is used to wake us up from select(2) in #join when signals
129
+ # are trapped. See trap_deferred.
130
+ init_self_pipe!
131
+
132
+ # setup signal handlers before writing pid file in case people get
133
+ # trigger happy and send signals as soon as the pid file exists.
134
+ # Note that signals don't actually get handled until the #join method
135
+ QUEUE_SIGS.each { |sig| trap(sig) { SIG_QUEUE << sig; awaken_master } }
136
+ trap(:CHLD) { awaken_master }
137
+
138
+ # write pid early for Mongrel compatibility if we're not inheriting sockets
139
+ # This was needed for compatibility with some health checker a long time
140
+ # ago. This unfortunately has the side effect of clobbering valid PID
141
+ # files.
142
+ self.pid = config[:pid] unless ENV["UNICORN_FD"]
143
+
144
+ self.master_pid = $$
145
+ build_app! if preload_app
146
+ bind_new_listeners!
147
+
148
+ # Assuming preload_app==false, we drop the pid file after the app is ready
149
+ # to process requests. If binding or build_app! fails with
150
+ # preload_app==true, we'll never get here and the parent will recover
151
+ self.pid = config[:pid] if ENV["UNICORN_FD"]
152
+
153
+ spawn_missing_workers
154
+ self
155
+ end
156
+
157
+ # replaces current listener set with +listeners+. This will
158
+ # close the socket if it will not exist in the new listener set
159
+ def listeners=(listeners)
160
+ cur_names, dead_names = [], []
161
+ listener_names.each do |name|
162
+ if ?/ == name[0]
163
+ # mark unlinked sockets as dead so we can rebind them
164
+ (File.socket?(name) ? cur_names : dead_names) << name
165
+ else
166
+ cur_names << name
167
+ end
168
+ end
169
+ set_names = listener_names(listeners)
170
+ dead_names.concat(cur_names - set_names).uniq!
171
+
172
+ LISTENERS.delete_if do |io|
173
+ if dead_names.include?(sock_name(io))
174
+ IO_PURGATORY.delete_if do |pio|
175
+ pio.fileno == io.fileno && (pio.close rescue nil).nil? # true
176
+ end
177
+ (io.close rescue nil).nil? # true
178
+ else
179
+ set_server_sockopt(io, listener_opts[sock_name(io)])
180
+ false
181
+ end
182
+ end
183
+
184
+ (set_names - cur_names).each { |addr| listen(addr) }
185
+ end
186
+
187
+ def stdout_path=(path); redirect_io($stdout, path); end
188
+ def stderr_path=(path); redirect_io($stderr, path); end
189
+
190
+ def logger=(obj)
191
+ Unicorn::HttpRequest::DEFAULTS["rack.logger"] = @logger = obj
192
+ end
193
+
194
+ def clobber_pid(path)
195
+ unlink_pid_safe(@pid) if @pid
196
+ if path
197
+ fp = begin
198
+ tmp = "#{File.dirname(path)}/#{rand}.#$$"
199
+ File.open(tmp, File::RDWR|File::CREAT|File::EXCL, 0644)
200
+ rescue Errno::EEXIST
201
+ retry
202
+ end
203
+ fp.syswrite("#$$\n")
204
+ File.rename(fp.path, path)
205
+ fp.close
206
+ end
207
+ end
208
+
209
+ # sets the path for the PID file of the master process
210
+ def pid=(path)
211
+ if path
212
+ if x = valid_pid?(path)
213
+ return path if pid && path == pid && x == $$
214
+ if x == reexec_pid && pid =~ /\.oldbin\z/
215
+ logger.warn("will not set pid=#{path} while reexec-ed "\
216
+ "child is running PID:#{x}")
217
+ return
218
+ end
219
+ raise ArgumentError, "Already running on PID:#{x} " \
220
+ "(or pid=#{path} is stale)"
221
+ end
222
+ end
223
+
224
+ # rename the old pid if possible
225
+ if @pid && path
226
+ begin
227
+ File.rename(@pid, path)
228
+ rescue Errno::ENOENT, Errno::EXDEV
229
+ # a user may have accidentally removed the original,
230
+ # obviously cross-FS renames don't work, either.
231
+ clobber_pid(path)
232
+ end
233
+ else
234
+ clobber_pid(path)
235
+ end
236
+ @pid = path
237
+ end
238
+
239
+ # add a given address to the +listeners+ set, idempotently
240
+ # Allows workers to add a private, per-process listener via the
241
+ # after_fork hook. Very useful for debugging and testing.
242
+ # +:tries+ may be specified as an option for the number of times
243
+ # to retry, and +:delay+ may be specified as the time in seconds
244
+ # to delay between retries.
245
+ # A negative value for +:tries+ indicates the listen will be
246
+ # retried indefinitely, this is useful when workers belonging to
247
+ # different masters are spawned during a transparent upgrade.
248
+ def listen(address, opt = {}.merge(listener_opts[address] || {}))
249
+ address = config.expand_addr(address)
250
+ return if String === address && listener_names.include?(address)
251
+
252
+ delay = opt[:delay] || 0.5
253
+ tries = opt[:tries] || 5
254
+ begin
255
+ io = bind_listen(address, opt)
256
+ unless Kgio::TCPServer === io || Kgio::UNIXServer === io
257
+ prevent_autoclose(io)
258
+ io = server_cast(io)
259
+ end
260
+ logger.info "listening on addr=#{sock_name(io)} fd=#{io.fileno}"
261
+ LISTENERS << io
262
+ io
263
+ rescue Errno::EADDRINUSE => err
264
+ logger.error "adding listener failed addr=#{address} (in use)"
265
+ raise err if tries == 0
266
+ tries -= 1
267
+ logger.error "retrying in #{delay} seconds " \
268
+ "(#{tries < 0 ? 'infinite' : tries} tries left)"
269
+ sleep(delay)
270
+ retry
271
+ rescue => err
272
+ logger.fatal "error adding listener addr=#{address}"
273
+ raise err
274
+ end
275
+ end
276
+
277
+ # monitors children and receives signals forever
278
+ # (or until a termination signal is sent). This handles signals
279
+ # one-at-a-time time and we'll happily drop signals in case somebody
280
+ # is signalling us too often.
281
+ def join
282
+ respawn = true
283
+ last_check = Time.now
284
+
285
+ proc_name 'master'
286
+ logger.info "master process ready" # test_exec.rb relies on this message
287
+ if @ready_pipe
288
+ @ready_pipe.syswrite($$.to_s)
289
+ @ready_pipe = @ready_pipe.close rescue nil
290
+ end
291
+ begin
292
+ reap_all_workers
293
+ case SIG_QUEUE.shift
294
+ when nil
295
+ # avoid murdering workers after our master process (or the
296
+ # machine) comes out of suspend/hibernation
297
+ if (last_check + @timeout) >= (last_check = Time.now)
298
+ sleep_time = murder_lazy_workers
299
+ else
300
+ sleep_time = @timeout/2.0 + 1
301
+ @logger.debug("waiting #{sleep_time}s after suspend/hibernation")
302
+ end
303
+ maintain_worker_count if respawn
304
+ master_sleep(sleep_time)
305
+ when :QUIT # graceful shutdown
306
+ break
307
+ when :TERM, :INT # immediate shutdown
308
+ stop(false)
309
+ break
310
+ when :USR1 # rotate logs
311
+ logger.info "master reopening logs..."
312
+ Unicorn::Util.reopen_logs
313
+ logger.info "master done reopening logs"
314
+ kill_each_worker(:USR1)
315
+ when :USR2 # exec binary, stay alive in case something went wrong
316
+ reexec
317
+ when :WINCH
318
+ if Unicorn::Configurator::RACKUP[:daemonized]
319
+ respawn = false
320
+ logger.info "gracefully stopping all workers"
321
+ kill_each_worker(:QUIT)
322
+ self.worker_processes = 0
323
+ else
324
+ logger.info "SIGWINCH ignored because we're not daemonized"
325
+ end
326
+ when :TTIN
327
+ respawn = true
328
+ self.worker_processes += 1
329
+ when :TTOU
330
+ self.worker_processes -= 1 if self.worker_processes > 0
331
+ when :HUP
332
+ respawn = true
333
+ if config.config_file
334
+ load_config!
335
+ else # exec binary and exit if there's no config file
336
+ logger.info "config_file not present, reexecuting binary"
337
+ reexec
338
+ end
339
+ end
340
+ rescue => e
341
+ Unicorn.log_error(@logger, "master loop error", e)
342
+ end while true
343
+ stop # gracefully shutdown all workers on our way out
344
+ logger.info "master complete"
345
+ unlink_pid_safe(pid) if pid
346
+ end
347
+
348
+ # Terminates all workers, but does not exit master process
349
+ def stop(graceful = true)
350
+ self.listeners = []
351
+ limit = Time.now + timeout
352
+ until WORKERS.empty? || Time.now > limit
353
+ kill_each_worker(graceful ? :QUIT : :TERM)
354
+ sleep(0.1)
355
+ reap_all_workers
356
+ end
357
+ kill_each_worker(:KILL)
358
+ end
359
+
360
+ def rewindable_input
361
+ Unicorn::HttpRequest.input_class.method_defined?(:rewind)
362
+ end
363
+
364
+ def rewindable_input=(bool)
365
+ Unicorn::HttpRequest.input_class = bool ?
366
+ Unicorn::TeeInput : Unicorn::StreamInput
367
+ end
368
+
369
+ def client_body_buffer_size
370
+ Unicorn::TeeInput.client_body_buffer_size
371
+ end
372
+
373
+ def client_body_buffer_size=(bytes)
374
+ Unicorn::TeeInput.client_body_buffer_size = bytes
375
+ end
376
+
377
+ def trust_x_forwarded
378
+ Unicorn::HttpParser.trust_x_forwarded?
379
+ end
380
+
381
+ def trust_x_forwarded=(bool)
382
+ Unicorn::HttpParser.trust_x_forwarded = bool
383
+ end
384
+
385
+ def check_client_connection
386
+ Unicorn::HttpRequest.check_client_connection
387
+ end
388
+
389
+ def check_client_connection=(bool)
390
+ Unicorn::HttpRequest.check_client_connection = bool
391
+ end
392
+
393
+ private
394
+
395
+ # wait for a signal hander to wake us up and then consume the pipe
396
+ def master_sleep(sec)
397
+ IO.select([ SELF_PIPE[0] ], nil, nil, sec) or return
398
+ SELF_PIPE[0].kgio_tryread(11)
399
+ end
400
+
401
+ def awaken_master
402
+ SELF_PIPE[1].kgio_trywrite('.') # wakeup master process from select
403
+ end
404
+
405
+ # reaps all unreaped workers
406
+ def reap_all_workers
407
+ begin
408
+ wpid, status = Process.waitpid2(-1, Process::WNOHANG)
409
+ wpid or return
410
+ if reexec_pid == wpid
411
+ logger.error "reaped #{status.inspect} exec()-ed"
412
+ self.reexec_pid = 0
413
+ self.pid = pid.chomp('.oldbin') if pid
414
+ proc_name 'master'
415
+ else
416
+ worker = WORKERS.delete(wpid) and worker.close rescue nil
417
+ m = "reaped #{status.inspect} worker=#{worker.nr rescue 'unknown'}"
418
+ status.success? ? logger.info(m) : logger.error(m)
419
+ end
420
+ rescue Errno::ECHILD
421
+ break
422
+ end while true
423
+ end
424
+
425
+ # reexecutes the START_CTX with a new binary
426
+ def reexec
427
+ if reexec_pid > 0
428
+ begin
429
+ Process.kill(0, reexec_pid)
430
+ logger.error "reexec-ed child already running PID:#{reexec_pid}"
431
+ return
432
+ rescue Errno::ESRCH
433
+ self.reexec_pid = 0
434
+ end
435
+ end
436
+
437
+ if pid
438
+ old_pid = "#{pid}.oldbin"
439
+ begin
440
+ self.pid = old_pid # clear the path for a new pid file
441
+ rescue ArgumentError
442
+ logger.error "old PID:#{valid_pid?(old_pid)} running with " \
443
+ "existing pid=#{old_pid}, refusing rexec"
444
+ return
445
+ rescue => e
446
+ logger.error "error writing pid=#{old_pid} #{e.class} #{e.message}"
447
+ return
448
+ end
449
+ end
450
+
451
+ self.reexec_pid = fork do
452
+ listener_fds = {}
453
+ 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=)
458
+ listener_fds[sock.fileno] = sock
459
+ end
460
+ ENV['UNICORN_FD'] = listener_fds.keys.join(',')
461
+ Dir.chdir(START_CTX[:cwd])
462
+ cmd = [ START_CTX[0] ].concat(START_CTX[:argv])
463
+
464
+ # avoid leaking FDs we don't know about, but let before_exec
465
+ # unset FD_CLOEXEC, if anything else in the app eventually
466
+ # relies on FD inheritence.
467
+ (3..1024).each do |io|
468
+ next if listener_fds.include?(io)
469
+ io = IO.for_fd(io) rescue next
470
+ prevent_autoclose(io)
471
+ io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
472
+ end
473
+
474
+ # exec(command, hash) works in at least 1.9.1+, but will only be
475
+ # required in 1.9.4/2.0.0 at earliest.
476
+ cmd << listener_fds if RUBY_VERSION >= "1.9.1"
477
+ logger.info "executing #{cmd.inspect} (in #{Dir.pwd})"
478
+ before_exec.call(self)
479
+ exec(*cmd)
480
+ end
481
+ proc_name 'master (old)'
482
+ end
483
+
484
+ # forcibly terminate all workers that haven't checked in in timeout seconds. The timeout is implemented using an unlinked File
485
+ def murder_lazy_workers
486
+ next_sleep = @timeout - 1
487
+ now = Time.now.to_i
488
+ WORKERS.dup.each_pair do |wpid, worker|
489
+ tick = worker.tick
490
+ 0 == tick and next # skip workers that haven't processed any clients
491
+ diff = now - tick
492
+ tmp = @timeout - diff
493
+ if tmp >= 0
494
+ next_sleep > tmp and next_sleep = tmp
495
+ next
496
+ end
497
+ next_sleep = 0
498
+ logger.error "worker=#{worker.nr} PID:#{wpid} timeout " \
499
+ "(#{diff}s > #{@timeout}s), killing"
500
+ kill_worker(:KILL, wpid) # take no prisoners for timeout violations
501
+ end
502
+ next_sleep <= 0 ? 1 : next_sleep
503
+ end
504
+
505
+ def after_fork_internal
506
+ @ready_pipe.close if @ready_pipe
507
+ Unicorn::Configurator::RACKUP.clear
508
+ @ready_pipe = @init_listeners = @before_exec = @before_fork = nil
509
+
510
+ srand # http://redmine.ruby-lang.org/issues/4338
511
+
512
+ # The OpenSSL PRNG is seeded with only the pid, and apps with frequently
513
+ # dying workers can recycle pids
514
+ OpenSSL::Random.seed(rand.to_s) if defined?(OpenSSL::Random)
515
+ end
516
+
517
+ def spawn_missing_workers
518
+ worker_nr = -1
519
+ until (worker_nr += 1) == @worker_processes
520
+ WORKERS.value?(worker_nr) and next
521
+ worker = Worker.new(worker_nr)
522
+ before_fork.call(self, worker)
523
+ if pid = fork
524
+ WORKERS[pid] = worker
525
+ else
526
+ after_fork_internal
527
+ t = Thread.new do
528
+ worker_loop(worker)
529
+ end
530
+ t.join
531
+ exit
532
+ end
533
+ end
534
+ rescue => e
535
+ @logger.error(e) rescue nil
536
+ exit!
537
+ end
538
+
539
+ def maintain_worker_count
540
+ (off = WORKERS.size - worker_processes) == 0 and return
541
+ off < 0 and return spawn_missing_workers
542
+ WORKERS.dup.each_pair { |wpid,w|
543
+ w.nr >= worker_processes and kill_worker(:QUIT, wpid) rescue nil
544
+ }
545
+ end
546
+
547
+ # if we get any error, try to write something back to the client
548
+ # assuming we haven't closed the socket, but don't get hung up
549
+ # if the socket is already closed or broken. We'll always ensure
550
+ # the socket is closed at the end of this function
551
+ def handle_error(client, e)
552
+ code = case e
553
+ when EOFError,Errno::ECONNRESET,Errno::EPIPE,Errno::ENOTCONN
554
+ # client disconnected on us and there's nothing we can do
555
+ when Unicorn::RequestURITooLongError
556
+ 414
557
+ when Unicorn::RequestEntityTooLargeError
558
+ 413
559
+ when Unicorn::HttpParserError # try to tell the client they're bad
560
+ 400
561
+ else
562
+ Unicorn.log_error(@logger, "app error", e)
563
+ 500
564
+ end
565
+ if code
566
+ client.kgio_trywrite(err_response(code, @request.response_start_sent))
567
+ end
568
+ client.close
569
+ rescue
570
+ end
571
+
572
+ def expect_100_response
573
+ if @request.response_start_sent
574
+ Unicorn::Const::EXPECT_100_RESPONSE_SUFFIXED
575
+ else
576
+ Unicorn::Const::EXPECT_100_RESPONSE
577
+ end
578
+ end
579
+
580
+ # once a client is accepted, it is processed in its entirety here
581
+ # in 3 easy steps: read request, call app, write app response
582
+ def process_client(client)
583
+ status, headers, body = @app.call(env = @request.read(client))
584
+ return if @request.hijacked?
585
+
586
+ if 100 == status.to_i
587
+ client.write(expect_100_response)
588
+ env.delete(Unicorn::Const::HTTP_EXPECT)
589
+ status, headers, body = @app.call(env)
590
+ return if @request.hijacked?
591
+ end
592
+ @request.headers? or headers = nil
593
+ http_response_write(client, status, headers, body,
594
+ @request.response_start_sent)
595
+ unless client.closed? # rack.hijack may've close this for us
596
+ client.shutdown # in case of fork() in Rack app
597
+ client.close # flush and uncork socket immediately, no keepalive
598
+ end
599
+ rescue => e
600
+ handle_error(client, e)
601
+ end
602
+
603
+ EXIT_SIGS = [ :QUIT, :TERM, :INT ]
604
+ WORKER_QUEUE_SIGS = QUEUE_SIGS - EXIT_SIGS
605
+
606
+ # gets rid of stuff the worker has no business keeping track of
607
+ # to free some resources and drops all sig handlers.
608
+ # traps for USR1, USR2, and HUP may be set in the after_fork Proc
609
+ # by the user.
610
+ def init_worker_process(worker)
611
+ # we'll re-trap :QUIT later for graceful shutdown iff we accept clients
612
+ EXIT_SIGS.each { |sig| trap(sig) { exit!(0) } }
613
+ exit!(0) if (SIG_QUEUE & EXIT_SIGS)[0]
614
+ WORKER_QUEUE_SIGS.each { |sig| trap(sig, nil) }
615
+ trap(:CHLD, 'DEFAULT')
616
+ SIG_QUEUE.clear
617
+ proc_name "worker[#{worker.nr}]"
618
+ START_CTX.clear
619
+ init_self_pipe!
620
+ WORKERS.clear
621
+ LISTENERS.each { |sock| sock.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
622
+ after_fork.call(self, worker) # can drop perms
623
+ worker.user(*user) if user.kind_of?(Array) && ! worker.switched
624
+ self.timeout /= 2.0 # halve it for select()
625
+ @config = nil
626
+ build_app! unless preload_app
627
+ ssl_enable!
628
+ @after_fork = @listener_opts = @orig_app = nil
629
+ end
630
+
631
+ def reopen_worker_logs(worker_nr)
632
+ logger.info "worker=#{worker_nr} reopening logs..."
633
+ Unicorn::Util.reopen_logs
634
+ logger.info "worker=#{worker_nr} done reopening logs"
635
+ init_self_pipe!
636
+ rescue => e
637
+ logger.error(e) rescue nil
638
+ exit!(77) # EX_NOPERM in sysexits.h
639
+ end
640
+
641
+ # runs inside each forked worker, this sits around and waits
642
+ # for connections and doesn't die until the parent dies (or is
643
+ # given a INT, QUIT, or TERM signal)
644
+ def worker_loop(worker)
645
+ ppid = master_pid
646
+ init_worker_process(worker)
647
+ nr = 0 # this becomes negative if we need to reopen logs
648
+ l = LISTENERS.dup
649
+ ready = l.dup
650
+
651
+ # closing anything we IO.select on will raise EBADF
652
+ trap(:USR1) { nr = -65536; SELF_PIPE[0].close rescue nil }
653
+ trap(:QUIT) { worker = nil; LISTENERS.each { |s| s.close rescue nil }.clear }
654
+ logger.info "worker=#{worker.nr} ready"
655
+
656
+ begin
657
+ nr < 0 and reopen_worker_logs(worker.nr)
658
+ nr = 0
659
+
660
+ worker.tick = Time.now.to_i
661
+ while sock = ready.shift
662
+ if client = sock.kgio_tryaccept
663
+ process_client(client)
664
+ nr += 1
665
+ worker.tick = Time.now.to_i
666
+ end
667
+ break if nr < 0
668
+ end
669
+
670
+ # make the following bet: if we accepted clients this round,
671
+ # we're probably reasonably busy, so avoid calling select()
672
+ # and do a speculative non-blocking accept() on ready listeners
673
+ # before we sleep again in select().
674
+ unless nr == 0 # (nr < 0) => reopen logs (unlikely)
675
+ ready = l.dup
676
+ redo
677
+ end
678
+
679
+ ppid == Process.ppid or return
680
+
681
+ # timeout used so we can detect parent death:
682
+ worker.tick = Time.now.to_i
683
+ ret = IO.select(l, nil, SELF_PIPE, @timeout) and ready = ret[0]
684
+ rescue => e
685
+ redo if nr < 0 && (Errno::EBADF === e || IOError === e) # reopen logs
686
+ Unicorn.log_error(@logger, "listen loop error", e) if worker
687
+ end while worker
688
+ end
689
+
690
+ # delivers a signal to a worker and fails gracefully if the worker
691
+ # is no longer running.
692
+ def kill_worker(signal, wpid)
693
+ Process.kill(signal, wpid)
694
+ rescue Errno::ESRCH
695
+ worker = WORKERS.delete(wpid) and worker.close rescue nil
696
+ end
697
+
698
+ # delivers a signal to each worker
699
+ def kill_each_worker(signal)
700
+ WORKERS.keys.each { |wpid| kill_worker(signal, wpid) }
701
+ end
702
+
703
+ # unlinks a PID file at given +path+ if it contains the current PID
704
+ # still potentially racy without locking the directory (which is
705
+ # non-portable and may interact badly with other programs), but the
706
+ # window for hitting the race condition is small
707
+ def unlink_pid_safe(path)
708
+ (File.read(path).to_i == $$ and File.unlink(path)) rescue nil
709
+ end
710
+
711
+ # returns a PID if a given path contains a non-stale PID file,
712
+ # nil otherwise.
713
+ def valid_pid?(path)
714
+ wpid = File.read(path).to_i
715
+ wpid <= 0 and return
716
+ Process.kill(0, wpid)
717
+ wpid
718
+ rescue Errno::EPERM
719
+ logger.info "pid=#{path} possibly stale, got EPERM signalling PID:#{wpid}"
720
+ nil
721
+ rescue Errno::ESRCH, Errno::ENOENT
722
+ # don't unlink stale pid files, racy without non-portable locking...
723
+ end
724
+
725
+ def load_config!
726
+ loaded_app = app
727
+ logger.info "reloading config_file=#{config.config_file}"
728
+ config[:listeners].replace(@init_listeners)
729
+ config.reload
730
+ config.commit!(self)
731
+ kill_each_worker(:QUIT)
732
+ Unicorn::Util.reopen_logs
733
+ self.app = orig_app
734
+ build_app! if preload_app
735
+ logger.info "done reloading config_file=#{config.config_file}"
736
+ rescue StandardError, LoadError, SyntaxError => e
737
+ Unicorn.log_error(@logger,
738
+ "error reloading config_file=#{config.config_file}", e)
739
+ self.app = loaded_app
740
+ end
741
+
742
+ # returns an array of string names for the given listener array
743
+ def listener_names(listeners = LISTENERS)
744
+ listeners.map { |io| sock_name(io) }
745
+ end
746
+
747
+ def build_app!
748
+ if app.respond_to?(:arity) && app.arity == 0
749
+ if defined?(Gem) && Gem.respond_to?(:refresh)
750
+ logger.info "Refreshing Gem list"
751
+ Gem.refresh
752
+ end
753
+ self.app = app.call
754
+ end
755
+ end
756
+
757
+ def proc_name(tag)
758
+ $0 = ([ File.basename(START_CTX[0]), tag
759
+ ]).concat(START_CTX[:argv]).join(' ')
760
+ end
761
+
762
+ def redirect_io(io, path)
763
+ File.open(path, 'ab') { |fp| io.reopen(fp) } if path
764
+ io.sync = true
765
+ end
766
+
767
+ def init_self_pipe!
768
+ SELF_PIPE.each { |io| io.close rescue nil }
769
+ SELF_PIPE.replace(Kgio::Pipe.new)
770
+ SELF_PIPE.each { |io| io.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC) }
771
+ end
772
+
773
+ def inherit_listeners!
774
+ # inherit sockets from parents, they need to be plain Socket objects
775
+ # before they become Kgio::UNIXServer or Kgio::TCPServer
776
+ inherited = ENV['UNICORN_FD'].to_s.split(/,/).map do |fd|
777
+ io = Socket.for_fd(fd.to_i)
778
+ set_server_sockopt(io, listener_opts[sock_name(io)])
779
+ prevent_autoclose(io)
780
+ logger.info "inherited addr=#{sock_name(io)} fd=#{fd}"
781
+ server_cast(io)
782
+ end
783
+
784
+ config_listeners = config[:listeners].dup
785
+ LISTENERS.replace(inherited)
786
+
787
+ # we start out with generic Socket objects that get cast to either
788
+ # Kgio::TCPServer or Kgio::UNIXServer objects; but since the Socket
789
+ # objects share the same OS-level file descriptor as the higher-level
790
+ # *Server objects; we need to prevent Socket objects from being
791
+ # garbage-collected
792
+ config_listeners -= listener_names
793
+ if config_listeners.empty? && LISTENERS.empty?
794
+ config_listeners << Unicorn::Const::DEFAULT_LISTEN
795
+ @init_listeners << Unicorn::Const::DEFAULT_LISTEN
796
+ START_CTX[:argv] << "-l#{Unicorn::Const::DEFAULT_LISTEN}"
797
+ end
798
+ NEW_LISTENERS.replace(config_listeners)
799
+ end
800
+
801
+ # call only after calling inherit_listeners!
802
+ # This binds any listeners we did NOT inherit from the parent
803
+ def bind_new_listeners!
804
+ NEW_LISTENERS.each { |addr| listen(addr) }
805
+ raise ArgumentError, "no listeners" if LISTENERS.empty?
806
+ NEW_LISTENERS.clear
807
+ end
808
+ end