puma 3.12.6 → 4.3.10
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 +145 -3
- data/README.md +76 -48
- data/docs/architecture.md +1 -0
- data/docs/deployment.md +24 -4
- 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/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 +58 -70
- data/ext/puma_http11/http11_parser.java.rl +21 -37
- data/ext/puma_http11/http11_parser_common.rl +4 -4
- data/ext/puma_http11/mini_ssl.c +78 -8
- data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +86 -99
- 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/accept_nonblock.rb +7 -1
- data/lib/puma/app/status.rb +37 -29
- data/lib/puma/binder.rb +38 -60
- data/lib/puma/cli.rb +4 -0
- data/lib/puma/client.rb +242 -208
- data/lib/puma/cluster.rb +53 -30
- data/lib/puma/configuration.rb +4 -3
- data/lib/puma/const.rb +22 -18
- data/lib/puma/control_cli.rb +30 -5
- data/lib/puma/dsl.rb +299 -75
- data/lib/puma/events.rb +4 -1
- data/lib/puma/io_buffer.rb +1 -6
- data/lib/puma/launcher.rb +95 -53
- data/lib/puma/minissl/context_builder.rb +76 -0
- data/lib/puma/minissl.rb +35 -17
- data/lib/puma/plugin/tmp_restart.rb +2 -0
- data/lib/puma/plugin.rb +5 -2
- data/lib/puma/rack/builder.rb +2 -0
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +2 -0
- data/lib/puma/reactor.rb +110 -57
- data/lib/puma/runner.rb +11 -3
- data/lib/puma/server.rb +73 -57
- data/lib/puma/single.rb +3 -3
- data/lib/puma/thread_pool.rb +15 -33
- data/lib/puma/util.rb +1 -6
- data/lib/puma.rb +8 -0
- data/lib/rack/handler/puma.rb +3 -3
- data/tools/docker/Dockerfile +16 -0
- data/tools/jungle/init.d/puma +6 -6
- data/tools/trickletest.rb +0 -1
- metadata +26 -13
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/convenient.rb +0 -25
- data/lib/puma/daemon_ext.rb +0 -33
- data/lib/puma/delegation.rb +0 -13
- data/lib/puma/java_io_buffer.rb +0 -47
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
data/lib/puma/reactor.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require 'puma/util'
|
4
4
|
require 'puma/minissl'
|
5
5
|
|
6
|
+
require 'nio'
|
7
|
+
|
6
8
|
module Puma
|
7
9
|
# Internal Docs, Not a public interface.
|
8
10
|
#
|
@@ -18,12 +20,13 @@ module Puma
|
|
18
20
|
#
|
19
21
|
# ## Reactor Flow
|
20
22
|
#
|
21
|
-
# A
|
22
|
-
#
|
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.
|
23
25
|
#
|
24
|
-
#
|
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
|
25
28
|
# return the references to any objects that caused it to "wake". The reactor
|
26
|
-
# 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
|
27
30
|
# have a full header and body then the reactor passes the request to a thread pool.
|
28
31
|
# Once in a thread pool, a "worker thread" can run the the application's Ruby code against the request.
|
29
32
|
#
|
@@ -38,7 +41,7 @@ module Puma
|
|
38
41
|
# Creates an instance of Puma::Reactor
|
39
42
|
#
|
40
43
|
# The `server` argument is an instance of `Puma::Server`
|
41
|
-
#
|
44
|
+
# that is used to write a response for "low level errors"
|
42
45
|
# when there is an exception inside of the reactor.
|
43
46
|
#
|
44
47
|
# The `app_pool` is an instance of `Puma::ThreadPool`.
|
@@ -49,6 +52,8 @@ module Puma
|
|
49
52
|
@events = server.events
|
50
53
|
@app_pool = app_pool
|
51
54
|
|
55
|
+
@selector = NIO::Selector.new
|
56
|
+
|
52
57
|
@mutex = Mutex.new
|
53
58
|
|
54
59
|
# Read / Write pipes to wake up internal while loop
|
@@ -57,24 +62,26 @@ module Puma
|
|
57
62
|
@sleep_for = DefaultSleepFor
|
58
63
|
@timeouts = []
|
59
64
|
|
60
|
-
|
65
|
+
mon = @selector.register(@ready, :r)
|
66
|
+
mon.value = @ready
|
67
|
+
|
68
|
+
@monitors = [mon]
|
61
69
|
end
|
62
70
|
|
63
71
|
private
|
64
72
|
|
65
|
-
|
66
73
|
# Until a request is added via the `add` method this method will internally
|
67
74
|
# loop, waiting on the `sockets` array objects. The only object in this
|
68
75
|
# array at first is the `@ready` IO object, which is the read end of a pipe
|
69
76
|
# connected to `@trigger` object. When `@trigger` is written to, then the loop
|
70
|
-
# will break on `
|
77
|
+
# will break on `NIO::Selector#select` and return an array.
|
71
78
|
#
|
72
79
|
# ## When a request is added:
|
73
80
|
#
|
74
81
|
# When the `add` method is called, an instance of `Puma::Client` is added to the `@input` array.
|
75
82
|
# Next the `@ready` pipe is "woken" by writing a string of `"*"` to `@trigger`.
|
76
83
|
#
|
77
|
-
# 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
|
78
85
|
# to whatever "woke" it up. On the very first loop, the only thing in `sockets` is `@ready`.
|
79
86
|
# When `@trigger` is written-to, the loop "wakes" and the `ready`
|
80
87
|
# variable returns an array of arrays that looks like `[[#<IO:fd 10>], [], []]` where the
|
@@ -90,11 +97,11 @@ module Puma
|
|
90
97
|
# to the `@ready` IO object. For example: `[#<IO:fd 10>, #<Puma::Client:0x3fdc1103bee8 @ready=false>]`.
|
91
98
|
#
|
92
99
|
# Since the `Puma::Client` in this example has data that has not been read yet,
|
93
|
-
# the `
|
100
|
+
# the `NIO::Selector#select` is immediately able to "wake" and read from the `Puma::Client`. At this point the
|
94
101
|
# `ready` output looks like this: `[[#<Puma::Client:0x3fdc1103bee8 @ready=false>], [], []]`.
|
95
102
|
#
|
96
103
|
# Each element in the first entry is iterated over. The `Puma::Client` object is not
|
97
|
-
# 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
|
98
105
|
# the `Puma::Client#try_to_finish` method. If the full request has been sent,
|
99
106
|
# then the request is passed off to the `@app_pool` thread pool so that a "worker thread"
|
100
107
|
# can pick up the request and begin to execute application logic. This is done
|
@@ -102,56 +109,93 @@ module Puma
|
|
102
109
|
#
|
103
110
|
# If the request body is not present then nothing will happen, and the loop will iterate
|
104
111
|
# again. When the client sends more data to the socket the `Puma::Client` object will
|
105
|
-
# wake up the `
|
112
|
+
# wake up the `NIO::Selector#select` and it can again be checked to see if it's ready to be
|
106
113
|
# passed to the thread pool.
|
107
114
|
#
|
108
115
|
# ## Time Out Case
|
109
116
|
#
|
110
|
-
# 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
|
111
118
|
# periodically "time out" of the sleep. One of the functions of this is to check for
|
112
119
|
# any requests that have "timed out". At the end of the loop it's checked to see if
|
113
|
-
# the first element in the `@timeout` array has exceed
|
114
|
-
# the client object is removed from the timeout
|
115
|
-
# 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
|
116
123
|
# that watches for new data.
|
117
124
|
#
|
118
125
|
# This behavior loops until all the objects that have timed out have been removed.
|
119
126
|
#
|
120
|
-
# 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
|
121
128
|
# will be set to be equal to the amount of time it will take for the next timeout to occur.
|
122
129
|
# This calculation happens in `calculate_sleep`.
|
123
130
|
def run_internal
|
124
|
-
|
131
|
+
monitors = @monitors
|
132
|
+
selector = @selector
|
125
133
|
|
126
134
|
while true
|
127
135
|
begin
|
128
|
-
ready =
|
136
|
+
ready = selector.select @sleep_for
|
129
137
|
rescue IOError => e
|
130
138
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
131
|
-
if
|
139
|
+
if monitors.any? { |mon| mon.value.closed? }
|
132
140
|
STDERR.puts "Error in select: #{e.message} (#{e.class})"
|
133
141
|
STDERR.puts e.backtrace
|
134
|
-
|
142
|
+
|
143
|
+
monitors.reject! do |mon|
|
144
|
+
if mon.value.closed?
|
145
|
+
selector.deregister mon.value
|
146
|
+
true
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
135
150
|
retry
|
136
151
|
else
|
137
152
|
raise
|
138
153
|
end
|
139
154
|
end
|
140
155
|
|
141
|
-
if ready
|
142
|
-
|
143
|
-
if
|
156
|
+
if ready
|
157
|
+
ready.each do |mon|
|
158
|
+
if mon.value == @ready
|
144
159
|
@mutex.synchronize do
|
145
160
|
case @ready.read(1)
|
146
161
|
when "*"
|
147
|
-
|
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
|
148
183
|
@input.clear
|
184
|
+
|
185
|
+
@timeouts.sort! { |a,b| a.value.timeout_at <=> b.value.timeout_at }
|
186
|
+
calculate_sleep
|
149
187
|
when "c"
|
150
|
-
|
151
|
-
if
|
188
|
+
monitors.reject! do |submon|
|
189
|
+
if submon.value == @ready
|
152
190
|
false
|
153
191
|
else
|
154
|
-
|
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
|
155
199
|
true
|
156
200
|
end
|
157
201
|
end
|
@@ -160,40 +204,48 @@ module Puma
|
|
160
204
|
end
|
161
205
|
end
|
162
206
|
else
|
207
|
+
c = mon.value
|
208
|
+
|
163
209
|
# We have to be sure to remove it from the timeout
|
164
210
|
# list or we'll accidentally close the socket when
|
165
211
|
# it's in use!
|
166
212
|
if c.timeout_at
|
167
213
|
@mutex.synchronize do
|
168
|
-
@timeouts.delete
|
214
|
+
@timeouts.delete mon
|
169
215
|
end
|
170
216
|
end
|
171
217
|
|
172
218
|
begin
|
173
219
|
if c.try_to_finish
|
174
220
|
@app_pool << c
|
175
|
-
|
221
|
+
clear_monitor mon
|
176
222
|
end
|
177
223
|
|
178
224
|
# Don't report these to the lowlevel_error handler, otherwise
|
179
225
|
# will be flooding them with errors when persistent connections
|
180
226
|
# are closed.
|
181
227
|
rescue ConnectionError
|
182
|
-
c.
|
228
|
+
c.write_error(500)
|
183
229
|
c.close
|
184
230
|
|
185
|
-
|
231
|
+
clear_monitor mon
|
186
232
|
|
187
233
|
# SSL handshake failure
|
188
234
|
rescue MiniSSL::SSLError => e
|
189
235
|
@server.lowlevel_error(e, c.env)
|
190
236
|
|
191
237
|
ssl_socket = c.io
|
192
|
-
|
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
|
+
|
193
245
|
cert = ssl_socket.peercert
|
194
246
|
|
195
247
|
c.close
|
196
|
-
|
248
|
+
clear_monitor mon
|
197
249
|
|
198
250
|
@events.ssl_error @server, addr, cert, e
|
199
251
|
|
@@ -201,19 +253,19 @@ module Puma
|
|
201
253
|
rescue HttpParserError => e
|
202
254
|
@server.lowlevel_error(e, c.env)
|
203
255
|
|
204
|
-
c.
|
256
|
+
c.write_error(400)
|
205
257
|
c.close
|
206
258
|
|
207
|
-
|
259
|
+
clear_monitor mon
|
208
260
|
|
209
261
|
@events.parse_error @server, c.env, e
|
210
262
|
rescue StandardError => e
|
211
263
|
@server.lowlevel_error(e, c.env)
|
212
264
|
|
213
|
-
c.
|
265
|
+
c.write_error(500)
|
214
266
|
c.close
|
215
267
|
|
216
|
-
|
268
|
+
clear_monitor mon
|
217
269
|
end
|
218
270
|
end
|
219
271
|
end
|
@@ -223,11 +275,13 @@ module Puma
|
|
223
275
|
@mutex.synchronize do
|
224
276
|
now = Time.now
|
225
277
|
|
226
|
-
while @timeouts.first.timeout_at < now
|
227
|
-
|
228
|
-
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
|
229
282
|
c.close
|
230
|
-
|
283
|
+
|
284
|
+
clear_monitor mon
|
231
285
|
|
232
286
|
break if @timeouts.empty?
|
233
287
|
end
|
@@ -238,6 +292,11 @@ module Puma
|
|
238
292
|
end
|
239
293
|
end
|
240
294
|
|
295
|
+
def clear_monitor(mon)
|
296
|
+
@selector.deregister mon.value
|
297
|
+
@monitors.delete mon
|
298
|
+
end
|
299
|
+
|
241
300
|
public
|
242
301
|
|
243
302
|
def run
|
@@ -249,6 +308,7 @@ module Puma
|
|
249
308
|
|
250
309
|
def run_in_thread
|
251
310
|
@thread = Thread.new do
|
311
|
+
Puma.set_thread_name "reactor"
|
252
312
|
begin
|
253
313
|
run_internal
|
254
314
|
rescue StandardError => e
|
@@ -262,7 +322,7 @@ module Puma
|
|
262
322
|
end
|
263
323
|
end
|
264
324
|
|
265
|
-
# The `calculate_sleep` sets the value that the `
|
325
|
+
# The `calculate_sleep` sets the value that the `NIO::Selector#select` will
|
266
326
|
# sleep for in the main reactor loop when no sockets are being written to.
|
267
327
|
#
|
268
328
|
# The values kept in `@timeouts` are sorted so that the first timeout
|
@@ -276,7 +336,7 @@ module Puma
|
|
276
336
|
if @timeouts.empty?
|
277
337
|
@sleep_for = DefaultSleepFor
|
278
338
|
else
|
279
|
-
diff = @timeouts.first.timeout_at.to_f - Time.now.to_f
|
339
|
+
diff = @timeouts.first.value.timeout_at.to_f - Time.now.to_f
|
280
340
|
|
281
341
|
if diff < 0.0
|
282
342
|
@sleep_for = 0
|
@@ -293,18 +353,18 @@ module Puma
|
|
293
353
|
# object.
|
294
354
|
#
|
295
355
|
# The main body of the reactor loop is in `run_internal` and it
|
296
|
-
# will sleep on `
|
297
|
-
# reactor it cannot be added directly to the `sockets`
|
298
|
-
# 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.
|
299
359
|
#
|
300
|
-
# Instead what needs to happen is that `
|
360
|
+
# Instead what needs to happen is that `NIO::Selector#select` needs to be woken up,
|
301
361
|
# the contents of `@input` added to the `sockets` array, and then
|
302
|
-
# another call to `
|
362
|
+
# another call to `NIO::Selector#select` needs to happen. Since the `Puma::Client`
|
303
363
|
# object can be read immediately, it does not block, but instead returns
|
304
364
|
# right away.
|
305
365
|
#
|
306
366
|
# This behavior is accomplished by writing to `@trigger` which wakes up
|
307
|
-
# the `
|
367
|
+
# the `NIO::Selector#select` and then there is logic to detect the value of `*`,
|
308
368
|
# pull the contents from `@input` and add them to the sockets array.
|
309
369
|
#
|
310
370
|
# If the object passed in has a timeout value in `timeout_at` then
|
@@ -315,13 +375,6 @@ module Puma
|
|
315
375
|
@mutex.synchronize do
|
316
376
|
@input << c
|
317
377
|
@trigger << "*"
|
318
|
-
|
319
|
-
if c.timeout_at
|
320
|
-
@timeouts << c
|
321
|
-
@timeouts.sort! { |a,b| a.timeout_at <=> b.timeout_at }
|
322
|
-
|
323
|
-
calculate_sleep
|
324
|
-
end
|
325
378
|
end
|
326
379
|
end
|
327
380
|
|
data/lib/puma/runner.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'puma/server'
|
4
4
|
require 'puma/const'
|
5
|
+
require 'puma/minissl/context_builder'
|
5
6
|
|
6
7
|
module Puma
|
7
8
|
# Generic class that is used by `Puma::Cluster` and `Puma::Single` to
|
@@ -14,6 +15,7 @@ module Puma
|
|
14
15
|
@options = cli.options
|
15
16
|
@app = nil
|
16
17
|
@control = nil
|
18
|
+
@started_at = Time.now
|
17
19
|
end
|
18
20
|
|
19
21
|
def daemon?
|
@@ -52,17 +54,23 @@ module Puma
|
|
52
54
|
|
53
55
|
uri = URI.parse str
|
54
56
|
|
55
|
-
app = Puma::App::Status.new @launcher
|
56
|
-
|
57
57
|
if token = @options[:control_auth_token]
|
58
|
-
|
58
|
+
token = nil if token.empty? || token == 'none'
|
59
59
|
end
|
60
60
|
|
61
|
+
app = Puma::App::Status.new @launcher, token
|
62
|
+
|
61
63
|
control = Puma::Server.new app, @launcher.events
|
62
64
|
control.min_threads = 0
|
63
65
|
control.max_threads = 1
|
64
66
|
|
65
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
|
66
74
|
when "tcp"
|
67
75
|
log "* Starting control server on #{str}"
|
68
76
|
control.add_tcp_listener uri.host, uri.port
|
data/lib/puma/server.rb
CHANGED
@@ -6,21 +6,16 @@ require 'puma/thread_pool'
|
|
6
6
|
require 'puma/const'
|
7
7
|
require 'puma/events'
|
8
8
|
require 'puma/null_io'
|
9
|
-
require 'puma/compat'
|
10
9
|
require 'puma/reactor'
|
11
10
|
require 'puma/client'
|
12
11
|
require 'puma/binder'
|
13
|
-
require 'puma/delegation'
|
14
12
|
require 'puma/accept_nonblock'
|
15
13
|
require 'puma/util'
|
16
14
|
|
17
15
|
require 'puma/puma_http11'
|
18
16
|
|
19
|
-
unless Puma.const_defined? "IOBuffer"
|
20
|
-
require 'puma/io_buffer'
|
21
|
-
end
|
22
|
-
|
23
17
|
require 'socket'
|
18
|
+
require 'forwardable'
|
24
19
|
|
25
20
|
module Puma
|
26
21
|
|
@@ -28,7 +23,7 @@ module Puma
|
|
28
23
|
#
|
29
24
|
# This class is used by the `Puma::Single` and `Puma::Cluster` classes
|
30
25
|
# to generate one or more `Puma::Server` instances capable of handling requests.
|
31
|
-
# Each Puma process will contain one `Puma::Server`
|
26
|
+
# Each Puma process will contain one `Puma::Server` instance.
|
32
27
|
#
|
33
28
|
# The `Puma::Server` instance pulls requests from the socket, adds them to a
|
34
29
|
# `Puma::Reactor` where they get eventually passed to a `Puma::ThreadPool`.
|
@@ -37,7 +32,7 @@ module Puma
|
|
37
32
|
class Server
|
38
33
|
|
39
34
|
include Puma::Const
|
40
|
-
extend
|
35
|
+
extend Forwardable
|
41
36
|
|
42
37
|
attr_reader :thread
|
43
38
|
attr_reader :events
|
@@ -79,7 +74,6 @@ module Puma
|
|
79
74
|
@first_data_timeout = options.fetch(:first_data_timeout, FIRST_DATA_TIMEOUT)
|
80
75
|
|
81
76
|
@binder = Binder.new(events)
|
82
|
-
@own_binder = true
|
83
77
|
|
84
78
|
@leak_stack_on_error = true
|
85
79
|
|
@@ -95,14 +89,10 @@ module Puma
|
|
95
89
|
|
96
90
|
attr_accessor :binder, :leak_stack_on_error, :early_hints
|
97
91
|
|
98
|
-
|
99
|
-
forward :add_ssl_listener, :@binder
|
100
|
-
forward :add_unix_listener, :@binder
|
101
|
-
forward :connected_port, :@binder
|
92
|
+
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener, :add_unix_listener, :connected_port
|
102
93
|
|
103
94
|
def inherit_binder(bind)
|
104
95
|
@binder = bind
|
105
|
-
@own_binder = false
|
106
96
|
end
|
107
97
|
|
108
98
|
def tcp_mode!
|
@@ -214,7 +204,10 @@ module Puma
|
|
214
204
|
@events.fire :state, :running
|
215
205
|
|
216
206
|
if background
|
217
|
-
@thread = Thread.new
|
207
|
+
@thread = Thread.new do
|
208
|
+
Puma.set_thread_name "server"
|
209
|
+
handle_servers_lopez_mode
|
210
|
+
end
|
218
211
|
return @thread
|
219
212
|
else
|
220
213
|
handle_servers_lopez_mode
|
@@ -270,10 +263,11 @@ module Puma
|
|
270
263
|
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
271
264
|
end
|
272
265
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
266
|
+
# Prevent can't modify frozen IOError (RuntimeError)
|
267
|
+
begin
|
268
|
+
@notify.close
|
269
|
+
rescue IOError
|
270
|
+
# no biggy
|
277
271
|
end
|
278
272
|
end
|
279
273
|
|
@@ -323,7 +317,7 @@ module Puma
|
|
323
317
|
|
324
318
|
@events.ssl_error self, addr, cert, e
|
325
319
|
rescue HttpParserError => e
|
326
|
-
client.
|
320
|
+
client.write_error(400)
|
327
321
|
client.close
|
328
322
|
|
329
323
|
@events.parse_error self, client.env, e
|
@@ -357,7 +351,10 @@ module Puma
|
|
357
351
|
@events.fire :state, :running
|
358
352
|
|
359
353
|
if background
|
360
|
-
@thread = Thread.new
|
354
|
+
@thread = Thread.new do
|
355
|
+
Puma.set_thread_name "server"
|
356
|
+
handle_servers
|
357
|
+
end
|
361
358
|
return @thread
|
362
359
|
else
|
363
360
|
handle_servers
|
@@ -398,7 +395,10 @@ module Puma
|
|
398
395
|
end
|
399
396
|
|
400
397
|
pool << client
|
401
|
-
pool.wait_until_not_full
|
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
402
|
end
|
403
403
|
rescue SystemCallError
|
404
404
|
# nothing
|
@@ -430,10 +430,6 @@ module Puma
|
|
430
430
|
ensure
|
431
431
|
@check.close
|
432
432
|
@notify.close
|
433
|
-
|
434
|
-
if @status != :restart and @own_binder
|
435
|
-
@binder.close
|
436
|
-
end
|
437
433
|
end
|
438
434
|
|
439
435
|
@events.fire :state, :done
|
@@ -487,15 +483,20 @@ module Puma
|
|
487
483
|
|
488
484
|
requests += 1
|
489
485
|
|
490
|
-
|
486
|
+
# Closing keepalive sockets after they've made a reasonable
|
487
|
+
# number of requests allows Puma to service many connections
|
488
|
+
# fairly, even when the number of concurrent connections exceeds
|
489
|
+
# the size of the threadpool. It also allows cluster mode Pumas
|
490
|
+
# to keep load evenly distributed across workers, because clients
|
491
|
+
# are randomly assigned a new worker when opening a new connection.
|
492
|
+
#
|
493
|
+
# Previously, Puma would kick connections in this conditional back
|
494
|
+
# to the reactor. However, because this causes the todo set to increase
|
495
|
+
# in size, the wait_until_full mutex would never unlock, leaving
|
496
|
+
# any additional connections unserviced.
|
497
|
+
break if requests >= MAX_FAST_INLINE
|
491
498
|
|
492
|
-
|
493
|
-
# This will mean that reset will only try to use the data it already
|
494
|
-
# has buffered and won't try to read more data. What this means is that
|
495
|
-
# every client, independent of their request speed, gets treated like a slow
|
496
|
-
# one once every MAX_FAST_INLINE requests.
|
497
|
-
check_for_more_data = false
|
498
|
-
end
|
499
|
+
check_for_more_data = @status == :run
|
499
500
|
|
500
501
|
unless client.reset(check_for_more_data)
|
501
502
|
close_socket = false
|
@@ -526,7 +527,7 @@ module Puma
|
|
526
527
|
rescue HttpParserError => e
|
527
528
|
lowlevel_error(e, client.env)
|
528
529
|
|
529
|
-
client.
|
530
|
+
client.write_error(400)
|
530
531
|
|
531
532
|
@events.parse_error self, client.env, e
|
532
533
|
|
@@ -534,7 +535,7 @@ module Puma
|
|
534
535
|
rescue StandardError => e
|
535
536
|
lowlevel_error(e, client.env)
|
536
537
|
|
537
|
-
client.
|
538
|
+
client.write_error(500)
|
538
539
|
|
539
540
|
@events.unknown_error self, e, "Read"
|
540
541
|
|
@@ -609,19 +610,26 @@ module Puma
|
|
609
610
|
end
|
610
611
|
|
611
612
|
def default_server_port(env)
|
612
|
-
|
613
|
-
|
613
|
+
if ['on', HTTPS].include?(env[HTTPS_KEY]) || env[HTTP_X_FORWARDED_PROTO].to_s[0...5] == HTTPS || env[HTTP_X_FORWARDED_SCHEME] == HTTPS || env[HTTP_X_FORWARDED_SSL] == "on"
|
614
|
+
PORT_443
|
615
|
+
else
|
616
|
+
PORT_80
|
617
|
+
end
|
614
618
|
end
|
615
619
|
|
616
|
-
#
|
617
|
-
#
|
618
|
-
#
|
619
|
-
# +
|
620
|
+
# Takes the request +req+, invokes the Rack application to construct
|
621
|
+
# the response and writes it back to +req.io+.
|
622
|
+
#
|
623
|
+
# The second parameter +lines+ is a IO-like object unique to this thread.
|
624
|
+
# This is normally an instance of Puma::IOBuffer.
|
620
625
|
#
|
621
|
-
# +
|
622
|
-
#
|
623
|
-
# it up again.
|
626
|
+
# It'll return +false+ when the connection is closed, this doesn't mean
|
627
|
+
# that the response wasn't successful.
|
624
628
|
#
|
629
|
+
# It'll return +:async+ if the connection remains open but will be handled
|
630
|
+
# elsewhere, i.e. the connection has been hijacked by the Rack application.
|
631
|
+
#
|
632
|
+
# Finally, it'll return +true+ on keep-alive connections.
|
625
633
|
def handle_request(req, lines)
|
626
634
|
env = req.env
|
627
635
|
client = req.io
|
@@ -644,24 +652,28 @@ module Puma
|
|
644
652
|
head = env[REQUEST_METHOD] == HEAD
|
645
653
|
|
646
654
|
env[RACK_INPUT] = body
|
647
|
-
env[RACK_URL_SCHEME] =
|
655
|
+
env[RACK_URL_SCHEME] = default_server_port(env) == PORT_443 ? HTTPS : HTTP
|
648
656
|
|
649
657
|
if @early_hints
|
650
658
|
env[EARLY_HINTS] = lambda { |headers|
|
651
|
-
|
659
|
+
begin
|
660
|
+
fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze
|
652
661
|
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
662
|
+
headers.each_pair do |k, vs|
|
663
|
+
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
664
|
+
vs.to_s.split(NEWLINE).each do |v|
|
665
|
+
next if possible_header_injection?(v)
|
666
|
+
fast_write client, "#{k}: #{v}\r\n"
|
667
|
+
end
|
668
|
+
else
|
669
|
+
fast_write client, "#{k}: #{vs}\r\n"
|
658
670
|
end
|
659
|
-
else
|
660
|
-
fast_write client, "#{k}: #{vs}\r\n"
|
661
671
|
end
|
662
|
-
end
|
663
672
|
|
664
|
-
|
673
|
+
fast_write client, "\r\n".freeze
|
674
|
+
rescue ConnectionError
|
675
|
+
# noop, if we lost the socket we just won't send the early hints
|
676
|
+
end
|
665
677
|
}
|
666
678
|
end
|
667
679
|
|
@@ -687,7 +699,7 @@ module Puma
|
|
687
699
|
to_add = {}
|
688
700
|
end
|
689
701
|
|
690
|
-
to_add[k.
|
702
|
+
to_add[k.tr(",", "_")] = v
|
691
703
|
end
|
692
704
|
end
|
693
705
|
|
@@ -990,6 +1002,10 @@ module Puma
|
|
990
1002
|
@events.debug "Drained #{count} additional connections."
|
991
1003
|
end
|
992
1004
|
|
1005
|
+
if @status != :restart
|
1006
|
+
@binder.close
|
1007
|
+
end
|
1008
|
+
|
993
1009
|
if @thread_pool
|
994
1010
|
if timeout = @options[:force_shutdown_after]
|
995
1011
|
@thread_pool.shutdown timeout.to_i
|