puma 4.3.6 → 5.3.2
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.
- checksums.yaml +4 -4
- data/History.md +1346 -518
- data/LICENSE +23 -20
- data/README.md +74 -31
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +24 -20
- data/docs/compile_options.md +19 -0
- data/docs/deployment.md +15 -10
- data/docs/fork_worker.md +33 -0
- data/docs/jungle/README.md +9 -0
- data/{tools → docs}/jungle/rc.d/README.md +1 -1
- data/{tools → docs}/jungle/rc.d/puma +2 -2
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +66 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +2 -2
- data/docs/rails_dev_mode.md +29 -0
- data/docs/restart.md +46 -23
- data/docs/signals.md +7 -6
- data/docs/stats.md +142 -0
- data/docs/systemd.md +27 -67
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +22 -8
- data/ext/puma_http11/http11_parser.c +45 -47
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +211 -118
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +5 -7
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +77 -18
- data/ext/puma_http11/puma_http11.c +31 -50
- data/lib/puma.rb +46 -0
- data/lib/puma/app/status.rb +47 -36
- data/lib/puma/binder.rb +177 -103
- data/lib/puma/cli.rb +11 -15
- data/lib/puma/client.rb +73 -74
- data/lib/puma/cluster.rb +184 -198
- data/lib/puma/cluster/worker.rb +183 -0
- data/lib/puma/cluster/worker_handle.rb +90 -0
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +55 -49
- data/lib/puma/const.rb +13 -5
- data/lib/puma/control_cli.rb +93 -76
- data/lib/puma/detect.rb +24 -3
- data/lib/puma/dsl.rb +266 -92
- data/lib/puma/error_logger.rb +104 -0
- data/lib/puma/events.rb +55 -34
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/json.rb +96 -0
- data/lib/puma/launcher.rb +113 -45
- data/lib/puma/minissl.rb +114 -33
- data/lib/puma/minissl/context_builder.rb +6 -3
- data/lib/puma/null_io.rb +13 -1
- data/lib/puma/plugin.rb +1 -10
- data/lib/puma/queue_close.rb +26 -0
- data/lib/puma/rack/builder.rb +0 -4
- data/lib/puma/reactor.rb +85 -369
- data/lib/puma/request.rb +467 -0
- data/lib/puma/runner.rb +29 -58
- data/lib/puma/server.rb +267 -729
- data/lib/puma/single.rb +9 -65
- data/lib/puma/state_file.rb +8 -3
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +119 -53
- data/lib/puma/util.rb +12 -0
- data/lib/rack/handler/puma.rb +2 -3
- data/tools/{docker/Dockerfile → Dockerfile} +0 -0
- metadata +25 -21
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/accept_nonblock.rb +0 -29
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
- data/tools/jungle/upstart/README.md +0 -61
- data/tools/jungle/upstart/puma-manager.conf +0 -31
- data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/const.rb
CHANGED
@@ -100,8 +100,9 @@ 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 = "5.3.2".freeze
|
104
|
+
CODE_NAME = "Sweetnighter".freeze
|
105
|
+
|
105
106
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
106
107
|
|
107
108
|
FAST_TRACK_KA_TIMEOUT = 0.2
|
@@ -175,7 +176,6 @@ module Puma
|
|
175
176
|
PORT_443 = "443".freeze
|
176
177
|
LOCALHOST = "localhost".freeze
|
177
178
|
LOCALHOST_IP = "127.0.0.1".freeze
|
178
|
-
LOCALHOST_ADDR = "127.0.0.1:0".freeze
|
179
179
|
|
180
180
|
SERVER_PROTOCOL = "SERVER_PROTOCOL".freeze
|
181
181
|
HTTP_11 = "HTTP/1.1".freeze
|
@@ -228,7 +228,6 @@ module Puma
|
|
228
228
|
COLON = ": ".freeze
|
229
229
|
|
230
230
|
NEWLINE = "\n".freeze
|
231
|
-
HTTP_INJECTION_REGEX = /[\r\n]/.freeze
|
232
231
|
|
233
232
|
HIJACK_P = "rack.hijack?".freeze
|
234
233
|
HIJACK = "rack.hijack".freeze
|
@@ -236,8 +235,17 @@ module Puma
|
|
236
235
|
|
237
236
|
EARLY_HINTS = "rack.early_hints".freeze
|
238
237
|
|
239
|
-
#
|
238
|
+
# Minimum interval to checks worker health
|
240
239
|
WORKER_CHECK_INTERVAL = 5
|
241
240
|
|
241
|
+
# Illegal character in the key or value of response header
|
242
|
+
DQUOTE = "\"".freeze
|
243
|
+
HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
|
244
|
+
ILLEGAL_HEADER_KEY_REGEX = /[\x00-\x20#{DQUOTE}#{HTTP_HEADER_DELIMITER}]/.freeze
|
245
|
+
# header values can contain HTAB?
|
246
|
+
ILLEGAL_HEADER_VALUE_REGEX = /[\x00-\x08\x0A-\x1F]/.freeze
|
247
|
+
|
248
|
+
# Banned keys of response header
|
249
|
+
BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
|
242
250
|
end
|
243
251
|
end
|
data/lib/puma/control_cli.rb
CHANGED
@@ -11,7 +11,32 @@ require 'socket'
|
|
11
11
|
module Puma
|
12
12
|
class ControlCLI
|
13
13
|
|
14
|
-
|
14
|
+
# values must be string or nil
|
15
|
+
# value of `nil` means command cannot be processed via signal
|
16
|
+
# @version 5.0.3
|
17
|
+
CMD_PATH_SIG_MAP = {
|
18
|
+
'gc' => nil,
|
19
|
+
'gc-stats' => nil,
|
20
|
+
'halt' => 'SIGQUIT',
|
21
|
+
'phased-restart' => 'SIGUSR1',
|
22
|
+
'refork' => 'SIGURG',
|
23
|
+
'reload-worker-directory' => nil,
|
24
|
+
'restart' => 'SIGUSR2',
|
25
|
+
'start' => nil,
|
26
|
+
'stats' => nil,
|
27
|
+
'status' => '',
|
28
|
+
'stop' => 'SIGTERM',
|
29
|
+
'thread-backtraces' => nil
|
30
|
+
}.freeze
|
31
|
+
|
32
|
+
# @deprecated 6.0.0
|
33
|
+
COMMANDS = CMD_PATH_SIG_MAP.keys.freeze
|
34
|
+
|
35
|
+
# commands that cannot be used in a request
|
36
|
+
NO_REQ_COMMANDS = %w{refork}.freeze
|
37
|
+
|
38
|
+
# @version 5.0.0
|
39
|
+
PRINTABLE_COMMANDS = %w{gc-stats stats thread-backtraces}.freeze
|
15
40
|
|
16
41
|
def initialize(argv, stdout=STDOUT, stderr=STDERR)
|
17
42
|
@state = nil
|
@@ -22,7 +47,7 @@ module Puma
|
|
22
47
|
@control_auth_token = nil
|
23
48
|
@config_file = nil
|
24
49
|
@command = nil
|
25
|
-
@environment = ENV['RACK_ENV']
|
50
|
+
@environment = ENV['RACK_ENV'] || ENV['RAILS_ENV']
|
26
51
|
|
27
52
|
@argv = argv.dup
|
28
53
|
@stdout = stdout
|
@@ -30,7 +55,7 @@ module Puma
|
|
30
55
|
@cli_options = {}
|
31
56
|
|
32
57
|
opts = OptionParser.new do |o|
|
33
|
-
o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{
|
58
|
+
o.banner = "Usage: pumactl (-p PID | -P pidfile | -S status_file | -C url -T token | -F config.rb) (#{CMD_PATH_SIG_MAP.keys.join("|")})"
|
34
59
|
|
35
60
|
o.on "-S", "--state PATH", "Where the state file to use is" do |arg|
|
36
61
|
@state = arg
|
@@ -71,7 +96,7 @@ module Puma
|
|
71
96
|
end
|
72
97
|
|
73
98
|
o.on_tail("-V", "--version", "Show version") do
|
74
|
-
puts Const::PUMA_VERSION
|
99
|
+
@stdout.puts Const::PUMA_VERSION
|
75
100
|
exit
|
76
101
|
end
|
77
102
|
end
|
@@ -81,6 +106,15 @@ module Puma
|
|
81
106
|
|
82
107
|
@command = argv.shift
|
83
108
|
|
109
|
+
# check presence of command
|
110
|
+
unless @command
|
111
|
+
raise "Available commands: #{CMD_PATH_SIG_MAP.keys.join(", ")}"
|
112
|
+
end
|
113
|
+
|
114
|
+
unless CMD_PATH_SIG_MAP.key? @command
|
115
|
+
raise "Invalid command: #{@command}"
|
116
|
+
end
|
117
|
+
|
84
118
|
unless @config_file == '-'
|
85
119
|
environment = @environment || 'development'
|
86
120
|
|
@@ -99,16 +133,6 @@ module Puma
|
|
99
133
|
@pidfile ||= config.options[:pidfile]
|
100
134
|
end
|
101
135
|
end
|
102
|
-
|
103
|
-
# check present of command
|
104
|
-
unless @command
|
105
|
-
raise "Available commands: #{COMMANDS.join(", ")}"
|
106
|
-
end
|
107
|
-
|
108
|
-
unless COMMANDS.include? @command
|
109
|
-
raise "Invalid command: #{@command}"
|
110
|
-
end
|
111
|
-
|
112
136
|
rescue => e
|
113
137
|
@stdout.puts e.message
|
114
138
|
exit 1
|
@@ -132,7 +156,7 @@ module Puma
|
|
132
156
|
@pid = sf.pid
|
133
157
|
elsif @pidfile
|
134
158
|
# get pid from pid_file
|
135
|
-
File.
|
159
|
+
@pid = File.read(@pidfile, mode: 'rb:UTF-8').to_i
|
136
160
|
end
|
137
161
|
end
|
138
162
|
|
@@ -140,23 +164,29 @@ module Puma
|
|
140
164
|
uri = URI.parse @control_url
|
141
165
|
|
142
166
|
# create server object by scheme
|
143
|
-
server =
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
167
|
+
server =
|
168
|
+
case uri.scheme
|
169
|
+
when 'ssl'
|
170
|
+
require 'openssl'
|
171
|
+
OpenSSL::SSL::SSLSocket.new(
|
172
|
+
TCPSocket.new(uri.host, uri.port),
|
173
|
+
OpenSSL::SSL::SSLContext.new)
|
174
|
+
.tap { |ssl| ssl.sync_close = true } # default is false
|
175
|
+
.tap(&:connect)
|
176
|
+
when 'tcp'
|
177
|
+
TCPSocket.new uri.host, uri.port
|
178
|
+
when 'unix'
|
179
|
+
# check for abstract UNIXSocket
|
180
|
+
UNIXSocket.new(@control_url.start_with?('unix://@') ?
|
181
|
+
"\0#{uri.host}#{uri.path}" : "#{uri.host}#{uri.path}")
|
182
|
+
else
|
183
|
+
raise "Invalid scheme: #{uri.scheme}"
|
184
|
+
end
|
185
|
+
|
186
|
+
if @command == 'status'
|
187
|
+
message 'Puma is started'
|
188
|
+
elsif NO_REQ_COMMANDS.include? @command
|
189
|
+
raise "Invalid request command: #{@command}"
|
160
190
|
else
|
161
191
|
url = "/#{@command}"
|
162
192
|
|
@@ -164,10 +194,10 @@ module Puma
|
|
164
194
|
url = url + "?token=#{@control_auth_token}"
|
165
195
|
end
|
166
196
|
|
167
|
-
server
|
197
|
+
server.syswrite "GET #{url} HTTP/1.0\r\n\r\n"
|
168
198
|
|
169
199
|
unless data = server.read
|
170
|
-
raise
|
200
|
+
raise 'Server closed connection before responding'
|
171
201
|
end
|
172
202
|
|
173
203
|
response = data.split("\r\n")
|
@@ -176,67 +206,55 @@ module Puma
|
|
176
206
|
raise "Server sent empty response"
|
177
207
|
end
|
178
208
|
|
179
|
-
|
209
|
+
@http, @code, @message = response.first.split(' ',3)
|
180
210
|
|
181
|
-
if @code ==
|
182
|
-
raise
|
183
|
-
elsif @code ==
|
211
|
+
if @code == '403'
|
212
|
+
raise 'Unauthorized access to server (wrong auth token)'
|
213
|
+
elsif @code == '404'
|
184
214
|
raise "Command error: #{response.last}"
|
185
|
-
elsif @code !=
|
215
|
+
elsif @code != '200'
|
186
216
|
raise "Bad response from server: #{@code}"
|
187
217
|
end
|
188
218
|
|
189
219
|
message "Command #{@command} sent success"
|
190
|
-
message response.last if @command
|
220
|
+
message response.last if PRINTABLE_COMMANDS.include?(@command)
|
191
221
|
end
|
192
222
|
ensure
|
193
|
-
|
223
|
+
if server
|
224
|
+
if uri.scheme == 'ssl'
|
225
|
+
server.sysclose
|
226
|
+
else
|
227
|
+
server.close unless server.closed?
|
228
|
+
end
|
229
|
+
end
|
194
230
|
end
|
195
231
|
|
196
232
|
def send_signal
|
197
233
|
unless @pid
|
198
|
-
raise
|
234
|
+
raise 'Neither pid nor control url available'
|
199
235
|
end
|
200
236
|
|
201
237
|
begin
|
238
|
+
sig = CMD_PATH_SIG_MAP[@command]
|
202
239
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
when "halt"
|
208
|
-
Process.kill "QUIT", @pid
|
209
|
-
|
210
|
-
when "stop"
|
211
|
-
Process.kill "SIGTERM", @pid
|
212
|
-
|
213
|
-
when "stats"
|
214
|
-
puts "Stats not available via pid only"
|
215
|
-
return
|
216
|
-
|
217
|
-
when "reload-worker-directory"
|
218
|
-
puts "reload-worker-directory not available via pid only"
|
240
|
+
if sig.nil?
|
241
|
+
@stdout.puts "'#{@command}' not available via pid only"
|
242
|
+
@stdout.flush unless @stdout.sync
|
219
243
|
return
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
when "status"
|
244
|
+
elsif sig.start_with? 'SIG'
|
245
|
+
Process.kill sig, @pid
|
246
|
+
elsif @command == 'status'
|
225
247
|
begin
|
226
248
|
Process.kill 0, @pid
|
227
|
-
puts
|
249
|
+
@stdout.puts 'Puma is started'
|
250
|
+
@stdout.flush unless @stdout.sync
|
228
251
|
rescue Errno::ESRCH
|
229
|
-
raise
|
252
|
+
raise 'Puma is not running'
|
230
253
|
end
|
231
|
-
|
232
|
-
return
|
233
|
-
|
234
|
-
else
|
235
254
|
return
|
236
255
|
end
|
237
|
-
|
238
256
|
rescue SystemCallError
|
239
|
-
if @command ==
|
257
|
+
if @command == 'restart'
|
240
258
|
start
|
241
259
|
else
|
242
260
|
raise "No pid '#{@pid}' found"
|
@@ -247,14 +265,13 @@ module Puma
|
|
247
265
|
end
|
248
266
|
|
249
267
|
def run
|
250
|
-
return start if @command ==
|
251
|
-
|
268
|
+
return start if @command == 'start'
|
252
269
|
prepare_configuration
|
253
270
|
|
254
|
-
if Puma.windows?
|
271
|
+
if Puma.windows? || @control_url
|
255
272
|
send_request
|
256
273
|
else
|
257
|
-
|
274
|
+
send_signal
|
258
275
|
end
|
259
276
|
|
260
277
|
rescue => e
|
@@ -262,7 +279,7 @@ module Puma
|
|
262
279
|
exit 1
|
263
280
|
end
|
264
281
|
|
265
|
-
|
282
|
+
private
|
266
283
|
def start
|
267
284
|
require 'puma/cli'
|
268
285
|
|
data/lib/puma/detect.rb
CHANGED
@@ -1,15 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# This file can be loaded independently of puma.rb, so it cannot have any code
|
4
|
+
# that assumes puma.rb is loaded.
|
5
|
+
|
6
|
+
|
3
7
|
module Puma
|
4
|
-
|
8
|
+
# @version 5.2.1
|
9
|
+
HAS_FORK = ::Process.respond_to? :fork
|
10
|
+
|
11
|
+
IS_JRUBY = Object.const_defined? :JRUBY_VERSION
|
12
|
+
|
13
|
+
IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/ ||
|
14
|
+
IS_JRUBY && RUBY_DESCRIPTION =~ /mswin/)
|
15
|
+
|
16
|
+
# @version 5.2.0
|
17
|
+
IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
|
5
18
|
|
6
19
|
def self.jruby?
|
7
20
|
IS_JRUBY
|
8
21
|
end
|
9
22
|
|
10
|
-
IS_WINDOWS = RUBY_PLATFORM =~ /mswin|ming|cygwin/
|
11
|
-
|
12
23
|
def self.windows?
|
13
24
|
IS_WINDOWS
|
14
25
|
end
|
26
|
+
|
27
|
+
# @version 5.0.0
|
28
|
+
def self.mri?
|
29
|
+
IS_MRI
|
30
|
+
end
|
31
|
+
|
32
|
+
# @version 5.0.0
|
33
|
+
def self.forkable?
|
34
|
+
HAS_FORK
|
35
|
+
end
|
15
36
|
end
|
data/lib/puma/dsl.rb
CHANGED
@@ -14,25 +14,60 @@ module Puma
|
|
14
14
|
# end
|
15
15
|
# config.load
|
16
16
|
#
|
17
|
-
# puts config.options[:binds]
|
18
|
-
# "tcp://127.0.0.1:3001"
|
17
|
+
# puts config.options[:binds] # => "tcp://127.0.0.1:3001"
|
19
18
|
#
|
20
19
|
# Used to load file:
|
21
20
|
#
|
22
21
|
# $ cat puma_config.rb
|
23
|
-
#
|
22
|
+
# port 3002
|
23
|
+
#
|
24
|
+
# Resulting configuration:
|
24
25
|
#
|
25
26
|
# config = Configuration.new(config_file: "puma_config.rb")
|
26
27
|
# config.load
|
27
28
|
#
|
28
|
-
# puts config.options[:binds]
|
29
|
-
# # => "tcp://127.0.0.1:3002"
|
29
|
+
# puts config.options[:binds] # => "tcp://127.0.0.1:3002"
|
30
30
|
#
|
31
31
|
# You can also find many examples being used by the test suite in
|
32
32
|
# +test/config+.
|
33
|
+
#
|
33
34
|
class DSL
|
34
35
|
include ConfigDefault
|
35
36
|
|
37
|
+
# convenience method so logic can be used in CI
|
38
|
+
# @see ssl_bind
|
39
|
+
#
|
40
|
+
def self.ssl_bind_str(host, port, opts)
|
41
|
+
verify = opts.fetch(:verify_mode, 'none').to_s
|
42
|
+
|
43
|
+
tls_str =
|
44
|
+
if opts[:no_tlsv1_1] then '&no_tlsv1_1=true'
|
45
|
+
elsif opts[:no_tlsv1] then '&no_tlsv1=true'
|
46
|
+
else ''
|
47
|
+
end
|
48
|
+
|
49
|
+
ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
|
50
|
+
|
51
|
+
if defined?(JRUBY_VERSION)
|
52
|
+
ssl_cipher_list = opts[:ssl_cipher_list] ?
|
53
|
+
"&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil
|
54
|
+
|
55
|
+
keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
|
56
|
+
|
57
|
+
"ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
|
58
|
+
"&verify_mode=#{verify}#{tls_str}#{ca_additions}"
|
59
|
+
else
|
60
|
+
ssl_cipher_filter = opts[:ssl_cipher_filter] ?
|
61
|
+
"&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
|
62
|
+
|
63
|
+
v_flags = (ary = opts[:verification_flags]) ?
|
64
|
+
"&verification_flags=#{Array(ary).join ','}" : nil
|
65
|
+
|
66
|
+
"ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \
|
67
|
+
"#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
36
71
|
def initialize(options, config)
|
37
72
|
@config = config
|
38
73
|
@options = options
|
@@ -98,6 +133,9 @@ module Puma
|
|
98
133
|
# [body]
|
99
134
|
# ]
|
100
135
|
# end
|
136
|
+
#
|
137
|
+
# @see Puma::Configuration#app
|
138
|
+
#
|
101
139
|
def app(obj=nil, &block)
|
102
140
|
obj ||= block
|
103
141
|
|
@@ -160,12 +198,12 @@ module Puma
|
|
160
198
|
#
|
161
199
|
# You can use query parameters within the url to specify options:
|
162
200
|
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
166
|
-
#
|
167
|
-
#
|
168
|
-
#
|
201
|
+
# * Set the socket backlog depth with +backlog+, default is 1024.
|
202
|
+
# * Set up an SSL certificate with +key+ & +cert+.
|
203
|
+
# * Set whether to optimize for low latency instead of throughput with
|
204
|
+
# +low_latency+, default is to not optimize for low latency. This is done
|
205
|
+
# via +Socket::TCP_NODELAY+.
|
206
|
+
# * Set socket permissions with +umask+.
|
169
207
|
#
|
170
208
|
# @example Backlog depth
|
171
209
|
# bind 'unix:///var/run/puma.sock?backlog=512'
|
@@ -175,6 +213,9 @@ module Puma
|
|
175
213
|
# bind 'tcp://0.0.0.0:9292?low_latency=false'
|
176
214
|
# @example Socket permissions
|
177
215
|
# bind 'unix:///var/run/puma.sock?umask=0111'
|
216
|
+
# @see Puma::Runner#load_and_bind
|
217
|
+
# @see Puma::Cluster#run
|
218
|
+
#
|
178
219
|
def bind(url)
|
179
220
|
@options[:binds] ||= []
|
180
221
|
@options[:binds] << url
|
@@ -184,22 +225,49 @@ module Puma
|
|
184
225
|
@options[:binds] = []
|
185
226
|
end
|
186
227
|
|
228
|
+
# Bind to (systemd) activated sockets, regardless of configured binds.
|
229
|
+
#
|
230
|
+
# Systemd can present sockets as file descriptors that are already opened.
|
231
|
+
# By default Puma will use these but only if it was explicitly told to bind
|
232
|
+
# to the socket. If not, it will close the activated sockets. This means
|
233
|
+
# all configuration is duplicated.
|
234
|
+
#
|
235
|
+
# Binds can contain additional configuration, but only SSL config is really
|
236
|
+
# relevant since the unix and TCP socket options are ignored.
|
237
|
+
#
|
238
|
+
# This means there is a lot of duplicated configuration for no additional
|
239
|
+
# value in most setups. This method tells the launcher to bind to all
|
240
|
+
# activated sockets, regardless of existing bind.
|
241
|
+
#
|
242
|
+
# To clear configured binds, the value only can be passed. This will clear
|
243
|
+
# out any binds that may have been configured.
|
244
|
+
#
|
245
|
+
# @example Use any systemd activated sockets as well as configured binds
|
246
|
+
# bind_to_activated_sockets
|
247
|
+
#
|
248
|
+
# @example Only bind to systemd activated sockets, ignoring other binds
|
249
|
+
# bind_to_activated_sockets 'only'
|
250
|
+
def bind_to_activated_sockets(bind=true)
|
251
|
+
@options[:bind_to_activated_sockets] = bind
|
252
|
+
end
|
253
|
+
|
187
254
|
# Define the TCP port to bind to. Use +bind+ for more advanced options.
|
188
255
|
#
|
189
256
|
# @example
|
190
257
|
# port 9292
|
191
258
|
def port(port, host=nil)
|
192
259
|
host ||= default_host
|
193
|
-
bind
|
260
|
+
bind URI::Generic.build(scheme: 'tcp', host: host, port: Integer(port)).to_s
|
194
261
|
end
|
195
262
|
|
196
|
-
# Define how long persistent connections can be idle before Puma closes
|
197
|
-
#
|
263
|
+
# Define how long persistent connections can be idle before Puma closes them.
|
264
|
+
# @see Puma::Server.new
|
198
265
|
def persistent_timeout(seconds)
|
199
266
|
@options[:persistent_timeout] = Integer(seconds)
|
200
267
|
end
|
201
268
|
|
202
269
|
# Define how long the tcp socket stays open, if no data has been received.
|
270
|
+
# @see Puma::Server.new
|
203
271
|
def first_data_timeout(seconds)
|
204
272
|
@options[:first_data_timeout] = Integer(seconds)
|
205
273
|
end
|
@@ -210,24 +278,11 @@ module Puma
|
|
210
278
|
@options[:clean_thread_locals] = which
|
211
279
|
end
|
212
280
|
|
213
|
-
#
|
214
|
-
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
# @example
|
219
|
-
# daemonize
|
281
|
+
# When shutting down, drain the accept socket of pending connections and
|
282
|
+
# process them. This loops over the accept socket until there are no more
|
283
|
+
# read events and then stops looking and waits for the requests to finish.
|
284
|
+
# @see Puma::Server#graceful_shutdown
|
220
285
|
#
|
221
|
-
# @example
|
222
|
-
# daemonize false
|
223
|
-
def daemonize(which=true)
|
224
|
-
@options[:daemon] = which
|
225
|
-
end
|
226
|
-
|
227
|
-
# When shutting down, drain the accept socket of pending
|
228
|
-
# connections and process them. This loops over the accept
|
229
|
-
# socket until there are no more read events and then stops
|
230
|
-
# looking and waits for the requests to finish.
|
231
286
|
def drain_on_shutdown(which=true)
|
232
287
|
@options[:drain_on_shutdown] = which
|
233
288
|
end
|
@@ -250,6 +305,7 @@ module Puma
|
|
250
305
|
#
|
251
306
|
# Puma always waits a few seconds after killing a thread for it to try
|
252
307
|
# to finish up it's work, even in :immediately mode.
|
308
|
+
# @see Puma::Server#graceful_shutdown
|
253
309
|
def force_shutdown_after(val=:forever)
|
254
310
|
i = case val
|
255
311
|
when :forever
|
@@ -257,7 +313,7 @@ module Puma
|
|
257
313
|
when :immediately
|
258
314
|
0
|
259
315
|
else
|
260
|
-
|
316
|
+
Float(val)
|
261
317
|
end
|
262
318
|
|
263
319
|
@options[:force_shutdown_after] = i
|
@@ -322,20 +378,14 @@ module Puma
|
|
322
378
|
# @example
|
323
379
|
# rackup '/u/apps/lolcat/config.ru'
|
324
380
|
def rackup(path)
|
325
|
-
@options[:rackup]
|
326
|
-
end
|
327
|
-
|
328
|
-
# Run Puma in TCP mode
|
329
|
-
#
|
330
|
-
def tcp_mode!
|
331
|
-
@options[:mode] = :tcp
|
381
|
+
@options[:rackup] ||= path.to_s
|
332
382
|
end
|
333
383
|
|
334
384
|
def early_hints(answer=true)
|
335
385
|
@options[:early_hints] = answer
|
336
386
|
end
|
337
387
|
|
338
|
-
# Redirect STDOUT and STDERR to files specified. The +append+ parameter
|
388
|
+
# Redirect +STDOUT+ and +STDERR+ to files specified. The +append+ parameter
|
339
389
|
# specifies whether the output is appended, the default is +false+.
|
340
390
|
#
|
341
391
|
# @example
|
@@ -355,7 +405,10 @@ module Puma
|
|
355
405
|
# Configure +min+ to be the minimum number of threads to use to answer
|
356
406
|
# requests and +max+ the maximum.
|
357
407
|
#
|
358
|
-
# The default is
|
408
|
+
# The default is the environment variables +PUMA_MIN_THREADS+ / +PUMA_MAX_THREADS+
|
409
|
+
# (or +MIN_THREADS+ / +MAX_THREADS+ if the +PUMA_+ variables aren't set).
|
410
|
+
#
|
411
|
+
# If these environment variables aren't set, the default is "0, 5" in MRI or "0, 16" for other interpreters.
|
359
412
|
#
|
360
413
|
# @example
|
361
414
|
# threads 0, 16
|
@@ -376,8 +429,8 @@ module Puma
|
|
376
429
|
@options[:max_threads] = max
|
377
430
|
end
|
378
431
|
|
379
|
-
# Instead of
|
380
|
-
# can also use the
|
432
|
+
# Instead of `bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'` you
|
433
|
+
# can also use the this method.
|
381
434
|
#
|
382
435
|
# @example
|
383
436
|
# ssl_bind '127.0.0.1', '9292', {
|
@@ -385,29 +438,17 @@ module Puma
|
|
385
438
|
# key: path_to_key,
|
386
439
|
# ssl_cipher_filter: cipher_filter, # optional
|
387
440
|
# verify_mode: verify_mode, # default 'none'
|
441
|
+
# verification_flags: flags, # optional, not supported by JRuby
|
388
442
|
# }
|
389
|
-
# @example For JRuby
|
443
|
+
# @example For JRuby, two keys are required: keystore & keystore_pass.
|
390
444
|
# ssl_bind '127.0.0.1', '9292', {
|
391
|
-
# cert: path_to_cert,
|
392
|
-
# key: path_to_key,
|
393
|
-
# ssl_cipher_filter: cipher_filter, # optional
|
394
|
-
# verify_mode: verify_mode, # default 'none'
|
395
445
|
# keystore: path_to_keystore,
|
396
|
-
# keystore_pass: password
|
446
|
+
# keystore_pass: password,
|
447
|
+
# ssl_cipher_list: cipher_list, # optional
|
448
|
+
# verify_mode: verify_mode # default 'none'
|
397
449
|
# }
|
398
450
|
def ssl_bind(host, port, opts)
|
399
|
-
|
400
|
-
no_tlsv1 = opts.fetch(:no_tlsv1, 'false')
|
401
|
-
no_tlsv1_1 = opts.fetch(:no_tlsv1_1, 'false')
|
402
|
-
ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
|
403
|
-
|
404
|
-
if defined?(JRUBY_VERSION)
|
405
|
-
keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
|
406
|
-
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}&#{keystore_additions}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}"
|
407
|
-
else
|
408
|
-
ssl_cipher_filter = "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" if opts[:ssl_cipher_filter]
|
409
|
-
bind "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}#{ssl_cipher_filter}&verify_mode=#{verify}&no_tlsv1=#{no_tlsv1}&no_tlsv1_1=#{no_tlsv1_1}#{ca_additions}"
|
410
|
-
end
|
451
|
+
bind self.class.ssl_bind_str(host, port, opts)
|
411
452
|
end
|
412
453
|
|
413
454
|
# Use +path+ as the file to store the server info state. This is
|
@@ -419,16 +460,46 @@ module Puma
|
|
419
460
|
@options[:state] = path.to_s
|
420
461
|
end
|
421
462
|
|
463
|
+
# Use +permission+ to restrict permissions for the state file.
|
464
|
+
#
|
465
|
+
# @example
|
466
|
+
# state_permission 0600
|
467
|
+
# @version 5.0.0
|
468
|
+
#
|
469
|
+
def state_permission(permission)
|
470
|
+
@options[:state_permission] = permission
|
471
|
+
end
|
472
|
+
|
422
473
|
# How many worker processes to run. Typically this is set to
|
423
|
-
#
|
474
|
+
# the number of available cores.
|
424
475
|
#
|
425
|
-
# The default is
|
476
|
+
# The default is the value of the environment variable +WEB_CONCURRENCY+ if
|
477
|
+
# set, otherwise 0.
|
426
478
|
#
|
427
479
|
# @note Cluster mode only.
|
480
|
+
# @see Puma::Cluster
|
428
481
|
def workers(count)
|
429
482
|
@options[:workers] = count.to_i
|
430
483
|
end
|
431
484
|
|
485
|
+
# Disable warning message when running in cluster mode with a single worker.
|
486
|
+
#
|
487
|
+
# Cluster mode has some overhead of running an additional 'control' process
|
488
|
+
# in order to manage the cluster. If only running a single worker it is
|
489
|
+
# likely not worth paying that overhead vs running in single mode with
|
490
|
+
# additional threads instead.
|
491
|
+
#
|
492
|
+
# There are some scenarios where running cluster mode with a single worker
|
493
|
+
# may still be warranted and valid under certain deployment scenarios, see
|
494
|
+
# https://github.com/puma/puma/issues/2534
|
495
|
+
#
|
496
|
+
# Moving from workers = 1 to workers = 0 will save 10-30% of memory use.
|
497
|
+
#
|
498
|
+
# @note Cluster mode only.
|
499
|
+
def silence_single_worker_warning
|
500
|
+
@options[:silence_single_worker_warning] = true
|
501
|
+
end
|
502
|
+
|
432
503
|
# Code to run immediately before master process
|
433
504
|
# forks workers (once on boot). These hooks can block if necessary
|
434
505
|
# to wait for background operations unknown to Puma to finish before
|
@@ -455,8 +526,8 @@ module Puma
|
|
455
526
|
#
|
456
527
|
# @note Cluster mode only.
|
457
528
|
# @example
|
458
|
-
#
|
459
|
-
# puts 'Before worker
|
529
|
+
# on_worker_boot do
|
530
|
+
# puts 'Before worker boot...'
|
460
531
|
# end
|
461
532
|
def on_worker_boot(&block)
|
462
533
|
@options[:before_worker_boot] ||= []
|
@@ -512,6 +583,29 @@ module Puma
|
|
512
583
|
|
513
584
|
alias_method :after_worker_boot, :after_worker_fork
|
514
585
|
|
586
|
+
# When `fork_worker` is enabled, code to run in Worker 0
|
587
|
+
# before all other workers are re-forked from this process,
|
588
|
+
# after the server has temporarily stopped serving requests
|
589
|
+
# (once per complete refork cycle).
|
590
|
+
#
|
591
|
+
# This can be used to trigger extra garbage-collection to maximize
|
592
|
+
# copy-on-write efficiency, or close any connections to remote servers
|
593
|
+
# (database, Redis, ...) that were opened while the server was running.
|
594
|
+
#
|
595
|
+
# This can be called multiple times to add several hooks.
|
596
|
+
#
|
597
|
+
# @note Cluster mode with `fork_worker` enabled only.
|
598
|
+
# @example
|
599
|
+
# on_refork do
|
600
|
+
# 3.times {GC.start}
|
601
|
+
# end
|
602
|
+
# @version 5.0.0
|
603
|
+
#
|
604
|
+
def on_refork(&block)
|
605
|
+
@options[:before_refork] ||= []
|
606
|
+
@options[:before_refork] << block
|
607
|
+
end
|
608
|
+
|
515
609
|
# Code to run out-of-band when the worker is idle.
|
516
610
|
# These hooks run immediately after a request has finished
|
517
611
|
# processing and there are no busy threads on the worker.
|
@@ -536,19 +630,8 @@ module Puma
|
|
536
630
|
@options[:directory] = dir.to_s
|
537
631
|
end
|
538
632
|
|
539
|
-
# DEPRECATED: The directory to operate out of.
|
540
|
-
def worker_directory(dir)
|
541
|
-
$stderr.puts "worker_directory is deprecated. Please use `directory`"
|
542
|
-
directory dir
|
543
|
-
end
|
544
|
-
|
545
|
-
# Run the app as a raw TCP app instead of an HTTP rack app.
|
546
|
-
def tcp_mode
|
547
|
-
@options[:mode] = :tcp
|
548
|
-
end
|
549
|
-
|
550
633
|
# Preload the application before starting the workers; this conflicts with
|
551
|
-
# phased restart feature.
|
634
|
+
# phased restart feature. On by default if your app uses more than 1 worker.
|
552
635
|
#
|
553
636
|
# @note Cluster mode only.
|
554
637
|
# @example
|
@@ -583,7 +666,7 @@ module Puma
|
|
583
666
|
# new Bundler context and thus can float around as the release
|
584
667
|
# dictates.
|
585
668
|
#
|
586
|
-
#
|
669
|
+
# @see extra_runtime_dependencies
|
587
670
|
#
|
588
671
|
# @note This is incompatible with +preload_app!+.
|
589
672
|
# @note This is only supported for RubyGems 2.2+
|
@@ -600,6 +683,9 @@ module Puma
|
|
600
683
|
#
|
601
684
|
# @example
|
602
685
|
# raise_exception_on_sigterm false
|
686
|
+
# @see Puma::Launcher#setup_signals
|
687
|
+
# @see Puma::Cluster#setup_signals
|
688
|
+
#
|
603
689
|
def raise_exception_on_sigterm(answer=true)
|
604
690
|
@options[:raise_exception_on_sigterm] = answer
|
605
691
|
end
|
@@ -615,6 +701,8 @@ module Puma
|
|
615
701
|
# extra_runtime_dependencies ['gem_name_1', 'gem_name_2']
|
616
702
|
# @example
|
617
703
|
# extra_runtime_dependencies ['puma_worker_killer', 'puma-heroku']
|
704
|
+
# @see Puma::Launcher#extra_runtime_deps_directories
|
705
|
+
#
|
618
706
|
def extra_runtime_dependencies(answer = [])
|
619
707
|
@options[:extra_runtime_dependencies] = Array(answer)
|
620
708
|
end
|
@@ -642,6 +730,8 @@ module Puma
|
|
642
730
|
# @note Cluster mode only.
|
643
731
|
# @example
|
644
732
|
# worker_timeout 60
|
733
|
+
# @see Puma::Cluster::Worker#ping_timeout
|
734
|
+
#
|
645
735
|
def worker_timeout(timeout)
|
646
736
|
timeout = Integer(timeout)
|
647
737
|
min = Const::WORKER_CHECK_INTERVAL
|
@@ -658,15 +748,20 @@ module Puma
|
|
658
748
|
# If unspecified, this defaults to the value of worker_timeout.
|
659
749
|
#
|
660
750
|
# @note Cluster mode only.
|
661
|
-
#
|
751
|
+
#
|
752
|
+
# @example
|
662
753
|
# worker_boot_timeout 60
|
754
|
+
# @see Puma::Cluster::Worker#ping_timeout
|
755
|
+
#
|
663
756
|
def worker_boot_timeout(timeout)
|
664
757
|
@options[:worker_boot_timeout] = Integer(timeout)
|
665
758
|
end
|
666
759
|
|
667
|
-
# Set the timeout for worker shutdown
|
760
|
+
# Set the timeout for worker shutdown.
|
668
761
|
#
|
669
762
|
# @note Cluster mode only.
|
763
|
+
# @see Puma::Cluster::Worker#term
|
764
|
+
#
|
670
765
|
def worker_shutdown_timeout(timeout)
|
671
766
|
@options[:worker_shutdown_timeout] = Integer(timeout)
|
672
767
|
end
|
@@ -684,6 +779,7 @@ module Puma
|
|
684
779
|
# slow clients will occupy a handler thread while the request
|
685
780
|
# is being sent. A reverse proxy, such as nginx, can handle
|
686
781
|
# slow clients and queue requests before they reach Puma.
|
782
|
+
# @see Puma::Server
|
687
783
|
def queue_requests(answer=true)
|
688
784
|
@options[:queue_requests] = answer
|
689
785
|
end
|
@@ -691,10 +787,25 @@ module Puma
|
|
691
787
|
# When a shutdown is requested, the backtraces of all the
|
692
788
|
# threads will be written to $stdout. This can help figure
|
693
789
|
# out why shutdown is hanging.
|
790
|
+
#
|
694
791
|
def shutdown_debug(val=true)
|
695
792
|
@options[:shutdown_debug] = val
|
696
793
|
end
|
697
794
|
|
795
|
+
|
796
|
+
# Attempts to route traffic to less-busy workers by causing them to delay
|
797
|
+
# listening on the socket, allowing workers which are not processing any
|
798
|
+
# requests to pick up new requests first.
|
799
|
+
#
|
800
|
+
# Only works on MRI. For all other interpreters, this setting does nothing.
|
801
|
+
# @see Puma::Server#handle_servers
|
802
|
+
# @see Puma::ThreadPool#wait_for_less_busy_worker
|
803
|
+
# @version 5.0.0
|
804
|
+
#
|
805
|
+
def wait_for_less_busy_worker(val=0.005)
|
806
|
+
@options[:wait_for_less_busy_worker] = val.to_f
|
807
|
+
end
|
808
|
+
|
698
809
|
# Control how the remote address of the connection is set. This
|
699
810
|
# is configurable because to calculate the true socket peer address
|
700
811
|
# a kernel syscall is required which for very fast rack handlers
|
@@ -702,18 +813,18 @@ module Puma
|
|
702
813
|
#
|
703
814
|
# There are 4 possible values:
|
704
815
|
#
|
705
|
-
#
|
706
|
-
#
|
707
|
-
#
|
708
|
-
#
|
709
|
-
#
|
710
|
-
#
|
711
|
-
#
|
712
|
-
#
|
713
|
-
#
|
714
|
-
#
|
715
|
-
#
|
716
|
-
#
|
816
|
+
# 1. **:socket** (the default) - read the peername from the socket using the
|
817
|
+
# syscall. This is the normal behavior.
|
818
|
+
# 2. **:localhost** - set the remote address to "127.0.0.1"
|
819
|
+
# 3. **header: <http_header>**- set the remote address to the value of the
|
820
|
+
# provided http header. For instance:
|
821
|
+
# `set_remote_address header: "X-Real-IP"`.
|
822
|
+
# Only the first word (as separated by spaces or comma) is used, allowing
|
823
|
+
# headers such as X-Forwarded-For to be used as well.
|
824
|
+
# 4. **\<Any string\>** - this allows you to hardcode remote address to any value
|
825
|
+
# you wish. Because Puma never uses this field anyway, it's format is
|
826
|
+
# entirely in your hands.
|
827
|
+
#
|
717
828
|
def set_remote_address(val=:socket)
|
718
829
|
case val
|
719
830
|
when :socket
|
@@ -736,5 +847,68 @@ module Puma
|
|
736
847
|
end
|
737
848
|
end
|
738
849
|
|
850
|
+
# When enabled, workers will be forked from worker 0 instead of from the master process.
|
851
|
+
# This option is similar to `preload_app` because the app is preloaded before forking,
|
852
|
+
# but it is compatible with phased restart.
|
853
|
+
#
|
854
|
+
# This option also enables the `refork` command (SIGURG), which optimizes copy-on-write performance
|
855
|
+
# in a running app.
|
856
|
+
#
|
857
|
+
# A refork will automatically trigger once after the specified number of requests
|
858
|
+
# (default 1000), or pass 0 to disable auto refork.
|
859
|
+
#
|
860
|
+
# @note Cluster mode only.
|
861
|
+
# @version 5.0.0
|
862
|
+
#
|
863
|
+
def fork_worker(after_requests=1000)
|
864
|
+
@options[:fork_worker] = Integer(after_requests)
|
865
|
+
end
|
866
|
+
|
867
|
+
# When enabled, Puma will GC 4 times before forking workers.
|
868
|
+
# If available (Ruby 2.7+), we will also call GC.compact.
|
869
|
+
# Not recommended for non-MRI Rubies.
|
870
|
+
#
|
871
|
+
# Based on the work of Koichi Sasada and Aaron Patterson, this option may
|
872
|
+
# decrease memory utilization of preload-enabled cluster-mode Pumas. It will
|
873
|
+
# also increase time to boot and fork. See your logs for details on how much
|
874
|
+
# time this adds to your boot process. For most apps, it will be less than one
|
875
|
+
# second.
|
876
|
+
#
|
877
|
+
# @see Puma::Cluster#nakayoshi_gc
|
878
|
+
# @version 5.0.0
|
879
|
+
#
|
880
|
+
def nakayoshi_fork(enabled=true)
|
881
|
+
@options[:nakayoshi_fork] = enabled
|
882
|
+
end
|
883
|
+
|
884
|
+
# The number of requests to attempt inline before sending a client back to
|
885
|
+
# the reactor to be subject to normal ordering.
|
886
|
+
#
|
887
|
+
def max_fast_inline(num_of_requests)
|
888
|
+
@options[:max_fast_inline] = Float(num_of_requests)
|
889
|
+
end
|
890
|
+
|
891
|
+
# Specify the backend for the IO selector.
|
892
|
+
#
|
893
|
+
# Provided values will be passed directly to +NIO::Selector.new+, with the
|
894
|
+
# exception of +:auto+ which will let nio4r choose the backend.
|
895
|
+
#
|
896
|
+
# Check the documentation of +NIO::Selector.backends+ for the list of valid
|
897
|
+
# options. Note that the available options on your system will depend on the
|
898
|
+
# operating system. If you want to use the pure Ruby backend (not
|
899
|
+
# recommended due to its comparatively low performance), set environment
|
900
|
+
# variable +NIO4R_PURE+ to +true+.
|
901
|
+
#
|
902
|
+
# The default is +:auto+.
|
903
|
+
#
|
904
|
+
# @see https://github.com/socketry/nio4r/blob/master/lib/nio/selector.rb
|
905
|
+
#
|
906
|
+
def io_selector_backend(backend)
|
907
|
+
@options[:io_selector_backend] = backend.to_sym
|
908
|
+
end
|
909
|
+
|
910
|
+
def mutate_stdout_and_stderr_to_sync_on_write(enabled=true)
|
911
|
+
@options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
|
912
|
+
end
|
739
913
|
end
|
740
914
|
end
|