puma 3.12.6 → 4.3.10

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.

Files changed (61) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +145 -3
  3. data/README.md +76 -48
  4. data/docs/architecture.md +1 -0
  5. data/docs/deployment.md +24 -4
  6. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  7. data/docs/images/puma-connection-flow.png +0 -0
  8. data/docs/images/puma-general-arch.png +0 -0
  9. data/docs/plugins.md +20 -10
  10. data/docs/restart.md +4 -2
  11. data/docs/systemd.md +27 -9
  12. data/docs/tcp_mode.md +96 -0
  13. data/ext/puma_http11/PumaHttp11Service.java +2 -0
  14. data/ext/puma_http11/extconf.rb +13 -0
  15. data/ext/puma_http11/http11_parser.c +58 -70
  16. data/ext/puma_http11/http11_parser.java.rl +21 -37
  17. data/ext/puma_http11/http11_parser_common.rl +4 -4
  18. data/ext/puma_http11/mini_ssl.c +78 -8
  19. data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
  20. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +86 -99
  21. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  22. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -4
  23. data/ext/puma_http11/puma_http11.c +3 -0
  24. data/lib/puma/accept_nonblock.rb +7 -1
  25. data/lib/puma/app/status.rb +37 -29
  26. data/lib/puma/binder.rb +38 -60
  27. data/lib/puma/cli.rb +4 -0
  28. data/lib/puma/client.rb +242 -208
  29. data/lib/puma/cluster.rb +53 -30
  30. data/lib/puma/configuration.rb +4 -3
  31. data/lib/puma/const.rb +22 -18
  32. data/lib/puma/control_cli.rb +30 -5
  33. data/lib/puma/dsl.rb +299 -75
  34. data/lib/puma/events.rb +4 -1
  35. data/lib/puma/io_buffer.rb +1 -6
  36. data/lib/puma/launcher.rb +95 -53
  37. data/lib/puma/minissl/context_builder.rb +76 -0
  38. data/lib/puma/minissl.rb +35 -17
  39. data/lib/puma/plugin/tmp_restart.rb +2 -0
  40. data/lib/puma/plugin.rb +5 -2
  41. data/lib/puma/rack/builder.rb +2 -0
  42. data/lib/puma/rack/urlmap.rb +2 -0
  43. data/lib/puma/rack_default.rb +2 -0
  44. data/lib/puma/reactor.rb +110 -57
  45. data/lib/puma/runner.rb +11 -3
  46. data/lib/puma/server.rb +73 -57
  47. data/lib/puma/single.rb +3 -3
  48. data/lib/puma/thread_pool.rb +15 -33
  49. data/lib/puma/util.rb +1 -6
  50. data/lib/puma.rb +8 -0
  51. data/lib/rack/handler/puma.rb +3 -3
  52. data/tools/docker/Dockerfile +16 -0
  53. data/tools/jungle/init.d/puma +6 -6
  54. data/tools/trickletest.rb +0 -1
  55. metadata +26 -13
  56. data/lib/puma/compat.rb +0 -14
  57. data/lib/puma/convenient.rb +0 -25
  58. data/lib/puma/daemon_ext.rb +0 -33
  59. data/lib/puma/delegation.rb +0 -13
  60. data/lib/puma/java_io_buffer.rb +0 -47
  61. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
data/lib/puma/reactor.rb CHANGED
@@ -3,6 +3,8 @@
3
3
  require 'puma/util'
4
4
  require 'puma/minissl'
5
5
 
6
+ require 'nio'
7
+
6
8
  module Puma
7
9
  # Internal Docs, Not a public interface.
8
10
  #
@@ -18,12 +20,13 @@ module Puma
18
20
  #
19
21
  # ## Reactor Flow
20
22
  #
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
+ # A connection comes into a `Puma::Server` instance, it is then passed to a `Puma::Reactor` instance,
24
+ # which stores it in an array and waits for any of the connections to be ready for reading.
23
25
  #
