puma 3.12.6 → 4.3.5
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 +122 -2
- 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 +37 -62
- data/ext/puma_http11/http11_parser.java.rl +21 -37
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- 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 +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 +2 -0
- data/lib/puma.rb +8 -0
- data/lib/puma/accept_nonblock.rb +7 -1
- data/lib/puma/app/status.rb +35 -29
- data/lib/puma/binder.rb +38 -60
- data/lib/puma/cli.rb +4 -0
- data/lib/puma/client.rb +229 -207
- 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.rb +35 -17
- data/lib/puma/minissl/context_builder.rb +76 -0
- data/lib/puma/plugin.rb +5 -2
- data/lib/puma/plugin/tmp_restart.rb +2 -0
- 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 +59 -48
- data/lib/puma/single.rb +3 -3
- data/lib/puma/thread_pool.rb +15 -33
- data/lib/puma/util.rb +1 -6
- 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 +21 -8
- 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/rack/builder.rb
CHANGED
data/lib/puma/rack/urlmap.rb
CHANGED
data/lib/puma/rack_default.rb
CHANGED
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
|
@@ -526,7 +522,7 @@ module Puma
|
|
526
522
|
rescue HttpParserError => e
|
527
523
|
lowlevel_error(e, client.env)
|
528
524
|
|
529
|
-
client.
|
525
|
+
client.write_error(400)
|
530
526
|
|
531
527
|
@events.parse_error self, client.env, e
|
532
528
|
|
@@ -534,7 +530,7 @@ module Puma
|
|
534
530
|
rescue StandardError => e
|
535
531
|
lowlevel_error(e, client.env)
|
536
532
|
|
537
|
-
client.
|
533
|
+
client.write_error(500)
|
538
534
|
|
539
535
|
@events.unknown_error self, e, "Read"
|
540
536
|
|
@@ -609,19 +605,26 @@ module Puma
|
|
609
605
|
end
|
610
606
|
|
611
607
|
def default_server_port(env)
|
612
|
-
|
613
|
-
|
608
|
+
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"
|
609
|
+
PORT_443
|
610
|
+
else
|
611
|
+
PORT_80
|
612
|
+
end
|
614
613
|
end
|
615
614
|
|
616
|
-
#
|
617
|
-
#
|
618
|
-
#
|
619
|
-
# +
|
615
|
+
# Takes the request +req+, invokes the Rack application to construct
|
616
|
+
# the response and writes it back to +req.io+.
|
617
|
+
#
|
618
|
+
# The second parameter +lines+ is a IO-like object unique to this thread.
|
619
|
+
# This is normally an instance of Puma::IOBuffer.
|
620
|
+
#
|
621
|
+
# It'll return +false+ when the connection is closed, this doesn't mean
|
622
|
+
# that the response wasn't successful.
|
620
623
|
#
|
621
|
-
# +
|
622
|
-
#
|
623
|
-
# it up again.
|
624
|
+
# It'll return +:async+ if the connection remains open but will be handled
|
625
|
+
# elsewhere, i.e. the connection has been hijacked by the Rack application.
|
624
626
|
#
|
627
|
+
# Finally, it'll return +true+ on keep-alive connections.
|
625
628
|
def handle_request(req, lines)
|
626
629
|
env = req.env
|
627
630
|
client = req.io
|
@@ -644,24 +647,28 @@ module Puma
|
|
644
647
|
head = env[REQUEST_METHOD] == HEAD
|
645
648
|
|
646
649
|
env[RACK_INPUT] = body
|
647
|
-
env[RACK_URL_SCHEME] =
|
650
|
+
env[RACK_URL_SCHEME] = default_server_port(env) == PORT_443 ? HTTPS : HTTP
|
648
651
|
|
649
652
|
if @early_hints
|
650
653
|
env[EARLY_HINTS] = lambda { |headers|
|
651
|
-
|
654
|
+
begin
|
655
|
+
fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze
|
652
656
|
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
657
|
+
headers.each_pair do |k, vs|
|
658
|
+
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
659
|
+
vs.to_s.split(NEWLINE).each do |v|
|
660
|
+
next if possible_header_injection?(v)
|
661
|
+
fast_write client, "#{k}: #{v}\r\n"
|
662
|
+
end
|
663
|
+
else
|
664
|
+
fast_write client, "#{k}: #{vs}\r\n"
|
658
665
|
end
|
659
|
-
else
|
660
|
-
fast_write client, "#{k}: #{vs}\r\n"
|
661
666
|
end
|
662
|
-
end
|
663
667
|
|
664
|
-
|
668
|
+
fast_write client, "\r\n".freeze
|
669
|
+
rescue ConnectionError
|
670
|
+
# noop, if we lost the socket we just won't send the early hints
|
671
|
+
end
|
665
672
|
}
|
666
673
|
end
|
667
674
|
|
@@ -990,6 +997,10 @@ module Puma
|
|
990
997
|
@events.debug "Drained #{count} additional connections."
|
991
998
|
end
|
992
999
|
|
1000
|
+
if @status != :restart
|
1001
|
+
@binder.close
|
1002
|
+
end
|
1003
|
+
|
993
1004
|
if @thread_pool
|
994
1005
|
if timeout = @options[:force_shutdown_after]
|
995
1006
|
@thread_pool.shutdown timeout.to_i
|