puma 5.0.0 → 5.0.2

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.

@@ -10,23 +10,26 @@ require 'stringio'
10
10
 
11
11
  require 'thread'
12
12
 
13
- require_relative 'puma/puma_http11'
14
- require_relative 'puma/detect'
13
+ require 'puma/puma_http11'
14
+ require 'puma/detect'
15
15
 
16
16
  module Puma
17
17
  autoload :Const, 'puma/const'
18
18
  autoload :Server, 'puma/server'
19
19
  autoload :Launcher, 'puma/launcher'
20
20
 
21
+ # @!attribute [rw] stats_object=
21
22
  def self.stats_object=(val)
22
23
  @get_stats = val
23
24
  end
24
25
 
26
+ # @!attribute [rw] stats_object
25
27
  def self.stats
26
28
  require 'json'
27
29
  @get_stats.stats.to_json
28
30
  end
29
31
 
32
+ # @!attribute [r] stats_hash
30
33
  # @version 5.0.0
31
34
  def self.stats_hash
32
35
  @get_stats.stats
@@ -66,6 +66,7 @@ module Puma
66
66
  @ios.each { |i| i.close }
67
67
  end
68
68
 
69
+ # @!attribute [r] connected_ports
69
70
  # @version 5.0.0
70
71
  def connected_ports
71
72
  ios.map { |io| io.addr[1] }.uniq
@@ -391,6 +392,7 @@ module Puma
391
392
 
392
393
  private
393
394
 
395
+ # @!attribute [r] loopback_addresses
394
396
  def loopback_addresses
395
397
  Socket.ip_address_list.select do |addrinfo|
396
398
  addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
@@ -85,6 +85,7 @@ module Puma
85
85
 
86
86
  def_delegators :@io, :closed?
87
87
 
88
+ # @!attribute [r] inspect
88
89
  def inspect
89
90
  "#<Puma::Client:0x#{object_id.to_s(16)} @ready=#{@ready.inspect}>"
90
91
  end
@@ -96,6 +97,7 @@ module Puma
96
97
  env[HIJACK_IO] ||= @io
97
98
  end
98
99
 
100
+ # @!attribute [r] in_data_phase
99
101
  def in_data_phase
100
102
  !@read_header
101
103
  end
@@ -155,7 +157,9 @@ module Puma
155
157
  data = @io.read_nonblock(CHUNK_SIZE)
156
158
  rescue IO::WaitReadable
157
159
  return false
158
- rescue SystemCallError, IOError, EOFError
160
+ rescue EOFError
161
+ # Swallow error, don't log
162
+ rescue SystemCallError, IOError
159
163
  raise ConnectionError, "Connection error detected during read"
160
164
  end
161
165
 
@@ -194,6 +194,7 @@ module Puma
194
194
  end
195
195
  end
196
196
 
197
+ # @!attribute [r] next_worker_index
197
198
  def next_worker_index
198
199
  all_positions = 0...@options[:workers]
199
200
  occupied_positions = @workers.map { |w| w.index }
@@ -396,6 +397,7 @@ module Puma
396
397
 
397
398
  # Inside of a child process, this will return all zeroes, as @workers is only populated in
398
399
  # the master process.
400
+ # @!attribute [r] stats
399
401
  def stats
400
402
  old_worker_count = @workers.count { |w| w.phase != @phase }
401
403
  worker_status = @workers.map do |w|
@@ -100,7 +100,7 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "5.0.0".freeze
103
+ PUMA_VERSION = VERSION = "5.0.2".freeze
104
104
  CODE_NAME = "Spoony Bard".freeze
105
105
 
106
106
  PUMA_SERVER_STRING = ['puma', PUMA_VERSION, CODE_NAME].join(' ').freeze
@@ -51,8 +51,8 @@ module Puma
51
51
 
52
52
  string_block = []
53
53
  string_block << title(options)
54
- string_block << request_dump(req) if req
55
- string_block << error_backtrace(options) if error
54
+ string_block << request_dump(req) if request_parsed?(req)
55
+ string_block << error.backtrace if error
56
56
 
57
57
  ioerr.puts string_block.join("\n")
58
58
  end
@@ -106,10 +106,12 @@ module Puma
106
106
  end
107
107
 
108
108
  # An SSL error has occurred.