24
- # When the request is written to by the client then the `IO.select` will "wake up" and
26
+ # The waiting/wake up is performed with nio4r, which will use the appropriate backend (libev, Java NIO or
27
+ # just plain IO#select). The call to `NIO::Selector#select` will "wake up" and
25
28
  # 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
29
+ # then loops through each of these request objects, and sees if they're complete. If they
27
30
  # have a full header and body then the reactor passes the request to a thread pool.
28
31
  # Once in a thread pool, a "worker thread" can run the the application's Ruby code against the request.
29
32
  #
@@ -38,7 +41,7 @@ module Puma
38
41
  # Creates an instance of Puma::Reactor
39
42
  #
40
43
  # The `server` argument is an instance of `Puma::Server`
41
- # this is used to write a response for "low level errors"
44
+ # that is used to write a response for "low level errors"
42
45
  # when there is an exception inside of the reactor.
43
46
  #
44
47
  # The `app_pool` is an instance of `Puma::ThreadPool`.
@@ -49,6 +52,8 @@ module Puma
49
52
  @events = server.events
50
53
  @app_pool = app_pool
51
54
 
55
+ @selector = NIO::Selector.new
56
+
52
57
  @mutex = Mutex.new
53
58
 
54
59
  # Read / Write pipes to wake up internal while loop
@@ -57,24 +62,26 @@ module Puma
57
62
  @sleep_for = DefaultSleepFor
58
63
  @timeouts = []
59
64
 
60
- @sockets = [@ready]
65
+ mon = @selector.register(@ready, :r)
66
+ mon.value = @ready
67
+
68
+ @monitors = [mon]
61
69
  end
62
70
 
63
71
  private
64
72
 
65
-
66
73
  # Until a request is added via the `add` method this method will internally
67
74
  # loop, waiting on the `sockets` array objects. The only object in this
68
75
  # array at first is the `@ready` IO object, which is the read end of a pipe
69
76
  # connected to `@trigger` object. When `@trigger` is written to, then the loop
70
- # will break on `IO.select` and return an array.
77
+ # will break on `NIO::Selector#select` and return an array.
71
78
  #
72
79
  # ## When a request is added:
73
80
  #
74
81
  # When the `add` method is called, an instance of `Puma::Client` is added to the `@input` array.
75
82
  # Next the `@ready` pipe is "woken" by writing a string of `"*"` to `@trigger`.
76
83
  #
77
- # When that happens, the internal loop stops blocking at `IO.select` and returns a reference
84
+ # When that happens, the internal loop stops blocking at `NIO::Selector#select` and returns a reference
78
85
  # to whatever "woke" it up. On the very first loop, the only thing in `sockets` is `@ready`.
79
86
  # When `@trigger` is written-to, the loop "wakes" and the `ready`
80
87
  # variable returns an array of arrays that looks like `[[#<IO:fd 10>], [], []]` where the
@@ -90,11 +97,11 @@ module Puma
90
97
  # to the `@ready` IO object. For example: `[#<IO:fd 10>, #<Puma::Client:0x3fdc1103bee8 @ready=false>]`.
91
98
  #
92
99
  # 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
100
+ # the `NIO::Selector#select` is immediately able to "wake" and read from the `Puma::Client`. At this point the
94
101
  # `ready` output looks like this: `[[#<Puma::Client:0x3fdc1103bee8 @ready=false>], [], []]`.
95
102
  #
96
103
  # 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
104
+ # the `@ready` pipe, so the reactor checks to see if it has the full header and body with
98
105
  # the `Puma::Client#try_to_finish` method. If the full request has been sent,
99
106
  # then the request is passed off to the `@app_pool` thread pool so that a "worker thread"
100
107
  # can pick up the request and begin to execute application logic. This is done
@@ -102,56 +109,93 @@ module Puma
102
109
  #
103
110
  # If the request body is not present then nothing will happen, and the loop will iterate
104
111
  # 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
112
+ # wake up the `NIO::Selector#select` and it can again be checked to see if it's ready to be
106
113
  # passed to the thread pool.
107
114
  #
108
115
  # ## Time Out Case
109
116
  #
