puma 5.3.1 → 5.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +152 -5
- 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 +51 -6
- data/lib/puma/cli.rb +14 -4
- data/lib/puma/client.rb +106 -21
- data/lib/puma/cluster/worker.rb +14 -17
- 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 +6 -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 +106 -12
- 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 +27 -11
- data/lib/puma/runner.rb +22 -8
- data/lib/puma/server.rb +45 -44
- 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
|
@@ -69,7 +86,9 @@ module Puma
|
|
69
86
|
@hijacked = false
|
70
87
|
|
71
88
|
@peerip = nil
|
89
|
+
@listener = nil
|
72
90
|
@remote_addr_header = nil
|
91
|
+
@expect_proxy_proto = false
|
73
92
|
|
74
93
|
@body_remain = 0
|
75
94
|
|
@@ -81,7 +100,7 @@ module Puma
|
|
81
100
|
|
82
101
|
attr_writer :peerip
|
83
102
|
|
84
|
-
attr_accessor :remote_addr_header
|
103
|
+
attr_accessor :remote_addr_header, :listener
|
85
104
|
|
86
105
|
def_delegators :@io, :closed?
|
87
106
|
|
@@ -105,7 +124,7 @@ module Puma
|
|
105
124
|
|
106
125
|
# @!attribute [r] in_data_phase
|
107
126
|
def in_data_phase
|
108
|
-
|
127
|
+
!(@read_header || @read_proxy)
|
109
128
|
end
|
110
129
|
|
111
130
|
def set_timeout(val)
|
@@ -120,6 +139,7 @@ module Puma
|
|
120
139
|
def reset(fast_check=true)
|
121
140
|
@parser.reset
|
122
141
|
@read_header = true
|
142
|
+
@read_proxy = !!@expect_proxy_proto
|
123
143
|
@env = @proto_env.dup
|
124
144
|
@body = nil
|
125
145
|
@tempfile = nil
|
@@ -130,6 +150,8 @@ module Puma
|
|
130
150
|
@in_last_chunk = false
|
131
151
|
|
132
152
|
if @buffer
|
153
|
+
return false unless try_to_parse_proxy_protocol
|
154
|
+
|
133
155
|
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
134
156
|
|
135
157
|
if @parser.finished?
|
@@ -142,8 +164,7 @@ module Puma
|
|
142
164
|
return false
|
143
165
|
else
|
144
166
|
begin
|
145
|
-
if fast_check &&
|
146
|
-
IO.select([@to_io], nil, nil, FAST_TRACK_KA_TIMEOUT)
|
167
|
+
if fast_check && @to_io.wait_readable(FAST_TRACK_KA_TIMEOUT)
|
147
168
|
return try_to_finish
|
148
169
|
end
|
149
170
|
rescue IOError
|
@@ -156,13 +177,37 @@ module Puma
|
|
156
177
|
def close
|
157
178
|
begin
|
158
179
|
@io.close
|
159
|
-
rescue IOError
|
160
|
-
|
180
|
+
rescue IOError, Errno::EBADF
|
181
|
+
Puma::Util.purge_interrupt_queue
|
161
182
|
end
|
162
183
|
end
|
163
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
|
+
|
164
209
|
def try_to_finish
|
165
|
-
return read_body
|
210
|
+
return read_body if in_data_phase
|
166
211
|
|
167
212
|
begin
|
168
213
|
data = @io.read_nonblock(CHUNK_SIZE)
|
@@ -187,6 +232,8 @@ module Puma
|
|
187
232
|
@buffer = data
|
188
233
|
end
|
189
234
|
|
235
|
+
return false unless try_to_parse_proxy_protocol
|
236
|
+
|
190
237
|
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
191
238
|
|
192
239
|
if @parser.finished?
|
@@ -201,13 +248,13 @@ module Puma
|
|
201
248
|
|
202
249
|
def eagerly_finish
|
203
250
|
return true if @ready
|
204
|
-
return false unless
|
251
|
+
return false unless @to_io.wait_readable(0)
|
205
252
|
try_to_finish
|
206
253
|
end
|
207
254
|
|
208
255
|
def finish(timeout)
|
209
256
|
return if @ready
|
210
|
-
|
257
|
+
@to_io.wait_readable(timeout) || timeout! until try_to_finish
|
211
258
|
end
|
212
259
|
|
213
260
|
def timeout!
|
@@ -243,6 +290,17 @@ module Puma
|
|
243
290
|
@parsed_bytes == 0
|
244
291
|
end
|
245
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
|
+
|
246
304
|
private
|
247
305
|
|
248
306
|
def setup_body
|
@@ -260,16 +318,27 @@ module Puma
|
|
260
318
|
body = @parser.body
|
261
319
|
|
262
320
|
te = @env[TRANSFER_ENCODING2]
|
263
|
-
|
264
321
|
if te
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
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}'"
|
270
334
|
end
|
271
|
-
elsif
|
272
|
-
|
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}'"
|
273
342
|
end
|
274
343
|
end
|
275
344
|
|
@@ -277,7 +346,12 @@ module Puma
|
|
277
346
|
|
278
347
|
cl = @env[CONTENT_LENGTH]
|
279
348
|
|
280
|
-
|
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
|
281
355
|
@buffer = body.empty? ? nil : body
|
282
356
|
@body = EmptyBody
|
283
357
|
set_ready
|
@@ -308,7 +382,7 @@ module Puma
|
|
308
382
|
|
309
383
|
@body_remain = remain
|
310
384
|
|
311
|
-
|
385
|
+
false
|
312
386
|
end
|
313
387
|
|
314
388
|
def read_body
|
@@ -436,7 +510,13 @@ module Puma
|
|
436
510
|
while !io.eof?
|
437
511
|
line = io.gets
|
438
512
|
if line.end_with?("\r\n")
|
439
|
-
|
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)
|
440
520
|
if len == 0
|
441
521
|
@in_last_chunk = true
|
442
522
|
@body.rewind
|
@@ -467,7 +547,12 @@ module Puma
|
|
467
547
|
|
468
548
|
case
|
469
549
|
when got == len
|
470
|
-
|
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
|
471
556
|
when got <= len - 2
|
472
557
|
write_chunk(part)
|
473
558
|
@partial_part_left = len - part.size
|
data/lib/puma/cluster/worker.rb
CHANGED
@@ -34,8 +34,8 @@ module Puma
|
|
34
34
|
Signal.trap "SIGCHLD", "DEFAULT"
|
35
35
|
|
36
36
|
Thread.new do
|
37
|
-
Puma.set_thread_name "
|
38
|
-
|
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
|
@@ -54,7 +54,14 @@ module Puma
|
|
54
54
|
# things in shape before booting the app.
|
55
55
|
@launcher.config.run_hooks :before_worker_boot, index, @launcher.events
|
56
56
|
|
57
|
+
begin
|
57
58
|
server = @server ||= start_server
|
59
|
+
rescue Exception => e
|
60
|
+
log "! Unable to start worker"
|
61
|
+
log e.backtrace[0]
|
62
|
+
exit 1
|
63
|
+
end
|
64
|
+
|
58
65
|
restart_server = Queue.new << true << false
|
59
66
|
|
60
67
|
fork_worker = @options[:fork_worker] && index == 0
|
@@ -69,7 +76,7 @@ module Puma
|
|
69
76
|
end
|
70
77
|
|
71
78
|
Thread.new do
|
72
|
-
Puma.set_thread_name "
|
79
|
+
Puma.set_thread_name "wrkr fork"
|
73
80
|
while (idx = @fork_pipe.gets)
|
74
81
|
idx = idx.to_i
|
75
82
|
if idx == -1 # stop server
|
@@ -99,7 +106,7 @@ module Puma
|
|
99
106
|
begin
|
100
107
|
@worker_write << "b#{Process.pid}:#{index}\n"
|
101
108
|
rescue SystemCallError, IOError
|
102
|
-
|
109
|
+
Puma::Util.purge_interrupt_queue
|
103
110
|
STDERR.puts "Master seems to have exited, exiting."
|
104
111
|
return
|
105
112
|
end
|
@@ -107,7 +114,7 @@ module Puma
|
|
107
114
|
while restart_server.pop
|
108
115
|
server_thread = server.run
|
109
116
|
stat_thread ||= Thread.new(@worker_write) do |io|
|
110
|
-
Puma.set_thread_name "stat
|
117
|
+
Puma.set_thread_name "stat pld"
|
111
118
|
base_payload = "p#{Process.pid}"
|
112
119
|
|
113
120
|
while true
|
@@ -120,10 +127,10 @@ module Puma
|
|
120
127
|
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r}, "pool_capacity":#{t}, "max_threads": #{m}, "requests_count": #{rc} }\n!
|
121
128
|
io << payload
|
122
129
|
rescue IOError
|
123
|
-
|
130
|
+
Puma::Util.purge_interrupt_queue
|
124
131
|
break
|
125
132
|
end
|
126
|
-
sleep
|
133
|
+
sleep @options[:worker_check_interval]
|
127
134
|
end
|
128
135
|
end
|
129
136
|
server_thread.join
|
@@ -161,16 +168,6 @@ module Puma
|
|
161
168
|
@launcher.config.run_hooks :after_worker_fork, idx, @launcher.events
|
162
169
|
pid
|
163
170
|
end
|
164
|
-
|
165
|
-
def wakeup!
|
166
|
-
return unless @wakeup
|
167
|
-
|
168
|
-
begin
|
169
|
-
@wakeup.write "!" unless @wakeup.closed?
|
170
|
-
rescue SystemCallError, IOError
|
171
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
172
|
-
end
|
173
|
-
end
|
174
171
|
end
|
175
172
|
end
|
176
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,
|
@@ -343,6 +346,8 @@ module Puma
|
|
343
346
|
raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
|
344
347
|
|
345
348
|
rack_app, rack_options = rack_builder.parse_file(rackup)
|
349
|
+
rack_options = rack_options || {}
|
350
|
+
|
346
351
|
@options.file_options.merge!(rack_options)
|
347
352
|
|
348
353
|
config_ru_binds = []
|
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
|