109
- # +error+ an exception object, +peeraddr+ peer address,
110
- # and +peercert+ any peer certificate (if present).
109
+ # @param error <Puma::MiniSSL::SSLError>
110
+ # @param ssl_socket <Puma::MiniSSL::Socket>
111
111
  #
112
- def ssl_error(error, peeraddr, peercert)
112
+ def ssl_error(error, ssl_socket)
113
+ peeraddr = ssl_socket.peeraddr.last rescue "<unknown>"
114
+ peercert = ssl_socket.peercert
113
115
  subject = peercert ? peercert.subject : nil
114
116
  @error_logger.info(error: error, text: "SSL error, peer: #{peeraddr}, peer cert: #{subject}")
115
117
  end
@@ -188,11 +188,13 @@ module Puma
188
188
  end
189
189
 
190
190
  # Return all tcp ports the launcher may be using, TCP or SSL
191
+ # @!attribute [r] connected_ports
191
192
  # @version 5.0.0
192
193
  def connected_ports
193
194
  @binder.connected_ports
194
195
  end
195
196
 
197
+ # @!attribute [r] restart_args
196
198
  def restart_args
197
199
  cmd = @options[:restart_cmd]
198
200
  if cmd
@@ -207,6 +209,7 @@ module Puma
207
209
  @binder.close_listeners
208
210
  end
209
211
 
212
+ # @!attribute [r] thread_status
210
213
  # @version 5.0.0
211
214
  def thread_status
212
215
  Thread.list.each do |thread|
@@ -261,6 +264,7 @@ module Puma
261
264
  end
262
265
  end
263
266
 
267
+ # @!attribute [r] dependencies_and_files_to_require_after_prune
264
268
  def dependencies_and_files_to_require_after_prune
265
269
  puma = spec_for_gem("puma")
266
270
 
@@ -271,6 +275,7 @@ module Puma
271
275
  [deps, require_paths_for_gem(puma) + extra_runtime_deps_directories]
272
276
  end
273
277
 
278
+ # @!attribute [r] extra_runtime_deps_directories
274
279
  def extra_runtime_deps_directories
275
280
  Array(@options[:extra_runtime_dependencies]).map do |d_name|
276
281
  if (spec = spec_for_gem(d_name))
@@ -282,6 +287,7 @@ module Puma
282
287
  end.flatten.compact
283
288
  end
284
289
 
290
+ # @!attribute [r] puma_wild_location
285
291
  def puma_wild_location
286
292
  puma = spec_for_gem("puma")
287
293
  dirs = require_paths_for_gem(puma)
@@ -345,6 +351,7 @@ module Puma
345
351
  Process.respond_to?(:setproctitle) ? Process.setproctitle(title) : $0 = title
346
352
  end
347
353
 
354
+ # @!attribute [r] title
348
355
  def title
349
356
  buffer = "puma #{Puma::Const::VERSION} (#{@options[:binds].join(',')})"
350
357
  buffer += " [#{@options[:tag]}]" if @options[:tag] && !@options[:tag].empty?
@@ -356,6 +363,7 @@ module Puma
356
363
  ENV['RACK_ENV'] = environment
357
364
  end
358
365
 
366
+ # @!attribute [r] environment
359
367
  def environment
360
368
  @environment
361
369
  end
@@ -24,6 +24,7 @@ module Puma
24
24
  @peercert = nil
25
25
  end
26
26
 
27
+ # @!attribute [r] to_io
27
28
  def to_io
28
29
  @socket
29
30
  end
@@ -38,6 +39,7 @@ module Puma
38
39
  #
39
40
  # Used for dropping tcp connections to ssl.
40
41
  # See OpenSSL ssl/ssl_stat.c SSL_state_string for info
42
+ # @!attribute [r] ssl_version_state
41
43
  # @version 5.0.0
42
44
  #
43
45
  def ssl_version_state
@@ -188,10 +190,12 @@ module Puma
188
190
  end
189
191
  end
190
192
 
193
+ # @!attribute [r] peeraddr
191
194
  def peeraddr
192
195
  @socket.peeraddr
193
196
  end
194
197
 
198
+ # @!attribute [r] peercert
195
199
  def peercert
196
200
  return @peercert if @peercert
197
201
 
@@ -264,12 +268,14 @@ module Puma
264
268
  end
