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/const.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  #encoding: utf-8
2
+ # frozen_string_literal: true
3
+
2
4
  module Puma
3
5
  class UnsupportedOption < RuntimeError
4
6
  end
@@ -98,8 +100,8 @@ module Puma
98
100
  # too taxing on performance.
99
101
  module Const
100
102
 
101
- PUMA_VERSION = VERSION = "3.11.4".freeze
102
- CODE_NAME = "Love Song".freeze
103
+ PUMA_VERSION = VERSION = "3.12.6".freeze
104
+ CODE_NAME = "Llamas in Pajamas".freeze
103
105
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
104
106
 
105
107
  FAST_TRACK_KA_TIMEOUT = 0.2
@@ -116,6 +118,13 @@ module Puma
116
118
  # sending data back
117
119
  WRITE_TIMEOUT = 10
118
120
 
121
+ # How many requests to attempt inline before sending a client back to
122
+ # the reactor to be subject to normal ordering. The idea here is that
123
+ # we amortize the cost of going back to the reactor for a well behaved
124
+ # but very "greedy" client across 10 requests. This prevents a not
125
+ # well behaved client from monopolizing the thread forever.
126
+ MAX_FAST_INLINE = 10
127
+
119
128
  # The original URI requested by the client.
120
129
  REQUEST_URI= 'REQUEST_URI'.freeze
121
130
  REQUEST_PATH = 'REQUEST_PATH'.freeze
@@ -219,6 +228,7 @@ module Puma
219
228
  COLON = ": ".freeze
220
229
 
221
230
  NEWLINE = "\n".freeze
231
+ HTTP_INJECTION_REGEX = /[\r\n]/.freeze
222
232
 
223
233
  HIJACK_P = "rack.hijack?".freeze
224
234
  HIJACK = "rack.hijack".freeze
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
- require 'puma/state_file'
3
- require 'puma/const'
4
- require 'puma/detect'
5
- require 'puma/configuration'
4
+ require_relative 'state_file'
5
+ require_relative 'const'
6
+ require_relative 'detect'
7
+ require_relative 'configuration'
6
8
  require 'uri'
7
9
  require 'socket'
8
10
 
@@ -129,7 +131,7 @@ module Puma
129
131
  uri = URI.parse @control_url
130
132
 
131
133
  # create server object by scheme
132
- @server = case uri.scheme
134
+ server = case uri.scheme
133
135
  when "tcp"
134
136
  TCPSocket.new uri.host, uri.port
135
137
  when "unix"
@@ -147,9 +149,9 @@ module Puma
147
149
  url = url + "?token=#{@control_auth_token}"
148
150
  end
149
151
 
150
- @server << "GET #{url} HTTP/1.0\r\n\r\n"
152
+ server << "GET #{url} HTTP/1.0\r\n\r\n"
151
153
 
152
- unless data = @server.read
154
+ unless data = server.read
153
155
  raise "Server closed connection before responding"
154
156
  end
155
157
 
@@ -172,8 +174,8 @@ module Puma
172
174
  message "Command #{@command} sent success"
173
175
  message response.last if @command == "stats" || @command == "gc-stats"
174
176
  end
175
-
176
- @server.close
177
+ ensure
178
+ server.close if server && !server.closed?
177
179
  end
178
180
 
179
181
  def send_signal
@@ -220,7 +222,7 @@ module Puma
220
222
  end
221
223
 
222
224
  def run
223
- start if @command == "start"
225
+ return start if @command == "start"
224
226
 
225
227
  prepare_configuration
226
228
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/launcher'
2
4
  require 'puma/configuration'
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Process
2
4
 
3
5
  # This overrides the default version because it is broken if it
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  module Delegation
3
5
  def forward(what, who)
data/lib/puma/detect.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  IS_JRUBY = defined?(JRUBY_VERSION)
3
5
 
data/lib/puma/dsl.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  # The methods that are available for use inside the config file.
3
5
  # These same methods are used in Puma cli and the rack handler
@@ -55,6 +57,14 @@ module Puma
55
57
  @plugins.clear
56
58
  end
57
59
 
60
+ def set_default_host(host)
61
+ @options[:default_host] = host
62
+ end
63
+
64
+ def default_host
65
+ @options[:default_host] || Configuration::DefaultTCPHost
66
+ end
67
+
58
68
  def inject(&blk)
59
69
  instance_eval(&blk)
