puma 3.12.1 → 5.6.7

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 (94) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1608 -447
  3. data/LICENSE +23 -20
  4. data/README.md +175 -63
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +59 -21
  7. data/docs/compile_options.md +21 -0
  8. data/docs/deployment.md +69 -58
  9. data/docs/fork_worker.md +33 -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/docs/kubernetes.md +66 -0
  14. data/docs/nginx.md +1 -1
  15. data/docs/plugins.md +22 -12
  16. data/docs/rails_dev_mode.md +28 -0
  17. data/docs/restart.md +47 -22
  18. data/docs/signals.md +13 -11
  19. data/docs/stats.md +142 -0
  20. data/docs/systemd.md +95 -120
  21. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  22. data/ext/puma_http11/ext_help.h +1 -1
  23. data/ext/puma_http11/extconf.rb +57 -2
  24. data/ext/puma_http11/http11_parser.c +105 -117
  25. data/ext/puma_http11/http11_parser.h +1 -1
  26. data/ext/puma_http11/http11_parser.java.rl +22 -38
  27. data/ext/puma_http11/http11_parser.rl +4 -2
  28. data/ext/puma_http11/http11_parser_common.rl +4 -4
  29. data/ext/puma_http11/mini_ssl.c +339 -98
  30. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  31. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  32. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
  33. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +124 -71
  34. data/ext/puma_http11/puma_http11.c +35 -51
  35. data/lib/puma/app/status.rb +71 -49
  36. data/lib/puma/binder.rb +234 -137
  37. data/lib/puma/cli.rb +28 -18
  38. data/lib/puma/client.rb +350 -230
  39. data/lib/puma/cluster/worker.rb +173 -0
  40. data/lib/puma/cluster/worker_handle.rb +94 -0
  41. data/lib/puma/cluster.rb +247 -232
  42. data/lib/puma/commonlogger.rb +2 -2
  43. data/lib/puma/configuration.rb +61 -51
  44. data/lib/puma/const.rb +42 -21
  45. data/lib/puma/control_cli.rb +115 -67
  46. data/lib/puma/detect.rb +29 -2
  47. data/lib/puma/dsl.rb +619 -123
  48. data/lib/puma/error_logger.rb +104 -0
  49. data/lib/puma/events.rb +55 -31
  50. data/lib/puma/io_buffer.rb +7 -5
  51. data/lib/puma/jruby_restart.rb +0 -58
  52. data/lib/puma/json_serialization.rb +96 -0
  53. data/lib/puma/launcher.rb +193 -69
  54. data/lib/puma/minissl/context_builder.rb +81 -0
  55. data/lib/puma/minissl.rb +170 -65
  56. data/lib/puma/null_io.rb +18 -1
  57. data/lib/puma/plugin/tmp_restart.rb +2 -0
  58. data/lib/puma/plugin.rb +7 -13
  59. data/lib/puma/queue_close.rb +26 -0
  60. data/lib/puma/rack/builder.rb +3 -5
  61. data/lib/puma/rack/urlmap.rb +2 -0
  62. data/lib/puma/rack_default.rb +2 -0
  63. data/lib/puma/reactor.rb +85 -316
  64. data/lib/puma/request.rb +476 -0
  65. data/lib/puma/runner.rb +48 -55
  66. data/lib/puma/server.rb +305 -695
  67. data/lib/puma/single.rb +11 -67
  68. data/lib/puma/state_file.rb +48 -8
  69. data/lib/puma/systemd.rb +46 -0
  70. data/lib/puma/thread_pool.rb +132 -82
  71. data/lib/puma/util.rb +33 -10
  72. data/lib/puma.rb +56 -0
  73. data/lib/rack/handler/puma.rb +5 -6
  74. data/lib/rack/version_restriction.rb +15 -0
  75. data/tools/Dockerfile +16 -0
  76. data/tools/trickletest.rb +0 -1
  77. metadata +46 -29
  78. data/ext/puma_http11/io_buffer.c +0 -155
  79. data/lib/puma/accept_nonblock.rb +0 -23
  80. data/lib/puma/compat.rb +0 -14
  81. data/lib/puma/convenient.rb +0 -25
  82. data/lib/puma/daemon_ext.rb +0 -33
  83. data/lib/puma/delegation.rb +0 -13
  84. data/lib/puma/java_io_buffer.rb +0 -47
  85. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  86. data/lib/puma/tcp_logger.rb +0 -41
  87. data/tools/jungle/README.md +0 -19
  88. data/tools/jungle/init.d/README.md +0 -61
  89. data/tools/jungle/init.d/puma +0 -421
  90. data/tools/jungle/init.d/run-puma +0 -18
  91. data/tools/jungle/upstart/README.md +0 -61
  92. data/tools/jungle/upstart/puma-manager.conf +0 -31
  93. data/tools/jungle/upstart/puma.conf +0 -69
  94. /data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
