puma 7.2.2 → 8.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.
- checksums.yaml +4 -4
- data/History.md +39 -22
- data/README.md +1 -2
- data/docs/5.0-Upgrade.md +98 -0
- data/docs/6.0-Upgrade.md +56 -0
- data/docs/7.0-Upgrade.md +52 -0
- data/docs/8.0-Upgrade.md +100 -0
- data/docs/grpc.md +62 -0
- data/docs/images/favicon.svg +1 -0
- data/docs/images/running-puma.svg +1 -0
- data/docs/images/standard-logo.svg +1 -0
- data/docs/signals.md +1 -1
- data/ext/puma_http11/http11_parser.java.rl +51 -65
- data/ext/puma_http11/mini_ssl.c +1 -8
- data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +168 -104
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +71 -85
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +34 -44
- data/ext/puma_http11/puma_http11.c +3 -4
- data/lib/puma/cli.rb +1 -1
- data/lib/puma/client.rb +114 -196
- data/lib/puma/client_env.rb +171 -0
- data/lib/puma/cluster.rb +1 -1
- data/lib/puma/configuration.rb +69 -8
- data/lib/puma/const.rb +3 -7
- data/lib/puma/control_cli.rb +4 -15
- data/lib/puma/detect.rb +11 -0
- data/lib/puma/dsl.rb +75 -22
- data/lib/puma/launcher.rb +3 -4
- data/lib/puma/minissl.rb +56 -101
- data/lib/puma/reactor.rb +3 -35
- data/lib/puma/{request.rb → response.rb} +16 -201
- data/lib/puma/server.rb +84 -120
- data/lib/puma/server_plugin_control.rb +32 -0
- data/lib/puma/thread_pool.rb +129 -23
- data/lib/puma/util.rb +4 -8
- data/lib/rack/handler/puma.rb +1 -1
- metadata +14 -3
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Puma
|
|
4
|
+
|
|
5
|
+
#———————————————————————— DO NOT USE — this class is for internal use only ———
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# This module is included in `Client`. It contains code to process the `env`
|
|
9
|
+
# before it is passed to the app.
|
|
10
|
+
#
|
|
11
|
+
module ClientEnv # :nodoc:
|
|
12
|
+
|
|
13
|
+
include Puma::Const
|
|
14
|
+
|
|
15
|
+
# Given a Hash +env+ for the request read from +client+, add
|
|
16
|
+
# and fixup keys to comply with Rack's env guidelines.
|
|
17
|
+
# @param env [Hash] see Puma::Client#env, from request
|
|
18
|
+
# @param client [Puma::Client] only needed for Client#peerip
|
|
19
|
+
#
|
|
20
|
+
def normalize_env
|
|
21
|
+
if host = @env[HTTP_HOST]
|
|
22
|
+
# host can be a hostname, ipv4 or bracketed ipv6. Followed by an optional port.
|
|
23
|
+
if colon = host.rindex("]:") # IPV6 with port
|
|
24
|
+
@env[SERVER_NAME] = host[0, colon+1]
|
|
25
|
+
@env[SERVER_PORT] = host[colon+2, host.bytesize]
|
|
26
|
+
elsif !host.start_with?("[") && colon = host.index(":") # not hostname or IPV4 with port
|
|
27
|
+
@env[SERVER_NAME] = host[0, colon]
|
|
28
|
+
@env[SERVER_PORT] = host[colon+1, host.bytesize]
|
|
29
|
+
else
|
|
30
|
+
@env[SERVER_NAME] = host
|
|
31
|
+
@env[SERVER_PORT] = default_server_port
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
@env[SERVER_NAME] = LOCALHOST
|
|
35
|
+
@env[SERVER_PORT] = default_server_port
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
unless @env[REQUEST_PATH]
|
|
39
|
+
# it might be a dumbass full host request header
|
|
40
|
+
uri = begin
|
|
41
|
+
URI.parse(@env[REQUEST_URI])
|
|
42
|
+
rescue URI::InvalidURIError
|
|
43
|
+
raise Puma::HttpParserError
|
|
44
|
+
end
|
|
45
|
+
@env[REQUEST_PATH] = uri.path
|
|
46
|
+
|
|
47
|
+
# A nil env value will cause a LintError (and fatal errors elsewhere),
|
|
48
|
+
# so only set the env value if there actually is a value.
|
|
49
|
+
@env[QUERY_STRING] = uri.query if uri.query
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
@env[PATH_INFO] = @env[REQUEST_PATH].to_s # #to_s in case it's nil
|
|
53
|
+
|
|
54
|
+
# From https://www.ietf.org/rfc/rfc3875 :
|
|
55
|
+
# "Script authors should be aware that the REMOTE_ADDR and
|
|
56
|
+
# REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
|
|
57
|
+
# may not identify the ultimate source of the request.
|
|
58
|
+
# They identify the client for the immediate request to the
|
|
59
|
+
# server; that client may be a proxy, gateway, or other
|
|
60
|
+
# intermediary acting on behalf of the actual source client."
|
|
61
|
+
#
|
|
62
|
+
|
|
63
|
+
unless @env.key?(REMOTE_ADDR)
|
|
64
|
+
begin
|
|
65
|
+
addr = peerip
|
|
66
|
+
rescue Errno::ENOTCONN
|
|
67
|
+
# Client disconnects can result in an inability to get the
|
|
68
|
+
# peeraddr from the socket; default to unspec.
|
|
69
|
+
if peer_family == Socket::AF_INET6
|
|
70
|
+
addr = UNSPECIFIED_IPV6
|
|
71
|
+
else
|
|
72
|
+
addr = UNSPECIFIED_IPV4
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Set unix socket addrs to localhost
|
|
77
|
+
if addr.empty?
|
|
78
|
+
addr = peer_family == Socket::AF_INET6 ? LOCALHOST_IPV6 : LOCALHOST_IPV4
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
@env[REMOTE_ADDR] = addr
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# The legacy HTTP_VERSION header can be sent as a client header.
|
|
85
|
+
# Rack v4 may remove using HTTP_VERSION. If so, remove this line.
|
|
86
|
+
@env[HTTP_VERSION] = @env[SERVER_PROTOCOL] if @env_set_http_version
|
|
87
|
+
|
|
88
|
+
@env[PUMA_SOCKET] = @io
|
|
89
|
+
|
|
90
|
+
if @env[HTTPS_KEY] && @io.peercert
|
|
91
|
+
@env[PUMA_PEERCERT] = @io.peercert
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
@env[HIJACK_P] = true
|
|
95
|
+
@env[HIJACK] = method(:full_hijack).to_proc
|
|
96
|
+
|
|
97
|
+
@env[RACK_INPUT] = @body || EmptyBody
|
|
98
|
+
@env[RACK_URL_SCHEME] ||= default_server_port == PORT_443 ? HTTPS : HTTP
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Fixup any headers with `,` in the name to have `_` now. We emit
|
|
102
|
+
# headers with `,` in them during the parse phase to avoid ambiguity
|
|
103
|
+
# with the `-` to `_` conversion for critical headers. But here for
|
|
104
|
+
# compatibility, we'll convert them back. This code is written to
|
|
105
|
+
# avoid allocation in the common case (ie there are no headers
|
|
106
|
+
# with `,` in their names), that's why it has the extra conditionals.
|
|
107
|
+
#
|
|
108
|
+
# @note If a normalized version of a `,` header already exists, we ignore
|
|
109
|
+
# the `,` version. This prevents clobbering headers managed by proxies
|
|
110
|
+
# but not by clients (Like X-Forwarded-For).
|
|
111
|
+
#
|
|
112
|
+
# @param env [Hash] see Puma::Client#env, from request, modifies in place
|
|
113
|
+
# @version 5.0.3
|
|
114
|
+
#
|
|
115
|
+
def req_env_post_parse
|
|
116
|
+
to_delete = nil
|
|
117
|
+
to_add = nil
|
|
118
|
+
|
|
119
|
+
@env.each do |k,v|
|
|
120
|
+
if k.start_with?("HTTP_") && k.include?(",") && !UNMASKABLE_HEADERS.key?(k)
|
|
121
|
+
if to_delete
|
|
122
|
+
to_delete << k
|
|
123
|
+
else
|
|
124
|
+
to_delete = [k]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
new_k = k.tr(",", "_")
|
|
128
|
+
if @env.key?(new_k)
|
|
129
|
+
next
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
unless to_add
|
|
133
|
+
to_add = {}
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
to_add[new_k] = v
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if to_delete # rubocop:disable Style/SafeNavigation
|
|
141
|
+
to_delete.each { |k| env.delete(k) }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
if to_add
|
|
145
|
+
@env.merge! to_add
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# A rack extension. If the app writes #call'ables to this
|
|
149
|
+
# array, we will invoke them when the request is done.
|
|
150
|
+
#
|
|
151
|
+
env[RACK_AFTER_REPLY] ||= []
|
|
152
|
+
env[RACK_RESPONSE_FINISHED] ||= []
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
HTTP_ON_VALUES = { "on" => true, HTTPS => true }
|
|
156
|
+
private_constant :HTTP_ON_VALUES
|
|
157
|
+
|
|
158
|
+
# @return [Puma::Const::PORT_443,Puma::Const::PORT_80]
|
|
159
|
+
#
|
|
160
|
+
def default_server_port
|
|
161
|
+
if HTTP_ON_VALUES[@env[HTTPS_KEY]] ||
|
|
162
|
+
@env[HTTP_X_FORWARDED_PROTO]&.start_with?(HTTPS) ||
|
|
163
|
+
@env[HTTP_X_FORWARDED_SCHEME] == HTTPS ||
|
|
164
|
+
@env[HTTP_X_FORWARDED_SSL] == "on"
|
|
165
|
+
PORT_443
|
|
166
|
+
else
|
|
167
|
+
PORT_80
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
data/lib/puma/cluster.rb
CHANGED
data/lib/puma/configuration.rb
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'socket'
|
|
4
|
+
require 'uri'
|
|
5
|
+
|
|
3
6
|
require_relative 'plugin'
|
|
4
7
|
require_relative 'const'
|
|
5
8
|
require_relative 'dsl'
|
|
@@ -131,15 +134,16 @@ module Puma
|
|
|
131
134
|
|
|
132
135
|
DEFAULTS = {
|
|
133
136
|
auto_trim_time: 30,
|
|
134
|
-
binds: ['tcp://
|
|
135
|
-
fiber_per_request: !!ENV.fetch("PUMA_FIBER_PER_REQUEST", false),
|
|
137
|
+
binds: ['tcp://[::]:9292'.freeze],
|
|
136
138
|
debug: false,
|
|
137
|
-
enable_keep_alives: true,
|
|
138
139
|
early_hints: nil,
|
|
140
|
+
enable_keep_alives: true,
|
|
139
141
|
environment: 'development'.freeze,
|
|
142
|
+
fiber_per_request: !!ENV.fetch("PUMA_FIBER_PER_REQUEST", false),
|
|
140
143
|
# Number of seconds to wait until we get the first data for the request.
|
|
141
144
|
first_data_timeout: 30,
|
|
142
|
-
|
|
145
|
+
force_shutdown_after: -1,
|
|
146
|
+
http_content_length_limit: nil,
|
|
143
147
|
# Number of seconds to wait until the next request before shutting down.
|
|
144
148
|
idle_timeout: nil,
|
|
145
149
|
io_selector_backend: :auto,
|
|
@@ -148,6 +152,7 @@ module Puma
|
|
|
148
152
|
# Limits how many requests a keep alive connection can make.
|
|
149
153
|
# The connection will be closed after it reaches `max_keep_alive`
|
|
150
154
|
# requests.
|
|
155
|
+
max_io_threads: 0,
|
|
151
156
|
max_keep_alive: 999,
|
|
152
157
|
max_threads: Puma.mri? ? 5 : 16,
|
|
153
158
|
min_threads: 0,
|
|
@@ -162,10 +167,10 @@ module Puma
|
|
|
162
167
|
raise_exception_on_sigterm: true,
|
|
163
168
|
reaping_time: 1,
|
|
164
169
|
remote_address: :socket,
|
|
165
|
-
silence_single_worker_warning: false,
|
|
166
170
|
silence_fork_callback_warning: false,
|
|
171
|
+
silence_single_worker_warning: false,
|
|
167
172
|
tag: File.basename(Dir.getwd),
|
|
168
|
-
tcp_host: '
|
|
173
|
+
tcp_host: '::'.freeze,
|
|
169
174
|
tcp_port: 9292,
|
|
170
175
|
wait_for_less_busy_worker: 0.005,
|
|
171
176
|
worker_boot_timeout: 60,
|
|
@@ -174,11 +179,10 @@ module Puma
|
|
|
174
179
|
worker_shutdown_timeout: 30,
|
|
175
180
|
worker_timeout: 60,
|
|
176
181
|
workers: 0,
|
|
177
|
-
http_content_length_limit: nil
|
|
178
182
|
}
|
|
179
183
|
|
|
180
184
|
def initialize(user_options={}, default_options = {}, env = ENV, &block)
|
|
181
|
-
default_options = self.puma_default_options(env).merge(default_options)
|
|
185
|
+
default_options = self.puma_default_options(env).merge(events: Events.new).merge(default_options)
|
|
182
186
|
|
|
183
187
|
@_options = UserFileDefaultOptions.new(user_options, default_options)
|
|
184
188
|
@plugins = PluginLoader.new
|
|
@@ -231,6 +235,8 @@ module Puma
|
|
|
231
235
|
|
|
232
236
|
def puma_default_options(env = ENV)
|
|
233
237
|
defaults = DEFAULTS.dup
|
|
238
|
+
defaults[:tcp_host] = self.class.default_tcp_host
|
|
239
|
+
defaults[:binds] = [self.class.default_tcp_bind]
|
|
234
240
|
puma_options_from_env(env).each { |k,v| defaults[k] = v if v }
|
|
235
241
|
defaults
|
|
236
242
|
end
|
|
@@ -278,8 +284,10 @@ module Puma
|
|
|
278
284
|
# This also calls load if it hasn't been called yet.
|
|
279
285
|
def clamp
|
|
280
286
|
load unless @loaded
|
|
287
|
+
run_mode_hooks
|
|
281
288
|
set_conditional_default_options
|
|
282
289
|
@_options.finalize_values
|
|
290
|
+
rewrite_unavailable_ipv6_binds!
|
|
283
291
|
@clamped = true
|
|
284
292
|
warn_hooks
|
|
285
293
|
options
|
|
@@ -358,6 +366,23 @@ module Puma
|
|
|
358
366
|
options.final_options
|
|
359
367
|
end
|
|
360
368
|
|
|
369
|
+
def self.default_tcp_host
|
|
370
|
+
ipv6_interface_available? ? Const::UNSPECIFIED_IPV6 : Const::UNSPECIFIED_IPV4
|
|
371
|
+
end
|
|
372
|
+
|
|
373
|
+
def self.default_tcp_bind(port = DEFAULTS[:tcp_port])
|
|
374
|
+
URI::Generic.build(scheme: 'tcp', host: default_tcp_host, port: Integer(port)).to_s
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
def self.ipv6_interface_available?
|
|
378
|
+
Socket.getifaddrs.any? do |ifaddr|
|
|
379
|
+
addr = ifaddr.addr
|
|
380
|
+
addr&.ipv6? && !addr&.ipv6_loopback?
|
|
381
|
+
end
|
|
382
|
+
rescue StandardError
|
|
383
|
+
false
|
|
384
|
+
end
|
|
385
|
+
|
|
361
386
|
def self.temp_path
|
|
362
387
|
require 'tmpdir'
|
|
363
388
|
|
|
@@ -373,6 +398,31 @@ module Puma
|
|
|
373
398
|
|
|
374
399
|
private
|
|
375
400
|
|
|
401
|
+
def rewrite_unavailable_ipv6_binds!
|
|
402
|
+
return if self.class.ipv6_interface_available?
|
|
403
|
+
|
|
404
|
+
tried_ipv6_bind = false
|
|
405
|
+
bind_schemes = ['tcp', 'ssl']
|
|
406
|
+
|
|
407
|
+
@_options[:binds] = Array(@_options[:binds]).map do |bind|
|
|
408
|
+
uri = URI.parse(bind)
|
|
409
|
+
next bind unless bind_schemes.include?(uri.scheme)
|
|
410
|
+
|
|
411
|
+
host = uri.host&.delete_prefix('[')&.delete_suffix(']')
|
|
412
|
+
next bind unless host&.include?(':')
|
|
413
|
+
|
|
414
|
+
tried_ipv6_bind = true
|
|
415
|
+
next bind unless host == Const::UNSPECIFIED_IPV6
|
|
416
|
+
|
|
417
|
+
uri.host = Const::UNSPECIFIED_IPV4
|
|
418
|
+
uri.to_s
|
|
419
|
+
rescue URI::InvalidURIError
|
|
420
|
+
bind
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
warn "WARNING: IPv6 bind requested but no non-loopback IPv6 interface was detected" if tried_ipv6_bind
|
|
424
|
+
end
|
|
425
|
+
|
|
376
426
|
def require_processor_counter
|
|
377
427
|
require 'concurrent/utility/processor_counter'
|
|
378
428
|
rescue LoadError
|
|
@@ -434,6 +484,17 @@ module Puma
|
|
|
434
484
|
rack_app
|
|
435
485
|
end
|
|
436
486
|
|
|
487
|
+
def run_mode_hooks
|
|
488
|
+
workers_before = @_options[:workers]
|
|
489
|
+
key = workers_before > 0 ? :cluster : :single
|
|
490
|
+
|
|
491
|
+
@_options.all_of(key).each(&:call)
|
|
492
|
+
|
|
493
|
+
unless @_options[:workers] == workers_before
|
|
494
|
+
raise "cannot change the number of workers inside a #{key} configuration hook"
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
|
|
437
498
|
def set_conditional_default_options
|
|
438
499
|
@_options.default_options[:preload_app] = !@_options[:prune_bundler] &&
|
|
439
500
|
(@_options[:workers] > 1) && Puma.forkable?
|
data/lib/puma/const.rb
CHANGED
|
@@ -100,8 +100,8 @@ 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 = "8.0.0"
|
|
104
|
+
CODE_NAME = "Into the Arena"
|
|
105
105
|
|
|
106
106
|
PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
|
|
107
107
|
|
|
@@ -109,9 +109,6 @@ module Puma
|
|
|
109
109
|
# sending data back
|
|
110
110
|
WRITE_TIMEOUT = 10
|
|
111
111
|
|
|
112
|
-
# How long the accept loop waits after running out of file descriptors
|
|
113
|
-
ACCEPT_ERROR_DELAY = 0.1
|
|
114
|
-
|
|
115
112
|
# The original URI requested by the client.
|
|
116
113
|
REQUEST_URI= "REQUEST_URI"
|
|
117
114
|
REQUEST_PATH = "REQUEST_PATH"
|
|
@@ -294,8 +291,7 @@ module Puma
|
|
|
294
291
|
# Banned keys of response header
|
|
295
292
|
BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
|
|
296
293
|
|
|
297
|
-
PROXY_PROTOCOL_V1_REGEX =
|
|
298
|
-
PROXY_PROTOCOL_V1_MAX_LENGTH = 107
|
|
294
|
+
PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
|
|
299
295
|
|
|
300
296
|
# All constants are prefixed with `PIPE_` to avoid name collisions.
|
|
301
297
|
module PipeRequest
|
data/lib/puma/control_cli.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Puma
|
|
|
16
16
|
'gc' => nil,
|
|
17
17
|
'gc-stats' => nil,
|
|
18
18
|
'halt' => 'SIGQUIT',
|
|
19
|
-
'info' =>
|
|
19
|
+
'info' => Puma.backtrace_signal,
|
|
20
20
|
'phased-restart' => 'SIGUSR1',
|
|
21
21
|
'refork' => 'SIGURG',
|
|
22
22
|
'reload-worker-directory' => nil,
|
|
@@ -44,7 +44,6 @@ module Puma
|
|
|
44
44
|
@pid = nil
|
|
45
45
|
@control_url = nil
|
|
46
46
|
@control_auth_token = nil
|
|
47
|
-
@control_ca_file = nil
|
|
48
47
|
@config_file = nil
|
|
49
48
|
@command = nil
|
|
50
49
|
@environment = env['APP_ENV'] || env['RACK_ENV'] || env['RAILS_ENV']
|
|
@@ -81,10 +80,6 @@ module Puma
|
|
|
81
80
|
@control_auth_token = arg
|
|
82
81
|
end
|
|
83
82
|
|
|
84
|
-
o.on "--control-ca-file PATH", "CA certificate bundle for the TLS control server" do |arg|
|
|
85
|
-
@control_ca_file = arg
|
|
86
|
-
end
|
|
87
|
-
|
|
88
83
|
o.on "-F", "--config-file PATH", "Puma config script" do |arg|
|
|
89
84
|
@config_file = arg
|
|
90
85
|
end
|
|
@@ -184,16 +179,11 @@ module Puma
|
|
|
184
179
|
when 'ssl'
|
|
185
180
|
require 'openssl'
|
|
186
181
|
host = host[1..-2] if host&.start_with? '['
|
|
187
|
-
|
|
188
|
-
ctx.ca_file = @control_ca_file if @control_ca_file
|
|
189
|
-
ctx.set_params
|
|
190
|
-
ssl = OpenSSL::SSL::SSLSocket.new(
|
|
182
|
+
OpenSSL::SSL::SSLSocket.new(
|
|
191
183
|
TCPSocket.new(host, uri.port),
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
.tap { |socket| socket.hostname = host }
|
|
184
|
+
OpenSSL::SSL::SSLContext.new)
|
|
185
|
+
.tap { |ssl| ssl.sync_close = true } # default is false
|
|
195
186
|
.tap(&:connect)
|
|
196
|
-
.tap { |socket| socket.post_connection_check host }
|
|
197
187
|
when 'tcp'
|
|
198
188
|
host = host[1..-2] if host&.start_with? '['
|
|
199
189
|
TCPSocket.new host, uri.port
|
|
@@ -240,7 +230,6 @@ module Puma
|
|
|
240
230
|
message response.last if PRINTABLE_COMMANDS.include?(@command)
|
|
241
231
|
end
|
|
242
232
|
ensure
|
|
243
|
-
server ||= ssl
|
|
244
233
|
if server
|
|
245
234
|
if uri.scheme == 'ssl'
|
|
246
235
|
server.sysclose
|
data/lib/puma/detect.rb
CHANGED
|
@@ -35,6 +35,13 @@ module Puma
|
|
|
35
35
|
IS_WINDOWS
|
|
36
36
|
end
|
|
37
37
|
|
|
38
|
+
BACKTRACE_SIGNAL =
|
|
39
|
+
if Signal.list.key?("INFO")
|
|
40
|
+
"SIGINFO"
|
|
41
|
+
elsif Signal.list.key?("PWR")
|
|
42
|
+
"SIGPWR"
|
|
43
|
+
end
|
|
44
|
+
|
|
38
45
|
# @version 5.0.0
|
|
39
46
|
def self.mri?
|
|
40
47
|
IS_MRI
|
|
@@ -44,4 +51,8 @@ module Puma
|
|
|
44
51
|
def self.forkable?
|
|
45
52
|
HAS_FORK
|
|
46
53
|
end
|
|
54
|
+
|
|
55
|
+
def self.backtrace_signal
|
|
56
|
+
BACKTRACE_SIGNAL
|
|
57
|
+
end
|
|
47
58
|
end
|
data/lib/puma/dsl.rb
CHANGED
|
@@ -155,7 +155,7 @@ module Puma
|
|
|
155
155
|
end
|
|
156
156
|
|
|
157
157
|
def default_host
|
|
158
|
-
@options[:default_host] || Configuration
|
|
158
|
+
@options[:default_host] || Configuration.default_tcp_host
|
|
159
159
|
end
|
|
160
160
|
|
|
161
161
|
def inject(&blk)
|
|
@@ -260,7 +260,8 @@ module Puma
|
|
|
260
260
|
# accepted protocols. Multiple urls can be bound to, calling +bind+ does
|
|
261
261
|
# not overwrite previous bindings.
|
|
262
262
|
#
|
|
263
|
-
# The default is "tcp://
|
|
263
|
+
# The default is "tcp://[::]:9292" when IPv6 interfaces are available,
|
|
264
|
+
# otherwise "tcp://0.0.0.0:9292".
|
|
264
265
|
#
|
|
265
266
|
# You can use query parameters within the url to specify options:
|
|
266
267
|
#
|
|
@@ -279,7 +280,7 @@ module Puma
|
|
|
279
280
|
# @example SSL cert for mutual TLS (mTLS)
|
|
280
281
|
# bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem&ca=ca.pem&verify_mode=force_peer'
|
|
281
282
|
# @example Disable optimization for low latency
|
|
282
|
-
# bind 'tcp://
|
|
283
|
+
# bind 'tcp://[::]:9292?low_latency=false'
|
|
283
284
|
# @example Socket permissions
|
|
284
285
|
# bind 'unix:///var/run/puma.sock?umask=0111'
|
|
285
286
|
#
|
|
@@ -349,7 +350,7 @@ module Puma
|
|
|
349
350
|
|
|
350
351
|
# Define how long persistent connections can be idle before Puma closes them.
|
|
351
352
|
#
|
|
352
|
-
# The default is
|
|
353
|
+
# The default is 65 seconds.
|
|
353
354
|
#
|
|
354
355
|
# @example
|
|
355
356
|
# persistent_timeout 30
|
|
@@ -398,20 +399,7 @@ module Puma
|
|
|
398
399
|
@options[:drain_on_shutdown] = which
|
|
399
400
|
end
|
|
400
401
|
|
|
401
|
-
#
|
|
402
|
-
#
|
|
403
|
-
# The default is 25 seconds.
|
|
404
|
-
#
|
|
405
|
-
# @example
|
|
406
|
-
# shutdown_body_timeout 10
|
|
407
|
-
#
|
|
408
|
-
# @see Puma::Server#graceful_shutdown
|
|
409
|
-
#
|
|
410
|
-
def shutdown_body_timeout(seconds)
|
|
411
|
-
@options[:shutdown_body_timeout] = Float(seconds)
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
# Set the environment in which the Rack app will run. The value must be
|
|
402
|
+
# Set the environment in which the rack's app will run. The value must be
|
|
415
403
|
# a string.
|
|
416
404
|
#
|
|
417
405
|
# The default is "development".
|
|
@@ -608,6 +596,29 @@ module Puma
|
|
|
608
596
|
@options[:max_threads] = max
|
|
609
597
|
end
|
|
610
598
|
|
|
599
|
+
# Configure the max number of IO threads.
|
|
600
|
+
#
|
|
601
|
+
# When request handlers know the current requests will no longer use a significant amount
|
|
602
|
+
# of CPU, they can mark the current request as IO bound using <tt>env["puma.mark_as_io_bound"]</tt>.
|
|
603
|
+
#
|
|
604
|
+
# Threads marked as IO bound are allowed to go over the max thread limit.
|
|
605
|
+
#
|
|
606
|
+
# @example
|
|
607
|
+
# threads 5
|
|
608
|
+
# max_io_threads 5
|
|
609
|
+
#
|
|
610
|
+
# The above example allows for 5 regular threads and 5 IO threads to process requests concurrently.
|
|
611
|
+
# Any IO thread over the limit is counted as a regular thread, hence the above configuration also
|
|
612
|
+
# allows for 3 regular threads and 7 IO threads for example.
|
|
613
|
+
def max_io_threads(max)
|
|
614
|
+
max = Integer(max)
|
|
615
|
+
if max < 0
|
|
616
|
+
raise "The maximum number of IO threads (#{max}) must be a positive number"
|
|
617
|
+
end
|
|
618
|
+
|
|
619
|
+
@options[:max_io_threads] = max
|
|
620
|
+
end
|
|
621
|
+
|
|
611
622
|
# Instead of using +bind+ and manually constructing a URI like:
|
|
612
623
|
#
|
|
613
624
|
# bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'
|
|
@@ -740,6 +751,44 @@ module Puma
|
|
|
740
751
|
@options[:silence_fork_callback_warning] = true
|
|
741
752
|
end
|
|
742
753
|
|
|
754
|
+
# Code to run only in single mode.
|
|
755
|
+
# Runs after all config files are loaded.
|
|
756
|
+
#
|
|
757
|
+
# This can be called multiple times.
|
|
758
|
+
#
|
|
759
|
+
# @note Single mode only.
|
|
760
|
+
#
|
|
761
|
+
# @example
|
|
762
|
+
# single do
|
|
763
|
+
# silence_fork_callback_warning
|
|
764
|
+
# end
|
|
765
|
+
#
|
|
766
|
+
def single(&block)
|
|
767
|
+
raise ArgumentError, "A block must be provided to `single`" unless block
|
|
768
|
+
|
|
769
|
+
@options[:single] ||= []
|
|
770
|
+
@options[:single] << block
|
|
771
|
+
end
|
|
772
|
+
|
|
773
|
+
# Code to run only in cluster mode.
|
|
774
|
+
# Runs after all config files are loaded.
|
|
775
|
+
#
|
|
776
|
+
# This can be called multiple times.
|
|
777
|
+
#
|
|
778
|
+
# @note Cluster mode only.
|
|
779
|
+
#
|
|
780
|
+
# @example
|
|
781
|
+
# cluster do
|
|
782
|
+
# prune_bundler
|
|
783
|
+
# end
|
|
784
|
+
#
|
|
785
|
+
def cluster(&block)
|
|
786
|
+
raise ArgumentError, "A block must be provided to `cluster`" unless block
|
|
787
|
+
|
|
788
|
+
@options[:cluster] ||= []
|
|
789
|
+
@options[:cluster] << block
|
|
790
|
+
end
|
|
791
|
+
|
|
743
792
|
# Code to run immediately before master process
|
|
744
793
|
# forks workers (once on boot). These hooks can block if necessary
|
|
745
794
|
# to wait for background operations unknown to Puma to finish before
|
|
@@ -1053,6 +1102,7 @@ module Puma
|
|
|
1053
1102
|
# new Bundler context and thus can float around as the release
|
|
1054
1103
|
# dictates.
|
|
1055
1104
|
#
|
|
1105
|
+
# @note Cluster mode only.
|
|
1056
1106
|
# @note This is incompatible with +preload_app!+.
|
|
1057
1107
|
# @note This is only supported for RubyGems 2.2+
|
|
1058
1108
|
#
|
|
@@ -1158,7 +1208,7 @@ module Puma
|
|
|
1158
1208
|
|
|
1159
1209
|
# Change the default worker timeout for booting.
|
|
1160
1210
|
#
|
|
1161
|
-
# The default is
|
|
1211
|
+
# The default is 60 seconds.
|
|
1162
1212
|
#
|
|
1163
1213
|
# @note Cluster mode only.
|
|
1164
1214
|
#
|
|
@@ -1240,11 +1290,14 @@ module Puma
|
|
|
1240
1290
|
# threads will be written to $stdout. This can help figure
|
|
1241
1291
|
# out why shutdown is hanging.
|
|
1242
1292
|
#
|
|
1243
|
-
|
|
1244
|
-
|
|
1293
|
+
# If `on_force` is true, the backtraces will be written only
|
|
1294
|
+
# when the shutdown is forced i.e. not graceful.
|
|
1295
|
+
#
|
|
1296
|
+
# @see force_shutdown_after
|
|
1297
|
+
def shutdown_debug(val = true, on_force: false)
|
|
1298
|
+
@options[:shutdown_debug] = val && on_force ? :on_force : val
|
|
1245
1299
|
end
|
|
1246
1300
|
|
|
1247
|
-
|
|
1248
1301
|
# Maximum delay of worker accept loop.
|
|
1249
1302
|
#
|
|
1250
1303
|
# Attempts to route traffic to less-busy workers by causing a busy worker to delay
|
data/lib/puma/launcher.rb
CHANGED
|
@@ -476,8 +476,8 @@ module Puma
|
|
|
476
476
|
end
|
|
477
477
|
|
|
478
478
|
begin
|
|
479
|
-
|
|
480
|
-
Signal.trap
|
|
479
|
+
if Puma.backtrace_signal
|
|
480
|
+
Signal.trap Puma.backtrace_signal do
|
|
481
481
|
thread_status do |name, backtrace|
|
|
482
482
|
@log_writer.log(name)
|
|
483
483
|
@log_writer.log(backtrace.map { |bt| " #{bt}" })
|
|
@@ -485,8 +485,7 @@ module Puma
|
|
|
485
485
|
end
|
|
486
486
|
end
|
|
487
487
|
rescue Exception
|
|
488
|
-
|
|
489
|
-
# to see this constantly on Linux.
|
|
488
|
+
log "*** SIGINFO/SIGPWR not implemented, signal based backtrace unavailable!"
|
|
490
489
|
end
|
|
491
490
|
end
|
|
492
491
|
|