puma 4.3.12 → 6.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puma might be problematic. Click here for more details.

Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1618 -521
  3. data/LICENSE +23 -20
  4. data/README.md +130 -42
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +63 -26
  7. data/docs/compile_options.md +55 -0
  8. data/docs/deployment.md +60 -69
  9. data/docs/fork_worker.md +31 -0
  10. data/docs/jungle/README.md +9 -0
  11. data/{tools → docs}/jungle/rc.d/README.md +1 -1
  12. data/{tools → docs}/jungle/rc.d/puma +2 -2
  13. data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  14. data/docs/kubernetes.md +66 -0
  15. data/docs/nginx.md +2 -2
  16. data/docs/plugins.md +15 -15
  17. data/docs/rails_dev_mode.md +28 -0
  18. data/docs/restart.md +46 -23
  19. data/docs/signals.md +13 -11
  20. data/docs/stats.md +142 -0
  21. data/docs/systemd.md +85 -128
  22. data/docs/testing_benchmarks_local_files.md +150 -0
  23. data/docs/testing_test_rackup_ci_files.md +36 -0
  24. data/ext/puma_http11/PumaHttp11Service.java +2 -4
  25. data/ext/puma_http11/ext_help.h +1 -1
  26. data/ext/puma_http11/extconf.rb +49 -12
  27. data/ext/puma_http11/http11_parser.c +46 -48
  28. data/ext/puma_http11/http11_parser.h +2 -2
  29. data/ext/puma_http11/http11_parser.java.rl +3 -3
  30. data/ext/puma_http11/http11_parser.rl +3 -3
  31. data/ext/puma_http11/http11_parser_common.rl +2 -2
  32. data/ext/puma_http11/mini_ssl.c +250 -93
  33. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  34. data/ext/puma_http11/org/jruby/puma/Http11.java +6 -6
  35. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +4 -6
  36. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +241 -96
  37. data/ext/puma_http11/puma_http11.c +46 -57
  38. data/lib/puma/app/status.rb +52 -38
  39. data/lib/puma/binder.rb +232 -119
  40. data/lib/puma/cli.rb +33 -33
  41. data/lib/puma/client.rb +129 -88
  42. data/lib/puma/cluster/worker.rb +175 -0
  43. data/lib/puma/cluster/worker_handle.rb +97 -0
  44. data/lib/puma/cluster.rb +224 -231
  45. data/lib/puma/commonlogger.rb +2 -2
  46. data/lib/puma/configuration.rb +112 -87
  47. data/lib/puma/const.rb +86 -91
  48. data/lib/puma/control_cli.rb +99 -79
  49. data/lib/puma/detect.rb +31 -2
  50. data/lib/puma/dsl.rb +426 -110
  51. data/lib/puma/error_logger.rb +112 -0
  52. data/lib/puma/events.rb +16 -115
  53. data/lib/puma/io_buffer.rb +44 -2
  54. data/lib/puma/jruby_restart.rb +2 -59
  55. data/lib/puma/json_serialization.rb +96 -0
  56. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  57. data/lib/puma/launcher.rb +170 -148
  58. data/lib/puma/log_writer.rb +137 -0
  59. data/lib/puma/minissl/context_builder.rb +35 -19
  60. data/lib/puma/minissl.rb +213 -55
  61. data/lib/puma/null_io.rb +18 -1
  62. data/lib/puma/plugin/tmp_restart.rb +1 -1
  63. data/lib/puma/plugin.rb +3 -12
  64. data/lib/puma/rack/builder.rb +5 -9
  65. data/lib/puma/rack_default.rb +1 -1
  66. data/lib/puma/reactor.rb +85 -369
  67. data/lib/puma/request.rb +644 -0
  68. data/lib/puma/runner.rb +86 -76
  69. data/lib/puma/server.rb +306 -793
  70. data/lib/puma/single.rb +18 -74
  71. data/lib/puma/state_file.rb +45 -8
  72. data/lib/puma/systemd.rb +47 -0
  73. data/lib/puma/thread_pool.rb +136 -68
  74. data/lib/puma/util.rb +21 -4
  75. data/lib/puma.rb +54 -7
  76. data/lib/rack/handler/puma.rb +11 -12
  77. data/tools/{docker/Dockerfile → Dockerfile} +1 -1
  78. metadata +31 -23
  79. data/docs/tcp_mode.md +0 -96
  80. data/ext/puma_http11/io_buffer.c +0 -155
  81. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
  82. data/lib/puma/accept_nonblock.rb +0 -29
  83. data/lib/puma/tcp_logger.rb +0 -41
  84. data/tools/jungle/README.md +0 -19
  85. data/tools/jungle/init.d/README.md +0 -61
  86. data/tools/jungle/init.d/puma +0 -421
  87. data/tools/jungle/init.d/run-puma +0 -18
  88. data/tools/jungle/upstart/README.md +0 -61
  89. data/tools/jungle/upstart/puma-manager.conf +0 -31
  90. data/tools/jungle/upstart/puma.conf +0 -69