265
269
 
266
270
  # disables TLSv1
271
+ # @!attribute [w] no_tlsv1=
267
272
  def no_tlsv1=(tlsv1)
268
273
  raise ArgumentError, "Invalid value of no_tlsv1=" unless ['true', 'false', true, false].include?(tlsv1)
269
274
  @no_tlsv1 = tlsv1
270
275
  end
271
276
 
272
277
  # disables TLSv1 and TLSv1.1. Overrides `#no_tlsv1=`
278
+ # @!attribute [w] no_tlsv1_1=
273
279
  def no_tlsv1_1=(tlsv1_1)
274
280
  raise ArgumentError, "Invalid value of no_tlsv1_1=" unless ['true', 'false', true, false].include?(tlsv1_1)
275
281
  @no_tlsv1_1 = tlsv1_1
@@ -287,6 +293,7 @@ module Puma
287
293
  @ctx = ctx
288
294
  end
289
295
 
296
+ # @!attribute [r] to_io
290
297
  def to_io
291
298
  @socket
292
299
  end
@@ -307,6 +314,7 @@ module Puma
307
314
  Socket.new io, engine
308
315
  end
309
316
 
317
+ # @!attribute [r] addr
310
318
  # @version 5.0.0
311
319
  def addr
312
320
  @socket.addr
@@ -237,23 +237,12 @@ module Puma
237
237
 
238
238
  # SSL handshake failure
239
239
  rescue MiniSSL::SSLError => e
240
- @server.lowlevel_error(e, c.env)
241
-
242
- ssl_socket = c.io
243
- begin
244
- addr = ssl_socket.peeraddr.last
245
- # EINVAL can happen when browser closes socket w/security exception
246
- rescue IOError, Errno::EINVAL
247
- addr = "<unknown>"
248
- end
249
-
250
- cert = ssl_socket.peercert
240
+ @server.lowlevel_error e, c.env
241
+ @events.ssl_error e, c.io
251
242
 
252
243
  c.close
253
244
  clear_monitor mon
254
245
 
255
- @events.ssl_error e, addr, cert
256
-
257
246
  # The client doesn't know HTTP well
258
247
  rescue HttpParserError => e
259
248
  @server.lowlevel_error(e, c.env)
@@ -85,6 +85,8 @@ module Puma
85
85
  @precheck_closing = true
86
86
 
87
87
  @requests_count = 0
88
+
89
+ @shutdown_mutex = Mutex.new
88
90
  end
89
91
 
90
92
  attr_accessor :binder, :leak_stack_on_error, :early_hints
@@ -101,10 +103,18 @@ module Puma
101
103
  def tcp_cork_supported?
102
104
  RbConfig::CONFIG['host_os'] =~ /linux/ &&
103
105
  Socket.const_defined?(:IPPROTO_TCP) &&
104
- Socket.const_defined?(:TCP_CORK) &&
106
+ Socket.const_defined?(:TCP_CORK)
107
+ end
108
+
109
+ # :nodoc:
110
+ # @version 5.0.0
111
+ def closed_socket_supported?
112
+ RbConfig::CONFIG['host_os'] =~ /linux/ &&
113
+ Socket.const_defined?(:IPPROTO_TCP) &&
105
114
  Socket.const_defined?(:TCP_INFO)
106
115
  end
107
116
  private :tcp_cork_supported?
117
+ private :closed_socket_supported?
108
118
  end
109
119
 
110
120
  # On Linux, use TCP_CORK to better control how the TCP stack
@@ -131,7 +141,15 @@ module Puma
131
141
  Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
132
142
  end
133
143
  end
144
+ else
145
+ def cork_socket(socket)
146
+ end
147
+
148
+ def uncork_socket(socket)
149
+ end
150
+ end
134
151
 
152
+ if closed_socket_supported?
135
153
  def closed_socket?(socket)
136
154
  return false unless socket.kind_of? TCPSocket
137
155
  return false unless @precheck_closing
@@ -149,12 +167,6 @@ module Puma
149
167
  end
150
168
  end
151
169
  else
152
- def cork_socket(socket)
153
- end
154
-
155
- def uncork_socket(socket)
156
- end
157
-
158
170
  def closed_socket?(socket)
159
171
  false
160
172
  end