110
- # In addition to being woken via a write to one of the sockets the `IO.select` will
117
+ # In addition to being woken via a write to one of the sockets the `NIO::Selector#select` will
111
118
  # periodically "time out" of the sleep. One of the functions of this is to check for
112
119
  # 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
120
+ # the first element in the `@timeout` array has exceed its allowed time. If so,
121
+ # the client object is removed from the timeout array, a 408 response is written.
122
+ # Then its connection is closed, and the object is removed from the `sockets` array
116
123
  # that watches for new data.
117
124
  #
118
125
  # This behavior loops until all the objects that have timed out have been removed.
119
126
  #
120
- # Once all the timeouts have been processed, the next duration of the `IO.select` sleep
127
+ # Once all the timeouts have been processed, the next duration of the `NIO::Selector#select` sleep
121
128
  # will be set to be equal to the amount of time it will take for the next timeout to occur.
122
129
  # This calculation happens in `calculate_sleep`.
123
130
  def run_internal
124
- sockets = @sockets
131
+ monitors = @monitors
132
+ selector = @selector
125
133
 
126
134
  while true
127
135
  begin
128
- ready = IO.select sockets, nil, nil, @sleep_for
136
+ ready = selector.select @sleep_for
129
137
  rescue IOError => e
130
138
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
131
- if sockets.any? { |socket| socket.closed? }
139
+ if monitors.any? { |mon| mon.value.closed? }
132
140
  STDERR.puts "Error in select: #{e.message} (#{e.class})"
133
141
  STDERR.puts e.backtrace
134
- sockets = sockets.reject { |socket| socket.closed? }
142
+
143
+ monitors.reject! do |mon|
144
+ if mon.value.closed?
145
+ selector.deregister mon.value
146
+ true
147
+ end
148
+ end
149
+
135
150
  retry
136
151
  else
137
152
  raise
138
153
  end
139
154
  end
140
155
 
141
- if ready and reads = ready[0]
142
- reads.each do |c|
143
- if c == @ready
156
+ if ready
157
+ ready.each do |mon|
158
+ if mon.value == @ready
144
159
  @mutex.synchronize do
145
160
  case @ready.read(1)
146
161
  when "*"
147
- sockets += @input
162
+ @input.each do |c|
163
+ mon = nil
164
+ begin
165
+ begin
166
+ mon = selector.register(c, :r)
167
+ rescue ArgumentError
168
+ # There is a bug where we seem to be registering an already registered
169
+ # client. This code deals with this situation but I wish we didn't have to.
170
+ monitors.delete_if { |submon| submon.value.to_io == c.to_io }
171
+ selector.deregister(c)
172
+ mon = selector.register(c, :r)
173
+ end
174
+ rescue IOError
175
+ # Means that the io is closed, so we should ignore this request
176
+ # entirely
177
+ else
178
+ mon.value = c
179
+ @timeouts << mon if c.timeout_at
180
+ monitors << mon
181
+ end
182
+ end
148
183
  @input.clear
184
+
185
+ @timeouts.sort! { |a,b| a.value.timeout_at <=> b.value.timeout_at }
186
+ calculate_sleep
149
187
  when "c"
150
- sockets.delete_if do |s|
151
- if s == @ready
188
+ monitors.reject! do |submon|
189
+ if submon.value == @ready
152
190
  false
153
191
  else
154
- s.close
192
+ submon.value.close
193
+ begin
194
+ selector.deregister submon.value
195
+ rescue IOError
196
+ # nio4r on jruby seems to throw an IOError here if the IO is closed, so
197
+ # we need to swallow it.
198
+ end
155
199
  true
156
200
  end
157
201
  end
@@ -160,40 +204,48 @@ module Puma
160
204
  end
161
205
  end
162
206
  else
207
+ c = mon.value
208
+
163
209
  # We have to be sure to remove it from the timeout
164
210
  # list or we'll accidentally close the socket when
165
211
  # it's in use!
166
212
  if c.timeout_at
167
213
  @mutex.synchronize do
168
- @timeouts.delete c
214
+ @timeouts.delete mon
169
215
  end
170
216
  end
171
217
 
172
218
  begin
