puma 5.3.1-java → 5.5.1-java
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.
- checksums.yaml +4 -4
- data/History.md +80 -5
- data/README.md +42 -6
- data/docs/architecture.md +49 -16
- data/docs/compile_options.md +4 -2
- data/docs/deployment.md +53 -67
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +2 -3
- data/docs/restart.md +6 -6
- data/docs/signals.md +10 -10
- data/docs/stats.md +8 -8
- data/docs/systemd.md +64 -67
- data/ext/puma_http11/extconf.rb +18 -1
- data/ext/puma_http11/http11_parser.c +19 -12
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +16 -1
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +34 -34
- data/lib/puma/app/status.rb +4 -4
- data/lib/puma/binder.rb +34 -3
- data/lib/puma/cli.rb +5 -0
- data/lib/puma/client.rb +51 -9
- data/lib/puma/cluster/worker.rb +11 -14
- data/lib/puma/cluster.rb +1 -13
- data/lib/puma/configuration.rb +3 -1
- data/lib/puma/const.rb +4 -2
- data/lib/puma/control_cli.rb +1 -1
- data/lib/puma/dsl.rb +21 -4
- data/lib/puma/{json.rb → json_serialization.rb} +1 -1
- data/lib/puma/launcher.rb +2 -0
- data/lib/puma/minissl.rb +6 -21
- data/lib/puma/plugin.rb +1 -1
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/rack/builder.rb +1 -1
- data/lib/puma/request.rb +17 -6
- data/lib/puma/runner.rb +19 -6
- data/lib/puma/server.rb +28 -24
- data/lib/puma/thread_pool.rb +6 -4
- data/lib/puma/util.rb +8 -1
- data/lib/puma.rb +2 -2
- metadata +6 -6
data/lib/puma/binder.rb
CHANGED
|
@@ -41,6 +41,7 @@ module Puma
|
|
|
41
41
|
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
|
42
42
|
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
|
43
43
|
"rack.run_once".freeze => false,
|
|
44
|
+
RACK_URL_SCHEME => conf.options[:rack_url_scheme],
|
|
44
45
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
|
45
46
|
|
|
46
47
|
# I'd like to set a default CONTENT_TYPE here but some things
|
|
@@ -56,6 +57,7 @@ module Puma
|
|
|
56
57
|
|
|
57
58
|
@envs = {}
|
|
58
59
|
@ios = []
|
|
60
|
+
localhost_authority
|
|
59
61
|
end
|
|
60
62
|
|
|
61
63
|
attr_reader :ios
|
|
@@ -95,6 +97,7 @@ module Puma
|
|
|
95
97
|
# @version 5.0.0
|
|
96
98
|
#
|
|
97
99
|
def create_activated_fds(env_hash)
|
|
100
|
+
@events.debug "ENV['LISTEN_FDS'] #{ENV['LISTEN_FDS'].inspect} env_hash['LISTEN_PID'] #{env_hash['LISTEN_PID'].inspect}"
|
|
98
101
|
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
|
99
102
|
env_hash['LISTEN_FDS'].to_i.times do |index|
|
|
100
103
|
sock = TCPServer.for_fd(socket_activation_fd(index))
|
|
@@ -163,7 +166,7 @@ module Puma
|
|
|
163
166
|
ios_len = @ios.length
|
|
164
167
|
params = Util.parse_query uri.query
|
|
165
168
|
|
|
166
|
-
opt = params.key?('low_latency')
|
|
169
|
+
opt = params.key?('low_latency') && params['low_latency'] != 'false'
|
|
167
170
|
bak = params.fetch('backlog', 1024).to_i
|
|
168
171
|
|
|
169
172
|
io = add_tcp_listener uri.host, uri.port, opt, bak
|
|
@@ -188,7 +191,8 @@ module Puma
|
|
|
188
191
|
@unix_paths << path unless abstract
|
|
189
192
|
io = inherit_unix_listener path, fd
|
|
190
193
|
logger.log "* Inherited #{str}"
|
|
191
|
-
elsif sock = @activated_sockets.delete([ :unix, path ])
|
|
194
|
+
elsif sock = @activated_sockets.delete([ :unix, path ]) ||
|
|
195
|
+
@activated_sockets.delete([ :unix, File.realdirpath(path) ])
|
|
192
196
|
@unix_paths << path unless abstract || File.exist?(path)
|
|
193
197
|
io = inherit_unix_listener path, sock
|
|
194
198
|
logger.log "* Activated #{str}"
|
|
@@ -224,7 +228,13 @@ module Puma
|
|
|
224
228
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
|
225
229
|
|
|
226
230
|
params = Util.parse_query uri.query
|
|
227
|
-
|
|
231
|
+
|
|
232
|
+
# If key and certs are not defined and localhost gem is required.
|
|
233
|
+
# localhost gem will be used for self signed
|
|
234
|
+
# Load localhost authority if not loaded.
|
|
235
|
+
ctx = localhost_authority && localhost_authority_context if params.empty?
|
|
236
|
+
|
|
237
|
+
ctx ||= MiniSSL::ContextBuilder.new(params, @events).context
|
|
228
238
|
|
|
229
239
|
if fd = @inherited_fds.delete(str)
|
|
230
240
|
logger.log "* Inherited #{str}"
|
|
@@ -282,6 +292,22 @@ module Puma
|
|
|
282
292
|
end
|
|
283
293
|
end
|
|
284
294
|
|
|
295
|
+
def localhost_authority
|
|
296
|
+
@localhost_authority ||= Localhost::Authority.fetch if defined?(Localhost::Authority) && !Puma::IS_JRUBY
|
|
297
|
+
end
|
|
298
|
+
|
|
299
|
+
def localhost_authority_context
|
|
300
|
+
return unless localhost_authority
|
|
301
|
+
|
|
302
|
+
key_path, crt_path = if [:key_path, :certificate_path].all? { |m| localhost_authority.respond_to?(m) }
|
|
303
|
+
[localhost_authority.key_path, localhost_authority.certificate_path]
|
|
304
|
+
else
|
|
305
|
+
local_certificates_path = File.expand_path("~/.localhost")
|
|
306
|
+
[File.join(local_certificates_path, "localhost.key"), File.join(local_certificates_path, "localhost.crt")]
|
|
307
|
+
end
|
|
308
|
+
MiniSSL::ContextBuilder.new({ "key" => key_path, "cert" => crt_path }, @events).context
|
|
309
|
+
end
|
|
310
|
+
|
|
285
311
|
# Tell the server to listen on host +host+, port +port+.
|
|
286
312
|
# If +optimize_for_latency+ is true (the default) then clients connecting
|
|
287
313
|
# will be optimized for latency over throughput.
|
|
@@ -299,6 +325,7 @@ module Puma
|
|
|
299
325
|
|
|
300
326
|
host = host[1..-2] if host and host[0..0] == '['
|
|
301
327
|
tcp_server = TCPServer.new(host, port)
|
|
328
|
+
|
|
302
329
|
if optimize_for_latency
|
|
303
330
|
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
|
304
331
|
end
|
|
@@ -320,6 +347,8 @@ module Puma
|
|
|
320
347
|
optimize_for_latency=true, backlog=1024)
|
|
321
348
|
|
|
322
349
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
|
350
|
+
# Puma will try to use local authority context if context is supplied nil
|
|
351
|
+
ctx ||= localhost_authority_context
|
|
323
352
|
|
|
324
353
|
if host == "localhost"
|
|
325
354
|
loopback_addresses.each do |addr|
|
|
@@ -347,6 +376,8 @@ module Puma
|
|
|
347
376
|
|
|
348
377
|
def inherit_ssl_listener(fd, ctx)
|
|
349
378
|
raise "Puma compiled without SSL support" unless HAS_SSL
|
|
379
|
+
# Puma will try to use local authority context if context is supplied nil
|
|
380
|
+
ctx ||= localhost_authority_context
|
|
350
381
|
|
|
351
382
|
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
|
352
383
|
|
data/lib/puma/cli.rb
CHANGED
|
@@ -112,6 +112,11 @@ module Puma
|
|
|
112
112
|
file_config.load arg
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
+
# Identical to supplying --config "-", but more semantic
|
|
116
|
+
o.on "--no-config", "Prevent Puma from searching for a config file" do |arg|
|
|
117
|
+
file_config.load "-"
|
|
118
|
+
end
|
|
119
|
+
|
|
115
120
|
o.on "--control-url URL", "The bind url to use for the control server. Use 'auto' to use temp unix server" do |arg|
|
|
116
121
|
configure_control_url(arg)
|
|
117
122
|
end
|
data/lib/puma/client.rb
CHANGED
|
@@ -56,6 +56,7 @@ module Puma
|
|
|
56
56
|
@parser = HttpParser.new
|
|
57
57
|
@parsed_bytes = 0
|
|
58
58
|
@read_header = true
|
|
59
|
+
@read_proxy = false
|
|
59
60
|
@ready = false
|
|
60
61
|
|
|
61
62
|
@body = nil
|
|
@@ -69,7 +70,9 @@ module Puma
|
|
|
69
70
|
@hijacked = false
|
|
70
71
|
|
|
71
72
|
@peerip = nil
|
|
73
|
+
@listener = nil
|
|
72
74
|
@remote_addr_header = nil
|
|
75
|
+
@expect_proxy_proto = false
|
|
73
76
|
|
|
74
77
|
@body_remain = 0
|
|
75
78
|
|
|
@@ -81,7 +84,7 @@ module Puma
|
|
|
81
84
|
|
|
82
85
|
attr_writer :peerip
|
|
83
86
|
|
|
84
|
-
attr_accessor :remote_addr_header
|
|
87
|
+
attr_accessor :remote_addr_header, :listener
|
|
85
88
|
|
|
86
89
|
def_delegators :@io, :closed?
|
|
87
90
|
|
|
@@ -105,7 +108,7 @@ module Puma
|
|
|
105
108
|
|
|
106
109
|
# @!attribute [r] in_data_phase
|
|
107
110
|
def in_data_phase
|
|
108
|
-
|
|
111
|
+
!(@read_header || @read_proxy)
|
|
109
112
|
end
|
|
110
113
|
|
|
111
114
|
def set_timeout(val)
|
|
@@ -120,6 +123,7 @@ module Puma
|
|
|
120
123
|
def reset(fast_check=true)
|
|
121
124
|
@parser.reset
|
|
122
125
|
@read_header = true
|
|
126
|
+
@read_proxy = !!@expect_proxy_proto
|
|
123
127
|
@env = @proto_env.dup
|
|
124
128
|
@body = nil
|
|
125
129
|
@tempfile = nil
|
|
@@ -130,6 +134,8 @@ module Puma
|
|
|
130
134
|
@in_last_chunk = false
|
|
131
135
|
|
|
132
136
|
if @buffer
|
|
137
|
+
return false unless try_to_parse_proxy_protocol
|
|
138
|
+
|
|
133
139
|
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
|
134
140
|
|
|
135
141
|
if @parser.finished?
|
|
@@ -142,8 +148,7 @@ module Puma
|
|
|
142
148
|
return false
|
|
143
149
|
else
|
|
144
150
|
begin
|
|
145
|
-
if fast_check &&
|
|
146
|
-
IO.select([@to_io], nil, nil, FAST_TRACK_KA_TIMEOUT)
|
|
151
|
+
if fast_check && @to_io.wait_readable(FAST_TRACK_KA_TIMEOUT)
|
|
147
152
|
return try_to_finish
|
|
148
153
|
end
|
|
149
154
|
rescue IOError
|
|
@@ -157,12 +162,36 @@ module Puma
|
|
|
157
162
|
begin
|
|
158
163
|
@io.close
|
|
159
164
|
rescue IOError
|
|
160
|
-
|
|
165
|
+
Puma::Util.purge_interrupt_queue
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# If necessary, read the PROXY protocol from the buffer. Returns
|
|
170
|
+
# false if more data is needed.
|
|
171
|
+
def try_to_parse_proxy_protocol
|
|
172
|
+
if @read_proxy
|
|
173
|
+
if @expect_proxy_proto == :v1
|
|
174
|
+
if @buffer.include? "\r\n"
|
|
175
|
+
if md = PROXY_PROTOCOL_V1_REGEX.match(@buffer)
|
|
176
|
+
if md[1]
|
|
177
|
+
@peerip = md[1].split(" ")[0]
|
|
178
|
+
end
|
|
179
|
+
@buffer = md.post_match
|
|
180
|
+
end
|
|
181
|
+
# if the buffer has a \r\n but doesn't have a PROXY protocol
|
|
182
|
+
# request, this is just HTTP from a non-PROXY client; move on
|
|
183
|
+
@read_proxy = false
|
|
184
|
+
return @buffer.size > 0
|
|
185
|
+
else
|
|
186
|
+
return false
|
|
187
|
+
end
|
|
188
|
+
end
|
|
161
189
|
end
|
|
190
|
+
true
|
|
162
191
|
end
|
|
163
192
|
|
|
164
193
|
def try_to_finish
|
|
165
|
-
return read_body
|
|
194
|
+
return read_body if in_data_phase
|
|
166
195
|
|
|
167
196
|
begin
|
|
168
197
|
data = @io.read_nonblock(CHUNK_SIZE)
|
|
@@ -187,6 +216,8 @@ module Puma
|
|
|
187
216
|
@buffer = data
|
|
188
217
|
end
|
|
189
218
|
|
|
219
|
+
return false unless try_to_parse_proxy_protocol
|
|
220
|
+
|
|
190
221
|
@parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
|
|
191
222
|
|
|
192
223
|
if @parser.finished?
|
|
@@ -201,13 +232,13 @@ module Puma
|
|
|
201
232
|
|
|
202
233
|
def eagerly_finish
|
|
203
234
|
return true if @ready
|
|
204
|
-
return false unless
|
|
235
|
+
return false unless @to_io.wait_readable(0)
|
|
205
236
|
try_to_finish
|
|
206
237
|
end
|
|
207
238
|
|
|
208
239
|
def finish(timeout)
|
|
209
240
|
return if @ready
|
|
210
|
-
|
|
241
|
+
@to_io.wait_readable(timeout) || timeout! until try_to_finish
|
|
211
242
|
end
|
|
212
243
|
|
|
213
244
|
def timeout!
|
|
@@ -243,6 +274,17 @@ module Puma
|
|
|
243
274
|
@parsed_bytes == 0
|
|
244
275
|
end
|
|
245
276
|
|
|
277
|
+
def expect_proxy_proto=(val)
|
|
278
|
+
if val
|
|
279
|
+
if @read_header
|
|
280
|
+
@read_proxy = true
|
|
281
|
+
end
|
|
282
|
+
else
|
|
283
|
+
@read_proxy = false
|
|
284
|
+
end
|
|
285
|
+
@expect_proxy_proto = val
|
|
286
|
+
end
|
|
287
|
+
|
|
246
288
|
private
|
|
247
289
|
|
|
248
290
|
def setup_body
|
|
@@ -308,7 +350,7 @@ module Puma
|
|
|
308
350
|
|
|
309
351
|
@body_remain = remain
|
|
310
352
|
|
|
311
|
-
|
|
353
|
+
false
|
|
312
354
|
end
|
|
313
355
|
|
|
314
356
|
def read_body
|
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
|
-
|
|
36
|
+
Thread.new do
|
|
37
37
|
Puma.set_thread_name "worker check pipe"
|
|
38
|
-
|
|
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
|
|
@@ -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
|
|
@@ -120,7 +127,7 @@ 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
133
|
sleep Const::WORKER_CHECK_INTERVAL
|
|
@@ -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
|
@@ -164,16 +164,6 @@ module Puma
|
|
|
164
164
|
].compact.min
|
|
165
165
|
end
|
|
166
166
|
|
|
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
167
|
def worker(index, master)
|
|
178
168
|
@workers = []
|
|
179
169
|
|
|
@@ -426,9 +416,7 @@ module Puma
|
|
|
426
416
|
|
|
427
417
|
check_workers
|
|
428
418
|
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
if res
|
|
419
|
+
if read.wait_readable([0, @next_check - Time.now].max)
|
|
432
420
|
req = read.read_nonblock(1)
|
|
433
421
|
|
|
434
422
|
@next_check = Time.now if req == "!"
|
data/lib/puma/configuration.rb
CHANGED
|
@@ -200,7 +200,7 @@ module Puma
|
|
|
200
200
|
:worker_shutdown_timeout => DefaultWorkerShutdownTimeout,
|
|
201
201
|
:remote_address => :socket,
|
|
202
202
|
:tag => method(:infer_tag),
|
|
203
|
-
:environment => -> { ENV['RACK_ENV'] || ENV['RAILS_ENV'] ||
|
|
203
|
+
:environment => -> { ENV['APP_ENV'] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development' },
|
|
204
204
|
:rackup => DefaultRackup,
|
|
205
205
|
:logger => STDOUT,
|
|
206
206
|
:persistent_timeout => Const::PERSISTENT_TIMEOUT,
|
|
@@ -343,6 +343,8 @@ module Puma
|
|
|
343
343
|
raise "Missing rackup file '#{rackup}'" unless File.exist?(rackup)
|
|
344
344
|
|
|
345
345
|
rack_app, rack_options = rack_builder.parse_file(rackup)
|
|
346
|
+
rack_options = rack_options || {}
|
|
347
|
+
|
|
346
348
|
@options.file_options.merge!(rack_options)
|
|
347
349
|
|
|
348
350
|
config_ru_binds = []
|
data/lib/puma/const.rb
CHANGED
|
@@ -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.5.1".freeze
|
|
104
|
+
CODE_NAME = "Zawgyi".freeze
|
|
105
105
|
|
|
106
106
|
PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
|
|
107
107
|
|
|
@@ -247,5 +247,7 @@ module Puma
|
|
|
247
247
|
|
|
248
248
|
# Banned keys of response header
|
|
249
249
|
BANNED_HEADER_KEY = /\A(rack\.|status\z)/.freeze
|
|
250
|
+
|
|
251
|
+
PROXY_PROTOCOL_V1_REGEX = /^PROXY (?:TCP4|TCP6|UNKNOWN) ([^\r]+)\r\n/.freeze
|
|
250
252
|
end
|
|
251
253
|
end
|
data/lib/puma/control_cli.rb
CHANGED
data/lib/puma/dsl.rb
CHANGED
|
@@ -201,7 +201,7 @@ module Puma
|
|
|
201
201
|
# * Set the socket backlog depth with +backlog+, default is 1024.
|
|
202
202
|
# * Set up an SSL certificate with +key+ & +cert+.
|
|
203
203
|
# * Set whether to optimize for low latency instead of throughput with
|
|
204
|
-
# +low_latency+, default is to optimize for low latency. This is done
|
|
204
|
+
# +low_latency+, default is to not optimize for low latency. This is done
|
|
205
205
|
# via +Socket::TCP_NODELAY+.
|
|
206
206
|
# * Set socket permissions with +umask+.
|
|
207
207
|
#
|
|
@@ -381,6 +381,13 @@ module Puma
|
|
|
381
381
|
@options[:rackup] ||= path.to_s
|
|
382
382
|
end
|
|
383
383
|
|
|
384
|
+
# Allows setting `env['rack.url_scheme']`.
|
|
385
|
+
# Only necessary if X-Forwarded-Proto is not being set by your proxy
|
|
386
|
+
# Normal values are 'http' or 'https'.
|
|
387
|
+
def rack_url_scheme(scheme=nil)
|
|
388
|
+
@options[:rack_url_scheme] = scheme
|
|
389
|
+
end
|
|
390
|
+
|
|
384
391
|
def early_hints(answer=true)
|
|
385
392
|
@options[:early_hints] = answer
|
|
386
393
|
end
|
|
@@ -578,7 +585,7 @@ module Puma
|
|
|
578
585
|
# end
|
|
579
586
|
def after_worker_fork(&block)
|
|
580
587
|
@options[:after_worker_fork] ||= []
|
|
581
|
-
@options[:after_worker_fork]
|
|
588
|
+
@options[:after_worker_fork] << block
|
|
582
589
|
end
|
|
583
590
|
|
|
584
591
|
alias_method :after_worker_boot, :after_worker_fork
|
|
@@ -811,7 +818,7 @@ module Puma
|
|
|
811
818
|
# a kernel syscall is required which for very fast rack handlers
|
|
812
819
|
# slows down the handling significantly.
|
|
813
820
|
#
|
|
814
|
-
# There are
|
|
821
|
+
# There are 5 possible values:
|
|
815
822
|
#
|
|
816
823
|
# 1. **:socket** (the default) - read the peername from the socket using the
|
|
817
824
|
# syscall. This is the normal behavior.
|
|
@@ -821,7 +828,10 @@ module Puma
|
|
|
821
828
|
# `set_remote_address header: "X-Real-IP"`.
|
|
822
829
|
# Only the first word (as separated by spaces or comma) is used, allowing
|
|
823
830
|
# headers such as X-Forwarded-For to be used as well.
|
|
824
|
-
# 4.
|
|
831
|
+
# 4. **proxy_protocol: :v1**- set the remote address to the value read from the
|
|
832
|
+
# HAproxy PROXY protocol, version 1. If the request does not have the PROXY
|
|
833
|
+
# protocol attached to it, will fall back to :socket
|
|
834
|
+
# 5. **\<Any string\>** - this allows you to hardcode remote address to any value
|
|
825
835
|
# you wish. Because Puma never uses this field anyway, it's format is
|
|
826
836
|
# entirely in your hands.
|
|
827
837
|
#
|
|
@@ -839,6 +849,13 @@ module Puma
|
|
|
839
849
|
if hdr = val[:header]
|
|
840
850
|
@options[:remote_address] = :header
|
|
841
851
|
@options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_")
|
|
852
|
+
elsif protocol_version = val[:proxy_protocol]
|
|
853
|
+
@options[:remote_address] = :proxy_protocol
|
|
854
|
+
protocol_version = protocol_version.downcase.to_sym
|
|
855
|
+
unless [:v1].include?(protocol_version)
|
|
856
|
+
raise "Invalid value for proxy_protocol - #{protocol_version.inspect}"
|
|
857
|
+
end
|
|
858
|
+
@options[:remote_address_proxy_protocol] = protocol_version
|
|
842
859
|
else
|
|
843
860
|
raise "Invalid value for set_remote_address - #{val.inspect}"
|
|
844
861
|
end
|
|
@@ -17,7 +17,7 @@ module Puma
|
|
|
17
17
|
# be particularly full-featured or fast. It just has to handle the few places
|
|
18
18
|
# where Puma relies on JSON serialization internally.
|
|
19
19
|
|
|
20
|
-
module
|
|
20
|
+
module JSONSerialization
|
|
21
21
|
QUOTE = /"/
|
|
22
22
|
BACKSLASH = /\\/
|
|
23
23
|
CONTROL_CHAR_TO_ESCAPE = /[\x00-\x1F]/ # As required by ECMA-404
|
data/lib/puma/launcher.rb
CHANGED
|
@@ -319,10 +319,12 @@ module Puma
|
|
|
319
319
|
log '* Pruning Bundler environment'
|
|
320
320
|
home = ENV['GEM_HOME']
|
|
321
321
|
bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
|
|
322
|
+
bundle_app_config = Bundler.original_env['BUNDLE_APP_CONFIG']
|
|
322
323
|
with_unbundled_env do
|
|
323
324
|
ENV['GEM_HOME'] = home
|
|
324
325
|
ENV['BUNDLE_GEMFILE'] = bundle_gemfile
|
|
325
326
|
ENV['PUMA_BUNDLER_PRUNED'] = '1'
|
|
327
|
+
ENV["BUNDLE_APP_CONFIG"] = bundle_app_config
|
|
326
328
|
args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':')] + @original_argv
|
|
327
329
|
# Ruby 2.0+ defaults to true which breaks socket activation
|
|
328
330
|
args += [{:close_others => false}]
|
data/lib/puma/minissl.rb
CHANGED
|
@@ -161,30 +161,15 @@ module Puma
|
|
|
161
161
|
@socket.flush
|
|
162
162
|
end
|
|
163
163
|
|
|
164
|
-
def read_and_drop(timeout = 1)
|
|
165
|
-
return :timeout unless IO.select([@socket], nil, nil, timeout)
|
|
166
|
-
case @socket.read_nonblock(1024, exception: false)
|
|
167
|
-
when nil
|
|
168
|
-
:eof
|
|
169
|
-
when :wait_readable
|
|
170
|
-
:eagain
|
|
171
|
-
else
|
|
172
|
-
:drop
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def should_drop_bytes?
|
|
177
|
-
@engine.init? || !@engine.shutdown
|
|
178
|
-
end
|
|
179
|
-
|
|
180
164
|
def close
|
|
181
165
|
begin
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
166
|
+
unless @engine.shutdown
|
|
167
|
+
while alert_data = @engine.extract
|
|
168
|
+
@socket.write alert_data
|
|
169
|
+
end
|
|
170
|
+
end
|
|
186
171
|
rescue IOError, SystemCallError
|
|
187
|
-
|
|
172
|
+
Puma::Util.purge_interrupt_queue
|
|
188
173
|
# nothing
|
|
189
174
|
ensure
|
|
190
175
|
@socket.close
|
data/lib/puma/plugin.rb
CHANGED
data/lib/puma/puma_http11.jar
CHANGED
|
Binary file
|
data/lib/puma/rack/builder.rb
CHANGED
data/lib/puma/request.rb
CHANGED
|
@@ -26,9 +26,10 @@ module Puma
|
|
|
26
26
|
# Finally, it'll return +true+ on keep-alive connections.
|
|
27
27
|
# @param client [Puma::Client]
|
|
28
28
|
# @param lines [Puma::IOBuffer]
|
|
29
|
+
# @param requests [Integer]
|
|
29
30
|
# @return [Boolean,:async]
|
|
30
31
|
#
|
|
31
|
-
def handle_request(client, lines)
|
|
32
|
+
def handle_request(client, lines, requests)
|
|
32
33
|
env = client.env
|
|
33
34
|
io = client.io # io may be a MiniSSL::Socket
|
|
34
35
|
|
|
@@ -50,7 +51,7 @@ module Puma
|
|
|
50
51
|
head = env[REQUEST_METHOD] == HEAD
|
|
51
52
|
|
|
52
53
|
env[RACK_INPUT] = body
|
|
53
|
-
env[RACK_URL_SCHEME]
|
|
54
|
+
env[RACK_URL_SCHEME] ||= default_server_port(env) == PORT_443 ? HTTPS : HTTP
|
|
54
55
|
|
|
55
56
|
if @early_hints
|
|
56
57
|
env[EARLY_HINTS] = lambda { |headers|
|
|
@@ -110,7 +111,7 @@ module Puma
|
|
|
110
111
|
|
|
111
112
|
cork_socket io
|
|
112
113
|
|
|
113
|
-
str_headers(env, status, headers, res_info, lines)
|
|
114
|
+
str_headers(env, status, headers, res_info, lines, requests, client)
|
|
114
115
|
|
|
115
116
|
line_ending = LINE_END
|
|
116
117
|
|
|
@@ -175,7 +176,7 @@ module Puma
|
|
|
175
176
|
after_reply.each { |o| o.call }
|
|
176
177
|
end
|
|
177
178
|
|
|
178
|
-
|
|
179
|
+
res_info[:keep_alive]
|
|
179
180
|
end
|
|
180
181
|
|
|
181
182
|
# @param env [Hash] see Puma::Client#env, from request
|
|
@@ -200,7 +201,7 @@ module Puma
|
|
|
200
201
|
begin
|
|
201
202
|
n = io.syswrite str
|
|
202
203
|
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
|
203
|
-
|
|
204
|
+
unless io.wait_writable WRITE_TIMEOUT
|
|
204
205
|
raise ConnectionError, "Socket timeout writing data"
|
|
205
206
|
end
|
|
206
207
|
|
|
@@ -367,9 +368,11 @@ module Puma
|
|
|
367
368
|
# @param headers [Hash] the headers returned by the Rack application
|
|
368
369
|
# @param res_info [Hash] used to pass info between this method and #handle_request
|
|
369
370
|
# @param lines [Puma::IOBuffer] modified inn place
|
|
371
|
+
# @param requests [Integer] number of inline requests handled
|
|
372
|
+
# @param client [Puma::Client]
|
|
370
373
|
# @version 5.0.3
|
|
371
374
|
#
|
|
372
|
-
def str_headers(env, status, headers, res_info, lines)
|
|
375
|
+
def str_headers(env, status, headers, res_info, lines, requests, client)
|
|
373
376
|
line_ending = LINE_END
|
|
374
377
|
colon = COLON
|
|
375
378
|
|
|
@@ -410,6 +413,14 @@ module Puma
|
|
|
410
413
|
# if running without request queueing
|
|
411
414
|
res_info[:keep_alive] &&= @queue_requests
|
|
412
415
|
|
|
416
|
+
# Close the connection after a reasonable number of inline requests
|
|
417
|
+
# if the server is at capacity and the listener has a new connection ready.
|
|
418
|
+
# This allows Puma to service connections fairly when the number
|
|
419
|
+
# of concurrent connections exceeds the size of the threadpool.
|
|
420
|
+
res_info[:keep_alive] &&= requests < @max_fast_inline ||
|
|
421
|
+
@thread_pool.busy_threads < @max_threads ||
|
|
422
|
+
!client.listener.to_io.wait_readable(0)
|
|
423
|
+
|
|
413
424
|
res_info[:response_hijack] = nil
|
|
414
425
|
|
|
415
426
|
headers.each do |k, vs|
|
data/lib/puma/runner.rb
CHANGED
|
@@ -15,6 +15,16 @@ module Puma
|
|
|
15
15
|
@app = nil
|
|
16
16
|
@control = nil
|
|
17
17
|
@started_at = Time.now
|
|
18
|
+
@wakeup = nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def wakeup!
|
|
22
|
+
return unless @wakeup
|
|
23
|
+
|
|
24
|
+
@wakeup.write "!" unless @wakeup.closed?
|
|
25
|
+
|
|
26
|
+
rescue SystemCallError, IOError
|
|
27
|
+
Puma::Util.purge_interrupt_queue
|
|
18
28
|
end
|
|
19
29
|
|
|
20
30
|
def development?
|
|
@@ -108,9 +118,7 @@ module Puma
|
|
|
108
118
|
append = @options[:redirect_append]
|
|
109
119
|
|
|
110
120
|
if stdout
|
|
111
|
-
|
|
112
|
-
raise "Cannot redirect STDOUT to #{stdout}"
|
|
113
|
-
end
|
|
121
|
+
ensure_output_directory_exists(stdout, 'STDOUT')
|
|
114
122
|
|
|
115
123
|
STDOUT.reopen stdout, (append ? "a" : "w")
|
|
116
124
|
STDOUT.puts "=== puma startup: #{Time.now} ==="
|
|
@@ -118,9 +126,7 @@ module Puma
|
|
|
118
126
|
end
|
|
119
127
|
|
|
120
128
|
if stderr
|
|
121
|
-
|
|
122
|
-
raise "Cannot redirect STDERR to #{stderr}"
|
|
123
|
-
end
|
|
129
|
+
ensure_output_directory_exists(stderr, 'STDERR')
|
|
124
130
|
|
|
125
131
|
STDERR.reopen stderr, (append ? "a" : "w")
|
|
126
132
|
STDERR.puts "=== puma startup: #{Time.now} ==="
|
|
@@ -159,5 +165,12 @@ module Puma
|
|
|
159
165
|
server.inherit_binder @launcher.binder
|
|
160
166
|
server
|
|
161
167
|
end
|
|
168
|
+
|
|
169
|
+
private
|
|
170
|
+
def ensure_output_directory_exists(path, io_name)
|
|
171
|
+
unless Dir.exist?(File.dirname(path))
|
|
172
|
+
raise "Cannot redirect #{io_name} to #{path}"
|
|
173
|
+
end
|
|
174
|
+
end
|
|
162
175
|
end
|
|
163
176
|
end
|