@@ -206,33 +218,36 @@ module Puma
206
218
  if @queue_requests
207
219
  process_now = client.eagerly_finish
208
220
  else
209
- client.finish(@first_data_timeout)
221
+ @thread_pool.with_force_shutdown do
222
+ client.finish(@first_data_timeout)
223
+ end
210
224
  process_now = true
211
225
  end
212
226
  rescue MiniSSL::SSLError => e
213
- ssl_socket = client.io
214
- addr = ssl_socket.peeraddr.last
215
- cert = ssl_socket.peercert
216
-
227
+ @events.ssl_error e, client.io
217
228
  client.close
218
229
 
219
- @events.ssl_error e, addr, cert
220
230
  rescue HttpParserError => e
221
231
  client.write_error(400)
222
232
  client.close
223
233
 
224
234
  @events.parse_error e, client
225
- rescue ConnectionError, EOFError => e
235
+ rescue EOFError => e
236
+ client.close
237
+
238
+ # Swallow, do not log
239
+ rescue ConnectionError, ThreadPool::ForceShutdown => e
226
240
  client.close
227
241
 
228
242
  @events.connection_error e, client
229
243
  else
230
- if process_now
231
- process_client client, buffer
232
- else
233
- client.set_timeout @first_data_timeout
234
- @reactor.add client
235
- end
244
+ process_now ||= @shutdown_mutex.synchronize do
245
+ next true unless @queue_requests
246
+ client.set_timeout @first_data_timeout
247
+ @reactor.add client
248
+ false
249
+ end
250
+ process_client client, buffer if process_now
236
251
  end
237
252
 
238
253
  process_now
@@ -318,7 +333,9 @@ module Puma
318
333
  @events.fire :state, @status
319
334
 
320
335
  if queue_requests
321
- @queue_requests = false
336
+ @shutdown_mutex.synchronize do
337
+ @queue_requests = false
338
+ end
322
339
  @reactor.clear!
323
340
  @reactor.shutdown
324
341
  end
@@ -397,32 +414,32 @@ module Puma
397
414
  check_for_more_data = false
398
415
  end
399
416
 
400
- unless client.reset(check_for_more_data)
401
- return unless @queue_requests
402
- close_socket = false
403
- client.set_timeout @persistent_timeout
404
- @reactor.add client
405
- return
417
+ next_request_ready = @thread_pool.with_force_shutdown do
418
+ client.reset(check_for_more_data)
419
+ end
420
+
421
+ unless next_request_ready
422
+ @shutdown_mutex.synchronize do
423
+ return unless @queue_requests
424
+ close_socket = false
425
+ client.set_timeout @persistent_timeout
426
+ @reactor.add client
427
+ return
428
+ end
406
429
  end
407
430
  end
408
431
  end
409
432
 
410
433
  # The client disconnected while we were reading data
411
- rescue ConnectionError
434
+ rescue ConnectionError, ThreadPool::ForceShutdown
412
435
  # Swallow them. The ensure tries to close +client+ down
413
436
 
414
437
  # SSL handshake error
415
438
  rescue MiniSSL::SSLError => e
416
- lowlevel_error(e, client.env)
417
-
418
- ssl_socket = client.io
419
- addr = ssl_socket.peeraddr.last
420
- cert = ssl_socket.peercert
421
-
439
+ lowlevel_error e, client.env
440
+ @events.ssl_error e, client.io
422
441
  close_socket = true
423
442
 
424
- @events.ssl_error e, addr, cert
425
-
426
443
  # The client doesn't know HTTP well
427
444
  rescue HttpParserError => e
428
445
  lowlevel_error(e, client.env)
@@ -617,7 +634,9 @@ module Puma
617
634
 
618
635
  begin
619
636
  begin
620
- status, headers, res_body = @app.call(env)
637
+ status, headers, res_body = @thread_pool.with_force_shutdown do
638
+ @app.call(env)
639
+ end
621
640
 
622
641
  return :async if req.hijacked
623
642
 
@@ -915,7 +934,7 @@ module Puma
915
934
 
916
935
  if @thread_pool
917
936
  if timeout = @options[:force_shutdown_after]
918
- @thread_pool.shutdown timeout.to_i
937
+ @thread_pool.shutdown timeout.to_f
919
938
  else
920
939
  @thread_pool.shutdown
921
940
  end