173
219
  if c.try_to_finish
174
220
  @app_pool << c
175
- sockets.delete c
221
+ clear_monitor mon
176
222
  end
177
223
 
178
224
  # Don't report these to the lowlevel_error handler, otherwise
179
225
  # will be flooding them with errors when persistent connections
180
226
  # are closed.
181
227
  rescue ConnectionError
182
- c.write_500
228
+ c.write_error(500)
183
229
  c.close
184
230
 
185
- sockets.delete c
231
+ clear_monitor mon
186
232
 
187
233
  # SSL handshake failure
188
234
  rescue MiniSSL::SSLError => e
189
235
  @server.lowlevel_error(e, c.env)
190
236
 
191
237
  ssl_socket = c.io
192
- addr = ssl_socket.peeraddr.last
238
+ begin
239
+ addr = ssl_socket.peeraddr.last
240
+ # EINVAL can happen when browser closes socket w/security exception
241
+ rescue IOError, Errno::EINVAL
242
+ addr = "<unknown>"
243
+ end
244
+
193
245
  cert = ssl_socket.peercert
194
246
 
195
247
  c.close
196
- sockets.delete c
248
+ clear_monitor mon
197
249
 
198
250
  @events.ssl_error @server, addr, cert, e
199
251
 
@@ -201,19 +253,19 @@ module Puma
201
253
  rescue HttpParserError => e
202
254
  @server.lowlevel_error(e, c.env)
203
255
 
204
- c.write_400
256
+ c.write_error(400)
205
257
  c.close
206
258
 
207
- sockets.delete c
259
+ clear_monitor mon
208
260
 
209
261
  @events.parse_error @server, c.env, e
210
262
  rescue StandardError => e
211
263
  @server.lowlevel_error(e, c.env)
212
264
 
213
- c.write_500
265
+ c.write_error(500)
214
266
  c.close
215
267
 
216
- sockets.delete c
268
+ clear_monitor mon
217
269
  end
218
270
  end
219
271
  end
@@ -223,11 +275,13 @@ module Puma
223
275
  @mutex.synchronize do
224
276
  now = Time.now
225
277
 
226
- while @timeouts.first.timeout_at < now
227
- c = @timeouts.shift
228
- c.write_408 if c.in_data_phase
278
+ while @timeouts.first.value.timeout_at < now
279
+ mon = @timeouts.shift
280
+ c = mon.value
281
+ c.write_error(408) if c.in_data_phase
229
282
  c.close
230
- sockets.delete c
283
+
284
+ clear_monitor mon
231
285
 
232
286
  break if @timeouts.empty?
233
287
  end
@@ -238,6 +292,11 @@ module Puma
238
292
  end
239
293
  end
240
294
 
295
+ def clear_monitor(mon)
296
+ @selector.deregister mon.value
297
+ @monitors.delete mon
298
+ end
299
+
241
300
  public
242
301
 
243
302
  def run
@@ -249,6 +308,7 @@ module Puma
249
308
 
250
309
  def run_in_thread
251
310
  @thread = Thread.new do
311
+ Puma.set_thread_name "reactor"
252
312
  begin
253
313
  run_internal
254
314
  rescue StandardError => e
@@ -262,7 +322,7 @@ module Puma
262
322
  end
263
323
  end
264
324
 
265
- # The `calculate_sleep` sets the value that the `IO.select` will
325
+ # The `calculate_sleep` sets the value that the `NIO::Selector#select` will
266
326
  # sleep for in the main reactor loop when no sockets are being written to.
267
327
  #
268
328
  # The values kept in `@timeouts` are sorted so that the first timeout
@@ -276,7 +336,7 @@ module Puma
276
336
  if @timeouts.empty?
277
337
  @sleep_for = DefaultSleepFor
278
338
  else
279
- diff = @timeouts.first.timeout_at.to_f - Time.now.to_f
339
+ diff = @timeouts.first.value.timeout_at.to_f - Time.now.to_f
280
340
 
281
341
  if diff < 0.0
282
342
  @sleep_for = 0
@@ -293,18 +353,18 @@ module Puma
293
353
  # object.
