puma-simon 3.7.1

Sign up to get free protection for your applications and to get access to all the features.
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