puma 3.12.6 → 4.3.12

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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +155 -3
  3. data/LICENSE +0 -0
  4. data/README.md +76 -48
  5. data/bin/puma-wild +0 -0
  6. data/docs/architecture.md +1 -0
  7. data/docs/deployment.md +24 -4
  8. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  9. data/docs/images/puma-connection-flow.png +0 -0
  10. data/docs/images/puma-general-arch.png +0 -0
  11. data/docs/nginx.md +0 -0
  12. data/docs/plugins.md +20 -10
  13. data/docs/restart.md +4 -2
  14. data/docs/signals.md +0 -0
  15. data/docs/systemd.md +27 -9
  16. data/docs/tcp_mode.md +96 -0
  17. data/ext/puma_http11/PumaHttp11Service.java +2 -0
  18. data/ext/puma_http11/ext_help.h +0 -0
  19. data/ext/puma_http11/extconf.rb +21 -0
  20. data/ext/puma_http11/http11_parser.c +58 -70
  21. data/ext/puma_http11/http11_parser.h +0 -0
  22. data/ext/puma_http11/http11_parser.java.rl +21 -37
  23. data/ext/puma_http11/http11_parser.rl +0 -0
  24. data/ext/puma_http11/http11_parser_common.rl +4 -4
  25. data/ext/puma_http11/io_buffer.c +0 -0
  26. data/ext/puma_http11/mini_ssl.c +138 -33
  27. data/ext/puma_http11/org/jruby/puma/Http11.java +106 -114
  28. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +86 -99
  29. data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
  30. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +15 -4
  31. data/ext/puma_http11/puma_http11.c +3 -0
  32. data/lib/puma/accept_nonblock.rb +7 -1
  33. data/lib/puma/app/status.rb +37 -29
  34. data/lib/puma/binder.rb +38 -60
  35. data/lib/puma/cli.rb +4 -0
  36. data/lib/puma/client.rb +285 -208
  37. data/lib/puma/cluster.rb +53 -30
  38. data/lib/puma/commonlogger.rb +0 -0
  39. data/lib/puma/configuration.rb +4 -3
  40. data/lib/puma/const.rb +26 -20
  41. data/lib/puma/control_cli.rb +30 -5
  42. data/lib/puma/detect.rb +0 -0
  43. data/lib/puma/dsl.rb +299 -75
  44. data/lib/puma/events.rb +4 -1
  45. data/lib/puma/io_buffer.rb +1 -6
  46. data/lib/puma/jruby_restart.rb +0 -0
  47. data/lib/puma/launcher.rb +95 -53
  48. data/lib/puma/minissl/context_builder.rb +76 -0
  49. data/lib/puma/minissl.rb +35 -17
  50. data/lib/puma/null_io.rb +0 -0
  51. data/lib/puma/plugin/tmp_restart.rb +2 -0
  52. data/lib/puma/plugin.rb +5 -2
  53. data/lib/puma/rack/builder.rb +2 -0
  54. data/lib/puma/rack/urlmap.rb +2 -0
  55. data/lib/puma/rack_default.rb +2 -0
  56. data/lib/puma/reactor.rb +110 -57
  57. data/lib/puma/runner.rb +11 -3
  58. data/lib/puma/server.rb +89 -61
  59. data/lib/puma/single.rb +3 -3
  60. data/lib/puma/state_file.rb +0 -0
  61. data/lib/puma/tcp_logger.rb +0 -0
  62. data/lib/puma/thread_pool.rb +15 -33
  63. data/lib/puma/util.rb +1 -6
  64. data/lib/puma.rb +8 -0
  65. data/lib/rack/handler/puma.rb +3 -3
  66. data/tools/docker/Dockerfile +16 -0
  67. data/tools/jungle/README.md +0 -0
  68. data/tools/jungle/init.d/README.md +0 -0
  69. data/tools/jungle/init.d/puma +6 -6
  70. data/tools/jungle/rc.d/README.md +0 -0
  71. data/tools/jungle/rc.d/puma.conf +0 -0
  72. data/tools/jungle/upstart/README.md +0 -0
  73. data/tools/jungle/upstart/puma-manager.conf +0 -0
  74. data/tools/jungle/upstart/puma.conf +0 -0
  75. data/tools/trickletest.rb +0 -1
  76. metadata +26 -13
  77. data/lib/puma/compat.rb +0 -14
  78. data/lib/puma/convenient.rb +0 -25
  79. data/lib/puma/daemon_ext.rb +0 -33
  80. data/lib/puma/delegation.rb +0 -13
  81. data/lib/puma/java_io_buffer.rb +0 -47
  82. 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