puma 3.12.0 → 4.3.8
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 +164 -0
- data/README.md +76 -48
- data/docs/architecture.md +1 -0
- data/docs/deployment.md +24 -4
- data/docs/plugins.md +20 -10
- data/docs/restart.md +4 -2
- data/docs/systemd.md +27 -9
- data/docs/tcp_mode.md +96 -0
- data/ext/puma_http11/PumaHttp11Service.java +2 -0
- data/ext/puma_http11/extconf.rb +13 -0
- data/ext/puma_http11/http11_parser.c +40 -63
- data/ext/puma_http11/http11_parser.java.rl +21 -37
- data/ext/puma_http11/http11_parser.rl +3 -1
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +86 -4
- data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +91 -106
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -4
- data/ext/puma_http11/puma_http11.c +3 -0
- data/lib/puma.rb +8 -0
- data/lib/puma/accept_nonblock.rb +7 -1
- data/lib/puma/app/status.rb +37 -29
- data/lib/puma/binder.rb +47 -68
- data/lib/puma/cli.rb +6 -0
- data/lib/puma/client.rb +244 -199
- data/lib/puma/cluster.rb +55 -30
- data/lib/puma/commonlogger.rb +2 -0
- data/lib/puma/configuration.rb +6 -3
- data/lib/puma/const.rb +32 -18
- data/lib/puma/control_cli.rb +41 -14
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +311 -77
- data/lib/puma/events.rb +6 -1
- data/lib/puma/io_buffer.rb +3 -6
- data/lib/puma/jruby_restart.rb +2 -0
- data/lib/puma/launcher.rb +99 -55
- data/lib/puma/minissl.rb +37 -17
- data/lib/puma/minissl/context_builder.rb +76 -0
- data/lib/puma/null_io.rb +2 -0
- data/lib/puma/plugin.rb +7 -2
- data/lib/puma/plugin/tmp_restart.rb +2 -0
- data/lib/puma/rack/builder.rb +4 -1
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +2 -0
- data/lib/puma/reactor.rb +112 -57
- data/lib/puma/runner.rb +13 -3
- data/lib/puma/server.rb +119 -48
- data/lib/puma/single.rb +5 -3
- data/lib/puma/state_file.rb +2 -0
- data/lib/puma/tcp_logger.rb +2 -0
- data/lib/puma/thread_pool.rb +17 -33
- data/lib/puma/util.rb +2 -6
- data/lib/rack/handler/puma.rb +6 -3
- data/tools/docker/Dockerfile +16 -0
- data/tools/jungle/init.d/puma +6 -6
- data/tools/trickletest.rb +0 -1
- metadata +26 -14
- 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/null_io.rb
CHANGED
data/lib/puma/plugin.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Puma
|
2
4
|
class UnknownPlugin < RuntimeError; end
|
3
5
|
|
@@ -60,8 +62,11 @@ module Puma
|
|
60
62
|
end
|
61
63
|
|
62
64
|
def fire_background
|
63
|
-
@background.
|
64
|
-
Thread.new
|
65
|
+
@background.each_with_index do |b, i|
|
66
|
+
Thread.new do
|
67
|
+
Puma.set_thread_name "plugin background #{i}"
|
68
|
+
b.call
|
69
|
+
end
|
65
70
|
end
|
66
71
|
end
|
67
72
|
end
|
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
|
|
@@ -110,7 +112,8 @@ module Puma::Rack
|
|
110
112
|
|
111
113
|
has_options = false
|
112
114
|
server.valid_options.each do |name, description|
|
113
|
-
next if name.to_s
|
115
|
+
next if name.to_s =~ /^(Host|Port)[^a-zA-Z]/ # ignore handler's host and port options, we do our own.
|
116
|
+
|
114
117
|
info << " -O %-21s %s" % [name, description]
|
115
118
|
has_options = true
|
116
119
|
end
|
data/lib/puma/rack/urlmap.rb
CHANGED
data/lib/puma/rack_default.rb
CHANGED
data/lib/puma/reactor.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'puma/util'
|
2
4
|
require 'puma/minissl'
|
3
5
|
|
6
|
+
require 'nio'
|
7
|
+
|
4
8
|
module Puma
|
5
9
|
# Internal Docs, Not a public interface.
|
6
10
|
#
|
@@ -16,12 +20,13 @@ module Puma
|
|
16
20
|
#
|
17
21
|
# ## Reactor Flow
|
18
22
|
#
|
19
|
-
# A
|
20
|
-
#
|
23
|
+
# A connection comes into a `Puma::Server` instance, it is then passed to a `Puma::Reactor` instance,
|
24
|
+
# which stores it in an array and waits for any of the connections to be ready for reading.
|
21
25
|
#
|
22
|
-
#
|
26
|
+
# The waiting/wake up is performed with nio4r, which will use the appropriate backend (libev, Java NIO or
|
27
|
+
# just plain IO#select). The call to `NIO::Selector#select` will "wake up" and
|
23
28
|
# return the references to any objects that caused it to "wake". The reactor
|
24
|
-
# then loops through each of these request objects, and sees if they're
|
29
|
+
# then loops through each of these request objects, and sees if they're complete. If they
|
25
30
|
# have a full header and body then the reactor passes the request to a thread pool.
|
26
31
|
# Once in a thread pool, a "worker thread" can run the the application's Ruby code against the request.
|
27
32
|
#
|
@@ -36,7 +41,7 @@ module Puma
|
|
36
41
|
# Creates an instance of Puma::Reactor
|
37
42
|
#
|
38
43
|
# The `server` argument is an instance of `Puma::Server`
|
39
|
-
#
|
44
|
+
# that is used to write a response for "low level errors"
|
40
45
|
# when there is an exception inside of the reactor.
|
41
46
|
#
|
42
47
|
# The `app_pool` is an instance of `Puma::ThreadPool`.
|
@@ -47,6 +52,8 @@ module Puma
|
|
47
52
|
@events = server.events
|
48
53
|
@app_pool = app_pool
|
49
54
|
|
55
|
+
@selector = NIO::Selector.new
|
56
|
+
|
50
57
|
@mutex = Mutex.new
|
51
58
|
|
52
59
|
# Read / Write pipes to wake up internal while loop
|
@@ -55,24 +62,26 @@ module Puma
|
|
55
62
|
@sleep_for = DefaultSleepFor
|
56
63
|
@timeouts = []
|
57
64
|
|
58
|
-
|
65
|
+
mon = @selector.register(@ready, :r)
|
66
|
+
mon.value = @ready
|
67
|
+
|
68
|
+
@monitors = [mon]
|
59
69
|
end
|
60
70
|
|
61
71
|
private
|
62
72
|
|
63
|
-
|
64
73
|
# Until a request is added via the `add` method this method will internally
|
65
74
|
# loop, waiting on the `sockets` array objects. The only object in this
|
66
75
|
# array at first is the `@ready` IO object, which is the read end of a pipe
|
67
76
|
# connected to `@trigger` object. When `@trigger` is written to, then the loop
|
68
|
-
# will break on `
|
77
|
+
# will break on `NIO::Selector#select` and return an array.
|
69
78
|
#
|
70
79
|
# ## When a request is added:
|
71
80
|
#
|
72
81
|
# When the `add` method is called, an instance of `Puma::Client` is added to the `@input` array.
|
73
82
|
# Next the `@ready` pipe is "woken" by writing a string of `"*"` to `@trigger`.
|
74
83
|
#
|
75
|
-
# When that happens, the internal loop stops blocking at `
|
84
|
+
# When that happens, the internal loop stops blocking at `NIO::Selector#select` and returns a reference
|
76
85
|
# to whatever "woke" it up. On the very first loop, the only thing in `sockets` is `@ready`.
|
77
86
|
# When `@trigger` is written-to, the loop "wakes" and the `ready`
|
78
87
|
# variable returns an array of arrays that looks like `[[#<IO:fd 10>], [], []]` where the
|
@@ -88,11 +97,11 @@ module Puma
|
|
88
97
|
# to the `@ready` IO object. For example: `[#<IO:fd 10>, #<Puma::Client:0x3fdc1103bee8 @ready=false>]`.
|
89
98
|
#
|
90
99
|
# Since the `Puma::Client` in this example has data that has not been read yet,
|
91
|
-
# the `
|
100
|
+
# the `NIO::Selector#select` is immediately able to "wake" and read from the `Puma::Client`. At this point the
|
92
101
|
# `ready` output looks like this: `[[#<Puma::Client:0x3fdc1103bee8 @ready=false>], [], []]`.
|
93
102
|
#
|
94
103
|
# Each element in the first entry is iterated over. The `Puma::Client` object is not
|
95
|
-
# the `@ready` pipe, so the reactor checks to see if it has the
|
104
|
+
# the `@ready` pipe, so the reactor checks to see if it has the full header and body with
|
96
105
|
# the `Puma::Client#try_to_finish` method. If the full request has been sent,
|
97
106
|
# then the request is passed off to the `@app_pool` thread pool so that a "worker thread"
|
98
107
|
# can pick up the request and begin to execute application logic. This is done
|
@@ -100,56 +109,93 @@ module Puma
|
|
100
109
|
#
|
101
110
|
# If the request body is not present then nothing will happen, and the loop will iterate
|
102
111
|
# again. When the client sends more data to the socket the `Puma::Client` object will
|
103
|
-
# wake up the `
|
112
|
+
# wake up the `NIO::Selector#select` and it can again be checked to see if it's ready to be
|
104
113
|
# passed to the thread pool.
|
105
114
|
#
|
106
115
|
# ## Time Out Case
|
107
116
|
#
|
108
|
-
# In addition to being woken via a write to one of the sockets the `
|
117
|
+
# In addition to being woken via a write to one of the sockets the `NIO::Selector#select` will
|
109
118
|
# periodically "time out" of the sleep. One of the functions of this is to check for
|
110
119
|
# any requests that have "timed out". At the end of the loop it's checked to see if
|
111
|
-
# the first element in the `@timeout` array has exceed
|
112
|
-
# the client object is removed from the timeout
|
113
|
-
# Then
|
120
|
+
# the first element in the `@timeout` array has exceed its allowed time. If so,
|
121
|
+
# the client object is removed from the timeout array, a 408 response is written.
|
122
|
+
# Then its connection is closed, and the object is removed from the `sockets` array
|
114
123
|
# that watches for new data.
|
115
124
|
#
|
116
125
|
# This behavior loops until all the objects that have timed out have been removed.
|
117
126
|
#
|
118
|
-
# Once all the timeouts have been processed, the next duration of the `
|
127
|
+
# Once all the timeouts have been processed, the next duration of the `NIO::Selector#select` sleep
|
119
128
|
# will be set to be equal to the amount of time it will take for the next timeout to occur.
|
120
129
|
# This calculation happens in `calculate_sleep`.
|
121
130
|
def run_internal
|
122
|
-
|
131
|
+
monitors = @monitors
|
132
|
+
selector = @selector
|
123
133
|
|
124
134
|
while true
|
125
135
|
begin
|
126
|
-
ready =
|
136
|
+
ready = selector.select @sleep_for
|
127
137
|
rescue IOError => e
|
128
138
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
129
|
-
if
|
139
|
+
if monitors.any? { |mon| mon.value.closed? }
|
130
140
|
STDERR.puts "Error in select: #{e.message} (#{e.class})"
|
131
141
|
STDERR.puts e.backtrace
|
132
|
-
|
142
|
+
|
143
|
+
monitors.reject! do |mon|
|
144
|
+
if mon.value.closed?
|
145
|
+
selector.deregister mon.value
|
146
|
+
true
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
133
150
|
retry
|
134
151
|
else
|
135
152
|
raise
|
136
153
|
end
|
137
154
|
end
|
138
155
|
|
139
|
-
if ready
|
140
|
-
|
141
|
-
if
|
156
|
+
if ready
|
157
|
+
ready.each do |mon|
|
158
|
+
if mon.value == @ready
|
142
159
|
@mutex.synchronize do
|
143
160
|
case @ready.read(1)
|
144
161
|
when "*"
|
145
|
-
|
162
|
+
@input.each do |c|
|
163
|
+
mon = nil
|
164
|
+
begin
|
165
|
+
begin
|
166
|
+
mon = selector.register(c, :r)
|
167
|
+
rescue ArgumentError
|
168
|
+
# There is a bug where we seem to be registering an already registered
|
169
|
+
# client. This code deals with this situation but I wish we didn't have to.
|
170
|
+
monitors.delete_if { |submon| submon.value.to_io == c.to_io }
|
171
|
+
selector.deregister(c)
|
172
|
+
mon = selector.register(c, :r)
|
173
|
+
end
|
174
|
+
rescue IOError
|
175
|
+
# Means that the io is closed, so we should ignore this request
|
176
|
+
# entirely
|
177
|
+
else
|
178
|
+
mon.value = c
|
179
|
+
@timeouts << mon if c.timeout_at
|
180
|
+
monitors << mon
|
181
|
+
end
|
182
|
+
end
|
146
183
|
@input.clear
|
184
|
+
|
185
|
+
@timeouts.sort! { |a,b| a.value.timeout_at <=> b.value.timeout_at }
|
186
|
+
calculate_sleep
|
147
187
|
when "c"
|
148
|
-
|
149
|
-
if
|
188
|
+
monitors.reject! do |submon|
|
189
|
+
if submon.value == @ready
|
150
190
|
false
|
151
191
|
else
|
152
|
-
|
192
|
+
submon.value.close
|
193
|
+
begin
|
194
|
+
selector.deregister submon.value
|
195
|
+
rescue IOError
|
196
|
+
# nio4r on jruby seems to throw an IOError here if the IO is closed, so
|
197
|
+
# we need to swallow it.
|
198
|
+
end
|
153
199
|
true
|
154
200
|
end
|
155
201
|
end
|
@@ -158,40 +204,48 @@ module Puma
|
|
158
204
|
end
|
159
205
|
end
|
160
206
|
else
|
207
|
+
c = mon.value
|
208
|
+
|
161
209
|
# We have to be sure to remove it from the timeout
|
162
210
|
# list or we'll accidentally close the socket when
|
163
211
|
# it's in use!
|
164
212
|
if c.timeout_at
|
165
213
|
@mutex.synchronize do
|
166
|
-
@timeouts.delete
|
214
|
+
@timeouts.delete mon
|
167
215
|
end
|
168
216
|
end
|
169
217
|
|
170
218
|
begin
|
171
219
|
if c.try_to_finish
|
172
220
|
@app_pool << c
|
173
|
-
|
221
|
+
clear_monitor mon
|
174
222
|
end
|
175
223
|
|
176
224
|
# Don't report these to the lowlevel_error handler, otherwise
|
177
225
|
# will be flooding them with errors when persistent connections
|
178
226
|
# are closed.
|
179
227
|
rescue ConnectionError
|
180
|
-
c.
|
228
|
+
c.write_error(500)
|
181
229
|
c.close
|
182
230
|
|
183
|
-
|
231
|
+
clear_monitor mon
|
184
232
|
|
185
233
|
# SSL handshake failure
|
186
234
|
rescue MiniSSL::SSLError => e
|
187
235
|
@server.lowlevel_error(e, c.env)
|
188
236
|
|
189
237
|
ssl_socket = c.io
|
190
|
-
|
238
|
+
begin
|
239
|
+
addr = ssl_socket.peeraddr.last
|
240
|
+
# EINVAL can happen when browser closes socket w/security exception
|
241
|
+
rescue IOError, Errno::EINVAL
|
242
|
+
addr = "<unknown>"
|
243
|
+
end
|
244
|
+
|
191
245
|
cert = ssl_socket.peercert
|
192
246
|
|
193
247
|
c.close
|
194
|
-
|
248
|
+
clear_monitor mon
|
195
249
|
|
196
250
|
@events.ssl_error @server, addr, cert, e
|
197
251
|
|
@@ -199,19 +253,19 @@ module Puma
|
|
199
253
|
rescue HttpParserError => e
|
200
254
|
@server.lowlevel_error(e, c.env)
|
201
255
|
|
202
|
-
c.
|
256
|
+
c.write_error(400)
|
203
257
|
c.close
|
204
258
|
|
205
|
-
|
259
|
+
clear_monitor mon
|
206
260
|
|
207
261
|
@events.parse_error @server, c.env, e
|
208
262
|
rescue StandardError => e
|
209
263
|
@server.lowlevel_error(e, c.env)
|
210
264
|
|
211
|
-
c.
|
265
|
+
c.write_error(500)
|
212
266
|
c.close
|
213
267
|
|
214
|
-
|
268
|
+
clear_monitor mon
|
215
269
|
end
|
216
270
|
end
|
217
271
|
end
|
@@ -221,11 +275,13 @@ module Puma
|
|
221
275
|
@mutex.synchronize do
|
222
276
|
now = Time.now
|
223
277
|
|
224
|
-
while @timeouts.first.timeout_at < now
|
225
|
-
|
226
|
-
c
|
278
|
+
while @timeouts.first.value.timeout_at < now
|
279
|
+
mon = @timeouts.shift
|
280
|
+
c = mon.value
|
281
|
+
c.write_error(408) if c.in_data_phase
|
227
282
|
c.close
|
228
|
-
|
283
|
+
|
284
|
+
clear_monitor mon
|
229
285
|
|
230
286
|
break if @timeouts.empty?
|
231
287
|
end
|
@@ -236,6 +292,11 @@ module Puma
|
|
236
292
|
end
|
237
293
|
end
|
238
294
|
|
295
|
+
def clear_monitor(mon)
|
296
|
+
@selector.deregister mon.value
|
297
|
+
@monitors.delete mon
|
298
|
+
end
|
299
|
+
|
239
300
|
public
|
240
301
|
|
241
302
|
def run
|
@@ -247,6 +308,7 @@ module Puma
|
|
247
308
|
|
248
309
|
def run_in_thread
|
249
310
|
@thread = Thread.new do
|
311
|
+
Puma.set_thread_name "reactor"
|
250
312
|
begin
|
251
313
|
run_internal
|
252
314
|
rescue StandardError => e
|
@@ -260,7 +322,7 @@ module Puma
|
|
260
322
|
end
|
261
323
|
end
|
262
324
|
|
263
|
-
# The `calculate_sleep` sets the value that the `
|
325
|
+
# The `calculate_sleep` sets the value that the `NIO::Selector#select` will
|
264
326
|
# sleep for in the main reactor loop when no sockets are being written to.
|
265
327
|
#
|
266
328
|
# The values kept in `@timeouts` are sorted so that the first timeout
|
@@ -274,7 +336,7 @@ module Puma
|
|
274
336
|
if @timeouts.empty?
|
275
337
|
@sleep_for = DefaultSleepFor
|
276
338
|
else
|
277
|
-
diff = @timeouts.first.timeout_at.to_f - Time.now.to_f
|
339
|
+
diff = @timeouts.first.value.timeout_at.to_f - Time.now.to_f
|
278
340
|
|
279
341
|
if diff < 0.0
|
280
342
|
@sleep_for = 0
|
@@ -291,18 +353,18 @@ module Puma
|
|
291
353
|
# object.
|
292
354
|
#
|
293
355
|
# The main body of the reactor loop is in `run_internal` and it
|
294
|
-
# will sleep on `
|
295
|
-
# reactor it cannot be added directly to the `sockets`
|
296
|
-
# the `
|
356
|
+
# will sleep on `NIO::Selector#select`. When a new connection is added to the
|
357
|
+
# reactor it cannot be added directly to the `sockets` array, because
|
358
|
+
# the `NIO::Selector#select` will not be watching for it yet.
|
297
359
|
#
|
298
|
-
# Instead what needs to happen is that `
|
360
|
+
# Instead what needs to happen is that `NIO::Selector#select` needs to be woken up,
|
299
361
|
# the contents of `@input` added to the `sockets` array, and then
|
300
|
-
# another call to `
|
362
|
+
# another call to `NIO::Selector#select` needs to happen. Since the `Puma::Client`
|
301
363
|
# object can be read immediately, it does not block, but instead returns
|
302
364
|
# right away.
|
303
365
|
#
|
304
366
|
# This behavior is accomplished by writing to `@trigger` which wakes up
|
305
|
-
# the `
|
367
|
+
# the `NIO::Selector#select` and then there is logic to detect the value of `*`,
|
306
368
|
# pull the contents from `@input` and add them to the sockets array.
|
307
369
|
#
|
308
370
|
# If the object passed in has a timeout value in `timeout_at` then
|
@@ -313,13 +375,6 @@ module Puma
|
|
313
375
|
@mutex.synchronize do
|
314
376
|
@input << c
|
315
377
|
@trigger << "*"
|
316
|
-
|
317
|
-
if c.timeout_at
|
318
|
-
@timeouts << c
|
319
|
-
@timeouts.sort! { |a,b| a.timeout_at <=> b.timeout_at }
|
320
|
-
|
321
|
-
calculate_sleep
|
322
|
-
end
|
323
378
|
end
|
324
379
|
end
|
325
380
|
|
data/lib/puma/runner.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'puma/server'
|
2
4
|
require 'puma/const'
|
5
|
+
require 'puma/minissl/context_builder'
|
3
6
|
|
4
7
|
module Puma
|
5
8
|
# Generic class that is used by `Puma::Cluster` and `Puma::Single` to
|
@@ -12,6 +15,7 @@ module Puma
|
|
12
15
|
@options = cli.options
|
13
16
|
@app = nil
|
14
17
|
@control = nil
|
18
|
+
@started_at = Time.now
|
15
19
|
end
|
16
20
|
|
17
21
|
def daemon?
|
@@ -50,17 +54,23 @@ module Puma
|
|
50
54
|
|
51
55
|
uri = URI.parse str
|
52
56
|
|
53
|
-
app = Puma::App::Status.new @launcher
|
54
|
-
|
55
57
|
if token = @options[:control_auth_token]
|
56
|
-
|
58
|
+
token = nil if token.empty? || token == 'none'
|
57
59
|
end
|
58
60
|
|
61
|
+
app = Puma::App::Status.new @launcher, token
|
62
|
+
|
59
63
|
control = Puma::Server.new app, @launcher.events
|
60
64
|
control.min_threads = 0
|
61
65
|
control.max_threads = 1
|
62
66
|
|
63
67
|
case uri.scheme
|
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
|
64
74
|
when "tcp"
|
65
75
|
log "* Starting control server on #{str}"
|
66
76
|
control.add_tcp_listener uri.host, uri.port
|