@@ -11,7 +11,33 @@ require 'socket'
11
11
  module Puma
12
12
  class ControlCLI
13
13
 
14
- COMMANDS = %w{halt restart phased-restart start stats status stop reload-worker-directory gc gc-stats}
14
+ # values must be string or nil
15
+ # value of `nil` means command cannot be processed via signal
16
+ # @version 5.0.3
17
+ CMD_PATH_SIG_MAP = {
18
+ 'gc' => nil,
19
+ 'gc-stats' => nil,
20
+ 'halt' => 'SIGQUIT',
21
+ 'info' => 'SIGINFO',
22
+ 'phased-restart' => 'SIGUSR1',
23
+ 'refork' => 'SIGURG',
24
+ 'reload-worker-directory' => nil,
25
+ 'reopen-log' => 'SIGHUP',
26
+ 'restart' => 'SIGUSR2',
27
+ 'start' => nil,
28
+ 'stats' => nil,
29
+ 'status' => '',
30
+ 'stop' => 'SIGTERM',
31
+ 'thread-backtraces' => nil,
32
+ 'worker-count-down' => 'SIGTTOU',
33
+ 'worker-count-up' => 'SIGTTIN'
34
+ }.freeze
35
+
36
+ # commands that cannot be used in a request
37
+ NO_REQ_COMMANDS = %w[info reopen-log worker-count-down worker-count-up].freeze
38
+
39
+ # @version 5.0.0
40
+ PRINTABLE_COMMANDS = %w[gc-stats stats thread-backtraces].freeze
15
41
 
16
42
  def initialize(argv, stdout=STDOUT, stderr=STDERR)
17
43
  @state = nil
@@ -22,7 +48,7 @@ module Puma
22
48
  @control_auth_token = nil
23
49
  @config_file = nil
24
50
  @command = nil
25
- @environment = ENV['RACK_ENV']
51
+ @environment = ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV']
26
52
 
27
53
  @argv = argv.dup
28
54
  @stdout = stdout
@@ -30,7 +56,7 @@ module Puma
30
56
  @cli_options = {}
31
57
 
32
58
  opts = OptionParser.new do |o|
33
- o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{COMMANDS.join("|")})"
59
+ o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{CMD_PATH_SIG_MAP.keys.join("|")})"
34
60
 
35
61
  o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
36
62
  @state = arg
@@ -71,7 +97,7 @@ module Puma
71
97
  end
72
98
 
73
99
  o.on_tail("-V", "--version", "Show version") do
74
- puts Const::PUMA_VERSION
100
+ @stdout.puts Const::PUMA_VERSION
75
101
  exit
76
102
  end
77
103
  end
@@ -81,6 +107,15 @@ module Puma
81
107
 
82
108
  @command = argv.shift
83
109
 
110
+ # check presence of command
111
+ unless @command
112
+ raise "Available commands: #{CMD_PATH_SIG_MAP.keys.join(", ")}"
113
+ end
114
+
115
+ unless CMD_PATH_SIG_MAP.key? @command
116
+ raise "Invalid command: #{@command}"
117
+ end
118
+
84
119
  unless @config_file == '-'
85
120
  environment = @environment || 'development'
86
121
 
@@ -99,16 +134,6 @@ module Puma
99
134
  @pidfile ||= config.options[:pidfile]
100
135
  end
101
136
  end
102
-
103
- # check present of command
104
- unless @command
105
- raise "Available commands: #{COMMANDS.join(", ")}"
106
- end
107
-
108
- unless COMMANDS.include? @command
109
- raise "Invalid command: #{@command}"
110
- end
111
-
112
137
  rescue => e
113
138
  @stdout.puts e.message
114
139
  exit 1
@@ -132,7 +157,7 @@ module Puma
132
157
  @pid = sf.pid
