puma 3.11.4 → 4.2.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.

Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +130 -1
  3. data/README.md +100 -44
  4. data/docs/architecture.md +1 -0
  5. data/docs/deployment.md +24 -4
  6. data/docs/restart.md +4 -2
  7. data/docs/systemd.md +27 -9
  8. data/ext/puma_http11/PumaHttp11Service.java +2 -0
  9. data/ext/puma_http11/extconf.rb +8 -0
  10. data/ext/puma_http11/http11_parser.c +37 -62
  11. data/ext/puma_http11/http11_parser_common.rl +3 -3
  12. data/ext/puma_http11/mini_ssl.c +96 -5
  13. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  14. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +21 -4
  15. data/lib/puma/accept_nonblock.rb +7 -1
  16. data/lib/puma/app/status.rb +35 -29
  17. data/lib/puma/binder.rb +47 -11
  18. data/lib/puma/cli.rb +21 -7
  19. data/lib/puma/client.rb +227 -191
  20. data/lib/puma/cluster.rb +70 -31
  21. data/lib/puma/commonlogger.rb +2 -0
  22. data/lib/puma/configuration.rb +6 -3
  23. data/lib/puma/const.rb +24 -18
  24. data/lib/puma/control_cli.rb +33 -14
  25. data/lib/puma/convenient.rb +2 -0
  26. data/lib/puma/delegation.rb +2 -0
  27. data/lib/puma/detect.rb +2 -0
  28. data/lib/puma/dsl.rb +308 -76
  29. data/lib/puma/events.rb +6 -1
  30. data/lib/puma/io_buffer.rb +3 -6
  31. data/lib/puma/jruby_restart.rb +2 -0
  32. data/lib/puma/launcher.rb +102 -55
  33. data/lib/puma/minissl.rb +41 -19
  34. data/lib/puma/null_io.rb +2 -0
  35. data/lib/puma/plugin/tmp_restart.rb +2 -0
  36. data/lib/puma/plugin.rb +7 -2
  37. data/lib/puma/rack/builder.rb +4 -1
  38. data/lib/puma/rack/urlmap.rb +2 -0
  39. data/lib/puma/rack_default.rb +2 -0
  40. data/lib/puma/reactor.rb +220 -34
  41. data/lib/puma/runner.rb +14 -4
  42. data/lib/puma/server.rb +82 -40
  43. data/lib/puma/single.rb +15 -3
  44. data/lib/puma/state_file.rb +2 -0
  45. data/lib/puma/tcp_logger.rb +2 -0
  46. data/lib/puma/thread_pool.rb +59 -36
  47. data/lib/puma/util.rb +2 -6
  48. data/lib/puma.rb +8 -0
  49. data/lib/rack/handler/puma.rb +6 -3
  50. data/tools/docker/Dockerfile +16 -0
  51. data/tools/jungle/init.d/puma +6 -6
  52. data/tools/trickletest.rb +0 -1
  53. metadata +22 -10
  54. data/lib/puma/compat.rb +0 -14
  55. data/lib/puma/daemon_ext.rb +0 -31
  56. data/lib/puma/java_io_buffer.rb +0 -45
  57. data/lib/puma/rack/backports/uri/common_193.rb +0 -33
data/lib/puma/reactor.rb CHANGED
@@ -1,58 +1,201 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'puma/util'
2
4
  require 'puma/minissl'
3
5
 
6
+ require 'nio'
7
+
4
8
  module Puma
