puma 4.3.7-java → 5.0.0.beta1-java
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 +59 -17
- data/LICENSE +23 -20
- data/README.md +17 -11
- data/docs/deployment.md +3 -1
- data/docs/fork_worker.md +31 -0
- data/docs/jungle/README.md +13 -0
- data/{tools → docs}/jungle/rc.d/README.md +0 -0
- data/{tools → docs}/jungle/rc.d/puma +0 -0
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/{tools → docs}/jungle/upstart/README.md +0 -0
- data/{tools → docs}/jungle/upstart/puma-manager.conf +0 -0
- data/{tools → docs}/jungle/upstart/puma.conf +0 -0
- data/docs/signals.md +1 -0
- data/docs/systemd.md +1 -63
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/extconf.rb +3 -2
- data/ext/puma_http11/http11_parser.c +1 -3
- data/ext/puma_http11/http11_parser.rl +1 -3
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/puma_http11.c +2 -38
- data/lib/puma.rb +4 -0
- data/lib/puma/app/status.rb +16 -5
- data/lib/puma/binder.rb +62 -60
- data/lib/puma/cli.rb +7 -15
- data/lib/puma/client.rb +35 -32
- data/lib/puma/cluster.rb +179 -74
- data/lib/puma/configuration.rb +30 -42
- data/lib/puma/const.rb +2 -3
- data/lib/puma/control_cli.rb +27 -17
- data/lib/puma/detect.rb +8 -0
- data/lib/puma/dsl.rb +70 -34
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/launcher.rb +41 -29
- data/lib/puma/minissl.rb +13 -8
- data/lib/puma/null_io.rb +1 -1
- data/lib/puma/plugin.rb +1 -10
- data/lib/puma/puma_http11.jar +0 -0
- data/lib/puma/rack/builder.rb +0 -4
- data/lib/puma/reactor.rb +6 -1
- data/lib/puma/runner.rb +5 -34
- data/lib/puma/server.rb +62 -177
- data/lib/puma/single.rb +7 -64
- data/lib/puma/state_file.rb +5 -2
- data/lib/puma/thread_pool.rb +85 -47
- data/lib/rack/handler/puma.rb +1 -3
- data/tools/{docker/Dockerfile → Dockerfile} +0 -0
- metadata +16 -20
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- 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/lib/puma/launcher.rb
CHANGED
@@ -48,7 +48,8 @@ module Puma
|
|
48
48
|
@config = conf
|
49
49
|
|
50
50
|
@binder = Binder.new(@events)
|
51
|
-
@binder.
|
51
|
+
@binder.create_inherited_fds(ENV).each { |k| ENV.delete k }
|
52
|
+
@binder.create_activated_fds(ENV).each { |k| ENV.delete k }
|
52
53
|
|
53
54
|
@environment = conf.environment
|
54
55
|
|
@@ -69,10 +70,6 @@ module Puma
|
|
69
70
|
unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
|
70
71
|
end
|
71
72
|
|
72
|
-
if @options[:daemon] && Puma.windows?
|
73
|
-
unsupported 'daemon mode not supported on Windows'
|
74
|
-
end
|
75
|
-
|
76
73
|
Dir.chdir(@restart_dir)
|
77
74
|
|
78
75
|
prune_bundler if prune_bundler?
|
@@ -105,6 +102,7 @@ module Puma
|
|
105
102
|
write_pid
|
106
103
|
|
107
104
|
path = @options[:state]
|
105
|
+
permission = @options[:state_permission]
|
108
106
|
return unless path
|
109
107
|
|
110
108
|
require 'puma/state_file'
|
@@ -114,7 +112,7 @@ module Puma
|
|
114
112
|
sf.control_url = @options[:control_url]
|
115
113
|
sf.control_auth_token = @options[:control_auth_token]
|
116
114
|
|
117
|
-
sf.save path
|
115
|
+
sf.save path, permission
|
118
116
|
end
|
119
117
|
|
120
118
|
# Delete the configured pidfile
|
@@ -184,12 +182,12 @@ module Puma
|
|
184
182
|
when :exit
|
185
183
|
# nothing
|
186
184
|
end
|
187
|
-
@
|
185
|
+
close_binder_listeners unless @status == :restart
|
188
186
|
end
|
189
187
|
|
190
|
-
# Return
|
191
|
-
def
|
192
|
-
@binder.
|
188
|
+
# Return all tcp ports the launcher may be using, TCP or SSL
|
189
|
+
def connected_ports
|
190
|
+
@binder.connected_ports
|
193
191
|
end
|
194
192
|
|
195
193
|
def restart_args
|
@@ -202,9 +200,21 @@ module Puma
|
|
202
200
|
end
|
203
201
|
|
204
202
|
def close_binder_listeners
|
203
|
+
@runner.close_control_listeners
|
205
204
|
@binder.close_listeners
|
206
205
|
end
|
207
206
|
|
207
|
+
def thread_status
|
208
|
+
Thread.list.each do |thread|
|
209
|
+
name = "Thread: TID-#{thread.object_id.to_s(36)}"
|
210
|
+
name += " #{thread['label']}" if thread['label']
|
211
|
+
name += " #{thread.name}" if thread.respond_to?(:name) && thread.name
|
212
|
+
backtrace = thread.backtrace || ["<no backtrace available>"]
|
213
|
+
|
214
|
+
yield name, backtrace
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
208
218
|
private
|
209
219
|
|
210
220
|
# If configured, write the pid of the current process out
|
@@ -225,7 +235,7 @@ module Puma
|
|
225
235
|
end
|
226
236
|
|
227
237
|
def restart!
|
228
|
-
@config.run_hooks :on_restart, self
|
238
|
+
@config.run_hooks :on_restart, self, @events
|
229
239
|
|
230
240
|
if Puma.jruby?
|
231
241
|
close_binder_listeners
|
@@ -241,6 +251,7 @@ module Puma
|
|
241
251
|
else
|
242
252
|
argv = restart_args
|
243
253
|
Dir.chdir(@restart_dir)
|
254
|
+
ENV.update(@binder.redirects_for_restart_env)
|
244
255
|
argv += [@binder.redirects_for_restart]
|
245
256
|
Kernel.exec(*argv)
|
246
257
|
end
|
@@ -286,8 +297,10 @@ module Puma
|
|
286
297
|
|
287
298
|
log '* Pruning Bundler environment'
|
288
299
|
home = ENV['GEM_HOME']
|
289
|
-
Bundler.
|
300
|
+
bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
|
301
|
+
with_unbundled_env do
|
290
302
|
ENV['GEM_HOME'] = home
|
303
|
+
ENV['BUNDLE_GEMFILE'] = bundle_gemfile
|
291
304
|
ENV['PUMA_BUNDLER_PRUNED'] = '1'
|
292
305
|
args = [Gem.ruby, puma_wild_location, '-I', dirs.join(':'), deps.join(',')] + @original_argv
|
293
306
|
# Ruby 2.0+ defaults to true which breaks socket activation
|
@@ -323,21 +336,6 @@ module Puma
|
|
323
336
|
log "- Goodbye!"
|
324
337
|
end
|
325
338
|
|
326
|
-
def log_thread_status
|
327
|
-
Thread.list.each do |thread|
|
328
|
-
log "Thread TID-#{thread.object_id.to_s(36)} #{thread['label']}"
|
329
|
-
logstr = "Thread: TID-#{thread.object_id.to_s(36)}"
|
330
|
-
logstr += " #{thread.name}" if thread.respond_to?(:name)
|
331
|
-
log logstr
|
332
|
-
|
333
|
-
if thread.backtrace
|
334
|
-
log thread.backtrace.join("\n")
|
335
|
-
else
|
336
|
-
log "<no backtrace available>"
|
337
|
-
end
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
339
|
def set_process_title
|
342
340
|
Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
|
343
341
|
end
|
@@ -456,8 +454,13 @@ module Puma
|
|
456
454
|
end
|
457
455
|
|
458
456
|
begin
|
459
|
-
|
460
|
-
|
457
|
+
unless Puma.jruby? # INFO in use by JVM already
|
458
|
+
Signal.trap "SIGINFO" do
|
459
|
+
thread_status do |name, backtrace|
|
460
|
+
@events.log name
|
461
|
+
@events.log backtrace.map { |bt| " #{bt}" }
|
462
|
+
end
|
463
|
+
end
|
461
464
|
end
|
462
465
|
rescue Exception
|
463
466
|
# Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying
|
@@ -471,5 +474,14 @@ module Puma
|
|
471
474
|
raise "#{feature} is not supported on your version of RubyGems. " \
|
472
475
|
"You must have RubyGems #{min_version}+ to use this feature."
|
473
476
|
end
|
477
|
+
|
478
|
+
def with_unbundled_env
|
479
|
+
bundler_ver = Gem::Version.new(Bundler::VERSION)
|
480
|
+
if bundler_ver < Gem::Version.new('2.1.0')
|
481
|
+
Bundler.with_clean_env { yield }
|
482
|
+
else
|
483
|
+
Bundler.with_unbundled_env { yield }
|
484
|
+
end
|
485
|
+
end
|
474
486
|
end
|
475
487
|
end
|
data/lib/puma/minissl.rb
CHANGED
@@ -125,11 +125,14 @@ module Puma
|
|
125
125
|
|
126
126
|
def read_and_drop(timeout = 1)
|
127
127
|
return :timeout unless IO.select([@socket], nil, nil, timeout)
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
128
|
+
case @socket.read_nonblock(1024, exception: false)
|
129
|
+
when nil
|
130
|
+
:eof
|
131
|
+
when :wait_readable
|
132
|
+
:eagain
|
133
|
+
else
|
134
|
+
:drop
|
135
|
+
end
|
133
136
|
end
|
134
137
|
|
135
138
|
def should_drop_bytes?
|
@@ -141,9 +144,7 @@ module Puma
|
|
141
144
|
# Read any drop any partially initialized sockets and any received bytes during shutdown.
|
142
145
|
# Don't let this socket hold this loop forever.
|
143
146
|
# If it can't send more packets within 1s, then give up.
|
144
|
-
while should_drop_bytes?
|
145
|
-
return if [:timeout, :eof].include?(read_and_drop(1))
|
146
|
-
end
|
147
|
+
return if [:timeout, :eof].include?(read_and_drop(1)) while should_drop_bytes?
|
147
148
|
rescue IOError, SystemCallError
|
148
149
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
149
150
|
# nothing
|
@@ -270,6 +271,10 @@ module Puma
|
|
270
271
|
Socket.new io, engine
|
271
272
|
end
|
272
273
|
|
274
|
+
def addr
|
275
|
+
@socket.addr
|
276
|
+
end
|
277
|
+
|
273
278
|
def close
|
274
279
|
@socket.close unless @socket.closed? # closed? call is for Windows
|
275
280
|
end
|
data/lib/puma/null_io.rb
CHANGED
data/lib/puma/plugin.rb
CHANGED
@@ -10,7 +10,7 @@ module Puma
|
|
10
10
|
|
11
11
|
def create(name)
|
12
12
|
if cls = Plugins.find(name)
|
13
|
-
plugin = cls.new
|
13
|
+
plugin = cls.new
|
14
14
|
@instances << plugin
|
15
15
|
return plugin
|
16
16
|
end
|
@@ -104,17 +104,8 @@ module Puma
|
|
104
104
|
Plugins.register name, cls
|
105
105
|
end
|
106
106
|
|
107
|
-
def initialize(loader)
|
108
|
-
@loader = loader
|
109
|
-
end
|
110
|
-
|
111
107
|
def in_background(&blk)
|
112
108
|
Plugins.add_background blk
|
113
109
|
end
|
114
|
-
|
115
|
-
def workers_supported?
|
116
|
-
return false if Puma.jruby? || Puma.windows?
|
117
|
-
true
|
118
|
-
end
|
119
110
|
end
|
120
111
|
end
|
data/lib/puma/puma_http11.jar
CHANGED
Binary file
|
data/lib/puma/rack/builder.rb
CHANGED
@@ -67,10 +67,6 @@ module Puma::Rack
|
|
67
67
|
options[:environment] = e
|
68
68
|
}
|
69
69
|
|
70
|
-
opts.on("-D", "--daemonize", "run daemonized in the background") { |d|
|
71
|
-
options[:daemonize] = d ? true : false
|
72
|
-
}
|
73
|
-
|
74
70
|
opts.on("-P", "--pid FILE", "file to store PID") { |f|
|
75
71
|
options[:pid] = ::File.expand_path(f)
|
76
72
|
}
|
data/lib/puma/reactor.rb
CHANGED
@@ -189,7 +189,12 @@ module Puma
|
|
189
189
|
if submon.value == @ready
|
190
190
|
false
|
191
191
|
else
|
192
|
-
submon.value.
|
192
|
+
if submon.value.can_close?
|
193
|
+
submon.value.close
|
194
|
+
else
|
195
|
+
# Pass remaining open client connections to the thread pool.
|
196
|
+
@app_pool << submon.value
|
197
|
+
end
|
193
198
|
begin
|
194
199
|
selector.deregister submon.value
|
195
200
|
rescue IOError
|
data/lib/puma/runner.rb
CHANGED
@@ -18,10 +18,6 @@ module Puma
|
|
18
18
|
@started_at = Time.now
|
19
19
|
end
|
20
20
|
|
21
|
-
def daemon?
|
22
|
-
@options[:daemon]
|
23
|
-
end
|
24
|
-
|
25
21
|
def development?
|
26
22
|
@options[:environment] == "development"
|
27
23
|
end
|
@@ -52,8 +48,6 @@ module Puma
|
|
52
48
|
|
53
49
|
require 'puma/app/status'
|
54
50
|
|
55
|
-
uri = URI.parse str
|
56
|
-
|
57
51
|
if token = @options[:control_auth_token]
|
58
52
|
token = nil if token.empty? || token == 'none'
|
59
53
|
end
|
@@ -64,30 +58,16 @@ module Puma
|
|
64
58
|
control.min_threads = 0
|
65
59
|
control.max_threads = 1
|
66
60
|
|
67
|
-
|
68
|
-
when "ssl"
|
69
|
-
log "* Starting control server on #{str}"
|
70
|
-
params = Util.parse_query uri.query
|
71
|
-
ctx = MiniSSL::ContextBuilder.new(params, @events).context
|
72
|
-
|
73
|
-
control.add_ssl_listener uri.host, uri.port, ctx
|
74
|
-
when "tcp"
|
75
|
-
log "* Starting control server on #{str}"
|
76
|
-
control.add_tcp_listener uri.host, uri.port
|
77
|
-
when "unix"
|
78
|
-
log "* Starting control server on #{str}"
|
79
|
-
path = "#{uri.host}#{uri.path}"
|
80
|
-
mask = @options[:control_url_umask]
|
81
|
-
|
82
|
-
control.add_unix_listener path, mask
|
83
|
-
else
|
84
|
-
error "Invalid control URI: #{str}"
|
85
|
-
end
|
61
|
+
control.binder.parse [str], self, 'Starting control server'
|
86
62
|
|
87
63
|
control.run
|
88
64
|
@control = control
|
89
65
|
end
|
90
66
|
|
67
|
+
def close_control_listeners
|
68
|
+
@control.binder.close_listeners if @control
|
69
|
+
end
|
70
|
+
|
91
71
|
def ruby_engine
|
92
72
|
if !defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby"
|
93
73
|
"ruby #{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
|
@@ -108,10 +88,6 @@ module Puma
|
|
108
88
|
log "* Version #{Puma::Const::PUMA_VERSION} (#{ruby_engine}), codename: #{Puma::Const::CODE_NAME}"
|
109
89
|
log "* Min threads: #{min_t}, max threads: #{max_t}"
|
110
90
|
log "* Environment: #{ENV['RACK_ENV']}"
|
111
|
-
|
112
|
-
if @options[:mode] == :tcp
|
113
|
-
log "* Mode: Lopez Express (tcp)"
|
114
|
-
end
|
115
91
|
end
|
116
92
|
|
117
93
|
def redirected_io?
|
@@ -150,7 +126,6 @@ module Puma
|
|
150
126
|
exit 1
|
151
127
|
end
|
152
128
|
|
153
|
-
# Load the app before we daemonize.
|
154
129
|
begin
|
155
130
|
@app = @launcher.config.app
|
156
131
|
rescue Exception => e
|
@@ -174,10 +149,6 @@ module Puma
|
|
174
149
|
server.max_threads = max_t
|
175
150
|
server.inherit_binder @launcher.binder
|
176
151
|
|
177
|
-
if @options[:mode] == :tcp
|
178
|
-
server.tcp_mode!
|
179
|
-
end
|
180
|
-
|
181
152
|
if @options[:early_hints]
|
182
153
|
server.early_hints = true
|
183
154
|
end
|
data/lib/puma/server.rb
CHANGED
@@ -11,6 +11,7 @@ require 'puma/client'
|
|
11
11
|
require 'puma/binder'
|
12
12
|
require 'puma/accept_nonblock'
|
13
13
|
require 'puma/util'
|
14
|
+
require 'puma/io_buffer'
|
14
15
|
|
15
16
|
require 'puma/puma_http11'
|
16
17
|
|
@@ -36,6 +37,7 @@ module Puma
|
|
36
37
|
|
37
38
|
attr_reader :thread
|
38
39
|
attr_reader :events
|
40
|
+
attr_reader :requests_count
|
39
41
|
attr_accessor :app
|
40
42
|
|
41
43
|
attr_accessor :min_threads
|
@@ -57,8 +59,7 @@ module Puma
|
|
57
59
|
@app = app
|
58
60
|
@events = events
|
59
61
|
|
60
|
-
@check, @notify =
|
61
|
-
|
62
|
+
@check, @notify = nil
|
62
63
|
@status = :stop
|
63
64
|
|
64
65
|
@min_threads = 0
|
@@ -85,20 +86,18 @@ module Puma
|
|
85
86
|
@mode = :http
|
86
87
|
|
87
88
|
@precheck_closing = true
|
89
|
+
|
90
|
+
@requests_count = 0
|
88
91
|
end
|
89
92
|
|
90
93
|
attr_accessor :binder, :leak_stack_on_error, :early_hints
|
91
94
|
|
92
|
-
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener, :add_unix_listener, :
|
95
|
+
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener, :add_unix_listener, :connected_ports
|
93
96
|
|
94
97
|
def inherit_binder(bind)
|
95
98
|
@binder = bind
|
96
99
|
end
|
97
100
|
|
98
|
-
def tcp_mode!
|
99
|
-
@mode = :tcp
|
100
|
-
end
|
101
|
-
|
102
101
|
# On Linux, use TCP_CORK to better control how the TCP stack
|
103
102
|
# packetizes our stream. This improves both latency and throughput.
|
104
103
|
#
|
@@ -172,107 +171,6 @@ module Puma
|
|
172
171
|
@thread_pool and @thread_pool.pool_capacity
|
173
172
|
end
|
174
173
|
|
175
|
-
# Lopez Mode == raw tcp apps
|
176
|
-
|
177
|
-
def run_lopez_mode(background=true)
|
178
|
-
@thread_pool = ThreadPool.new(@min_threads,
|
179
|
-
@max_threads,
|
180
|
-
Hash) do |client, tl|
|
181
|
-
|
182
|
-
io = client.to_io
|
183
|
-
addr = io.peeraddr.last
|
184
|
-
|
185
|
-
if addr.empty?
|
186
|
-
# Set unix socket addrs to localhost
|
187
|
-
addr = "127.0.0.1:0"
|
188
|
-
else
|
189
|
-
addr = "#{addr}:#{io.peeraddr[1]}"
|
190
|
-
end
|
191
|
-
|
192
|
-
env = { 'thread' => tl, REMOTE_ADDR => addr }
|
193
|
-
|
194
|
-
begin
|
195
|
-
@app.call env, client.to_io
|
196
|
-
rescue Object => e
|
197
|
-
STDERR.puts "! Detected exception at toplevel: #{e.message} (#{e.class})"
|
198
|
-
STDERR.puts e.backtrace
|
199
|
-
end
|
200
|
-
|
201
|
-
client.close unless env['detach']
|
202
|
-
end
|
203
|
-
|
204
|
-
@events.fire :state, :running
|
205
|
-
|
206
|
-
if background
|
207
|
-
@thread = Thread.new do
|
208
|
-
Puma.set_thread_name "server"
|
209
|
-
handle_servers_lopez_mode
|
210
|
-
end
|
211
|
-
return @thread
|
212
|
-
else
|
213
|
-
handle_servers_lopez_mode
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
def handle_servers_lopez_mode
|
218
|
-
begin
|
219
|
-
check = @check
|
220
|
-
sockets = [check] + @binder.ios
|
221
|
-
pool = @thread_pool
|
222
|
-
|
223
|
-
while @status == :run
|
224
|
-
begin
|
225
|
-
ios = IO.select sockets
|
226
|
-
ios.first.each do |sock|
|
227
|
-
if sock == check
|
228
|
-
break if handle_check
|
229
|
-
else
|
230
|
-
begin
|
231
|
-
if io = sock.accept_nonblock
|
232
|
-
client = Client.new io, nil
|
233
|
-
pool << client
|
234
|
-
end
|
235
|
-
rescue SystemCallError
|
236
|
-
# nothing
|
237
|
-
rescue Errno::ECONNABORTED
|
238
|
-
# client closed the socket even before accept
|
239
|
-
begin
|
240
|
-
io.close
|
241
|
-
rescue
|
242
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
243
|
-
end
|
244
|
-
end
|
245
|
-
end
|
246
|
-
end
|
247
|
-
rescue Object => e
|
248
|
-
@events.unknown_error self, e, "Listen loop"
|
249
|
-
end
|
250
|
-
end
|
251
|
-
|
252
|
-
@events.fire :state, @status
|
253
|
-
|
254
|
-
graceful_shutdown if @status == :stop || @status == :restart
|
255
|
-
|
256
|
-
rescue Exception => e
|
257
|
-
STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
|
258
|
-
STDERR.puts e.backtrace
|
259
|
-
ensure
|
260
|
-
begin
|
261
|
-
@check.close
|
262
|
-
rescue
|
263
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
264
|
-
end
|
265
|
-
|
266
|
-
# Prevent can't modify frozen IOError (RuntimeError)
|
267
|
-
begin
|
268
|
-
@notify.close
|
269
|
-
rescue IOError
|
270
|
-
# no biggy
|
271
|
-
end
|
272
|
-
end
|
273
|
-
|
274
|
-
@events.fire :state, :done
|
275
|
-
end
|
276
174
|
# Runs the server.
|
277
175
|
#
|
278
176
|
# If +background+ is true (the default) then a thread is spun
|
@@ -286,15 +184,9 @@ module Puma
|
|
286
184
|
|
287
185
|
@status = :run
|
288
186
|
|
289
|
-
if @mode == :tcp
|
290
|
-
return run_lopez_mode(background)
|
291
|
-
end
|
292
|
-
|
293
|
-
queue_requests = @queue_requests
|
294
|
-
|
295
187
|
@thread_pool = ThreadPool.new(@min_threads,
|
296
188
|
@max_threads,
|
297
|
-
IOBuffer) do |client, buffer|
|
189
|
+
::Puma::IOBuffer) do |client, buffer|
|
298
190
|
|
299
191
|
# Advertise this server into the thread
|
300
192
|
Thread.current[ThreadLocalKey] = self
|
@@ -302,10 +194,10 @@ module Puma
|
|
302
194
|
process_now = false
|
303
195
|
|
304
196
|
begin
|
305
|
-
if queue_requests
|
197
|
+
if @queue_requests
|
306
198
|
process_now = client.eagerly_finish
|
307
199
|
else
|
308
|
-
client.finish
|
200
|
+
client.finish(@first_data_timeout)
|
309
201
|
process_now = true
|
310
202
|
end
|
311
203
|
rescue MiniSSL::SSLError => e
|
@@ -331,11 +223,14 @@ module Puma
|
|
331
223
|
@reactor.add client
|
332
224
|
end
|
333
225
|
end
|
226
|
+
|
227
|
+
process_now
|
334
228
|
end
|
335
229
|
|
230
|
+
@thread_pool.out_of_band_hook = @options[:out_of_band]
|
336
231
|
@thread_pool.clean_thread_locals = @options[:clean_thread_locals]
|
337
232
|
|
338
|
-
if queue_requests
|
233
|
+
if @queue_requests
|
339
234
|
@reactor = Reactor.new self, @thread_pool
|
340
235
|
@reactor.run_in_thread
|
341
236
|
end
|
@@ -362,6 +257,7 @@ module Puma
|
|
362
257
|
end
|
363
258
|
|
364
259
|
def handle_servers
|
260
|
+
@check, @notify = Puma::Util.pipe unless @notify
|
365
261
|
begin
|
366
262
|
check = @check
|
367
263
|
sockets = [check] + @binder.ios
|
@@ -386,6 +282,10 @@ module Puma
|
|
386
282
|
break if handle_check
|
387
283
|
else
|
388
284
|
begin
|
285
|
+
pool.wait_until_not_full
|
286
|
+
pool.wait_for_less_busy_worker(
|
287
|
+
@options[:wait_for_less_busy_worker].to_f)
|
288
|
+
|
389
289
|
if io = sock.accept_nonblock
|
390
290
|
client = Client.new io, @binder.env(sock)
|
391
291
|
if remote_addr_value
|
@@ -395,10 +295,6 @@ module Puma
|
|
395
295
|
end
|
396
296
|
|
397
297
|
pool << client
|
398
|
-
busy_threads = pool.wait_until_not_full
|
399
|
-
if busy_threads == 0
|
400
|
-
@options[:out_of_band].each(&:call) if @options[:out_of_band]
|
401
|
-
end
|
402
298
|
end
|
403
299
|
rescue SystemCallError
|
404
300
|
# nothing
|
@@ -419,17 +315,20 @@ module Puma
|
|
419
315
|
|
420
316
|
@events.fire :state, @status
|
421
317
|
|
422
|
-
graceful_shutdown if @status == :stop || @status == :restart
|
423
318
|
if queue_requests
|
319
|
+
@queue_requests = false
|
424
320
|
@reactor.clear!
|
425
321
|
@reactor.shutdown
|
426
322
|
end
|
323
|
+
graceful_shutdown if @status == :stop || @status == :restart
|
427
324
|
rescue Exception => e
|
428
325
|
STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
|
429
326
|
STDERR.puts e.backtrace
|
430
327
|
ensure
|
431
|
-
@check.close
|
328
|
+
@check.close unless @check.closed? # Ruby 2.2 issue
|
432
329
|
@notify.close
|
330
|
+
@notify = nil
|
331
|
+
@check = nil
|
433
332
|
end
|
434
333
|
|
435
334
|
@events.fire :state, :done
|
@@ -476,7 +375,6 @@ module Puma
|
|
476
375
|
close_socket = false
|
477
376
|
return
|
478
377
|
when true
|
479
|
-
return unless @queue_requests
|
480
378
|
buffer.reset
|
481
379
|
|
482
380
|
ThreadPool.clean_thread_locals if clean_thread_locals
|
@@ -494,6 +392,7 @@ module Puma
|
|
494
392
|
end
|
495
393
|
|
496
394
|
unless client.reset(check_for_more_data)
|
395
|
+
return unless @queue_requests
|
497
396
|
close_socket = false
|
498
397
|
client.set_timeout @persistent_timeout
|
499
398
|
@reactor.add client
|
@@ -626,6 +525,8 @@ module Puma
|
|
626
525
|
#
|
627
526
|
# Finally, it'll return +true+ on keep-alive connections.
|
628
527
|
def handle_request(req, lines)
|
528
|
+
@requests_count +=1
|
529
|
+
|
629
530
|
env = req.env
|
630
531
|
client = req.io
|
631
532
|
|
@@ -672,37 +573,6 @@ module Puma
|
|
672
573
|
}
|
673
574
|
end
|
674
575
|
|
675
|
-
# Fixup any headers with , in the name to have _ now. We emit
|
676
|
-
# headers with , in them during the parse phase to avoid ambiguity
|
677
|
-
# with the - to _ conversion for critical headers. But here for
|
678
|
-
# compatibility, we'll convert them back. This code is written to
|
679
|
-
# avoid allocation in the common case (ie there are no headers
|
680
|
-
# with , in their names), that's why it has the extra conditionals.
|
681
|
-
|
682
|
-
to_delete = nil
|
683
|
-
to_add = nil
|
684
|
-
|
685
|
-
env.each do |k,v|
|
686
|
-
if k.start_with?("HTTP_") and k.include?(",") and k != "HTTP_TRANSFER,ENCODING"
|
687
|
-
if to_delete
|
688
|
-
to_delete << k
|
689
|
-
else
|
690
|
-
to_delete = [k]
|
691
|
-
end
|
692
|
-
|
693
|
-
unless to_add
|
694
|
-
to_add = {}
|
695
|
-
end
|
696
|
-
|
697
|
-
to_add[k.tr(",", "_")] = v
|
698
|
-
end
|
699
|
-
end
|
700
|
-
|
701
|
-
if to_delete
|
702
|
-
to_delete.each { |k| env.delete(k) }
|
703
|
-
env.merge! to_add
|
704
|
-
end
|
705
|
-
|
706
576
|
# A rack extension. If the app writes #call'ables to this
|
707
577
|
# array, we will invoke them when the request is done.
|
708
578
|
#
|
@@ -724,17 +594,14 @@ module Puma
|
|
724
594
|
return :async
|
725
595
|
end
|
726
596
|
rescue ThreadPool::ForceShutdown => e
|
727
|
-
@events.
|
728
|
-
@events.
|
729
|
-
|
730
|
-
status = 503
|
731
|
-
headers = {}
|
732
|
-
res_body = ["Request was internally terminated early\n"]
|
597
|
+
@events.unknown_error self, e, "Rack app", env
|
598
|
+
@events.log "Detected force shutdown of a thread"
|
733
599
|
|
600
|
+
status, headers, res_body = lowlevel_error(e, env, 503)
|
734
601
|
rescue Exception => e
|
735
602
|
@events.unknown_error self, e, "Rack app", env
|
736
603
|
|
737
|
-
status, headers, res_body = lowlevel_error(e, env)
|
604
|
+
status, headers, res_body = lowlevel_error(e, env, 500)
|
738
605
|
end
|
739
606
|
|
740
607
|
content_length = nil
|
@@ -749,10 +616,10 @@ module Puma
|
|
749
616
|
line_ending = LINE_END
|
750
617
|
colon = COLON
|
751
618
|
|
752
|
-
http_11 =
|
619
|
+
http_11 = env[HTTP_VERSION] == HTTP_11
|
620
|
+
if http_11
|
753
621
|
allow_chunked = true
|
754
622
|
keep_alive = env.fetch(HTTP_CONNECTION, "").downcase != CLOSE
|
755
|
-
include_keepalive_header = false
|
756
623
|
|
757
624
|
# An optimization. The most common response is 200, so we can
|
758
625
|
# reply with the proper 200 status without having to compute
|
@@ -766,11 +633,9 @@ module Puma
|
|
766
633
|
|
767
634
|
no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
768
635
|
end
|
769
|
-
true
|
770
636
|
else
|
771
637
|
allow_chunked = false
|
772
638
|
keep_alive = env.fetch(HTTP_CONNECTION, "").downcase == KEEP_ALIVE
|
773
|
-
include_keepalive_header = keep_alive
|
774
639
|
|
775
640
|
# Same optimization as above for HTTP/1.1
|
776
641
|
#
|
@@ -782,9 +647,12 @@ module Puma
|
|
782
647
|
|
783
648
|
no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
784
649
|
end
|
785
|
-
false
|
786
650
|
end
|
787
651
|
|
652
|
+
# regardless of what the client wants, we always close the connection
|
653
|
+
# if running without request queueing
|
654
|
+
keep_alive &&= @queue_requests
|
655
|
+
|
788
656
|
response_hijack = nil
|
789
657
|
|
790
658
|
headers.each do |k, vs|
|
@@ -811,10 +679,15 @@ module Puma
|
|
811
679
|
end
|
812
680
|
end
|
813
681
|
|
814
|
-
|
815
|
-
|
816
|
-
|
817
|
-
|
682
|
+
# HTTP/1.1 & 1.0 assume different defaults:
|
683
|
+
# - HTTP 1.0 assumes the connection will be closed if not specified
|
684
|
+
# - HTTP 1.1 assumes the connection will be kept alive if not specified.
|
685
|
+
# Only set the header if we're doing something which is not the default
|
686
|
+
# for this protocol version
|
687
|
+
if http_11
|
688
|
+
lines << CONNECTION_CLOSE if !keep_alive
|
689
|
+
else
|
690
|
+
lines << CONNECTION_KEEP_ALIVE if keep_alive
|
818
691
|
end
|
819
692
|
|
820
693
|
if no_body
|
@@ -941,19 +814,21 @@ module Puma
|
|
941
814
|
|
942
815
|
# A fallback rack response if +@app+ raises as exception.
|
943
816
|
#
|
944
|
-
def lowlevel_error(e, env)
|
817
|
+
def lowlevel_error(e, env, status=500)
|
945
818
|
if handler = @options[:lowlevel_error_handler]
|
946
819
|
if handler.arity == 1
|
947
820
|
return handler.call(e)
|
948
|
-
|
821
|
+
elsif handler.arity == 2
|
949
822
|
return handler.call(e, env)
|
823
|
+
else
|
824
|
+
return handler.call(e, env, status)
|
950
825
|
end
|
951
826
|
end
|
952
827
|
|
953
828
|
if @leak_stack_on_error
|
954
|
-
[
|
829
|
+
[status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{e.backtrace.join("\n")}"]]
|
955
830
|
else
|
956
|
-
[
|
831
|
+
[status, {}, ["An unhandled lowlevel error occurred. The application logs may have details.\n"]]
|
957
832
|
end
|
958
833
|
end
|
959
834
|
|
@@ -1011,6 +886,7 @@ module Puma
|
|
1011
886
|
end
|
1012
887
|
|
1013
888
|
def notify_safely(message)
|
889
|
+
@check, @notify = Puma::Util.pipe unless @notify
|
1014
890
|
begin
|
1015
891
|
@notify << message
|
1016
892
|
rescue IOError
|
@@ -1040,8 +916,9 @@ module Puma
|
|
1040
916
|
@thread.join if @thread && sync
|
1041
917
|
end
|
1042
918
|
|
1043
|
-
def begin_restart
|
919
|
+
def begin_restart(sync=false)
|
1044
920
|
notify_safely(RESTART_COMMAND)
|
921
|
+
@thread.join if @thread && sync
|
1045
922
|
end
|
1046
923
|
|
1047
924
|
def fast_write(io, str)
|
@@ -1079,5 +956,13 @@ module Puma
|
|
1079
956
|
HTTP_INJECTION_REGEX =~ header_value.to_s
|
1080
957
|
end
|
1081
958
|
private :possible_header_injection?
|
959
|
+
|
960
|
+
# List of methods invoked by #stats.
|
961
|
+
STAT_METHODS = [:backlog, :running, :pool_capacity, :max_threads, :requests_count].freeze
|
962
|
+
|
963
|
+
# Returns a hash of stats about the running server for reporting purposes.
|
964
|
+
def stats
|
965
|
+
STAT_METHODS.map {|name| [name, send(name) || 0]}.to_h
|
966
|
+
end
|
1082
967
|
end
|
1083
968
|
end
|