puma 3.12.2 → 4.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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +122 -2
  3. data/README.md +76 -48
  4. data/docs/architecture.md +1 -0
  5. data/docs/deployment.md +24 -4
  6. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  7. data/docs/images/puma-connection-flow.png +0 -0
  8. data/docs/images/puma-general-arch.png +0 -0
  9. data/docs/plugins.md +20 -10
  10. data/docs/restart.md +4 -2
  11. data/docs/systemd.md +27 -9
  12. data/docs/tcp_mode.md +96 -0
  13. data/ext/puma_http11/PumaHttp11Service.java +2 -0
  14. data/ext/puma_http11/extconf.rb +13 -0
  15. data/ext/puma_http11/http11_parser.c +37 -62
  16. data/ext/puma_http11/http11_parser.java.rl +21 -37
  17. data/ext/puma_http11/http11_parser_common.rl +3 -3
  18. data/ext/puma_http11/mini_ssl.c +78 -8
  19. data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
  20. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +91 -106
  21. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  22. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -4
  23. data/ext/puma_http11/puma_http11.c +2 -0
  24. data/lib/puma.rb +8 -0
  25. data/lib/puma/accept_nonblock.rb +7 -1
  26. data/lib/puma/app/status.rb +35 -29
  27. data/lib/puma/binder.rb +38 -60
  28. data/lib/puma/cli.rb +4 -0
  29. data/lib/puma/client.rb +221 -199
  30. data/lib/puma/cluster.rb +53 -30
  31. data/lib/puma/configuration.rb +4 -3
  32. data/lib/puma/const.rb +22 -18
  33. data/lib/puma/control_cli.rb +30 -5
  34. data/lib/puma/dsl.rb +299 -75
  35. data/lib/puma/events.rb +4 -1
  36. data/lib/puma/io_buffer.rb +1 -6
  37. data/lib/puma/launcher.rb +95 -53
  38. data/lib/puma/minissl.rb +35 -17
  39. data/lib/puma/minissl/context_builder.rb +76 -0
  40. data/lib/puma/plugin.rb +5 -2
  41. data/lib/puma/plugin/tmp_restart.rb +2 -0
  42. data/lib/puma/rack/builder.rb +2 -0
  43. data/lib/puma/rack/urlmap.rb +2 -0
  44. data/lib/puma/rack_default.rb +2 -0
  45. data/lib/puma/reactor.rb +110 -57
  46. data/lib/puma/runner.rb +11 -3
  47. data/lib/puma/server.rb +58 -47
  48. data/lib/puma/single.rb +3 -3
  49. data/lib/puma/thread_pool.rb +15 -33
  50. data/lib/puma/util.rb +1 -6
  51. data/lib/rack/handler/puma.rb +3 -3
  52. data/tools/docker/Dockerfile +16 -0
  53. data/tools/jungle/init.d/puma +6 -6
  54. data/tools/trickletest.rb +0 -1
  55. metadata +21 -8
  56. data/lib/puma/compat.rb +0 -14
  57. data/lib/puma/convenient.rb +0 -25
  58. data/lib/puma/daemon_ext.rb +0 -33
  59. data/lib/puma/delegation.rb +0 -13
  60. data/lib/puma/java_io_buffer.rb +0 -47
  61. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
@@ -18,7 +18,7 @@ module Puma
18
18
  r = @server.running || 0
19
19
  t = @server.pool_capacity || 0
20
20
  m = @server.max_threads || 0