9
+ # Internal Docs, Not a public interface.
10
+ #
11
+ # The Reactor object is responsible for ensuring that a request has been
12
+ # completely received before it starts to be processed. This may be known as read buffering.
13
+ # If read buffering is not done, and no other read buffering is performed (such as by an application server
14
+ # such as nginx) then the application would be subject to a slow client attack.
15
+ #
16
+ # Each Puma "worker" process has its own Reactor. For example if you start puma with `$ puma -w 5` then
17
+ # it will have 5 workers and each worker will have it's own reactor.
18
+ #
19
+ # For a graphical representation of how the reactor works see [architecture.md](https://github.com/puma/puma/blob/master/docs/architecture.md#connection-pipeline).
20
+ #
21
+ # ## Reactor Flow
22
+ #
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.
25
+ #
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
28
+ # return the references to any objects that caused it to "wake". The reactor
29
+ # then loops through each of these request objects, and sees if they're complete. If they
30
+ # have a full header and body then the reactor passes the request to a thread pool.
31
+ # Once in a thread pool, a "worker thread" can run the the application's Ruby code against the request.
32
+ #
33
+ # If the request is not complete, then it stays in the array, and the next time any
34
+ # data is written to that socket reference, then the loop is woken up and it is checked for completeness again.
35
+ #
36
+ # A detailed example is given in the docs for `run_internal` which is where the bulk
37
+ # of this logic lives.
5
38
  class Reactor
6
39
  DefaultSleepFor = 5
7
40
 
41
+ # Creates an instance of Puma::Reactor
42
+ #
43
+ # The `server` argument is an instance of `Puma::Server`
44
+ # that is used to write a response for "low level errors"
45
+ # when there is an exception inside of the reactor.
46
+ #
47
+ # The `app_pool` is an instance of `Puma::ThreadPool`.
48
+ # Once a request is fully formed (header and body are received)
49
+ # it will be passed to the `app_pool`.
8
50
  def initialize(server, app_pool)
9
51
  @server = server
10
52
  @events = server.events
11
53
  @app_pool = app_pool
12
54
 
55
+ @selector = NIO::Selector.new
56
+
13
57
  @mutex = Mutex.new
58
+
59
+ # Read / Write pipes to wake up internal while loop
14
60
  @ready, @trigger = Puma::Util.pipe
15
61
  @input = []
16
62
  @sleep_for = DefaultSleepFor
17
63
  @timeouts = []
18
64
 
19
- @sockets = [@ready]
65
+ mon = @selector.register(@ready, :r)
66
+ mon.value = @ready
67
+
68
+ @monitors = [mon]
20
69
  end
21
70
 
22
71
  private
23
72
 
73
+ # Until a request is added via the `add` method this method will internally
74
+ # loop, waiting on the `sockets` array objects. The only object in this
75
+ # array at first is the `@ready` IO object, which is the read end of a pipe
76
+ # connected to `@trigger` object. When `@trigger` is written to, then the loop
77
+ # will break on `NIO::Selector#select` and return an array.
78
+ #
79
+ # ## When a request is added:
80
+ #
81
+ # When the `add` method is called, an instance of `Puma::Client` is added to the `@input` array.
82
+ # Next the `@ready` pipe is "woken" by writing a string of `"*"` to `@trigger`.
83
+ #
84
+ # When that happens, the internal loop stops blocking at `NIO::Selector#select` and returns a reference
85
+ # to whatever "woke" it up. On the very first loop, the only thing in `sockets` is `@ready`.
86
+ # When `@trigger` is written-to, the loop "wakes" and the `ready`
87
+ # variable returns an array of arrays that looks like `[[#<IO:fd 10>], [], []]` where the
88
+ # first IO object is the `@ready` object. This first array `[#<IO:fd 10>]`
89
+ # is saved as a `reads` variable.
90
+ #
91
+ # The `reads` variable is iterated through. In the case that the object
92
+ # is the same as the `@ready` input pipe, then we know that there was a `trigger` event.
93
+ #
94
+ # If there was a trigger event, then one byte of `@ready` is read into memory. In the case of the first request,
95
+ # the reactor sees that it's a `"*"` value and the reactor adds the contents of `@input` into the `sockets` array.
96
+ # The while then loop continues to iterate again, but now the `sockets` array contains a `Puma::Client` instance in addition
97
+ # to the `@ready` IO object. For example: `[#<IO:fd 10>, #<Puma::Client:0x3fdc1103bee8 @ready=false>]`.
98
+ #
99
+ # Since the `Puma::Client` in this example has data that has not been read yet,
100
+ # the `NIO::Selector#select` is immediately able to "wake" and read from the `Puma::Client`. At this point the
101
+ # `ready` output looks like this: `[[#<Puma::Client:0x3fdc1103bee8 @ready=false>], [], []]`.
102
+ #
103
+ # Each element in the first entry is iterated over. The `Puma::Client` object is not
104
+ # the `@ready` pipe, so the reactor checks to see if it has the full header and body with
105
+ # the `Puma::Client#try_to_finish` method. If the full request has been sent,
106
+ # then the request is passed off to the `@app_pool` thread pool so that a "worker thread"
107
+ # can pick up the request and begin to execute application logic. This is done
108
+ # via `@app_pool << c`. The `Puma::Client` is then removed from the `sockets` array.
109
+ #
110
+ # If the request body is not present then nothing will happen, and the loop will iterate
111
+ # again. When the client sends more data to the socket the `Puma::Client` object will
112
+ # wake up the `NIO::Selector#select` and it can again be checked to see if it's ready to be
113
+ # passed to the thread pool.
114
+ #
115
+ # ## Time Out Case
116
+ #
117
+ # In addition to being woken via a write to one of the sockets the `NIO::Selector#select` will
118
+ # periodically "time out" of the sleep. One of the functions of this is to check for
119
+ # any requests that have "timed out". At the end of the loop it's checked to see if
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
123
+ # that watches for new data.
124
+ #
125
+ # This behavior loops until all the objects that have timed out have been removed.
126
+ #
127
+ # Once all the timeouts have been processed, the next duration of the `NIO::Selector#select` sleep
128
+ # will be set to be equal to the amount of time it will take for the next timeout to occur.
129
+ # This calculation happens in `calculate_sleep`.
24
130
  def run_internal
