puma 6.0.2 → 6.1.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.
- checksums.yaml +4 -4
- data/History.md +28 -0
- data/README.md +1 -0
- data/docs/systemd.md +1 -2
- data/lib/puma/binder.rb +4 -3
- data/lib/puma/client.rb +29 -4
- data/lib/puma/cluster/worker.rb +5 -0
- data/lib/puma/cluster.rb +2 -0
- data/lib/puma/configuration.rb +1 -0
- data/lib/puma/const.rb +2 -2
- data/lib/puma/dsl.rb +15 -1
- data/lib/puma/launcher.rb +4 -22
- data/lib/puma/log_writer.rb +4 -0
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/rack_default.rb +18 -3
- data/lib/puma/request.rb +28 -17
- data/lib/puma/runner.rb +7 -0
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +2 -0
- data/lib/puma/single.rb +2 -0
- data/lib/rack/handler/puma.rb +116 -94
- metadata +4 -3
- data/lib/puma/systemd.rb +0 -47
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1df130ccb43143b23de48c5dc2f0890165e078eba4f09432b0a73411b5b54a94
|
4
|
+
data.tar.gz: e562fc40625877488fd13719e9620f2401394a33a74bee46e5a4731becfab6ae
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e436db46a9761b73dc370a38e1e59a0e2ac1e385ecf578fc631fc4671329b9cbcfd634e9bed73e17657c13eaf53387a55726d9c986739f6f9ccdb4223fea026e
|
7
|
+
data.tar.gz: db4526766e567440a95129831ef30cea038a98e9d613dc76308c96d0d81913d4e15fbb9c1c8d111ce395af0d908268316cb65c04511dbfbd8361fab31a62634a
|
data/History.md
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
## 6.1.0 / 2022-02-12
|
2
|
+
|
3
|
+
* Features
|
4
|
+
* WebSocket support via partial hijack ([#3058], [#3007])
|
5
|
+
* Add built-in systemd notify support ([#3011])
|
6
|
+
* Periodically send status to systemd ([#3006], [#2604])
|
7
|
+
* Introduce the ability to return 413: payload too large for requests ([#3040])
|
8
|
+
* Log loaded extensions when `PUMA_DEBUG` is set ([#3036], [#3020])
|
9
|
+
|
10
|
+
* Bugfixes
|
11
|
+
* Fix issue with rack 3 compatibility re: rackup ([#3061], [#3057])
|
12
|
+
* Allow setting TCP low_latency with SSL listener ([#3065])
|
13
|
+
|
14
|
+
* Performance
|
15
|
+
* Reduce memory usage for large file uploads ([#3062])
|
16
|
+
|
1
17
|
## 6.0.2 / 2023-01-01
|
2
18
|
|
3
19
|
* Refactor
|
@@ -1932,6 +1948,18 @@ be added back in a future date when a java Puma::MiniSSL is added.
|
|
1932
1948
|
* Bugfixes
|
1933
1949
|
* Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
|
1934
1950
|
|
1951
|
+
[#3058]:https://github.com/puma/puma/pull/3058 "PR by @dentarg, merged 2023-01-29"
|
1952
|
+
[#3007]:https://github.com/puma/puma/issues/3007 "Issue by @MSP-Greg, closed 2023-01-29"
|
1953
|
+
[#3011]:https://github.com/puma/puma/pull/3011 "PR by @joaomarcos96, merged 2023-01-03"
|
1954
|
+
[#3006]:https://github.com/puma/puma/pull/3006 "PR by @QWYNG, merged 2023-02-09"
|
1955
|
+
[#2604]:https://github.com/puma/puma/issues/2604 "Issue by @dgoetz, closed 2023-02-09"
|
1956
|
+
[#3040]:https://github.com/puma/puma/pull/3040 "PR by @shayonj, merged 2023-01-02"
|
1957
|
+
[#3036]:https://github.com/puma/puma/pull/3036 "PR by @MSP-Greg, merged 2023-01-13"
|
1958
|
+
[#3020]:https://github.com/puma/puma/issues/3020 "Issue by @dentarg, closed 2023-01-13"
|
1959
|
+
[#3061]:https://github.com/puma/puma/pull/3061 "PR by @MSP-Greg, merged 2023-02-12"
|
1960
|
+
[#3057]:https://github.com/puma/puma/issues/3057 "Issue by @mmarvb8h, closed 2023-02-12"
|
1961
|
+
[#3065]:https://github.com/puma/puma/pull/3065 "PR by @MSP-Greg, merged 2023-02-11"
|
1962
|
+
[#3062]:https://github.com/puma/puma/pull/3062 "PR by @willkoehler, merged 2023-01-29"
|
1935
1963
|
[#3035]:https://github.com/puma/puma/pull/3035 "PR by @MSP-Greg, merged 2022-12-24"
|
1936
1964
|
[#3033]:https://github.com/puma/puma/issues/3033 "Issue by @jules-w2, closed 2022-12-24"
|
1937
1965
|
[#3016]:https://github.com/puma/puma/pull/3016 "PR by @MSP-Greg, merged 2022-12-24"
|
data/README.md
CHANGED
@@ -365,6 +365,7 @@ Community guides:
|
|
365
365
|
* [puma-metrics](https://github.com/harmjanblok/puma-metrics) — export Puma metrics to Prometheus
|
366
366
|
* [puma-plugin-statsd](https://github.com/yob/puma-plugin-statsd) — send Puma metrics to statsd
|
367
367
|
* [puma-plugin-systemd](https://github.com/sj26/puma-plugin-systemd) — deeper integration with systemd for notify, status and watchdog
|
368
|
+
* [puma-plugin-telemetry](https://github.com/babbel/puma-plugin-telemetry) - telemetry plugin for Puma offering various targets to publish
|
368
369
|
|
369
370
|
### Monitoring
|
370
371
|
|
data/docs/systemd.md
CHANGED
@@ -24,8 +24,7 @@ After=network.target
|
|
24
24
|
|
25
25
|
[Service]
|
26
26
|
# Puma supports systemd's `Type=notify` and watchdog service
|
27
|
-
# monitoring,
|
28
|
-
# as of Puma 5.1 or later.
|
27
|
+
# monitoring, as of Puma 5.1 or later.
|
29
28
|
# On earlier versions of Puma or JRuby, change this to `Type=simple` and remove
|
30
29
|
# the `WatchdogSec` line.
|
31
30
|
Type=notify
|
data/lib/puma/binder.rb
CHANGED
@@ -158,10 +158,10 @@ module Puma
|
|
158
158
|
ios_len = @ios.length
|
159
159
|
params = Util.parse_query uri.query
|
160
160
|
|
161
|
-
|
161
|
+
low_latency = params.key?('low_latency') && params['low_latency'] != 'false'
|
162
162
|
backlog = params.fetch('backlog', 1024).to_i
|
163
163
|
|
164
|
-
io = add_tcp_listener uri.host, uri.port,
|
164
|
+
io = add_tcp_listener uri.host, uri.port, low_latency, backlog
|
165
165
|
|
166
166
|
@ios[ios_len..-1].each do |i|
|
167
167
|
addr = loc_addr_str i
|
@@ -251,7 +251,8 @@ module Puma
|
|
251
251
|
else
|
252
252
|
ios_len = @ios.length
|
253
253
|
backlog = params.fetch('backlog', 1024).to_i
|
254
|
-
|
254
|
+
low_latency = params['low_latency'] != 'false'
|
255
|
+
io = add_ssl_listener uri.host, uri.port, ctx, low_latency, backlog
|
255
256
|
|
256
257
|
@ios[ios_len..-1].each do |i|
|
257
258
|
addr = loc_addr_str i
|
data/lib/puma/client.rb
CHANGED
@@ -86,6 +86,9 @@ module Puma
|
|
86
86
|
@requests_served = 0
|
87
87
|
@hijacked = false
|
88
88
|
|
89
|
+
@http_content_length_limit = nil
|
90
|
+
@http_content_length_limit_exceeded = false
|
91
|
+
|
89
92
|
@peerip = nil
|
90
93
|
@peer_family = nil
|
91
94
|
@listener = nil
|
@@ -95,12 +98,14 @@ module Puma
|
|
95
98
|
@body_remain = 0
|
96
99
|
|
97
100
|
@in_last_chunk = false
|
101
|
+
|
102
|
+
@read_buffer = +""
|
98
103
|
end
|
99
104
|
|
100
105
|
attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked,
|
101
|
-
:tempfile, :io_buffer
|
106
|
+
:tempfile, :io_buffer, :http_content_length_limit_exceeded
|
102
107
|
|
103
|
-
attr_writer :peerip
|
108
|
+
attr_writer :peerip, :http_content_length_limit
|
104
109
|
|
105
110
|
attr_accessor :remote_addr_header, :listener
|
106
111
|
|
@@ -151,6 +156,7 @@ module Puma
|
|
151
156
|
@body_remain = 0
|
152
157
|
@peerip = nil if @remote_addr_header
|
153
158
|
@in_last_chunk = false
|
159
|
+
@http_content_length_limit_exceeded = false
|
154
160
|
|
155
161
|
if @buffer
|
156
162
|
return false unless try_to_parse_proxy_protocol
|
@@ -210,6 +216,17 @@ module Puma
|
|
210
216
|
end
|
211
217
|
|
212
218
|
def try_to_finish
|
219
|
+
if env[CONTENT_LENGTH] && above_http_content_limit(env[CONTENT_LENGTH].to_i)
|
220
|
+
@http_content_length_limit_exceeded = true
|
221
|
+
end
|
222
|
+
|
223
|
+
if @http_content_length_limit_exceeded
|
224
|
+
@buffer = nil
|
225
|
+
@body = EmptyBody
|
226
|
+
set_ready
|
227
|
+
return true
|
228
|
+
end
|
229
|
+
|
213
230
|
return read_body if in_data_phase
|
214
231
|
|
215
232
|
begin
|
@@ -239,6 +256,10 @@ module Puma
|
|
239
256
|
|
240
257
|
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
241
258
|
|
259
|
+
if @parser.finished? && above_http_content_limit(@parser.body.bytesize)
|
260
|
+
@http_content_length_limit_exceeded = true
|
261
|
+
end
|
262
|
+
|
242
263
|
if @parser.finished?
|
243
264
|
return setup_body
|
244
265
|
elsif @parsed_bytes >= MAX_HEADER
|
@@ -414,7 +435,7 @@ module Puma
|
|
414
435
|
end
|
415
436
|
|
416
437
|
begin
|
417
|
-
chunk = @io.read_nonblock(want)
|
438
|
+
chunk = @io.read_nonblock(want, @read_buffer)
|
418
439
|
rescue IO::WaitReadable
|
419
440
|
return false
|
420
441
|
rescue SystemCallError, IOError
|
@@ -446,7 +467,7 @@ module Puma
|
|
446
467
|
def read_chunked_body
|
447
468
|
while true
|
448
469
|
begin
|
449
|
-
chunk = @io.read_nonblock(4096)
|
470
|
+
chunk = @io.read_nonblock(4096, @read_buffer)
|
450
471
|
rescue IO::WaitReadable
|
451
472
|
return false
|
452
473
|
rescue SystemCallError, IOError
|
@@ -594,5 +615,9 @@ module Puma
|
|
594
615
|
@requests_served += 1
|
595
616
|
@ready = true
|
596
617
|
end
|
618
|
+
|
619
|
+
def above_http_content_limit(value)
|
620
|
+
@http_content_length_limit&.< value
|
621
|
+
end
|
597
622
|
end
|
598
623
|
end
|
data/lib/puma/cluster/worker.rb
CHANGED
@@ -115,6 +115,11 @@ module Puma
|
|
115
115
|
|
116
116
|
while restart_server.pop
|
117
117
|
server_thread = server.run
|
118
|
+
|
119
|
+
if @log_writer.debug? && index == 0
|
120
|
+
debug_loaded_extensions "Loaded Extensions - worker 0:"
|
121
|
+
end
|
122
|
+
|
118
123
|
stat_thread ||= Thread.new(@worker_write) do |io|
|
119
124
|
Puma.set_thread_name "stat pld"
|
120
125
|
base_payload = "p#{Process.pid}"
|
data/lib/puma/cluster.rb
CHANGED
@@ -467,6 +467,7 @@ module Puma
|
|
467
467
|
@events.fire(:ping!, w)
|
468
468
|
if !booted && @workers.none? {|worker| worker.last_status.empty?}
|
469
469
|
@events.fire_on_booted!
|
470
|
+
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
470
471
|
booted = true
|
471
472
|
end
|
472
473
|
end
|
@@ -476,6 +477,7 @@ module Puma
|
|
476
477
|
end
|
477
478
|
if in_phased_restart && workers_not_booted.zero?
|
478
479
|
@events.fire_on_booted!
|
480
|
+
debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
|
479
481
|
in_phased_restart = false
|
480
482
|
end
|
481
483
|
|
data/lib/puma/configuration.rb
CHANGED
data/lib/puma/const.rb
CHANGED
@@ -99,8 +99,8 @@ module Puma
|
|
99
99
|
# too taxing on performance.
|
100
100
|
module Const
|
101
101
|
|
102
|
-
PUMA_VERSION = VERSION = "6.0
|
103
|
-
CODE_NAME = "
|
102
|
+
PUMA_VERSION = VERSION = "6.1.0"
|
103
|
+
CODE_NAME = "The Way Up"
|
104
104
|
|
105
105
|
PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
|
106
106
|
|
data/lib/puma/dsl.rb
CHANGED
@@ -65,6 +65,7 @@ module Puma
|
|
65
65
|
|
66
66
|
ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify)
|
67
67
|
|
68
|
+
low_latency_str = opts.key?(:low_latency) ? "&low_latency=#{opts[:low_latency]}" : ''
|
68
69
|
backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
|
69
70
|
|
70
71
|
if defined?(JRUBY_VERSION)
|
@@ -114,7 +115,7 @@ module Puma
|
|
114
115
|
end
|
115
116
|
|
116
117
|
"ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{ssl_cipher_filter}" \
|
117
|
-
"#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
|
118
|
+
"#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}#{low_latency_str}"
|
118
119
|
end
|
119
120
|
end
|
120
121
|
|
@@ -1022,6 +1023,19 @@ module Puma
|
|
1022
1023
|
@options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
|
1023
1024
|
end
|
1024
1025
|
|
1026
|
+
# Specify how big the request payload should be, in bytes.
|
1027
|
+
# This limit is compared against Content-Length HTTP header.
|
1028
|
+
# If the payload size (CONTENT_LENGTH) is larger than http_content_length_limit,
|
1029
|
+
# HTTP 413 status code is returned.
|
1030
|
+
#
|
1031
|
+
# When no Content-Length http header is present, it is compared against the
|
1032
|
+
# size of the body of the request.
|
1033
|
+
#
|
1034
|
+
# The default value for http_content_length_limit is nil.
|
1035
|
+
def http_content_length_limit(limit)
|
1036
|
+
@options[:http_content_length_limit] = limit
|
1037
|
+
end
|
1038
|
+
|
1025
1039
|
private
|
1026
1040
|
|
1027
1041
|
# To avoid adding cert_pem and key_pem as URI params, we store them on the
|
data/lib/puma/launcher.rb
CHANGED
@@ -59,6 +59,10 @@ module Puma
|
|
59
59
|
|
60
60
|
@environment = conf.environment
|
61
61
|
|
62
|
+
if ENV["NOTIFY_SOCKET"]
|
63
|
+
@config.plugins.create('systemd')
|
64
|
+
end
|
65
|
+
|
62
66
|
if @config.options[:bind_to_activated_sockets]
|
63
67
|
@config.options[:binds] = @binder.synthesize_binds_from_activated_fs(
|
64
68
|
@config.options[:binds],
|
@@ -180,7 +184,6 @@ module Puma
|
|
180
184
|
|
181
185
|
setup_signals
|
182
186
|
set_process_title
|
183
|
-
integrate_with_systemd
|
184
187
|
|
185
188
|
# This blocks until the server is stopped
|
186
189
|
@runner.run
|
@@ -311,27 +314,6 @@ module Puma
|
|
311
314
|
@runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
|
312
315
|
end
|
313
316
|
|
314
|
-
# Puma's systemd integration allows Puma to inform systemd:
|
315
|
-
# 1. when it has successfully started
|
316
|
-
# 2. when it is starting shutdown
|
317
|
-
# 3. periodically for a liveness check with a watchdog thread
|
318
|
-
def integrate_with_systemd
|
319
|
-
return unless ENV["NOTIFY_SOCKET"]
|
320
|
-
|
321
|
-
begin
|
322
|
-
require_relative 'systemd'
|
323
|
-
rescue LoadError
|
324
|
-
log "Systemd integration failed. It looks like you're trying to use systemd notify but don't have sd_notify gem installed"
|
325
|
-
return
|
326
|
-
end
|
327
|
-
|
328
|
-
log "* Enabling systemd notification integration"
|
329
|
-
|
330
|
-
systemd = Systemd.new(@log_writer, @events)
|
331
|
-
systemd.hook_events
|
332
|
-
systemd.start_watchdog
|
333
|
-
end
|
334
|
-
|
335
317
|
def log(str)
|
336
318
|
@log_writer.log(str)
|
337
319
|
end
|
data/lib/puma/log_writer.rb
CHANGED
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../plugin'
|
4
|
+
|
5
|
+
# Puma's systemd integration allows Puma to inform systemd:
|
6
|
+
# 1. when it has successfully started
|
7
|
+
# 2. when it is starting shutdown
|
8
|
+
# 3. periodically for a liveness check with a watchdog thread
|
9
|
+
# 4. periodically set the status
|
10
|
+
Puma::Plugin.create do
|
11
|
+
def start(launcher)
|
12
|
+
require_relative '../sd_notify'
|
13
|
+
|
14
|
+
launcher.log_writer.log "* Enabling systemd notification integration"
|
15
|
+
|
16
|
+
# hook_events
|
17
|
+
launcher.events.on_booted { Puma::SdNotify.ready }
|
18
|
+
launcher.events.on_stopped { Puma::SdNotify.stopping }
|
19
|
+
launcher.events.on_restart { Puma::SdNotify.reloading }
|
20
|
+
|
21
|
+
# start watchdog
|
22
|
+
if Puma::SdNotify.watchdog?
|
23
|
+
ping_f = watchdog_sleep_time
|
24
|
+
|
25
|
+
in_background do
|
26
|
+
launcher.log_writer.log "Pinging systemd watchdog every #{ping_f.round(1)} sec"
|
27
|
+
loop do
|
28
|
+
sleep ping_f
|
29
|
+
Puma::SdNotify.watchdog
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# start status loop
|
35
|
+
instance = self
|
36
|
+
sleep_time = 1.0
|
37
|
+
in_background do
|
38
|
+
launcher.log_writer.log "Sending status to systemd every #{sleep_time.round(1)} sec"
|
39
|
+
|
40
|
+
loop do
|
41
|
+
sleep sleep_time
|
42
|
+
# TODO: error handling?
|
43
|
+
Puma::SdNotify.status(instance.status)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def status
|
49
|
+
if clustered?
|
50
|
+
messages = stats[:worker_status].map do |worker|
|
51
|
+
common_message(worker[:last_status])
|
52
|
+
end.join(',')
|
53
|
+
|
54
|
+
"Puma #{Puma::Const::VERSION}: cluster: #{booted_workers}/#{workers}, worker_status: [#{messages}]"
|
55
|
+
else
|
56
|
+
"Puma #{Puma::Const::VERSION}: worker: #{common_message(stats)}"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def watchdog_sleep_time
|
63
|
+
usec = Integer(ENV["WATCHDOG_USEC"])
|
64
|
+
|
65
|
+
sec_f = usec / 1_000_000.0
|
66
|
+
# "It is recommended that a daemon sends a keep-alive notification message
|
67
|
+
# to the service manager every half of the time returned here."
|
68
|
+
sec_f / 2
|
69
|
+
end
|
70
|
+
|
71
|
+
def stats
|
72
|
+
Puma.stats_hash
|
73
|
+
end
|
74
|
+
|
75
|
+
def clustered?
|
76
|
+
stats.has_key?(:workers)
|
77
|
+
end
|
78
|
+
|
79
|
+
def workers
|
80
|
+
stats.fetch(:workers, 1)
|
81
|
+
end
|
82
|
+
|
83
|
+
def booted_workers
|
84
|
+
stats.fetch(:booted_workers, 1)
|
85
|
+
end
|
86
|
+
|
87
|
+
def common_message(stats)
|
88
|
+
"{ #{stats[:running]}/#{stats[:max_threads]} threads, #{stats[:pool_capacity]} available, #{stats[:backlog]} backlog }"
|
89
|
+
end
|
90
|
+
end
|
data/lib/puma/rack_default.rb
CHANGED
@@ -2,8 +2,23 @@
|
|
2
2
|
|
3
3
|
require_relative '../rack/handler/puma'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
# rackup was removed in Rack 3, it is now a separate gem
|
6
|
+
if Object.const_defined? :Rackup
|
7
|
+
module Rackup
|
8
|
+
module Handler
|
9
|
+
def self.default(options = {})
|
10
|
+
::Rackup::Handler::Puma
|
11
|
+
end
|
12
|
+
end
|
8
13
|
end
|
14
|
+
elsif Object.const_defined?(:Rack) && Rack::RELEASE < '3'
|
15
|
+
module Rack
|
16
|
+
module Handler
|
17
|
+
def self.default(options = {})
|
18
|
+
::Rack::Handler::Puma
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
else
|
23
|
+
raise "Rack 3 must be used with the Rackup gem"
|
9
24
|
end
|
data/lib/puma/request.rb
CHANGED
@@ -53,8 +53,13 @@ module Puma
|
|
53
53
|
socket = client.io # io may be a MiniSSL::Socket
|
54
54
|
app_body = nil
|
55
55
|
|
56
|
+
|
56
57
|
return false if closed_socket?(socket)
|
57
58
|
|
59
|
+
if client.http_content_length_limit_exceeded
|
60
|
+
return prepare_response(413, {}, ["Payload Too Large"], requests, client)
|
61
|
+
end
|
62
|
+
|
58
63
|
normalize_env env, client
|
59
64
|
|
60
65
|
env[PUMA_SOCKET] = socket
|
@@ -168,7 +173,7 @@ module Puma
|
|
168
173
|
# below converts app_body into body, dependent on app_body's characteristics, and
|
169
174
|
# resp_info[:content_length] will be set if it can be determined
|
170
175
|
if !resp_info[:content_length] && !resp_info[:transfer_encoding] && status != 204
|
171
|
-
if res_body.respond_to?(:to_ary) && (array_body = res_body.to_ary)
|
176
|
+
if res_body.respond_to?(:to_ary) && (array_body = res_body.to_ary) && array_body.is_a?(Array)
|
172
177
|
body = array_body
|
173
178
|
resp_info[:content_length] = body.sum(&:bytesize)
|
174
179
|
elsif res_body.is_a?(File) && res_body.respond_to?(:size)
|
@@ -215,27 +220,33 @@ module Puma
|
|
215
220
|
cork_socket socket
|
216
221
|
|
217
222
|
if resp_info[:no_body]
|
218
|
-
|
223
|
+
# 101 (Switching Protocols) doesn't return here or have content_length,
|
224
|
+
# it should be using `response_hijack`
|
225
|
+
unless status == 101
|
226
|
+
if content_length && status != 204
|
227
|
+
io_buffer.append CONTENT_LENGTH_S, content_length.to_s, line_ending
|
228
|
+
end
|
229
|
+
|
230
|
+
io_buffer << LINE_END
|
231
|
+
fast_write_str socket, io_buffer.read_and_reset
|
232
|
+
socket.flush
|
233
|
+
return keep_alive
|
234
|
+
end
|
235
|
+
else
|
236
|
+
if content_length
|
219
237
|
io_buffer.append CONTENT_LENGTH_S, content_length.to_s, line_ending
|
238
|
+
chunked = false
|
239
|
+
elsif !response_hijack && resp_info[:allow_chunked]
|
240
|
+
io_buffer << TRANSFER_ENCODING_CHUNKED
|
241
|
+
chunked = true
|
220
242
|
end
|
221
|
-
|
222
|
-
io_buffer << LINE_END
|
223
|
-
fast_write_str socket, io_buffer.read_and_reset
|
224
|
-
socket.flush
|
225
|
-
return keep_alive
|
226
|
-
end
|
227
|
-
if content_length
|
228
|
-
io_buffer.append CONTENT_LENGTH_S, content_length.to_s, line_ending
|
229
|
-
chunked = false
|
230
|
-
elsif !response_hijack and resp_info[:allow_chunked]
|
231
|
-
io_buffer << TRANSFER_ENCODING_CHUNKED
|
232
|
-
chunked = true
|
233
243
|
end
|
234
244
|
|
235
245
|
io_buffer << line_ending
|
236
246
|
|
237
247
|
if response_hijack
|
238
248
|
fast_write_str socket, io_buffer.read_and_reset
|
249
|
+
uncork_socket socket
|
239
250
|
response_hijack.call socket
|
240
251
|
return :async
|
241
252
|
end
|
@@ -295,8 +306,8 @@ module Puma
|
|
295
306
|
def fast_write_response(socket, body, io_buffer, chunked, content_length)
|
296
307
|
if body.is_a?(::File) && body.respond_to?(:read)
|
297
308
|
if chunked # would this ever happen?
|
298
|
-
while
|
299
|
-
io_buffer.append
|
309
|
+
while chunk = body.read(BODY_LEN_MAX)
|
310
|
+
io_buffer.append chunk.bytesize.to_s(16), LINE_END, chunk, LINE_END
|
300
311
|
end
|
301
312
|
fast_write_str socket, CLOSE_CHUNKED
|
302
313
|
else
|
@@ -476,7 +487,7 @@ module Puma
|
|
476
487
|
to_add = nil
|
477
488
|
|
478
489
|
env.each do |k,v|
|
479
|
-
if k.start_with?("HTTP_")
|
490
|
+
if k.start_with?("HTTP_") && k.include?(",") && k != "HTTP_TRANSFER,ENCODING"
|
480
491
|
if to_delete
|
481
492
|
to_delete << k
|
482
493
|
else
|
data/lib/puma/runner.rb
CHANGED
@@ -198,5 +198,12 @@ module Puma
|
|
198
198
|
}
|
199
199
|
}
|
200
200
|
end
|
201
|
+
|
202
|
+
# this method call should always be guarded by `@log_writer.debug?`
|
203
|
+
def debug_loaded_extensions(str)
|
204
|
+
@log_writer.debug "────────────────────────────────── #{str}"
|
205
|
+
re_ext = /\.#{RbConfig::CONFIG['DLEXT']}\z/i
|
206
|
+
$LOADED_FEATURES.grep(re_ext).each { |f| @log_writer.debug(" #{f}") }
|
207
|
+
end
|
201
208
|
end
|
202
209
|
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
|
data/lib/puma/server.rb
CHANGED
@@ -95,6 +95,7 @@ module Puma
|
|
95
95
|
@queue_requests = @options[:queue_requests]
|
96
96
|
@max_fast_inline = @options[:max_fast_inline]
|
97
97
|
@io_selector_backend = @options[:io_selector_backend]
|
98
|
+
@http_content_length_limit = @options[:http_content_length_limit]
|
98
99
|
|
99
100
|
temp = !!(@options[:environment] =~ /\A(development|test)\z/)
|
100
101
|
@leak_stack_on_error = @options[:environment] ? temp : true
|
@@ -334,6 +335,7 @@ module Puma
|
|
334
335
|
drain += 1 if shutting_down?
|
335
336
|
pool << Client.new(io, @binder.env(sock)).tap { |c|
|
336
337
|
c.listener = sock
|
338
|
+
c.http_content_length_limit = @http_content_length_limit
|
337
339
|
c.send(addr_send_name, addr_value) if addr_value
|
338
340
|
}
|
339
341
|
end
|
data/lib/puma/single.rb
CHANGED
data/lib/rack/handler/puma.rb
CHANGED
@@ -1,114 +1,136 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
module
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
(options.keys - user_supplied_options).each do |k|
|
28
|
-
default_options[k] = options.delete(k)
|
29
|
-
end
|
3
|
+
# This module is used as an 'include' file in code at bottom of file
|
4
|
+
module Puma
|
5
|
+
module RackHandler
|
6
|
+
DEFAULT_OPTIONS = {
|
7
|
+
:Verbose => false,
|
8
|
+
:Silent => false
|
9
|
+
}
|
10
|
+
|
11
|
+
def config(app, options = {})
|
12
|
+
require_relative '../../puma'
|
13
|
+
require_relative '../../puma/configuration'
|
14
|
+
require_relative '../../puma/log_writer'
|
15
|
+
require_relative '../../puma/launcher'
|
16
|
+
|
17
|
+
default_options = DEFAULT_OPTIONS.dup
|
18
|
+
|
19
|
+
# Libraries pass in values such as :Port and there is no way to determine
|
20
|
+
# if it is a default provided by the library or a special value provided
|
21
|
+
# by the user. A special key `user_supplied_options` can be passed. This
|
22
|
+
# contains an array of all explicitly defined user options. We then
|
23
|
+
# know that all other values are defaults
|
24
|
+
if user_supplied_options = options.delete(:user_supplied_options)
|
25
|
+
(options.keys - user_supplied_options).each do |k|
|
26
|
+
default_options[k] = options.delete(k)
|
30
27
|
end
|
28
|
+
end
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
if options[:environment]
|
39
|
-
user_config.environment options[:environment]
|
40
|
-
end
|
41
|
-
|
42
|
-
if options[:Threads]
|
43
|
-
min, max = options.delete(:Threads).split(':', 2)
|
44
|
-
user_config.threads min, max
|
45
|
-
end
|
46
|
-
|
47
|
-
if options[:Host] || options[:Port]
|
48
|
-
host = options[:Host] || default_options[:Host]
|
49
|
-
port = options[:Port] || default_options[:Port]
|
50
|
-
self.set_host_port_to_config(host, port, user_config)
|
51
|
-
end
|
52
|
-
|
53
|
-
if default_options[:Host]
|
54
|
-
file_config.set_default_host(default_options[:Host])
|
55
|
-
end
|
56
|
-
self.set_host_port_to_config(default_options[:Host], default_options[:Port], default_config)
|
57
|
-
|
58
|
-
user_config.app app
|
30
|
+
conf = ::Puma::Configuration.new(options, default_options) do |user_config, file_config, default_config|
|
31
|
+
if options.delete(:Verbose)
|
32
|
+
require 'rack/common_logger'
|
33
|
+
app = Rack::CommonLogger.new(app, STDOUT)
|
59
34
|
end
|
60
|
-
conf
|
61
|
-
end
|
62
35
|
|
63
|
-
|
64
|
-
|
36
|
+
if options[:environment]
|
37
|
+
user_config.environment options[:environment]
|
38
|
+
end
|
65
39
|
|
66
|
-
|
40
|
+
if options[:Threads]
|
41
|
+
min, max = options.delete(:Threads).split(':', 2)
|
42
|
+
user_config.threads min, max
|
43
|
+
end
|
67
44
|
|
68
|
-
|
45
|
+
if options[:Host] || options[:Port]
|
46
|
+
host = options[:Host] || default_options[:Host]
|
47
|
+
port = options[:Port] || default_options[:Port]
|
48
|
+
self.set_host_port_to_config(host, port, user_config)
|
49
|
+
end
|
69
50
|
|
70
|
-
|
71
|
-
|
72
|
-
launcher.run
|
73
|
-
rescue Interrupt
|
74
|
-
puts "* Gracefully stopping, waiting for requests to finish"
|
75
|
-
launcher.stop
|
76
|
-
puts "* Goodbye!"
|
51
|
+
if default_options[:Host]
|
52
|
+
file_config.set_default_host(default_options[:Host])
|
77
53
|
end
|
54
|
+
self.set_host_port_to_config(default_options[:Host], default_options[:Port], default_config)
|
55
|
+
|
56
|
+
user_config.app app
|
78
57
|
end
|
58
|
+
conf
|
59
|
+
end
|
60
|
+
|
61
|
+
def run(app, **options)
|
62
|
+
conf = self.config(app, options)
|
63
|
+
|
64
|
+
log_writer = options.delete(:Silent) ? ::Puma::LogWriter.strings : ::Puma::LogWriter.stdio
|
65
|
+
|
66
|
+
launcher = ::Puma::Launcher.new(conf, :log_writer => log_writer)
|
79
67
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
68
|
+
yield launcher if block_given?
|
69
|
+
begin
|
70
|
+
launcher.run
|
71
|
+
rescue Interrupt
|
72
|
+
puts "* Gracefully stopping, waiting for requests to finish"
|
73
|
+
launcher.stop
|
74
|
+
puts "* Goodbye!"
|
87
75
|
end
|
76
|
+
end
|
88
77
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
78
|
+
def valid_options
|
79
|
+
{
|
80
|
+
"Host=HOST" => "Hostname to listen on (default: localhost)",
|
81
|
+
"Port=PORT" => "Port to listen on (default: 8080)",
|
82
|
+
"Threads=MIN:MAX" => "min:max threads to use (default 0:16)",
|
83
|
+
"Verbose" => "Don't report each request (default: false)"
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def set_host_port_to_config(host, port, config)
|
88
|
+
config.clear_binds! if host || port
|
89
|
+
|
90
|
+
if host && (host[0,1] == '.' || host[0,1] == '/')
|
91
|
+
config.bind "unix://#{host}"
|
92
|
+
elsif host && host =~ /^ssl:\/\//
|
93
|
+
uri = URI.parse(host)
|
94
|
+
uri.port ||= port || ::Puma::Configuration::DEFAULTS[:tcp_port]
|
95
|
+
config.bind uri.to_s
|
96
|
+
else
|
97
|
+
|
98
|
+
if host
|
99
|
+
port ||= ::Puma::Configuration::DEFAULTS[:tcp_port]
|
100
|
+
end
|
101
|
+
|
102
|
+
if port
|
103
|
+
host ||= ::Puma::Configuration::DEFAULTS[:tcp_host]
|
104
|
+
config.port port, host
|
108
105
|
end
|
109
106
|
end
|
110
107
|
end
|
108
|
+
end
|
109
|
+
end
|
111
110
|
|
112
|
-
|
111
|
+
# rackup was removed in Rack 3, it is now a separate gem
|
112
|
+
if Object.const_defined? :Rackup
|
113
|
+
module Rackup
|
114
|
+
module Handler
|
115
|
+
module Puma
|
116
|
+
class << self
|
117
|
+
include ::Puma::RackHandler
|
118
|
+
end
|
119
|
+
end
|
120
|
+
register :puma, Puma
|
121
|
+
end
|
122
|
+
end
|
123
|
+
elsif Object.const_defined?(:Rack) && Rack::RELEASE < '3'
|
124
|
+
module Rack
|
125
|
+
module Handler
|
126
|
+
module Puma
|
127
|
+
class << self
|
128
|
+
include ::Puma::RackHandler
|
129
|
+
end
|
130
|
+
end
|
131
|
+
register :puma, Puma
|
132
|
+
end
|
113
133
|
end
|
134
|
+
else
|
135
|
+
raise "You must install the rackup gem when using Rack 3"
|
114
136
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 6.0
|
4
|
+
version: 6.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Phoenix
|
@@ -104,6 +104,7 @@ files:
|
|
104
104
|
- lib/puma/minissl/context_builder.rb
|
105
105
|
- lib/puma/null_io.rb
|
106
106
|
- lib/puma/plugin.rb
|
107
|
+
- lib/puma/plugin/systemd.rb
|
107
108
|
- lib/puma/plugin/tmp_restart.rb
|
108
109
|
- lib/puma/rack/builder.rb
|
109
110
|
- lib/puma/rack/urlmap.rb
|
@@ -111,10 +112,10 @@ files:
|
|
111
112
|
- lib/puma/reactor.rb
|
112
113
|
- lib/puma/request.rb
|
113
114
|
- lib/puma/runner.rb
|
115
|
+
- lib/puma/sd_notify.rb
|
114
116
|
- lib/puma/server.rb
|
115
117
|
- lib/puma/single.rb
|
116
118
|
- lib/puma/state_file.rb
|
117
|
-
- lib/puma/systemd.rb
|
118
119
|
- lib/puma/thread_pool.rb
|
119
120
|
- lib/puma/util.rb
|
120
121
|
- lib/rack/handler/puma.rb
|
@@ -144,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
144
145
|
- !ruby/object:Gem::Version
|
145
146
|
version: '0'
|
146
147
|
requirements: []
|
147
|
-
rubygems_version: 3.
|
148
|
+
rubygems_version: 3.3.20
|
148
149
|
signing_key:
|
149
150
|
specification_version: 4
|
150
151
|
summary: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server for
|
data/lib/puma/systemd.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'sd_notify'
|
4
|
-
|
5
|
-
module Puma
|
6
|
-
class Systemd
|
7
|
-
def initialize(log_writer, events)
|
8
|
-
@log_writer = log_writer
|
9
|
-
@events = events
|
10
|
-
end
|
11
|
-
|
12
|
-
def hook_events
|
13
|
-
@events.on_booted { SdNotify.ready }
|
14
|
-
@events.on_stopped { SdNotify.stopping }
|
15
|
-
@events.on_restart { SdNotify.reloading }
|
16
|
-
end
|
17
|
-
|
18
|
-
def start_watchdog
|
19
|
-
return unless SdNotify.watchdog?
|
20
|
-
|
21
|
-
ping_f = watchdog_sleep_time
|
22
|
-
|
23
|
-
log "Pinging systemd watchdog every #{ping_f.round(1)} sec"
|
24
|
-
Thread.new do
|
25
|
-
loop do
|
26
|
-
sleep ping_f
|
27
|
-
SdNotify.watchdog
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def watchdog_sleep_time
|
35
|
-
usec = Integer(ENV["WATCHDOG_USEC"])
|
36
|
-
|
37
|
-
sec_f = usec / 1_000_000.0
|
38
|
-
# "It is recommended that a daemon sends a keep-alive notification message
|
39
|
-
# to the service manager every half of the time returned here."
|
40
|
-
sec_f / 2
|
41
|
-
end
|
42
|
-
|
43
|
-
def log(str)
|
44
|
-
@log_writer.log(str)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|