puma-simon 3.7.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 (157) hide show
  1. checksums.yaml +7 -0
  2. data/.github/issue_template.md +20 -0
  3. data/.gitignore +18 -0
  4. data/.hoeignore +12 -0
  5. data/.travis.yml +29 -0
  6. data/DEPLOYMENT.md +91 -0
  7. data/Gemfile +12 -0
  8. data/History.md +1254 -0
  9. data/LICENSE +26 -0
  10. data/Manifest.txt +78 -0
  11. data/README.md +353 -0
  12. data/Rakefile +158 -0
  13. data/Release.md +9 -0
  14. data/bin/puma +10 -0
  15. data/bin/puma-wild +31 -0
  16. data/bin/pumactl +12 -0
  17. data/docs/nginx.md +80 -0
  18. data/docs/signals.md +43 -0
  19. data/docs/systemd.md +197 -0
  20. data/examples/CA/cacert.pem +23 -0
  21. data/examples/CA/newcerts/cert_1.pem +19 -0
  22. data/examples/CA/newcerts/cert_2.pem +19 -0
  23. data/examples/CA/private/cakeypair.pem +30 -0
  24. data/examples/CA/serial +1 -0
  25. data/examples/config.rb +200 -0
  26. data/examples/plugins/redis_stop_puma.rb +46 -0
  27. data/examples/puma/cert_puma.pem +19 -0
  28. data/examples/puma/client-certs/ca.crt +19 -0
  29. data/examples/puma/client-certs/ca.key +27 -0
  30. data/examples/puma/client-certs/client.crt +19 -0
  31. data/examples/puma/client-certs/client.key +27 -0
  32. data/examples/puma/client-certs/client_expired.crt +19 -0
  33. data/examples/puma/client-certs/client_expired.key +27 -0
  34. data/examples/puma/client-certs/client_unknown.crt +19 -0
  35. data/examples/puma/client-certs/client_unknown.key +27 -0
  36. data/examples/puma/client-certs/generate.rb +78 -0
  37. data/examples/puma/client-certs/keystore.jks +0 -0
  38. data/examples/puma/client-certs/server.crt +19 -0
  39. data/examples/puma/client-certs/server.key +27 -0
  40. data/examples/puma/client-certs/server.p12 +0 -0
  41. data/examples/puma/client-certs/unknown_ca.crt +19 -0
  42. data/examples/puma/client-certs/unknown_ca.key +27 -0
  43. data/examples/puma/csr_puma.pem +11 -0
  44. data/examples/puma/keystore.jks +0 -0
  45. data/examples/puma/puma_keypair.pem +15 -0
  46. data/examples/qc_config.rb +13 -0
  47. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  48. data/ext/puma_http11/ext_help.h +15 -0
  49. data/ext/puma_http11/extconf.rb +15 -0
  50. data/ext/puma_http11/http11_parser.c +1069 -0
  51. data/ext/puma_http11/http11_parser.h +65 -0
  52. data/ext/puma_http11/http11_parser.java.rl +161 -0
  53. data/ext/puma_http11/http11_parser.rl +147 -0
  54. data/ext/puma_http11/http11_parser_common.rl +54 -0
  55. data/ext/puma_http11/io_buffer.c +155 -0
  56. data/ext/puma_http11/mini_ssl.c +457 -0
  57. data/ext/puma_http11/org/jruby/puma/Http11.java +234 -0
  58. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +473 -0
  59. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +339 -0
  60. data/ext/puma_http11/puma_http11.c +500 -0
  61. data/gemfiles/2.1-Gemfile +12 -0
  62. data/lib/puma.rb +15 -0
  63. data/lib/puma/accept_nonblock.rb +23 -0
  64. data/lib/puma/app/status.rb +66 -0
  65. data/lib/puma/binder.rb +402 -0
  66. data/lib/puma/cli.rb +220 -0
  67. data/lib/puma/client.rb +434 -0
  68. data/lib/puma/cluster.rb +510 -0
  69. data/lib/puma/commonlogger.rb +106 -0
  70. data/lib/puma/compat.rb +14 -0
  71. data/lib/puma/configuration.rb +364 -0
  72. data/lib/puma/const.rb +224 -0
  73. data/lib/puma/control_cli.rb +259 -0
  74. data/lib/puma/convenient.rb +23 -0
  75. data/lib/puma/daemon_ext.rb +31 -0
  76. data/lib/puma/delegation.rb +11 -0
  77. data/lib/puma/detect.rb +13 -0
  78. data/lib/puma/dsl.rb +486 -0
  79. data/lib/puma/events.rb +152 -0
  80. data/lib/puma/io_buffer.rb +7 -0
  81. data/lib/puma/java_io_buffer.rb +45 -0
  82. data/lib/puma/jruby_restart.rb +83 -0
  83. data/lib/puma/launcher.rb +410 -0
  84. data/lib/puma/minissl.rb +221 -0
  85. data/lib/puma/null_io.rb +42 -0
  86. data/lib/puma/plugin.rb +115 -0
  87. data/lib/puma/plugin/tmp_restart.rb +35 -0
  88. data/lib/puma/rack/backports/uri/common_193.rb +33 -0
  89. data/lib/puma/rack/builder.rb +298 -0
  90. data/lib/puma/rack/urlmap.rb +91 -0
  91. data/lib/puma/rack_default.rb +7 -0
  92. data/lib/puma/reactor.rb +210 -0
  93. data/lib/puma/runner.rb +171 -0
  94. data/lib/puma/server.rb +949 -0
  95. data/lib/puma/single.rb +112 -0
  96. data/lib/puma/state_file.rb +29 -0
  97. data/lib/puma/tcp_logger.rb +39 -0
  98. data/lib/puma/thread_pool.rb +297 -0
  99. data/lib/puma/util.rb +128 -0
  100. data/lib/rack/handler/puma.rb +78 -0
  101. data/puma.gemspec +52 -0
  102. data/test/ab_rs.rb +22 -0
  103. data/test/config.rb +2 -0
  104. data/test/config/app.rb +9 -0
  105. data/test/config/plugin.rb +1 -0
  106. data/test/config/settings.rb +2 -0
  107. data/test/config/state_file_testing_config.rb +14 -0
  108. data/test/hello-bind.ru +2 -0
  109. data/test/hello-delay.ru +3 -0
  110. data/test/hello-map.ru +3 -0
  111. data/test/hello-post.ru +4 -0
  112. data/test/hello-stuck.ru +1 -0
  113. data/test/hello-tcp.ru +5 -0
  114. data/test/hello.ru +1 -0
  115. data/test/hijack.ru +6 -0
  116. data/test/hijack2.ru +5 -0
  117. data/test/lobster.ru +4 -0
  118. data/test/shell/run.sh +24 -0
  119. data/test/shell/t1.rb +19 -0
  120. data/test/shell/t1_conf.rb +3 -0
  121. data/test/shell/t2.rb +17 -0
  122. data/test/shell/t2_conf.rb +6 -0
  123. data/test/shell/t3.rb +25 -0
  124. data/test/shell/t3_conf.rb +5 -0
  125. data/test/slow.ru +4 -0
  126. data/test/ssl_config.rb +4 -0
  127. data/test/test_app_status.rb +93 -0
  128. data/test/test_binder.rb +31 -0
  129. data/test/test_cli.rb +209 -0
  130. data/test/test_config.rb +95 -0
  131. data/test/test_events.rb +161 -0
  132. data/test/test_helper.rb +50 -0
  133. data/test/test_http10.rb +27 -0
  134. data/test/test_http11.rb +186 -0
  135. data/test/test_integration.rb +247 -0
  136. data/test/test_iobuffer.rb +39 -0
  137. data/test/test_minissl.rb +29 -0
  138. data/test/test_null_io.rb +49 -0
  139. data/test/test_persistent.rb +245 -0
  140. data/test/test_puma_server.rb +626 -0
  141. data/test/test_puma_server_ssl.rb +222 -0
  142. data/test/test_rack_handler.rb +57 -0
  143. data/test/test_rack_server.rb +138 -0
  144. data/test/test_tcp_logger.rb +39 -0
  145. data/test/test_tcp_rack.rb +36 -0
  146. data/test/test_thread_pool.rb +250 -0
  147. data/test/test_unix_socket.rb +35 -0
  148. data/test/test_web_server.rb +88 -0
  149. data/tools/jungle/README.md +9 -0
  150. data/tools/jungle/init.d/README.md +59 -0
  151. data/tools/jungle/init.d/puma +421 -0
  152. data/tools/jungle/init.d/run-puma +18 -0
  153. data/tools/jungle/upstart/README.md +61 -0
  154. data/tools/jungle/upstart/puma-manager.conf +31 -0
  155. data/tools/jungle/upstart/puma.conf +69 -0
  156. data/tools/trickletest.rb +45 -0
  157. metadata +297 -0
