puma 5.3.2 → 5.6.4
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 +148 -8
- data/LICENSE +0 -0
- data/README.md +47 -6
- data/bin/puma-wild +0 -0
- data/docs/architecture.md +49 -16
- data/docs/compile_options.md +4 -2
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +0 -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 +0 -0
- data/docs/jungle/rc.d/README.md +0 -0
- data/docs/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +0 -0
- data/docs/nginx.md +0 -0
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +2 -3
- data/docs/restart.md +6 -6
- data/docs/signals.md +11 -10
- data/docs/stats.md +8 -8
- data/docs/systemd.md +64 -67
- data/ext/puma_http11/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +28 -5
- data/ext/puma_http11/http11_parser.c +23 -10
- data/ext/puma_http11/http11_parser.h +0 -0
- data/ext/puma_http11/http11_parser.java.rl +0 -0
- data/ext/puma_http11/http11_parser.rl +0 -0
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +69 -9
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +49 -47
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +28 -43
- data/ext/puma_http11/puma_http11.c +1 -1
- data/lib/puma/app/status.rb +4 -4
- data/lib/puma/binder.rb +50 -5
- data/lib/puma/cli.rb +14 -4
- data/lib/puma/client.rb +104 -20
- data/lib/puma/cluster/worker.rb +8 -18
- data/lib/puma/cluster/worker_handle.rb +4 -0
- data/lib/puma/cluster.rb +30 -24
- data/lib/puma/commonlogger.rb +0 -0
- data/lib/puma/configuration.rb +4 -1
- data/lib/puma/const.rb +9 -8
- data/lib/puma/control_cli.rb +1 -1
- data/lib/puma/detect.rb +8 -2
- data/lib/puma/dsl.rb +105 -11
- data/lib/puma/error_logger.rb +0 -0
- data/lib/puma/events.rb +0 -0
- data/lib/puma/io_buffer.rb +0 -0
- data/lib/puma/jruby_restart.rb +0 -0
- data/lib/puma/{json.rb → json_serialization.rb} +1 -1
- data/lib/puma/launcher.rb +4 -1
- data/lib/puma/minissl/context_builder.rb +8 -6
- data/lib/puma/minissl.rb +24 -23
- data/lib/puma/null_io.rb +0 -0
- data/lib/puma/plugin/tmp_restart.rb +0 -0
- data/lib/puma/plugin.rb +2 -2
- data/lib/puma/queue_close.rb +0 -0
- data/lib/puma/rack/builder.rb +1 -1
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +0 -0
- data/lib/puma/reactor.rb +0 -0
- data/lib/puma/request.rb +14 -9
- data/lib/puma/runner.rb +22 -8
- data/lib/puma/server.rb +35 -29
- data/lib/puma/single.rb +0 -0
- data/lib/puma/state_file.rb +41 -7
- data/lib/puma/systemd.rb +0 -0
- data/lib/puma/thread_pool.rb +7 -5
- data/lib/puma/util.rb +8 -1
- data/lib/puma.rb +2 -2
- data/lib/rack/handler/puma.rb +0 -0
- data/tools/Dockerfile +1 -1
- data/tools/trickletest.rb +0 -0
- metadata +7 -7
data/lib/puma/cli.rb
CHANGED
@@ -11,16 +11,17 @@ require 'puma/events'
|
|
11
11
|
|
12
12
|
module Puma
|
13
13
|
class << self
|
14
|
-
# The CLI exports
|
15
|
-
# apps to pick it up. An app
|
16
|
-
#
|
17
|
-
#
|
14
|
+
# The CLI exports a Puma::Configuration instance here to allow
|
15
|
+
# apps to pick it up. An app must load this object conditionally
|
16
|
+
# because it is not set if the app is launched via any mechanism
|
17
|
+
# other than the CLI class.
|
18
18
|
attr_accessor :cli_config
|
19
19
|
end
|
20
20
|
|
21
21
|
# Handles invoke a Puma::Server in a command line style.
|
22
22
|
#
|
23
23
|
class CLI
|
24
|
+
# @deprecated 6.0.0
|
24
25
|
KEYS_NOT_TO_PERSIST_IN_STATE = Launcher::KEYS_NOT_TO_PERSIST_IN_STATE
|
25
26
|
|
26
27
|
# Create a new CLI object using +argv+ as the command line
|
@@ -112,6 +113,11 @@ module Puma
|
|
112
113
|
file_config.load arg
|
113
114
|
end
|
114
115
|
|
116
|
+
# Identical to supplying --config "-", but more semantic
|
117
|
+
o.on "--no-config", "Prevent Puma from searching for a config file" do |arg|
|
118
|
+
file_config.load "-"
|
119
|
+
end
|
120
|
+
|
115
121
|
o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
|
116
122
|
configure_control_url(arg)
|
117
123
|
end
|
@@ -179,6 +185,10 @@ module Puma
|
|
179
185
|
user_config.restart_command cmd
|
180
186
|
end
|
181
187
|
|
188
|
+
o.on "-s", "--silent", "Do not log prompt messages other than errors" do
|
189
|
+
@events = Events.new NullIO.new, $stderr
|
190
|
+
end
|
191
|
+
|
182
192
|
o.on "-S", "--state PATH", "Where to store the state details" do |arg|
|
183
193
|
user_config.state_path arg
|
184
194
|
end
|
data/lib/puma/client.rb
CHANGED
@@ -23,6 +23,8 @@ module Puma
|
|
23
23
|
|
24
24
|
class ConnectionError < RuntimeError; end
|
25
25
|
|
26
|
+
class HttpParserError501 < IOError; end
|
27
|
+
|
26
28
|
# An instance of this class represents a unique request from a client.
|
27
29
|
# For example, this could be a web request from a browser or from CURL.
|
28
30
|
#
|
@@ -35,7 +37,21 @@ module Puma
|
|
35
37
|
# Instances of this class are responsible for knowing if
|
36
38
|
# the header and body are fully buffered via the `try_to_finish` method.
|
37
39
|
# They can be used to "time out" a response via the `timeout_at` reader.
|
40
|
+
#
|
38
41
|
class Client
|
42
|
+
|
43
|
+
# this tests all values but the last, which must be chunked
|
44
|
+
ALLOWED_TRANSFER_ENCODING = %w[compress deflate gzip].freeze
|
45
|
+
|
46
|
+
# chunked body validation
|
47
|
+
CHUNK_SIZE_INVALID = /[^\h]/.freeze
|
48
|
+
CHUNK_VALID_ENDING = "\r\n".freeze
|
49
|
+
|
50
|
+
# Content-Length header value validation
|
51
|
+
CONTENT_LENGTH_VALUE_INVALID = /[^\d]/.freeze
|
52
|
+
|
53
|
+
TE_ERR_MSG = 'Invalid Transfer-Encoding'
|
54
|
+
|
39
55
|
# The object used for a request with no body. All requests with
|
40
56
|
# no body share this one object since it has no state.
|
41
57
|
EmptyBody = NullIO.new
|
@@ -56,6 +72,7 @@ module Puma
|
|
56
72
|
@parser = HttpParser.new
|
57
73
|
@parsed_bytes = 0
|
58
74
|
@read_header = true
|
75
|
+
@read_proxy = false
|
59
76
|
@ready = false
|
60
77
|
|
61
78
|
@body = nil
|
@@ -71,6 +88,7 @@ module Puma
|
|
71
88
|
@peerip = nil
|
72
89
|
@listener = nil
|
73
90
|
@remote_addr_header = nil
|
91
|
+
@expect_proxy_proto = false
|
74
92
|
|
75
93
|
@body_remain = 0
|
76
94
|
|
@@ -106,7 +124,7 @@ module Puma
|
|
106
124
|
|
107
125
|
# @!attribute [r] in_data_phase
|
108
126
|
def in_data_phase
|
109
|
-
|
127
|
+
!(@read_header || @read_proxy)
|
110
128
|
end
|
111
129
|
|
112
130
|
def set_timeout(val)
|
@@ -121,6 +139,7 @@ module Puma
|
|
121
139
|
def reset(fast_check=true)
|
122
140
|
@parser.reset
|
123
141
|
@read_header = true
|
142
|
+
@read_proxy = !!@expect_proxy_proto
|
124
143
|
@env = @proto_env.dup
|
125
144
|
@body = nil
|
126
145
|
@tempfile = nil
|
@@ -131,6 +150,8 @@ module Puma
|
|
131
150
|
@in_last_chunk = false
|
132
151
|
|
133
152
|
if @buffer
|
153
|
+
return false unless try_to_parse_proxy_protocol
|
154
|
+
|
134
155
|
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
135
156
|
|
136
157
|
if @parser.finished?
|
@@ -143,8 +164,7 @@ module Puma
|
|
143
164
|
return false
|
144
165
|
else
|
145
166
|
begin
|
146
|
-
if fast_check &&
|
147
|
-
IO.select([@to_io], nil, nil, FAST_TRACK_KA_TIMEOUT)
|
167
|
+
if fast_check && @to_io.wait_readable(FAST_TRACK_KA_TIMEOUT)
|
148
168
|
return try_to_finish
|
149
169
|
end
|
150
170
|
rescue IOError
|
@@ -157,13 +177,37 @@ module Puma
|
|
157
177
|
def close
|
158
178
|
begin
|
159
179
|
@io.close
|
160
|
-
rescue IOError
|
161
|
-
|
180
|
+
rescue IOError, Errno::EBADF
|
181
|
+
Puma::Util.purge_interrupt_queue
|
162
182
|
end
|
163
183
|
end
|
164
184
|
|
185
|
+
# If necessary, read the PROXY protocol from the buffer. Returns
|
186
|
+
# false if more data is needed.
|
187
|
+
def try_to_parse_proxy_protocol
|
188
|
+
if @read_proxy
|
189
|
+
if @expect_proxy_proto == :v1
|
190
|
+
if @buffer.include? "\r\n"
|
191
|
+
if md = PROXY_PROTOCOL_V1_REGEX.match(@buffer)
|
192
|
+
if md[1]
|
193
|
+
@peerip = md[1].split(" ")[0]
|
194
|
+
end
|
195
|
+
@buffer = md.post_match
|
196
|
+
end
|
197
|
+
# if the buffer has a \r\n but doesn't have a PROXY protocol
|
198
|
+
# request, this is just HTTP from a non-PROXY client; move on
|
199
|
+
@read_proxy = false
|
200
|
+
return @buffer.size > 0
|
201
|
+
else
|
202
|
+
return false
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
true
|
207
|
+
end
|
208
|
+
|
165
209
|
def try_to_finish
|
166
|
-
return read_body
|
210
|
+
return read_body if in_data_phase
|
167
211
|
|
168
212
|
begin
|
169
213
|
data = @io.read_nonblock(CHUNK_SIZE)
|
@@ -188,6 +232,8 @@ module Puma
|
|
188
232
|
@buffer = data
|
189
233
|
end
|
190
234
|
|
235
|
+
return false unless try_to_parse_proxy_protocol
|
236
|
+
|
191
237
|
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
192
238
|
|
193
239
|
if @parser.finished?
|
@@ -202,13 +248,13 @@ module Puma
|
|
202
248
|
|
203
249
|
def eagerly_finish
|
204
250
|
return true if @ready
|
205
|
-
return false unless
|
251
|
+
return false unless @to_io.wait_readable(0)
|
206
252
|
try_to_finish
|
207
253
|
end
|
208
254
|
|
209
255
|
def finish(timeout)
|
210
256
|
return if @ready
|
211
|
-
|
257
|
+
@to_io.wait_readable(timeout) || timeout! until try_to_finish
|
212
258
|
end
|
213
259
|
|
214
260
|
def timeout!
|
@@ -244,6 +290,17 @@ module Puma
|
|
244
290
|
@parsed_bytes == 0
|
245
291
|
end
|
246
292
|
|
293
|
+
def expect_proxy_proto=(val)
|
294
|
+
if val
|
295
|
+
if @read_header
|
296
|
+
@read_proxy = true
|
297
|
+
end
|
298
|
+
else
|
299
|
+
@read_proxy = false
|
300
|
+
end
|
301
|
+
@expect_proxy_proto = val
|
302
|
+
end
|
303
|
+
|
247
304
|
private
|
248
305
|
|
249
306
|
def setup_body
|
@@ -261,16 +318,27 @@ module Puma
|
|
261
318
|
body = @parser.body
|
262
319
|
|
263
320
|
te = @env[TRANSFER_ENCODING2]
|
264
|
-
|
265
321
|
if te
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
322
|
+
te_lwr = te.downcase
|
323
|
+
if te.include? ','
|
324
|
+
te_ary = te_lwr.split ','
|
325
|
+
te_count = te_ary.count CHUNKED
|
326
|
+
te_valid = te_ary[0..-2].all? { |e| ALLOWED_TRANSFER_ENCODING.include? e }
|
327
|
+
if te_ary.last == CHUNKED && te_count == 1 && te_valid
|
328
|
+
@env.delete TRANSFER_ENCODING2
|
329
|
+
return setup_chunked_body body
|
330
|
+
elsif te_count >= 1
|
331
|
+
raise HttpParserError , "#{TE_ERR_MSG}, multiple chunked: '#{te}'"
|
332
|
+
elsif !te_valid
|
333
|
+
raise HttpParserError501, "#{TE_ERR_MSG}, unknown value: '#{te}'"
|
271
334
|
end
|
272
|
-
elsif
|
273
|
-
|
335
|
+
elsif te_lwr == CHUNKED
|
336
|
+
@env.delete TRANSFER_ENCODING2
|
337
|
+
return setup_chunked_body body
|
338
|
+
elsif ALLOWED_TRANSFER_ENCODING.include? te_lwr
|
339
|
+
raise HttpParserError , "#{TE_ERR_MSG}, single value must be chunked: '#{te}'"
|
340
|
+
else
|
341
|
+
raise HttpParserError501 , "#{TE_ERR_MSG}, unknown value: '#{te}'"
|
274
342
|
end
|
275
343
|
end
|
276
344
|
|
@@ -278,7 +346,12 @@ module Puma
|
|
278
346
|
|
279
347
|
cl = @env[CONTENT_LENGTH]
|
280
348
|
|
281
|
-
|
349
|
+
if cl
|
350
|
+
# cannot contain characters that are not \d
|
351
|
+
if cl =~ CONTENT_LENGTH_VALUE_INVALID
|
352
|
+
raise HttpParserError, "Invalid Content-Length: #{cl.inspect}"
|
353
|
+
end
|
354
|
+
else
|
282
355
|
@buffer = body.empty? ? nil : body
|
283
356
|
@body = EmptyBody
|
284
357
|
set_ready
|
@@ -309,7 +382,7 @@ module Puma
|
|
309
382
|
|
310
383
|
@body_remain = remain
|
311
384
|
|
312
|
-
|
385
|
+
false
|
313
386
|
end
|
314
387
|
|
315
388
|
def read_body
|
@@ -437,7 +510,13 @@ module Puma
|
|
437
510
|
while !io.eof?
|
438
511
|
line = io.gets
|
439
512
|
if line.end_with?("\r\n")
|
440
|
-
|
513
|
+
# Puma doesn't process chunk extensions, but should parse if they're
|
514
|
+
# present, which is the reason for the semicolon regex
|
515
|
+
chunk_hex = line.strip[/\A[^;]+/]
|
516
|
+
if chunk_hex =~ CHUNK_SIZE_INVALID
|
517
|
+
raise HttpParserError, "Invalid chunk size: '#{chunk_hex}'"
|
518
|
+
end
|
519
|
+
len = chunk_hex.to_i(16)
|
441
520
|
if len == 0
|
442
521
|
@in_last_chunk = true
|
443
522
|
@body.rewind
|
@@ -468,7 +547,12 @@ module Puma
|
|
468
547
|
|
469
548
|
case
|
470
549
|
when got == len
|
471
|
-
|
550
|
+
# proper chunked segment must end with "\r\n"
|
551
|
+
if part.end_with? CHUNK_VALID_ENDING
|
552
|
+
write_chunk(part[0..-3]) # to skip the ending \r\n
|
553
|
+
else
|
554
|
+
raise HttpParserError, "Chunk size mismatch"
|
555
|
+
end
|
472
556
|
when got <= len - 2
|
473
557
|
write_chunk(part)
|
474
558
|
@partial_part_left = len - part.size
|
data/lib/puma/cluster/worker.rb
CHANGED
@@ -33,9 +33,9 @@ module Puma
|
|
33
33
|
Signal.trap "SIGINT", "IGNORE"
|
34
34
|
Signal.trap "SIGCHLD", "DEFAULT"
|
35
35
|
|
36
|
-
|
37
|
-
Puma.set_thread_name "
|
38
|
-
|
36
|
+
Thread.new do
|
37
|
+
Puma.set_thread_name "wrkr check"
|
38
|
+
@check_pipe.wait_readable
|
39
39
|
log "! Detected parent died, dying"
|
40
40
|
exit! 1
|
41
41
|
end
|
@@ -76,7 +76,7 @@ module Puma
|
|
76
76
|
end
|
77
77
|
|
78
78
|
Thread.new do
|
79
|
-
Puma.set_thread_name "
|
79
|
+
Puma.set_thread_name "wrkr fork"
|
80
80
|
while (idx = @fork_pipe.gets)
|
81
81
|
idx = idx.to_i
|
82
82
|
if idx == -1 # stop server
|
@@ -106,7 +106,7 @@ module Puma
|
|
106
106
|
begin
|
107
107
|
@worker_write << "b#{Process.pid}:#{index}\n"
|
108
108
|
rescue SystemCallError, IOError
|
109
|
-
|
109
|
+
Puma::Util.purge_interrupt_queue
|
110
110
|
STDERR.puts "Master seems to have exited, exiting."
|
111
111
|
return
|
112
112
|
end
|
@@ -114,7 +114,7 @@ module Puma
|
|
114
114
|
while restart_server.pop
|
115
115
|
server_thread = server.run
|
116
116
|
stat_thread ||= Thread.new(@worker_write) do |io|
|
117
|
-
Puma.set_thread_name "stat
|
117
|
+
Puma.set_thread_name "stat pld"
|
118
118
|
base_payload = "p#{Process.pid}"
|
119
119
|
|
120
120
|
while true
|
@@ -127,10 +127,10 @@ module Puma
|
|
127
127
|
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m}, "requests_count": #{rc} }\n!
|
128
128
|
io << payload
|
129
129
|
rescue IOError
|
130
|
-
|
130
|
+
Puma::Util.purge_interrupt_queue
|
131
131
|
break
|
132
132
|
end
|
133
|
-
sleep
|
133
|
+
sleep @options[:worker_check_interval]
|
134
134
|
end
|
135
135
|
end
|
136
136
|
server_thread.join
|
@@ -168,16 +168,6 @@ module Puma
|
|
168
168
|
@launcher.config.run_hooks :after_worker_fork, idx, @launcher.events
|
169
169
|
pid
|
170
170
|
end
|
171
|
-
|
172
|
-
def wakeup!
|
173
|
-
return unless @wakeup
|
174
|
-
|
175
|
-
begin
|
176
|
-
@wakeup.write "!" unless @wakeup.closed?
|
177
|
-
rescue SystemCallError, IOError
|
178
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
179
|
-
end
|
180
|
-
end
|
181
171
|
end
|
182
172
|
end
|
183
173
|
end
|
data/lib/puma/cluster.rb
CHANGED
@@ -108,24 +108,42 @@ module Puma
|
|
108
108
|
def cull_workers
|
109
109
|
diff = @workers.size - @options[:workers]
|
110
110
|
return if diff < 1
|
111
|
+
debug "Culling #{diff} workers"
|
111
112
|
|
112
|
-
|
113
|
+
workers = workers_to_cull(diff)
|
114
|
+
debug "Workers to cull: #{workers.inspect}"
|
113
115
|
|
114
|
-
|
115
|
-
debug "Workers to cull: #{workers_to_cull.inspect}"
|
116
|
-
|
117
|
-
workers_to_cull.each do |worker|
|
116
|
+
workers.each do |worker|
|
118
117
|
log "- Worker #{worker.index} (PID: #{worker.pid}) terminating"
|
119
118
|
worker.term
|
120
119
|
end
|
121
120
|
end
|
122
121
|
|
122
|
+
def workers_to_cull(diff)
|
123
|
+
workers = @workers.sort_by(&:started_at)
|
124
|
+
|
125
|
+
# In fork_worker mode, worker 0 acts as our master process.
|
126
|
+
# We should avoid culling it to preserve copy-on-write memory gains.
|
127
|
+
workers.reject! { |w| w.index == 0 } if @options[:fork_worker]
|
128
|
+
|
129
|
+
workers[cull_start_index(diff), diff]
|
130
|
+
end
|
131
|
+
|
132
|
+
def cull_start_index(diff)
|
133
|
+
case @options[:worker_culling_strategy]
|
134
|
+
when :oldest
|
135
|
+
0
|
136
|
+
else # :youngest
|
137
|
+
-diff
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
123
141
|
# @!attribute [r] next_worker_index
|
124
142
|
def next_worker_index
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
143
|
+
occupied_positions = @workers.map(&:index)
|
144
|
+
idx = 0
|
145
|
+
idx += 1 until !occupied_positions.include?(idx)
|
146
|
+
idx
|
129
147
|
end
|
130
148
|
|
131
149
|
def all_workers_booted?
|
@@ -135,7 +153,7 @@ module Puma
|
|
135
153
|
def check_workers
|
136
154
|
return if @next_check >= Time.now
|
137
155
|
|
138
|
-
@next_check = Time.now +
|
156
|
+
@next_check = Time.now + @options[:worker_check_interval]
|
139
157
|
|
140
158
|
timeout_workers
|
141
159
|
wait_workers
|
@@ -164,16 +182,6 @@ module Puma
|
|
164
182
|
].compact.min
|
165
183
|
end
|
166
184
|
|
167
|
-
def wakeup!
|
168
|
-
return unless @wakeup
|
169
|
-
|
170
|
-
begin
|
171
|
-
@wakeup.write "!" unless @wakeup.closed?
|
172
|
-
rescue SystemCallError, IOError
|
173
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
174
|
-
end
|
175
|
-
end
|
176
|
-
|
177
185
|
def worker(index, master)
|
178
186
|
@workers = []
|
179
187
|
|
@@ -426,9 +434,7 @@ module Puma
|
|
426
434
|
|
427
435
|
check_workers
|
428
436
|
|
429
|
-
|
430
|
-
|
431
|
-
if res
|
437
|
+
if read.wait_readable([0, @next_check - Time.now].max)
|
432
438
|
req = read.read_nonblock(1)
|
433
439
|
|
434
440
|
@next_check = Time.now if req == "!"
|
@@ -452,7 +458,7 @@ module Puma
|
|
452
458
|
workers_not_booted -= 1
|
453
459
|
when "e"
|
454
460
|
# external term, see worker method, Signal.trap "SIGTERM"
|
455
|
-
w.
|
461
|
+
w.term!
|
456
462
|
when "t"
|
457
463
|
w.term unless w.term?
|
458
464
|
when "p"
|
data/lib/puma/commonlogger.rb
CHANGED
File without changes
|
data/lib/puma/configuration.rb
CHANGED
@@ -11,6 +11,7 @@ module Puma
|
|
11
11
|
|
12
12
|
DefaultTCPHost = "0.0.0.0"
|
13
13
|
DefaultTCPPort = 9292
|
14
|
+
DefaultWorkerCheckInterval = 5
|
14
15
|
DefaultWorkerTimeout = 60
|
15
16
|
DefaultWorkerShutdownTimeout = 30
|
16
17
|
end
|
@@ -195,12 +196,14 @@ module Puma
|
|
195
196
|
:workers => Integer(ENV['WEB_CONCURRENCY'] || 0),
|
196
197
|
:silence_single_worker_warning => false,
|
197
198
|
:mode => :http,
|
199
|
+
:worker_check_interval => DefaultWorkerCheckInterval,
|
198
200
|
:worker_timeout => DefaultWorkerTimeout,
|
199
201
|
:worker_boot_timeout => DefaultWorkerTimeout,
|
200
202
|
:worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
|
203
|
+
:worker_culling_strategy => :youngest,
|
201
204
|
:remote_address => :socket,
|
202
205
|
:tag => method(:infer_tag),
|
203
|
-
:environment => -> { ENV['RACK_ENV'] || ENV['RAILS_ENV'] ||
|
206
|
+
:environment => -> { ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' },
|
204
207
|
:rackup => DefaultRackup,
|
205
208
|
:logger => STDOUT,
|
206
209
|
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
data/lib/puma/const.rb
CHANGED
@@ -76,7 +76,7 @@ module Puma
|
|
76
76
|
508 => 'Loop Detected',
|
77
77
|
510 => 'Not Extended',
|
78
78
|
511 => 'Network Authentication Required'
|
79
|
-
}
|
79
|
+
}.freeze
|
80
80
|
|
81
81
|
# For some HTTP status codes the client only expects headers.
|
82
82
|
#
|
@@ -85,7 +85,7 @@ module Puma
|
|
85
85
|
204 => true,
|
86
86
|
205 => true,
|
87
87
|
304 => true
|
88
|
-
}
|
88
|
+
}.freeze
|
89
89
|
|
90
90
|
# Frequently used constants when constructing requests or responses. Many times
|
91
91
|
# the constant just refers to a string with the same contents. Using these constants
|
@@ -100,8 +100,8 @@ module Puma
|
|
100
100
|
# too taxing on performance.
|
101
101
|
module Const
|
102
102
|
|
103
|
-
PUMA_VERSION = VERSION = "5.
|
104
|
-
CODE_NAME = "
|
103
|
+
PUMA_VERSION = VERSION = "5.6.4".freeze
|
104
|
+
CODE_NAME = "Birdie's Version".freeze
|
105
105
|
|
106
106
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
107
107
|
|
@@ -145,9 +145,11 @@ module Puma
|
|
145
145
|
408 => "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\nServer: Puma #{PUMA_VERSION}\r\n\r\n".freeze,
|
146
146
|
# Indicate that there was an internal error, obviously.
|
147
147
|
500 => "HTTP/1.1 500 Internal Server Error\r\n\r\n".freeze,
|
148
|
+
# Incorrect or invalid header value
|
149
|
+
501 => "HTTP/1.1 501 Not Implemented\r\n\r\n".freeze,
|
148
150
|
# A common header for indicating the server is too busy. Not used yet.
|
149
151
|
503 => "HTTP/1.1 503 Service Unavailable\r\n\r\nBUSY".freeze
|
150
|
-
}
|
152
|
+
}.freeze
|
151
153
|
|
152
154
|
# The basic max request size we'll try to read.
|
153
155
|
CHUNK_SIZE = 16 * 1024
|
@@ -235,9 +237,6 @@ module Puma
|
|
235
237
|
|
236
238
|
EARLY_HINTS = "rack.early_hints".freeze
|
237
239
|
|
238
|
-
# Minimum interval to checks worker health
|
239
|
-
WORKER_CHECK_INTERVAL = 5
|
240
|
-
|
241
240
|
# Illegal character in the key or value of response header
|
242
241
|
DQUOTE = "\"".freeze
|
243
242
|
HTTP_HEADER_DELIMITER = Regexp.escape("(),/:;<=>?@[]{}\\").freeze
|
@@ -247,5 +246,7 @@ module Puma
|
|
247
246
|
|
248
247
|
# Banned keys of response header
|
249
248
|
BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
|
249
|
+
|
250
|
+
PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
|
250
251
|
end
|
251
252
|
end
|
data/lib/puma/control_cli.rb
CHANGED
data/lib/puma/detect.rb
CHANGED
@@ -10,8 +10,10 @@ module Puma
|
|
10
10
|
|
11
11
|
IS_JRUBY = Object.const_defined? :JRUBY_VERSION
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
IS_OSX = RUBY_PLATFORM.include? 'darwin'
|
14
|
+
|
15
|
+
IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/) ||
|
16
|
+
IS_JRUBY && RUBY_DESCRIPTION.include?('mswin')
|
15
17
|
|
16
18
|
# @version 5.2.0
|
17
19
|
IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
|
@@ -20,6 +22,10 @@ module Puma
|
|
20
22
|
IS_JRUBY
|
21
23
|
end
|
22
24
|
|
25
|
+
def self.osx?
|
26
|
+
IS_OSX
|
27
|
+
end
|
28
|
+
|
23
29
|
def self.windows?
|
24
30
|
IS_WINDOWS
|
25
31
|
end
|