25
- sockets = @sockets
131
+ monitors = @monitors
132
+ selector = @selector
26
133
 
27
134
  while true
28
135
  begin
29
- ready = IO.select sockets, nil, nil, @sleep_for
136
+ ready = selector.select @sleep_for
30
137
  rescue IOError => e
31
138
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
32
- if sockets.any? { |socket| socket.closed? }
139
+ if monitors.any? { |mon| mon.value.closed? }
33
140
  STDERR.puts "Error in select: #{e.message} (#{e.class})"
34
141
  STDERR.puts e.backtrace
35
- 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
+
36
150
  retry
37
151
  else
38
152
  raise
39
153
  end
40
154
  end
41
155
 
42
- if ready and reads = ready[0]
43
- reads.each do |c|
44
- if c == @ready
156
+ if ready
157
+ ready.each do |mon|
158
+ if mon.value == @ready
45
159
  @mutex.synchronize do
46
160
  case @ready.read(1)
47
161
  when "*"
48
- 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
49
183
  @input.clear
184
+
185
+ @timeouts.sort! { |a,b| a.value.timeout_at <=> b.value.timeout_at }
186
+ calculate_sleep
50
187
  when "c"
51
- sockets.delete_if do |s|
52
- if s == @ready
188
+ monitors.reject! do |submon|
189
+ if submon.value == @ready
53
190
  false
54
191
  else
55
- 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
56
199
  true
57
200
  end
58
201
  end
@@ -61,40 +204,47 @@ module Puma
61
204
  end
62
205
  end
63
206
  else
207
+ c = mon.value
208
+
64
209
  # We have to be sure to remove it from the timeout
65
210
  # list or we'll accidentally close the socket when
66
211
  # it's in use!
67
212
  if c.timeout_at
68
213
  @mutex.synchronize do
69
- @timeouts.delete c
214
+ @timeouts.delete mon
70
215
  end
71
216
  end
72
217
 
73
218
  begin
74
219
  if c.try_to_finish
75
220
  @app_pool << c
76
- sockets.delete c
221
+ clear_monitor mon
77
222
  end
78
223
 
79
224
  # Don't report these to the lowlevel_error handler, otherwise
80
225
  # will be flooding them with errors when persistent connections
81
226
  # are closed.
82
227
  rescue ConnectionError
83
- c.write_500
228
+ c.write_error(500)
84
229
  c.close
85
230
 
