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