puma 5.6.7 → 6.4.0

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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +269 -13
  3. data/README.md +78 -29
  4. data/bin/puma-wild +1 -1
  5. data/docs/compile_options.md +34 -0
  6. data/docs/fork_worker.md +1 -3
  7. data/docs/kubernetes.md +12 -0
  8. data/docs/nginx.md +1 -1
  9. data/docs/systemd.md +1 -2
  10. data/docs/testing_benchmarks_local_files.md +150 -0
  11. data/docs/testing_test_rackup_ci_files.md +36 -0
  12. data/ext/puma_http11/extconf.rb +11 -8
  13. data/ext/puma_http11/http11_parser.c +1 -1
  14. data/ext/puma_http11/http11_parser.h +1 -1
  15. data/ext/puma_http11/http11_parser.java.rl +2 -2
  16. data/ext/puma_http11/http11_parser.rl +2 -2
  17. data/ext/puma_http11/http11_parser_common.rl +2 -2
  18. data/ext/puma_http11/mini_ssl.c +122 -18
  19. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  20. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  21. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +156 -53
  22. data/ext/puma_http11/puma_http11.c +17 -9
  23. data/lib/puma/app/status.rb +4 -4
  24. data/lib/puma/binder.rb +50 -53
  25. data/lib/puma/cli.rb +16 -18
  26. data/lib/puma/client.rb +59 -19
  27. data/lib/puma/cluster/worker.rb +18 -11
  28. data/lib/puma/cluster/worker_handle.rb +4 -1
  29. data/lib/puma/cluster.rb +33 -30
  30. data/lib/puma/commonlogger.rb +21 -14
  31. data/lib/puma/configuration.rb +78 -58
  32. data/lib/puma/const.rb +129 -92
  33. data/lib/puma/control_cli.rb +15 -11
  34. data/lib/puma/detect.rb +4 -0
  35. data/lib/puma/dsl.rb +237 -56
  36. data/lib/puma/error_logger.rb +18 -9
  37. data/lib/puma/events.rb +6 -126
  38. data/lib/puma/io_buffer.rb +39 -4
  39. data/lib/puma/jruby_restart.rb +2 -1
  40. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  41. data/lib/puma/launcher.rb +102 -175
  42. data/lib/puma/log_writer.rb +147 -0
  43. data/lib/puma/minissl/context_builder.rb +24 -12
  44. data/lib/puma/minissl.rb +99 -11
  45. data/lib/puma/plugin/systemd.rb +90 -0
  46. data/lib/puma/plugin/tmp_restart.rb +1 -1
  47. data/lib/puma/rack/builder.rb +6 -6
  48. data/lib/puma/rack/urlmap.rb +1 -1
  49. data/lib/puma/rack_default.rb +19 -4
  50. data/lib/puma/reactor.rb +19 -10
  51. data/lib/puma/request.rb +365 -170
  52. data/lib/puma/runner.rb +56 -20
  53. data/lib/puma/sd_notify.rb +149 -0
  54. data/lib/puma/server.rb +116 -89
  55. data/lib/puma/single.rb +13 -11
  56. data/lib/puma/state_file.rb +1 -4
  57. data/lib/puma/thread_pool.rb +57 -19
  58. data/lib/puma/util.rb +0 -11
  59. data/lib/puma.rb +9 -10
  60. data/lib/rack/handler/puma.rb +113 -86
  61. metadata +9 -5
  62. data/lib/puma/queue_close.rb +0 -26
  63. data/lib/puma/systemd.rb +0 -46
  64. data/lib/rack/version_restriction.rb +0 -15
data/lib/puma/runner.rb CHANGED
@@ -1,23 +1,29 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/server'
4
- require 'puma/const'
3
+ require_relative 'server'
4
+ require_relative 'const'
5
5
 
6
6
  module Puma
7
7
  # Generic class that is used by `Puma::Cluster` and `Puma::Single` to
8
8
  # serve requests. This class spawns a new instance of `Puma::Server` via
9
9
  # a call to `start_server`.
10
10
  class Runner
11
- def initialize(cli, events)
12
- @launcher = cli
13
- @events = events
14
- @options = cli.options
11
+ def initialize(launcher)
12
+ @launcher = launcher
13
+ @log_writer = launcher.log_writer
14
+ @events = launcher.events
15
+ @config = launcher.config
16
+ @options = launcher.options
15
17
  @app = nil
16
18
  @control = nil
17
19
  @started_at = Time.now
18
20
  @wakeup = nil
19
21
  end
20
22
 
23
+ # Returns the hash of configuration options.
24
+ # @return [Puma::UserFileDefaultOptions]
25
+ attr_reader :options
26
+
21
27
  def wakeup!
22
28
  return unless @wakeup