86
- sockets.delete c
231
+ clear_monitor mon
87
232
 
88
233
  # SSL handshake failure
89
234
  rescue MiniSSL::SSLError => e
90
235
  @server.lowlevel_error(e, c.env)
91
236
 
92
237
  ssl_socket = c.io
93
- addr = ssl_socket.peeraddr.last
238
+ begin
239
+ addr = ssl_socket.peeraddr.last
240
+ rescue IOError
241
+ addr = "<unknown>"
242
+ end
243
+
94
244
  cert = ssl_socket.peercert
95
245
 
96
246
  c.close
97
- sockets.delete c
247
+ clear_monitor mon
98
248
 
99
249
  @events.ssl_error @server, addr, cert, e
100
250
 
@@ -102,19 +252,19 @@ module Puma
102
252
  rescue HttpParserError => e
103
253
  @server.lowlevel_error(e, c.env)
104
254
 
105
- c.write_400
255
+ c.write_error(400)
106
256
  c.close
107
257
 
108
- sockets.delete c
258
+ clear_monitor mon
109
259
 
110
260
  @events.parse_error @server, c.env, e
111
261
  rescue StandardError => e
112
262
  @server.lowlevel_error(e, c.env)
113
263
 
114
- c.write_500
264
+ c.write_error(500)
115
265
  c.close
116
266
 
117
- sockets.delete c
267
+ clear_monitor mon
118
268
  end
119
269
  end
120
270
  end
@@ -124,11 +274,13 @@ module Puma
124
274
  @mutex.synchronize do
125
275
  now = Time.now
126
276
 
127
- while @timeouts.first.timeout_at < now
128
- c = @timeouts.shift
129
- c.write_408 if c.in_data_phase
277
+ while @timeouts.first.value.timeout_at < now
278
+ mon = @timeouts.shift
279
+ c = mon.value
280
+ c.write_error(408) if c.in_data_phase
130
281
  c.close
131
- sockets.delete c
282
+
283
+ clear_monitor mon
132
284
 
133
285
  break if @timeouts.empty?
134
286
  end
@@ -139,6 +291,11 @@ module Puma
139
291
  end
140
292
  end
141
293
 
294
+ def clear_monitor(mon)
295
+ @selector.deregister mon.value
296
+ @monitors.delete mon
297
+ end
298
+
142
299
  public
143
300
 
144
301
  def run
@@ -150,6 +307,7 @@ module Puma
150
307
 
151
308
  def run_in_thread
152
309
  @thread = Thread.new do
310
+ Puma.set_thread_name "reactor"
153
311
  begin
154
312
  run_internal
155
313
  rescue StandardError => e
@@ -163,11 +321,21 @@ module Puma
163
321
  end
164
322
  end
165
323
 
324
+ # The `calculate_sleep` sets the value that the `NIO::Selector#select` will
325
+ # sleep for in the main reactor loop when no sockets are being written to.
326
+ #
327
+ # The values kept in `@timeouts` are sorted so that the first timeout
328
+ # comes first in the array. When there are no timeouts the default timeout is used.
329
+ #
330
+ # Otherwise a sleep value is set that is the same as the amount of time it
331
+ # would take for the first element to time out.
332
+ #
333
+ # If that value is in the past, then a sleep value of zero is used.
166
334
  def calculate_sleep
167
335
  if @timeouts.empty?
168
336
  @sleep_for = DefaultSleepFor
169
337
  else
170
- diff = @timeouts.first.timeout_at.to_f - Time.now.to_f
338
+ diff = @timeouts.first.value.timeout_at.to_f - Time.now.to_f
171
339
 
172
340
  if diff < 0.0
173
341
  @sleep_for = 0
@@ -177,17 +345,35 @@ module Puma
177
345
  end
178
346
  end
179
347
 
