puma 3.0.0.rc1 → 3.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 +4 -4
- data/Gemfile +1 -1
- data/History.txt +50 -0
- data/Manifest.txt +0 -1
- data/README.md +21 -12
- data/ext/puma_http11/io_buffer.c +1 -1
- data/ext/puma_http11/mini_ssl.c +7 -1
- data/lib/puma/binder.rb +8 -2
- data/lib/puma/cli.rb +5 -1
- data/lib/puma/client.rb +4 -0
- data/lib/puma/cluster.rb +47 -34
- data/lib/puma/configuration.rb +3 -3
- data/lib/puma/const.rb +2 -2
- data/lib/puma/control_cli.rb +1 -1
- data/lib/puma/dsl.rb +8 -2
- data/lib/puma/server.rb +81 -48
- data/lib/puma/state_file.rb +2 -0
- data/lib/puma/thread_pool.rb +8 -3
- data/lib/rack/handler/puma.rb +11 -3
- metadata +4 -5
- data/COPYING +0 -55
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3aaeb164dd6e399e4155a81117c4ad6cde4a0420
|
4
|
+
data.tar.gz: d040b98c0015ce89e4d5c7d793a488eace1d6422
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa50dc14fdac0d976b3f0e480039f301aad9c09cd898eb0fc8c901f900b5f1167ff50818d10c68601c8a70122a7ae865b75f5702c83425cd6005ecca11862236
|
7
|
+
data.tar.gz: e2da1eb6e42bdad2925391be65b152af26ac784f70dcbc1c4074cd0a269eff58e6b2cc22572b60febc46777a488bfca402b71ed0f1dcbcbe6655b561d7460543
|
data/Gemfile
CHANGED
data/History.txt
CHANGED
@@ -1,3 +1,53 @@
|
|
1
|
+
=== 3.0.0 / 2016-02-25
|
2
|
+
|
3
|
+
* 2 major changes:
|
4
|
+
|
5
|
+
* Ruby pre-2.0 is no longer supported. We'll do our best to not add
|
6
|
+
features that break those rubies but will no longer be testing
|
7
|
+
with them.
|
8
|
+
* Don't log requests by default. Fixes #852
|
9
|
+
|
10
|
+
* 2 major features:
|
11
|
+
|
12
|
+
* Plugin support! Plugins can interact with configuration as well
|
13
|
+
as provide augment server functionality!
|
14
|
+
* Experimental env['async.callback'] support
|
15
|
+
|
16
|
+
* 4 minor features:
|
17
|
+
|
18
|
+
* Listen to unix socket with provided backlog if any
|
19
|
+
* Improves the clustered stats to report worker stats
|
20
|
+
* Pass the env to the lowlevel_error handler. Fixes #854
|
21
|
+
* Treat path-like hosts as unix sockets. Fixes #824
|
22
|
+
|
23
|
+
* 5 bug fixes:
|
24
|
+
|
25
|
+
* Clean thread locals when using keepalive. Fixes #823
|
26
|
+
* Cleanup compiler warnings. Fixes #815
|
27
|
+
* Expose closed? for use by the reactor. Fixes #835
|
28
|
+
* Move signal handlers to separate method to prevent space leak. Fixes #798
|
29
|
+
* Signal not full on worker exit #876
|
30
|
+
|
31
|
+
* 5 doc fixes:
|
32
|
+
|
33
|
+
* Update README.md with various grammar fixes
|
34
|
+
* Use newest version of Minitest
|
35
|
+
* Add directory configuration docs, fix typo [ci skip]
|
36
|
+
* Remove old COPYING notice. Fixes #849
|
37
|
+
|
38
|
+
* 10 merged PRs:
|
39
|
+
|
40
|
+
* Merge pull request #871 from deepj/travis
|
41
|
+
* Merge pull request #874 from wallclockbuilder/master
|
42
|
+
* Merge pull request #883 from dadah89/igor/trim_only_worker
|
43
|
+
* Merge pull request #884 from uistudio/async-callback
|
44
|
+
* Merge pull request #888 from mlarraz/tick_minitest
|
45
|
+
* Merge pull request #890 from todd/directory_docs
|
46
|
+
* Merge pull request #891 from ctaintor/improve_clustered_status
|
47
|
+
* Merge pull request #893 from spastorino/add_missing_require
|
48
|
+
* Merge pull request #897 from zendesk/master
|
49
|
+
* Merge pull request #899 from kch/kch-readme-fixes
|
50
|
+
|
1
51
|
=== 2.16.0 / 2016-01-27
|
2
52
|
|
3
53
|
* 7 minor features:
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
@@ -74,7 +74,7 @@ Puma utilizes a dynamic thread pool which you can modify. You can set the minimu
|
|
74
74
|
|
75
75
|
$ puma -t 8:32
|
76
76
|
|
77
|
-
Puma will automatically scale the number of threads based on how much traffic is present. The current default is `0:16`. Feel free to experiment, but be careful not to set the number of maximum threads to a very large number, as you may exhaust resources on the system (or hit resource limits).
|
77
|
+
Puma will automatically scale the number of threads, from the minimum until it caps out at the maximum, based on how much traffic is present. The current default is `0:16`. Feel free to experiment, but be careful not to set the number of maximum threads to a very large number, as you may exhaust resources on the system (or hit resource limits).
|
78
78
|
|
79
79
|
### Clustered mode
|
80
80
|
|
@@ -111,7 +111,7 @@ you to do some Puma-specific things that you don't want to embed in your applica
|
|
111
111
|
For instance, you could fire a log notification that a worker booted or send something to statsd.
|
112
112
|
This can be called multiple times to add hooks.
|
113
113
|
|
114
|
-
If you're preloading your application and using ActiveRecord, it's
|
114
|
+
If you're preloading your application and using ActiveRecord, it's recommended that you setup your connection pool here:
|
115
115
|
|
116
116
|
# config/puma.rb
|
117
117
|
on_worker_boot do
|
@@ -130,7 +130,7 @@ On top of that, you can specify a block in your configuration file that will be
|
|
130
130
|
This code can be used to clean up before forking to clients, allowing
|
131
131
|
you to do some Puma-specific things that you don't want to embed in your application.
|
132
132
|
|
133
|
-
If you're preloading your application and using ActiveRecord, it's
|
133
|
+
If you're preloading your application and using ActiveRecord, it's recommended that you close any connections to the database here to prevent connection leakage:
|
134
134
|
|
135
135
|
# config/puma.rb
|
136
136
|
before_fork do
|
@@ -139,7 +139,7 @@ If you're preloading your application and using ActiveRecord, it's recommend you
|
|
139
139
|
|
140
140
|
This rule applies to any connections to external services (Redis, databases, memcache, ...) that might be started automatically by the framework.
|
141
141
|
|
142
|
-
When you use preload_app, your new code goes
|
142
|
+
When you use preload_app, all of your new code goes into the master process, and is then copied into the workers (meaning it’s only compatible with cluster mode). General rule is to use preload_app when your workers die often and need fast starts. If you don’t have many workers, you probably should not use preload_app.
|
143
143
|
|
144
144
|
Note that preload_app can’t be used with phased restart, since phased restart kills and restarts workers one-by-one, and preload_app is all about copying the code of master into the workers.
|
145
145
|
|
@@ -177,7 +177,7 @@ Need a bit of security? Use SSL sockets!
|
|
177
177
|
|
178
178
|
### Control/Status Server
|
179
179
|
|
180
|
-
Puma comes with a builtin status/control app that can be used query and control Puma itself. Here is an example of starting Puma with the control server:
|
180
|
+
Puma comes with a builtin status/control app that can be used to query and control Puma itself. Here is an example of starting Puma with the control server:
|
181
181
|
|
182
182
|
$ puma --control tcp://127.0.0.1:9293 --control-token foo
|
183
183
|
|
@@ -212,26 +212,35 @@ If the new process is unable to load, it will simply exit. You should therefore
|
|
212
212
|
|
213
213
|
### Normal vs Hot vs Phased Restart
|
214
214
|
|
215
|
-
A hot restart means that no requests while deploying your new code
|
215
|
+
A hot restart means that no requests will be lost while deploying your new code, since the server socket is kept open between restarts.
|
216
216
|
|
217
217
|
But beware, hot restart does not mean that the incoming requests won’t hang for multiple seconds while your new code has not fully deployed. If you need a zero downtime and zero hanging requests deploy, you must use phased restart.
|
218
218
|
|
219
|
-
When you run pumactl phased-restart, Puma kills workers one-by-one, meaning that at least another worker is still available to serve requests, which lead
|
219
|
+
When you run pumactl phased-restart, Puma kills workers one-by-one, meaning that at least another worker is still available to serve requests, which lead to zero hanging requests (yay!).
|
220
220
|
|
221
221
|
But again beware, upgrading an application sometimes involves upgrading the database schema. With phased restart, there may be a moment during the deployment where processes belonging to the previous version and processes belonging to the new version both exist at the same time. Any database schema upgrades you perform must therefore be backwards-compatible with the old application version.
|
222
222
|
|
223
|
-
|
223
|
+
If you perform a lot of database migrations, you probably should not use phased restart and use a normal/hot restart instead (pumactl restart). That way, no code is shared while deploying (in that case, preload_app might help for quicker deployment, see below).
|
224
224
|
|
225
|
+
### Release Directory
|
226
|
+
|
227
|
+
If you symlink releases into a common working directory (i.e., `/current` from Capistrano), Puma won't pick up your new changes when running phased restarts without additional configuration. You should set your working directory within Puma's config to specify the directory it should use. This is a change from earlier versions of Puma (< 2.15) that would infer the directory for you.
|
228
|
+
|
229
|
+
```ruby
|
230
|
+
# config/puma.rb
|
231
|
+
|
232
|
+
directory '/var/www/current'
|
233
|
+
```
|
225
234
|
|
226
235
|
### Cleanup Code
|
227
236
|
|
228
237
|
Puma isn't able to understand all the resources that your app may use, so it provides a hook in the configuration file you pass to `-C` called `on_restart`. The block passed to `on_restart` will be called, unsurprisingly, just before Puma restarts itself.
|
229
238
|
|
230
|
-
You should place code to close global log files, redis connections, etc in this block so that their file descriptors don't leak into the restarted process. Failure to do so will result in slowly running out of descriptors and eventually obscure crashes as the server is
|
239
|
+
You should place code to close global log files, redis connections, etc in this block so that their file descriptors don't leak into the restarted process. Failure to do so will result in slowly running out of descriptors and eventually obscure crashes as the server is restarted many times.
|
231
240
|
|
232
241
|
### Platform Constraints
|
233
242
|
|
234
|
-
Because of various platforms not being implement certain things, the following differences occur when Puma is used on different platforms:
|
243
|
+
Because of various platforms not being able to implement certain things, the following differences occur when Puma is used on different platforms:
|
235
244
|
|
236
245
|
* **JRuby**, **Windows**: server sockets are not seamless on restart, they must be closed and reopened. These platforms have no way to pass descriptors into a new process that is exposed to ruby
|
237
246
|
* **JRuby**, **Windows**: cluster mode is not supported due to a lack of fork(2)
|
@@ -243,7 +252,7 @@ Because of various platforms not being implement certain things, the following d
|
|
243
252
|
|
244
253
|
## Managing multiple Pumas / init.d / upstart scripts
|
245
254
|
|
246
|
-
If you want an easy way to manage multiple scripts at once check [tools/jungle](https://github.com/puma/puma/tree/master/tools/jungle) for init.d and upstart scripts.
|
255
|
+
If you want an easy way to manage multiple scripts at once, check [tools/jungle](https://github.com/puma/puma/tree/master/tools/jungle) for init.d and upstart scripts.
|
247
256
|
|
248
257
|
## Capistrano deployment
|
249
258
|
|
@@ -284,4 +293,4 @@ $ bundle exec rake
|
|
284
293
|
|
285
294
|
## License
|
286
295
|
|
287
|
-
Puma is copyright 2014 Evan Phoenix and contributors. It is licensed under the BSD 3-Clause license. See the
|
296
|
+
Puma is copyright 2014 Evan Phoenix and contributors. It is licensed under the BSD 3-Clause license. See the included LICENSE file for details.
|
data/ext/puma_http11/io_buffer.c
CHANGED
@@ -116,7 +116,7 @@ static VALUE buf_to_str(VALUE self) {
|
|
116
116
|
struct buf_int* b;
|
117
117
|
Data_Get_Struct(self, struct buf_int, b);
|
118
118
|
|
119
|
-
return rb_str_new(b->top, b->cur - b->top);
|
119
|
+
return rb_str_new((const char*)(b->top), b->cur - b->top);
|
120
120
|
}
|
121
121
|
|
122
122
|
static VALUE buf_used(VALUE self) {
|
data/ext/puma_http11/mini_ssl.c
CHANGED
@@ -1,7 +1,13 @@
|
|
1
1
|
#define RSTRING_NOT_MODIFIED 1
|
2
2
|
|
3
3
|
#include <ruby.h>
|
4
|
+
#include <ruby/version.h>
|
5
|
+
|
6
|
+
#if RUBY_API_VERSION_MAJOR == 1
|
4
7
|
#include <rubyio.h>
|
8
|
+
#else
|
9
|
+
#include <ruby/io.h>
|
10
|
+
#endif
|
5
11
|
|
6
12
|
#ifdef HAVE_OPENSSL_BIO_H
|
7
13
|
|
@@ -347,7 +353,7 @@ VALUE engine_peercert(VALUE self) {
|
|
347
353
|
}
|
348
354
|
}
|
349
355
|
|
350
|
-
rb_cert_buf = rb_str_new(buf, bytes);
|
356
|
+
rb_cert_buf = rb_str_new((const char*)(buf), bytes);
|
351
357
|
if(!cert_buf) {
|
352
358
|
OPENSSL_free(buf);
|
353
359
|
}
|
data/lib/puma/binder.rb
CHANGED
@@ -111,6 +111,7 @@ module Puma
|
|
111
111
|
|
112
112
|
umask = nil
|
113
113
|
mode = nil
|
114
|
+
backlog = nil
|
114
115
|
|
115
116
|
if uri.query
|
116
117
|
params = Util.parse_query uri.query
|
@@ -122,9 +123,13 @@ module Puma
|
|
122
123
|
if u = params['mode']
|
123
124
|
mode = Integer('0'+u)
|
124
125
|
end
|
126
|
+
|
127
|
+
if u = params['backlog']
|
128
|
+
backlog = Integer(u)
|
129
|
+
end
|
125
130
|
end
|
126
131
|
|
127
|
-
io = add_unix_listener path, umask, mode
|
132
|
+
io = add_unix_listener path, umask, mode, backlog
|
128
133
|
end
|
129
134
|
|
130
135
|
@listeners << [str, io]
|
@@ -298,7 +303,7 @@ module Puma
|
|
298
303
|
|
299
304
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
300
305
|
#
|
301
|
-
def add_unix_listener(path, umask=nil, mode=nil)
|
306
|
+
def add_unix_listener(path, umask=nil, mode=nil, backlog=nil)
|
302
307
|
@unix_paths << path
|
303
308
|
|
304
309
|
# Let anyone connect by default
|
@@ -319,6 +324,7 @@ module Puma
|
|
319
324
|
end
|
320
325
|
|
321
326
|
s = UNIXServer.new(path)
|
327
|
+
s.listen backlog if backlog
|
322
328
|
@ios << s
|
323
329
|
ensure
|
324
330
|
File.umask old_mask
|
data/lib/puma/cli.rb
CHANGED
@@ -147,10 +147,14 @@ module Puma
|
|
147
147
|
c.prune_bundler
|
148
148
|
end
|
149
149
|
|
150
|
-
o.on "-q", "--quiet", "
|
150
|
+
o.on "-q", "--quiet", "Do not log requests internally (default true)" do
|
151
151
|
c.quiet
|
152
152
|
end
|
153
153
|
|
154
|
+
o.on "-v", "--log-requests", "Log requests as they occur" do
|
155
|
+
c.log_requests
|
156
|
+
end
|
157
|
+
|
154
158
|
o.on "-R", "--restart-cmd CMD",
|
155
159
|
"The puma command to run during a hot restart",
|
156
160
|
"Default: inferred" do |cmd|
|
data/lib/puma/client.rb
CHANGED
@@ -7,6 +7,7 @@ class IO
|
|
7
7
|
end
|
8
8
|
|
9
9
|
require 'puma/detect'
|
10
|
+
require 'puma/delegation'
|
10
11
|
|
11
12
|
if Puma::IS_JRUBY
|
12
13
|
# We have to work around some OpenSSL buffer/io-readiness bugs
|
@@ -21,6 +22,7 @@ module Puma
|
|
21
22
|
|
22
23
|
class Client
|
23
24
|
include Puma::Const
|
25
|
+
extend Puma::Delegation
|
24
26
|
|
25
27
|
def initialize(io, env=nil)
|
26
28
|
@io = io
|
@@ -57,6 +59,8 @@ module Puma
|
|
57
59
|
|
58
60
|
attr_accessor :remote_addr_header
|
59
61
|
|
62
|
+
forward :closed?, :@io
|
63
|
+
|
60
64
|
def inspect
|
61
65
|
"#<Puma::Client:0x#{object_id.to_s(16)} @ready=#{@ready.inspect}>"
|
62
66
|
end
|
data/lib/puma/cluster.rb
CHANGED
@@ -52,10 +52,11 @@ module Puma
|
|
52
52
|
@options = options
|
53
53
|
@first_term_sent = nil
|
54
54
|
@last_checkin = Time.now
|
55
|
+
@last_status = '{}'
|
55
56
|
@dead = false
|
56
57
|
end
|
57
58
|
|
58
|
-
attr_reader :index, :pid, :phase, :signal, :last_checkin
|
59
|
+
attr_reader :index, :pid, :phase, :signal, :last_checkin, :last_status
|
59
60
|
|
60
61
|
def booted?
|
61
62
|
@stage == :booted
|
@@ -74,8 +75,9 @@ module Puma
|
|
74
75
|
@dead = true
|
75
76
|
end
|
76
77
|
|
77
|
-
def ping!
|
78
|
+
def ping!(status)
|
78
79
|
@last_checkin = Time.now
|
80
|
+
@last_status = status
|
79
81
|
end
|
80
82
|
|
81
83
|
def ping_timeout?(which)
|
@@ -238,11 +240,14 @@ module Puma
|
|
238
240
|
end
|
239
241
|
|
240
242
|
Thread.new(@worker_write) do |io|
|
241
|
-
|
243
|
+
base_payload = "p#{Process.pid}"
|
242
244
|
|
243
245
|
while true
|
244
246
|
sleep 5
|
245
247
|
begin
|
248
|
+
b = server.backlog
|
249
|
+
r = server.running
|
250
|
+
payload = %Q!#{base_payload}{ "backlog":#{b}, "running":#{r} }\n!
|
246
251
|
io << payload
|
247
252
|
rescue IOError
|
248
253
|
break
|
@@ -301,13 +306,47 @@ module Puma
|
|
301
306
|
def stats
|
302
307
|
old_worker_count = @workers.count { |w| w.phase != @phase }
|
303
308
|
booted_worker_count = @workers.count { |w| w.booted? }
|
304
|
-
%Q!{ "
|
309
|
+
worker_status = '[' + @workers.map{ |w| %Q!{ "pid": #{w.pid}, "index": #{w.index}, "phase": #{w.phase}, "booted": #{w.booted?}, "last_checkin": "#{w.last_checkin.utc.iso8601}", "last_status": #{w.last_status} }!}.join(",") + ']'
|
310
|
+
%Q!{ "workers": #{@workers.size}, "phase": #{@phase}, "booted_workers": #{booted_worker_count}, "old_workers": #{old_worker_count}, "worker_status": #{worker_status} }!
|
305
311
|
end
|
306
312
|
|
307
313
|
def preload?
|
308
314
|
@options[:preload_app]
|
309
315
|
end
|
310
316
|
|
317
|
+
# We do this in a separate method to keep the lambad scope
|
318
|
+
# of the signals handlers as small as possible.
|
319
|
+
def setup_signals
|
320
|
+
Signal.trap "SIGCHLD" do
|
321
|
+
wakeup!
|
322
|
+
end
|
323
|
+
|
324
|
+
Signal.trap "TTIN" do
|
325
|
+
@options[:workers] += 1
|
326
|
+
wakeup!
|
327
|
+
end
|
328
|
+
|
329
|
+
Signal.trap "TTOU" do
|
330
|
+
@options[:workers] -= 1 if @options[:workers] >= 2
|
331
|
+
@workers.last.term
|
332
|
+
wakeup!
|
333
|
+
end
|
334
|
+
|
335
|
+
master_pid = Process.pid
|
336
|
+
|
337
|
+
Signal.trap "SIGTERM" do
|
338
|
+
# The worker installs their own SIGTERM when booted.
|
339
|
+
# Until then, this is run by the worker and the worker
|
340
|
+
# should just exit if they get it.
|
341
|
+
if Process.pid != master_pid
|
342
|
+
log "Early termination of worker"
|
343
|
+
exit! 0
|
344
|
+
else
|
345
|
+
stop
|
346
|
+
end
|
347
|
+
end
|
348
|
+
end
|
349
|
+
|
311
350
|
def run
|
312
351
|
@status = :run
|
313
352
|
|
@@ -347,34 +386,7 @@ module Puma
|
|
347
386
|
|
348
387
|
read, @wakeup = Puma::Util.pipe
|
349
388
|
|
350
|
-
|
351
|
-
wakeup!
|
352
|
-
end
|
353
|
-
|
354
|
-
Signal.trap "TTIN" do
|
355
|
-
@options[:workers] += 1
|
356
|
-
wakeup!
|
357
|
-
end
|
358
|
-
|
359
|
-
Signal.trap "TTOU" do
|
360
|
-
@options[:workers] -= 1 if @options[:workers] >= 2
|
361
|
-
@workers.last.term
|
362
|
-
wakeup!
|
363
|
-
end
|
364
|
-
|
365
|
-
master_pid = Process.pid
|
366
|
-
|
367
|
-
Signal.trap "SIGTERM" do
|
368
|
-
# The worker installs their own SIGTERM when booted.
|
369
|
-
# Until then, this is run by the worker and the worker
|
370
|
-
# should just exit if they get it.
|
371
|
-
if Process.pid != master_pid
|
372
|
-
log "Early termination of worker"
|
373
|
-
exit! 0
|
374
|
-
else
|
375
|
-
stop
|
376
|
-
end
|
377
|
-
end
|
389
|
+
setup_signals
|
378
390
|
|
379
391
|
# Used by the workers to detect if the master process dies.
|
380
392
|
# If select says that @check_pipe is ready, it's because the
|
@@ -420,7 +432,8 @@ module Puma
|
|
420
432
|
|
421
433
|
next if !req || req == "!"
|
422
434
|
|
423
|
-
|
435
|
+
result = read.gets
|
436
|
+
pid = result.to_i
|
424
437
|
|
425
438
|
if w = @workers.find { |x| x.pid == pid }
|
426
439
|
case req
|
@@ -432,7 +445,7 @@ module Puma
|
|
432
445
|
w.dead!
|
433
446
|
force_check = true
|
434
447
|
when "p"
|
435
|
-
w.ping!
|
448
|
+
w.ping!(result.sub(/^\d+/,'').chomp)
|
436
449
|
end
|
437
450
|
else
|
438
451
|
log "! Out-of-sync worker list, no #{pid} worker"
|
data/lib/puma/configuration.rb
CHANGED
@@ -165,7 +165,7 @@ module Puma
|
|
165
165
|
{
|
166
166
|
:min_threads => 0,
|
167
167
|
:max_threads => 16,
|
168
|
-
:
|
168
|
+
:log_requests => false,
|
169
169
|
:debug => false,
|
170
170
|
:binds => ["tcp://#{DefaultTCPHost}:#{DefaultTCPPort}"],
|
171
171
|
:workers => 0,
|
@@ -242,10 +242,10 @@ module Puma
|
|
242
242
|
require 'puma/tcp_logger'
|
243
243
|
|
244
244
|
logger = @options[:logger]
|
245
|
-
return TCPLogger.new(logger, found, @options[:
|
245
|
+
return TCPLogger.new(logger, found, @options[:log_requests])
|
246
246
|
end
|
247
247
|
|
248
|
-
if
|
248
|
+
if @options[:log_requests]
|
249
249
|
logger = @options[:logger]
|
250
250
|
found = CommonLogger.new(found, logger)
|
251
251
|
end
|
data/lib/puma/const.rb
CHANGED
@@ -99,8 +99,8 @@ module Puma
|
|
99
99
|
# too taxing on performance.
|
100
100
|
module Const
|
101
101
|
|
102
|
-
PUMA_VERSION = VERSION = "3.0.0
|
103
|
-
CODE_NAME = "
|
102
|
+
PUMA_VERSION = VERSION = "3.0.0".freeze
|
103
|
+
CODE_NAME = "Plethora of Penguin Pinatas".freeze
|
104
104
|
|
105
105
|
FAST_TRACK_KA_TIMEOUT = 0.2
|
106
106
|
|
data/lib/puma/control_cli.rb
CHANGED
data/lib/puma/dsl.rb
CHANGED
@@ -160,8 +160,14 @@ module Puma
|
|
160
160
|
|
161
161
|
# Disable request logging.
|
162
162
|
#
|
163
|
-
def quiet
|
164
|
-
@options[:
|
163
|
+
def quiet(which=true)
|
164
|
+
@options[:log_requests] = !which
|
165
|
+
end
|
166
|
+
|
167
|
+
# Enable request logging
|
168
|
+
#
|
169
|
+
def log_requests(which=true)
|
170
|
+
@options[:log_requests] = which
|
165
171
|
end
|
166
172
|
|
167
173
|
# Show debugging info
|
data/lib/puma/server.rb
CHANGED
@@ -399,6 +399,7 @@ module Puma
|
|
399
399
|
#
|
400
400
|
def process_client(client, buffer)
|
401
401
|
begin
|
402
|
+
clean_thread_locals = @options[:clean_thread_locals]
|
402
403
|
close_socket = true
|
403
404
|
|
404
405
|
while true
|
@@ -412,6 +413,8 @@ module Puma
|
|
412
413
|
return unless @queue_requests
|
413
414
|
buffer.reset
|
414
415
|
|
416
|
+
ThreadPool.clean_thread_locals if clean_thread_locals
|
417
|
+
|
415
418
|
unless client.reset(@status == :run)
|
416
419
|
close_socket = false
|
417
420
|
client.set_timeout @persistent_timeout
|
@@ -517,63 +520,20 @@ module Puma
|
|
517
520
|
env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80
|
518
521
|
end
|
519
522
|
|
520
|
-
|
521
|
-
# in +body+, finish reading the body if there is one and invoke
|
522
|
-
# the rack app. Then construct the response and write it back to
|
523
|
-
# +client+
|
524
|
-
#
|
525
|
-
# +cl+ is the previously fetched Content-Length header if there
|
526
|
-
# was one. This is an optimization to keep from having to look
|
527
|
-
# it up again.
|
528
|
-
#
|
529
|
-
def handle_request(req, lines)
|
530
|
-
env = req.env
|
523
|
+
def handle_app_response(req, lines, env, status, headers, res_body)
|
531
524
|
client = req.io
|
532
525
|
|
533
|
-
normalize_env env, req
|
534
|
-
|
535
|
-
env[PUMA_SOCKET] = client
|
536
|
-
|
537
|
-
if env[HTTPS_KEY] && client.peercert
|
538
|
-
env[PUMA_PEERCERT] = client.peercert
|
539
|
-
end
|
540
|
-
|
541
|
-
env[HIJACK_P] = true
|
542
|
-
env[HIJACK] = req
|
543
|
-
|
544
526
|
body = req.body
|
545
527
|
|
546
528
|
head = env[REQUEST_METHOD] == HEAD
|
547
529
|
|
548
|
-
env[RACK_INPUT] = body
|
549
|
-
env[RACK_URL_SCHEME] = env[HTTPS_KEY] ? HTTPS : HTTP
|
550
|
-
|
551
530
|
# A rack extension. If the app writes #call'ables to this
|
552
531
|
# array, we will invoke them when the request is done.
|
553
532
|
#
|
554
|
-
|
533
|
+
# (This is already initialized in `handle_request`, just getting here)
|
534
|
+
after_reply = env[RACK_AFTER_REPLY]
|
555
535
|
|
556
536
|
begin
|
557
|
-
begin
|
558
|
-
status, headers, res_body = @app.call(env)
|
559
|
-
|
560
|
-
return :async if req.hijacked
|
561
|
-
|
562
|
-
status = status.to_i
|
563
|
-
|
564
|
-
if status == -1
|
565
|
-
unless headers.empty? and res_body == []
|
566
|
-
raise "async response must have empty headers and body"
|
567
|
-
end
|
568
|
-
|
569
|
-
return :async
|
570
|
-
end
|
571
|
-
rescue StandardError => e
|
572
|
-
@events.unknown_error self, e, "Rack app"
|
573
|
-
|
574
|
-
status, headers, res_body = lowlevel_error(e)
|
575
|
-
end
|
576
|
-
|
577
537
|
content_length = nil
|
578
538
|
no_body = head
|
579
539
|
|
@@ -715,6 +675,75 @@ module Puma
|
|
715
675
|
return keep_alive
|
716
676
|
end
|
717
677
|
|
678
|
+
# Given the request +env+ from +client+ and a partial request body
|
679
|
+
# in +body+, finish reading the body if there is one and invoke
|
680
|
+
# the rack app. Then construct the response and write it back to
|
681
|
+
# +client+
|
682
|
+
#
|
683
|
+
# +cl+ is the previously fetched Content-Length header if there
|
684
|
+
# was one. This is an optimization to keep from having to look
|
685
|
+
# it up again.
|
686
|
+
#
|
687
|
+
def handle_request(req, lines)
|
688
|
+
env = req.env
|
689
|
+
client = req.io
|
690
|
+
|
691
|
+
normalize_env env, req
|
692
|
+
|
693
|
+
env[PUMA_SOCKET] = client
|
694
|
+
|
695
|
+
if env[HTTPS_KEY] && client.peercert
|
696
|
+
env[PUMA_PEERCERT] = client.peercert
|
697
|
+
end
|
698
|
+
|
699
|
+
env[HIJACK_P] = true
|
700
|
+
env[HIJACK] = req
|
701
|
+
|
702
|
+
env['async.callback'] = lambda { |__response|
|
703
|
+
_status, _headers, _res_body = __response
|
704
|
+
# Faye websocket gem also calls this (when available)
|
705
|
+
# with status=101, and res_body being a stream
|
706
|
+
# but calling here just finishes the response
|
707
|
+
# that's why we only response if the request is not
|
708
|
+
# hijacked. not sure if this is a correct solution
|
709
|
+
# but working. for now.
|
710
|
+
unless req.hijacked
|
711
|
+
handle_app_response(req, lines, env, _status, _headers, _res_body)
|
712
|
+
end
|
713
|
+
}
|
714
|
+
|
715
|
+
body = req.body
|
716
|
+
|
717
|
+
env[RACK_INPUT] = body
|
718
|
+
env[RACK_URL_SCHEME] = env[HTTPS_KEY] ? HTTPS : HTTP
|
719
|
+
|
720
|
+
env[RACK_AFTER_REPLY] = []
|
721
|
+
|
722
|
+
begin
|
723
|
+
status, headers, res_body = @app.call(env)
|
724
|
+
|
725
|
+
return :async if req.hijacked
|
726
|
+
|
727
|
+
status = status.to_i
|
728
|
+
|
729
|
+
if status == -1
|
730
|
+
# unless headers.empty? and res_body == []
|
731
|
+
# puts "HEADERS: ".white.on_green + headers.inspect
|
732
|
+
# puts "BODY: ".white.on_green + res_body.inspect
|
733
|
+
# raise "async response must have empty headers and body"
|
734
|
+
# end
|
735
|
+
|
736
|
+
return :async
|
737
|
+
end
|
738
|
+
rescue StandardError => e
|
739
|
+
@events.unknown_error self, e, "Rack app"
|
740
|
+
|
741
|
+
status, headers, res_body = lowlevel_error(e, env)
|
742
|
+
end
|
743
|
+
|
744
|
+
return handle_app_response(req, lines, env, status, headers, res_body)
|
745
|
+
end
|
746
|
+
|
718
747
|
def fetch_status_code(status)
|
719
748
|
HTTP_STATUS_CODES.fetch(status) { 'CUSTOM' }
|
720
749
|
end
|
@@ -776,9 +805,13 @@ module Puma
|
|
776
805
|
|
777
806
|
# A fallback rack response if +@app+ raises as exception.
|
778
807
|
#
|
779
|
-
def lowlevel_error(e)
|
808
|
+
def lowlevel_error(e, env)
|
780
809
|
if handler = @options[:lowlevel_error_handler]
|
781
|
-
|
810
|
+
if handler.arity == 1
|
811
|
+
return handler.call(e)
|
812
|
+
else
|
813
|
+
return handler.call(e, env)
|
814
|
+
end
|
782
815
|
end
|
783
816
|
|
784
817
|
if @leak_stack_on_error
|
data/lib/puma/state_file.rb
CHANGED
data/lib/puma/thread_pool.rb
CHANGED
@@ -45,6 +45,12 @@ module Puma
|
|
45
45
|
attr_reader :spawned, :trim_requested
|
46
46
|
attr_accessor :clean_thread_locals
|
47
47
|
|
48
|
+
def self.clean_thread_locals
|
49
|
+
Thread.current.keys.each do |key|
|
50
|
+
Thread.current[key] = nil unless key == :__recursive_key__
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
48
54
|
# How many objects have yet to be processed by the pool?
|
49
55
|
#
|
50
56
|
def backlog
|
@@ -77,6 +83,7 @@ module Puma
|
|
77
83
|
if @trim_requested > 0
|
78
84
|
@trim_requested -= 1
|
79
85
|
continue = false
|
86
|
+
not_full.signal
|
80
87
|
break
|
81
88
|
end
|
82
89
|
|
@@ -97,9 +104,7 @@ module Puma
|
|
97
104
|
break unless continue
|
98
105
|
|
99
106
|
if @clean_thread_locals
|
100
|
-
|
101
|
-
Thread.current[key] = nil unless key == :__recursive_key__
|
102
|
-
end
|
107
|
+
ThreadPool.clean_thread_locals
|
103
108
|
end
|
104
109
|
|
105
110
|
begin
|
data/lib/rack/handler/puma.rb
CHANGED
@@ -13,6 +13,8 @@ module Rack
|
|
13
13
|
options = DEFAULT_OPTIONS.merge(options)
|
14
14
|
|
15
15
|
conf = ::Puma::Configuration.new do |c|
|
16
|
+
c.quiet
|
17
|
+
|
16
18
|
if options.delete(:Verbose)
|
17
19
|
app = Rack::CommonLogger.new(app, STDOUT)
|
18
20
|
end
|
@@ -26,10 +28,16 @@ module Rack
|
|
26
28
|
c.threads min, max
|
27
29
|
end
|
28
30
|
|
29
|
-
host = options[:Host]
|
30
|
-
|
31
|
+
host = options[:Host]
|
32
|
+
|
33
|
+
if host && (host[0,1] == '.' || host[0,1] == '/')
|
34
|
+
c.bind "unix://#{host}"
|
35
|
+
else
|
36
|
+
host ||= ::Puma::Configuration::DefaultTCPHost
|
37
|
+
port = options[:Port] || ::Puma::Configuration::DefaultTCPPort
|
31
38
|
|
32
|
-
|
39
|
+
c.port port, host
|
40
|
+
end
|
33
41
|
|
34
42
|
c.app app
|
35
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Phoenix
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-02-
|
11
|
+
date: 2016-02-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rdoc
|
@@ -95,7 +95,6 @@ extra_rdoc_files:
|
|
95
95
|
- tools/jungle/init.d/README.md
|
96
96
|
- tools/jungle/upstart/README.md
|
97
97
|
files:
|
98
|
-
- COPYING
|
99
98
|
- DEPLOYMENT.md
|
100
99
|
- Gemfile
|
101
100
|
- History.txt
|
@@ -191,9 +190,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
191
190
|
version: 1.8.7
|
192
191
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
193
192
|
requirements:
|
194
|
-
- - "
|
193
|
+
- - ">="
|
195
194
|
- !ruby/object:Gem::Version
|
196
|
-
version:
|
195
|
+
version: '0'
|
197
196
|
requirements: []
|
198
197
|
rubyforge_project:
|
199
198
|
rubygems_version: 2.5.1
|
data/COPYING
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
Mongrel Web Server (Mongrel) is copyrighted free software by Zed A. Shaw
|
2
|
-
<zedshaw at zedshaw dot com> You can redistribute it and/or modify it under
|
3
|
-
either the terms of the GPL or the conditions below:
|
4
|
-
|
5
|
-
1. You may make and give away verbatim copies of the source form of the
|
6
|
-
software without restriction, provided that you duplicate all of the
|
7
|
-
original copyright notices and associated disclaimers.
|
8
|
-
|
9
|
-
2. You may modify your copy of the software in any way, provided that
|
10
|
-
you do at least ONE of the following:
|
11
|
-
|
12
|
-
a) place your modifications in the Public Domain or otherwise make them
|
13
|
-
Freely Available, such as by posting said modifications to Usenet or an
|
14
|
-
equivalent medium, or by allowing the author to include your
|
15
|
-
modifications in the software.
|
16
|
-
|
17
|
-
b) use the modified software only within your corporation or
|
18
|
-
organization.
|
19
|
-
|
20
|
-
c) rename any non-standard executables so the names do not conflict with
|
21
|
-
standard executables, which must also be provided.
|
22
|
-
|
23
|
-
d) make other distribution arrangements with the author.
|
24
|
-
|
25
|
-
3. You may distribute the software in object code or executable
|
26
|
-
form, provided that you do at least ONE of the following:
|
27
|
-
|
28
|
-
a) distribute the executables and library files of the software,
|
29
|
-
together with instructions (in the manual page or equivalent) on where
|
30
|
-
to get the original distribution.
|
31
|
-
|
32
|
-
b) accompany the distribution with the machine-readable source of the
|
33
|
-
software.
|
34
|
-
|
35
|
-
c) give non-standard executables non-standard names, with
|
36
|
-
instructions on where to get the original software distribution.
|
37
|
-
|
38
|
-
d) make other distribution arrangements with the author.
|
39
|
-
|
40
|
-
4. You may modify and include the part of the software into any other
|
41
|
-
software (possibly commercial). But some files in the distribution
|
42
|
-
are not written by the author, so that they are not under this terms.
|
43
|
-
|
44
|
-
5. The scripts and library files supplied as input to or produced as
|
45
|
-
output from the software do not automatically fall under the
|
46
|
-
copyright of the software, but belong to whomever generated them,
|
47
|
-
and may be sold commercially, and may be aggregated with this
|
48
|
-
software.
|
49
|
-
|
50
|
-
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
51
|
-
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
52
|
-
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
53
|
-
PURPOSE.
|
54
|
-
|
55
|
-
|