60
70
  end
@@ -138,7 +148,7 @@ module Puma
138
148
  # Define the TCP port to bind to. Use +bind+ for more advanced options.
139
149
  #
140
150
  def port(port, host=nil)
141
- host ||= Configuration::DefaultTCPHost
151
+ host ||= default_host
142
152
  bind "tcp://#{host}:#{port}"
143
153
  end
144
154
 
@@ -493,7 +503,7 @@ module Puma
493
503
  when Hash
494
504
  if hdr = val[:header]
495
505
  @options[:remote_address] = :header
496
- @options[:remote_address_header] = "HTTP_" + hdr.upcase.gsub("-", "_")
506
+ @options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_")
497
507
  else
498
508
  raise "Invalid value for set_remote_address - #{val.inspect}"
499
509
  end
data/lib/puma/events.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/const'
2
4
  require "puma/null_io"
3
5
  require 'stringio'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/detect'
2
4
 
3
5
  if Puma.jruby?
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'java'
2
4
 
3
5
  # Conservative native JRuby/Java implementation of IOBuffer
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'ffi'
2
4
 
3
5
  module Puma
data/lib/puma/launcher.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/events'
2
4
  require 'puma/detect'
3
5
 
@@ -63,8 +65,8 @@ module Puma
63
65
 
64
66
  generate_restart_data
65
67
 
66
- if clustered? && (Puma.jruby? || Puma.windows?)
67
- unsupported 'worker mode not supported on JRuby or Windows'
68
+ if clustered? && !Process.respond_to?(:fork)
69
+ unsupported "worker mode not supported on #{RUBY_ENGINE} on this platform"
68
70
  end
69
71
 
70
72
  if @options[:daemon] && Puma.windows?
data/lib/puma/minissl.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'io/wait'
3
5
  rescue LoadError
@@ -124,7 +126,7 @@ module Puma
124
126
 
125
127
  def read_and_drop(timeout = 1)
126
128
  return :timeout unless IO.select([@socket], nil, nil, timeout)
127
- read_nonblock(1024)
129
+ return :eof unless read_nonblock(1024)
128
130
  :drop
129
131
  rescue Errno::EAGAIN
130
132
  # do nothing
@@ -141,7 +143,7 @@ module Puma
141
143
  # Don't let this socket hold this loop forever.
142
144
  # If it can't send more packets within 1s, then give up.
143
145
  while should_drop_bytes?
144
- return if read_and_drop(1) == :timeout
146
+ return if [:timeout, :eof].include?(read_and_drop(1))
145
147
  end
146
148
  rescue IOError, SystemCallError
147
149
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
@@ -180,6 +182,7 @@ module Puma
180
182
  # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair
181
183
  attr_reader :keystore
182
184
  attr_accessor :keystore_pass
185
+ attr_accessor :ssl_cipher_list
183
186
 
184
187
  def keystore=(keystore)
185
188
  raise ArgumentError, "No such keystore file '#{keystore}'" unless File.exist? keystore
@@ -195,6 +198,7 @@ module Puma
195
198
  attr_reader :key
196
199
  attr_reader :cert
197
200
  attr_reader :ca
201
+ attr_accessor :ssl_cipher_filter
198
202
 
199
203
  def key=(key)
200
204
  raise ArgumentError, "No such key file '#{key}'" unless File.exist? key
data/lib/puma/null_io.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  # Provides an IO-like object that always appears to contain no data.
3
5
  # Used as the value for rack.input when the request has no body.
data/lib/puma/plugin.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Puma
2
4
  class UnknownPlugin < RuntimeError; end
3
5
 
@@ -110,7 +110,8 @@ module Puma::Rack
110
110
 
111
111
  has_options = false
112
112
  server.valid_options.each do |name, description|
113
- next if name.to_s.match(/^(Host|Port)[^a-zA-Z]/) # ignore handler's host and port options, we do our own.
113
+ next if name.to_s =~ /^(Host|Port)[^a-zA-Z]/ # ignore handler's host and port options, we do our own.
114
+
114
115
  info << " -O %-21s %s" % [name, description]
115
116
  has_options = true
116
117
  end
data/lib/puma/reactor.rb CHANGED
@@ -1,16 +1,57 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/util'
2
4
  require 'puma/minissl'
3
5
 
4
6
  module Puma
