puma 2.1.1 → 7.2.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 (125) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +3273 -0
  3. data/LICENSE +23 -20
  4. data/README.md +388 -100
  5. data/bin/puma-wild +25 -0
  6. data/bin/pumactl +1 -1
  7. data/docs/architecture.md +74 -0
  8. data/docs/compile_options.md +55 -0
  9. data/docs/deployment.md +137 -0
  10. data/docs/fork_worker.md +41 -0
  11. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  12. data/docs/images/puma-connection-flow.png +0 -0
  13. data/docs/images/puma-general-arch.png +0 -0
  14. data/docs/java_options.md +54 -0
  15. data/docs/jungle/README.md +9 -0
  16. data/docs/jungle/rc.d/README.md +74 -0
  17. data/docs/jungle/rc.d/puma +61 -0
  18. data/docs/jungle/rc.d/puma.conf +10 -0
  19. data/docs/kubernetes.md +73 -0
  20. data/docs/nginx.md +5 -5
  21. data/docs/plugins.md +42 -0
  22. data/docs/rails_dev_mode.md +28 -0
  23. data/docs/restart.md +65 -0
  24. data/docs/signals.md +98 -0
  25. data/docs/stats.md +148 -0
  26. data/docs/systemd.md +253 -0
  27. data/docs/testing_benchmarks_local_files.md +150 -0
  28. data/docs/testing_test_rackup_ci_files.md +36 -0
  29. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  30. data/ext/puma_http11/extconf.rb +59 -2
  31. data/ext/puma_http11/http11_parser.c +319 -487
  32. data/ext/puma_http11/http11_parser.h +15 -13
  33. data/ext/puma_http11/http11_parser.java.rl +27 -43
  34. data/ext/puma_http11/http11_parser.rl +27 -24
  35. data/ext/puma_http11/http11_parser_common.rl +8 -8
  36. data/ext/puma_http11/mini_ssl.c +696 -38
  37. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  38. data/ext/puma_http11/org/jruby/puma/Http11.java +145 -113
  39. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +139 -172
  40. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +413 -193
  41. data/ext/puma_http11/puma_http11.c +183 -175
  42. data/lib/puma/app/status.rb +77 -29
  43. data/lib/puma/binder.rb +349 -119
  44. data/lib/puma/cli.rb +164 -769
  45. data/lib/puma/client.rb +591 -132
  46. data/lib/puma/cluster/worker.rb +183 -0
  47. data/lib/puma/cluster/worker_handle.rb +127 -0
  48. data/lib/puma/cluster.rb +634 -0
  49. data/lib/puma/cluster_accept_loop_delay.rb +91 -0
  50. data/lib/puma/commonlogger.rb +115 -0
  51. data/lib/puma/configuration.rb +370 -202
  52. data/lib/puma/const.rb +266 -115
  53. data/lib/puma/control_cli.rb +219 -120
  54. data/lib/puma/detect.rb +45 -2
  55. data/lib/puma/dsl.rb +1496 -0
  56. data/lib/puma/error_logger.rb +115 -0
  57. data/lib/puma/events.rb +44 -60
  58. data/lib/puma/io_buffer.rb +48 -5
  59. data/lib/puma/jruby_restart.rb +2 -51
  60. data/lib/puma/json_serialization.rb +96 -0
  61. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  62. data/lib/puma/launcher.rb +502 -0
  63. data/lib/puma/log_writer.rb +147 -0
  64. data/lib/puma/minissl/context_builder.rb +96 -0
  65. data/lib/puma/minissl.rb +355 -37
  66. data/lib/puma/null_io.rb +79 -12
  67. data/lib/puma/plugin/systemd.rb +90 -0
  68. data/lib/puma/plugin/tmp_restart.rb +36 -0
  69. data/lib/puma/plugin.rb +111 -0
  70. data/lib/puma/rack/builder.rb +297 -0
  71. data/lib/puma/rack/urlmap.rb +93 -0
  72. data/lib/puma/rack_default.rb +21 -4
  73. data/lib/puma/reactor.rb +102 -133
  74. data/lib/puma/request.rb +703 -0
  75. data/lib/puma/runner.rb +211 -0
  76. data/lib/puma/sd_notify.rb +146 -0
  77. data/lib/puma/server.rb +544 -436
  78. data/lib/puma/single.rb +72 -0
  79. data/lib/puma/state_file.rb +69 -0
  80. data/lib/puma/thread_pool.rb +282 -56
  81. data/lib/puma/util.rb +125 -0
  82. data/lib/puma.rb +80 -6
  83. data/lib/rack/handler/puma.rb +125 -47
  84. data/tools/Dockerfile +26 -0
  85. data/tools/trickletest.rb +44 -0
  86. metadata +77 -148
  87. data/COPYING +0 -55
  88. data/Gemfile +0 -10
  89. data/History.txt +0 -291
  90. data/Manifest.txt +0 -60
  91. data/Rakefile +0 -121
  92. data/TODO +0 -5
  93. data/docs/config.md +0 -0
  94. data/ext/puma_http11/ext_help.h +0 -15
  95. data/ext/puma_http11/io_buffer.c +0 -154
  96. data/lib/puma/accept_nonblock.rb +0 -23
  97. data/lib/puma/capistrano.rb +0 -34
  98. data/lib/puma/compat.rb +0 -11
  99. data/lib/puma/daemon_ext.rb +0 -20
  100. data/lib/puma/delegation.rb +0 -11
  101. data/lib/puma/java_io_buffer.rb +0 -45
  102. data/lib/puma/rack_patch.rb +0 -25
  103. data/puma.gemspec +0 -45
  104. data/test/test_app_status.rb +0 -88
  105. data/test/test_cli.rb +0 -171
  106. data/test/test_config.rb +0 -16
  107. data/test/test_http10.rb +0 -27
  108. data/test/test_http11.rb +0 -126
  109. data/test/test_integration.rb +0 -154
  110. data/test/test_iobuffer.rb +0 -38
  111. data/test/test_minissl.rb +0 -25
  112. data/test/test_null_io.rb +0 -31
  113. data/test/test_persistent.rb +0 -238
  114. data/test/test_puma_server.rb +0 -207
  115. data/test/test_rack_handler.rb +0 -10
  116. data/test/test_rack_server.rb +0 -141
  117. data/test/test_thread_pool.rb +0 -146
  118. data/test/test_unix_socket.rb +0 -39
  119. data/test/test_ws.rb +0 -89
  120. data/tools/jungle/init.d/README.md +0 -54
  121. data/tools/jungle/init.d/puma +0 -332
  122. data/tools/jungle/init.d/run-puma +0 -3
  123. data/tools/jungle/upstart/README.md +0 -61
  124. data/tools/jungle/upstart/puma-manager.conf +0 -31
  125. data/tools/jungle/upstart/puma.conf +0 -52