294
354
  #
295
355
  # 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.
356
+ # will sleep on `NIO::Selector#select`. When a new connection is added to the
357
+ # reactor it cannot be added directly to the `sockets` array, because
358
+ # the `NIO::Selector#select` will not be watching for it yet.
299
359
  #
300
- # Instead what needs to happen is that `IO.select` needs to be woken up,
360
+ # Instead what needs to happen is that `NIO::Selector#select` needs to be woken up,
301
361
  # the contents of `@input` added to the `sockets` array, and then
302
- # another call to `IO.select` needs to happen. Since the `Puma::Client`
362
+ # another call to `NIO::Selector#select` needs to happen. Since the `Puma::Client`
303
363
  # object can be read immediately, it does not block, but instead returns
304
364
  # right away.
305
365
  #
306
366
  # 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 `*`,
367
+ # the `NIO::Selector#select` and then there is logic to detect the value of `*`,
308
368
  # pull the contents from `@input` and add them to the sockets array.
309
369
  #
310
370
  # If the object passed in has a timeout value in `timeout_at` then
@@ -315,13 +375,6 @@ module Puma
315
375
  @mutex.synchronize do
316
376
  @input << c
317
377
  @trigger << "*"
318
-
319
- if c.timeout_at
320
- @timeouts << c
321
- @timeouts.sort! { |a,b| a.timeout_at <=> b.timeout_at }
322
-
323
- calculate_sleep
324
- end
325
378
  end
326
379
  end
327
380
 
data/lib/puma/runner.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'puma/server'
4
4
  require 'puma/const'
5
+ require 'puma/minissl/context_builder'
5
6
 
6
7
  module Puma
7
8
  # Generic class that is used by `Puma::Cluster` and `Puma::Single` to
@@ -14,6 +15,7 @@ module Puma
14
15
  @options = cli.options
15
16
  @app = nil
16
17
  @control = nil
18
+ @started_at = Time.now
17
19
  end
18
20
 
19
21
  def daemon?
@@ -52,17 +54,23 @@ module Puma
52
54
 
53
55
  uri = URI.parse str
54
56
 
55
- app = Puma::App::Status.new @launcher
56
-
57
57
  if token = @options[:control_auth_token]
58
- app.auth_token = token unless token.empty? or token == :none
58
+ token = nil if token.empty? || token == 'none'
59
59
  end
60
60
 
61
+ app = Puma::App::Status.new @launcher, token
62
+
61
63
  control = Puma::Server.new app, @launcher.events
62
64
  control.min_threads = 0
63
65
  control.max_threads = 1
64
66
 
65
67
  case uri.scheme
68
+ when "ssl"
69
+ log "* Starting control server on #{str}"
70
+ params = Util.parse_query uri.query
71
+ ctx = MiniSSL::ContextBuilder.new(params, @events).context
72
+
73
+ control.add_ssl_listener uri.host, uri.port, ctx
66
74
  when "tcp"
67
75
  log "* Starting control server on #{str}"
68
76
  control.add_tcp_listener uri.host, uri.port
data/lib/puma/server.rb CHANGED
@@ -6,21 +6,16 @@ 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'
13
- require 'puma/delegation'
14
12
  require 'puma/accept_nonblock'
15
13
  require 'puma/util'
16
14
 
17
15
  require 'puma/puma_http11'
18
16
 
19
- unless Puma.const_defined? "IOBuffer"
20
- require 'puma/io_buffer'
21
- end
22
-
23
17
  require 'socket'
18
+ require 'forwardable'
24
19
 
25
20
  module Puma
26
21
 
@@ -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` instacne.
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`.
@@ -37,7 +32,7 @@ module Puma
37
32
  class Server
38
33
 
39
34
  include Puma::Const
40
- extend Puma::Delegation
35
+ extend Forwardable
41
36
 
42
37
  attr_reader :thread
43
38
  attr_reader :events
@@ -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
 
@@ -95,14 +89,10 @@ module Puma
95
89
 
96
90
  attr_accessor :binder, :leak_stack_on_error, :early_hints
