puma 5.3.2 → 6.0.0
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.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +284 -11
- data/LICENSE +0 -0
- data/README.md +61 -16
- data/bin/puma-wild +1 -1
- data/docs/architecture.md +49 -16
- data/docs/compile_options.md +38 -2
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +1 -3
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/jungle/README.md +0 -0
- data/docs/jungle/rc.d/README.md +0 -0
- data/docs/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +0 -0
- data/docs/nginx.md +0 -0
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +2 -3
- data/docs/restart.md +6 -6
- data/docs/signals.md +11 -10
- data/docs/stats.md +8 -8
- data/docs/systemd.md +64 -67
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +44 -13
- data/ext/puma_http11/http11_parser.c +24 -11
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +2 -2
- data/ext/puma_http11/http11_parser.rl +2 -2
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +122 -23
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +50 -48
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +188 -102
- data/ext/puma_http11/puma_http11.c +18 -10
- data/lib/puma/app/status.rb +9 -6
- data/lib/puma/binder.rb +81 -42
- data/lib/puma/cli.rb +23 -19
- data/lib/puma/client.rb +124 -30
- data/lib/puma/cluster/worker.rb +21 -29
- data/lib/puma/cluster/worker_handle.rb +8 -1
- data/lib/puma/cluster.rb +57 -48
- data/lib/puma/commonlogger.rb +0 -0
- data/lib/puma/configuration.rb +74 -55
- data/lib/puma/const.rb +21 -24
- data/lib/puma/control_cli.rb +22 -19
- data/lib/puma/detect.rb +10 -2
- data/lib/puma/dsl.rb +196 -57
- data/lib/puma/error_logger.rb +17 -9
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +29 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/{json.rb → json_serialization.rb} +1 -1
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +108 -154
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +29 -16
- data/lib/puma/minissl.rb +115 -38
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +2 -2
- data/lib/puma/rack/builder.rb +5 -5
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +3 -3
- data/lib/puma/request.rb +293 -153
- data/lib/puma/runner.rb +63 -28
- data/lib/puma/server.rb +83 -88
- data/lib/puma/single.rb +10 -10
- data/lib/puma/state_file.rb +39 -7
- data/lib/puma/systemd.rb +3 -2
- data/lib/puma/thread_pool.rb +22 -17
- data/lib/puma/util.rb +20 -15
- data/lib/puma.rb +12 -9
- data/lib/rack/handler/puma.rb +9 -9
- data/tools/Dockerfile +1 -1
- data/tools/trickletest.rb +0 -0
- metadata +13 -9
- data/lib/puma/queue_close.rb +0 -26
data/lib/puma/cluster.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
3
|
+
require_relative 'runner'
|
4
|
+
require_relative 'util'
|
5
|
+
require_relative 'plugin'
|
6
|
+
require_relative 'cluster/worker_handle'
|
7
|
+
require_relative 'cluster/worker'
|
8
8
|
|
9
9
|
require 'time'
|
10
10
|
|
@@ -17,8 +17,8 @@ module Puma
|
|
17
17
|
# via the `spawn_workers` method call. Each worker will have it's own
|
18
18
|
# instance of a `Puma::Server`.
|
19
19
|
class Cluster < Runner
|
20
|
-
def initialize(
|
21
|
-
super
|
20
|
+
def initialize(launcher)
|
21
|
+
super(launcher)
|
22
22
|
|
23
23
|
@phase = 0
|
24
24
|
@workers = []
|
@@ -27,6 +27,10 @@ module Puma
|
|
27
27
|
@phased_restart = false
|
28
28
|
end
|
29
29
|
|
30
|
+
# Returns the list of cluster worker handles.
|
31
|
+
# @return [Array<Puma::Cluster::WorkerHandle>]
|
32
|
+
attr_reader :workers
|
33
|
+
|
30
34
|
def stop_workers
|
31
35
|
log "- Gracefully shutting down workers..."
|
32
36
|
@workers.each { |x| x.term }
|
@@ -92,7 +96,7 @@ module Puma
|
|
92
96
|
|
93
97
|
# @version 5.0.0
|
94
98
|
def spawn_worker(idx, master)
|
95
|
-
@
|
99
|
+
@config.run_hooks(:before_worker_fork, idx, @log_writer)
|
96
100
|
|
97
101
|
pid = fork { worker(idx, master) }
|
98
102
|
if !pid
|
@@ -101,31 +105,49 @@ module Puma
|
|
101
105
|
exit! 1
|
102
106
|
end
|
103
107
|
|
104
|
-
@
|
108
|
+
@config.run_hooks(:after_worker_fork, idx, @log_writer)
|
105
109
|
pid
|
106
110
|
end
|
107
111
|
|
108
112
|
def cull_workers
|
109
113
|
diff = @workers.size - @options[:workers]
|
110
114
|
return if diff < 1
|
115
|
+
debug "Culling #{diff} workers"
|
111
116
|
|
112
|
-
|
113
|
-
|
114
|
-
workers_to_cull = @workers[-diff,diff]
|
115
|
-
debug "Workers to cull: #{workers_to_cull.inspect}"
|
117
|
+
workers = workers_to_cull(diff)
|
118
|
+
debug "Workers to cull: #{workers.inspect}"
|
116
119
|
|
117
|
-
|
120
|
+
workers.each do |worker|
|
118
121
|
log "- Worker #{worker.index} (PID: #{worker.pid}) terminating"
|
119
122
|
worker.term
|
120
123
|
end
|
121
124
|
end
|
122
125
|
|
126
|
+
def workers_to_cull(diff)
|
127
|
+
workers = @workers.sort_by(&:started_at)
|
128
|
+
|
129
|
+
# In fork_worker mode, worker 0 acts as our master process.
|
130
|
+
# We should avoid culling it to preserve copy-on-write memory gains.
|
131
|
+
workers.reject! { |w| w.index == 0 } if @options[:fork_worker]
|
132
|
+
|
133
|
+
workers[cull_start_index(diff), diff]
|
134
|
+
end
|
135
|
+
|
136
|
+
def cull_start_index(diff)
|
137
|
+
case @options[:worker_culling_strategy]
|
138
|
+
when :oldest
|
139
|
+
0
|
140
|
+
else # :youngest
|
141
|
+
-diff
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
123
145
|
# @!attribute [r] next_worker_index
|
124
146
|
def next_worker_index
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
147
|
+
occupied_positions = @workers.map(&:index)
|
148
|
+
idx = 0
|
149
|
+
idx += 1 until !occupied_positions.include?(idx)
|
150
|
+
idx
|
129
151
|
end
|
130
152
|
|
131
153
|
def all_workers_booted?
|
@@ -135,7 +157,7 @@ module Puma
|
|
135
157
|
def check_workers
|
136
158
|
return if @next_check >= Time.now
|
137
159
|
|
138
|
-
@next_check = Time.now +
|
160
|
+
@next_check = Time.now + @options[:worker_check_interval]
|
139
161
|
|
140
162
|
timeout_workers
|
141
163
|
wait_workers
|
@@ -158,20 +180,10 @@ module Puma
|
|
158
180
|
end
|
159
181
|
end
|
160
182
|
|
161
|
-
|
162
|
-
|
163
|
-
@next_check
|
164
|
-
].compact.min
|
165
|
-
end
|
166
|
-
|
167
|
-
def wakeup!
|
168
|
-
return unless @wakeup
|
183
|
+
t = @workers.reject(&:term?)
|
184
|
+
t.map!(&:ping_timeout)
|
169
185
|
|
170
|
-
|
171
|
-
@wakeup.write "!" unless @wakeup.closed?
|
172
|
-
rescue SystemCallError, IOError
|
173
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
174
|
-
end
|
186
|
+
@next_check = [t.min, @next_check].compact.min
|
175
187
|
end
|
176
188
|
|
177
189
|
def worker(index, master)
|
@@ -201,8 +213,8 @@ module Puma
|
|
201
213
|
stop
|
202
214
|
end
|
203
215
|
|
204
|
-
def phased_restart
|
205
|
-
return false if @options[:preload_app]
|
216
|
+
def phased_restart(refork = false)
|
217
|
+
return false if @options[:preload_app] && !refork
|
206
218
|
|
207
219
|
@phased_restart = true
|
208
220
|
wakeup!
|
@@ -218,7 +230,7 @@ module Puma
|
|
218
230
|
def stop_blocked
|
219
231
|
@status = :stop if @status == :run
|
220
232
|
wakeup!
|
221
|
-
@control
|
233
|
+
@control&.stop true
|
222
234
|
Process.waitall
|
223
235
|
end
|
224
236
|
|
@@ -257,7 +269,7 @@ module Puma
|
|
257
269
|
booted_workers: worker_status.count { |w| w[:booted] },
|
258
270
|
old_workers: old_worker_count,
|
259
271
|
worker_status: worker_status,
|
260
|
-
}
|
272
|
+
}.merge(super)
|
261
273
|
end
|
262
274
|
|
263
275
|
def preload?
|
@@ -269,7 +281,7 @@ module Puma
|
|
269
281
|
if (worker = @workers.find { |w| w.index == 0 })
|
270
282
|
worker.phase += 1
|
271
283
|
end
|
272
|
-
phased_restart
|
284
|
+
phased_restart(true)
|
273
285
|
end
|
274
286
|
|
275
287
|
# We do this in a separate method to keep the lambda scope
|
@@ -282,7 +294,7 @@ module Puma
|
|
282
294
|
|
283
295
|
# Auto-fork after the specified number of requests.
|
284
296
|
if (fork_requests = @options[:fork_worker].to_i) > 0
|
285
|
-
@
|
297
|
+
@events.register(:ping!) do |w|
|
286
298
|
fork_worker! if w.index == 0 &&
|
287
299
|
w.phase == 0 &&
|
288
300
|
w.last_status[:requests_count] >= fork_requests
|
@@ -364,12 +376,12 @@ module Puma
|
|
364
376
|
else
|
365
377
|
log "* Restarts: (\u2714) hot (\u2714) phased"
|
366
378
|
|
367
|
-
unless @
|
379
|
+
unless @config.app_configured?
|
368
380
|
error "No application configured, nothing to run"
|
369
381
|
exit 1
|
370
382
|
end
|
371
383
|
|
372
|
-
@launcher.binder.parse @options[:binds]
|
384
|
+
@launcher.binder.parse @options[:binds]
|
373
385
|
end
|
374
386
|
|
375
387
|
read, @wakeup = Puma::Util.pipe
|
@@ -401,8 +413,7 @@ module Puma
|
|
401
413
|
|
402
414
|
@master_read, @worker_write = read, @wakeup
|
403
415
|
|
404
|
-
@
|
405
|
-
Puma::Util.nakayoshi_gc @events if @options[:nakayoshi_fork]
|
416
|
+
@config.run_hooks(:before_fork, nil, @log_writer)
|
406
417
|
|
407
418
|
spawn_workers
|
408
419
|
|
@@ -426,9 +437,7 @@ module Puma
|
|
426
437
|
|
427
438
|
check_workers
|
428
439
|
|
429
|
-
|
430
|
-
|
431
|
-
if res
|
440
|
+
if read.wait_readable([0, @next_check - Time.now].max)
|
432
441
|
req = read.read_nonblock(1)
|
433
442
|
|
434
443
|
@next_check = Time.now if req == "!"
|
@@ -452,14 +461,14 @@ module Puma
|
|
452
461
|
workers_not_booted -= 1
|
453
462
|
when "e"
|
454
463
|
# external term, see worker method, Signal.trap "SIGTERM"
|
455
|
-
w.
|
464
|
+
w.term!
|
456
465
|
when "t"
|
457
466
|
w.term unless w.term?
|
458
467
|
when "p"
|
459
468
|
w.ping!(result.sub(/^\d+/,'').chomp)
|
460
|
-
@
|
469
|
+
@events.fire(:ping!, w)
|
461
470
|
if !booted && @workers.none? {|worker| worker.last_status.empty?}
|
462
|
-
@
|
471
|
+
@events.fire_on_booted!
|
463
472
|
booted = true
|
464
473
|
end
|
465
474
|
end
|
data/lib/puma/commonlogger.rb
CHANGED
File without changes
|
data/lib/puma/configuration.rb
CHANGED
@@ -1,20 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require_relative 'rack/builder'
|
4
|
+
require_relative 'plugin'
|
5
|
+
require_relative 'const'
|
6
|
+
# note that dsl is loaded at end of file, requires ConfigDefault constants
|
6
7
|
|
7
8
|
module Puma
|
8
|
-
|
9
|
-
module ConfigDefault
|
10
|
-
DefaultRackup = "config.ru"
|
11
|
-
|
12
|
-
DefaultTCPHost = "0.0.0.0"
|
13
|
-
DefaultTCPPort = 9292
|
14
|
-
DefaultWorkerTimeout = 60
|
15
|
-
DefaultWorkerShutdownTimeout = 30
|
16
|
-
end
|
17
|
-
|
18
9
|
# A class used for storing "leveled" configuration options.
|
19
10
|
#
|
20
11
|
# In this class any "user" specified options take precedence over any
|
@@ -135,7 +126,48 @@ module Puma
|
|
135
126
|
# is done because an environment variable may have been modified while loading
|
136
127
|
# configuration files.
|
137
128
|
class Configuration
|
138
|
-
|
129
|
+
DEFAULTS = {
|
130
|
+
auto_trim_time: 30,
|
131
|
+
binds: ['tcp://0.0.0.0:9292'.freeze],
|
132
|
+
clean_thread_locals: false,
|
133
|
+
debug: false,
|
134
|
+
early_hints: nil,
|
135
|
+
environment: 'development'.freeze,
|
136
|
+
# Number of seconds to wait until we get the first data for the request
|
137
|
+
first_data_timeout: 30,
|
138
|
+
io_selector_backend: :auto,
|
139
|
+
log_requests: false,
|
140
|
+
logger: STDOUT,
|
141
|
+
# How many requests to attempt inline before sending a client back to
|
142
|
+
# the reactor to be subject to normal ordering. The idea here is that
|
143
|
+
# we amortize the cost of going back to the reactor for a well behaved
|
144
|
+
# but very "greedy" client across 10 requests. This prevents a not
|
145
|
+
# well behaved client from monopolizing the thread forever.
|
146
|
+
max_fast_inline: 10,
|
147
|
+
max_threads: Puma.mri? ? 5 : 16,
|
148
|
+
min_threads: 0,
|
149
|
+
mode: :http,
|
150
|
+
mutate_stdout_and_stderr_to_sync_on_write: true,
|
151
|
+
out_of_band: [],
|
152
|
+
# Number of seconds for another request within a persistent session.
|
153
|
+
persistent_timeout: 20,
|
154
|
+
queue_requests: true,
|
155
|
+
rackup: 'config.ru'.freeze,
|
156
|
+
raise_exception_on_sigterm: true,
|
157
|
+
reaping_time: 1,
|
158
|
+
remote_address: :socket,
|
159
|
+
silence_single_worker_warning: false,
|
160
|
+
tag: File.basename(Dir.getwd),
|
161
|
+
tcp_host: '0.0.0.0'.freeze,
|
162
|
+
tcp_port: 9292,
|
163
|
+
wait_for_less_busy_worker: 0.005,
|
164
|
+
worker_boot_timeout: 60,
|
165
|
+
worker_check_interval: 5,
|
166
|
+
worker_culling_strategy: :youngest,
|
167
|
+
worker_shutdown_timeout: 30,
|
168
|
+
worker_timeout: 60,
|
169
|
+
workers: 0,
|
170
|
+
}
|
139
171
|
|
140
172
|
def initialize(user_options={}, default_options = {}, &block)
|
141
173
|
default_options = self.puma_default_options.merge(default_options)
|
@@ -180,35 +212,22 @@ module Puma
|
|
180
212
|
self
|
181
213
|
end
|
182
214
|
|
183
|
-
|
184
|
-
|
185
|
-
|
215
|
+
def puma_default_options
|
216
|
+
defaults = DEFAULTS.dup
|
217
|
+
puma_options_from_env.each { |k,v| defaults[k] = v if v }
|
218
|
+
defaults
|
186
219
|
end
|
187
220
|
|
188
|
-
def
|
221
|
+
def puma_options_from_env
|
222
|
+
min = ENV['PUMA_MIN_THREADS'] || ENV['MIN_THREADS']
|
223
|
+
max = ENV['PUMA_MAX_THREADS'] || ENV['MAX_THREADS']
|
224
|
+
workers = ENV['WEB_CONCURRENCY']
|
225
|
+
|
189
226
|
{
|
190
|
-
:
|
191
|
-
:
|
192
|
-
:
|
193
|
-
:
|
194
|
-
:binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
|
195
|
-
:workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
|
196
|
-
:silence_single_worker_warning => false,
|
197
|
-
:mode => :http,
|
198
|
-
:worker_timeout => DefaultWorkerTimeout,
|
199
|
-
:worker_boot_timeout => DefaultWorkerTimeout,
|
200
|
-
:worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
|
201
|
-
:remote_address => :socket,
|
202
|
-
:tag => method(:infer_tag),
|
203
|
-
:environment => -> { ENV['RACK_ENV'] || ENV['RAILS_ENV'] || "development" },
|
204
|
-
:rackup => DefaultRackup,
|
205
|
-
:logger => STDOUT,
|
206
|
-
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
207
|
-
:first_data_timeout => Const::FIRST_DATA_TIMEOUT,
|
208
|
-
:raise_exception_on_sigterm => true,
|
209
|
-
:max_fast_inline => Const::MAX_FAST_INLINE,
|
210
|
-
:io_selector_backend => :auto,
|
211
|
-
:mutate_stdout_and_stderr_to_sync_on_write => true,
|
227
|
+
min_threads: min && Integer(min),
|
228
|
+
max_threads: max && Integer(max),
|
229
|
+
workers: workers && Integer(workers),
|
230
|
+
environment: ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'],
|
212
231
|
}
|
213
232
|
end
|
214
233
|
|
@@ -224,7 +243,7 @@ module Puma
|
|
224
243
|
return [] if files == ['-']
|
225
244
|
return files if files.any?
|
226
245
|
|
227
|
-
first_default_file = %W(config/puma/#{
|
246
|
+
first_default_file = %W(config/puma/#{@options[:environment]}.rb config/puma.rb).find do |f|
|
228
247
|
File.exist?(f)
|
229
248
|
end
|
230
249
|
|
@@ -267,7 +286,7 @@ module Puma
|
|
267
286
|
found = options[:app] || load_rackup
|
268
287
|
|
269
288
|
if @options[:log_requests]
|
270
|
-
|
289
|
+
require_relative 'commonlogger'
|
271
290
|
logger = @options[:logger]
|
272
291
|
found = CommonLogger.new(found, logger)
|
273
292
|
end
|
@@ -280,21 +299,25 @@ module Puma
|
|
280
299
|
@options[:environment]
|
281
300
|
end
|
282
301
|
|
283
|
-
def environment_str
|
284
|
-
environment.respond_to?(:call) ? environment.call : environment
|
285
|
-
end
|
286
|
-
|
287
302
|
def load_plugin(name)
|
288
303
|
@plugins.create name
|
289
304
|
end
|
290
305
|
|
291
|
-
|
306
|
+
# @param key [:Symbol] hook to run
|
307
|
+
# @param arg [Launcher, Int] `:on_restart` passes Launcher
|
308
|
+
#
|
309
|
+
def run_hooks(key, arg, log_writer, hook_data = nil)
|
292
310
|
@options.all_of(key).each do |b|
|
293
311
|
begin
|
294
|
-
b
|
312
|
+
if Array === b
|
313
|
+
hook_data[b[1]] ||= Hash.new
|
314
|
+
b[0].call arg, hook_data[b[1]]
|
315
|
+
else
|
316
|
+
b.call arg
|
317
|
+
end
|
295
318
|
rescue => e
|
296
|
-
|
297
|
-
|
319
|
+
log_writer.log "WARNING hook #{key} failed with exception (#{e.class}) #{e.message}"
|
320
|
+
log_writer.debug e.backtrace.join("\n")
|
298
321
|
end
|
299
322
|
end
|
300
323
|
end
|
@@ -312,10 +335,6 @@ module Puma
|
|
312
335
|
|
313
336
|
private
|
314
337
|
|
315
|
-
def infer_tag
|
316
|
-
File.basename(Dir.getwd)
|
317
|
-
end
|
318
|
-
|
319
338
|
# Load and use the normal Rack builder if we can, otherwise
|
320
339
|
# fallback to our minimal version.
|
321
340
|
def rack_builder
|
@@ -365,4 +384,4 @@ module Puma
|
|
365
384
|
end
|
366
385
|
end
|
367
386
|
|
368
|
-
|
387
|
+
require_relative 'dsl'
|
data/lib/puma/const.rb
CHANGED
@@ -76,7 +76,7 @@ module Puma
|
|
76
76
|
508 => 'Loop Detected',
|
77
77
|
510 => 'Not Extended',
|
78
78
|
511 => 'Network Authentication Required'
|
79
|
-
}
|
79
|
+
}.freeze
|
80
80
|
|
81
81
|
# For some HTTP status codes the client only expects headers.
|
82
82
|
#
|
@@ -85,7 +85,7 @@ module Puma
|
|
85
85
|
204 => true,
|
86
86
|
205 => true,
|
87
87
|
304 => true
|
88
|
-
}
|
88
|
+
}.freeze
|
89
89
|
|
90
90
|
# Frequently used constants when constructing requests or responses. Many times
|
91
91
|
# the constant just refers to a string with the same contents. Using these constants
|
@@ -100,32 +100,17 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "6.0.0".freeze
|
104
|
+
CODE_NAME = "Sunflower".freeze
|
105
105
|
|
106
106
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
107
107
|
|
108
108
|
FAST_TRACK_KA_TIMEOUT = 0.2
|
109
109
|
|
110
|
-
# The default number of seconds for another request within a persistent
|
111
|
-
# session.
|
112
|
-
PERSISTENT_TIMEOUT = 20
|
113
|
-
|
114
|
-
# The default number of seconds to wait until we get the first data
|
115
|
-
# for the request
|
116
|
-
FIRST_DATA_TIMEOUT = 30
|
117
|
-
|
118
110
|
# How long to wait when getting some write blocking on the socket when
|
119
111
|
# sending data back
|
120
112
|
WRITE_TIMEOUT = 10
|
121
113
|
|
122
|
-
# How many requests to attempt inline before sending a client back to
|
123
|
-
# the reactor to be subject to normal ordering. The idea here is that
|
124
|
-
# we amortize the cost of going back to the reactor for a well behaved
|
125
|
-
# but very "greedy" client across 10 requests. This prevents a not
|
126
|
-
# well behaved client from monopolizing the thread forever.
|
127
|
-
MAX_FAST_INLINE = 10
|
128
|
-
|
129
114
|
# The original URI requested by the client.
|
130
115
|
REQUEST_URI= 'REQUEST_URI'.freeze
|
131
116
|
REQUEST_PATH = 'REQUEST_PATH'.freeze
|
@@ -145,9 +130,11 @@ module Puma
|
|
145
130
|
408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze,
|
146
131
|
# Indicate that there was an internal error, obviously.
|
147
132
|
500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze,
|
133
|
+
# Incorrect or invalid header value
|
134
|
+
501 => "HTTP/1.1 501 Not Implemented\r\n\r\n".freeze,
|
148
135
|
# A common header for indicating the server is too busy. Not used yet.
|
149
136
|
503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
150
|
-
}
|
137
|
+
}.freeze
|
151
138
|
|
152
139
|
# The basic max request size we'll try to read.
|
153
140
|
CHUNK_SIZE = 16 * 1024
|
@@ -161,6 +148,14 @@ module Puma
|
|
161
148
|
|
162
149
|
REQUEST_METHOD = "REQUEST_METHOD".freeze
|
163
150
|
HEAD = "HEAD".freeze
|
151
|
+
GET = "GET".freeze
|
152
|
+
POST = "POST".freeze
|
153
|
+
PUT = "PUT".freeze
|
154
|
+
DELETE = "DELETE".freeze
|
155
|
+
OPTIONS = "OPTIONS".freeze
|
156
|
+
TRACE = "TRACE".freeze
|
157
|
+
PATCH = "PATCH".freeze
|
158
|
+
SUPPORTED_HTTP_METHODS = [HEAD, GET, POST, PUT, DELETE, OPTIONS, TRACE, PATCH].freeze
|
164
159
|
# ETag is based on the apache standard of hex mtime-size-inode (inode is 0 on win32)
|
165
160
|
LINE_END = "\r\n".freeze
|
166
161
|
REMOTE_ADDR = "REMOTE_ADDR".freeze
|
@@ -175,7 +170,10 @@ module Puma
|
|
175
170
|
PORT_80 = "80".freeze
|
176
171
|
PORT_443 = "443".freeze
|
177
172
|
LOCALHOST = "localhost".freeze
|
178
|
-
|
173
|
+
LOCALHOST_IPV4 = "127.0.0.1".freeze
|
174
|
+
LOCALHOST_IPV6 = "::1".freeze
|
175
|
+
UNSPECIFIED_IPV4 = "0.0.0.0".freeze
|
176
|
+
UNSPECIFIED_IPV6 = "::".freeze
|
179
177
|
|
180
178
|
SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
|
181
179
|
HTTP_11 = "HTTP/1.1".freeze
|
@@ -235,9 +233,6 @@ module Puma
|
|
235
233
|
|
236
234
|
EARLY_HINTS = "rack.early_hints".freeze
|
237
235
|
|
238
|
-
# Minimum interval to checks worker health
|
239
|
-
WORKER_CHECK_INTERVAL = 5
|
240
|
-
|
241
236
|
# Illegal character in the key or value of response header
|
242
237
|
DQUOTE = "\"".freeze
|
243
238
|
HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
|
@@ -247,5 +242,7 @@ module Puma
|
|
247
242
|
|
248
243
|
# Banned keys of response header
|
249
244
|
BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
|
245
|
+
|
246
|
+
PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
|
250
247
|
end
|
251
248
|
end
|
data/lib/puma/control_cli.rb
CHANGED
@@ -17,26 +17,27 @@ module Puma
|
|
17
17
|
CMD_PATH_SIG_MAP = {
|
18
18
|
'gc' => nil,
|
19
19
|
'gc-stats' => nil,
|
20
|
-
'halt'
|
21
|
-
'
|
22
|
-
'
|
20
|
+
'halt' => 'SIGQUIT',
|
21
|
+
'info' => 'SIGINFO',
|
22
|
+
'phased-restart' => 'SIGUSR1',
|
23
|
+
'refork' => 'SIGURG',
|
23
24
|
'reload-worker-directory' => nil,
|
24
|
-
'
|
25
|
+
'reopen-log' => 'SIGHUP',
|
26
|
+
'restart' => 'SIGUSR2',
|
25
27
|
'start' => nil,
|
26
28
|
'stats' => nil,
|
27
29
|
'status' => '',
|
28
|
-
'stop'
|
29
|
-
'thread-backtraces' => nil
|
30
|
+
'stop' => 'SIGTERM',
|
31
|
+
'thread-backtraces' => nil,
|
32
|
+
'worker-count-down' => 'SIGTTOU',
|
33
|
+
'worker-count-up' => 'SIGTTIN'
|
30
34
|
}.freeze
|
31
35
|
|
32
|
-
# @deprecated 6.0.0
|
33
|
-
COMMANDS = CMD_PATH_SIG_MAP.keys.freeze
|
34
|
-
|
35
36
|
# commands that cannot be used in a request
|
36
|
-
NO_REQ_COMMANDS = %w
|
37
|
+
NO_REQ_COMMANDS = %w[info reopen-log worker-count-down worker-count-up].freeze
|
37
38
|
|
38
39
|
# @version 5.0.0
|
39
|
-
PRINTABLE_COMMANDS = %w
|
40
|
+
PRINTABLE_COMMANDS = %w[gc-stats stats thread-backtraces].freeze
|
40
41
|
|
41
42
|
def initialize(argv, stdout=STDOUT, stderr=STDERR)
|
42
43
|
@state = nil
|
@@ -47,7 +48,7 @@ module Puma
|
|
47
48
|
@control_auth_token = nil
|
48
49
|
@config_file = nil
|
49
50
|
@command = nil
|
50
|
-
@environment = ENV['RACK_ENV'] || ENV['RAILS_ENV']
|
51
|
+
@environment = ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV']
|
51
52
|
|
52
53
|
@argv = argv.dup
|
53
54
|
@stdout = stdout
|
@@ -185,8 +186,6 @@ module Puma
|
|
185
186
|
|
186
187
|
if @command == 'status'
|
187
188
|
message 'Puma is started'
|
188
|
-
elsif NO_REQ_COMMANDS.include? @command
|
189
|
-
raise "Invalid request command: #{@command}"
|
190
189
|
else
|
191
190
|
url = "/#{@command}"
|
192
191
|
|
@@ -242,7 +241,11 @@ module Puma
|
|
242
241
|
@stdout.flush unless @stdout.sync
|
243
242
|
return
|
244
243
|
elsif sig.start_with? 'SIG'
|
245
|
-
|
244
|
+
if Signal.list.key? sig.sub(/\ASIG/, '')
|
245
|
+
Process.kill sig, @pid
|
246
|
+
else
|
247
|
+
raise "Signal '#{sig}' not available'"
|
248
|
+
end
|
246
249
|
elsif @command == 'status'
|
247
250
|
begin
|
248
251
|
Process.kill 0, @pid
|
@@ -268,7 +271,7 @@ module Puma
|
|
268
271
|
return start if @command == 'start'
|
269
272
|
prepare_configuration
|
270
273
|
|
271
|
-
if Puma.windows? || @control_url
|
274
|
+
if Puma.windows? || @control_url && !NO_REQ_COMMANDS.include?(@command)
|
272
275
|
send_request
|
273
276
|
else
|
274
277
|
send_signal
|
@@ -281,7 +284,7 @@ module Puma
|
|
281
284
|
|
282
285
|
private
|
283
286
|
def start
|
284
|
-
|
287
|
+
require_relative 'cli'
|
285
288
|
|
286
289
|
run_args = []
|
287
290
|
|
@@ -293,13 +296,13 @@ module Puma
|
|
293
296
|
run_args += ["-C", @config_file] if @config_file
|
294
297
|
run_args += ["-e", @environment] if @environment
|
295
298
|
|
296
|
-
|
299
|
+
log_writer = Puma::LogWriter.new(@stdout, @stderr)
|
297
300
|
|
298
301
|
# replace $0 because puma use it to generate restart command
|
299
302
|
puma_cmd = $0.gsub(/pumactl$/, 'puma')
|
300
303
|
$0 = puma_cmd if File.exist?(puma_cmd)
|
301
304
|
|
302
|
-
cli = Puma::CLI.new run_args,
|
305
|
+
cli = Puma::CLI.new run_args, log_writer
|
303
306
|
cli.run
|
304
307
|
end
|
305
308
|
end
|
data/lib/puma/detect.rb
CHANGED
@@ -8,10 +8,14 @@ module Puma
|
|
8
8
|
# @version 5.2.1
|
9
9
|
HAS_FORK = ::Process.respond_to? :fork
|
10
10
|
|
11
|
+
HAS_NATIVE_IO_WAIT = ::IO.public_instance_methods(false).include? :wait_readable
|
12
|
+
|
11
13
|
IS_JRUBY = Object.const_defined? :JRUBY_VERSION
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
+
IS_OSX = RUBY_PLATFORM.include? 'darwin'
|
16
|
+
|
17
|
+
IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/) ||
|
18
|
+
IS_JRUBY && RUBY_DESCRIPTION.include?('mswin')
|
15
19
|
|
16
20
|
# @version 5.2.0
|
17
21
|
IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
|
@@ -20,6 +24,10 @@ module Puma
|
|
20
24
|
IS_JRUBY
|
21
25
|
end
|
22
26
|
|
27
|
+
def self.osx?
|
28
|
+
IS_OSX
|
29
|
+
end
|
30
|
+
|
23
31
|
def self.windows?
|
24
32
|
IS_WINDOWS
|
25
33
|
end
|