puma 3.11.2 → 6.0.0
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 +5 -5
- data/History.md +1708 -422
- data/LICENSE +23 -20
- data/README.md +190 -64
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +59 -21
- data/docs/compile_options.md +55 -0
- data/docs/deployment.md +69 -58
- data/docs/fork_worker.md +31 -0
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/jungle/README.md +9 -0
- data/docs/jungle/rc.d/README.md +74 -0
- data/docs/jungle/rc.d/puma +61 -0
- data/docs/jungle/rc.d/puma.conf +10 -0
- data/docs/kubernetes.md +66 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +22 -12
- data/docs/rails_dev_mode.md +28 -0
- data/docs/restart.md +47 -22
- data/docs/signals.md +13 -11
- data/docs/stats.md +142 -0
- data/docs/systemd.md +100 -115
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -2
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +61 -3
- data/ext/puma_http11/http11_parser.c +106 -118
- data/ext/puma_http11/http11_parser.h +2 -2
- data/ext/puma_http11/http11_parser.java.rl +22 -38
- data/ext/puma_http11/http11_parser.rl +6 -4
- data/ext/puma_http11/http11_parser_common.rl +6 -6
- data/ext/puma_http11/mini_ssl.c +376 -93
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +84 -99
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +250 -88
- data/ext/puma_http11/puma_http11.c +49 -57
- data/lib/puma/app/status.rb +71 -49
- data/lib/puma/binder.rb +243 -148
- data/lib/puma/cli.rb +50 -35
- data/lib/puma/client.rb +369 -232
- data/lib/puma/cluster/worker.rb +175 -0
- data/lib/puma/cluster/worker_handle.rb +97 -0
- data/lib/puma/cluster.rb +268 -235
- data/lib/puma/commonlogger.rb +4 -2
- data/lib/puma/configuration.rb +116 -88
- data/lib/puma/const.rb +49 -30
- data/lib/puma/control_cli.rb +124 -77
- data/lib/puma/detect.rb +33 -2
- data/lib/puma/dsl.rb +685 -138
- data/lib/puma/error_logger.rb +112 -0
- data/lib/puma/events.rb +17 -111
- data/lib/puma/io_buffer.rb +34 -5
- data/lib/puma/jruby_restart.rb +4 -59
- data/lib/puma/json_serialization.rb +96 -0
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +197 -130
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +92 -0
- data/lib/puma/minissl.rb +256 -70
- data/lib/puma/null_io.rb +20 -1
- data/lib/puma/plugin/tmp_restart.rb +3 -1
- data/lib/puma/plugin.rb +9 -13
- data/lib/puma/rack/builder.rb +8 -9
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +3 -1
- data/lib/puma/reactor.rb +90 -187
- data/lib/puma/request.rb +607 -0
- data/lib/puma/runner.rb +94 -71
- data/lib/puma/server.rb +336 -703
- data/lib/puma/single.rb +27 -72
- data/lib/puma/state_file.rb +46 -7
- data/lib/puma/systemd.rb +47 -0
- data/lib/puma/thread_pool.rb +185 -91
- data/lib/puma/util.rb +23 -10
- data/lib/puma.rb +68 -3
- data/lib/rack/handler/puma.rb +17 -14
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +53 -30
- data/ext/puma_http11/io_buffer.c +0 -155
- data/lib/puma/accept_nonblock.rb +0 -23
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/convenient.rb +0 -23
- data/lib/puma/daemon_ext.rb +0 -31
- data/lib/puma/delegation.rb +0 -11
- data/lib/puma/java_io_buffer.rb +0 -45
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
- data/lib/puma/tcp_logger.rb +0 -39
- data/tools/jungle/README.md +0 -13
- data/tools/jungle/init.d/README.md +0 -59
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
- data/tools/jungle/upstart/README.md +0 -61
- data/tools/jungle/upstart/puma-manager.conf +0 -31
- data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/rack/builder.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Puma
|
2
4
|
end
|
3
5
|
|
@@ -65,10 +67,6 @@ module Puma::Rack
|
|
65
67
|
options[:environment] = e
|
66
68
|
}
|
67
69
|
|
68
|
-
opts.on("-D", "--daemonize", "run daemonized in the background") { |d|
|
69
|
-
options[:daemonize] = d ? true : false
|
70
|
-
}
|
71
|
-
|
72
70
|
opts.on("-P", "--pid FILE", "file to store PID") { |f|
|
73
71
|
options[:pid] = ::File.expand_path(f)
|
74
72
|
}
|
@@ -104,13 +102,14 @@ module Puma::Rack
|
|
104
102
|
begin
|
105
103
|
info = []
|
106
104
|
server = Rack::Handler.get(options[:server]) || Rack::Handler.default(options)
|
107
|
-
if server
|
105
|
+
if server&.respond_to?(:valid_options)
|
108
106
|
info << ""
|
109
107
|
info << "Server-specific options for #{server.name}:"
|
110
108
|
|
111
109
|
has_options = false
|
112
110
|
server.valid_options.each do |name, description|
|
113
|
-
next if
|
111
|
+
next if /^(Host|Port)[^a-zA-Z]/.match? name.to_s # ignore handler's host and port options, we do our own.
|
112
|
+
|
114
113
|
info << " -O %-21s %s" % [name, description]
|
115
114
|
has_options = true
|
116
115
|
end
|
@@ -166,7 +165,7 @@ module Puma::Rack
|
|
166
165
|
require config
|
167
166
|
app = Object.const_get(::File.basename(config, '.rb').capitalize)
|
168
167
|
end
|
169
|
-
|
168
|
+
[app, options]
|
170
169
|
end
|
171
170
|
|
172
171
|
def self.new_from_string(builder_script, file="(rackup)")
|
@@ -277,7 +276,7 @@ module Puma::Rack
|
|
277
276
|
app = @map ? generate_map(@run, @map) : @run
|
278
277
|
fail "missing run or map statement" unless app
|
279
278
|
app = @use.reverse.inject(app) { |a,e| e[a] }
|
280
|
-
@warmup
|
279
|
+
@warmup&.call app
|
281
280
|
app
|
282
281
|
end
|
283
282
|
|
@@ -288,7 +287,7 @@ module Puma::Rack
|
|
288
287
|
private
|
289
288
|
|
290
289
|
def generate_map(default_app, mapping)
|
291
|
-
|
290
|
+
require_relative 'urlmap'
|
292
291
|
|
293
292
|
mapped = default_app ? {'/' => default_app} : {}
|
294
293
|
mapping.each { |r,b| mapped[r] = self.class.new(default_app, &b).to_app }
|
data/lib/puma/rack/urlmap.rb
CHANGED
data/lib/puma/rack_default.rb
CHANGED
data/lib/puma/reactor.rb
CHANGED
@@ -1,213 +1,116 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'queue_close' unless ::Queue.instance_methods.include? :close
|
3
4
|
|
4
5
|
module Puma
|
6
|
+
class UnsupportedBackend < StandardError; end
|
7
|
+
|
8
|
+
# Monitors a collection of IO objects, calling a block whenever
|
9
|
+
# any monitored object either receives data or times out, or when the Reactor shuts down.
|
10
|
+
#
|
11
|
+
# The waiting/wake up is performed with nio4r, which will use the appropriate backend (libev,
|
12
|
+
# Java NIO or just plain IO#select). The call to `NIO::Selector#select` will
|
13
|
+
# 'wakeup' any IO object that receives data.
|
14
|
+
#
|
15
|
+
# This class additionally tracks a timeout for every added object,
|
16
|
+
# and wakes up any object when its timeout elapses.
|
17
|
+
#
|
18
|
+
# The implementation uses a Queue to synchronize adding new objects from the internal select loop.
|
5
19
|
class Reactor
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
@
|
15
|
-
@input =
|
16
|
-
@sleep_for = DefaultSleepFor
|
20
|
+
# Create a new Reactor to monitor IO objects added by #add.
|
21
|
+
# The provided block will be invoked when an IO has data available to read,
|
22
|
+
# its timeout elapses, or when the Reactor shuts down.
|
23
|
+
def initialize(backend, &block)
|
24
|
+
require 'nio'
|
25
|
+
unless backend == :auto || NIO::Selector.backends.include?(backend)
|
26
|
+
raise "unsupported IO selector backend: #{backend} (available backends: #{NIO::Selector.backends.join(', ')})"
|
27
|
+
end
|
28
|
+
@selector = backend == :auto ? NIO::Selector.new : NIO::Selector.new(backend)
|
29
|
+
@input = Queue.new
|
17
30
|
@timeouts = []
|
18
|
-
|
19
|
-
@sockets = [@ready]
|
31
|
+
@block = block
|
20
32
|
end
|
21
33
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
begin
|
29
|
-
ready = IO.select sockets, nil, nil, @sleep_for
|
30
|
-
rescue IOError => e
|
31
|
-
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
32
|
-
if sockets.any? { |socket| socket.closed? }
|
33
|
-
STDERR.puts "Error in select: #{e.message} (#{e.class})"
|
34
|
-
STDERR.puts e.backtrace
|
35
|
-
sockets = sockets.reject { |socket| socket.closed? }
|
36
|
-
retry
|
37
|
-
else
|
38
|
-
raise
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
if ready and reads = ready[0]
|
43
|
-
reads.each do |c|
|
44
|
-
if c == @ready
|
45
|
-
@mutex.synchronize do
|
46
|
-
case @ready.read(1)
|
47
|
-
when "*"
|
48
|
-
sockets += @input
|
49
|
-
@input.clear
|
50
|
-
when "c"
|
51
|
-
sockets.delete_if do |s|
|
52
|
-
if s == @ready
|
53
|
-
false
|
54
|
-
else
|
55
|
-
s.close
|
56
|
-
true
|
57
|
-
end
|
58
|
-
end
|
59
|
-
when "!"
|
60
|
-
return
|
61
|
-
end
|
62
|
-
end
|
63
|
-
else
|
64
|
-
# We have to be sure to remove it from the timeout
|
65
|
-
# list or we'll accidentally close the socket when
|
66
|
-
# it's in use!
|
67
|
-
if c.timeout_at
|
68
|
-
@mutex.synchronize do
|
69
|
-
@timeouts.delete c
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
begin
|
74
|
-
if c.try_to_finish
|
75
|
-
@app_pool << c
|
76
|
-
sockets.delete c
|
77
|
-
end
|
78
|
-
|
79
|
-
# Don't report these to the lowlevel_error handler, otherwise
|
80
|
-
# will be flooding them with errors when persistent connections
|
81
|
-
# are closed.
|
82
|
-
rescue ConnectionError
|
83
|
-
c.write_500
|
84
|
-
c.close
|
85
|
-
|
86
|
-
sockets.delete c
|
87
|
-
|
88
|
-
# SSL handshake failure
|
89
|
-
rescue MiniSSL::SSLError => e
|
90
|
-
@server.lowlevel_error(e, c.env)
|
91
|
-
|
92
|
-
ssl_socket = c.io
|
93
|
-
addr = ssl_socket.peeraddr.last
|
94
|
-
cert = ssl_socket.peercert
|
95
|
-
|
96
|
-
c.close
|
97
|
-
sockets.delete c
|
98
|
-
|
99
|
-
@events.ssl_error @server, addr, cert, e
|
100
|
-
|
101
|
-
# The client doesn't know HTTP well
|
102
|
-
rescue HttpParserError => e
|
103
|
-
@server.lowlevel_error(e, c.env)
|
104
|
-
|
105
|
-
c.write_400
|
106
|
-
c.close
|
107
|
-
|
108
|
-
sockets.delete c
|
109
|
-
|
110
|
-
@events.parse_error @server, c.env, e
|
111
|
-
rescue StandardError => e
|
112
|
-
@server.lowlevel_error(e, c.env)
|
113
|
-
|
114
|
-
c.write_500
|
115
|
-
c.close
|
116
|
-
|
117
|
-
sockets.delete c
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
unless @timeouts.empty?
|
124
|
-
@mutex.synchronize do
|
125
|
-
now = Time.now
|
126
|
-
|
127
|
-
while @timeouts.first.timeout_at < now
|
128
|
-
c = @timeouts.shift
|
129
|
-
c.write_408 if c.in_data_phase
|
130
|
-
c.close
|
131
|
-
sockets.delete c
|
132
|
-
|
133
|
-
break if @timeouts.empty?
|
134
|
-
end
|
135
|
-
|
136
|
-
calculate_sleep
|
137
|
-
end
|
34
|
+
# Run the internal select loop, using a background thread by default.
|
35
|
+
def run(background=true)
|
36
|
+
if background
|
37
|
+
@thread = Thread.new do
|
38
|
+
Puma.set_thread_name "reactor"
|
39
|
+
select_loop
|
138
40
|
end
|
41
|
+
else
|
42
|
+
select_loop
|
139
43
|
end
|
140
44
|
end
|
141
45
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
@
|
148
|
-
|
46
|
+
# Add a new client to monitor.
|
47
|
+
# The object must respond to #timeout and #timeout_at.
|
48
|
+
# Returns false if the reactor is already shut down.
|
49
|
+
def add(client)
|
50
|
+
@input << client
|
51
|
+
@selector.wakeup
|
52
|
+
true
|
53
|
+
rescue ClosedQueueError
|
54
|
+
false
|
149
55
|
end
|
150
56
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
STDERR.puts e.backtrace
|
158
|
-
retry
|
159
|
-
ensure
|
160
|
-
@trigger.close
|
161
|
-
@ready.close
|
162
|
-
end
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
def calculate_sleep
|
167
|
-
if @timeouts.empty?
|
168
|
-
@sleep_for = DefaultSleepFor
|
169
|
-
else
|
170
|
-
diff = @timeouts.first.timeout_at.to_f - Time.now.to_f
|
171
|
-
|
172
|
-
if diff < 0.0
|
173
|
-
@sleep_for = 0
|
174
|
-
else
|
175
|
-
@sleep_for = diff
|
176
|
-
end
|
57
|
+
# Shutdown the reactor, blocking until the background thread is finished.
|
58
|
+
def shutdown
|
59
|
+
@input.close
|
60
|
+
begin
|
61
|
+
@selector.wakeup
|
62
|
+
rescue IOError # Ignore if selector is already closed
|
177
63
|
end
|
64
|
+
@thread&.join
|
178
65
|
end
|
179
66
|
|
180
|
-
|
181
|
-
@mutex.synchronize do
|
182
|
-
@input << c
|
183
|
-
@trigger << "*"
|
184
|
-
|
185
|
-
if c.timeout_at
|
186
|
-
@timeouts << c
|
187
|
-
@timeouts.sort! { |a,b| a.timeout_at <=> b.timeout_at }
|
67
|
+
private
|
188
68
|
|
189
|
-
|
69
|
+
def select_loop
|
70
|
+
begin
|
71
|
+
until @input.closed? && @input.empty?
|
72
|
+
# Wakeup any registered object that receives incoming data.
|
73
|
+
# Block until the earliest timeout or Selector#wakeup is called.
|
74
|
+
timeout = (earliest = @timeouts.first) && earliest.timeout
|
75
|
+
@selector.select(timeout) {|mon| wakeup!(mon.value)}
|
76
|
+
|
77
|
+
# Wakeup all objects that timed out.
|
78
|
+
timed_out = @timeouts.take_while {|t| t.timeout == 0}
|
79
|
+
timed_out.each { |c| wakeup! c }
|
80
|
+
|
81
|
+
unless @input.empty?
|
82
|
+
until @input.empty?
|
83
|
+
client = @input.pop
|
84
|
+
register(client) if client.io_ok?
|
85
|
+
end
|
86
|
+
@timeouts.sort_by!(&:timeout_at)
|
87
|
+
end
|
190
88
|
end
|
89
|
+
rescue StandardError => e
|
90
|
+
STDERR.puts "Error in reactor loop escaped: #{e.message} (#{e.class})"
|
91
|
+
STDERR.puts e.backtrace
|
92
|
+
retry
|
191
93
|
end
|
94
|
+
# Wakeup all remaining objects on shutdown.
|
95
|
+
@timeouts.each(&@block)
|
96
|
+
@selector.close
|
192
97
|
end
|
193
98
|
|
194
|
-
#
|
195
|
-
def
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
end
|
99
|
+
# Start monitoring the object.
|
100
|
+
def register(client)
|
101
|
+
@selector.register(client.to_io, :r).value = client
|
102
|
+
@timeouts << client
|
103
|
+
rescue ArgumentError
|
104
|
+
# unreadable clients raise error when processed by NIO
|
201
105
|
end
|
202
106
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
107
|
+
# 'Wake up' a monitored object by calling the provided block.
|
108
|
+
# Stop monitoring the object if the block returns `true`.
|
109
|
+
def wakeup!(client)
|
110
|
+
if @block.call client
|
111
|
+
@selector.deregister client.to_io
|
112
|
+
@timeouts.delete client
|
208
113
|
end
|
209
|
-
|
210
|
-
@thread.join
|
211
114
|
end
|
212
115
|
end
|
213
116
|
end
|