puma 5.0.4 → 5.5.1

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 +250 -48
  3. data/README.md +90 -24
  4. data/docs/architecture.md +57 -20
  5. data/docs/compile_options.md +21 -0
  6. data/docs/deployment.md +53 -67
  7. data/docs/fork_worker.md +2 -0
  8. data/docs/jungle/rc.d/README.md +1 -1
  9. data/docs/kubernetes.md +66 -0
  10. data/docs/plugins.md +15 -15
  11. data/docs/rails_dev_mode.md +28 -0
  12. data/docs/restart.md +7 -7
  13. data/docs/signals.md +10 -10
  14. data/docs/stats.md +142 -0
  15. data/docs/systemd.md +85 -66
  16. data/ext/puma_http11/extconf.rb +36 -6
  17. data/ext/puma_http11/http11_parser.c +64 -59
  18. data/ext/puma_http11/http11_parser.h +1 -1
  19. data/ext/puma_http11/http11_parser.java.rl +1 -1
  20. data/ext/puma_http11/http11_parser.rl +1 -1
  21. data/ext/puma_http11/http11_parser_common.rl +1 -1
  22. data/ext/puma_http11/mini_ssl.c +177 -84
  23. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +39 -41
  24. data/ext/puma_http11/puma_http11.c +8 -2
  25. data/lib/puma/app/status.rb +4 -7
  26. data/lib/puma/binder.rb +121 -46
  27. data/lib/puma/cli.rb +9 -0
  28. data/lib/puma/client.rb +58 -19
  29. data/lib/puma/cluster/worker.rb +19 -16
  30. data/lib/puma/cluster/worker_handle.rb +9 -2
  31. data/lib/puma/cluster.rb +46 -22
  32. data/lib/puma/configuration.rb +18 -2
  33. data/lib/puma/const.rb +14 -4
  34. data/lib/puma/control_cli.rb +76 -71
  35. data/lib/puma/detect.rb +14 -10
  36. data/lib/puma/dsl.rb +143 -26
  37. data/lib/puma/error_logger.rb +12 -5
  38. data/lib/puma/events.rb +18 -3
  39. data/lib/puma/json_serialization.rb +96 -0
  40. data/lib/puma/launcher.rb +54 -6
  41. data/lib/puma/minissl/context_builder.rb +6 -0
  42. data/lib/puma/minissl.rb +54 -38
  43. data/lib/puma/null_io.rb +12 -0
  44. data/lib/puma/plugin.rb +1 -1
  45. data/lib/puma/queue_close.rb +7 -7
  46. data/lib/puma/rack/builder.rb +1 -1
  47. data/lib/puma/reactor.rb +19 -12
  48. data/lib/puma/request.rb +45 -16
  49. data/lib/puma/runner.rb +38 -13
  50. data/lib/puma/server.rb +62 -123
  51. data/lib/puma/state_file.rb +5 -3
  52. data/lib/puma/systemd.rb +46 -0
  53. data/lib/puma/thread_pool.rb +10 -7
  54. data/lib/puma/util.rb +8 -1
  55. data/lib/puma.rb +36 -10
  56. data/lib/rack/handler/puma.rb +1 -0
  57. metadata +15 -9
data/lib/puma/server.rb CHANGED
@@ -14,6 +14,7 @@ require 'puma/io_buffer'
14
14
  require 'puma/request'
15
15
 
16
16
  require 'socket'
17
+ require 'io/wait'
17
18
  require 'forwardable'
18
19
 
19
20
  module Puma
@@ -84,12 +85,14 @@ module Puma
84
85
 
85
86
  @options = options
86
87
 