133
158
  elsif @pidfile
134
159
  # get pid from pid_file
135
- File.open(@pidfile) { |f| @pid = f.read.to_i }
160
+ @pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
136
161
  end
137
162
  end
138
163
 
@@ -140,23 +165,27 @@ module Puma
140
165
  uri = URI.parse @control_url
141
166
 
142
167
  # create server object by scheme
143
- server = case uri.scheme
144
- when "ssl"
145
- require 'openssl'
146
- OpenSSL::SSL::SSLSocket.new(
147
- TCPSocket.new(uri.host, uri.port),
148
- OpenSSL::SSL::SSLContext.new
149
- ).tap(&:connect)
150
- when "tcp"
151
- TCPSocket.new uri.host, uri.port
152
- when "unix"
153
- UNIXSocket.new "#{uri.host}#{uri.path}"
154
- else
155
- raise "Invalid scheme: #{uri.scheme}"
156
- end
157
-
158
- if @command == "status"
159
- message "Puma is started"
168
+ server =
169
+ case uri.scheme
170
+ when 'ssl'
171
+ require 'openssl'
172
+ OpenSSL::SSL::SSLSocket.new(
173
+ TCPSocket.new(uri.host, uri.port),
174
+ OpenSSL::SSL::SSLContext.new)
175
+ .tap { |ssl| ssl.sync_close = true } # default is false
176
+ .tap(&:connect)
177
+ when 'tcp'
178
+ TCPSocket.new uri.host, uri.port
179
+ when 'unix'
180
+ # check for abstract UNIXSocket
181
+ UNIXSocket.new(@control_url.start_with?('unix://@') ?
182
+ "\0#{uri.host}#{uri.path}" : "#{uri.host}#{uri.path}")
183
+ else
184
+ raise "Invalid scheme: #{uri.scheme}"
185
+ end
186
+
187
+ if @command == 'status'
188
+ message 'Puma is started'
160
189
  else
161
190
  url = "/#{@command}"
162
191
 
@@ -164,10 +193,10 @@ module Puma
164
193
  url = url + "?token=#{@control_auth_token}"
165
194
  end
166
195
 
167
- server << "GET #{url} HTTP/1.0\r\n\r\n"
196
+ server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
168
197
 
169
198
  unless data = server.read
170
- raise "Server closed connection before responding"
199
+ raise 'Server closed connection before responding'
171
200
  end
172
201
 
173
202
  response = data.split("\r\n")
@@ -176,67 +205,59 @@ module Puma
176
205
  raise "Server sent empty response"
177
206
  end
178
207
 
179
- (@http,@code,@message) = response.first.split(" ",3)
208
+ @http, @code, @message = response.first.split(' ',3)
180
209
 
181
- if @code == "403"
182
- raise "Unauthorized access to server (wrong auth token)"
183
- elsif @code == "404"
210
+ if @code == '403'
211
+ raise 'Unauthorized access to server (wrong auth token)'
212
+ elsif @code == '404'
184
213
  raise "Command error: #{response.last}"
185
- elsif @code != "200"
214
+ elsif @code != '200'
186
215
  raise "Bad response from server: #{@code}"
187
216
  end
188
217
 
189
218
  message "Command #{@command} sent success"
190
- message response.last if @command == "stats" || @command == "gc-stats"
219
+ message response.last if PRINTABLE_COMMANDS.include?(@command)
191
220
  end
192
221
  ensure
193
- server.close if server && !server.closed?
222
+ if server
223
+ if uri.scheme == 'ssl'
224
+ server.sysclose
225
+ else
226
+ server.close unless server.closed?
227
+ end
228
+ end
194
229
  end
195
230
 
196
231
  def send_signal
197
232
  unless @pid
198
- raise "Neither pid nor control url available"
233
+ raise 'Neither pid nor control url available'
199
234
  end
200
235
 
201
236
  begin
237
+ sig = CMD_PATH_SIG_MAP[@command]
202
238
 
203
- case @command
204
- when "restart"
205
- Process.kill "SIGUSR2", @pid
206
-
207
- when "halt"
208
- Process.kill "QUIT", @pid
209
-
210
- when "stop"
211
- Process.kill "SIGTERM", @pid
212
-
213
- when "stats"
214
- puts "Stats not available via pid only"
215
- return
216
-
217
- when "reload-worker-directory"
218
- puts "reload-worker-directory not available via pid only"
239
+ if sig.nil?
240
+ @stdout.puts "'#{@command}' not available via pid only"
241
+ @stdout.flush unless @stdout.sync
219
242
  return