@@ -0,0 +1,152 @@
1
+ require 'puma/const'
2
+ require "puma/null_io"
3
+ require 'stringio'
4
+
5
+ module Puma
6
+ # The default implement of an event sink object used by Server
7
+ # for when certain kinds of events occur in the life of the server.
8
+ #
9
+ # The methods available are the events that the Server fires.
10
+ #
11
+ class Events
12
+ class DefaultFormatter
13
+ def call(str)
14
+ str
15
+ end
16
+ end
17
+
18
+ class PidFormatter
19
+ def call(str)
20
+ "[#{$$}] #{str}"
21
+ end
22
+ end
23
+
24
+ include Const
25
+
26
+ # Create an Events object that prints to +stdout+ and +stderr+.
27
+ #
28
+ def initialize(stdout, stderr)
29
+ @formatter = DefaultFormatter.new
30
+ @stdout = stdout
31
+ @stderr = stderr
32
+
33
+ @stdout.sync = true
34
+ @stderr.sync = true
35
+
36
+ @debug = ENV.key? 'PUMA_DEBUG'
37
+
38
+ @hooks = Hash.new { |h,k| h[k] = [] }
39
+ end
40
+
41
+ attr_reader :stdout, :stderr
42
+ attr_accessor :formatter
43
+
44
+ # Fire callbacks for the named hook
45
+ #
46
+ def fire(hook, *args)
47
+ @hooks[hook].each { |t| t.call(*args) }
48
+ end
49
+
50
+ # Register a callback for a given hook
51
+ #
52
+ def register(hook, obj=nil, &blk)
53
+ if obj and blk
54
+ raise "Specify either an object or a block, not both"
55
+ end
56
+
57
+ h = obj || blk
58
+
59
+ @hooks[hook] << h
60
+
61
+ h
62
+ end
63
+
64
+ # Write +str+ to +@stdout+
65
+ #
66
+ def log(str)
67
+ @stdout.puts format(str)
68
+ end
69
+
70
+ def write(str)
71
+ @stdout.write format(str)
72
+ end
73
+
74
+ def debug(str)
75
+ log("% #{str}") if @debug
76
+ end
77
+
78
+ # Write +str+ to +@stderr+
79
+ #
80
+ def error(str)
81
+ @stderr.puts format("ERROR: #{str}")
82
+ exit 1
83
+ end
84
+
85
+ def format(str)
86
+ formatter.call(str)
87
+ end
88
+
89
+ # An HTTP parse error has occurred.
90
+ # +server+ is the Server object, +env+ the request, and +error+ a
91
+ # parsing exception.
92
+ #
93
+ def parse_error(server, env, error)
94
+ @stderr.puts "#{Time.now}: HTTP parse error, malformed request (#{env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR]}): #{error.inspect}"
95
+ @stderr.puts "#{Time.now}: ENV: #{env.inspect}\n---\n"
96
+ end
97
+
98
+ # An SSL error has occurred.
99
+ # +server+ is the Server object, +peeraddr+ peer address, +peercert+
100
+ # any peer certificate (if present), and +error+ an exception object.
101
+ #
102
+ def ssl_error(server, peeraddr, peercert, error)
103
+ subject = peercert ? peercert.subject : nil
104
+ @stderr.puts "#{Time.now}: SSL error, peer: #{peeraddr}, peer cert: #{subject}, #{error.inspect}"
105
+ end
106
+
107
+ # An unknown error has occurred.
108
+ # +server+ is the Server object, +error+ an exception object,
109
+ # +kind+ some additional info, and +env+ the request.
110
+ #
111
+ def unknown_error(server, error, kind="Unknown", env=nil)
112
+ if error.respond_to? :render
113
+ error.render "#{Time.now}: #{kind} error", @stderr
114
+ else
115
+ if env
116
+ string_block = [ "#{Time.now}: #{kind} error handling request { #{env['REQUEST_METHOD']} #{env['PATH_INFO']} }" ]
117
+ string_block << error.inspect
118
+ else
119
+ string_block = [ "#{Time.now}: #{kind} error: #{error.inspect}" ]
120
+ end
121
+ string_block << error.backtrace
122
+ @stderr.puts string_block.join("\n")
123
+ end
124
+ end
125
+
126
+ def on_booted(&block)
127
+ register(:on_booted, &block)
128
+ end
129
+
130
+ def fire_on_booted!
131
+ fire(:on_booted)
132
+ end
133
+
134
+ DEFAULT = new(STDOUT, STDERR)
135
+
136
+ # Returns an Events object which writes its status to 2 StringIO
137
+ # objects.
138
+ #
139
+ def self.strings
140
+ Events.new StringIO.new, StringIO.new
141
+ end
142
+
143
+ def self.stdio
144
+ Events.new $stdout, $stderr
145
+ end
146
+
147
+ def self.null
148
+ n = NullIO.new
149
+ Events.new n, n
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,7 @@
1
+ require 'puma/detect'
2
+
3
+ if Puma.jruby?
4
+ require 'puma/java_io_buffer'
5
+ else
6
+ require 'puma/puma_http11'
7
+ end
@@ -0,0 +1,45 @@
1
+ require 'java'
2
+
3
+ # Conservative native JRuby/Java implementation of IOBuffer
4
+ # backed by a ByteArrayOutputStream and conversion between
5
+ # Ruby String and Java bytes
6
+ module Puma
7
+ class JavaIOBuffer < java.io.ByteArrayOutputStream
8
+ field_reader :buf
9
+ end
10
+
11
+ class IOBuffer
12
+ BUF_DEFAULT_SIZE = 4096
13
+
14
+ def initialize
15
+ @buf = JavaIOBuffer.new(BUF_DEFAULT_SIZE)
16
+ end
17
+
18
+ def reset
19
+ @buf.reset
20
+ end
21
+
22
+ def <<(str)
23
+ bytes = str.to_java_bytes
24
+ @buf.write(bytes, 0, bytes.length)
25
+ end
26
+
27
+ def append(*strs)
28
+ strs.each { |s| self << s; }
29
+ end
30
+
31
+ def to_s
32
+ String.from_java_bytes @buf.to_byte_array
33
+ end
34
+
35
+ alias_method :to_str, :to_s
36
+
37
+ def used
38
+ @buf.size
39
+ end
40
+
41
+ def capacity
42
+ @buf.buf.length
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,83 @@
1
+ require 'ffi'
2
+
3
+ module Puma
4
+ module JRubyRestart
5
+ extend FFI::Library
6
+ ffi_lib 'c'
7
+
8
+ attach_function :execlp, [:string, :varargs], :int
9
+ attach_function :chdir, [:string], :int
10
+ attach_function :fork, [], :int
11
+ attach_function :exit, [:int], :void
12
+ attach_function :setsid, [], :int
13
+
14
+ def self.chdir_exec(dir, argv)
15
+ chdir(dir)
16
+ cmd = argv.first
17
+ argv = ([:string] * argv.size).zip(argv).flatten
18
+ argv << :string
19
+ argv << nil
20
+ execlp(cmd, *argv)
21
+ raise SystemCallError.new(FFI.errno)
22
+ end
23
+
24
+ PermKey = 'PUMA_DAEMON_PERM'
25
+ RestartKey = 'PUMA_DAEMON_RESTART'
26
+
27
+ # Called to tell things "Your now always in daemon mode,
28
+ # don't try to reenter it."
29
+ #
30
+ def self.perm_daemonize
31
+ ENV[PermKey] = "1"
32
+ end
33
+
34
+ def self.daemon?
35
+ ENV.key?(PermKey) || ENV.key?(RestartKey)
36
+ end
37
+
38
+ def self.daemon_init
39
+ return true if ENV.key?(PermKey)
40
+
41
+ return false unless ENV.key? RestartKey
42
+
43
+ master = ENV[RestartKey]
44
+
45
+ # In case the master disappears early
46
+ begin
47
+ Process.kill "SIGUSR2", master.to_i
48
+ rescue SystemCallError => e
49
+ end
50
+
51
+ ENV[RestartKey] = ""
52
+
53
+ setsid
54
+
55
+ null = File.open "/dev/null", "w+"
56
+ STDIN.reopen null
57
+ STDOUT.reopen null
58
+ STDERR.reopen null
59
+
60
+ true
61
+ end
62
+
63
+ def self.daemon_start(dir, argv)
64
+ ENV[RestartKey] = Process.pid.to_s
65
+
66
+ if k = ENV['PUMA_JRUBY_DAEMON_OPTS']
67
+ ENV['JRUBY_OPTS'] = k
68
+ end
69
+
70
+ cmd = argv.first
71
+ argv = ([:string] * argv.size).zip(argv).flatten
72
+ argv << :string
73
+ argv << nil
74
+
75
+ chdir(dir)
76
+ ret = fork
77
+ return ret if ret != 0
78
+ execlp(cmd, *argv)
79
+ raise SystemCallError.new(FFI.errno)
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,410 @@
1
+ require 'puma/events'
2
+ require 'puma/detect'
3
+
4
+ require 'puma/cluster'
5
+ require 'puma/single'
6
+
7
+ require 'puma/const'
8
+
9
+ require 'puma/binder'
10
+
11
+ module Puma
12
+ # Puma::Launcher is the single entry point for starting a Puma server based on user
13
+ # configuration. It is responsible for taking user supplied arguments and resolving them
14
+ # with configuration in `config/puma.rb` or `config/puma/<env>.rb`.
15
+ #
16
+ # It is responsible for either launching a cluster of Puma workers or a single
17
+ # puma server.
18
+ class Launcher
19
+ KEYS_NOT_TO_PERSIST_IN_STATE = [
20
+ :logger, :lowlevel_error_handler,
21
+ :before_worker_shutdown, :before_worker_boot, :before_worker_fork,
22
+ :after_worker_boot, :before_fork, :on_restart
23
+ ]
24
+ # Returns an instance of Launcher
25
+ #
26
+ # +conf+ A Puma::Configuration object indicating how to run the server.
27
+ #
28
+ # +launcher_args+ A Hash that currently has one required key `:events`,
29
+ # this is expected to hold an object similar to an `Puma::Events.stdio`,
30
+ # this object will be responsible for broadcasting Puma's internal state
31
+ # to a logging destination. An optional key `:argv` can be supplied,
32
+ # this should be an array of strings, these arguments are re-used when
33
+ # restarting the puma server.
34
+ #
35
+ # Examples:
36
+ #
37
+ # conf = Puma::Configuration.new do |c|
38
+ # c.threads 1, 10
39
+ # c.app do |env|
40
+ # [200, {}, ["hello world"]]
41
+ # end
42
+ # end
43
+ # Puma::Launcher.new(conf, argv: Puma::Events.stdio).run
44
+ def initialize(conf, launcher_args={})
45
+ @runner = nil
46
+ @events = launcher_args[:events] || Events::DEFAULT
47
+ @argv = launcher_args[:argv] || []
48
+ @original_argv = @argv.dup
49
+ @config = conf
50
+
51
+ @binder = Binder.new(@events)
52
+ @binder.import_from_env
53
+
54
+ @environment = conf.environment
55
+
56
+ # Advertise the Configuration
57
+ Puma.cli_config = @config if defined?(Puma.cli_config)
58
+
59
+ @config.load
60
+
61
+ @options = @config.options
62
+
63
+ generate_restart_data
64
+
65
+ if clustered? && (Puma.jruby? || Puma.windows?)
66
+ unsupported 'worker mode not supported on JRuby or Windows'
67
+ end
68
+
69
+ if @options[:daemon] && Puma.windows?
70
+ unsupported 'daemon mode not supported on Windows'
71
+ end
72
+
73
+ Dir.chdir(@restart_dir)
74
+
75
+ prune_bundler if prune_bundler?
76
+
77
+ @environment = @options[:environment] if @options[:environment]
78
+ set_rack_environment
79
+
80
+ if clustered?
81
+ @events.formatter = Events::PidFormatter.new
82
+ @options[:logger] = @events
83
+
84
+ @runner = Cluster.new(self, @events)
85
+ else
86
+ @runner = Single.new(self, @events)
87
+ end
88
+
89
+ @status = :run
90
+ end
91
+
92
+ attr_reader :binder, :events, :config, :options, :restart_dir
93
+
94
+ # Return stats about the server
95
+ def stats
96
+ @runner.stats
97
+ end
98
+
99
+ # Write a state file that can be used by pumactl to control
100
+ # the server
101
+ def write_state
102
+ write_pid
103
+
104
+ path = @options[:state]
105
+ return unless path
106
+
107
+ require 'puma/state_file'
108
+
109
+ sf = StateFile.new
110
+ sf.pid = Process.pid
111
+ sf.control_url = @options[:control_url]
112
+ sf.control_auth_token = @options[:control_auth_token]
113
+
114
+ sf.save path
115
+ end
116
+
117
+ # Delete the configured pidfile
118
+ def delete_pidfile
119
+ path = @options[:pidfile]
120
+ File.unlink(path) if path && File.exist?(path)
121
+ end
122
+
123
+ # If configured, write the pid of the current process out
124
+ # to a file.
125
+ def write_pid
126
+ path = @options[:pidfile]
127
+ return unless path
128
+
129
+ File.open(path, 'w') { |f| f.puts Process.pid }
130
+ cur = Process.pid
131
+ at_exit do
132
+ delete_pidfile if cur == Process.pid
133
+ end
134
+ end
135
+
136
+ # Begin async shutdown of the server
137
+ def halt
138
+ @status = :halt
139
+ @runner.halt
140
+ end
141
+
142
+ # Begin async shutdown of the server gracefully
143
+ def stop
144
+ @status = :stop
145
+ @runner.stop
146
+ end
147
+
148
+ # Begin async restart of the server
149
+ def restart
150
+ @status = :restart
151
+ @runner.restart
152
+ end
153
+
154
+ # Begin a phased restart if supported
155
+ def phased_restart
156
+ unless @runner.respond_to?(:phased_restart) and @runner.phased_restart
157
+ log "* phased-restart called but not available, restarting normally."
158
+ return restart
159
+ end
160
+ true
161
+ end
162
+
163
+ # Run the server. This blocks until the server is stopped
164
+ def run
165
+ @config.clamp
166
+
167
+ @config.plugins.fire_starts self
168
+
169
+ setup_signals
170
+ set_process_title
171
+ @runner.run
172
+
173
+ case @status
174
+ when :halt
175
+ log "* Stopping immediately!"
176
+ when :run, :stop
177
+ graceful_stop
178
+ when :restart
179
+ log "* Restarting..."
180
+ @runner.before_restart
181
+ restart!
182
+ when :exit
183
+ # nothing
184
+ end
185
+ end
186
+
187
+ # Return which tcp port the launcher is using, if it's using TCP
188
+ def connected_port
189
+ @binder.connected_port
190
+ end
191
+
192
+ def restart_args
193
+ cmd = @options[:restart_cmd]
194
+ if cmd
195
+ cmd.split(' ') + @original_argv
196
+ else
197
+ @restart_argv
198
+ end
199
+ end
200
+
201
+ private
202
+
203
+ def reload_worker_directory
204
+ @runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
205
+ end
206
+
207
+ def restart!
208
+ @config.run_hooks :on_restart, self
209
+
210
+ if Puma.jruby?
211
+ close_binder_listeners
212
+
213
+ require 'puma/jruby_restart'
214
+ JRubyRestart.chdir_exec(@restart_dir, restart_args)
215
+ elsif Puma.windows?
216
+ close_binder_listeners
217
+
218
+ argv = restart_args
219
+ Dir.chdir(@restart_dir)
220
+ Kernel.exec(*argv)
221
+ else
222
+ redirects = {:close_others => true}
223
+ @binder.listeners.each_with_index do |(l, io), i|
224
+ ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
225
+ redirects[io.to_i] = io.to_i
226
+ end
227
+
228
+ argv = restart_args
229
+ Dir.chdir(@restart_dir)
230
+ argv += [redirects] if RUBY_VERSION >= '1.9'
231
+ Kernel.exec(*argv)
232
+ end
233
+ end
234
+
235
+ def prune_bundler
236
+ return unless defined?(Bundler)
237
+ puma = Bundler.rubygems.loaded_specs("puma")
238
+ dirs = puma.require_paths.map { |x| File.join(puma.full_gem_path, x) }
239
+ puma_lib_dir = dirs.detect { |x| File.exist? File.join(x, '../bin/puma-wild') }
240
+
241
+ unless puma_lib_dir
242
+ log "! Unable to prune Bundler environment, continuing"
243
+ return
244
+ end
245
+
246
+ deps = puma.runtime_dependencies.map do |d|
247
+ spec = Bundler.rubygems.loaded_specs(d.name)
248
+ "#{d.name}:#{spec.version.to_s}"
249
+ end
250
+
251
+ log '* Pruning Bundler environment'
252
+ home = ENV['GEM_HOME']
253
+ Bundler.with_clean_env do
254
+ ENV['GEM_HOME'] = home
255
+ ENV['PUMA_BUNDLER_PRUNED'] = '1'
256
+ wild = File.expand_path(File.join(puma_lib_dir, "../bin/puma-wild"))
257
+ args = [Gem.ruby, wild, '-I', dirs.join(':'), deps.join(',')] + @original_argv
258
+ # Ruby 2.0+ defaults to true which breaks socket activation
259
+ args += [{:close_others => false}] if RUBY_VERSION >= '2.0'
260
+ Kernel.exec(*args)
261
+ end
262
+ end
263
+
264
+ def log(str)
265
+ @events.log str
266
+ end
267
+
268
+ def clustered?
269
+ (@options[:workers] || 0) > 0
270
+ end
271
+
272
+ def unsupported(str)
273
+ @events.error(str)
274
+ raise UnsupportedOption
275
+ end
276
+
277
+ def graceful_stop
278
+ @runner.stop_blocked
279
+ log "=== puma shutdown: #{Time.now} ==="
280
+ log "- Goodbye!"
281
+ end
282
+
283
+ def set_process_title
284
+ Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
285
+ end
286
+
287
+ def title
288
+ buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
289
+ buffer << " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
290
+ buffer
291
+ end
292
+
293
+ def set_rack_environment
294
+ @options[:environment] = environment
295
+ ENV['RACK_ENV'] = environment
296
+ end
297
+
298
+ def environment
299
+ @environment
300
+ end
301
+
302
+ def prune_bundler?
303
+ @options[:prune_bundler] && clustered? && !@options[:preload_app]
304
+ end
305
+
306
+ def close_binder_listeners
307
+ @binder.listeners.each do |l, io|
308
+ io.close
309
+ uri = URI.parse(l)
310
+ next unless uri.scheme == 'unix'
311
+ File.unlink("#{uri.host}#{uri.path}")
312
+ end
313
+ end
314
+
315
+
316
+ def generate_restart_data
317
+ if dir = @options[:directory]
318
+ @restart_dir = dir
319
+
320
+ elsif Puma.windows?
321
+ # I guess the value of PWD is garbage on windows so don't bother
322
+ # using it.
323
+ @restart_dir = Dir.pwd
324
+
325
+ # Use the same trick as unicorn, namely favor PWD because
326
+ # it will contain an unresolved symlink, useful for when
327
+ # the pwd is /data/releases/current.
328
+ elsif dir = ENV['PWD']
329
+ s_env = File.stat(dir)
330
+ s_pwd = File.stat(Dir.pwd)
331
+
332
+ if s_env.ino == s_pwd.ino and (Puma.jruby? or s_env.dev == s_pwd.dev)
333
+ @restart_dir = dir
334
+ end
335
+ end
336
+
337
+ @restart_dir ||= Dir.pwd
338
+
339
+ require 'rubygems'
340
+
341
+ # if $0 is a file in the current directory, then restart
342
+ # it the same, otherwise add -S on there because it was
343
+ # picked up in PATH.
344
+ #
345
+ if File.exist?($0)
346
+ arg0 = [Gem.ruby, $0]
347
+ else
348
+ arg0 = [Gem.ruby, "-S", $0]
349
+ end
350
+
351
+ # Detect and reinject -Ilib from the command line
352
+ lib = File.expand_path "lib"
353
+ arg0[1,0] = ["-I", lib] if $:[0] == lib
354
+
355
+ if defined? Puma::WILD_ARGS
356
+ @restart_argv = arg0 + Puma::WILD_ARGS + @original_argv
357
+ else
358
+ @restart_argv = arg0 + @original_argv
359
+ end
360
+ end
361
+
362
+ def setup_signals
363
+ begin
364
+ Signal.trap "SIGUSR2" do
365
+ restart
366
+ end
367
+ rescue Exception
368
+ log "*** SIGUSR2 not implemented, signal based restart unavailable!"
369
+ end
370
+
371
+ unless Puma.jruby?
372
+ begin
373
+ Signal.trap "SIGUSR1" do
374
+ phased_restart
375
+ end
376
+ rescue Exception
377
+ log "*** SIGUSR1 not implemented, signal based restart unavailable!"
378
+ end
379
+ end
380
+
381
+ begin
382
+ Signal.trap "SIGTERM" do
383
+ stop
384
+ end
385
+ rescue Exception
386
+ log "*** SIGTERM not implemented, signal based gracefully stopping unavailable!"
387
+ end
388
+
389
+ begin
390
+ Signal.trap "SIGHUP" do
391
+ if @runner.redirected_io?
392
+ @runner.redirect_io
393
+ else
394
+ stop
395
+ end
396
+ end
397
+ rescue Exception
398
+ log "*** SIGHUP not implemented, signal based logs reopening unavailable!"
399
+ end
400
+
401
+ if Puma.jruby?
402
+ Signal.trap("INT") do
403
+ @status = :exit
404
+ graceful_stop
405
+ exit
406
+ end
407
+ end
408
+ end
409
+ end
410
+ end