@@ -0,0 +1,173 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ class Cluster < Puma::Runner
5
+ # This class is instantiated by the `Puma::Cluster` and represents a single
6
+ # worker process.
7
+ #
8
+ # At the core of this class is running an instance of `Puma::Server` which
9
+ # gets created via the `start_server` method from the `Puma::Runner` class
10
+ # that this inherits from.
11
+ class Worker < Puma::Runner
12
+ attr_reader :index, :master
13
+
14
+ def initialize(index:, master:, launcher:, pipes:, server: nil)
15
+ super launcher, launcher.events
16
+
17
+ @index = index
18
+ @master = master
19
+ @launcher = launcher
20
+ @options = launcher.options
21
+ @check_pipe = pipes[:check_pipe]
22
+ @worker_write = pipes[:worker_write]
23
+ @fork_pipe = pipes[:fork_pipe]
24
+ @wakeup = pipes[:wakeup]
25
+ @server = server
26
+ end
27
+
28
+ def run
29
+ title = "puma: cluster worker #{index}: #{master}"
30
+ title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
31
+ $0 = title
32
+
33
+ Signal.trap "SIGINT", "IGNORE"
34
+ Signal.trap "SIGCHLD", "DEFAULT"
35
+
36
+ Thread.new do
37
+ Puma.set_thread_name "wrkr check"
38
+ @check_pipe.wait_readable
39
+ log "! Detected parent died, dying"
40
+ exit! 1
41
+ end
42
+
43
+ # If we're not running under a Bundler context, then
44
+ # report the info about the context we will be using
45
+ if !ENV['BUNDLE_GEMFILE']
46
+ if File.exist?("Gemfile")
47
+ log "+ Gemfile in context: #{File.expand_path("Gemfile")}"
48
+ elsif File.exist?("gems.rb")
49
+ log "+ Gemfile in context: #{File.expand_path("gems.rb")}"
50
+ end
51
+ end
52
+
53
+ # Invoke any worker boot hooks so they can get
54
+ # things in shape before booting the app.
55
+ @launcher.config.run_hooks :before_worker_boot, index, @launcher.events
56
+
57
+ begin
58
+ server = @server ||= start_server
59
+ rescue Exception => e
60
+ log "! Unable to start worker"
61
+ log e.backtrace[0]
62
+ exit 1
63
+ end
64
+
65
+ restart_server = Queue.new << true << false
66
+
67
+ fork_worker = @options[:fork_worker] && index == 0
68
+
69
+ if fork_worker
70
+ restart_server.clear
71
+ worker_pids = []
72
+ Signal.trap "SIGCHLD" do
73
+ wakeup! if worker_pids.reject! do |p|
74
+ Process.wait(p, Process::WNOHANG) rescue true
75
+ end
76
+ end
77
+
78
+ Thread.new do
79
+ Puma.set_thread_name "wrkr fork"
80
+ while (idx = @fork_pipe.gets)
81
+ idx = idx.to_i
82
+ if idx == -1 # stop server
83
+ if restart_server.length > 0
84
+ restart_server.clear
85
+ server.begin_restart(true)
86
+ @launcher.config.run_hooks :before_refork, nil, @launcher.events
87
+ Puma::Util.nakayoshi_gc @events if @options[:nakayoshi_fork]
88
+ end
89
+ elsif idx == 0 # restart server
90
+ restart_server << true << false
91
+ else # fork worker
92
+ worker_pids << pid = spawn_worker(idx)
93
+ @worker_write << "f#{pid}:#{idx}\n" rescue nil
94
+ end
95
+ end
96
+ end
97
+ end
98
+
99
+ Signal.trap "SIGTERM" do
100
+ @worker_write << "e#{Process.pid}\n" rescue nil
101
+ restart_server.clear
102
+ server.stop
103
+ restart_server << false
104
+ end
105
+
106
+ begin
107
+ @worker_write << "b#{Process.pid}:#{index}\n"
108
+ rescue SystemCallError, IOError
109
+ Puma::Util.purge_interrupt_queue
110
+ STDERR.puts "Master seems to have exited, exiting."
111
+ return
112
+ end
113
+
114
+ while restart_server.pop
115
+ server_thread = server.run
116
+ stat_thread ||= Thread.new(@worker_write) do |io|
117
+ Puma.set_thread_name "stat pld"
118
+ base_payload = "p#{Process.pid}"
119
+
120
+ while true
121
+ begin
122
+ b = server.backlog || 0
123
+ r = server.running || 0
124
+ t = server.pool_capacity || 0
125
+ m = server.max_threads || 0
126
+ rc = server.requests_count || 0
127
+ payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m}, "requests_count": #{rc} }\n!
128
+ io << payload
129
+ rescue IOError
130
+ Puma::Util.purge_interrupt_queue
131
+ break
132
+ end
133
+ sleep @options[:worker_check_interval]
134
+ end
135
+ end
136
+ server_thread.join
137
+ end
138
+
139
+ # Invoke any worker shutdown hooks so they can prevent the worker
140
+ # exiting until any background operations are completed
141
+ @launcher.config.run_hooks :before_worker_shutdown, index, @launcher.events
142
+ ensure
143
+ @worker_write << "t#{Process.pid}\n" rescue nil
144
+ @worker_write.close
145
+ end
146
+
147
+ private
148
+
149
+ def spawn_worker(idx)
150
+ @launcher.config.run_hooks :before_worker_fork, idx, @launcher.events
151
+
152
+ pid = fork do
153
+ new_worker = Worker.new index: idx,
154
+ master: master,
155
+ launcher: @launcher,
156
+ pipes: { check_pipe: @check_pipe,
157
+ worker_write: @worker_write },
158
+ server: @server
159
+ new_worker.run
160
+ end
161
+
162
+ if !pid
163
+ log "! Complete inability to spawn new workers detected"
164
+ log "! Seppuku is the only choice."
165
+ exit! 1
166
+ end
167
+
168
+ @launcher.config.run_hooks :after_worker_fork, idx, @launcher.events
169
+ pid
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ class Cluster < Runner
5
+ # This class represents a worker process from the perspective of the puma
6
+ # master process. It contains information about the process and its health
7
+ # and it exposes methods to control the process via IPC. It does not
8
+ # include the actual logic executed by the worker process itself. For that,
9
+ # see Puma::Cluster::Worker.
10
+ class WorkerHandle
11
+ def initialize(idx, pid, phase, options)
12
+ @index = idx
13
+ @pid = pid
14
+ @phase = phase
15
+ @stage = :started
16
+ @signal = "TERM"
17
+ @options = options
18
+ @first_term_sent = nil
19
+ @started_at = Time.now
20
+ @last_checkin = Time.now
21
+ @last_status = {}
22
+ @term = false
23
+ end
24
+
25
+ attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at
26
+
27
+ # @version 5.0.0
28
+ attr_writer :pid, :phase
29
+
30
+ def booted?
31
+ @stage == :booted
32
+ end
33
+
34
+ def uptime
35
+ Time.now - started_at
36
+ end
37
+
38
+ def boot!
39
+ @last_checkin = Time.now
40
+ @stage = :booted
41
+ end
42
+
43
+ def term!
44
+ @term = true
45
+ end
46
+
47
+ def term?
48
+ @term
49
+ end
50
+
51
+ def ping!(status)
52
+ @last_checkin = Time.now
53
+ captures = status.match(/{ "backlog":(?<backlog>\d*), "running":(?<running>\d*), "pool_capacity":(?<pool_capacity>\d*), "max_threads": (?<max_threads>\d*), "requests_count": (?<requests_count>\d*) }/)
54
+ @last_status = captures.names.inject({}) do |hash, key|
55
+ hash[key.to_sym] = captures[key].to_i
56
+ hash
57
+ end
58
+ end
59
+
60
+ # @see Puma::Cluster#check_workers
61
+ # @version 5.0.0
62
+ def ping_timeout
63
+ @last_checkin +
64
+ (booted? ?
65
+ @options[:worker_timeout] :
66
+ @options[:worker_boot_timeout]
67
+ )
68
+ end
69
+
70
+ def term
71
+ begin
72
+ if @first_term_sent && (Time.now - @first_term_sent) > @options[:worker_shutdown_timeout]
73
+ @signal = "KILL"
74
+ else
75
+ @term ||= true
76
+ @first_term_sent ||= Time.now
77
+ end
78
+ Process.kill @signal, @pid if @pid
79
+ rescue Errno::ESRCH
80
+ end
81
+ end
82
+
83
+ def kill
84
+ @signal = 'KILL'
85
+ term
86
+ end
87
+
88
+ def hup
89
+ Process.kill "HUP", @pid
90
+ rescue Errno::ESRCH
91
+ end
92
+ end
93
+ end
94
+ end