220
-
221
- when "phased-restart"
222
- Process.kill "SIGUSR1", @pid
223
-
224
- when "status"
243
+ elsif sig.start_with? 'SIG'
244
+ if Signal.list.key? sig.sub(/\ASIG/, '')
245
+ Process.kill sig, @pid
246
+ else
247
+ raise "Signal '#{sig}' not available'"
248
+ end
249
+ elsif @command == 'status'
225
250
  begin
226
251
  Process.kill 0, @pid
227
- puts "Puma is started"
252
+ @stdout.puts 'Puma is started'
253
+ @stdout.flush unless @stdout.sync
228
254
  rescue Errno::ESRCH
229
- raise "Puma is not running"
255
+ raise 'Puma is not running'
230
256
  end
231
-
232
- return
233
-
234
- else
235
257
  return
236
258
  end
237
-
238
259
  rescue SystemCallError
239
- if @command == "restart"
260
+ if @command == 'restart'
240
261
  start
241
262
  else
242
263
  raise "No pid '#{@pid}' found"
@@ -247,14 +268,13 @@ module Puma
247
268
  end
248
269
 
249
270
  def run
250
- return start if @command == "start"
251
-
271
+ return start if @command == 'start'
252
272
  prepare_configuration
253
273
 
254
- if Puma.windows?
274
+ if Puma.windows? || @control_url && !NO_REQ_COMMANDS.include?(@command)
255
275
  send_request
256
276
  else
257
- @control_url ? send_request : send_signal
277
+ send_signal
258
278
  end
259
279
 
260
280
  rescue => e
@@ -262,9 +282,9 @@ module Puma
262
282
  exit 1
263
283
  end
264
284
 
265
- private
285
+ private
266
286
  def start
267
- require 'puma/cli'
287
+ require_relative 'cli'
268
288
 
269
289
  run_args = []
270
290
 
@@ -276,13 +296,13 @@ module Puma
276
296
  run_args += ["-C", @config_file] if @config_file
277
297
  run_args += ["-e", @environment] if @environment
278
298
 
279
- events = Puma::Events.new @stdout, @stderr
299
+ log_writer = Puma::LogWriter.new(@stdout, @stderr)
280
300
 
281
301
  # replace $0 because puma use it to generate restart command
282
302
  puma_cmd = $0.gsub(/pumactl$/, 'puma')
283
303
  $0 = puma_cmd if File.exist?(puma_cmd)
284
304
 
285
- cli = Puma::CLI.new run_args, events
305
+ cli = Puma::CLI.new run_args, log_writer
286
306
  cli.run
287
307
  end
288
308
  end
data/lib/puma/detect.rb CHANGED
@@ -1,15 +1,44 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # This file can be loaded independently of puma.rb, so it cannot have any code
4
+ # that assumes puma.rb is loaded.
5
+
6
+
3
7
  module Puma
4
- IS_JRUBY = defined?(JRUBY_VERSION)
8
+ # @version 5.2.1
9
+ HAS_FORK = ::Process.respond_to? :fork
10
+
11
+ HAS_NATIVE_IO_WAIT = ::IO.public_instance_methods(false).include? :wait_readable
12
+
13
+ IS_JRUBY = Object.const_defined? :JRUBY_VERSION
14
+
15
+ IS_OSX = RUBY_PLATFORM.include? 'darwin'
16
+
17
+ IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/) ||
18
+ IS_JRUBY && RUBY_DESCRIPTION.include?('mswin')
19
+
20
+ # @version 5.2.0
21
+ IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
5
22
 
6
23
  def self.jruby?
7
24
  IS_JRUBY
8
25
  end
9
26
 
10
- IS_WINDOWS = RUBY_PLATFORM =~ /mswin|ming|cygwin/
27
+ def self.osx?
28
+ IS_OSX
29
+ end
11
30
 
12
31
  def self.windows?
13
32
  IS_WINDOWS
14
33
  end
34
+
35
+ # @version 5.0.0
36
+ def self.mri?
37
+ IS_MRI
38
+ end
39
+
40
+ # @version 5.0.0
41
+ def self.forkable?
42
+ HAS_FORK
43
+ end
15
44
  end