348
+ # This method adds a connection to the reactor
349
+ #
350
+ # Typically called by `Puma::Server` the value passed in
351
+ # is usually a `Puma::Client` object that responds like an IO
352
+ # object.
353
+ #
354
+ # The main body of the reactor loop is in `run_internal` and it
355
+ # will sleep on `NIO::Selector#select`. When a new connection is added to the
356
+ # reactor it cannot be added directly to the `sockets` array, because
357
+ # the `NIO::Selector#select` will not be watching for it yet.
358
+ #
359
+ # Instead what needs to happen is that `NIO::Selector#select` needs to be woken up,
360
+ # the contents of `@input` added to the `sockets` array, and then
361
+ # another call to `NIO::Selector#select` needs to happen. Since the `Puma::Client`
362
+ # object can be read immediately, it does not block, but instead returns
363
+ # right away.
364
+ #
365
+ # This behavior is accomplished by writing to `@trigger` which wakes up
366
+ # the `NIO::Selector#select` and then there is logic to detect the value of `*`,
367
+ # pull the contents from `@input` and add them to the sockets array.
368
+ #
369
+ # If the object passed in has a timeout value in `timeout_at` then
370
+ # it is added to a `@timeouts` array. This array is then re-arranged
371
+ # so that the first element to timeout will be at the front of the
372
+ # array. Then a value to sleep for is derived in the call to `calculate_sleep`
180
373
  def add(c)
181
374
  @mutex.synchronize do
182
375
  @input << c
183
376
  @trigger << "*"
184
-
185
- if c.timeout_at
186
- @timeouts << c
187
- @timeouts.sort! { |a,b| a.timeout_at <=> b.timeout_at }
188
-
189
- calculate_sleep
190
- end
191
377
  end
192
378
  end
193
379
 
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
@@ -9,6 +14,7 @@ module Puma
9
14
  @options = cli.options
10
15
  @app = nil
11
16
  @control = nil
17
+ @started_at = Time.now
12
18
  end
13
19
 
14
20
  def daemon?
@@ -19,6 +25,10 @@ module Puma
19
25
  @options[:environment] == "development"
20
26
  end
21
27
 
28
+ def test?
29
+ @options[:environment] == "test"
30
+ end
31
+
22
32
  def log(str)
23
33
  @events.log str
24
34
  end
@@ -43,12 +53,12 @@ module Puma
43
53
 
44
54
  uri = URI.parse str
45
55
 
46
- app = Puma::App::Status.new @launcher
47
-
48
56
  if token = @options[:control_auth_token]
49
- app.auth_token = token unless token.empty? or token == :none
57
+ token = nil if token.empty? || token == 'none'
50
58
  end
51
59
 
60
+ app = Puma::App::Status.new @launcher, token
61
+
52
62
  control = Puma::Server.new app, @launcher.events
53
63
  control.min_threads = 0
54
64
  control.max_threads = 1
@@ -165,7 +175,7 @@ module Puma
165
175
  server.early_hints = true
166
176
  end
167
177
 
168
- unless development?
178
+ unless development? || test?
169
179
  server.leak_stack_on_error = false
170
180
  end
171
181
 
data/lib/puma/server.rb CHANGED
@@ -1,10 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'stringio'
2
4
 
3
5
  require 'puma/thread_pool'
4
6
  require 'puma/const'
5
7
  require 'puma/events'
6
8
  require 'puma/null_io'
7
- require 'puma/compat'
8
9
  require 'puma/reactor'
9
10
  require 'puma/client'
10
11
  require 'puma/binder'
@@ -14,15 +15,20 @@ require 'puma/util'
14
15
 
15
16
  require 'puma/puma_http11'
16
17
 
17
- unless Puma.const_defined? "IOBuffer"
18
- require 'puma/io_buffer'
19
- end
20
-
21
18
  require 'socket'
22
19
 
23
20
  module Puma
24
21
 
25
22
  # The HTTP Server itself. Serves out a single Rack app.
23
+ #
24
+ # This class is used by the `Puma::Single` and `Puma::Cluster` classes
25
+ # to generate one or more `Puma::Server` instances capable of handling requests.
26
+ # Each Puma process will contain one `Puma::Server` instance.
27
+ #
28
+ # The `Puma::Server` instance pulls requests from the socket, adds them to a
29
+ # `Puma::Reactor` where they get eventually passed to a `Puma::ThreadPool`.
30
+ #
31
+ # Each `Puma::Server` will have one reactor and one thread pool.
26
32
  class Server