23
29
 
@@ -36,27 +42,27 @@ module Puma
36
42
  end
37
43
 
38
44
  def log(str)
39
- @events.log str
45
+ @log_writer.log str
40
46
  end
41
47
 
42
48
  # @version 5.0.0
43
49
  def stop_control
44
- @control.stop(true) if @control
50
+ @control&.stop true
45
51
  end
46
52
 
47
53
  def error(str)
48
- @events.error str
54
+ @log_writer.error str
49
55
  end
50
56
 
51
57
  def debug(str)
52
- @events.log "- #{str}" if @options[:debug]
58
+ @log_writer.log "- #{str}" if @options[:debug]
53
59
  end
54
60
 
55
61
  def start_control
56
62
  str = @options[:control_url]
57
63
  return unless str
58
64
 
59
- require 'puma/app/status'
65
+ require_relative 'app/status'
60
66
 
61
67
  if token = @options[:control_auth_token]
62
68
  token = nil if token.empty? || token == 'none'
@@ -64,10 +70,16 @@ module Puma
64
70
 
65
71
  app = Puma::App::Status.new @launcher, token
66
72
 
67
- control = Puma::Server.new app, @launcher.events,
68
- { min_threads: 0, max_threads: 1, queue_requests: false }
73
+ # A Reactor is not created aand nio4r is not loaded when 'queue_requests: false'
74
+ # Use `nil` for events, no hooks in control server
75
+ control = Puma::Server.new app, nil,
76
+ { min_threads: 0, max_threads: 1, queue_requests: false, log_writer: @log_writer }
69
77
 
70
- control.binder.parse [str], self, 'Starting control server'
78
+ begin
79
+ control.binder.parse [str], nil, 'Starting control server'
80
+ rescue Errno::EADDRINUSE, Errno::EACCES => e
81
+ raise e, "Error: Control server address '#{str}' is already in use. Original error: #{e.message}"
82
+ end
71
83
 
72
84
  control.run thread_name: 'ctl'
73
85
  @control = control
@@ -141,29 +153,29 @@ module Puma
141
153
  end
142
154
 
143
155
  def load_and_bind
144
- unless @launcher.config.app_configured?
156
+ unless @config.app_configured?
145
157
  error "No application configured, nothing to run"
146
158
  exit 1
147
159
  end
148
160
 
149
161
  begin
150
- @app = @launcher.config.app
162
+ @app = @config.app
151
163
  rescue Exception => e
152
164
  log "! Unable to load application: #{e.class}: #{e.message}"
153
165
  raise e
154
166
  end
155
167
 
156
- @launcher.binder.parse @options[:binds], self
168
+ @launcher.binder.parse @options[:binds]
157
169
  end
158
170
 
159
171
  # @!attribute [r] app
160
172
  def app
161
- @app ||= @launcher.config.app
173
+ @app ||= @config.app
162
174
  end
163
175
 
164
176
  def start_server
165
- server = Puma::Server.new app, @launcher.events, @options
166
- server.inherit_binder @launcher.binder
177
+ server = Puma::Server.new(app, @events, @options)
178
+ server.inherit_binder(@launcher.binder)
167
179
  server
168
180
  end
169
181
 
@@ -173,5 +185,29 @@ module Puma
173
185
  raise "Cannot redirect #{io_name} to #{path}"
174
186
  end
175
187
  end
188
+
189
+ def utc_iso8601(val)
190
+ "#{val.utc.strftime '%FT%T'}Z"
191
+ end
192
+
193
+ def stats
194
+ {
195
+ versions: {
196
+ puma: Puma::Const::PUMA_VERSION,
197
+ ruby: {
198
+ engine: RUBY_ENGINE,
199
+ version: RUBY_VERSION,
200
+ patchlevel: RUBY_PATCHLEVEL
201
+ }
202
+ }
203
+ }
204
+ end
205
+
206
+ # this method call should always be guarded by `@log_writer.debug?`
207
+ def debug_loaded_extensions(str)
208
+ @log_writer.debug "────────────────────────────────── #{str}"
209
+ re_ext = /\.#{RbConfig::CONFIG['DLEXT']}\z/i
210
+ $LOADED_FEATURES.grep(re_ext).each { |f| @log_writer.debug(" #{f}") }
211
+ end
176
212
  end