@@ -0,0 +1,183 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ class Cluster < Puma::Runner
5
+ #—————————————————————— DO NOT USE — this class is for internal use only ———
6
+
7
+
8
+ # This class is instantiated by the `Puma::Cluster` and represents a single
9
+ # worker process.
10
+ #
11
+ # At the core of this class is running an instance of `Puma::Server` which
12
+ # gets created via the `start_server` method from the `Puma::Runner` class
13
+ # that this inherits from.
14
+ class Worker < Puma::Runner # :nodoc:
15
+ attr_reader :index, :master
16
+
17
+ def initialize(index:, master:, launcher:, pipes:, app: nil)
18
+ super(launcher)
19
+
20
+ @index = index
21
+ @master = master
22
+ @check_pipe = pipes[:check_pipe]
23
+ @worker_write = pipes[:worker_write]
24
+ @fork_pipe = pipes[:fork_pipe]
25
+ @wakeup = pipes[:wakeup]
26
+ @app = app
27
+ @server = nil
28
+ @hook_data = {}
29
+ end
30
+
31
+ def run
32
+ title = "puma: cluster worker #{index}: #{master}"
33
+ title += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
34
+ $0 = title
35
+
36
+ Signal.trap "SIGINT", "IGNORE"
37
+ Signal.trap "SIGCHLD", "DEFAULT"
38
+
39
+ Thread.new do
40
+ Puma.set_thread_name "wrkr check"
41
+ @check_pipe.wait_readable
42
+ log "! Detected parent died, dying"
43
+ exit! 1
44
+ end
45
+
46
+ # If we're not running under a Bundler context, then
47
+ # report the info about the context we will be using
48
+ if !ENV['BUNDLE_GEMFILE']
49
+ if File.exist?("Gemfile")
50
+ log "+ Gemfile in context: #{File.expand_path("Gemfile")}"
51
+ elsif File.exist?("gems.rb")
52
+ log "+ Gemfile in context: #{File.expand_path("gems.rb")}"
53
+ end
54
+ end
55
+
56
+ # Invoke any worker boot hooks so they can get
57
+ # things in shape before booting the app.
58
+ @config.run_hooks(:before_worker_boot, index, @log_writer, @hook_data)
59
+
60
+ begin
61
+ @server = start_server
62
+ rescue Exception => e
63
+ log "! Unable to start worker"
64
+ log e
65
+ log e.backtrace.join("\n ")
66
+ exit 1
67
+ end
68
+
69
+ restart_server = Queue.new << true << false
70
+
71
+ fork_worker = @options[:fork_worker] && index == 0
72
+
73
+ if fork_worker
74
+ restart_server.clear
75
+ worker_pids = []
76
+ Signal.trap "SIGCHLD" do
77
+ wakeup! if worker_pids.reject! do |p|
78
+ Process.wait(p, Process::WNOHANG) rescue true
79
+ end
80
+ end
81
+
82
+ Thread.new do
83
+ Puma.set_thread_name "wrkr fork"
84
+ while (idx = @fork_pipe.gets)
85
+ idx = idx.to_i
86
+ if idx == -1 # stop server
87
+ if restart_server.length > 0
88
+ restart_server.clear
89
+ @server.begin_restart(true)
90
+ @config.run_hooks(:before_refork, nil, @log_writer, @hook_data)
91
+ end
92
+ elsif idx == -2 # refork cycle is done
93
+ @config.run_hooks(:after_refork, nil, @log_writer, @hook_data)
94
+ elsif idx == 0 # restart server
95
+ restart_server << true << false
96
+ else # fork worker
97
+ worker_pids << pid = spawn_worker(idx)
98
+ @worker_write << "#{PIPE_FORK}#{pid}:#{idx}\n" rescue nil
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ Signal.trap "SIGTERM" do
105
+ @worker_write << "#{PIPE_EXTERNAL_TERM}#{Process.pid}\n" rescue nil
106
+ restart_server.clear
107
+ @server.stop
108
+ restart_server << false
109
+ end
110
+
111
+ begin
112
+ @worker_write << "#{PIPE_BOOT}#{Process.pid}:#{index}\n"
113
+ rescue SystemCallError, IOError
114
+ STDERR.puts "Master seems to have exited, exiting."
115
+ return
116
+ end
117
+
118
+ while restart_server.pop
119
+ server_thread = @server.run
120
+
121
+ if @log_writer.debug? && index == 0
122
+ debug_loaded_extensions "Loaded Extensions - worker 0:"
123
+ end
124
+
125
+ stat_thread ||= Thread.new(@worker_write) do |io|
126
+ Puma.set_thread_name "stat pld"
127
+ base_payload = "#{PIPE_PING}#{Process.pid}"
128
+
129
+ while true
130
+ begin
131
+ payload = base_payload.dup
132
+
133
+ hsh = @server.stats
134
+ hsh.each do |k, v|
135
+ payload << %Q! "#{k}":#{v || 0},!
136
+ end
137
+ # sub call properly adds 'closing' string
138
+ io << payload.sub(/,\z/, " }\n")
139
+ @server.reset_max
140
+ rescue IOError
141
+ break
142
+ end
143
+ sleep @options[:worker_check_interval]
144
+ end
145
+ end
146
+ server_thread.join
147
+ end
148
+
149
+ # Invoke any worker shutdown hooks so they can prevent the worker
150
+ # exiting until any background operations are completed
151
+ @config.run_hooks(:before_worker_shutdown, index, @log_writer, @hook_data)
152
+ ensure
153
+ @worker_write << "#{PIPE_TERM}#{Process.pid}\n" rescue nil
154
+ @worker_write.close
155
+ end
156
+
157
+ private
158
+
159
+ def spawn_worker(idx)
160
+ @config.run_hooks(:before_worker_fork, idx, @log_writer, @hook_data)
161
+
162
+ pid = fork do
163
+ new_worker = Worker.new index: idx,
164
+ master: master,
165
+ launcher: @launcher,
166
+ pipes: { check_pipe: @check_pipe,
167
+ worker_write: @worker_write },
168
+ app: @app
169
+ new_worker.run
170
+ end
171
+
172
+ if !pid
173
+ log "! Complete inability to spawn new workers detected"
174
+ log "! Seppuku is the only choice."
175
+ exit! 1
176
+ end
177
+
178
+ @config.run_hooks(:after_worker_fork, idx, @log_writer, @hook_data)
179
+ pid
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ class Cluster < Runner
5
+ #—————————————————————— DO NOT USE — this class is for internal use only ———
6
+
7
+ # This class represents a worker process from the perspective of the puma
8
+ # master process. It contains information about the process and its health
9
+ # and it exposes methods to control the process via IPC. It does not
10
+ # include the actual logic executed by the worker process itself. For that,
11
+ # see Puma::Cluster::Worker.
12
+ class WorkerHandle # :nodoc:
13
+ # array of stat 'max' keys
14
+ WORKER_MAX_KEYS = [:backlog_max, :reactor_max]
15
+
16
+ def initialize(idx, pid, phase, options)
17
+ @index = idx
18
+ @pid = pid
19
+ @phase = phase
20
+ @stage = :started
21
+ @signal = "TERM"
22
+ @options = options
23
+ @first_term_sent = nil
24
+ @started_at = Time.now
25
+ @last_checkin = Time.now
26
+ @last_status = {}
27
+ @term = false
28
+ @worker_max = Array.new WORKER_MAX_KEYS.length, 0
29
+ end
30
+
31
+ attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status, :started_at, :process_status
32
+
33
+ # @version 5.0.0
34
+ attr_writer :pid, :phase, :process_status
35
+
36
+ def booted?
37
+ @stage == :booted
38
+ end
39
+
40
+ def uptime
41
+ Time.now - started_at
42
+ end
43
+
44
+ def boot!
45
+ @last_checkin = Time.now
46
+ @stage = :booted
47
+ end
48
+
49
+ def term!
50
+ @term = true
51
+ end
52
+
53
+ def term?
54
+ @term
55
+ end
56
+
57
+ def ping!(status)
58
+ hsh = {}
59
+ k, v = nil, nil
60
+ status.tr('}{"', '').strip.split(", ") do |kv|
61
+ cntr = 0
62
+ kv.split(':') do |t|
63
+ if cntr == 0
64
+ k = t
65
+ cntr = 1
66
+ else
67
+ v = t
68
+ end
69
+ end
70
+ hsh[k.to_sym] = v.to_i
71
+ end
72
+
73
+ # check stat max values, we can't signal workers to reset the max values,
74
+ # so we do so here
75
+ WORKER_MAX_KEYS.each_with_index do |key, idx|
76
+ next unless hsh[key]
77
+
78
+ if hsh[key] < @worker_max[idx]
79
+ hsh[key] = @worker_max[idx]
80
+ else
81
+ @worker_max[idx] = hsh[key]
82
+ end
83
+ end
84
+ @last_checkin = Time.now
85
+ @last_status = hsh
86
+ end
87
+
88
+ # Resets max values to zero. Called whenever `Cluster#stats` is called
89
+ def reset_max
90
+ WORKER_MAX_KEYS.length.times { |idx| @worker_max[idx] = 0 }
91
+ end
92
+
93
+ # @see Puma::Cluster#check_workers
94
+ # @version 5.0.0
95
+ def ping_timeout
96
+ @last_checkin +
97
+ (booted? ?
98
+ @options[:worker_timeout] :
99
+ @options[:worker_boot_timeout]
100
+ )
101
+ end
102
+
103
+ def term
104
+ begin
105
+ if @first_term_sent && (Time.now - @first_term_sent) > @options[:worker_shutdown_timeout]
106
+ @signal = "KILL"
107
+ else
108
+ @term ||= true
109
+ @first_term_sent ||= Time.now
110
+ end
111
+ Process.kill @signal, @pid if @pid
112
+ rescue Errno::ESRCH
113
+ end
114
+ end
115
+
116
+ def kill
117
+ @signal = 'KILL'
118
+ term
119
+ end
120
+
121
+ def hup
122
+ Process.kill "HUP", @pid
123
+ rescue Errno::ESRCH
124
+ end
125
+ end
126
+ end
127
+ end