piesync-puma 3.12.6

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 (82) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +1429 -0
  3. data/LICENSE +26 -0
  4. data/README.md +280 -0
  5. data/bin/puma +10 -0
  6. data/bin/puma-wild +31 -0
  7. data/bin/pumactl +12 -0
  8. data/docs/architecture.md +36 -0
  9. data/docs/deployment.md +91 -0
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/nginx.md +80 -0
  14. data/docs/plugins.md +28 -0
  15. data/docs/restart.md +39 -0
  16. data/docs/signals.md +96 -0
  17. data/docs/systemd.md +272 -0
  18. data/ext/puma_http11/PumaHttp11Service.java +17 -0
  19. data/ext/puma_http11/ext_help.h +15 -0
  20. data/ext/puma_http11/extconf.rb +15 -0
  21. data/ext/puma_http11/http11_parser.c +1071 -0
  22. data/ext/puma_http11/http11_parser.h +65 -0
  23. data/ext/puma_http11/http11_parser.java.rl +161 -0
  24. data/ext/puma_http11/http11_parser.rl +149 -0
  25. data/ext/puma_http11/http11_parser_common.rl +54 -0
  26. data/ext/puma_http11/io_buffer.c +155 -0
  27. data/ext/puma_http11/mini_ssl.c +494 -0
  28. data/ext/puma_http11/org/jruby/puma/Http11.java +234 -0
  29. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +470 -0
  30. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +352 -0
  31. data/ext/puma_http11/puma_http11.c +500 -0
  32. data/lib/puma.rb +23 -0
  33. data/lib/puma/accept_nonblock.rb +23 -0
  34. data/lib/puma/app/status.rb +74 -0
  35. data/lib/puma/binder.rb +413 -0
  36. data/lib/puma/cli.rb +235 -0
  37. data/lib/puma/client.rb +480 -0
  38. data/lib/puma/cluster.rb +531 -0
  39. data/lib/puma/commonlogger.rb +108 -0
  40. data/lib/puma/compat.rb +14 -0
  41. data/lib/puma/configuration.rb +361 -0
  42. data/lib/puma/const.rb +239 -0
  43. data/lib/puma/control_cli.rb +264 -0
  44. data/lib/puma/convenient.rb +25 -0
  45. data/lib/puma/daemon_ext.rb +33 -0
  46. data/lib/puma/delegation.rb +13 -0
  47. data/lib/puma/detect.rb +15 -0
  48. data/lib/puma/dsl.rb +518 -0
  49. data/lib/puma/events.rb +153 -0
  50. data/lib/puma/io_buffer.rb +9 -0
  51. data/lib/puma/java_io_buffer.rb +47 -0
  52. data/lib/puma/jruby_restart.rb +84 -0
  53. data/lib/puma/launcher.rb +433 -0
  54. data/lib/puma/minissl.rb +285 -0
  55. data/lib/puma/null_io.rb +44 -0
  56. data/lib/puma/plugin.rb +117 -0
  57. data/lib/puma/plugin/tmp_restart.rb +34 -0
  58. data/lib/puma/rack/backports/uri/common_193.rb +33 -0
  59. data/lib/puma/rack/builder.rb +299 -0
  60. data/lib/puma/rack/urlmap.rb +91 -0
  61. data/lib/puma/rack_default.rb +7 -0
  62. data/lib/puma/reactor.rb +347 -0
  63. data/lib/puma/runner.rb +184 -0
  64. data/lib/puma/server.rb +1072 -0
  65. data/lib/puma/single.rb +123 -0
  66. data/lib/puma/state_file.rb +31 -0
  67. data/lib/puma/tcp_logger.rb +41 -0
  68. data/lib/puma/thread_pool.rb +346 -0
  69. data/lib/puma/util.rb +129 -0
  70. data/lib/rack/handler/puma.rb +115 -0
  71. data/tools/jungle/README.md +19 -0
  72. data/tools/jungle/init.d/README.md +61 -0
  73. data/tools/jungle/init.d/puma +421 -0
  74. data/tools/jungle/init.d/run-puma +18 -0
  75. data/tools/jungle/rc.d/README.md +74 -0
  76. data/tools/jungle/rc.d/puma +61 -0
  77. data/tools/jungle/rc.d/puma.conf +10 -0
  78. data/tools/jungle/upstart/README.md +61 -0
  79. data/tools/jungle/upstart/puma-manager.conf +31 -0
  80. data/tools/jungle/upstart/puma.conf +69 -0
  81. data/tools/trickletest.rb +45 -0
  82. metadata +131 -0