177
213
  end
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "socket"
4
+
5
+ module Puma
6
+ # The MIT License
7
+ #
8
+ # Copyright (c) 2017-2022 Agis Anastasopoulos
9
+ #
10
+ # Permission is hereby granted, free of charge, to any person obtaining a copy of
11
+ # this software and associated documentation files (the "Software"), to deal in
12
+ # the Software without restriction, including without limitation the rights to
13
+ # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
14
+ # the Software, and to permit persons to whom the Software is furnished to do so,
15
+ # subject to the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be included in all
18
+ # copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
22
+ # FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
23
+ # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24
+ # IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25
+ # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
+ #
27
+ # This is a copy of https://github.com/agis/ruby-sdnotify as of commit cca575c
28
+ # The only changes made was "rehoming" it within the Puma module to avoid
29
+ # namespace collisions and applying standard's code formatting style.
30
+ #
31
+ # SdNotify is a pure-Ruby implementation of sd_notify(3). It can be used to
32
+ # notify systemd about state changes. Methods of this package are no-op on
33
+ # non-systemd systems (eg. Darwin).
34
+ #
35
+ # The API maps closely to the original implementation of sd_notify(3),
36
+ # therefore be sure to check the official man pages prior to using SdNotify.
37
+ #
38
+ # @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
39
+ module SdNotify
40
+ # Exception raised when there's an error writing to the notification socket
41
+ class NotifyError < RuntimeError; end
42
+
43
+ READY = "READY=1"
44
+ RELOADING = "RELOADING=1"
45
+ STOPPING = "STOPPING=1"
46
+ STATUS = "STATUS="
47
+ ERRNO = "ERRNO="
48
+ MAINPID = "MAINPID="
49
+ WATCHDOG = "WATCHDOG=1"
50
+ FDSTORE = "FDSTORE=1"
51
+
52
+ def self.ready(unset_env=false)
53
+ notify(READY, unset_env)
54
+ end
55
+
56
+ def self.reloading(unset_env=false)
57
+ notify(RELOADING, unset_env)
58
+ end
59
+
60
+ def self.stopping(unset_env=false)
61
+ notify(STOPPING, unset_env)
62
+ end
63
+
64
+ # @param status [String] a custom status string that describes the current
65
+ # state of the service
66
+ def self.status(status, unset_env=false)
67
+ notify("#{STATUS}#{status}", unset_env)
68
+ end
69
+
70
+ # @param errno [Integer]
71
+ def self.errno(errno, unset_env=false)
72
+ notify("#{ERRNO}#{errno}", unset_env)
73
+ end
74
+
75
+ # @param pid [Integer]
76
+ def self.mainpid(pid, unset_env=false)
77
+ notify("#{MAINPID}#{pid}", unset_env)
78
+ end
79
+
80
+ def self.watchdog(unset_env=false)
81
+ notify(WATCHDOG, unset_env)
82
+ end
83
+
84
+ def self.fdstore(unset_env=false)
85
+ notify(FDSTORE, unset_env)
86
+ end
87
+
88
+ # @param [Boolean] true if the service manager expects watchdog keep-alive
89
+ # notification messages to be sent from this process.
90
+ #
91
+ # If the $WATCHDOG_USEC environment variable is set,
92
+ # and the $WATCHDOG_PID variable is unset or set to the PID of the current
93
+ # process
94
+ #
95
+ # @note Unlike sd_watchdog_enabled(3), this method does not mutate the
96
+ # environment.
97
+ def self.watchdog?
98
+ wd_usec = ENV["WATCHDOG_USEC"]
99
+ wd_pid = ENV["WATCHDOG_PID"]
100
+
101
+ return false if !wd_usec
102
+
103
+ begin
104
+ wd_usec = Integer(wd_usec)
105
+ rescue
106
+ return false
107
+ end
108
+
109
+ return false if wd_usec <= 0
110
+ return true if !wd_pid || wd_pid == $$.to_s
111
+
112
+ false
113
+ end
114
+
115
+ # Notify systemd with the provided state, via the notification socket, if
116
+ # any.
117
+ #
118
+ # Generally this method will be used indirectly through the other methods
119
+ # of the library.
120
+ #
121
+ # @param state [String]
122
+ # @param unset_env [Boolean]
123
+ #
124
+ # @return [Fixnum, nil] the number of bytes written to the notification
125
+ # socket or nil if there was no socket to report to (eg. the program wasn't
126
+ # started by systemd)
127
+ #
128
+ # @raise [NotifyError] if there was an error communicating with the systemd
129
+ # socket
130
+ #
131
+ # @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
132
+ def self.notify(state, unset_env=false)
133
+ sock = ENV["NOTIFY_SOCKET"]
134
+
135
+ return nil if !sock
136
+
137
+ ENV.delete("NOTIFY_SOCKET") if unset_env
138
+
139
+ begin
140
+ Addrinfo.unix(sock, :DGRAM).connect do |s|
141
+ s.close_on_exec = true
142
+ s.write(state)
143
+ end
144
+ rescue StandardError => e
145
+ raise NotifyError, "#{e.class}: #{e.message}", e.backtrace
146
+ end
147
+ end
148
+ end
149
+ end