puma 3.11.4 → 3.12.6
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 +46 -1
- data/README.md +15 -5
- 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/ext/puma_http11/http11_parser.c +3 -1
- data/ext/puma_http11/http11_parser.rl +3 -1
- data/ext/puma_http11/mini_ssl.c +22 -1
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +6 -0
- data/lib/puma/binder.rb +13 -9
- data/lib/puma/cli.rb +17 -7
- data/lib/puma/client.rb +24 -2
- data/lib/puma/cluster.rb +16 -1
- data/lib/puma/commonlogger.rb +2 -0
- data/lib/puma/configuration.rb +2 -0
- data/lib/puma/const.rb +12 -2
- data/lib/puma/control_cli.rb +12 -10
- 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 +12 -2
- data/lib/puma/events.rb +2 -0
- data/lib/puma/io_buffer.rb +2 -0
- data/lib/puma/java_io_buffer.rb +2 -0
- data/lib/puma/jruby_restart.rb +2 -0
- data/lib/puma/launcher.rb +4 -2
- data/lib/puma/minissl.rb +6 -2
- 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 +134 -0
- data/lib/puma/runner.rb +10 -1
- data/lib/puma/server.rb +83 -2
- data/lib/puma/single.rb +12 -1
- data/lib/puma/state_file.rb +2 -0
- data/lib/puma/tcp_logger.rb +2 -0
- data/lib/puma/thread_pool.rb +46 -5
- data/lib/puma/util.rb +1 -0
- data/lib/rack/handler/puma.rb +3 -0
- metadata +4 -5
data/lib/puma/server.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'stringio'
|
2
4
|
|
3
5
|
require 'puma/thread_pool'
|
@@ -23,6 +25,15 @@ require 'socket'
|
|
23
25
|
module Puma
|
24
26
|
|
25
27
|
# The HTTP Server itself. Serves out a single Rack app.
|
28
|
+
#
|
29
|
+
# This class is used by the `Puma::Single` and `Puma::Cluster` classes
|
30
|
+
# to generate one or more `Puma::Server` instances capable of handling requests.
|
31
|
+
# Each Puma process will contain one `Puma::Server` instacne.
|
32
|
+
#
|
33
|
+
# The `Puma::Server` instance pulls requests from the socket, adds them to a
|
34
|
+
# `Puma::Reactor` where they get eventually passed to a `Puma::ThreadPool`.
|
35
|
+
#
|
36
|
+
# Each `Puma::Server` will have one reactor and one thread pool.
|
26
37
|
class Server
|
27
38
|
|
28
39
|
include Puma::Const
|
@@ -159,6 +170,18 @@ module Puma
|
|
159
170
|
@thread_pool and @thread_pool.spawned
|
160
171
|
end
|
161
172
|
|
173
|
+
|
174
|
+
# This number represents the number of requests that
|
175
|
+
# the server is capable of taking right now.
|
176
|
+
#
|
177
|
+
# For example if the number is 5 then it means
|
178
|
+
# there are 5 threads sitting idle ready to take
|
179
|
+
# a request. If one request comes in, then the
|
180
|
+
# value would be 4 until it finishes processing.
|
181
|
+
def pool_capacity
|
182
|
+
@thread_pool and @thread_pool.pool_capacity
|
183
|
+
end
|
184
|
+
|
162
185
|
# Lopez Mode == raw tcp apps
|
163
186
|
|
164
187
|
def run_lopez_mode(background=true)
|
@@ -241,7 +264,12 @@ module Puma
|
|
241
264
|
STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
|
242
265
|
STDERR.puts e.backtrace
|
243
266
|
ensure
|
244
|
-
|
267
|
+
begin
|
268
|
+
@check.close
|
269
|
+
rescue
|
270
|
+
Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
|
271
|
+
end
|
272
|
+
|
245
273
|
@notify.close
|
246
274
|
|
247
275
|
if @status != :restart and @own_binder
|
@@ -442,6 +470,8 @@ module Puma
|
|
442
470
|
clean_thread_locals = @options[:clean_thread_locals]
|
443
471
|
close_socket = true
|
444
472
|
|
473
|
+
requests = 0
|
474
|
+
|
445
475
|
while true
|
446
476
|
case handle_request(client, buffer)
|
447
477
|
when false
|
@@ -455,7 +485,19 @@ module Puma
|
|
455
485
|
|
456
486
|
ThreadPool.clean_thread_locals if clean_thread_locals
|
457
487
|
|
458
|
-
|
488
|
+
requests += 1
|
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)
|
459
501
|
close_socket = false
|
460
502
|
client.set_timeout @persistent_timeout
|
461
503
|
@reactor.add client
|
@@ -611,6 +653,7 @@ module Puma
|
|
611
653
|
headers.each_pair do |k, vs|
|
612
654
|
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
613
655
|
vs.to_s.split(NEWLINE).each do |v|
|
656
|
+
next if possible_header_injection?(v)
|
614
657
|
fast_write client, "#{k}: #{v}\r\n"
|
615
658
|
end
|
616
659
|
else
|
@@ -622,6 +665,37 @@ module Puma
|
|
622
665
|
}
|
623
666
|
end
|
624
667
|
|
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
|
+
|
625
699
|
# A rack extension. If the app writes #call'ables to this
|
626
700
|
# array, we will invoke them when the request is done.
|
627
701
|
#
|
@@ -709,6 +783,7 @@ module Puma
|
|
709
783
|
headers.each do |k, vs|
|
710
784
|
case k.downcase
|
711
785
|
when CONTENT_LENGTH2
|
786
|
+
next if possible_header_injection?(vs)
|
712
787
|
content_length = vs
|
713
788
|
next
|
714
789
|
when TRANSFER_ENCODING
|
@@ -721,6 +796,7 @@ module Puma
|
|
721
796
|
|
722
797
|
if vs.respond_to?(:to_s) && !vs.to_s.empty?
|
723
798
|
vs.to_s.split(NEWLINE).each do |v|
|
799
|
+
next if possible_header_injection?(v)
|
724
800
|
lines.append k, colon, v, line_ending
|
725
801
|
end
|
726
802
|
else
|
@@ -987,5 +1063,10 @@ module Puma
|
|
987
1063
|
def shutting_down?
|
988
1064
|
@status == :stop || @status == :restart
|
989
1065
|
end
|
1066
|
+
|
1067
|
+
def possible_header_injection?(header_value)
|
1068
|
+
HTTP_INJECTION_REGEX =~ header_value.to_s
|
1069
|
+
end
|
1070
|
+
private :possible_header_injection?
|
990
1071
|
end
|
991
1072
|
end
|
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
|
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,42 @@ 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.
|
156
197
|
def wait_until_not_full
|
157
198
|
@mutex.synchronize do
|
158
199
|
while true
|
159
200
|
return if @shutdown
|
160
|
-
return if @waiting > 0
|
161
201
|
|
162
202
|
# If we can still spin up new threads and there
|
163
|
-
# is work queued
|
203
|
+
# is work queued that cannot be handled by waiting
|
204
|
+
# threads, then accept more work until we would
|
164
205
|
# spin up the max number of threads.
|
165
|
-
return if @todo.size < @max - @spawned
|
206
|
+
return if @todo.size - @waiting < @max - @spawned
|
166
207
|
|
167
208
|
@not_full.wait @mutex
|
168
209
|
end
|
data/lib/puma/util.rb
CHANGED
data/lib/rack/handler/puma.rb
CHANGED
@@ -49,6 +49,9 @@ module Rack
|
|
49
49
|
self.set_host_port_to_config(host, port, user_config)
|
50
50
|
end
|
51
51
|
|
52
|
+
if default_options[:Host]
|
53
|
+
file_config.set_default_host(default_options[:Host])
|
54
|
+
end
|
52
55
|
self.set_host_port_to_config(default_options[:Host], default_options[:Port], default_config)
|
53
56
|
|
54
57
|
user_config.app app
|
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.
|
4
|
+
version: 3.12.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Evan Phoenix
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-05-19 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server
|
14
14
|
for Ruby/Rack applications. Puma is intended for use in both development and production
|
@@ -116,15 +116,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
116
116
|
requirements:
|
117
117
|
- - ">="
|
118
118
|
- !ruby/object:Gem::Version
|
119
|
-
version:
|
119
|
+
version: '2.2'
|
120
120
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - ">="
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: '0'
|
125
125
|
requirements: []
|
126
|
-
|
127
|
-
rubygems_version: 2.7.6
|
126
|
+
rubygems_version: 3.0.3
|
128
127
|
signing_key:
|
129
128
|
specification_version: 4
|
130
129
|
summary: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for
|