97
91
 
98
- forward :add_tcp_listener, :@binder
99
- forward :add_ssl_listener, :@binder
100
- forward :add_unix_listener, :@binder
101
- forward :connected_port, :@binder
92
+ def_delegators :@binder, :add_tcp_listener, :add_ssl_listener, :add_unix_listener, :connected_port
102
93
 
103
94
  def inherit_binder(bind)
104
95
  @binder = bind
105
- @own_binder = false
106
96
  end
107
97
 
108
98
  def tcp_mode!
@@ -214,7 +204,10 @@ module Puma
214
204
  @events.fire :state, :running
215
205
 
216
206
  if background
217
- @thread = Thread.new { handle_servers_lopez_mode }
207
+ @thread = Thread.new do
208
+ Puma.set_thread_name "server"
209
+ handle_servers_lopez_mode
210
+ end
218
211
  return @thread
219
212
  else
220
213
  handle_servers_lopez_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
- @notify.close
274
-
275
- if @status != :restart and @own_binder
276
- @binder.close
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
 
@@ -323,7 +317,7 @@ module Puma
323
317
 
324
318
  @events.ssl_error self, addr, cert, e
325
319
  rescue HttpParserError => e
326
- client.write_400
320
+ client.write_error(400)
327
321
  client.close
328
322
 
329
323
  @events.parse_error self, client.env, e
@@ -357,7 +351,10 @@ module Puma
357
351
  @events.fire :state, :running
358
352
 
359
353
  if background
360
- @thread = Thread.new { handle_servers }
354
+ @thread = Thread.new do
355
+ Puma.set_thread_name "server"
356
+ handle_servers
357
+ end
361
358
  return @thread
362
359
  else
363
360
  handle_servers
@@ -398,7 +395,10 @@ module Puma
398
395
  end
399
396
 
400
397
  pool << client
401
- pool.wait_until_not_full
398
+ busy_threads = pool.wait_until_not_full
399
+ if busy_threads == 0
400
+ @options[:out_of_band].each(&:call) if @options[:out_of_band]
401
+ end
402
402
  end
403
403
  rescue SystemCallError
404
404
  # nothing
@@ -430,10 +430,6 @@ module Puma
430
430
  ensure
431
431
  @check.close
432
432
  @notify.close
433
-
434
- if @status != :restart and @own_binder
435
- @binder.close
436
- end
437
433
  end
438
434
 
439
435
  @events.fire :state, :done
@@ -487,15 +483,20 @@ module Puma
487
483
 
488
484
  requests += 1
489
485
 
490
- check_for_more_data = @status == :run
486
+ # Closing keepalive sockets after they've made a reasonable
487
+ # number of requests allows Puma to service many connections
488
+ # fairly, even when the number of concurrent connections exceeds
489
+ # the size of the threadpool. It also allows cluster mode Pumas
490
+ # to keep load evenly distributed across workers, because clients
491
+ # are randomly assigned a new worker when opening a new connection.
492
+ #
493
+ # Previously, Puma would kick connections in this conditional back
494
+ # to the reactor. However, because this causes the todo set to increase
495
+ # in size, the wait_until_full mutex would never unlock, leaving
496
+ # any additional connections unserviced.
497
+ break if requests >= MAX_FAST_INLINE
491
498
 
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
+ check_for_more_data = @status == :run
499
500
 
500
501
  unless client.reset(check_for_more_data)
501
502
  close_socket = false
@@ -526,7 +527,7 @@ module Puma
526
527
  rescue HttpParserError => e
527
528
  lowlevel_error(e, client.env)
528
529
 
529
- client.write_400
530
+ client.write_error(400)
530
531
 
531
532
  @events.parse_error self, client.env, e
532
533
 
@@ -534,7 +535,7 @@ module Puma
534
535
  rescue StandardError => e
535
536
  lowlevel_error(e, client.env)
536
537
 
537
- client.write_500
538
+ client.write_error(500)
538
539
 
539
540
  @events.unknown_error self, e, "Read"
540
541
 
@@ -609,19 +610,26 @@ module Puma
609
610
  end