21
- %Q!{ "backlog": #{b}, "running": #{r}, "pool_capacity": #{t}, "max_threads": #{m} }!
21
+ %Q!{ "started_at": "#{@started_at.utc.iso8601}", "backlog": #{b}, "running": #{r}, "pool_capacity": #{t}, "max_threads": #{m} }!
22
22
  end
23
23
 
24
24
  def restart
@@ -26,7 +26,7 @@ module Puma
26
26
  end
27
27
 
28
28
  def stop
29
- @server.stop false
29
+ @server.stop(false) if @server
30
30
  end
31
31
 
32
32
  def halt
@@ -36,7 +36,7 @@ module Puma
36
36
  def stop_blocked
37
37
  log "- Gracefully stopping, waiting for requests to finish"
38
38
  @control.stop(true) if @control
39
- @server.stop(true)
39
+ @server.stop(true) if @server
40
40
  end
41
41
 
42
42
  def jruby_daemon?
@@ -87,8 +87,7 @@ module Puma
87
87
  @spawned += 1
88
88
 
89
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=)
90
+ Puma.set_thread_name 'threadpool %03i' % spawned
92
91
  todo = @todo
93
92
  block = @block
94
93
  mutex = @mutex
@@ -190,10 +189,13 @@ module Puma
190
189
  # request, it might not be added to the `@todo` array right away.
191
190
  # For example if a slow client has only sent a header, but not a body
192
191
  # 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
192
+ # to try to buffer the request. In that scenario the next call to this
194
193
  # method would not block and another request would be added into the reactor
195
194
  # by the server. This would continue until a fully bufferend request
196
195
  # makes it through the reactor and can then be processed by the thread pool.
196
+ #
197
+ # Returns the current number of busy threads, or +nil+ if shutting down.
198
+ #
197
199
  def wait_until_not_full
198
200
  @mutex.synchronize do
199
201
  while true
@@ -203,7 +205,8 @@ module Puma
203
205
  # is work queued that cannot be handled by waiting
204
206
  # threads, then accept more work until we would
205
207
  # spin up the max number of threads.
206
- return if @todo.size - @waiting < @max - @spawned
208
+ busy_threads = @spawned - @waiting + @todo.size
209
+ return busy_threads if @max > busy_threads
207
210
 
208
211
  @not_full.wait @mutex
209
212
  end
@@ -240,10 +243,12 @@ module Puma
240
243
  end
241
244
  end
242
245
 
243
- class AutoTrim
244
- def initialize(pool, timeout)
246
+ class Automaton
247
+ def initialize(pool, timeout, thread_name, message)
245
248
  @pool = pool
246
249
  @timeout = timeout
250
+ @thread_name = thread_name
251
+ @message = message
247
252
  @running = false
248
253
  end
249
254
 
@@ -251,8 +256,9 @@ module Puma
251
256
  @running = true
252
257
 
253
258
  @thread = Thread.new do
259
+ Puma.set_thread_name @thread_name
254
260
  while @running
255
- @pool.trim
261
+ @pool.public_send(@message)
256
262
  sleep @timeout
257
263
  end
258
264
  end
@@ -265,36 +271,12 @@ module Puma
265
271
  end
266
272
 
267
273
  def auto_trim!(timeout=30)
268
- @auto_trim = AutoTrim.new(self, timeout)
274
+ @auto_trim = Automaton.new(self, timeout, "threadpool trimmer", :trim)
269
275
  @auto_trim.start!
270
276
  end
271
277
 
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
278
  def auto_reap!(timeout=5)
297
- @reaper = Reaper.new(self, timeout)
279
+ @reaper = Automaton.new(self, timeout, "threadpool reaper", :reap)
298
280
  @reaper.start!
299
281
  end
300
282
 
@@ -1,11 +1,6 @@
1
1
  # frozen_string_literal: true
2
- major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
3
2
 
4
- if major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125
5
- require 'puma/rack/backports/uri/common_193'
6
- else
7
- require 'uri/common'
8
- end
3
+ require 'uri/common'
9
4
 
10
5
  module Puma
11
6
  module Util
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rack/handler'
2
4
 
3
5
  module Rack
@@ -59,8 +61,6 @@ module Rack
59
61
  conf
60
62
  end
61
63
 
62
-
63
-
64
64
  def self.run(app, options = {})
65
65
  conf = self.config(app, options)
66
66
 
@@ -86,7 +86,7 @@ module Rack
86
86
  "Verbose" => "Don't report each request (default: false)"
87
87
  }
88
88
  end
89
- private
89
+
90
90
  def self.set_host_port_to_config(host, port, config)
91
91
  config.clear_binds! if host || port
92
92
 