7
+ # Internal Docs, Not a public interface.
8
+ #
9
+ # The Reactor object is responsible for ensuring that a request has been
10
+ # completely received before it starts to be processed. This may be known as read buffering.
11
+ # If read buffering is not done, and no other read buffering is performed (such as by an application server
12
+ # such as nginx) then the application would be subject to a slow client attack.
13
+ #
14
+ # Each Puma "worker" process has its own Reactor. For example if you start puma with `$ puma -w 5` then
15
+ # it will have 5 workers and each worker will have it's own reactor.
16
+ #
17
+ # For a graphical representation of how the reactor works see [architecture.md](https://github.com/puma/puma/blob/master/docs/architecture.md#connection-pipeline).
18
+ #
19
+ # ## Reactor Flow
20
+ #
21
+ # A request comes into a `Puma::Server` instance, it is then passed to a `Puma::Reactor` instance.
22
+ # The reactor stores the request in an array and calls `IO.select` on the array in a loop.
23
+ #
24
+ # When the request is written to by the client then the `IO.select` will "wake up" and
25
+ # return the references to any objects that caused it to "wake". The reactor
26
+ # then loops through each of these request objects, and sees if they're complete. If they
27
+ # have a full header and body then the reactor passes the request to a thread pool.
28
+ # Once in a thread pool, a "worker thread" can run the the application's Ruby code against the request.
29
+ #
30
+ # If the request is not complete, then it stays in the array, and the next time any
31
+ # data is written to that socket reference, then the loop is woken up and it is checked for completeness again.
32
+ #
33
+ # A detailed example is given in the docs for `run_internal` which is where the bulk
34
+ # of this logic lives.
5
35
  class Reactor
6
36
  DefaultSleepFor = 5
7
37
 
38
+ # Creates an instance of Puma::Reactor
39
+ #
40
+ # The `server` argument is an instance of `Puma::Server`
41
+ # this is used to write a response for "low level errors"
42
+ # when there is an exception inside of the reactor.
43
+ #
44
+ # The `app_pool` is an instance of `Puma::ThreadPool`.
45
+ # Once a request is fully formed (header and body are received)
46
+ # it will be passed to the `app_pool`.
8
47
  def initialize(server, app_pool)
9
48
  @server = server
10
49
  @events = server.events
11
50
  @app_pool = app_pool
12
51
 
13
52
  @mutex = Mutex.new
53
+
54
+ # Read / Write pipes to wake up internal while loop
14
55
  @ready, @trigger = Puma::Util.pipe
15
56
  @input = []
16
57
  @sleep_for = DefaultSleepFor
@@ -21,6 +62,64 @@ module Puma
21
62
 
22
63
  private
23
64
 
