puma 3.11.3 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/History.md +61 -0
- data/README.md +41 -11
- data/docs/architecture.md +2 -1
- data/docs/deployment.md +24 -4
- data/docs/restart.md +5 -3
- data/docs/systemd.md +37 -9
- data/ext/puma_http11/PumaHttp11Service.java +2 -0
- data/ext/puma_http11/mini_ssl.c +42 -5
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +17 -4
- data/lib/puma.rb +8 -0
- data/lib/puma/app/status.rb +3 -2
- data/lib/puma/binder.rb +22 -10
- data/lib/puma/cli.rb +18 -7
- data/lib/puma/client.rb +54 -22
- data/lib/puma/cluster.rb +54 -15
- data/lib/puma/commonlogger.rb +2 -0
- data/lib/puma/configuration.rb +4 -1
- data/lib/puma/const.rb +8 -2
- data/lib/puma/control_cli.rb +23 -11
- data/lib/puma/convenient.rb +2 -0
- data/lib/puma/daemon_ext.rb +2 -0
- data/lib/puma/delegation.rb +2 -0
- data/lib/puma/detect.rb +2 -0
- data/lib/puma/dsl.rb +63 -11
- data/lib/puma/events.rb +2 -0
- data/lib/puma/io_buffer.rb +3 -6
- data/lib/puma/jruby_restart.rb +2 -0
- data/lib/puma/launcher.rb +15 -13
- data/lib/puma/minissl.rb +20 -4
- data/lib/puma/null_io.rb +2 -0
- data/lib/puma/plugin.rb +2 -0
- data/lib/puma/rack/builder.rb +2 -1
- data/lib/puma/reactor.rb +215 -30
- data/lib/puma/runner.rb +11 -2
- data/lib/puma/server.rb +63 -26
- data/lib/puma/single.rb +14 -3
- data/lib/puma/state_file.rb +2 -0
- data/lib/puma/tcp_logger.rb +2 -0
- data/lib/puma/thread_pool.rb +50 -5
- data/lib/puma/util.rb +2 -6
- data/lib/rack/handler/puma.rb +4 -0
- data/tools/jungle/README.md +10 -4
- data/tools/jungle/init.d/README.md +2 -0
- data/tools/jungle/init.d/puma +7 -7
- data/tools/jungle/init.d/run-puma +1 -1
- data/tools/jungle/rc.d/README.md +74 -0
- data/tools/jungle/rc.d/puma +61 -0
- data/tools/jungle/rc.d/puma.conf +10 -0
- metadata +23 -9
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/java_io_buffer.rb +0 -45
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
data/lib/puma/runner.rb
CHANGED
@@ -1,7 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'puma/server'
|
2
4
|
require 'puma/const'
|
3
5
|
|
4
6
|
module Puma
|
7
|
+
# Generic class that is used by `Puma::Cluster` and `Puma::Single` to
|
8
|
+
# serve requests. This class spawns a new instance of `Puma::Server` via
|
9
|
+
# a call to `start_server`.
|
5
10
|
class Runner
|
6
11
|
def initialize(cli, events)
|
7
12
|
@launcher = cli
|
@@ -19,6 +24,10 @@ module Puma
|
|
19
24
|
@options[:environment] == "development"
|
20
25
|
end
|
21
26
|
|
27
|
+
def test?
|
28
|
+
@options[:environment] == "test"
|
29
|
+
end
|
30
|
+
|
22
31
|
def log(str)
|
23
32
|
@events.log str
|
24
33
|
end
|
@@ -46,7 +55,7 @@ module Puma
|
|
46
55
|
app = Puma::App::Status.new @launcher
|
47
56
|
|
48
57
|
if token = @options[:control_auth_token]
|
49
|
-
app.auth_token = token unless token.empty?
|
58
|
+
app.auth_token = token unless token.empty? || token == 'none'
|
50
59
|
end
|
51
60
|
|
52
61
|
control = Puma::Server.new app, @launcher.events
|
@@ -165,7 +174,7 @@ module Puma
|
|
165
174
|
server.early_hints = true
|
166
175
|
end
|
167
176
|
|
168
|
-
unless development?
|
177
|
+
unless development? || test?
|
169
178
|
server.leak_stack_on_error = false
|
170
179
|
end
|
171
180
|
|
data/lib/puma/server.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'stringio'
|
2
4
|
|
3
5
|
require 'puma/thread_pool'
|
4
6
|
require 'puma/const'
|
5
7
|
require 'puma/events'
|
6
8
|
require 'puma/null_io'
|
7
|
-
require 'puma/compat'
|
8
9
|
require 'puma/reactor'
|
9
10
|
require 'puma/client'
|
10
11
|
require 'puma/binder'
|
@@ -14,15 +15,20 @@ require 'puma/util'
|
|
14
15
|
|
15
16
|
require 'puma/puma_http11'
|
16
17
|
|
17
|
-
unless Puma.const_defined? "IOBuffer"
|
18
|
-
require 'puma/io_buffer'
|
19
|
-
end
|
20
|
-
|
21
18
|
require 'socket'
|
22
19
|
|
23
20
|
module Puma
|
24
21
|
|
25
22
|
# The HTTP Server itself. Serves out a single Rack app.
|
23
|
+
#
|
24
|
+
# This class is used by the `Puma::Single` and `Puma::Cluster` classes
|
25
|
+
# to generate one or more `Puma::Server` instances capable of handling requests.
|
26
|
+
# Each Puma process will contain one `Puma::Server` instance.
|
27
|
+
#
|
28
|
+
# The `Puma::Server` instance pulls requests from the socket, adds them to a
|
29
|
+
# `Puma::Reactor` where they get eventually passed to a `Puma::ThreadPool`.
|
30
|
+
#
|
31
|
+
# Each `Puma::Server` will have one reactor and one thread pool.
|
26
32
|
class Server
|
27
33
|
|
28
34
|
include Puma::Const
|
@@ -68,7 +74,6 @@ module Puma
|
|
68
74
|
@first_data_timeout = options.fetch(:first_data_timeout, FIRST_DATA_TIMEOUT)
|
69
75
|
|
70
76
|
@binder = Binder.new(events)
|
71
|
-
@own_binder = true
|
72
77
|
|
73
78
|
@leak_stack_on_error = true
|
74
79
|
|
@@ -91,7 +96,6 @@ module Puma
|
|
91
96
|
|
92
97
|
def inherit_binder(bind)
|
93
98
|
@binder = bind
|
94
|
-
@own_binder = false
|
95
99
|
end
|
96
100
|
|
97
101
|
def tcp_mode!
|
@@ -159,6 +163,18 @@ module Puma
|
|
159
163
|
@thread_pool and @thread_pool.spawned
|
160
164
|
end
|
161
165
|
|
166
|
+
|
167
|
+
# This number represents the number of requests that
|
168
|
+
# the server is capable of taking right now.
|
169
|
+
#
|
170
|
+
# For example if the number is 5 then it means
|
171
|
+
# there are 5 threads sitting idle ready to take
|
172
|
+
# a request. If one request comes in, then the
|
173
|
+
# value would be 4 until it finishes processing.
|
174
|
+
def pool_capacity
|
175
|
+
@thread_pool and @thread_pool.pool_capacity
|
176
|
+
end
|
177
|
+
|
162
178
|
# Lopez Mode == raw tcp apps
|
163
179
|
|
164
180
|
def run_lopez_mode(background=true)
|
@@ -220,7 +236,11 @@ module Puma
|
|
220
236
|
# nothing
|
221
237
|
rescue Errno::ECONNABORTED
|
222
238
|
# client closed the socket even before accept
|
223
|
-
|
239
|
+
begin
|
240
|
+
io.close
|
241
|
+
rescue
|
242
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
243
|
+
end
|
224
244
|
end
|
225
245
|
end
|
226
246
|
end
|
@@ -237,11 +257,17 @@ module Puma
|
|
237
257
|
STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
|
238
258
|
STDERR.puts e.backtrace
|
239
259
|
ensure
|
240
|
-
|
241
|
-
|
260
|
+
begin
|
261
|
+
@check.close
|
262
|
+
rescue
|
263
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
264
|
+
end
|
242
265
|
|
243
|
-
|
244
|
-
|
266
|
+
# Prevent can't modify frozen IOError (RuntimeError)
|
267
|
+
begin
|
268
|
+
@notify.close
|
269
|
+
rescue IOError
|
270
|
+
# no biggy
|
245
271
|
end
|
246
272
|
end
|
247
273
|
|
@@ -366,13 +392,20 @@ module Puma
|
|
366
392
|
end
|
367
393
|
|
368
394
|
pool << client
|
369
|
-
pool.wait_until_not_full
|
395
|
+
busy_threads = pool.wait_until_not_full
|
396
|
+
if busy_threads == 0
|
397
|
+
@options[:out_of_band].each(&:call) if @options[:out_of_band]
|
398
|
+
end
|
370
399
|
end
|
371
400
|
rescue SystemCallError
|
372
401
|
# nothing
|
373
402
|
rescue Errno::ECONNABORTED
|
374
403
|
# client closed the socket even before accept
|
375
|
-
|
404
|
+
begin
|
405
|
+
io.close
|
406
|
+
rescue
|
407
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
408
|
+
end
|
376
409
|
end
|
377
410
|
end
|
378
411
|
end
|
@@ -394,10 +427,6 @@ module Puma
|
|
394
427
|
ensure
|
395
428
|
@check.close
|
396
429
|
@notify.close
|
397
|
-
|
398
|
-
if @status != :restart and @own_binder
|
399
|
-
@binder.close
|
400
|
-
end
|
401
430
|
end
|
402
431
|
|
403
432
|
@events.fire :state, :done
|
@@ -563,15 +592,19 @@ module Puma
|
|
563
592
|
env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80
|
564
593
|
end
|
565
594
|
|
566
|
-
#
|
567
|
-
#
|
568
|
-
#
|
569
|
-
# +
|
595
|
+
# Takes the request +req+, invokes the Rack application to construct
|
596
|
+
# the response and writes it back to +req.io+.
|
597
|
+
#
|
598
|
+
# The second parameter +lines+ is a IO-like object unique to this thread.
|
599
|
+
# This is normally an instance of Puma::IOBuffer.
|
600
|
+
#
|
601
|
+
# It'll return +false+ when the connection is closed, this doesn't mean
|
602
|
+
# that the response wasn't successful.
|
570
603
|
#
|
571
|
-
# +
|
572
|
-
#
|
573
|
-
# it up again.
|
604
|
+
# It'll return +:async+ if the connection remains open but will be handled
|
605
|
+
# elsewhere, i.e. the connection has been hijacked by the Rack application.
|
574
606
|
#
|
607
|
+
# Finally, it'll return +true+ on keep-alive connections.
|
575
608
|
def handle_request(req, lines)
|
576
609
|
env = req.env
|
577
610
|
client = req.io
|
@@ -606,7 +639,7 @@ module Puma
|
|
606
639
|
fast_write client, "#{k}: #{v}\r\n"
|
607
640
|
end
|
608
641
|
else
|
609
|
-
fast_write client, "#{k}: #{
|
642
|
+
fast_write client, "#{k}: #{vs}\r\n"
|
610
643
|
end
|
611
644
|
end
|
612
645
|
|
@@ -906,6 +939,10 @@ module Puma
|
|
906
939
|
@events.debug "Drained #{count} additional connections."
|
907
940
|
end
|
908
941
|
|
942
|
+
if @status != :restart
|
943
|
+
@binder.close
|
944
|
+
end
|
945
|
+
|
909
946
|
if @thread_pool
|
910
947
|
if timeout = @options[:force_shutdown_after]
|
911
948
|
@thread_pool.shutdown timeout.to_i
|
data/lib/puma/single.rb
CHANGED
@@ -1,13 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'puma/runner'
|
2
4
|
require 'puma/detect'
|
3
5
|
require 'puma/plugin'
|
4
6
|
|
5
7
|
module Puma
|
8
|
+
# This class is instantiated by the `Puma::Launcher` and used
|
9
|
+
# to boot and serve a Ruby application when no puma "workers" are needed
|
10
|
+
# i.e. only using "threaded" mode. For example `$ puma -t 1:5`
|
11
|
+
#
|
12
|
+
# At the core of this class is running an instance of `Puma::Server` which
|
13
|
+
# gets created via the `start_server` method from the `Puma::Runner` class
|
14
|
+
# that this inherits from.
|
6
15
|
class Single < Runner
|
7
16
|
def stats
|
8
17
|
b = @server.backlog || 0
|
9
18
|
r = @server.running || 0
|
10
|
-
|
19
|
+
t = @server.pool_capacity || 0
|
20
|
+
m = @server.max_threads || 0
|
21
|
+
%Q!{ "backlog": #{b}, "running": #{r}, "pool_capacity": #{t}, "max_threads": #{m} }!
|
11
22
|
end
|
12
23
|
|
13
24
|
def restart
|
@@ -15,7 +26,7 @@ module Puma
|
|
15
26
|
end
|
16
27
|
|
17
28
|
def stop
|
18
|
-
@server.stop
|
29
|
+
@server.stop(false) if @server
|
19
30
|
end
|
20
31
|
|
21
32
|
def halt
|
@@ -25,7 +36,7 @@ module Puma
|
|
25
36
|
def stop_blocked
|
26
37
|
log "- Gracefully stopping, waiting for requests to finish"
|
27
38
|
@control.stop(true) if @control
|
28
|
-
@server.stop(true)
|
39
|
+
@server.stop(true) if @server
|
29
40
|
end
|
30
41
|
|
31
42
|
def jruby_daemon?
|
data/lib/puma/state_file.rb
CHANGED
data/lib/puma/tcp_logger.rb
CHANGED
data/lib/puma/thread_pool.rb
CHANGED
@@ -1,8 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'thread'
|
2
4
|
|
3
5
|
module Puma
|
4
|
-
# A simple thread pool management object.
|
6
|
+
# Internal Docs for A simple thread pool management object.
|
7
|
+
#
|
8
|
+
# Each Puma "worker" has a thread pool to process requests.
|
9
|
+
#
|
10
|
+
# First a connection to a client is made in `Puma::Server`. It is wrapped in a
|
11
|
+
# `Puma::Client` instance and then passed to the `Puma::Reactor` to ensure
|
12
|
+
# the whole request is buffered into memory. Once the request is ready, it is passed into
|
13
|
+
# a thread pool via the `Puma::ThreadPool#<<` operator where it is stored in a `@todo` array.
|
5
14
|
#
|
15
|
+
# Each thread in the pool has an internal loop where it pulls a request from the `@todo` array
|
16
|
+
# and proceses it.
|
6
17
|
class ThreadPool
|
7
18
|
class ForceShutdown < RuntimeError
|
8
19
|
end
|
@@ -49,7 +60,7 @@ module Puma
|
|
49
60
|
@clean_thread_locals = false
|
50
61
|
end
|
51
62
|
|
52
|
-
attr_reader :spawned, :trim_requested
|
63
|
+
attr_reader :spawned, :trim_requested, :waiting
|
53
64
|
attr_accessor :clean_thread_locals
|
54
65
|
|
55
66
|
def self.clean_thread_locals
|
@@ -64,6 +75,10 @@ module Puma
|
|
64
75
|
@mutex.synchronize { @todo.size }
|
65
76
|
end
|
66
77
|
|
78
|
+
def pool_capacity
|
79
|
+
waiting + (@max - spawned)
|
80
|
+
end
|
81
|
+
|
67
82
|
# :nodoc:
|
68
83
|
#
|
69
84
|
# Must be called with @mutex held!
|
@@ -153,16 +168,46 @@ module Puma
|
|
153
168
|
end
|
154
169
|
end
|
155
170
|
|
171
|
+
# This method is used by `Puma::Server` to let the server know when
|
172
|
+
# the thread pool can pull more requests from the socket and
|
173
|
+
# pass to the reactor.
|
174
|
+
#
|
175
|
+
# The general idea is that the thread pool can only work on a fixed
|
176
|
+
# number of requests at the same time. If it is already processing that
|
177
|
+
# number of requests then it is at capacity. If another Puma process has
|
178
|
+
# spare capacity, then the request can be left on the socket so the other
|
179
|
+
# worker can pick it up and process it.
|
180
|
+
#
|
181
|
+
# For example: if there are 5 threads, but only 4 working on
|
182
|
+
# requests, this method will not wait and the `Puma::Server`
|
183
|
+
# can pull a request right away.
|
184
|
+
#
|
185
|
+
# If there are 5 threads and all 5 of them are busy, then it will
|
186
|
+
# pause here, and wait until the `not_full` condition variable is
|
187
|
+
# signaled, usually this indicates that a request has been processed.
|
188
|
+
#
|
189
|
+
# It's important to note that even though the server might accept another
|
190
|
+
# request, it might not be added to the `@todo` array right away.
|
191
|
+
# For example if a slow client has only sent a header, but not a body
|
192
|
+
# then the `@todo` array would stay the same size as the reactor works
|
193
|
+
# to try to buffer the request. In tha scenario the next call to this
|
194
|
+
# method would not block and another request would be added into the reactor
|
195
|
+
# by the server. This would continue until a fully bufferend request
|
196
|
+
# makes it through the reactor and can then be processed by the thread pool.
|
197
|
+
#
|
198
|
+
# Returns the current number of busy threads, or +nil+ if shutting down.
|
199
|
+
#
|
156
200
|
def wait_until_not_full
|
157
201
|
@mutex.synchronize do
|
158
202
|
while true
|
159
203
|
return if @shutdown
|
160
|
-
return if @waiting > 0
|
161
204
|
|
162
205
|
# If we can still spin up new threads and there
|
163
|
-
# is work queued
|
206
|
+
# is work queued that cannot be handled by waiting
|
207
|
+
# threads, then accept more work until we would
|
164
208
|
# spin up the max number of threads.
|
165
|
-
|
209
|
+
busy_threads = @spawned - @waiting + @todo.size
|
210
|
+
return busy_threads if @max > busy_threads
|
166
211
|
|
167
212
|
@not_full.wait @mutex
|
168
213
|
end
|
data/lib/puma/util.rb
CHANGED
@@ -1,10 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
require 'puma/rack/backports/uri/common_193'
|
5
|
-
else
|
6
|
-
require 'uri/common'
|
7
|
-
end
|
3
|
+
require 'uri/common'
|
8
4
|
|
9
5
|
module Puma
|
10
6
|
module Util
|
data/lib/rack/handler/puma.rb
CHANGED
@@ -9,6 +9,7 @@ module Rack
|
|
9
9
|
}
|
10
10
|
|
11
11
|
def self.config(app, options = {})
|
12
|
+
require 'puma'
|
12
13
|
require 'puma/configuration'
|
13
14
|
require 'puma/events'
|
14
15
|
require 'puma/launcher'
|
@@ -48,6 +49,9 @@ module Rack
|
|
48
49
|
self.set_host_port_to_config(host, port, user_config)
|
49
50
|
end
|
50
51
|
|
52
|
+
if default_options[:Host]
|
53
|
+
file_config.set_default_host(default_options[:Host])
|
54
|
+
end
|
51
55
|
self.set_host_port_to_config(default_options[:Host], default_options[:Port], default_config)
|
52
56
|
|
53
57
|
user_config.app app
|
data/tools/jungle/README.md
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
# Puma as a service
|
2
2
|
|
3
|
-
## Init.d
|
4
|
-
|
5
|
-
See `/tools/jungle/init.d` for tools to use with init.d and start-stop-daemon.
|
6
|
-
|
7
3
|
## Upstart
|
8
4
|
|
9
5
|
See `/tools/jungle/upstart` for Ubuntu's upstart scripts.
|
@@ -11,3 +7,13 @@ See `/tools/jungle/upstart` for Ubuntu's upstart scripts.
|
|
11
7
|
## Systemd
|
12
8
|
|
13
9
|
See [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md).
|
10
|
+
|
11
|
+
## Init.d
|
12
|
+
|
13
|
+
Deprecatation Warning : `init.d` was replaced by `systemd` since Debian 8 and Ubuntu 16.04, you should look into [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md) unless you are on an older OS.
|
14
|
+
|
15
|
+
See `/tools/jungle/init.d` for tools to use with init.d and start-stop-daemon.
|
16
|
+
|
17
|
+
## rc.d
|
18
|
+
|
19
|
+
See `/tools/jungle/rc.d` for FreeBSD's rc.d scripts
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# Puma daemon service
|
2
2
|
|
3
|
+
Deprecatation Warning : `init.d` was replaced by `systemd` since Debian 8 and Ubuntu 16.04, you should look into [/docs/systemd](https://github.com/puma/puma/blob/master/docs/systemd.md) unless you are on an older OS.
|
4
|
+
|
3
5
|
Init script to manage multiple Puma servers on the same box using start-stop-daemon.
|
4
6
|
|
5
7
|
## Installation
|
data/tools/jungle/init.d/puma
CHANGED
@@ -47,11 +47,11 @@ do_start_one() {
|
|
47
47
|
PIDFILE=$1/tmp/puma/pid
|
48
48
|
if [ -e $PIDFILE ]; then
|
49
49
|
PID=`cat $PIDFILE`
|
50
|
-
# If the puma
|
51
|
-
if
|
52
|
-
do_start_one_do $1
|
53
|
-
else
|
50
|
+
# If the puma is running, restart it, otherwise run it.
|
51
|
+
if ps -p $PID > /dev/null; then
|
54
52
|
do_restart_one $1
|
53
|
+
else
|
54
|
+
do_start_one_do $1
|
55
55
|
fi
|
56
56
|
else
|
57
57
|
do_start_one_do $1
|
@@ -105,9 +105,7 @@ do_stop_one() {
|
|
105
105
|
STATEFILE=$1/tmp/puma/state
|
106
106
|
if [ -e $PIDFILE ]; then
|
107
107
|
PID=`cat $PIDFILE`
|
108
|
-
if
|
109
|
-
log_daemon_msg "---> Puma $1 isn't running."
|
110
|
-
else
|
108
|
+
if ps -p $PID > /dev/null; then
|
111
109
|
log_daemon_msg "---> About to kill PID `cat $PIDFILE`"
|
112
110
|
if [ "$USE_LOCAL_BUNDLE" -eq 1 ]; then
|
113
111
|
cd $1 && bundle exec pumactl --state $STATEFILE stop
|
@@ -116,6 +114,8 @@ do_stop_one() {
|
|
116
114
|
fi
|
117
115
|
# Many daemons don't delete their pidfiles when they exit.
|
118
116
|
rm -f $PIDFILE $STATEFILE
|
117
|
+
else
|
118
|
+
log_daemon_msg "---> Puma $1 isn't running."
|
119
119
|
fi
|
120
120
|
else
|
121
121
|
log_daemon_msg "---> No puma here..."
|