27
33
 
28
34
  include Puma::Const
@@ -68,7 +74,6 @@ module Puma
68
74
  @first_data_timeout = options.fetch(:first_data_timeout, FIRST_DATA_TIMEOUT)
69
75
 
70
76
  @binder = Binder.new(events)
71
- @own_binder = true
72
77
 
73
78
  @leak_stack_on_error = true
74
79
 
@@ -91,7 +96,6 @@ module Puma
91
96
 
92
97
  def inherit_binder(bind)
93
98
  @binder = bind
94
- @own_binder = false
95
99
  end
96
100
 
97
101
  def tcp_mode!
@@ -159,6 +163,18 @@ module Puma
159
163
  @thread_pool and @thread_pool.spawned
160
164
  end
161
165
 
166
+
167
+ # This number represents the number of requests that
168
+ # the server is capable of taking right now.
169
+ #
170
+ # For example if the number is 5 then it means
171
+ # there are 5 threads sitting idle ready to take
172
+ # a request. If one request comes in, then the
173
+ # value would be 4 until it finishes processing.
174
+ def pool_capacity
175
+ @thread_pool and @thread_pool.pool_capacity
176
+ end
177
+
162
178
  # Lopez Mode == raw tcp apps
163
179
 
164
180
  def run_lopez_mode(background=true)
@@ -191,7 +207,10 @@ module Puma
191
207
  @events.fire :state, :running
192
208
 
193
209
  if background
194
- @thread = Thread.new { handle_servers_lopez_mode }
210
+ @thread = Thread.new do
211
+ Puma.set_thread_name "server"
212
+ handle_servers_lopez_mode
213
+ end
195
214
  return @thread
196
215
  else
197
216
  handle_servers_lopez_mode
@@ -241,11 +260,17 @@ module Puma
241
260
  STDERR.puts "Exception handling servers: #{e.message} (#{e.class})"
242
261
  STDERR.puts e.backtrace
243
262
  ensure
244
- @check.close
245
- @notify.close
263
+ begin
264
+ @check.close
265
+ rescue
266
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
267
+ end
246
268
 
247
- if @status != :restart and @own_binder
248
- @binder.close
269
+ # Prevent can't modify frozen IOError (RuntimeError)
270
+ begin
271
+ @notify.close
272
+ rescue IOError
273
+ # no biggy
249
274
  end
250
275
  end
251
276
 
@@ -295,7 +320,7 @@ module Puma
295
320
 
296
321
  @events.ssl_error self, addr, cert, e
297
322
  rescue HttpParserError => e
298
- client.write_400
323
+ client.write_error(400)
299
324
  client.close
300
325
 
301
326
  @events.parse_error self, client.env, e
@@ -329,7 +354,10 @@ module Puma
329
354
  @events.fire :state, :running
330
355
 
331
356
  if background
332
- @thread = Thread.new { handle_servers }
357
+ @thread = Thread.new do
358
+ Puma.set_thread_name "server"
359
+ handle_servers
360
+ end
333
361
  return @thread
334
362
  else
335
363
  handle_servers
@@ -370,7 +398,10 @@ module Puma
370
398
  end
371
399
 
372
400
  pool << client
373
- pool.wait_until_not_full
401
+ busy_threads = pool.wait_until_not_full
402
+ if busy_threads == 0
403
+ @options[:out_of_band].each(&:call) if @options[:out_of_band]
404
+ end
374
405
  end
375
406
  rescue SystemCallError
376
407
  # nothing
@@ -402,10 +433,6 @@ module Puma
402
433
  ensure
403
434
  @check.close
404
435
  @notify.close
405
-
406
- if @status != :restart and @own_binder
407
- @binder.close
408
- end
409
436
  end
410
437
 
411
438
  @events.fire :state, :done
@@ -484,7 +511,7 @@ module Puma
484
511
  rescue HttpParserError => e
485
512
  lowlevel_error(e, client.env)
486
513
 
