piesync-puma 3.12.6

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