puma 3.12.6 → 6.2.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 +1775 -451
- data/LICENSE +23 -20
- data/README.md +193 -65
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +59 -21
- data/docs/compile_options.md +55 -0
- data/docs/deployment.md +69 -58
- data/docs/fork_worker.md +31 -0
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/jungle/README.md +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 +2 -2
- data/docs/plugins.md +22 -12
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +47 -22
- data/docs/signals.md +13 -11
- data/docs/stats.md +142 -0
- data/docs/systemd.md +94 -120
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -2
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +61 -3
- data/ext/puma_http11/http11_parser.c +103 -117
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +22 -38
- data/ext/puma_http11/http11_parser.rl +3 -3
- data/ext/puma_http11/http11_parser_common.rl +6 -6
- data/ext/puma_http11/mini_ssl.c +361 -99
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +248 -92
- data/ext/puma_http11/puma_http11.c +49 -57
- data/lib/puma/app/status.rb +71 -49
- data/lib/puma/binder.rb +242 -150
- data/lib/puma/cli.rb +38 -34
- data/lib/puma/client.rb +387 -244
- data/lib/puma/cluster/worker.rb +180 -0
- data/lib/puma/cluster/worker_handle.rb +97 -0
- data/lib/puma/cluster.rb +261 -243
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +116 -88
- data/lib/puma/const.rb +101 -100
- data/lib/puma/control_cli.rb +115 -70
- data/lib/puma/detect.rb +33 -2
- data/lib/puma/dsl.rb +731 -134
- data/lib/puma/error_logger.rb +113 -0
- data/lib/puma/events.rb +16 -112
- data/lib/puma/io_buffer.rb +42 -5
- data/lib/puma/jruby_restart.rb +2 -59
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +184 -133
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +92 -0
- data/lib/puma/minissl.rb +246 -70
- data/lib/puma/null_io.rb +18 -1
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +3 -1
- data/lib/puma/plugin.rb +7 -13
- data/lib/puma/rack/builder.rb +7 -9
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +21 -4
- data/lib/puma/reactor.rb +85 -316
- data/lib/puma/request.rb +665 -0
- data/lib/puma/runner.rb +94 -69
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +314 -771
- data/lib/puma/single.rb +20 -74
- data/lib/puma/state_file.rb +45 -8
- data/lib/puma/thread_pool.rb +142 -92
- data/lib/puma/util.rb +22 -10
- data/lib/puma.rb +60 -5
- data/lib/rack/handler/puma.rb +113 -91
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +54 -32
- data/ext/puma_http11/io_buffer.c +0 -155
- data/lib/puma/accept_nonblock.rb +0 -23
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/convenient.rb +0 -25
- data/lib/puma/daemon_ext.rb +0 -33
- data/lib/puma/delegation.rb +0 -13
- data/lib/puma/java_io_buffer.rb +0 -47
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
- 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
@@ -0,0 +1,92 @@
|
|
1
|
+
module Puma
|
2
|
+
module MiniSSL
|
3
|
+
class ContextBuilder
|
4
|
+
def initialize(params, log_writer)
|
5
|
+
@params = params
|
6
|
+
@log_writer = log_writer
|
7
|
+
end
|
8
|
+
|
9
|
+
def context
|
10
|
+
ctx = MiniSSL::Context.new
|
11
|
+
|
12
|
+
if defined?(JRUBY_VERSION)
|
13
|
+
unless params['keystore']
|
14
|
+
log_writer.error "Please specify the Java keystore via 'keystore='"
|
15
|
+
end
|
16
|
+
|
17
|
+
ctx.keystore = params['keystore']
|
18
|
+
|
19
|
+
unless params['keystore-pass']
|
20
|
+
log_writer.error "Please specify the Java keystore password via 'keystore-pass='"
|
21
|
+
end
|
22
|
+
|
23
|
+
ctx.keystore_pass = params['keystore-pass']
|
24
|
+
ctx.keystore_type = params['keystore-type']
|
25
|
+
|
26
|
+
if truststore = params['truststore']
|
27
|
+
ctx.truststore = truststore.eql?('default') ? :default : truststore
|
28
|
+
ctx.truststore_pass = params['truststore-pass']
|
29
|
+
ctx.truststore_type = params['truststore-type']
|
30
|
+
end
|
31
|
+
|
32
|
+
ctx.cipher_suites = params['cipher_suites'] || params['ssl_cipher_list']
|
33
|
+
ctx.protocols = params['protocols'] if params['protocols']
|
34
|
+
else
|
35
|
+
if params['key'].nil? && params['key_pem'].nil?
|
36
|
+
log_writer.error "Please specify the SSL key via 'key=' or 'key_pem='"
|
37
|
+
end
|
38
|
+
|
39
|
+
ctx.key = params['key'] if params['key']
|
40
|
+
ctx.key_pem = params['key_pem'] if params['key_pem']
|
41
|
+
|
42
|
+
if params['cert'].nil? && params['cert_pem'].nil?
|
43
|
+
log_writer.error "Please specify the SSL cert via 'cert=' or 'cert_pem='"
|
44
|
+
end
|
45
|
+
|
46
|
+
ctx.cert = params['cert'] if params['cert']
|
47
|
+
ctx.cert_pem = params['cert_pem'] if params['cert_pem']
|
48
|
+
|
49
|
+
if ['peer', 'force_peer'].include?(params['verify_mode'])
|
50
|
+
unless params['ca']
|
51
|
+
log_writer.error "Please specify the SSL ca via 'ca='"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
ctx.ca = params['ca'] if params['ca']
|
56
|
+
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
57
|
+
|
58
|
+
ctx.reuse = params['reuse'] if params['reuse']
|
59
|
+
end
|
60
|
+
|
61
|
+
ctx.no_tlsv1 = params['no_tlsv1'] == 'true'
|
62
|
+
ctx.no_tlsv1_1 = params['no_tlsv1_1'] == 'true'
|
63
|
+
|
64
|
+
if params['verify_mode']
|
65
|
+
ctx.verify_mode = case params['verify_mode']
|
66
|
+
when "peer"
|
67
|
+
MiniSSL::VERIFY_PEER
|
68
|
+
when "force_peer"
|
69
|
+
MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
70
|
+
when "none"
|
71
|
+
MiniSSL::VERIFY_NONE
|
72
|
+
else
|
73
|
+
log_writer.error "Please specify a valid verify_mode="
|
74
|
+
MiniSSL::VERIFY_NONE
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
if params['verification_flags']
|
79
|
+
ctx.verification_flags = params['verification_flags'].split(',').
|
80
|
+
map { |flag| MiniSSL::VERIFICATION_FLAGS.fetch(flag) }.
|
81
|
+
inject { |sum, flag| sum ? sum | flag : flag }
|
82
|
+
end
|
83
|
+
|
84
|
+
ctx
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
attr_reader :params, :log_writer
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/puma/minissl.rb
CHANGED
@@ -1,19 +1,32 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
begin
|
4
|
-
require 'io/wait'
|
5
|
-
|
4
|
+
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
5
|
+
rescue LoadError
|
6
6
|
end
|
7
7
|
|
8
|
+
# need for Puma::MiniSSL::OPENSSL constants used in `HAS_TLS1_3`
|
9
|
+
# use require, see https://github.com/puma/puma/pull/2381
|
10
|
+
require 'puma/puma_http11'
|
11
|
+
|
8
12
|
module Puma
|
9
13
|
module MiniSSL
|
14
|
+
# Define constant at runtime, as it's easy to determine at built time,
|
15
|
+
# but Puma could (it shouldn't) be loaded with an older OpenSSL version
|
16
|
+
# @version 5.0.0
|
17
|
+
HAS_TLS1_3 = IS_JRUBY ||
|
18
|
+
((OPENSSL_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) != -1 &&
|
19
|
+
(OPENSSL_LIBRARY_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) !=-1)
|
20
|
+
|
10
21
|
class Socket
|
11
22
|
def initialize(socket, engine)
|
12
23
|
@socket = socket
|
13
24
|
@engine = engine
|
14
25
|
@peercert = nil
|
26
|
+
@reuse = nil
|
15
27
|
end
|
16
28
|
|
29
|
+
# @!attribute [r] to_io
|
17
30
|
def to_io
|
18
31
|
@socket
|
19
32
|
end
|
@@ -22,6 +35,27 @@ module Puma
|
|
22
35
|
@socket.closed?
|
23
36
|
end
|
24
37
|
|
38
|
+
# Returns a two element array,
|
39
|
+
# first is protocol version (SSL_get_version),
|
40
|
+
# second is 'handshake' state (SSL_state_string)
|
41
|
+
#
|
42
|
+
# Used for dropping tcp connections to ssl.
|
43
|
+
# See OpenSSL ssl/ssl_stat.c SSL_state_string for info
|
44
|
+
# @!attribute [r] ssl_version_state
|
45
|
+
# @version 5.0.0
|
46
|
+
#
|
47
|
+
def ssl_version_state
|
48
|
+
IS_JRUBY ? [nil, nil] : @engine.ssl_vers_st
|
49
|
+
end
|
50
|
+
|
51
|
+
# Used to check the handshake status, in particular when a TCP connection
|
52
|
+
# is made with TLSv1.3 as an available protocol
|
53
|
+
# @version 5.0.0
|
54
|
+
def bad_tlsv1_3?
|
55
|
+
HAS_TLS1_3 && ssl_version_state == ['TLSv1.3', 'SSLERR']
|
56
|
+
end
|
57
|
+
private :bad_tlsv1_3?
|
58
|
+
|
25
59
|
def readpartial(size)
|
26
60
|
while true
|
27
61
|
output = @engine.read
|
@@ -54,22 +88,22 @@ module Puma
|
|
54
88
|
output = engine_read_all
|
55
89
|
return output if output
|
56
90
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
end
|
91
|
+
data = @socket.read_nonblock(size, exception: false)
|
92
|
+
if data == :wait_readable || data == :wait_writable
|
93
|
+
# It would make more sense to let @socket.read_nonblock raise
|
94
|
+
# EAGAIN if necessary but it seems like it'll misbehave on Windows.
|
95
|
+
# I don't have a Windows machine to debug this so I can't explain
|
96
|
+
# exactly whats happening in that OS. Please let me know if you
|
97
|
+
# find out!
|
98
|
+
#
|
99
|
+
# In the meantime, we can emulate the correct behavior by
|
100
|
+
# capturing :wait_readable & :wait_writable and raising EAGAIN
|
101
|
+
# ourselves.
|
102
|
+
raise IO::EAGAINWaitReadable
|
103
|
+
elsif data.nil?
|
104
|
+
raise SSLError.exception "HTTP connection?" if bad_tlsv1_3?
|
105
|
+
return nil
|
106
|
+
end
|
73
107
|
|
74
108
|
@engine.inject(data)
|
75
109
|
output = engine_read_all
|
@@ -85,22 +119,23 @@ module Puma
|
|
85
119
|
def write(data)
|
86
120
|
return 0 if data.empty?
|
87
121
|
|
88
|
-
|
122
|
+
data_size = data.bytesize
|
123
|
+
need = data_size
|
89
124
|
|
90
125
|
while true
|
91
126
|
wrote = @engine.write data
|
92
|
-
enc = @engine.extract
|
93
127
|
|
94
|
-
|
95
|
-
|
96
|
-
|
128
|
+
enc_wr = +''
|
129
|
+
while (enc = @engine.extract)
|
130
|
+
enc_wr << enc
|
97
131
|
end
|
132
|
+
@socket.write enc_wr unless enc_wr.empty?
|
98
133
|
|
99
134
|
need -= wrote
|
100
135
|
|
101
|
-
return
|
136
|
+
return data_size if need == 0
|
102
137
|
|
103
|
-
data = data
|
138
|
+
data = data.byteslice(wrote..-1)
|
104
139
|
end
|
105
140
|
end
|
106
141
|
|
@@ -108,14 +143,18 @@ module Puma
|
|
108
143
|
alias_method :<<, :write
|
109
144
|
|
110
145
|
# This is a temporary fix to deal with websockets code using
|
111
|
-
# write_nonblock.
|
146
|
+
# write_nonblock.
|
147
|
+
|
148
|
+
# The problem with implementing it properly
|
112
149
|
# is that it means we'd have to have the ability to rewind
|
113
150
|
# an engine because after we write+extract, the socket
|
114
151
|
# write_nonblock call might raise an exception and later
|
115
152
|
# code would pass the same data in, but the engine would think
|
116
|
-
# it had already written the data in.
|
117
|
-
#
|
118
|
-
#
|
153
|
+
# it had already written the data in.
|
154
|
+
#
|
155
|
+
# So for the time being (and since write blocking is quite rare),
|
156
|
+
# go ahead and actually block in write_nonblock.
|
157
|
+
#
|
119
158
|
def write_nonblock(data, *_)
|
120
159
|
write data
|
121
160
|
end
|
@@ -124,39 +163,27 @@ module Puma
|
|
124
163
|
@socket.flush
|
125
164
|
end
|
126
165
|
|
127
|
-
def read_and_drop(timeout = 1)
|
128
|
-
return :timeout unless IO.select([@socket], nil, nil, timeout)
|
129
|
-
return :eof unless read_nonblock(1024)
|
130
|
-
:drop
|
131
|
-
rescue Errno::EAGAIN
|
132
|
-
# do nothing
|
133
|
-
:eagain
|
134
|
-
end
|
135
|
-
|
136
|
-
def should_drop_bytes?
|
137
|
-
@engine.init? || !@engine.shutdown
|
138
|
-
end
|
139
|
-
|
140
166
|
def close
|
141
167
|
begin
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
return if [:timeout, :eof].include?(read_and_drop(1))
|
168
|
+
unless @engine.shutdown
|
169
|
+
while alert_data = @engine.extract
|
170
|
+
@socket.write alert_data
|
171
|
+
end
|
147
172
|
end
|
148
173
|
rescue IOError, SystemCallError
|
149
|
-
|
174
|
+
Puma::Util.purge_interrupt_queue
|
150
175
|
# nothing
|
151
176
|
ensure
|
152
177
|
@socket.close
|
153
178
|
end
|
154
179
|
end
|
155
180
|
|
181
|
+
# @!attribute [r] peeraddr
|
156
182
|
def peeraddr
|
157
183
|
@socket.peeraddr
|
158
184
|
end
|
159
185
|
|
186
|
+
# @!attribute [r] peercert
|
160
187
|
def peercert
|
161
188
|
return @peercert if @peercert
|
162
189
|
|
@@ -167,30 +194,84 @@ module Puma
|
|
167
194
|
end
|
168
195
|
end
|
169
196
|
|
170
|
-
if
|
171
|
-
|
172
|
-
|
173
|
-
end
|
174
|
-
|
175
|
-
def self.check; end
|
197
|
+
if IS_JRUBY
|
198
|
+
OPENSSL_NO_SSL3 = false
|
199
|
+
OPENSSL_NO_TLS1 = false
|
176
200
|
end
|
177
201
|
|
178
202
|
class Context
|
179
203
|
attr_accessor :verify_mode
|
204
|
+
attr_reader :no_tlsv1, :no_tlsv1_1
|
205
|
+
|
206
|
+
def initialize
|
207
|
+
@no_tlsv1 = false
|
208
|
+
@no_tlsv1_1 = false
|
209
|
+
@key = nil
|
210
|
+
@cert = nil
|
211
|
+
@key_pem = nil
|
212
|
+
@cert_pem = nil
|
213
|
+
@reuse = nil
|
214
|
+
@reuse_cache_size = nil
|
215
|
+
@reuse_timeout = nil
|
216
|
+
end
|
217
|
+
|
218
|
+
def check_file(file, desc)
|
219
|
+
raise ArgumentError, "#{desc} file '#{file}' does not exist" unless File.exist? file
|
220
|
+
raise ArgumentError, "#{desc} file '#{file}' is not readable" unless File.readable? file
|
221
|
+
end
|
180
222
|
|
181
|
-
if
|
223
|
+
if IS_JRUBY
|
182
224
|
# jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
|
183
225
|
attr_reader :keystore
|
226
|
+
attr_reader :keystore_type
|
184
227
|
attr_accessor :keystore_pass
|
185
|
-
|
228
|
+
attr_reader :truststore
|
229
|
+
attr_reader :truststore_type
|
230
|
+
attr_accessor :truststore_pass
|
231
|
+
attr_reader :cipher_suites
|
232
|
+
attr_reader :protocols
|
186
233
|
|
187
234
|
def keystore=(keystore)
|
188
|
-
|
235
|
+
check_file keystore, 'Keystore'
|
189
236
|
@keystore = keystore
|
190
237
|
end
|
191
238
|
|
239
|
+
def truststore=(truststore)
|
240
|
+
# NOTE: historically truststore was assumed the same as keystore, this is kept for backwards
|
241
|
+
# compatibility, to rely on JVM's trust defaults we allow setting `truststore = :default`
|
242
|
+
unless truststore.eql?(:default)
|
243
|
+
raise ArgumentError, "No such truststore file '#{truststore}'" unless File.exist?(truststore)
|
244
|
+
end
|
245
|
+
@truststore = truststore
|
246
|
+
end
|
247
|
+
|
248
|
+
def keystore_type=(type)
|
249
|
+
raise ArgumentError, "Invalid keystore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
|
250
|
+
@keystore_type = type
|
251
|
+
end
|
252
|
+
|
253
|
+
def truststore_type=(type)
|
254
|
+
raise ArgumentError, "Invalid truststore type: #{type.inspect}" unless ['pkcs12', 'jks', nil].include?(type)
|
255
|
+
@truststore_type = type
|
256
|
+
end
|
257
|
+
|
258
|
+
def cipher_suites=(list)
|
259
|
+
list = list.split(',').map(&:strip) if list.is_a?(String)
|
260
|
+
@cipher_suites = list
|
261
|
+
end
|
262
|
+
|
263
|
+
# aliases for backwards compatibility
|
264
|
+
alias_method :ssl_cipher_list, :cipher_suites
|
265
|
+
alias_method :ssl_cipher_list=, :cipher_suites=
|
266
|
+
|
267
|
+
def protocols=(list)
|
268
|
+
list = list.split(',').map(&:strip) if list.is_a?(String)
|
269
|
+
@protocols = list
|
270
|
+
end
|
271
|
+
|
192
272
|
def check
|
193
273
|
raise "Keystore not configured" unless @keystore
|
274
|
+
# @truststore defaults to @keystore due backwards compatibility
|
194
275
|
end
|
195
276
|
|
196
277
|
else
|
@@ -198,63 +279,158 @@ module Puma
|
|
198
279
|
attr_reader :key
|
199
280
|
attr_reader :cert
|
200
281
|
attr_reader :ca
|
282
|
+
attr_reader :cert_pem
|
283
|
+
attr_reader :key_pem
|
201
284
|
attr_accessor :ssl_cipher_filter
|
285
|
+
attr_accessor :verification_flags
|
286
|
+
|
287
|
+
attr_reader :reuse, :reuse_cache_size, :reuse_timeout
|
202
288
|
|
203
289
|
def key=(key)
|
204
|
-
|
290
|
+
check_file key, 'Key'
|
205
291
|
@key = key
|
206
292
|
end
|
207
293
|
|
208
294
|
def cert=(cert)
|
209
|
-
|
295
|
+
check_file cert, 'Cert'
|
210
296
|
@cert = cert
|
211
297
|
end
|
212
298
|
|
213
299
|
def ca=(ca)
|
214
|
-
|
300
|
+
check_file ca, 'ca'
|
215
301
|
@ca = ca
|
216
302
|
end
|
217
303
|
|
304
|
+
def cert_pem=(cert_pem)
|
305
|
+
raise ArgumentError, "'cert_pem' is not a String" unless cert_pem.is_a? String
|
306
|
+
@cert_pem = cert_pem
|
307
|
+
end
|
308
|
+
|
309
|
+
def key_pem=(key_pem)
|
310
|
+
raise ArgumentError, "'key_pem' is not a String" unless key_pem.is_a? String
|
311
|
+
@key_pem = key_pem
|
312
|
+
end
|
313
|
+
|
218
314
|
def check
|
219
|
-
raise "Key not configured"
|
220
|
-
raise "Cert not configured"
|
315
|
+
raise "Key not configured" if @key.nil? && @key_pem.nil?
|
316
|
+
raise "Cert not configured" if @cert.nil? && @cert_pem.nil?
|
317
|
+
end
|
318
|
+
|
319
|
+
# Controls session reuse. Allowed values are as follows:
|
320
|
+
# * 'off' - matches the behavior of Puma 5.6 and earlier. This is included
|
321
|
+
# in case reuse 'on' is made the default in future Puma versions.
|
322
|
+
# * 'dflt' - sets session reuse on, with OpenSSL default cache size of
|
323
|
+
# 20k and default timeout of 300 seconds.
|
324
|
+
# * 's,t' - where s and t are integer strings, for size and timeout.
|
325
|
+
# * 's' - where s is an integer strings for size.
|
326
|
+
# * ',t' - where t is an integer strings for timeout.
|
327
|
+
#
|
328
|
+
def reuse=(reuse_str)
|
329
|
+
case reuse_str
|
330
|
+
when 'off'
|
331
|
+
@reuse = nil
|
332
|
+
when 'dflt'
|
333
|
+
@reuse = true
|
334
|
+
when /\A\d+\z/
|
335
|
+
@reuse = true
|
336
|
+
@reuse_cache_size = reuse_str.to_i
|
337
|
+
when /\A\d+,\d+\z/
|
338
|
+
@reuse = true
|
339
|
+
size, time = reuse_str.split ','
|
340
|
+
@reuse_cache_size = size.to_i
|
341
|
+
@reuse_timeout = time.to_i
|
342
|
+
when /\A,\d+\z/
|
343
|
+
@reuse = true
|
344
|
+
@reuse_timeout = reuse_str.delete(',').to_i
|
345
|
+
end
|
221
346
|
end
|
222
347
|
end
|
348
|
+
|
349
|
+
# disables TLSv1
|
350
|
+
# @!attribute [w] no_tlsv1=
|
351
|
+
def no_tlsv1=(tlsv1)
|
352
|
+
raise ArgumentError, "Invalid value of no_tlsv1=" unless ['true', 'false', true, false].include?(tlsv1)
|
353
|
+
@no_tlsv1 = tlsv1
|
354
|
+
end
|
355
|
+
|
356
|
+
# disables TLSv1 and TLSv1.1. Overrides `#no_tlsv1=`
|
357
|
+
# @!attribute [w] no_tlsv1_1=
|
358
|
+
def no_tlsv1_1=(tlsv1_1)
|
359
|
+
raise ArgumentError, "Invalid value of no_tlsv1_1=" unless ['true', 'false', true, false].include?(tlsv1_1)
|
360
|
+
@no_tlsv1_1 = tlsv1_1
|
361
|
+
end
|
362
|
+
|
223
363
|
end
|
224
364
|
|
225
365
|
VERIFY_NONE = 0
|
226
366
|
VERIFY_PEER = 1
|
227
367
|
VERIFY_FAIL_IF_NO_PEER_CERT = 2
|
228
368
|
|
369
|
+
# https://github.com/openssl/openssl/blob/master/include/openssl/x509_vfy.h.in
|
370
|
+
# /* Certificate verify flags */
|
371
|
+
VERIFICATION_FLAGS = {
|
372
|
+
"USE_CHECK_TIME" => 0x2,
|
373
|
+
"CRL_CHECK" => 0x4,
|
374
|
+
"CRL_CHECK_ALL" => 0x8,
|
375
|
+
"IGNORE_CRITICAL" => 0x10,
|
376
|
+
"X509_STRICT" => 0x20,
|
377
|
+
"ALLOW_PROXY_CERTS" => 0x40,
|
378
|
+
"POLICY_CHECK" => 0x80,
|
379
|
+
"EXPLICIT_POLICY" => 0x100,
|
380
|
+
"INHIBIT_ANY" => 0x200,
|
381
|
+
"INHIBIT_MAP" => 0x400,
|
382
|
+
"NOTIFY_POLICY" => 0x800,
|
383
|
+
"EXTENDED_CRL_SUPPORT" => 0x1000,
|
384
|
+
"USE_DELTAS" => 0x2000,
|
385
|
+
"CHECK_SS_SIGNATURE" => 0x4000,
|
386
|
+
"TRUSTED_FIRST" => 0x8000,
|
387
|
+
"SUITEB_128_LOS_ONLY" => 0x10000,
|
388
|
+
"SUITEB_192_LOS" => 0x20000,
|
389
|
+
"SUITEB_128_LOS" => 0x30000,
|
390
|
+
"PARTIAL_CHAIN" => 0x80000,
|
391
|
+
"NO_ALT_CHAINS" => 0x100000,
|
392
|
+
"NO_CHECK_TIME" => 0x200000
|
393
|
+
}.freeze
|
394
|
+
|
229
395
|
class Server
|
230
396
|
def initialize(socket, ctx)
|
231
397
|
@socket = socket
|
232
398
|
@ctx = ctx
|
233
|
-
|
234
|
-
|
235
|
-
def to_io
|
236
|
-
@socket
|
399
|
+
@eng_ctx = IS_JRUBY ? @ctx : SSLContext.new(ctx)
|
237
400
|
end
|
238
401
|
|
239
402
|
def accept
|
240
403
|
@ctx.check
|
241
404
|
io = @socket.accept
|
242
|
-
engine = Engine.server @
|
243
|
-
|
405
|
+
engine = Engine.server @eng_ctx
|
244
406
|
Socket.new io, engine
|
245
407
|
end
|
246
408
|
|
247
409
|
def accept_nonblock
|
248
410
|
@ctx.check
|
249
411
|
io = @socket.accept_nonblock
|
250
|
-
engine = Engine.server @
|
251
|
-
|
412
|
+
engine = Engine.server @eng_ctx
|
252
413
|
Socket.new io, engine
|
253
414
|
end
|
254
415
|
|
416
|
+
# @!attribute [r] to_io
|
417
|
+
def to_io
|
418
|
+
@socket
|
419
|
+
end
|
420
|
+
|
421
|
+
# @!attribute [r] addr
|
422
|
+
# @version 5.0.0
|
423
|
+
def addr
|
424
|
+
@socket.addr
|
425
|
+
end
|
426
|
+
|
255
427
|
def close
|
256
428
|
@socket.close unless @socket.closed? # closed? call is for Windows
|
257
429
|
end
|
430
|
+
|
431
|
+
def closed?
|
432
|
+
@socket.closed?
|
433
|
+
end
|
258
434
|
end
|
259
435
|
end
|
260
436
|
end
|
data/lib/puma/null_io.rb
CHANGED
@@ -9,13 +9,17 @@ module Puma
|
|
9
9
|
nil
|
10
10
|
end
|
11
11
|
|
12
|
+
def string
|
13
|
+
""
|
14
|
+
end
|
15
|
+
|
12
16
|
def each
|
13
17
|
end
|
14
18
|
|
15
19
|
# Mimics IO#read with no data.
|
16
20
|
#
|
17
21
|
def read(count = nil, _buffer = nil)
|
18
|
-
|
22
|
+
count && count > 0 ? nil : ""
|
19
23
|
end
|
20
24
|
|
21
25
|
def rewind
|
@@ -32,6 +36,10 @@ module Puma
|
|
32
36
|
true
|
33
37
|
end
|
34
38
|
|
39
|
+
def sync
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
35
43
|
def sync=(v)
|
36
44
|
end
|
37
45
|
|
@@ -40,5 +48,14 @@ module Puma
|
|
40
48
|
|
41
49
|
def write(*ary)
|
42
50
|
end
|
51
|
+
|
52
|
+
def flush
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
# This is used as singleton class, so can't have state.
|
57
|
+
def closed?
|
58
|
+
false
|
59
|
+
end
|
43
60
|
end
|
44
61
|
end
|
@@ -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
|