puma 3.12.0 → 5.3.1

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 (93) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +1413 -439
  3. data/LICENSE +23 -20
  4. data/README.md +131 -60
  5. data/bin/puma-wild +3 -9
  6. data/docs/architecture.md +24 -19
  7. data/docs/compile_options.md +19 -0
  8. data/docs/deployment.md +38 -13
  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/{tools → docs}/jungle/rc.d/puma.conf +0 -0
  14. data/docs/kubernetes.md +66 -0
  15. data/docs/nginx.md +1 -1
  16. data/docs/plugins.md +20 -10
  17. data/docs/rails_dev_mode.md +29 -0
  18. data/docs/restart.md +47 -22
  19. data/docs/signals.md +7 -6
  20. data/docs/stats.md +142 -0
  21. data/docs/systemd.md +48 -70
  22. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  23. data/ext/puma_http11/ext_help.h +1 -1
  24. data/ext/puma_http11/extconf.rb +27 -0
  25. data/ext/puma_http11/http11_parser.c +84 -109
  26. data/ext/puma_http11/http11_parser.h +1 -1
  27. data/ext/puma_http11/http11_parser.java.rl +22 -38
  28. data/ext/puma_http11/http11_parser.rl +4 -2
  29. data/ext/puma_http11/http11_parser_common.rl +3 -3
  30. data/ext/puma_http11/mini_ssl.c +262 -87
  31. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  32. data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
  33. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +89 -106
  34. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +92 -22
  35. data/ext/puma_http11/puma_http11.c +34 -50
  36. data/lib/puma/app/status.rb +68 -49
  37. data/lib/puma/binder.rb +197 -144
  38. data/lib/puma/cli.rb +17 -15
  39. data/lib/puma/client.rb +257 -226
  40. data/lib/puma/cluster/worker.rb +176 -0
  41. data/lib/puma/cluster/worker_handle.rb +90 -0
  42. data/lib/puma/cluster.rb +223 -212
  43. data/lib/puma/commonlogger.rb +4 -2
  44. data/lib/puma/configuration.rb +58 -51
  45. data/lib/puma/const.rb +41 -19
  46. data/lib/puma/control_cli.rb +117 -73
  47. data/lib/puma/detect.rb +26 -3
  48. data/lib/puma/dsl.rb +531 -123
  49. data/lib/puma/error_logger.rb +104 -0
  50. data/lib/puma/events.rb +57 -31
  51. data/lib/puma/io_buffer.rb +9 -5
  52. data/lib/puma/jruby_restart.rb +2 -58
  53. data/lib/puma/json.rb +96 -0
  54. data/lib/puma/launcher.rb +182 -70
  55. data/lib/puma/minissl/context_builder.rb +79 -0
  56. data/lib/puma/minissl.rb +149 -48
  57. data/lib/puma/null_io.rb +15 -1
  58. data/lib/puma/plugin/tmp_restart.rb +2 -0
  59. data/lib/puma/plugin.rb +8 -12
  60. data/lib/puma/queue_close.rb +26 -0
  61. data/lib/puma/rack/builder.rb +4 -5
  62. data/lib/puma/rack/urlmap.rb +2 -0
  63. data/lib/puma/rack_default.rb +2 -0
  64. data/lib/puma/reactor.rb +87 -316
  65. data/lib/puma/request.rb +456 -0
  66. data/lib/puma/runner.rb +33 -52
  67. data/lib/puma/server.rb +288 -679
  68. data/lib/puma/single.rb +13 -67
  69. data/lib/puma/state_file.rb +10 -3
  70. data/lib/puma/systemd.rb +46 -0
  71. data/lib/puma/thread_pool.rb +131 -81
  72. data/lib/puma/util.rb +14 -6
  73. data/lib/puma.rb +54 -0
  74. data/lib/rack/handler/puma.rb +8 -6
  75. data/tools/Dockerfile +16 -0
  76. data/tools/trickletest.rb +0 -1
  77. metadata +45 -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 -23
  82. data/lib/puma/daemon_ext.rb +0 -31
  83. data/lib/puma/delegation.rb +0 -11
  84. data/lib/puma/java_io_buffer.rb +0 -45
  85. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
  86. data/lib/puma/tcp_logger.rb +0 -39
  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
data/lib/puma/single.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/runner'
2
4
  require 'puma/detect'
3
5
  require 'puma/plugin'
@@ -11,12 +13,11 @@ module Puma
11
13
  # gets created via the `start_server` method from the `Puma::Runner` class