610
611
 
611
612
  def default_server_port(env)
612
- return PORT_443 if env[HTTPS_KEY] == 'on' || env[HTTPS_KEY] == 'https'
613
- env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80
613
+ if ['on', HTTPS].include?(env[HTTPS_KEY]) || env[HTTP_X_FORWARDED_PROTO].to_s[0...5] == HTTPS || env[HTTP_X_FORWARDED_SCHEME] == HTTPS || env[HTTP_X_FORWARDED_SSL] == "on"
614
+ PORT_443
615
+ else
616
+ PORT_80
617
+ end
614
618
  end
615
619
 
616
- # Given the request +env+ from +client+ and a partial request body
617
- # in +body+, finish reading the body if there is one and invoke
618
- # the rack app. Then construct the response and write it back to
619
- # +client+
620
+ # Takes the request +req+, invokes the Rack application to construct
621
+ # the response and writes it back to +req.io+.
622
+ #
623
+ # The second parameter +lines+ is a IO-like object unique to this thread.
624
+ # This is normally an instance of Puma::IOBuffer.
620
625
  #
621
- # +cl+ is the previously fetched Content-Length header if there
622
- # was one. This is an optimization to keep from having to look
623
- # it up again.
626
+ # It'll return +false+ when the connection is closed, this doesn't mean
627
+ # that the response wasn't successful.
624
628
  #
629
+ # It'll return +:async+ if the connection remains open but will be handled
630
+ # elsewhere, i.e. the connection has been hijacked by the Rack application.
631
+ #
632
+ # Finally, it'll return +true+ on keep-alive connections.
625
633
  def handle_request(req, lines)
626
634
  env = req.env
627
635
  client = req.io
@@ -644,24 +652,28 @@ module Puma
644
652
  head = env[REQUEST_METHOD] == HEAD
645
653
 
646
654
  env[RACK_INPUT] = body
647
- env[RACK_URL_SCHEME] = env[HTTPS_KEY] ? HTTPS : HTTP
655
+ env[RACK_URL_SCHEME] = default_server_port(env) == PORT_443 ? HTTPS : HTTP
648
656
 
649
657
  if @early_hints
650
658
  env[EARLY_HINTS] = lambda { |headers|
651
- fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze
659
+ begin
660
+ fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze
652
661
 
653
- headers.each_pair do |k, vs|
654
- if vs.respond_to?(:to_s) && !vs.to_s.empty?
655
- vs.to_s.split(NEWLINE).each do |v|
656
- next if possible_header_injection?(v)
657
- fast_write client, "#{k}: #{v}\r\n"
662
+ headers.each_pair do |k, vs|
663
+ if vs.respond_to?(:to_s) && !vs.to_s.empty?
664
+ vs.to_s.split(NEWLINE).each do |v|
665
+ next if possible_header_injection?(v)
666
+ fast_write client, "#{k}: #{v}\r\n"
667
+ end
668
+ else
669
+ fast_write client, "#{k}: #{vs}\r\n"
658
670
  end
659
- else
660
- fast_write client, "#{k}: #{vs}\r\n"
661
671
  end
662
- end
663
672
 
664
- fast_write client, "\r\n".freeze
673
+ fast_write client, "\r\n".freeze
674
+ rescue ConnectionError
675
+ # noop, if we lost the socket we just won't send the early hints
676
+ end
665
677
  }
666
678
  end
667
679
 
@@ -687,7 +699,7 @@ module Puma
687
699
  to_add = {}
688
700
  end
689
701
 
690
- to_add[k.gsub(",", "_")] = v
702
+ to_add[k.tr(",", "_")] = v
691
703
  end
692
704
  end
693
705
 
@@ -990,6 +1002,10 @@ module Puma
990
1002
  @events.debug "Drained #{count} additional connections."
991
1003
  end
992
1004
 
1005
+ if @status != :restart
1006
+ @binder.close
1007
+ end
1008
+
993
1009
  if @thread_pool
994
1010
  if timeout = @options[:force_shutdown_after]
995
1011
  @thread_pool.shutdown timeout.to_i