@@ -0,0 +1,16 @@
1
+ # Use this Dockerfile to create minimal reproductions of issues
2
+
3
+ FROM ruby:2.6
4
+
5
+ # throw errors if Gemfile has been modified since Gemfile.lock
6
+ RUN bundle config --global frozen 1
7
+
8
+ WORKDIR /usr/src/app
9
+
10
+ COPY . .
11
+ RUN gem install bundler
12
+ RUN bundle install
13
+ RUN bundle exec rake compile
14
+
15
+ EXPOSE 9292
16
+ CMD bundle exec bin/puma test/rackup/hello.ru
@@ -47,11 +47,11 @@ do_start_one() {
47
47
  PIDFILE=$1/tmp/puma/pid
48
48
  if [ -e $PIDFILE ]; then
49
49
  PID=`cat $PIDFILE`
50
- # If the puma isn't running, run it, otherwise restart it.
50
+ # If the puma is running, restart it, otherwise run it.
51
51
  if ps -p $PID > /dev/null; then
52
- do_start_one_do $1
53
- else
54
52
  do_restart_one $1
53
+ else
54
+ do_start_one_do $1
55
55
  fi
56
56
  else
57
57
  do_start_one_do $1
@@ -106,8 +106,6 @@ do_stop_one() {
106
106
  if [ -e $PIDFILE ]; then
107
107
  PID=`cat $PIDFILE`
108
108
  if ps -p $PID > /dev/null; then
109
- log_daemon_msg "---> Puma $1 isn't running."
110
- else
111
109
  log_daemon_msg "---> About to kill PID `cat $PIDFILE`"
112
110
  if [ "$USE_LOCAL_BUNDLE" -eq 1 ]; then
113
111
  cd $1 && bundle exec pumactl --state $STATEFILE stop
@@ -116,6 +114,8 @@ do_stop_one() {
116
114
  fi
117
115
  # Many daemons don't delete their pidfiles when they exit.
118
116
  rm -f $PIDFILE $STATEFILE
117
+ else
118
+ log_daemon_msg "---> Puma $1 isn't running."
119
119
  fi
120
120
  else
121
121
  log_daemon_msg "---> No puma here..."
@@ -398,7 +398,7 @@ case "$1" in
398
398
  ;;
399
399
  remove)
400
400
  if [ "$#" -lt 2 ]; then
401
- echo "Please, specifiy the app's directory to remove."
401
+ echo "Please, specify the app's directory to remove."
402
402
  exit 1
403
403
  else
404
404
  do_remove $2
@@ -38,7 +38,6 @@ ARGV[1].to_i.times do
38
38
  do_test(st, size)
39
39
  end
40
40
 
41
- t.abort_on_exception = true
42
41
  threads << t
43
42
  end
44
43
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.12.2
4
+ version: 4.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Evan Phoenix
@@ -9,7 +9,21 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
  date: 2019-12-05 00:00:00.000000000 Z
12
- dependencies: []
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nio4r
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.0'
13
27
  description: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server
14
28
  for Ruby/Rack applications. Puma is intended for use in both development and production
15
29
  environments. It's great for highly concurrent Ruby implementations such as Rubinius
@@ -39,6 +53,7 @@ files:
39
53
  - docs/restart.md
40
54
  - docs/signals.md
41
55
  - docs/systemd.md
56
+ - docs/tcp_mode.md
42
57
  - ext/puma_http11/PumaHttp11Service.java
43
58
  - ext/puma_http11/ext_help.h
44
59
  - ext/puma_http11/extconf.rb
@@ -51,6 +66,7 @@ files:
51
66
  - ext/puma_http11/mini_ssl.c
52
67
  - ext/puma_http11/org/jruby/puma/Http11.java
53
68
  - ext/puma_http11/org/jruby/puma/Http11Parser.java
69
+ - ext/puma_http11/org/jruby/puma/IOBuffer.java
54
70
  - ext/puma_http11/org/jruby/puma/MiniSSL.java
55
71
  - ext/puma_http11/puma_http11.c
56
72
  - lib/puma.rb
@@ -61,25 +77,20 @@ files:
61
77
  - lib/puma/client.rb
62
78
  - lib/puma/cluster.rb
63
79
  - lib/puma/commonlogger.rb
64
- - lib/puma/compat.rb
65
80
  - lib/puma/configuration.rb
66
81
  - lib/puma/const.rb
67
82
  - lib/puma/control_cli.rb
68
- - lib/puma/convenient.rb
69
- - lib/puma/daemon_ext.rb
70
- - lib/puma/delegation.rb
71
83
  - lib/puma/detect.rb
72
84
  - lib/puma/dsl.rb
73
85
  - lib/puma/events.rb
74
86
  - lib/puma/io_buffer.rb
75
- - lib/puma/java_io_buffer.rb
76
87
  - lib/puma/jruby_restart.rb
77
88
  - lib/puma/launcher.rb
78
89
  - lib/puma/minissl.rb
90
+ - lib/puma/minissl/context_builder.rb
79
91
  - lib/puma/null_io.rb
80
92
  - lib/puma/plugin.rb
81
93
  - lib/puma/plugin/tmp_restart.rb
82
- - lib/puma/rack/backports/uri/common_193.rb
83
94
  - lib/puma/rack/builder.rb
84
95
  - lib/puma/rack/urlmap.rb
85
96
  - lib/puma/rack_default.rb
@@ -92,6 +103,7 @@ files:
92
103
  - lib/puma/thread_pool.rb
93
104
  - lib/puma/util.rb
94
105
  - lib/rack/handler/puma.rb
106
+ - tools/docker/Dockerfile
95
107
  - tools/jungle/README.md
96
108
  - tools/jungle/init.d/README.md
97
109
  - tools/jungle/init.d/puma
@@ -108,6 +120,7 @@ licenses:
108
120
  - BSD-3-Clause
109
121
  metadata:
110
122
  msys2_mingw_dependencies: openssl
123
+ changelog_uri: https://github.com/puma/puma/blob/master/History.md
111
124
  post_install_message:
112
125
  rdoc_options: []
113
126
  require_paths:
@@ -1,14 +0,0 @@
1
- # Provides code to work properly on 1.8 and 1.9
2
-
3
- class String
4
- unless method_defined? :bytesize
5
- alias_method :bytesize, :size
6
- end
7
-
8
- unless method_defined? :byteslice
9
- def byteslice(*arg)
10
- enc = self.encoding
11
- self.dup.force_encoding(Encoding::ASCII_8BIT).slice(*arg).force_encoding(enc)
12
- end
13
- end
14
- end
@@ -1,25 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'puma/launcher'
4
- require 'puma/configuration'
5
-
6
- module Puma
7
- def self.run(opts={})
8
- cfg = Puma::Configuration.new do |user_config|
9
- if port = opts[:port]
10
- user_config.port port
11
- end
12
-
13
- user_config.quiet
14
-
15
- yield c
16
- end
17
-
18
- cfg.clamp
19
-
20
- events = Puma::Events.null
21
-
22
- launcher = Puma::Launcher.new cfg, :events => events
23
- launcher.run
24
- end
25
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Process
4
-
5
- # This overrides the default version because it is broken if it
6
- # exists.
7
-
8
- if respond_to? :daemon
9
- class << self
10
- remove_method :daemon
11
- end
12
- end
13
-
14
- def self.daemon(nochdir=false, noclose=false)
15
- exit if fork # Parent exits, child continues.
16
-
17
- Process.setsid # Become session leader.
18
-
19
- exit if fork # Zap session leader. See [1].
20
-
21
- Dir.chdir "/" unless nochdir # Release old working directory.
22
-
23
- if !noclose
24
- STDIN.reopen File.open("/dev/null", "r")
25
-
26
- null_out = File.open "/dev/null", "w"
27
- STDOUT.reopen null_out
28
- STDERR.reopen null_out
29
- end
30
-
31
- 0
32
- end
33
- end
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Puma
4
- module Delegation
5
- def forward(what, who)
6
- module_eval <<-CODE
7
- def #{what}(*args, &block)
8
- #{who}.#{what}(*args, &block)
9
- end
10
- CODE
11
- end
12
- end
13
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'java'
4
-
5
- # Conservative native JRuby/Java implementation of IOBuffer
6
- # backed by a ByteArrayOutputStream and conversion between
7
- # Ruby String and Java bytes
8
- module Puma
9
- class JavaIOBuffer < java.io.ByteArrayOutputStream
10
- field_reader :buf
11
- end
12
-
13
- class IOBuffer
14
- BUF_DEFAULT_SIZE = 4096
15
-
16
- def initialize
17
- @buf = JavaIOBuffer.new(BUF_DEFAULT_SIZE)
18
- end
19
-
20
- def reset
21
- @buf.reset
22
- end
23
-
24
- def <<(str)
25
- bytes = str.to_java_bytes
26
- @buf.write(bytes, 0, bytes.length)
27
- end
28
-
29
- def append(*strs)
30
- strs.each { |s| self << s; }
31
- end
32
-
33
- def to_s
34
- String.from_java_bytes @buf.to_byte_array
35
- end
36
-
37
- alias_method :to_str, :to_s
38
-
39
- def used
40
- @buf.size
41
- end
42
-
43
- def capacity
44
- @buf.buf.length
45
- end
46
- end
47
- end