puma 3.12.6 → 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 +4 -4
- data/History.md +24 -20
- data/README.md +29 -9
- 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/restart.md +4 -2
- data/docs/systemd.md +27 -9
- data/ext/puma_http11/PumaHttp11Service.java +2 -0
- data/ext/puma_http11/http11_parser.c +1 -3
- data/ext/puma_http11/http11_parser.rl +1 -3
- data/ext/puma_http11/mini_ssl.c +20 -4
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +11 -4
- data/lib/puma/app/status.rb +3 -2
- data/lib/puma/binder.rb +9 -1
- data/lib/puma/client.rb +45 -35
- data/lib/puma/cluster.rb +38 -14
- data/lib/puma/configuration.rb +2 -1
- data/lib/puma/const.rb +6 -10
- data/lib/puma/control_cli.rb +10 -0
- data/lib/puma/dsl.rb +45 -3
- data/lib/puma/io_buffer.rb +1 -6
- data/lib/puma/launcher.rb +10 -11
- data/lib/puma/minissl.rb +13 -1
- data/lib/puma/reactor.rb +104 -53
- data/lib/puma/runner.rb +1 -1
- data/lib/puma/server.rb +26 -78
- data/lib/puma/single.rb +2 -2
- data/lib/puma/thread_pool.rb +5 -1
- data/lib/puma/util.rb +1 -6
- data/tools/jungle/init.d/puma +5 -5
- metadata +18 -6
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/java_io_buffer.rb +0 -47
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
data/lib/puma/runner.rb
CHANGED
@@ -55,7 +55,7 @@ module Puma
|
|
55
55
|
app = Puma::App::Status.new @launcher
|
56
56
|
|
57
57
|
if token = @options[:control_auth_token]
|
58
|
-
app.auth_token = token unless token.empty?
|
58
|
+
app.auth_token = token unless token.empty? || token == 'none'
|
59
59
|
end
|
60
60
|
|
61
61
|
control = Puma::Server.new app, @launcher.events
|
data/lib/puma/server.rb
CHANGED
@@ -6,7 +6,6 @@ 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'
|
@@ -16,10 +15,6 @@ require 'puma/util'
|
|
16
15
|
|
17
16
|
require 'puma/puma_http11'
|
18
17
|
|
19
|
-
unless Puma.const_defined? "IOBuffer"
|
20
|
-
require 'puma/io_buffer'
|
21
|
-
end
|
22
|
-
|
23
18
|
require 'socket'
|
24
19
|
|
25
20
|
module Puma
|
@@ -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`.
|
@@ -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
|
|
@@ -102,7 +96,6 @@ module Puma
|
|
102
96
|
|
103
97
|
def inherit_binder(bind)
|
104
98
|
@binder = bind
|
105
|
-
@own_binder = false
|
106
99
|
end
|
107
100
|
|
108
101
|
def tcp_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
|
|
@@ -398,7 +392,10 @@ module Puma
|
|
398
392
|
end
|
399
393
|
|
400
394
|
pool << client
|
401
|
-
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
|
402
399
|
end
|
403
400
|
rescue SystemCallError
|
404
401
|
# nothing
|
@@ -430,10 +427,6 @@ module Puma
|
|
430
427
|
ensure
|
431
428
|
@check.close
|
432
429
|
@notify.close
|
433
|
-
|
434
|
-
if @status != :restart and @own_binder
|
435
|
-
@binder.close
|
436
|
-
end
|
437
430
|
end
|
438
431
|
|
439
432
|
@events.fire :state, :done
|
@@ -470,8 +463,6 @@ module Puma
|
|
470
463
|
clean_thread_locals = @options[:clean_thread_locals]
|
471
464
|
close_socket = true
|
472
465
|
|
473
|
-
requests = 0
|
474
|
-
|
475
466
|
while true
|
476
467
|
case handle_request(client, buffer)
|
477
468
|
when false
|
@@ -485,19 +476,7 @@ module Puma
|
|
485
476
|
|
486
477
|
ThreadPool.clean_thread_locals if clean_thread_locals
|
487
478
|
|
488
|
-
|
489
|
-
|
490
|
-
check_for_more_data = @status == :run
|
491
|
-
|
492
|
-
if requests >= MAX_FAST_INLINE
|
493
|
-
# This will mean that reset will only try to use the data it already
|
494
|
-
# has buffered and won't try to read more data. What this means is that
|
495
|
-
# every client, independent of their request speed, gets treated like a slow
|
496
|
-
# one once every MAX_FAST_INLINE requests.
|
497
|
-
check_for_more_data = false
|
498
|
-
end
|
499
|
-
|
500
|
-
unless client.reset(check_for_more_data)
|
479
|
+
unless client.reset(@status == :run)
|
501
480
|
close_socket = false
|
502
481
|
client.set_timeout @persistent_timeout
|
503
482
|
@reactor.add client
|
@@ -613,15 +592,19 @@ module Puma
|
|
613
592
|
env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80
|
614
593
|
end
|
615
594
|
|
616
|
-
#
|
617
|
-
#
|
618
|
-
#
|
619
|
-
# +
|
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.
|
620
600
|
#
|
621
|
-
# +
|
622
|
-
#
|
623
|
-
# it up again.
|
601
|
+
# It'll return +false+ when the connection is closed, this doesn't mean
|
602
|
+
# that the response wasn't successful.
|
624
603
|
#
|
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.
|
606
|
+
#
|
607
|
+
# Finally, it'll return +true+ on keep-alive connections.
|
625
608
|
def handle_request(req, lines)
|
626
609
|
env = req.env
|
627
610
|
client = req.io
|
@@ -653,7 +636,6 @@ module Puma
|
|
653
636
|
headers.each_pair do |k, vs|
|
654
637
|
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
655
638
|
vs.to_s.split(NEWLINE).each do |v|
|
656
|
-
next if possible_header_injection?(v)
|
657
639
|
fast_write client, "#{k}: #{v}\r\n"
|
658
640
|
end
|
659
641
|
else
|
@@ -665,37 +647,6 @@ module Puma
|
|
665
647
|
}
|
666
648
|
end
|
667
649
|
|
668
|
-
# Fixup any headers with , in the name to have _ now. We emit
|
669
|
-
# headers with , in them during the parse phase to avoid ambiguity
|
670
|
-
# with the - to _ conversion for critical headers. But here for
|
671
|
-
# compatibility, we'll convert them back. This code is written to
|
672
|
-
# avoid allocation in the common case (ie there are no headers
|
673
|
-
# with , in their names), that's why it has the extra conditionals.
|
674
|
-
|
675
|
-
to_delete = nil
|
676
|
-
to_add = nil
|
677
|
-
|
678
|
-
env.each do |k,v|
|
679
|
-
if k.start_with?("HTTP_") and k.include?(",") and k != "HTTP_TRANSFER,ENCODING"
|
680
|
-
if to_delete
|
681
|
-
to_delete << k
|
682
|
-
else
|
683
|
-
to_delete = [k]
|
684
|
-
end
|
685
|
-
|
686
|
-
unless to_add
|
687
|
-
to_add = {}
|
688
|
-
end
|
689
|
-
|
690
|
-
to_add[k.gsub(",", "_")] = v
|
691
|
-
end
|
692
|
-
end
|
693
|
-
|
694
|
-
if to_delete
|
695
|
-
to_delete.each { |k| env.delete(k) }
|
696
|
-
env.merge! to_add
|
697
|
-
end
|
698
|
-
|
699
650
|
# A rack extension. If the app writes #call'ables to this
|
700
651
|
# array, we will invoke them when the request is done.
|
701
652
|
#
|
@@ -783,7 +734,6 @@ module Puma
|
|
783
734
|
headers.each do |k, vs|
|
784
735
|
case k.downcase
|
785
736
|
when CONTENT_LENGTH2
|
786
|
-
next if possible_header_injection?(vs)
|
787
737
|
content_length = vs
|
788
738
|
next
|
789
739
|
when TRANSFER_ENCODING
|
@@ -796,7 +746,6 @@ module Puma
|
|
796
746
|
|
797
747
|
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
798
748
|
vs.to_s.split(NEWLINE).each do |v|
|
799
|
-
next if possible_header_injection?(v)
|
800
749
|
lines.append k, colon, v, line_ending
|
801
750
|
end
|
802
751
|
else
|
@@ -990,6 +939,10 @@ module Puma
|
|
990
939
|
@events.debug "Drained #{count} additional connections."
|
991
940
|
end
|
992
941
|
|
942
|
+
if @status != :restart
|
943
|
+
@binder.close
|
944
|
+
end
|
945
|
+
|
993
946
|
if @thread_pool
|
994
947
|
if timeout = @options[:force_shutdown_after]
|
995
948
|
@thread_pool.shutdown timeout.to_i
|
@@ -1063,10 +1016,5 @@ module Puma
|
|
1063
1016
|
def shutting_down?
|
1064
1017
|
@status == :stop || @status == :restart
|
1065
1018
|
end
|
1066
|
-
|
1067
|
-
def possible_header_injection?(header_value)
|
1068
|
-
HTTP_INJECTION_REGEX =~ header_value.to_s
|
1069
|
-
end
|
1070
|
-
private :possible_header_injection?
|
1071
1019
|
end
|
1072
1020
|
end
|
data/lib/puma/single.rb
CHANGED
@@ -26,7 +26,7 @@ module Puma
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def stop
|
29
|
-
@server.stop
|
29
|
+
@server.stop(false) if @server
|
30
30
|
end
|
31
31
|
|
32
32
|
def halt
|
@@ -36,7 +36,7 @@ module Puma
|
|
36
36
|
def stop_blocked
|
37
37
|
log "- Gracefully stopping, waiting for requests to finish"
|
38
38
|
@control.stop(true) if @control
|
39
|
-
@server.stop(true)
|
39
|
+
@server.stop(true) if @server
|
40
40
|
end
|
41
41
|
|
42
42
|
def jruby_daemon?
|
data/lib/puma/thread_pool.rb
CHANGED
@@ -194,6 +194,9 @@ module Puma
|
|
194
194
|
# method would not block and another request would be added into the reactor
|
195
195
|
# by the server. This would continue until a fully bufferend request
|
196
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
|
+
#
|
197
200
|
def wait_until_not_full
|
198
201
|
@mutex.synchronize do
|
199
202
|
while true
|
@@ -203,7 +206,8 @@ module Puma
|
|
203
206
|
# is work queued that cannot be handled by waiting
|
204
207
|
# threads, then accept more work until we would
|
205
208
|
# spin up the max number of threads.
|
206
|
-
|
209
|
+
busy_threads = @spawned - @waiting + @todo.size
|
210
|
+
return busy_threads if @max > busy_threads
|
207
211
|
|
208
212
|
@not_full.wait @mutex
|
209
213
|
end
|
data/lib/puma/util.rb
CHANGED
@@ -1,11 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
|
3
2
|
|
4
|
-
|
5
|
-
require 'puma/rack/backports/uri/common_193'
|
6
|
-
else
|
7
|
-
require 'uri/common'
|
8
|
-
end
|
3
|
+
require 'uri/common'
|
9
4
|
|
10
5
|
module Puma
|
11
6
|
module Util
|
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
|
50
|
+
# If the puma is running, restart it, otherwise run it.
|
51
51
|
if ps -p $PID > /dev/null; then
|
52
|
-
do_start_one_do $1
|
53
|
-
else
|
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
|
@@ -106,8 +106,6 @@ do_stop_one() {
|
|
106
106
|
if [ -e $PIDFILE ]; then
|
107
107
|
PID=`cat $PIDFILE`
|
108
108
|
if ps -p $PID > /dev/null; then
|
109
|
-
log_daemon_msg "---> Puma $1 isn't running."
|
110
|
-
else
|
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..."
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: puma
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.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:
|
12
|
-
dependencies:
|
11
|
+
date: 2019-06-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nio4r
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
13
27
|
description: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server
|
14
28
|
for Ruby/Rack applications. Puma is intended for use in both development and production
|
15
29
|
environments. It's great for highly concurrent Ruby implementations such as Rubinius
|
@@ -51,6 +65,7 @@ files:
|
|
51
65
|
- ext/puma_http11/mini_ssl.c
|
52
66
|
- ext/puma_http11/org/jruby/puma/Http11.java
|
53
67
|
- ext/puma_http11/org/jruby/puma/Http11Parser.java
|
68
|
+
- ext/puma_http11/org/jruby/puma/IOBuffer.java
|
54
69
|
- ext/puma_http11/org/jruby/puma/MiniSSL.java
|
55
70
|
- ext/puma_http11/puma_http11.c
|
56
71
|
- lib/puma.rb
|
@@ -61,7 +76,6 @@ files:
|
|
61
76
|
- lib/puma/client.rb
|
62
77
|
- lib/puma/cluster.rb
|
63
78
|
- lib/puma/commonlogger.rb
|
64
|
-
- lib/puma/compat.rb
|
65
79
|
- lib/puma/configuration.rb
|
66
80
|
- lib/puma/const.rb
|
67
81
|
- lib/puma/control_cli.rb
|
@@ -72,14 +86,12 @@ files:
|
|
72
86
|
- lib/puma/dsl.rb
|
73
87
|
- lib/puma/events.rb
|
74
88
|
- lib/puma/io_buffer.rb
|
75
|
-
- lib/puma/java_io_buffer.rb
|
76
89
|
- lib/puma/jruby_restart.rb
|
77
90
|
- lib/puma/launcher.rb
|
78
91
|
- lib/puma/minissl.rb
|
79
92
|
- lib/puma/null_io.rb
|
80
93
|
- lib/puma/plugin.rb
|
81
94
|
- lib/puma/plugin/tmp_restart.rb
|
82
|
-
- lib/puma/rack/backports/uri/common_193.rb
|
83
95
|
- lib/puma/rack/builder.rb
|
84
96
|
- lib/puma/rack/urlmap.rb
|
85
97
|
- lib/puma/rack_default.rb
|
data/lib/puma/compat.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
# Provides code to work properly on 1.8 and 1.9
|
2
|
-
|
3
|
-
class String
|
4
|
-
unless method_defined? :bytesize
|
5
|
-
alias_method :bytesize, :size
|
6
|
-
end
|
7
|
-
|
8
|
-
unless method_defined? :byteslice
|
9
|
-
def byteslice(*arg)
|
10
|
-
enc = self.encoding
|
11
|
-
self.dup.force_encoding(Encoding::ASCII_8BIT).slice(*arg).force_encoding(enc)
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
data/lib/puma/java_io_buffer.rb
DELETED
@@ -1,47 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'java'
|
4
|
-
|
5
|
-
# Conservative native JRuby/Java implementation of IOBuffer
|
6
|
-
# backed by a ByteArrayOutputStream and conversion between
|
7
|
-
# Ruby String and Java bytes
|
8
|
-
module Puma
|
9
|
-
class JavaIOBuffer < java.io.ByteArrayOutputStream
|
10
|
-
field_reader :buf
|
11
|
-
end
|
12
|
-
|
13
|
-
class IOBuffer
|
14
|
-
BUF_DEFAULT_SIZE = 4096
|
15
|
-
|
16
|
-
def initialize
|
17
|
-
@buf = JavaIOBuffer.new(BUF_DEFAULT_SIZE)
|
18
|
-
end
|
19
|
-
|
20
|
-
def reset
|
21
|
-
@buf.reset
|
22
|
-
end
|
23
|
-
|
24
|
-
def <<(str)
|
25
|
-
bytes = str.to_java_bytes
|
26
|
-
@buf.write(bytes, 0, bytes.length)
|
27
|
-
end
|
28
|
-
|
29
|
-
def append(*strs)
|
30
|
-
strs.each { |s| self << s; }
|
31
|
-
end
|
32
|
-
|
33
|
-
def to_s
|
34
|
-
String.from_java_bytes @buf.to_byte_array
|
35
|
-
end
|
36
|
-
|
37
|
-
alias_method :to_str, :to_s
|
38
|
-
|
39
|
-
def used
|
40
|
-
@buf.size
|
41
|
-
end
|
42
|
-
|
43
|
-
def capacity
|
44
|
-
@buf.buf.length
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
@@ -1,33 +0,0 @@
|
|
1
|
-
# :stopdoc:
|
2
|
-
|
3
|
-
require 'uri/common'
|
4
|
-
|
5
|
-
# Issue:
|
6
|
-
# http://bugs.ruby-lang.org/issues/5925
|
7
|
-
#
|
8
|
-
# Relevant commit:
|
9
|
-
# https://github.com/ruby/ruby/commit/edb7cdf1eabaff78dfa5ffedfbc2e91b29fa9ca1
|
10
|
-
|
11
|
-
|
12
|
-
module URI
|
13
|
-
begin
|
14
|
-
256.times do |i|
|
15
|
-
TBLENCWWWCOMP_[i.chr] = '%%%02X' % i
|
16
|
-
end
|
17
|
-
TBLENCWWWCOMP_[' '] = '+'
|
18
|
-
TBLENCWWWCOMP_.freeze
|
19
|
-
|
20
|
-
256.times do |i|
|
21
|
-
h, l = i>>4, i&15
|
22
|
-
TBLDECWWWCOMP_['%%%X%X' % [h, l]] = i.chr
|
23
|
-
TBLDECWWWCOMP_['%%%x%X' % [h, l]] = i.chr
|
24
|
-
TBLDECWWWCOMP_['%%%X%x' % [h, l]] = i.chr
|
25
|
-
TBLDECWWWCOMP_['%%%x%x' % [h, l]] = i.chr
|
26
|
-
end
|
27
|
-
TBLDECWWWCOMP_['+'] = ' '
|
28
|
-
TBLDECWWWCOMP_.freeze
|
29
|
-
rescue Exception
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
# :startdoc:
|