87
- @early_hints = options.fetch :early_hints, nil
88
- @first_data_timeout = options.fetch :first_data_timeout, FIRST_DATA_TIMEOUT
89
- @min_threads = options.fetch :min_threads, 0
90
- @max_threads = options.fetch :max_threads , (Puma.mri? ? 5 : 16)
91
- @persistent_timeout = options.fetch :persistent_timeout, PERSISTENT_TIMEOUT
92
- @queue_requests = options.fetch :queue_requests, true
88
+ @early_hints = options.fetch :early_hints, nil
89
+ @first_data_timeout = options.fetch :first_data_timeout, FIRST_DATA_TIMEOUT
90
+ @min_threads = options.fetch :min_threads, 0
91
+ @max_threads = options.fetch :max_threads , (Puma.mri? ? 5 : 16)
92
+ @persistent_timeout = options.fetch :persistent_timeout, PERSISTENT_TIMEOUT
93
+ @queue_requests = options.fetch :queue_requests, true
94
+ @max_fast_inline = options.fetch :max_fast_inline, MAX_FAST_INLINE
95
+ @io_selector_backend = options.fetch :io_selector_backend, :auto
93
96
 
94
97
  temp = !!(@options[:environment] =~ /\A(development|test)\z/)
95
98
  @leak_stack_on_error = @options[:environment] ? temp : true
@@ -118,17 +121,13 @@ module Puma
118
121
  # :nodoc:
119
122
  # @version 5.0.0
120
123
  def tcp_cork_supported?
121
- RbConfig::CONFIG['host_os'] =~ /linux/ &&
122
- Socket.const_defined?(:IPPROTO_TCP) &&
123
- Socket.const_defined?(:TCP_CORK)
124
+ Socket.const_defined?(:TCP_CORK) && Socket.const_defined?(:IPPROTO_TCP)
124
125
  end
125
126
 
126
127
  # :nodoc:
127
128
  # @version 5.0.0
128
129
  def closed_socket_supported?
129
- RbConfig::CONFIG['host_os'] =~ /linux/ &&
130
- Socket.const_defined?(:IPPROTO_TCP) &&
131
- Socket.const_defined?(:TCP_INFO)
130
+ Socket.const_defined?(:TCP_INFO) && Socket.const_defined?(:IPPROTO_TCP)
132
131
  end
133
132
  private :tcp_cork_supported?
134
133
  private :closed_socket_supported?
@@ -136,26 +135,27 @@ module Puma
136
135
 
137
136
  # On Linux, use TCP_CORK to better control how the TCP stack
138
137
  # packetizes our stream. This improves both latency and throughput.
138
+ # socket parameter may be an MiniSSL::Socket, so use to_io
139
139
  #
140
140
  if tcp_cork_supported?
141
- UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
142
-
143
141
  # 6 == Socket::IPPROTO_TCP
144
142
  # 3 == TCP_CORK
145
143
  # 1/0 == turn on/off
146
144
  def cork_socket(socket)
145
+ skt = socket.to_io
147
146
  begin
148
- socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if socket.kind_of? TCPSocket
147
+ skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if skt.kind_of? TCPSocket
149
148
  rescue IOError, SystemCallError
150
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
149
+ Puma::Util.purge_interrupt_queue
151
150
  end
152
151
  end
153
152
 
154
153
  def uncork_socket(socket)
154
+ skt = socket.to_io
155
155
  begin
156
- socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if socket.kind_of? TCPSocket
156
+ skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if skt.kind_of? TCPSocket
157
157
  rescue IOError, SystemCallError
158
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
158
+ Puma::Util.purge_interrupt_queue
159
159
  end
160
160
  end
161
161
  else
@@ -167,14 +167,16 @@ module Puma
167
167
  end
168
168
 
169
169
  if closed_socket_supported?
170
+ UNPACK_TCP_STATE_FROM_TCP_INFO = "C".freeze
171
+
170
172
  def closed_socket?(socket)
171
- return false unless socket.kind_of? TCPSocket
172
- return false unless @precheck_closing
173
+ skt = socket.to_io
174
+ return false unless skt.kind_of?(TCPSocket) && @precheck_closing
173
175
 