487
- client.write_400
514
+ client.write_error(400)
488
515
 
489
516
  @events.parse_error self, client.env, e
490
517
 
@@ -492,7 +519,7 @@ module Puma
492
519
  rescue StandardError => e
493
520
  lowlevel_error(e, client.env)
494
521
 
495
- client.write_500
522
+ client.write_error(500)
496
523
 
497
524
  @events.unknown_error self, e, "Read"
498
525
 
@@ -567,19 +594,26 @@ module Puma
567
594
  end
568
595
 
569
596
  def default_server_port(env)
570
- return PORT_443 if env[HTTPS_KEY] == 'on' || env[HTTPS_KEY] == 'https'
571
- env['HTTP_X_FORWARDED_PROTO'] == 'https' ? PORT_443 : PORT_80
597
+ 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"
598
+ PORT_443
599
+ else
600
+ PORT_80
601
+ end
572
602
  end
573
603
 
574
- # Given the request +env+ from +client+ and a partial request body
575
- # in +body+, finish reading the body if there is one and invoke
576
- # the rack app. Then construct the response and write it back to
577
- # +client+
604
+ # Takes the request +req+, invokes the Rack application to construct
605
+ # the response and writes it back to +req.io+.
606
+ #
607
+ # The second parameter +lines+ is a IO-like object unique to this thread.
608
+ # This is normally an instance of Puma::IOBuffer.
578
609
  #
579
- # +cl+ is the previously fetched Content-Length header if there
580
- # was one. This is an optimization to keep from having to look
581
- # it up again.
610
+ # It'll return +false+ when the connection is closed, this doesn't mean
611
+ # that the response wasn't successful.
582
612
  #
613
+ # It'll return +:async+ if the connection remains open but will be handled
614
+ # elsewhere, i.e. the connection has been hijacked by the Rack application.
615
+ #
616
+ # Finally, it'll return +true+ on keep-alive connections.
583
617
  def handle_request(req, lines)
584
618
  env = req.env
585
619
  client = req.io
@@ -602,23 +636,27 @@ module Puma
602
636
  head = env[REQUEST_METHOD] == HEAD
603
637
 
604
638
  env[RACK_INPUT] = body
605
- env[RACK_URL_SCHEME] = env[HTTPS_KEY] ? HTTPS : HTTP
639
+ env[RACK_URL_SCHEME] = default_server_port(env) == PORT_443 ? HTTPS : HTTP
606
640
 
607
641
  if @early_hints
608
642
  env[EARLY_HINTS] = lambda { |headers|
609
- fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze
643
+ begin
644
+ fast_write client, "HTTP/1.1 103 Early Hints\r\n".freeze
610
645
 
611
- headers.each_pair do |k, vs|
612
- if vs.respond_to?(:to_s) && !vs.to_s.empty?
613
- vs.to_s.split(NEWLINE).each do |v|
614
- fast_write client, "#{k}: #{v}\r\n"
646
+ headers.each_pair do |k, vs|
647
+ if vs.respond_to?(:to_s) && !vs.to_s.empty?
648
+ vs.to_s.split(NEWLINE).each do |v|
649
+ fast_write client, "#{k}: #{v}\r\n"
650
+ end
651
+ else
652
+ fast_write client, "#{k}: #{vs}\r\n"
615
653
  end
616
- else
617
- fast_write client, "#{k}: #{vs}\r\n"
618
654
  end
619
- end
620
655
 
621
- fast_write client, "\r\n".freeze
656
+ fast_write client, "\r\n".freeze
657
+ rescue ConnectionError
658
+ # noop, if we lost the socket we just won't send the early hints
659
+ end
622
660
  }
623
661
  end
624
662
 
@@ -914,6 +952,10 @@ module Puma
914
952
  @events.debug "Drained #{count} additional connections."
915
953
  end
916
954
 
955
+ if @status != :restart
956
+ @binder.close
957
+ end
958
+
917
959
  if @thread_pool
918
960
  if timeout = @options[:force_shutdown_after]
919
961
  @thread_pool.shutdown timeout.to_i