65
+
66
+ # Until a request is added via the `add` method this method will internally
67
+ # loop, waiting on the `sockets` array objects. The only object in this
68
+ # array at first is the `@ready` IO object, which is the read end of a pipe
69
+ # connected to `@trigger` object. When `@trigger` is written to, then the loop
70
+ # will break on `IO.select` and return an array.
71
+ #
72
+ # ## When a request is added:
73
+ #
74
+ # When the `add` method is called, an instance of `Puma::Client` is added to the `@input` array.
75
+ # Next the `@ready` pipe is "woken" by writing a string of `"*"` to `@trigger`.
76
+ #
77
+ # When that happens, the internal loop stops blocking at `IO.select` and returns a reference
78
+ # to whatever "woke" it up. On the very first loop, the only thing in `sockets` is `@ready`.
79
+ # When `@trigger` is written-to, the loop "wakes" and the `ready`
80
+ # variable returns an array of arrays that looks like `[[#<IO:fd 10>], [], []]` where the
81
+ # first IO object is the `@ready` object. This first array `[#<IO:fd 10>]`
82
+ # is saved as a `reads` variable.
83
+ #
84
+ # The `reads` variable is iterated through. In the case that the object
85
+ # is the same as the `@ready` input pipe, then we know that there was a `trigger` event.
86
+ #
87
+ # If there was a trigger event, then one byte of `@ready` is read into memory. In the case of the first request,
88
+ # the reactor sees that it's a `"*"` value and the reactor adds the contents of `@input` into the `sockets` array.
89
+ # The while then loop continues to iterate again, but now the `sockets` array contains a `Puma::Client` instance in addition
90
+ # to the `@ready` IO object. For example: `[#<IO:fd 10>, #<Puma::Client:0x3fdc1103bee8 @ready=false>]`.
91
+ #
92
+ # Since the `Puma::Client` in this example has data that has not been read yet,
93
+ # the `IO.select` is immediately able to "wake" and read from the `Puma::Client`. At this point the
94
+ # `ready` output looks like this: `[[#<Puma::Client:0x3fdc1103bee8 @ready=false>], [], []]`.
95
+ #
96
+ # Each element in the first entry is iterated over. The `Puma::Client` object is not
97
+ # the `@ready` pipe, so the reactor checks to see if it has the fully header and body with
98
+ # the `Puma::Client#try_to_finish` method. If the full request has been sent,
99
+ # then the request is passed off to the `@app_pool` thread pool so that a "worker thread"
100
+ # can pick up the request and begin to execute application logic. This is done
101
+ # via `@app_pool << c`. The `Puma::Client` is then removed from the `sockets` array.
102
+ #
103
+ # If the request body is not present then nothing will happen, and the loop will iterate
104
+ # again. When the client sends more data to the socket the `Puma::Client` object will
105
+ # wake up the `IO.select` and it can again be checked to see if it's ready to be
106
+ # passed to the thread pool.
107
+ #
108
+ # ## Time Out Case
109
+ #
110
+ # In addition to being woken via a write to one of the sockets the `IO.select` will
111
+ # periodically "time out" of the sleep. One of the functions of this is to check for
112
+ # any requests that have "timed out". At the end of the loop it's checked to see if
113
+ # the first element in the `@timeout` array has exceed it's allowed time. If so,
114
+ # the client object is removed from the timeout aray, a 408 response is written.
115
+ # Then it's connection is closed, and the object is removed from the `sockets` array
116
+ # that watches for new data.
117
+ #
118
+ # This behavior loops until all the objects that have timed out have been removed.
119
+ #
120
+ # Once all the timeouts have been processed, the next duration of the `IO.select` sleep
121
+ # will be set to be equal to the amount of time it will take for the next timeout to occur.
122
+ # This calculation happens in `calculate_sleep`.
24
123
  def run_internal
25
124
  sockets = @sockets
26
125
 
@@ -163,6 +262,16 @@ module Puma
163
262
  end
164
263
  end
165
264
 
265
+ # The `calculate_sleep` sets the value that the `IO.select` will
266
+ # sleep for in the main reactor loop when no sockets are being written to.
267
+ #
268
+ # The values kept in `@timeouts` are sorted so that the first timeout
269
+ # comes first in the array. When there are no timeouts the default timeout is used.
270
+ #
271
+ # Otherwise a sleep value is set that is the same as the amount of time it
272
+ # would take for the first element to time out.
273
+ #
274
+ # If that value is in the past, then a sleep value of zero is used.
166
275
  def calculate_sleep
167
276
  if @timeouts.empty?
168
277
  @sleep_for = DefaultSleepFor
@@ -177,6 +286,31 @@ module Puma
177
286
  end
178
287
  end
179
288
 
289
+ # This method adds a connection to the reactor
290
+ #
291
+ # Typically called by `Puma::Server` the value passed in
292
+ # is usually a `Puma::Client` object that responds like an IO
293
+ # object.
294
+ #
295
+ # The main body of the reactor loop is in `run_internal` and it
296
+ # will sleep on `IO.select`. When a new connection is added to the
297
+ # reactor it cannot be added directly to the `sockets` aray, because
298
+ # the `IO.select` will not be watching for it yet.
299
+ #
300
+ # Instead what needs to happen is that `IO.select` needs to be woken up,
301
+ # the contents of `@input` added to the `sockets` array, and then
302
+ # another call to `IO.select` needs to happen. Since the `Puma::Client`
303
+ # object can be read immediately, it does not block, but instead returns
304
+ # right away.
305
+ #
306
+ # This behavior is accomplished by writing to `@trigger` which wakes up
307
+ # the `IO.select` and then there is logic to detect the value of `*`,
308
+ # pull the contents from `@input` and add them to the sockets array.
309
+ #
310
+ # If the object passed in has a timeout value in `timeout_at` then
311
+ # it is added to a `@timeouts` array. This array is then re-arranged
312
+ # so that the first element to timeout will be at the front of the
313
+ # array. Then a value to sleep for is derived in the call to `calculate_sleep`
180
314
  def add(c)
181
315
  @mutex.synchronize do
182
316
  @input << c
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
@@ -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