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.

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
- @check.close
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
- unless client.reset(@status == :run)
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
- %Q!{ "backlog": #{b}, "running": #{r} }!
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
 
3
5
  module Puma
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  class TCPLogger
3
5
  def initialize(logger, app, quiet=false)
@@ -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, then accept more work until we would
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
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
  major, minor, patch = RUBY_VERSION.split('.').map { |v| v.to_i }
2
3
 
3
4
  if major == 1 && minor == 9 && patch == 3 && RUBY_PATCHLEVEL < 125
@@ -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.11.4
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: 2018-04-12 00:00:00.000000000 Z
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: 1.9.3
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
- rubyforge_project:
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