12
14
  # that this inherits from.
13
15
  class Single < Runner
16
+ # @!attribute [r] stats
14
17
  def stats
15
- b = @server.backlog || 0
16
- r = @server.running || 0
17
- t = @server.pool_capacity || 0
18
- m = @server.max_threads || 0
19
- %Q!{ "backlog": #{b}, "running": #{r}, "pool_capacity": #{t}, "max_threads": #{m} }!
18
+ {
19
+ started_at: @started_at.utc.iso8601
20
+ }.merge(@server.stats)
20
21
  end
21
22
 
22
23
  def restart
@@ -24,7 +25,7 @@ module Puma
24
25
  end
25
26
 
26
27
  def stop
27
- @server.stop false
28
+ @server.stop(false) if @server
28
29
  end
29
30
 
30
31
  def halt
@@ -34,67 +35,13 @@ module Puma
34
35
  def stop_blocked
35
36
  log "- Gracefully stopping, waiting for requests to finish"
36
37
  @control.stop(true) if @control
37
- @server.stop(true)
38
- end
39
-
40
- def jruby_daemon?
41
- daemon? and Puma.jruby?
42
- end
43
-
44
- def jruby_daemon_start
45
- require 'puma/jruby_restart'
46
- JRubyRestart.daemon_start(@restart_dir, @launcher.restart_args)
38
+ @server.stop(true) if @server
47
39
  end
48
40
 
49
41
  def run
50
- already_daemon = false
51
-
52
- if jruby_daemon?
53
- require 'puma/jruby_restart'
54
-
55
- if JRubyRestart.daemon?
56
- # load and bind before redirecting IO so errors show up on stdout/stderr
57
- load_and_bind
58
- redirect_io
59
- end
60
-
61
- already_daemon = JRubyRestart.daemon_init
62
- end
63
-
64
42
  output_header "single"
65
43
 
66
- if jruby_daemon?
67
- if already_daemon
68
- JRubyRestart.perm_daemonize
69
- else
70
- pid = nil
71
-
72
- Signal.trap "SIGUSR2" do
73
- log "* Started new process #{pid} as daemon..."
74
-
75
- # Must use exit! so we don't unwind and run the ensures
76
- # that will be run by the new child (such as deleting the
77
- # pidfile)
78
- exit!(true)
79
- end
80
-
81
- Signal.trap "SIGCHLD" do
82
- log "! Error starting new process as daemon, exiting"
83
- exit 1
84
- end
85
-
86
- jruby_daemon_start
87
- sleep
88
- end
89
- else
90
- if daemon?
91
- log "* Daemonizing..."
92
- Process.daemon(true)
93
- redirect_io
94
- end
95
-
96
- load_and_bind
97
- end
44
+ load_and_bind
98
45
 
99
46
  Plugins.fire_background
100
47
 
@@ -103,16 +50,15 @@ module Puma
103
50
  start_control
104
51
 
105
52
  @server = server = start_server
53
+ server_thread = server.run
106
54
 
107
- unless daemon?
108
- log "Use Ctrl-C to stop"
109
- redirect_io
110
- end
55
+ log "Use Ctrl-C to stop"
56
+ redirect_io
111
57
 
112
58
  @launcher.events.fire_on_booted!
113
59
 
114
60
  begin
115
- server.run.join
61
+ server_thread.join
116
62
  rescue Interrupt
117
63
  # Swallow it
118
64
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
 
3
5
  module Puma
@@ -6,15 +8,20 @@ module Puma
6
8
  @options = {}
7
9
  end
8
10
 
9
- def save(path)
10
- File.write path, YAML.dump(@options)
11
+ def save(path, permission = nil)
12
+ contents =YAML.dump @options
13
+ if permission
14
+ File.write path, contents, mode: 'wb:UTF-8'
15
+ else
16
+ File.write path, contents, mode: 'wb:UTF-8', perm: permission
17
+ end
11
18
  end
12
19
 
13
20
  def load(path)
14
21
  @options = YAML.load File.read(path)
15
22
  end
16
23
 
17
- FIELDS = %w!control_url control_auth_token pid!
24
+ FIELDS = %w!control_url control_auth_token pid running_from!
18
25
 
19
26
  FIELDS.each do |f|
20
27
  define_method f do
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sd_notify'
4
+
5
+ module Puma
6
+ class Systemd
7
+ def initialize(events)
8
+ @events = events
9
+ end
10
+
11
+ def hook_events
12
+ @events.on_booted { SdNotify.ready }
13
+ @events.on_stopped { SdNotify.stopping }
14
+ @events.on_restart { SdNotify.reloading }
15
+ end
16
+
17
+ def start_watchdog
18
+ return unless SdNotify.watchdog?
19
+
20
+ ping_f = watchdog_sleep_time
21
+
22
+ log "Pinging systemd watchdog every #{ping_f.round(1)} sec"
23
+ Thread.new do
24
+ loop do
25
+ sleep ping_f
26
+ SdNotify.watchdog
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def watchdog_sleep_time
34
+ usec = Integer(ENV["WATCHDOG_USEC"])
35
+
36
+ sec_f = usec / 1_000_000.0
37
+ # "It is recommended that a daemon sends a keep-alive notification message
38
+ # to the service manager every half of the time returned here."
39
+ sec_f / 2
40
+ end
41
+
42
+ def log(str)
43
+ @events.log str
44
+ end
45
+ end
46
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'thread'
2
4
 
3
5
  module Puma
@@ -11,7 +13,7 @@ module Puma
11
13
  # a thread pool via the `Puma::ThreadPool#<<` operator where it is stored in a `@todo` array.
12
14
  #
13
15
  # Each thread in the pool has an internal loop where it pulls a request from the `@todo` array
14
- # and proceses it.
16
+ # and processes it.
15
17
  class ThreadPool
16
18
  class ForceShutdown < RuntimeError
17
19
  end
@@ -45,6 +47,7 @@ module Puma
45
47
  @shutdown = false
46
48
 
47
49
  @trim_requested = 0
50
+ @out_of_band_pending = false
48
51
 
49
52
  @workers = []
50
53
 
@@ -52,14 +55,20 @@ module Puma
52
55
  @reaper = nil
53
56
 
54
57
  @mutex.synchronize do
55
- @min.times { spawn_thread }
58
+ @min.times do
59
+ spawn_thread
60
+ @not_full.wait(@mutex)
61
+ end
56
62
  end
57
63
 
58
64
  @clean_thread_locals = false
65
+ @force_shutdown = false
66
+ @shutdown_mutex = Mutex.new
59
67
  end
60
68
 
61
69
  attr_reader :spawned, :trim_requested, :waiting
62
70
  attr_accessor :clean_thread_locals
71
+ attr_accessor :out_of_band_hook # @version 5.0.0
63
72
 
64
73
  def self.clean_thread_locals
65
74
  Thread.current.keys.each do |key| # rubocop: disable Performance/HashEachMethods
@@ -70,13 +79,20 @@ module Puma
70
79
  # How many objects have yet to be processed by the pool?
71
80
  #
72
81
  def backlog
73
- @mutex.synchronize { @todo.size }
82
+ with_mutex { @todo.size }
74
83
  end
75
84
 
85
+ # @!attribute [r] pool_capacity
76
86
  def pool_capacity
77
87
  waiting + (@max - spawned)
78
88
  end
79
89
 
90
+ # @!attribute [r] busy_threads
91
+ # @version 5.0.0
92
+ def busy_threads
93
+ with_mutex { @spawned - @waiting + @todo.size }
94
+ end
95
+
80
96
  # :nodoc:
81
97
  #
82
98
  # Must be called with @mutex held!
@@ -85,8 +101,7 @@ module Puma
85
101
  @spawned += 1
86
102
 
87
103
  th = Thread.new(@spawned) do |spawned|
88
- # Thread name is new in Ruby 2.3
89
- Thread.current.name = 'puma %03i' % spawned if Thread.current.respond_to?(:name=)
104
+ Puma.set_thread_name 'threadpool %03i' % spawned
90
105
  todo = @todo
91
106
  block = @block
92
107
  mutex = @mutex
@@ -98,48 +113,40 @@ module Puma
98
113
  while true
99
114
  work = nil
100
115
 
101
- continue = true
102
-
103
116
  mutex.synchronize do
104
117
  while todo.empty?
105
118
  if @trim_requested > 0
106
119
  @trim_requested -= 1
107
- continue = false
108
- not_full.signal
109
- break
110
- end
111
-
112
- if @shutdown
113
- continue = false
114
- break
120
+ @spawned -= 1
121
+ @workers.delete th
122
+ Thread.exit
115
123
  end
116
124
 
117
125
  @waiting += 1
126
+ if @out_of_band_pending && trigger_out_of_band_hook
127
+ @out_of_band_pending = false
128
+ end
118
129
  not_full.signal
119
- not_empty.wait mutex
120
- @waiting -= 1
130
+ begin
131
+ not_empty.wait mutex
132
+ ensure
133
+ @waiting -= 1
134
+ end
121
135
  end
122
136
 
123
- work = todo.shift if continue
137
+ work = todo.shift
124
138
  end
125
139
 
126
- break unless continue
127
-
128
140
  if @clean_thread_locals
129
141
  ThreadPool.clean_thread_locals
130
142
  end
131
143
 
132
144
  begin
133
- block.call(work, *extra)
145
+ @out_of_band_pending = true if block.call(work, *extra)
134
146
  rescue Exception => e
135
147
  STDERR.puts "Error reached top of thread-pool: #{e.message} (#{e.class})"
136
148
  end
137
149
  end
138
-
139
- mutex.synchronize do
140
- @spawned -= 1
141
- @workers.delete th
142
- end
143
150
  end
144
151
 
145
152
  @workers << th
@@ -149,9 +156,32 @@ module Puma
149
156
 
150
157
  private :spawn_thread
151
158
 
159
+ # @version 5.0.0
160
+ def trigger_out_of_band_hook
161
+ return false unless out_of_band_hook && out_of_band_hook.any?
162
+
163
+ # we execute on idle hook when all threads are free
164
+ return false unless @spawned == @waiting
165
+
166
+ out_of_band_hook.each(&:call)
167
+ true
168
+ rescue Exception => e
169
+ STDERR.puts "Exception calling out_of_band_hook: #{e.message} (#{e.class})"
170
+ true
171
+ end
172
+
173
+ private :trigger_out_of_band_hook
174
+
175
+ # @version 5.0.0
176
+ def with_mutex(&block)
177
+ @mutex.owned? ?
178
+ yield :
179
+ @mutex.synchronize(&block)
180
+ end
181
+
152
182
  # Add +work+ to the todo list for a Thread to pickup and process.
153
183
  def <<(work)
154
- @mutex.synchronize do
184
+ with_mutex do
155
185
  if @shutdown
156
186
  raise "Unable to add work while shutting down"
157
187
  end
@@ -188,12 +218,12 @@ module Puma
188
218
  # request, it might not be added to the `@todo` array right away.
189
219
  # For example if a slow client has only sent a header, but not a body
190
220
  # then the `@todo` array would stay the same size as the reactor works
191
- # to try to buffer the request. In tha scenario the next call to this
221
+ # to try to buffer the request. In that scenario the next call to this
192
222
  # method would not block and another request would be added into the reactor
193
- # by the server. This would continue until a fully bufferend request
223
+ # by the server. This would continue until a fully buffered request
194
224
  # makes it through the reactor and can then be processed by the thread pool.
195
225
  def wait_until_not_full
196
- @mutex.synchronize do
226
+ with_mutex do
197
227
  while true
198
228
  return if @shutdown
199
229
 
@@ -201,20 +231,42 @@ module Puma
201
231
  # is work queued that cannot be handled by waiting
202
232
  # threads, then accept more work until we would
203
233
  # spin up the max number of threads.
204
- return if @todo.size - @waiting < @max - @spawned
234
+ return if busy_threads < @max
205
235
 
206
236
  @not_full.wait @mutex
207
237
  end
208
238
  end
209
239
  end
210
240
 
211
- # If too many threads are in the pool, tell one to finish go ahead
241
+ # @version 5.0.0
242
+ def wait_for_less_busy_worker(delay_s)
243
+ return unless delay_s && delay_s > 0
244
+
245
+ # Ruby MRI does GVL, this can result
246
+ # in processing contention when multiple threads
247
+ # (requests) are running concurrently
248
+ return unless Puma.mri?
249
+
250
+ with_mutex do
251
+ return if @shutdown
252
+
253
+ # do not delay, if we are not busy
254
+ return unless busy_threads > 0
255
+
256
+ # this will be signaled once a request finishes,
257
+ # which can happen earlier than delay
258
+ @not_full.wait @mutex, delay_s
259
+ end
260
+ end
261
+
262
+ # If there are any free threads in the pool, tell one to go ahead
212
263
  # and exit. If +force+ is true, then a trim request is requested
213
264
  # even if all threads are being utilized.
214
265
  #
215
266
  def trim(force=false)
216
- @mutex.synchronize do
217
- if (force or @waiting > 0) and @spawned - @trim_requested > @min
267
+ with_mutex do
268
+ free = @waiting - @todo.size
269
+ if (force or free > 0) and @spawned - @trim_requested > @min
218
270
  @trim_requested += 1
219
271
  @not_empty.signal
220
272
  end
@@ -224,7 +276,7 @@ module Puma
224
276
  # If there are dead threads in the pool make them go away while decreasing
225
277
  # spawned counter so that new healthy threads could be created again.
226
278
  def reap
227
- @mutex.synchronize do
279
+ with_mutex do
228
280
  dead_workers = @workers.reject(&:alive?)
229
281
 
230
282
  dead_workers.each do |worker|
@@ -238,10 +290,12 @@ module Puma
238
290
  end
239
291
  end
240
292
 
241
- class AutoTrim
242
- def initialize(pool, timeout)
293
+ class Automaton
294
+ def initialize(pool, timeout, thread_name, message)
243
295
  @pool = pool
244
296
  @timeout = timeout
297
+ @thread_name = thread_name
298
+ @message = message
245
299
  @running = false
246
300
  end
247
301
 
@@ -249,8 +303,9 @@ module Puma
249
303
  @running = true
250
304
 
251
305
  @thread = Thread.new do
306
+ Puma.set_thread_name @thread_name
252
307
  while @running
253
- @pool.trim
308
+ @pool.public_send(@message)
254
309
  sleep @timeout
255
310
  end
256
311
  end
@@ -263,44 +318,37 @@ module Puma
263
318
  end
264
319
 
265
320
  def auto_trim!(timeout=30)
266
- @auto_trim = AutoTrim.new(self, timeout)
321
+ @auto_trim = Automaton.new(self, timeout, "threadpool trimmer", :trim)
267
322
  @auto_trim.start!
268
323
  end
269
324
 
270
- class Reaper
271
- def initialize(pool, timeout)
272
- @pool = pool
273
- @timeout = timeout
274
- @running = false
275
- end
276
-
277
- def start!
278
- @running = true
279
-
280
- @thread = Thread.new do
281
- while @running
282
- @pool.reap
283
- sleep @timeout
284
- end
285
- end
286
- end
287
-
288
- def stop
289
- @running = false
290
- @thread.wakeup
291
- end
292
- end
293
-
294
325
  def auto_reap!(timeout=5)
295
- @reaper = Reaper.new(self, timeout)
326
+ @reaper = Automaton.new(self, timeout, "threadpool reaper", :reap)
296
327
  @reaper.start!
297
328
  end
298
329
 
330
+ # Allows ThreadPool::ForceShutdown to be raised within the
331
+ # provided block if the thread is forced to shutdown during execution.
332
+ def with_force_shutdown
333
+ t = Thread.current
334
+ @shutdown_mutex.synchronize do
335
+ raise ForceShutdown if @force_shutdown
336
+ t[:with_force_shutdown] = true
337
+ end
338
+ yield
339
+ ensure
340
+ t[:with_force_shutdown] = false
341
+ end
342
+
299
343
  # Tell all threads in the pool to exit and wait for them to finish.
344
+ # Wait +timeout+ seconds then raise +ForceShutdown+ in remaining threads.
345
+ # Next, wait an extra +grace+ seconds then force-kill remaining threads.
346
+ # Finally, wait +kill_grace+ seconds for remaining threads to exit.
300
347
  #
301
348
  def shutdown(timeout=-1)
302
- threads = @mutex.synchronize do
349
+ threads = with_mutex do
303
350
  @shutdown = true
351
+ @trim_requested = @spawned
304
352
  @not_empty.broadcast
305
353
  @not_full.broadcast
306
354
 
@@ -314,27 +362,29 @@ module Puma
314
362
  # Wait for threads to finish without force shutdown.
315
363
  threads.each(&:join)
316
364
  else
317
- # Wait for threads to finish after n attempts (+timeout+).
318
- # If threads are still running, it will forcefully kill them.
319
- timeout.times do
320
- threads.delete_if do |t|
321
- t.join 1
322
- end
323
-
324
- if threads.empty?
325
- break
326
- else
327
- sleep 1
365
+ join = ->(inner_timeout) do
366
+ start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
367
+ threads.reject! do |t|
368
+ elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start
369
+ t.join inner_timeout - elapsed
328
370
  end
329
371
  end
330
372
 
331
- threads.each do |t|
332
- t.raise ForceShutdown
333
- end
373
+ # Wait +timeout+ seconds for threads to finish.
374
+ join.call(timeout)
334
375
 
335
- threads.each do |t|
336
- t.join SHUTDOWN_GRACE_TIME
376
+ # If threads are still running, raise ForceShutdown and wait to finish.
377
+ @shutdown_mutex.synchronize do
378
+ @force_shutdown = true
379
+ threads.each do |t|
380
+ t.raise ForceShutdown if t[:with_force_shutdown]
381
+ end
337
382
  end
383
+ join.call(SHUTDOWN_GRACE_TIME)
384
+
385
+ # If threads are _still_ running, forcefully kill them and wait to finish.
386
+ threads.each(&:kill)
387
+ join.call(1)
338
388
  end
339
389
 
340
390
  @spawned = 0
data/lib/puma/util.rb CHANGED
@@ -1,10 +1,6 @@
1
- major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
1
+ # frozen_string_literal: true
2
2
 
3
- if major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125
4
- require 'puma/rack/backports/uri/common_193'
5
- else
6
- require 'uri/common'
7
- end
3
+ require 'uri/common'
8
4
 
9
5
  module Puma
10
6
  module Util
@@ -27,6 +23,17 @@ module Puma
27
23
  end
28
24
  module_function :unescape
29
25
 
26
+ # @version 5.0.0
27
+ def nakayoshi_gc(events)
28
+ events.log "! Promoting existing objects to old generation..."
29
+ 4.times { GC.start(full_mark: false) }
30
+ if GC.respond_to?(:compact)
31
+ events.log "! Compacting..."
32
+ GC.compact
33
+ end
34
+ events.log "! Friendly fork preparation complete."
35
+ end
36
+
30
37
  DEFAULT_SEP = /[&;] */n
31
38
 
32
39
  # Stolen from Mongrel, with some small modifications:
@@ -76,6 +83,7 @@ module Puma
76
83
  end
77
84
  end
78
85
 
86
+ # @!attribute [r] to_hash
79
87
  def to_hash
80
88
  hash = {}
81
89
  each { |k,v| hash[k] = v }
data/lib/puma.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Standard libraries
2
4
  require 'socket'
3
5
  require 'tempfile'
@@ -8,16 +10,68 @@ require 'stringio'
8
10
 
9
11
  require 'thread'
10
12
 
13
+ require 'puma/puma_http11'
14
+ require 'puma/detect'
15
+ require 'puma/json'
16
+
11
17
  module Puma
12
18
  autoload :Const, 'puma/const'
13
19
  autoload :Server, 'puma/server'
14
20
  autoload :Launcher, 'puma/launcher'
15
21
 
22
+ # at present, MiniSSL::Engine is only defined in extension code (puma_http11),
23
+ # not in minissl.rb
24
+ HAS_SSL = const_defined?(:MiniSSL, false) && MiniSSL.const_defined?(:Engine, false)
25
+
26
+ HAS_UNIX_SOCKET = Object.const_defined? :UNIXSocket
27
+
28
+ if HAS_SSL
29
+ require 'puma/minissl'
30
+ else
31
+ module MiniSSL
32
+ # this class is defined so that it exists when Puma is compiled
33
+ # without ssl support, as Server and Reactor use it in rescue statements.
34
+ class SSLError < StandardError ; end
35
+ end
36
+ end
37
+
38
+ def self.ssl?
39
+ HAS_SSL
40
+ end
41
+
42
+ def self.abstract_unix_socket?
43
+ @abstract_unix ||=
44
+ if HAS_UNIX_SOCKET
45
+ begin
46
+ ::UNIXServer.new("\0puma.temp.unix").close
47
+ true
48
+ rescue ArgumentError # darwin
49
+ false
50
+ end
51
+ else
52
+ false
53
+ end
54
+ end
55
+
56
+ # @!attribute [rw] stats_object=
16
57
  def self.stats_object=(val)
17
58
  @get_stats = val
18
59
  end
19
60
 
61
+ # @!attribute [rw] stats_object
20
62
  def self.stats
63
+ Puma::JSON.generate @get_stats.stats
64
+ end
65
+
66
+ # @!attribute [r] stats_hash
67
+ # @version 5.0.0
68
+ def self.stats_hash
21
69
  @get_stats.stats
22
70
  end
71
+
72
+ # Thread name is new in Ruby 2.3
73
+ def self.set_thread_name(name)
74
+ return unless Thread.current.respond_to?(:name=)
75
+ Thread.current.name = "puma #{name}"
76
+ end
23
77
  end