@@ -0,0 +1,123 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'puma/runner'
4
+ require 'puma/detect'
5
+ require 'puma/plugin'
6
+
7
+ module Puma
8
+ # This class is instantiated by the `Puma::Launcher` and used
9
+ # to boot and serve a Ruby application when no puma "workers" are needed
10
+ # i.e. only using "threaded" mode. For example `$ puma -t 1:5`
11
+ #
12
+ # At the core of this class is running an instance of `Puma::Server` which
13
+ # gets created via the `start_server` method from the `Puma::Runner` class
14
+ # that this inherits from.
15
+ class Single < Runner
16
+ def stats
17
+ b = @server.backlog || 0
18
+ r = @server.running || 0
19
+ t = @server.pool_capacity || 0
20
+ m = @server.max_threads || 0
21
+ %Q!{ "backlog": #{b}, "running": #{r}, "pool_capacity": #{t}, "max_threads": #{m} }!
22
+ end
23
+
24
+ def restart
25
+ @server.begin_restart
26
+ end
27
+
28
+ def stop
29
+ @server.stop false
30
+ end
31
+
32
+ def halt
33
+ @server.halt
34
+ end
35
+
36
+ def stop_blocked
37
+ log "- Gracefully stopping, waiting for requests to finish"
38
+ @control.stop(true) if @control
39
+ @server.stop(true)
40
+ end
41
+
42
+ def jruby_daemon?
43
+ daemon? and Puma.jruby?
44
+ end
45
+
46
+ def jruby_daemon_start
47
+ require 'puma/jruby_restart'
48
+ JRubyRestart.daemon_start(@restart_dir, @launcher.restart_args)
49
+ end
50
+
51
+ def run
52
+ already_daemon = false
53
+
54
+ if jruby_daemon?
55
+ require 'puma/jruby_restart'
56
+
57
+ if JRubyRestart.daemon?
58
+ # load and bind before redirecting IO so errors show up on stdout/stderr
59
+ load_and_bind
60
+ redirect_io
61
+ end
62
+
63
+ already_daemon = JRubyRestart.daemon_init
64
+ end
65
+
66
+ output_header "single"
67
+
68
+ if jruby_daemon?
69
+ if already_daemon
70
+ JRubyRestart.perm_daemonize
71
+ else
72
+ pid = nil
73
+
74
+ Signal.trap "SIGUSR2" do
75
+ log "* Started new process #{pid} as daemon..."
76
+
77
+ # Must use exit! so we don't unwind and run the ensures
78
+ # that will be run by the new child (such as deleting the
79
+ # pidfile)
80
+ exit!(true)
81
+ end
82
+
83
+ Signal.trap "SIGCHLD" do
84
+ log "! Error starting new process as daemon, exiting"
85
+ exit 1
86
+ end
87
+
88
+ jruby_daemon_start
89
+ sleep
90
+ end
91
+ else
92
+ if daemon?
93
+ log "* Daemonizing..."
94
+ Process.daemon(true)
95
+ redirect_io
96
+ end
97
+
98
+ load_and_bind
99
+ end
100
+
101
+ Plugins.fire_background
102
+
103
+ @launcher.write_state
104
+
105
+ start_control
106
+
107
+ @server = server = start_server
108
+
109
+ unless daemon?
110
+ log "Use Ctrl-C to stop"
111
+ redirect_io
112
+ end
113
+
114
+ @launcher.events.fire_on_booted!
115
+
116
+ begin
117
+ server.run.join
118
+ rescue Interrupt
119
+ # Swallow it
120
+ end
121
+ end
122
+ end
123
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+
5
+ module Puma
6
+ class StateFile
7
+ def initialize
8
+ @options = {}
9
+ end
10
+
11
+ def save(path)
12
+ File.write path, YAML.dump(@options)
13
+ end
14
+
15
+ def load(path)
16
+ @options = YAML.load File.read(path)
17
+ end
18
+
19
+ FIELDS = %w!control_url control_auth_token pid!
20
+
21
+ FIELDS.each do |f|
22
+ define_method f do
23
+ @options[f]
24
+ end
25
+
26
+ define_method "#{f}=" do |v|
27
+ @options[f] = v
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Puma
4
+ class TCPLogger
5
+ def initialize(logger, app, quiet=false)
6
+ @logger = logger
7
+ @app = app
8
+ @quiet = quiet
9
+ end
10
+
11
+ FORMAT = "%s - %s"
12
+
13
+ def log(who, str)
14
+ now = Time.now.strftime("%d/%b/%Y %H:%M:%S")
15
+
16
+ log_str = "#{now} - #{who} - #{str}"
17
+
18
+ case @logger
19
+ when IO
20
+ @logger.puts log_str
21
+ when Events
22
+ @logger.log log_str
23
+ end
24
+ end
25
+
26
+ def call(env, socket)
27
+ who = env[Const::REMOTE_ADDR]
28
+ log who, "connected" unless @quiet
29
+
30
+ env['log'] = lambda { |str| log(who, str) }
31
+
32
+ begin
33
+ @app.call env, socket
34
+ rescue Object => e
35
+ log who, "exception: #{e.message} (#{e.class})"
36
+ else
37
+ log who, "disconnected" unless @quiet
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,346 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'thread'
4
+
5
+ module Puma
6
+ # Internal Docs for A simple thread pool management object.
7
+ #
8
+ # Each Puma "worker" has a thread pool to process requests.
9
+ #
10
+ # First a connection to a client is made in `Puma::Server`. It is wrapped in a
11
+ # `Puma::Client` instance and then passed to the `Puma::Reactor` to ensure
12
+ # the whole request is buffered into memory. Once the request is ready, it is passed into
13
+ # a thread pool via the `Puma::ThreadPool#<<` operator where it is stored in a `@todo` array.
14
+ #
15
+ # Each thread in the pool has an internal loop where it pulls a request from the `@todo` array
16
+ # and proceses it.
17
+ class ThreadPool
18
+ class ForceShutdown < RuntimeError
19
+ end
20
+
21
+ # How long, after raising the ForceShutdown of a thread during
22
+ # forced shutdown mode, to wait for the thread to try and finish
23
+ # up its work before leaving the thread to die on the vine.
24
+ SHUTDOWN_GRACE_TIME = 5 # seconds
25
+
26
+ # Maintain a minimum of +min+ and maximum of +max+ threads
27
+ # in the pool.
28
+ #
29
+ # The block passed is the work that will be performed in each
30
+ # thread.
31
+ #
32
+ def initialize(min, max, *extra, &block)
33
+ @not_empty = ConditionVariable.new
34
+ @not_full = ConditionVariable.new
35
+ @mutex = Mutex.new
36
+
37
+ @todo = []
38
+
39
+ @spawned = 0
40
+ @waiting = 0
41
+
42
+ @min = Integer(min)
43
+ @max = Integer(max)
44
+ @block = block
45
+ @extra = extra
46
+
47
+ @shutdown = false
48
+
49
+ @trim_requested = 0
50
+
51
+ @workers = []
52
+
53
+ @auto_trim = nil
54
+ @reaper = nil
55
+
56
+ @mutex.synchronize do
57
+ @min.times { spawn_thread }
58
+ end
59
+
60
+ @clean_thread_locals = false
61
+ end
62
+
63
+ attr_reader :spawned, :trim_requested, :waiting
64
+ attr_accessor :clean_thread_locals
65
+
66
+ def self.clean_thread_locals
67
+ Thread.current.keys.each do |key| # rubocop: disable Performance/HashEachMethods
68
+ Thread.current[key] = nil unless key == :__recursive_key__
69
+ end
70
+ end
71
+
72
+ # How many objects have yet to be processed by the pool?
73
+ #
74
+ def backlog
75
+ @mutex.synchronize { @todo.size }
76
+ end
77
+
78
+ def pool_capacity
79
+ waiting + (@max - spawned)
80
+ end
81
+
82
+ # :nodoc:
83
+ #
84
+ # Must be called with @mutex held!
85
+ #
86
+ def spawn_thread
87
+ @spawned += 1
88
+
89
+ th = Thread.new(@spawned) do |spawned|
90
+ # Thread name is new in Ruby 2.3
91
+ Thread.current.name = 'puma %03i' % spawned if Thread.current.respond_to?(:name=)
92
+ todo = @todo
93
+ block = @block
94
+ mutex = @mutex
95
+ not_empty = @not_empty
96
+ not_full = @not_full
97
+
98
+ extra = @extra.map { |i| i.new }
99
+
100
+ while true
101
+ work = nil
102
+
103
+ continue = true
104
+
105
+ mutex.synchronize do
106
+ while todo.empty?
107
+ if @trim_requested > 0
108
+ @trim_requested -= 1
109
+ continue = false
110
+ not_full.signal
111
+ break
112
+ end
113
+
114
+ if @shutdown
115
+ continue = false
116
+ break
117
+ end
118
+
119
+ @waiting += 1
120
+ not_full.signal
121
+ not_empty.wait mutex
122
+ @waiting -= 1
123
+ end
124
+
125
+ work = todo.shift if continue
126
+ end
127
+
128
+ break unless continue
129
+
130
+ if @clean_thread_locals
131
+ ThreadPool.clean_thread_locals
132
+ end
133
+
134
+ begin
135
+ block.call(work, *extra)
136
+ rescue Exception => e
137
+ STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
138
+ end
139
+ end
140
+
141
+ mutex.synchronize do
142
+ @spawned -= 1
143
+ @workers.delete th
144
+ end
145
+ end
146
+
147
+ @workers << th
148
+
149
+ th
150
+ end
151
+
152
+ private :spawn_thread
153
+
154
+ # Add +work+ to the todo list for a Thread to pickup and process.
155
+ def <<(work)
156
+ @mutex.synchronize do
157
+ if @shutdown
158
+ raise "Unable to add work while shutting down"
159
+ end
160
+
161
+ @todo << work
162
+
163
+ if @waiting < @todo.size and @spawned < @max
164
+ spawn_thread
165
+ end
166
+
167
+ @not_empty.signal
168
+ end
169
+ end
170
+
171
+ # This method is used by `Puma::Server` to let the server know when
172
+ # the thread pool can pull more requests from the socket and
173
+ # pass to the reactor.
174
+ #
175
+ # The general idea is that the thread pool can only work on a fixed
176
+ # number of requests at the same time. If it is already processing that
177
+ # number of requests then it is at capacity. If another Puma process has
178
+ # spare capacity, then the request can be left on the socket so the other
179
+ # worker can pick it up and process it.
180
+ #
181
+ # For example: if there are 5 threads, but only 4 working on
182
+ # requests, this method will not wait and the `Puma::Server`
183
+ # can pull a request right away.
184
+ #
185
+ # If there are 5 threads and all 5 of them are busy, then it will
186
+ # pause here, and wait until the `not_full` condition variable is
187
+ # signaled, usually this indicates that a request has been processed.
188
+ #
189
+ # It's important to note that even though the server might accept another
190
+ # request, it might not be added to the `@todo` array right away.
191
+ # For example if a slow client has only sent a header, but not a body
192
+ # then the `@todo` array would stay the same size as the reactor works
193
+ # to try to buffer the request. In tha scenario the next call to this
194
+ # method would not block and another request would be added into the reactor
195
+ # by the server. This would continue until a fully bufferend request
196
+ # makes it through the reactor and can then be processed by the thread pool.
197
+ def wait_until_not_full
198
+ @mutex.synchronize do
199
+ while true
200
+ return if @shutdown
201
+
202
+ # If we can still spin up new threads and there
203
+ # is work queued that cannot be handled by waiting
204
+ # threads, then accept more work until we would
205
+ # spin up the max number of threads.
206
+ return if @todo.size - @waiting < @max - @spawned
207
+
208
+ @not_full.wait @mutex
209
+ end
210
+ end
211
+ end
212
+
213
+ # If too many threads are in the pool, tell one to finish go ahead
214
+ # and exit. If +force+ is true, then a trim request is requested
215
+ # even if all threads are being utilized.
216
+ #
217
+ def trim(force=false)
218
+ @mutex.synchronize do
219
+ if (force or @waiting > 0) and @spawned - @trim_requested > @min
220
+ @trim_requested += 1
221
+ @not_empty.signal
222
+ end
223
+ end
224
+ end
225
+
226
+ # If there are dead threads in the pool make them go away while decreasing
227
+ # spawned counter so that new healthy threads could be created again.
228
+ def reap
229
+ @mutex.synchronize do
230
+ dead_workers = @workers.reject(&:alive?)
231
+
232
+ dead_workers.each do |worker|
233
+ worker.kill
234
+ @spawned -= 1
235
+ end
236
+
237
+ @workers.delete_if do |w|
238
+ dead_workers.include?(w)
239
+ end
240
+ end
241
+ end
242
+
243
+ class AutoTrim
244
+ def initialize(pool, timeout)
245
+ @pool = pool
246
+ @timeout = timeout
247
+ @running = false
248
+ end
249
+
250
+ def start!
251
+ @running = true
252
+
253
+ @thread = Thread.new do
254
+ while @running
255
+ @pool.trim
256
+ sleep @timeout
257
+ end
258
+ end
259
+ end
260
+
261
+ def stop
262
+ @running = false
263
+ @thread.wakeup
264
+ end
265
+ end
266
+
267
+ def auto_trim!(timeout=30)
268
+ @auto_trim = AutoTrim.new(self, timeout)
269
+ @auto_trim.start!
270
+ end
271
+
272
+ class Reaper
273
+ def initialize(pool, timeout)
274
+ @pool = pool
275
+ @timeout = timeout
276
+ @running = false
277
+ end
278
+
279
+ def start!
280
+ @running = true
281
+
282
+ @thread = Thread.new do
283
+ while @running
284
+ @pool.reap
285
+ sleep @timeout
286
+ end
287
+ end
288
+ end
289
+
290
+ def stop
291
+ @running = false
292
+ @thread.wakeup
293
+ end
294
+ end
295
+
296
+ def auto_reap!(timeout=5)
297
+ @reaper = Reaper.new(self, timeout)
298
+ @reaper.start!
299
+ end
300
+
301
+ # Tell all threads in the pool to exit and wait for them to finish.
302
+ #
303
+ def shutdown(timeout=-1)
304
+ threads = @mutex.synchronize do
305
+ @shutdown = true
306
+ @not_empty.broadcast
307
+ @not_full.broadcast
308
+
309
+ @auto_trim.stop if @auto_trim
310
+ @reaper.stop if @reaper
311
+ # dup workers so that we join them all safely
312
+ @workers.dup
313
+ end
314
+
315
+ if timeout == -1
316
+ # Wait for threads to finish without force shutdown.
317
+ threads.each(&:join)
318
+ else
319
+ # Wait for threads to finish after n attempts (+timeout+).
320
+ # If threads are still running, it will forcefully kill them.
321
+ timeout.times do
322
+ threads.delete_if do |t|
323
+ t.join 1
324
+ end
325
+
326
+ if threads.empty?
327
+ break
328
+ else
329
+ sleep 1
330
+ end
331
+ end
332
+
333
+ threads.each do |t|
334
+ t.raise ForceShutdown
335
+ end
336
+
337
+ threads.each do |t|
338
+ t.join SHUTDOWN_GRACE_TIME
339
+ end
340
+ end
341
+
342
+ @spawned = 0
343
+ @workers = []
344
+ end
345
+ end
346
+ end