174
176
  begin
175
- tcp_info = socket.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
177
+ tcp_info = skt.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
176
178
  rescue IOError, SystemCallError
177
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
179
+ Puma::Util.purge_interrupt_queue
178
180
  @precheck_closing = false
179
181
  false
180
182
  else
@@ -218,7 +220,7 @@ module Puma
218
220
  # up in the background to handle requests. Otherwise requests
219
221
  # are handled synchronously.
220
222
  #
221
- def run(background=true)
223
+ def run(background=true, thread_name: 'server')
222
224
  BasicSocket.do_not_reverse_lookup = true
223
225
 
224
226
  @events.fire :state, :booting
@@ -226,6 +228,7 @@ module Puma
226
228
  @status = :run
227
229
 
228
230
  @thread_pool = ThreadPool.new(
231
+ thread_name,
229
232
  @min_threads,
230
233
  @max_threads,
231
234
  ::Puma::IOBuffer,
@@ -236,7 +239,7 @@ module Puma
236
239
  @thread_pool.clean_thread_locals = @options[:clean_thread_locals]
237
240
 
238
241
  if @queue_requests
239
- @reactor = Reactor.new(&method(:reactor_wakeup))
242
+ @reactor = Reactor.new(@io_selector_backend, &method(:reactor_wakeup))
240
243
  @reactor.run
241
244
  end
242
245
 
@@ -254,7 +257,7 @@ module Puma
254
257
 
255
258
  if background
256
259
  @thread = Thread.new do
257
- Puma.set_thread_name "server"
260
+ Puma.set_thread_name thread_name
258
261
  handle_servers
259
262
  end
260
263
  return @thread
@@ -294,6 +297,9 @@ module Puma
294
297
  @thread_pool << client
295
298
  elsif shutdown || client.timeout == 0
296
299
  client.timeout!
300
+ else
301
+ client.set_timeout(@first_data_timeout)
302
+ false
297
303
  end
298
304
  rescue StandardError => e
299
305
  client_error(e, client)
@@ -307,6 +313,7 @@ module Puma
307
313
  sockets = [check] + @binder.ios
308
314
  pool = @thread_pool
309
315
  queue_requests = @queue_requests
316
+ drain = @options[:drain_on_shutdown] ? 0 : nil
310
317
 
311
318
  remote_addr_value = nil
312
319
  remote_addr_header = nil
@@ -316,38 +323,48 @@ module Puma
316
323
  remote_addr_value = @options[:remote_address_value]
317
324
  when :header
318
325
  remote_addr_header = @options[:remote_address_header]
326
+ when :proxy_protocol
327
+ remote_addr_proxy_protocol = @options[:remote_address_proxy_protocol]
319
328
  end
320
329
 
321
- while @status == :run
330
+ while @status == :run || (drain && shutting_down?)
322
331
  begin
323
- ios = IO.select sockets
332
+ ios = IO.select sockets, nil, nil, (shutting_down? ? 0 : nil)
333
+ break unless ios
324
334
  ios.first.each do |sock|
325
335
  if sock == check
326
336
  break if handle_check
327
337
  else
328
338
  pool.wait_until_not_full
329
- pool.wait_for_less_busy_worker(
330
- @options[:wait_for_less_busy_worker].to_f)
339
+ pool.wait_for_less_busy_worker(@options[:wait_for_less_busy_worker])
331
340
 
332
341
  io = begin
333
342
  sock.accept_nonblock
334
343
  rescue IO::WaitReadable
335
344
  next
336
345
  end
346
+ drain += 1 if shutting_down?
337
347
  client = Client.new io, @binder.env(sock)
348
+ client.listener = sock
338
349
  if remote_addr_value
339
350
  client.peerip = remote_addr_value
340
351
  elsif remote_addr_header
341
352
  client.remote_addr_header = remote_addr_header
353
+ elsif remote_addr_proxy_protocol
354
+ client.expect_proxy_proto = remote_addr_proxy_protocol
342
355
  end
343
356
  pool << client
344
357
  end
345
358
  end
346
- rescue Object => e
359
+ rescue IOError, Errno::EBADF
360
+ # In the case that any of the sockets are unexpectedly close.
361
+ raise
362
+ rescue StandardError => e
347
363
  @events.unknown_error e, nil, "Listen loop"
348
364
  end
349
365
  end
350
366
 
367
+ @events.debug "Drained #{drain} additional connections." if drain
351
368
  @events.fire :state, @status
352
369
 
353
370
  if queue_requests
@@ -388,7 +405,7 @@ module Puma
388
405
  return true
389
406
  end
390
407
 
391
- return false
408
+ false
392
409
  end
393
410
 
394
411
  # Given a connection on +client+, handle the incoming requests,
@@ -427,7 +444,7 @@ module Puma
427
444
 
428
445
  while true
429
446
  @requests_count += 1
430
- case handle_request(client, buffer)
447
+ case handle_request(client, buffer, requests + 1)
431
448
  when false
432
449
  break
433
450
  when :async
@@ -440,18 +457,17 @@ module Puma
440
457
 
441
458
  requests += 1
442
459
 
443
- check_for_more_data = @status == :run
460
+ # As an optimization, try to read the next request from the
461
+ # socket for a short time before returning to the reactor.
462
+ fast_check = @status == :run
444
463
 
445
- if requests >= MAX_FAST_INLINE
446
- # This will mean that reset will only try to use the data it already
447
- # has buffered and won't try to read more data. What this means is that
448
- # every client, independent of their request speed, gets treated like a slow
449
- # one once every MAX_FAST_INLINE requests.
450
- check_for_more_data = false
451
- end
464
+ # Always pass the client back to the reactor after a reasonable
465
+ # number of inline requests if there are other requests pending.
466
+ fast_check = false if requests >= @max_fast_inline &&
467
+ @thread_pool.backlog > 0
452
468
 
453
469
  next_request_ready = with_force_shutdown(client) do
454
- client.reset(check_for_more_data)
470
+ client.reset(fast_check)
455
471
  end
456
472
 
457
473
  unless next_request_ready
@@ -475,7 +491,7 @@ module Puma
475
491
  begin
476
492
  client.close if close_socket
477
493
  rescue IOError, SystemCallError
478
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
494
+ Puma::Util.purge_interrupt_queue
479
495
  # Already closed
480
496
  rescue StandardError => e
481
497
  @events.unknown_error e, nil, "Client"
@@ -493,62 +509,6 @@ module Puma
493
509
 
494
510
  # :nocov:
495
511
 
496
- # Given the request +env+ from +client+ and the partial body +body+
497
- # plus a potential Content-Length value +cl+, finish reading
498
- # the body and return it.
499
- #
500
- # If the body is larger than MAX_BODY, a Tempfile object is used
501
- # for the body, otherwise a StringIO is used.
502
- # @deprecated 6.0.0
503
- #
504
- def read_body(env, client, body, cl)
505
- content_length = cl.to_i
506
-
507
- remain = content_length - body.bytesize
508
-
509
- return StringIO.new(body) if remain <= 0
510
-
511
- # Use a Tempfile if there is a lot of data left
512
- if remain > MAX_BODY
513
- stream = Tempfile.new(Const::PUMA_TMP_BASE)
514
- stream.binmode
515
- else
516
- # The body[0,0] trick is to get an empty string in the same
517
- # encoding as body.
518
- stream = StringIO.new body[0,0]
519
- end
520
-
521
- stream.write body
522
-
523
- # Read an odd sized chunk so we can read even sized ones
524
- # after this
525
- chunk = client.readpartial(remain % CHUNK_SIZE)
526
-
527
- # No chunk means a closed socket
528
- unless chunk
529
- stream.close
530
- return nil
531
- end
532
-
533
- remain -= stream.write(chunk)
534
-
535
- # Read the rest of the chunks
536
- while remain > 0
537
- chunk = client.readpartial(CHUNK_SIZE)
538
- unless chunk
539
- stream.close
540
- return nil
541
- end
542
-
543
- remain -= stream.write(chunk)
544
- end
545
-
546
- stream.rewind
547
-
548
- return stream
549
- end
550
- # :nocov:
551
-
552
512
  # Handle various error types thrown by Client I/O operations.
553
513
  def client_error(e, client)
554
514
  # Swallow, do not log
@@ -581,7 +541,8 @@ module Puma
581
541
  end
582
542
 
583
543
  if @leak_stack_on_error
584
- [status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{e.backtrace.join("\n")}"]]
544
+ backtrace = e.backtrace.nil? ? '<no backtrace available>' : e.backtrace.join("\n")
545
+ [status, {}, ["Puma caught this error: #{e.message} (#{e.class})\n#{backtrace}"]]
585
546
  else
586
547
  [status, {}, ["An unhandled lowlevel error occurred. The application logs may have details.\n"]]
587
548
  end
@@ -605,28 +566,6 @@ module Puma
605
566
  $stdout.syswrite "#{pid}: === End thread backtrace dump ===\n"
606
567
  end
607
568
 
608
- if @options[:drain_on_shutdown]
609
- count = 0
610
-
611
- while true
612
- ios = IO.select @binder.ios, nil, nil, 0
613
- break unless ios
614
-
615
- ios.first.each do |sock|
616
- begin
617
- if io = sock.accept_nonblock
618
- count += 1
619
- client = Client.new io, @binder.env(sock)
620
- @thread_pool << client
621
- end
622
- rescue SystemCallError
623
- end
624
- end
625
- end
626
-
627
- @events.debug "Drained #{count} additional connections."
628
- end
629
-
630
569
  if @status != :restart
631
570
  @binder.close
632
571
  end
@@ -644,11 +583,11 @@ module Puma
644
583
  @notify << message
645
584
  rescue IOError, NoMethodError, Errno::EPIPE
646
585
  # The server, in another thread, is shutting down
647
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
586
+ Puma::Util.purge_interrupt_queue
648
587
  rescue RuntimeError => e
649
588
  # Temporary workaround for https://bugs.ruby-lang.org/issues/13239
650
589
  if e.message.include?('IOError')
651
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
590
+ Puma::Util.purge_interrupt_queue
652
591
  else
653
592
  raise e
654
593
  end
@@ -9,9 +9,11 @@ module Puma
9
9
  end
10
10
 
11
11
  def save(path, permission = nil)
12
- File.open(path, "w") do |file|
13
- file.chmod(permission) if permission
14
- file.write(YAML.dump(@options))
12
+ contents =YAML.dump @options
13
+ if permission
14
+ File.write path, contents, mode: 'wb:UTF-8'
15
+ else
16
+ File.write path, contents, mode: 'wb:UTF-8', perm: permission
15
17
  end
16
18
  end
17
19
 
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sd_notify'
4
+
5
+ module Puma
6
+ class Systemd
7
+ def initialize(events)
8
+ @events = events
9
+ end
10
+
11
+ def hook_events
12
+ @events.on_booted { SdNotify.ready }
13
+ @events.on_stopped { SdNotify.stopping }
14
+ @events.on_restart { SdNotify.reloading }
15
+ end
16
+
17
+ def start_watchdog
18
+ return unless SdNotify.watchdog?
19
+
20
+ ping_f = watchdog_sleep_time
21
+
22
+ log "Pinging systemd watchdog every #{ping_f.round(1)} sec"
23
+ Thread.new do
24
+ loop do
25
+ sleep ping_f
26
+ SdNotify.watchdog
27
+ end
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def watchdog_sleep_time
34
+ usec = Integer(ENV["WATCHDOG_USEC"])
35
+
36
+ sec_f = usec / 1_000_000.0
37
+ # "It is recommended that a daemon sends a keep-alive notification message
38
+ # to the service manager every half of the time returned here."
39
+ sec_f / 2
40
+ end
41
+
42
+ def log(str)
43
+ @events.log str
44
+ end
45
+ end
46
+ end
@@ -13,7 +13,7 @@ module Puma
13
13
  # a thread pool via the `Puma::ThreadPool#<<` operator where it is stored in a `@todo` array.
14
14
  #
15
15
  # Each thread in the pool has an internal loop where it pulls a request from the `@todo` array
16
- # and proceses it.
16
+ # and processes it.
17
17
  class ThreadPool
18
18
  class ForceShutdown < RuntimeError
19
19
  end
@@ -29,7 +29,7 @@ module Puma
29
29
  # The block passed is the work that will be performed in each
30
30
  # thread.
31
31
  #
32
- def initialize(min, max, *extra, &block)
32
+ def initialize(name, min, max, *extra, &block)
33
33
  @not_empty = ConditionVariable.new
34
34
  @not_full = ConditionVariable.new
35
35
  @mutex = Mutex.new
@@ -39,6 +39,7 @@ module Puma
39
39
  @spawned = 0
40
40
  @waiting = 0
41
41
 
42
+ @name = name
42
43
  @min = Integer(min)
43
44
  @max = Integer(max)
44
45
  @block = block
@@ -101,7 +102,7 @@ module Puma
101
102
  @spawned += 1
102
103
 
103
104
  th = Thread.new(@spawned) do |spawned|
104
- Puma.set_thread_name 'threadpool %03i' % spawned
105
+ Puma.set_thread_name '%s threadpool %03i' % [@name, spawned]
105
106
  todo = @todo
106
107
  block = @block
107
108
  mutex = @mutex
@@ -119,6 +120,7 @@ module Puma
119
120
  @trim_requested -= 1
120
121
  @spawned -= 1
121
122
  @workers.delete th
123
+ not_full.signal
122
124
  Thread.exit
123
125
  end
124
126
 
@@ -220,7 +222,7 @@ module Puma
220
222
  # then the `@todo` array would stay the same size as the reactor works
221
223
  # to try to buffer the request. In that scenario the next call to this
222
224
  # method would not block and another request would be added into the reactor
223
- # by the server. This would continue until a fully bufferend request
225
+ # by the server. This would continue until a fully buffered request
224
226
  # makes it through the reactor and can then be processed by the thread pool.
225
227
  def wait_until_not_full
226
228
  with_mutex do
@@ -240,11 +242,12 @@ module Puma
240
242
 
241
243
  # @version 5.0.0
242
244
  def wait_for_less_busy_worker(delay_s)
245
+ return unless delay_s && delay_s > 0
246
+
243
247
  # Ruby MRI does GVL, this can result
244
248
  # in processing contention when multiple threads
245
249
  # (requests) are running concurrently
246
250
  return unless Puma.mri?
247
- return unless delay_s > 0
248
251
 
249
252
  with_mutex do
250
253
  return if @shutdown
@@ -317,12 +320,12 @@ module Puma
317
320
  end
318
321
 
319
322
  def auto_trim!(timeout=30)
320
- @auto_trim = Automaton.new(self, timeout, "threadpool trimmer", :trim)
323
+ @auto_trim = Automaton.new(self, timeout, "#{@name} threadpool trimmer", :trim)
321
324
  @auto_trim.start!
322
325
  end
323
326
 
324
327
  def auto_reap!(timeout=5)
325
- @reaper = Automaton.new(self, timeout, "threadpool reaper", :reap)
328
+ @reaper = Automaton.new(self, timeout, "#{@name} threadpool reaper", :reap)
326
329
  @reaper.start!
327
330
  end
328
331
 
data/lib/puma/util.rb CHANGED
@@ -10,6 +10,13 @@ module Puma
10
10
  IO.pipe
11
11
  end
12
12
 
13
+ # An instance method on Thread has been provided to address https://bugs.ruby-lang.org/issues/13632,
14
+ # which currently effects some older versions of Ruby: 2.2.7 2.2.8 2.2.9 2.2.10 2.3.4 2.4.1
15
+ # Additional context: https://github.com/puma/puma/pull/1345
16
+ def purge_interrupt_queue
17
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
18
+ end
19
+
13
20
  # Unescapes a URI escaped string with +encoding+. +encoding+ will be the
14
21
  # target encoding of the string returned, and it defaults to UTF-8
15
22
  if defined?(::Encoding)
@@ -61,7 +68,7 @@ module Puma
61
68
  end
62
69
  end
63
70
 
64
- return params
71
+ params
65
72
  end
66
73
 
67
74
  # A case-insensitive Hash that preserves the original case of a
data/lib/puma.rb CHANGED
@@ -12,12 +12,47 @@ require 'thread'
12
12
 
13
13
  require 'puma/puma_http11'
14
14
  require 'puma/detect'
15
+ require 'puma/json_serialization'
15
16
 
16
17
  module Puma
17
18
  autoload :Const, 'puma/const'
18
19
  autoload :Server, 'puma/server'
19
20
  autoload :Launcher, 'puma/launcher'
20
21
 
22
+ # at present, MiniSSL::Engine is only defined in extension code (puma_http11),
23
+ # not in minissl.rb
24
+ HAS_SSL = const_defined?(:MiniSSL, false) && MiniSSL.const_defined?(:Engine, false)
25
+
26
+ HAS_UNIX_SOCKET = Object.const_defined? :UNIXSocket
27
+
28
+ if HAS_SSL
29
+ require 'puma/minissl'
30
+ else
31
+ module MiniSSL
32
+ # this class is defined so that it exists when Puma is compiled
33
+ # without ssl support, as Server and Reactor use it in rescue statements.
34
+ class SSLError < StandardError ; end
35
+ end
36
+ end
37
+
38
+ def self.ssl?
39
+ HAS_SSL
40
+ end
41
+
42
+ def self.abstract_unix_socket?
43
+ @abstract_unix ||=
44
+ if HAS_UNIX_SOCKET
45
+ begin
46
+ ::UNIXServer.new("\0puma.temp.unix").close
47
+ true
48
+ rescue ArgumentError # darwin
49
+ false
50
+ end
51
+ else
52
+ false
53
+ end
54
+ end
55
+
21
56
  # @!attribute [rw] stats_object=
22
57
  def self.stats_object=(val)
23
58
  @get_stats = val
@@ -25,8 +60,7 @@ module Puma
25
60
 
26
61
  # @!attribute [rw] stats_object
27
62
  def self.stats
28
- require 'json'
29
- @get_stats.stats.to_json
63
+ Puma::JSONSerialization.generate @get_stats.stats
30
64
  end
31
65
 
32
66
  # @!attribute [r] stats_hash
@@ -40,12 +74,4 @@ module Puma
40
74
  return unless Thread.current.respond_to?(:name=)
41
75
  Thread.current.name = "puma #{name}"
42
76
  end
43
-
44
- unless HAS_SSL
45
- module MiniSSL
46
- # this class is defined so that it exists when Puma is compiled
47
- # without ssl support, as Server and Reactor use it in rescue statements.
48
- class SSLError < StandardError ; end
49
- end
50
- end
51
77
  end
@@ -31,6 +31,7 @@ module Rack
31
31
 
32
32
  conf = ::Puma::Configuration.new(options, default_options) do |user_config, file_config, default_config|
33
33
  if options.delete(:Verbose)
34
+ require 'rack/common_logger'
34
35
  app = Rack::CommonLogger.new(app, STDOUT)
35
36
  end
36
37