puma 4.3.12 → 5.0.0.beta1
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 +58 -41
- data/LICENSE +23 -20
- data/README.md +17 -11
- data/bin/puma-wild +0 -0
- data/docs/architecture.md +0 -0
- data/docs/deployment.md +3 -1
- 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 +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/nginx.md +0 -0
- data/docs/plugins.md +0 -0
- data/docs/restart.md +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/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +3 -10
- data/ext/puma_http11/http11_parser.c +11 -26
- data/ext/puma_http11/http11_parser.h +0 -0
- data/ext/puma_http11/http11_parser.java.rl +0 -0
- data/ext/puma_http11/http11_parser.rl +1 -3
- data/ext/puma_http11/http11_parser_common.rl +1 -1
- data/ext/puma_http11/mini_ssl.c +47 -82
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +46 -48
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +0 -0
- data/ext/puma_http11/puma_http11.c +2 -38
- data/lib/puma/accept_nonblock.rb +0 -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 +38 -78
- data/lib/puma/cluster.rb +179 -74
- data/lib/puma/commonlogger.rb +0 -0
- data/lib/puma/configuration.rb +30 -42
- data/lib/puma/const.rb +5 -8
- 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/events.rb +0 -0
- 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/context_builder.rb +0 -0
- data/lib/puma/minissl.rb +13 -8
- data/lib/puma/null_io.rb +1 -1
- data/lib/puma/plugin/tmp_restart.rb +0 -0
- data/lib/puma/plugin.rb +1 -10
- data/lib/puma/rack/builder.rb +0 -4
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +0 -0
- data/lib/puma/reactor.rb +6 -1
- data/lib/puma/runner.rb +5 -34
- data/lib/puma/server.rb +74 -206
- 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/puma/util.rb +0 -0
- data/lib/puma.rb +4 -0
- data/lib/rack/handler/puma.rb +1 -3
- data/tools/{docker/Dockerfile → Dockerfile} +0 -0
- data/tools/trickletest.rb +0 -0
- metadata +20 -24
- 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/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
|
@@ -320,10 +212,6 @@ module Puma
|
|
320
212
|
client.write_error(400)
|
321
213
|
client.close
|
322
214
|
|
323
|
-
@events.parse_error self, client.env, e
|
324
|
-
rescue HttpParserError501 => e
|
325
|
-
client.write_error(501)
|
326
|
-
client.close
|
327
215
|
@events.parse_error self, client.env, e
|
328
216
|
rescue ConnectionError, EOFError
|
329
217
|
client.close
|
@@ -335,11 +223,14 @@ module Puma
|
|
335
223
|
@reactor.add client
|
336
224
|
end
|
337
225
|
end
|
226
|
+
|
227
|
+
process_now
|
338
228
|
end
|
339
229
|
|
230
|
+
@thread_pool.out_of_band_hook = @options[:out_of_band]
|
340
231
|
@thread_pool.clean_thread_locals = @options[:clean_thread_locals]
|
341
232
|
|
342
|
-
if queue_requests
|
233
|
+
if @queue_requests
|
343
234
|
@reactor = Reactor.new self, @thread_pool
|
344
235
|
@reactor.run_in_thread
|
345
236
|
end
|
@@ -366,6 +257,7 @@ module Puma
|
|
366
257
|
end
|
367
258
|
|
368
259
|
def handle_servers
|
260
|
+
@check, @notify = Puma::Util.pipe unless @notify
|
369
261
|
begin
|
370
262
|
check = @check
|
371
263
|
sockets = [check] + @binder.ios
|
@@ -390,6 +282,10 @@ module Puma
|
|
390
282
|
break if handle_check
|
391
283
|
else
|
392
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
|
+
|
393
289
|
if io = sock.accept_nonblock
|
394
290
|
client = Client.new io, @binder.env(sock)
|
395
291
|
if remote_addr_value
|
@@ -399,10 +295,6 @@ module Puma
|
|
399
295
|
end
|
400
296
|
|
401
297
|
pool << client
|
402
|
-
busy_threads = pool.wait_until_not_full
|
403
|
-
if busy_threads == 0
|
404
|
-
@options[:out_of_band].each(&:call) if @options[:out_of_band]
|
405
|
-
end
|
406
298
|
end
|
407
299
|
rescue SystemCallError
|
408
300
|
# nothing
|
@@ -423,17 +315,20 @@ module Puma
|
|
423
315
|
|
424
316
|
@events.fire :state, @status
|
425
317
|
|
426
|
-
graceful_shutdown if @status == :stop || @status == :restart
|
427
318
|
if queue_requests
|
319
|
+
@queue_requests = false
|
428
320
|
@reactor.clear!
|
429
321
|
@reactor.shutdown
|
430
322
|
end
|
323
|
+
graceful_shutdown if @status == :stop || @status == :restart
|
431
324
|
rescue Exception => e
|
432
325
|
STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
|
433
326
|
STDERR.puts e.backtrace
|
434
327
|
ensure
|
435
|
-
@check.close
|
328
|
+
@check.close unless @check.closed? # Ruby 2.2 issue
|
436
329
|
@notify.close
|
330
|
+
@notify = nil
|
331
|
+
@check = nil
|
437
332
|
end
|
438
333
|
|
439
334
|
@events.fire :state, :done
|
@@ -480,29 +375,24 @@ module Puma
|
|
480
375
|
close_socket = false
|
481
376
|
return
|
482
377
|
when true
|
483
|
-
return unless @queue_requests
|
484
378
|
buffer.reset
|
485
379
|
|
486
380
|
ThreadPool.clean_thread_locals if clean_thread_locals
|
487
381
|
|
488
382
|
requests += 1
|
489
383
|
|
490
|
-
# Closing keepalive sockets after they've made a reasonable
|
491
|
-
# number of requests allows Puma to service many connections
|
492
|
-
# fairly, even when the number of concurrent connections exceeds
|
493
|
-
# the size of the threadpool. It also allows cluster mode Pumas
|
494
|
-
# to keep load evenly distributed across workers, because clients
|
495
|
-
# are randomly assigned a new worker when opening a new connection.
|
496
|
-
#
|
497
|
-
# Previously, Puma would kick connections in this conditional back
|
498
|
-
# to the reactor. However, because this causes the todo set to increase
|
499
|
-
# in size, the wait_until_full mutex would never unlock, leaving
|
500
|
-
# any additional connections unserviced.
|
501
|
-
break if requests >= MAX_FAST_INLINE
|
502
|
-
|
503
384
|
check_for_more_data = @status == :run
|
504
385
|
|
386
|
+
if requests >= MAX_FAST_INLINE
|
387
|
+
# This will mean that reset will only try to use the data it already
|
388
|
+
# has buffered and won't try to read more data. What this means is that
|
389
|
+
# every client, independent of their request speed, gets treated like a slow
|
390
|
+
# one once every MAX_FAST_INLINE requests.
|
391
|
+
check_for_more_data = false
|
392
|
+
end
|
393
|
+
|
505
394
|
unless client.reset(check_for_more_data)
|
395
|
+
return unless @queue_requests
|
506
396
|
close_socket = false
|
507
397
|
client.set_timeout @persistent_timeout
|
508
398
|
@reactor.add client
|
@@ -534,12 +424,7 @@ module Puma
|
|
534
424
|
client.write_error(400)
|
535
425
|
|
536
426
|
@events.parse_error self, client.env, e
|
537
|
-
rescue HttpParserError501 => e
|
538
|
-
lowlevel_error(e, client.env)
|
539
427
|
|
540
|
-
client.write_error(501)
|
541
|
-
|
542
|
-
@events.parse_error self, client.env, e
|
543
428
|
# Server error
|
544
429
|
rescue StandardError => e
|
545
430
|
lowlevel_error(e, client.env)
|
@@ -640,6 +525,8 @@ module Puma
|
|
640
525
|
#
|
641
526
|
# Finally, it'll return +true+ on keep-alive connections.
|
642
527
|
def handle_request(req, lines)
|
528
|
+
@requests_count +=1
|
529
|
+
|
643
530
|
env = req.env
|
644
531
|
client = req.io
|
645
532
|
|
@@ -686,37 +573,6 @@ module Puma
|
|
686
573
|
}
|
687
574
|
end
|
688
575
|
|
689
|
-
# Fixup any headers with , in the name to have _ now. We emit
|
690
|
-
# headers with , in them during the parse phase to avoid ambiguity
|
691
|
-
# with the - to _ conversion for critical headers. But here for
|
692
|
-
# compatibility, we'll convert them back. This code is written to
|
693
|
-
# avoid allocation in the common case (ie there are no headers
|
694
|
-
# with , in their names), that's why it has the extra conditionals.
|
695
|
-
|
696
|
-
to_delete = nil
|
697
|
-
to_add = nil
|
698
|
-
|
699
|
-
env.each do |k,v|
|
700
|
-
if k.start_with?("HTTP_") and k.include?(",") and k != "HTTP_TRANSFER,ENCODING"
|
701
|
-
if to_delete
|
702
|
-
to_delete << k
|
703
|
-
else
|
704
|
-
to_delete = [k]
|
705
|
-
end
|
706
|
-
|
707
|
-
unless to_add
|
708
|
-
to_add = {}
|
709
|
-
end
|
710
|
-
|
711
|
-
to_add[k.tr(",", "_")] = v
|
712
|
-
end
|
713
|
-
end
|
714
|
-
|
715
|
-
if to_delete
|
716
|
-
to_delete.each { |k| env.delete(k) }
|
717
|
-
env.merge! to_add
|
718
|
-
end
|
719
|
-
|
720
576
|
# A rack extension. If the app writes #call'ables to this
|
721
577
|
# array, we will invoke them when the request is done.
|
722
578
|
#
|
@@ -738,17 +594,14 @@ module Puma
|
|
738
594
|
return :async
|
739
595
|
end
|
740
596
|
rescue ThreadPool::ForceShutdown => e
|
741
|
-
@events.
|
742
|
-
@events.
|
743
|
-
|
744
|
-
status = 503
|
745
|
-
headers = {}
|
746
|
-
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"
|
747
599
|
|
600
|
+
status, headers, res_body = lowlevel_error(e, env, 503)
|
748
601
|
rescue Exception => e
|
749
602
|
@events.unknown_error self, e, "Rack app", env
|
750
603
|
|
751
|
-
status, headers, res_body = lowlevel_error(e, env)
|
604
|
+
status, headers, res_body = lowlevel_error(e, env, 500)
|
752
605
|
end
|
753
606
|
|
754
607
|
content_length = nil
|
@@ -763,10 +616,10 @@ module Puma
|
|
763
616
|
line_ending = LINE_END
|
764
617
|
colon = COLON
|
765
618
|
|
766
|
-
http_11 =
|
619
|
+
http_11 = env[HTTP_VERSION] == HTTP_11
|
620
|
+
if http_11
|
767
621
|
allow_chunked = true
|
768
622
|
keep_alive = env.fetch(HTTP_CONNECTION, "").downcase != CLOSE
|
769
|
-
include_keepalive_header = false
|
770
623
|
|
771
624
|
# An optimization. The most common response is 200, so we can
|
772
625
|
# reply with the proper 200 status without having to compute
|
@@ -780,11 +633,9 @@ module Puma
|
|
780
633
|
|
781
634
|
no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
782
635
|
end
|
783
|
-
true
|
784
636
|
else
|
785
637
|
allow_chunked = false
|
786
638
|
keep_alive = env.fetch(HTTP_CONNECTION, "").downcase == KEEP_ALIVE
|
787
|
-
include_keepalive_header = keep_alive
|
788
639
|
|
789
640
|
# Same optimization as above for HTTP/1.1
|
790
641
|
#
|
@@ -796,9 +647,12 @@ module Puma
|
|
796
647
|
|
797
648
|
no_body ||= status < 200 || STATUS_WITH_NO_ENTITY_BODY[status]
|
798
649
|
end
|
799
|
-
false
|
800
650
|
end
|
801
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
|
+
|
802
656
|
response_hijack = nil
|
803
657
|
|
804
658
|
headers.each do |k, vs|
|
@@ -825,10 +679,15 @@ module Puma
|
|
825
679
|
end
|
826
680
|
end
|
827
681
|
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
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
|
832
691
|
end
|
833
692
|
|
834
693
|
if no_body
|
@@ -882,14 +741,11 @@ module Puma
|
|
882
741
|
end
|
883
742
|
|
884
743
|
ensure
|
885
|
-
|
886
|
-
uncork_socket client
|
744
|
+
uncork_socket client
|
887
745
|
|
888
|
-
|
889
|
-
|
890
|
-
|
891
|
-
res_body.close if res_body.respond_to? :close
|
892
|
-
end
|
746
|
+
body.close
|
747
|
+
req.tempfile.unlink if req.tempfile
|
748
|
+
res_body.close if res_body.respond_to? :close
|
893
749
|
|
894
750
|
after_reply.each { |o| o.call }
|
895
751
|
end
|
@@ -958,19 +814,21 @@ module Puma
|
|
958
814
|
|
959
815
|
# A fallback rack response if +@app+ raises as exception.
|
960
816
|
#
|
961
|
-
def lowlevel_error(e, env)
|
817
|
+
def lowlevel_error(e, env, status=500)
|
962
818
|
if handler = @options[:lowlevel_error_handler]
|
963
819
|
if handler.arity == 1
|
964
820
|
return handler.call(e)
|
965
|
-
|
821
|
+
elsif handler.arity == 2
|
966
822
|
return handler.call(e, env)
|
823
|
+
else
|
824
|
+
return handler.call(e, env, status)
|
967
825
|
end
|
968
826
|
end
|
969
827
|
|
970
828
|
if @leak_stack_on_error
|
971
|
-
[
|
829
|
+
[status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{e.backtrace.join("\n")}"]]
|
972
830
|
else
|
973
|
-
[
|
831
|
+
[status, {}, ["An unhandled lowlevel error occurred. The application logs may have details.\n"]]
|
974
832
|
end
|
975
833
|
end
|
976
834
|
|
@@ -1028,6 +886,7 @@ module Puma
|
|
1028
886
|
end
|
1029
887
|
|
1030
888
|
def notify_safely(message)
|
889
|
+
@check, @notify = Puma::Util.pipe unless @notify
|
1031
890
|
begin
|
1032
891
|
@notify << message
|
1033
892
|
rescue IOError
|
@@ -1057,8 +916,9 @@ module Puma
|
|
1057
916
|
@thread.join if @thread && sync
|
1058
917
|
end
|
1059
918
|
|
1060
|
-
def begin_restart
|
919
|
+
def begin_restart(sync=false)
|
1061
920
|
notify_safely(RESTART_COMMAND)
|
921
|
+
@thread.join if @thread && sync
|
1062
922
|
end
|
1063
923
|
|
1064
924
|
def fast_write(io, str)
|
@@ -1096,5 +956,13 @@ module Puma
|
|
1096
956
|
HTTP_INJECTION_REGEX =~ header_value.to_s
|
1097
957
|
end
|
1098
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
|
1099
967
|
end
|
1100
968
|
end
|
data/lib/puma/single.rb
CHANGED
@@ -14,11 +14,9 @@ module Puma
|
|
14
14
|
# that this inherits from.
|
15
15
|
class Single < Runner
|
16
16
|
def stats
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
m = @server.max_threads || 0
|
21
|
-
%Q!{ "started_at": "#{@started_at.utc.iso8601}", "backlog": #{b}, "running": #{r}, "pool_capacity": #{t}, "max_threads": #{m} }!
|
17
|
+
{
|
18
|
+
started_at: @started_at.utc.iso8601
|
19
|
+
}.merge(@server.stats)
|
22
20
|
end
|
23
21
|
|
24
22
|
def restart
|
@@ -39,64 +37,10 @@ module Puma
|
|
39
37
|
@server.stop(true) if @server
|
40
38
|
end
|
41
39
|
|
42
|
-
def jruby_daemon?
|
43
|
-
daemon? and Puma.jruby?
|
44
|
-
end
|
45
|
-
|
46
|
-
def jruby_daemon_start
|
47
|
-
require 'puma/jruby_restart'
|
48
|
-
JRubyRestart.daemon_start(@restart_dir, @launcher.restart_args)
|
49
|
-
end
|
50
|
-
|
51
40
|
def run
|
52
|
-
already_daemon = false
|
53
|
-
|
54
|
-
if jruby_daemon?
|
55
|
-
require 'puma/jruby_restart'
|
56
|
-
|
57
|
-
if JRubyRestart.daemon?
|
58
|
-
# load and bind before redirecting IO so errors show up on stdout/stderr
|
59
|
-
load_and_bind
|
60
|
-
redirect_io
|
61
|
-
end
|
62
|
-
|
63
|
-
already_daemon = JRubyRestart.daemon_init
|
64
|
-
end
|
65
|
-
|
66
41
|
output_header "single"
|
67
42
|
|
68
|
-
|
69
|
-
if already_daemon
|
70
|
-
JRubyRestart.perm_daemonize
|
71
|
-
else
|
72
|
-
pid = nil
|
73
|
-
|
74
|
-
Signal.trap "SIGUSR2" do
|
75
|
-
log "* Started new process #{pid} as daemon..."
|
76
|
-
|
77
|
-
# Must use exit! so we don't unwind and run the ensures
|
78
|
-
# that will be run by the new child (such as deleting the
|
79
|
-
# pidfile)
|
80
|
-
exit!(true)
|
81
|
-
end
|
82
|
-
|
83
|
-
Signal.trap "SIGCHLD" do
|
84
|
-
log "! Error starting new process as daemon, exiting"
|
85
|
-
exit 1
|
86
|
-
end
|
87
|
-
|
88
|
-
jruby_daemon_start
|
89
|
-
sleep
|
90
|
-
end
|
91
|
-
else
|
92
|
-
if daemon?
|
93
|
-
log "* Daemonizing..."
|
94
|
-
Process.daemon(true)
|
95
|
-
redirect_io
|
96
|
-
end
|
97
|
-
|
98
|
-
load_and_bind
|
99
|
-
end
|
43
|
+
load_and_bind
|
100
44
|
|
101
45
|
Plugins.fire_background
|
102
46
|
|
@@ -106,10 +50,9 @@ module Puma
|
|
106
50
|
|
107
51
|
@server = server = start_server
|
108
52
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
end
|
53
|
+
|
54
|
+
log "Use Ctrl-C to stop"
|
55
|
+
redirect_io
|
113
56
|
|
114
57
|
@launcher.events.fire_on_booted!
|
115
58
|
|
data/lib/puma/state_file.rb
CHANGED
@@ -8,8 +8,11 @@ module Puma
|
|
8
8
|
@options = {}
|
9
9
|
end
|
10
10
|
|
11
|
-
def save(path)
|
12
|
-
File.
|
11
|
+
def save(path, permission = nil)
|
12
|
+
File.open(path, "w") do |file|
|
13
|
+
file.chmod(permission) if permission
|
14
|
+
file.write(YAML.dump(@options))
|
15
|
+
end
|
13
16
|
end
|
14
17
|
|
15
18
|
def load(path)
|