puma 5.6.7 → 6.4.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +269 -13
  3. data/README.md +78 -29
  4. data/bin/puma-wild +1 -1
  5. data/docs/compile_options.md +34 -0
  6. data/docs/fork_worker.md +1 -3
  7. data/docs/kubernetes.md +12 -0
  8. data/docs/nginx.md +1 -1
  9. data/docs/systemd.md +1 -2
  10. data/docs/testing_benchmarks_local_files.md +150 -0
  11. data/docs/testing_test_rackup_ci_files.md +36 -0
  12. data/ext/puma_http11/extconf.rb +11 -8
  13. data/ext/puma_http11/http11_parser.c +1 -1
  14. data/ext/puma_http11/http11_parser.h +1 -1
  15. data/ext/puma_http11/http11_parser.java.rl +2 -2
  16. data/ext/puma_http11/http11_parser.rl +2 -2
  17. data/ext/puma_http11/http11_parser_common.rl +2 -2
  18. data/ext/puma_http11/mini_ssl.c +122 -18
  19. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  20. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  21. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +156 -53
  22. data/ext/puma_http11/puma_http11.c +17 -9
  23. data/lib/puma/app/status.rb +4 -4
  24. data/lib/puma/binder.rb +50 -53
  25. data/lib/puma/cli.rb +16 -18
  26. data/lib/puma/client.rb +59 -19
  27. data/lib/puma/cluster/worker.rb +18 -11
  28. data/lib/puma/cluster/worker_handle.rb +4 -1
  29. data/lib/puma/cluster.rb +33 -30
  30. data/lib/puma/commonlogger.rb +21 -14
  31. data/lib/puma/configuration.rb +78 -58
  32. data/lib/puma/const.rb +129 -92
  33. data/lib/puma/control_cli.rb +15 -11
  34. data/lib/puma/detect.rb +4 -0
  35. data/lib/puma/dsl.rb +237 -56
  36. data/lib/puma/error_logger.rb +18 -9
  37. data/lib/puma/events.rb +6 -126
  38. data/lib/puma/io_buffer.rb +39 -4
  39. data/lib/puma/jruby_restart.rb +2 -1
  40. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  41. data/lib/puma/launcher.rb +102 -175
  42. data/lib/puma/log_writer.rb +147 -0
  43. data/lib/puma/minissl/context_builder.rb +24 -12
  44. data/lib/puma/minissl.rb +99 -11
  45. data/lib/puma/plugin/systemd.rb +90 -0
  46. data/lib/puma/plugin/tmp_restart.rb +1 -1
  47. data/lib/puma/rack/builder.rb +6 -6
  48. data/lib/puma/rack/urlmap.rb +1 -1
  49. data/lib/puma/rack_default.rb +19 -4
  50. data/lib/puma/reactor.rb +19 -10
  51. data/lib/puma/request.rb +365 -170
  52. data/lib/puma/runner.rb +56 -20
  53. data/lib/puma/sd_notify.rb +149 -0
  54. data/lib/puma/server.rb +116 -89
  55. data/lib/puma/single.rb +13 -11
  56. data/lib/puma/state_file.rb +1 -4
  57. data/lib/puma/thread_pool.rb +57 -19
  58. data/lib/puma/util.rb +0 -11
  59. data/lib/puma.rb +9 -10
  60. data/lib/rack/handler/puma.rb +113 -86
  61. metadata +9 -5
  62. data/lib/puma/queue_close.rb +0 -26
  63. data/lib/puma/systemd.rb +0 -46
  64. data/lib/rack/version_restriction.rb +0 -15
data/lib/puma/dsl.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/const'
4
- require 'puma/util'
3
+ require_relative 'const'
4
+ require_relative 'util'
5
5
 
6
6
  module Puma
7
7
  # The methods that are available for use inside the configuration file.
@@ -32,8 +32,24 @@ module Puma
32
32
  # You can also find many examples being used by the test suite in
33
33
  # +test/config+.
34
34
  #
35
+ # Puma v6 adds the option to specify a key name (String or Symbol) to the
36
+ # hooks that run inside the forked workers. All the hooks run inside the
37
+ # {Puma::Cluster::Worker#run} method.
38
+ #
39
+ # Previously, the worker index and the LogWriter instance were passed to the
40
+ # hook blocks/procs. If a key name is specified, a hash is passed as the last
41
+ # parameter. This allows storage of data, typically objects that are created
42
+ # before the worker that need to be passed to the hook when the worker is shutdown.
43
+ #
44
+ # The following hooks have been updated:
45
+ #
46
+ # | DSL Method | Options Key | Fork Block Location |
47
+ # | on_worker_boot | :before_worker_boot | inside, before |
48
+ # | on_worker_shutdown | :before_worker_shutdown | inside, after |
49
+ # | on_refork | :before_refork | inside |
50
+ #
35
51
  class DSL
36
- include ConfigDefault
52
+ ON_WORKER_KEY = [String, Symbol].freeze
37
53
 
38
54
  # convenience method so logic can be used in CI
39
55
  # @see ssl_bind
@@ -49,28 +65,58 @@ module Puma
49
65
 
50
66
  ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify)
51
67
 
68
+ low_latency_str = opts.key?(:low_latency) ? "&low_latency=#{opts[:low_latency]}" : ''
52
69
  backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
53
70
 
54
71
  if defined?(JRUBY_VERSION)
55
- ssl_cipher_list = opts[:ssl_cipher_list] ?
56
- "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil
72
+ cipher_suites = opts[:ssl_cipher_list] ? "&ssl_cipher_list=#{opts[:ssl_cipher_list]}" : nil # old name
73
+ cipher_suites = "#{cipher_suites}&cipher_suites=#{opts[:cipher_suites]}" if opts[:cipher_suites]
74
+ protocols = opts[:protocols] ? "&protocols=#{opts[:protocols]}" : nil
57
75
 
58
76
  keystore_additions = "keystore=#{opts[:keystore]}&keystore-pass=#{opts[:keystore_pass]}"
77
+ keystore_additions = "#{keystore_additions}&keystore-type=#{opts[:keystore_type]}" if opts[:keystore_type]
78
+ if opts[:truststore]
79
+ truststore_additions = "&truststore=#{opts[:truststore]}"
80
+ truststore_additions = "#{truststore_additions}&truststore-pass=#{opts[:truststore_pass]}" if opts[:truststore_pass]
81
+ truststore_additions = "#{truststore_additions}&truststore-type=#{opts[:truststore_type]}" if opts[:truststore_type]
82
+ end
59
83
 
60
- "ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
84
+ "ssl://#{host}:#{port}?#{keystore_additions}#{truststore_additions}#{cipher_suites}#{protocols}" \
61
85
  "&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
62
86
  else
63
- ssl_cipher_filter = opts[:ssl_cipher_filter] ?
64
- "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
65
-
66
- v_flags = (ary = opts[:verification_flags]) ?
67
- "&verification_flags=#{Array(ary).join ','}" : nil
68
-
69
- cert_flags = (cert = opts[:cert]) ? "cert=#{Puma::Util.escape(opts[:cert])}" : nil
70
- key_flags = (cert = opts[:key]) ? "&key=#{Puma::Util.escape(opts[:key])}" : nil
87
+ ssl_cipher_filter = opts[:ssl_cipher_filter] ? "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
88
+ v_flags = (ary = opts[:verification_flags]) ? "&verification_flags=#{Array(ary).join ','}" : nil
89
+
90
+ cert_flags = (cert = opts[:cert]) ? "cert=#{Puma::Util.escape(cert)}" : nil
91
+ key_flags = (key = opts[:key]) ? "&key=#{Puma::Util.escape(key)}" : nil
92
+ password_flags = (password_command = opts[:key_password_command]) ? "&key_password_command=#{Puma::Util.escape(password_command)}" : nil
93
+
94
+ reuse_flag =
95
+ if (reuse = opts[:reuse])
96
+ if reuse == true
97
+ '&reuse=dflt'
98
+ elsif reuse.is_a?(Hash) && (reuse.key?(:size) || reuse.key?(:timeout))
99
+ val = +''
100
+ if (size = reuse[:size]) && Integer === size
101
+ val << size.to_s
102
+ end
103
+ if (timeout = reuse[:timeout]) && Integer === timeout
104
+ val << ",#{timeout}"
105
+ end
106
+ if val.empty?
107
+ nil
108
+ else
109
+ "&reuse=#{val}"
110
+ end
111
+ else
112
+ nil
113
+ end
114
+ else
115
+ nil
116
+ end
71
117
 
72
- "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}" \
73
- "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
118
+ "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{password_flags}#{ssl_cipher_filter}" \
119
+ "#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}#{low_latency_str}"
74
120
  end
75
121
  end
76
122
 
@@ -106,7 +152,7 @@ module Puma
106
152
  end
107
153
 
108
154
  def default_host
109
- @options[:default_host] || Configuration::DefaultTCPHost
155
+ @options[:default_host] || Configuration::DEFAULTS[:tcp_host]
110
156
  end
111
157
 
112
158
  def inject(&blk)
@@ -206,6 +252,7 @@ module Puma
206
252
  #
207
253
  # * Set the socket backlog depth with +backlog+, default is 1024.
208
254
  # * Set up an SSL certificate with +key+ & +cert+.
255
+ # * Set up an SSL certificate for mTLS with +key+, +cert+, +ca+ and +verify_mode+.
209
256
  # * Set whether to optimize for low latency instead of throughput with
210
257
  # +low_latency+, default is to not optimize for low latency. This is done
211
258
  # via +Socket::TCP_NODELAY+.
@@ -215,6 +262,8 @@ module Puma
215
262
  # bind 'unix:///var/run/puma.sock?backlog=512'
216
263
  # @example SSL cert
217
264
  # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem'
265
+ # @example SSL cert for mutual TLS (mTLS)
266
+ # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem&ca=ca.pem&verify_mode=force_peer'
218
267
  # @example Disable optimization for low latency
219
268
  # bind 'tcp://0.0.0.0:9292?low_latency=false'
220
269
  # @example Socket permissions
@@ -266,16 +315,22 @@ module Puma
266
315
  bind URI::Generic.build(scheme: 'tcp', host: host, port: Integer(port)).to_s
267
316
  end
268
317
 
318
+ # Define how long the tcp socket stays open, if no data has been received.
319
+ # @see Puma::Server.new
320
+ def first_data_timeout(seconds)
321
+ @options[:first_data_timeout] = Integer(seconds)
322
+ end
323
+
269
324
  # Define how long persistent connections can be idle before Puma closes them.
270
325
  # @see Puma::Server.new
271
326
  def persistent_timeout(seconds)
272
327
  @options[:persistent_timeout] = Integer(seconds)
273
328
  end
274
329
 
275
- # Define how long the tcp socket stays open, if no data has been received.
330
+ # If a new request is not received within this number of seconds, begin shutting down.
276
331
  # @see Puma::Server.new
277
- def first_data_timeout(seconds)
278
- @options[:first_data_timeout] = Integer(seconds)
332
+ def idle_timeout(seconds)
333
+ @options[:idle_timeout] = Integer(seconds)
279
334
  end
280
335
 
281
336
  # Work around leaky apps that leave garbage in Thread locals
@@ -371,6 +426,11 @@ module Puma
371
426
  @options[:log_requests] = which
372
427
  end
373
428
 
429
+ # Pass in a custom logging class instance
430
+ def custom_logger(custom_logger)
431
+ @options[:custom_logger] = custom_logger
432
+ end
433
+
374
434
  # Show debugging info
375
435
  #
376
436
  def debug
@@ -452,6 +512,16 @@ module Puma
452
512
  # Puma will assume you are using the +localhost+ gem and try to load the
453
513
  # appropriate files.
454
514
  #
515
+ # When using the options hash parameter, the `reuse:` value is either
516
+ # `true`, which sets reuse 'on' with default values, or a hash, with `:size`
517
+ # and/or `:timeout` keys, each with integer values.
518
+ #
519
+ # The `cert:` options hash parameter can be the path to a certificate
520
+ # file including all intermediate certificates in PEM format.
521
+ #
522
+ # The `cert_pem:` options hash parameter can be String containing the
523
+ # cerificate and all intermediate certificates in PEM format.
524
+ #
455
525
  # @example
456
526
  # ssl_bind '127.0.0.1', '9292', {
457
527
  # cert: path_to_cert,
@@ -459,6 +529,7 @@ module Puma
459
529
  # ssl_cipher_filter: cipher_filter, # optional
460
530
  # verify_mode: verify_mode, # default 'none'
461
531
  # verification_flags: flags, # optional, not supported by JRuby
532
+ # reuse: true # optional
462
533
  # }
463
534
  #
464
535
  # @example Using self-signed certificate with the +localhost+ gem:
@@ -468,6 +539,7 @@ module Puma
468
539
  # ssl_bind '127.0.0.1', '9292', {
469
540
  # cert_pem: File.read(path_to_cert),
470
541
  # key_pem: File.read(path_to_key),
542
+ # reuse: {size: 2_000, timeout: 20} # optional
471
543
  # }
472
544
  #
473
545
  # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
@@ -531,6 +603,11 @@ module Puma
531
603
  @options[:silence_single_worker_warning] = true
532
604
  end
533
605
 
606
+ # Disable warning message when running single mode with callback hook defined.
607
+ def silence_fork_callback_warning
608
+ @options[:silence_fork_callback_warning] = true
609
+ end
610
+
534
611
  # Code to run immediately before master process
535
612
  # forks workers (once on boot). These hooks can block if necessary
536
613
  # to wait for background operations unknown to Puma to finish before
@@ -546,6 +623,8 @@ module Puma
546
623
  # puts "Starting workers..."
547
624
  # end
548
625
  def before_fork(&block)
626
+ warn_if_in_single_mode('before_fork')
627
+
549
628
  @options[:before_fork] ||= []
550
629
  @options[:before_fork] << block
551
630
  end
@@ -560,9 +639,10 @@ module Puma
560
639
  # on_worker_boot do
561
640
  # puts 'Before worker boot...'
562
641
  # end
563
- def on_worker_boot(&block)
564
- @options[:before_worker_boot] ||= []
565
- @options[:before_worker_boot] << block
642
+ def on_worker_boot(key = nil, &block)
643
+ warn_if_in_single_mode('on_worker_boot')
644
+
645
+ process_hook :before_worker_boot, key, block, 'on_worker_boot'
566
646
  end
567
647
 
568
648
  # Code to run immediately before a worker shuts
@@ -577,9 +657,10 @@ module Puma
577
657
  # on_worker_shutdown do
578
658
  # puts 'On worker shutdown...'
579
659
  # end
580
- def on_worker_shutdown(&block)
581
- @options[:before_worker_shutdown] ||= []
582
- @options[:before_worker_shutdown] << block
660
+ def on_worker_shutdown(key = nil, &block)
661
+ warn_if_in_single_mode('on_worker_shutdown')
662
+
663
+ process_hook :before_worker_shutdown, key, block, 'on_worker_shutdown'
583
664
  end
584
665
 
585
666
  # Code to run in the master right before a worker is started. The worker's
@@ -593,8 +674,9 @@ module Puma
593
674
  # puts 'Before worker fork...'
594
675
  # end
595
676
  def on_worker_fork(&block)
596
- @options[:before_worker_fork] ||= []
597
- @options[:before_worker_fork] << block
677
+ warn_if_in_single_mode('on_worker_fork')
678
+
679
+ process_hook :before_worker_fork, nil, block, 'on_worker_fork'
598
680
  end
599
681
 
600
682
  # Code to run in the master after a worker has been started. The worker's
@@ -608,12 +690,23 @@ module Puma
608
690
  # puts 'After worker fork...'
609
691
  # end
610
692
  def after_worker_fork(&block)
611
- @options[:after_worker_fork] ||= []
612
- @options[:after_worker_fork] << block
693
+ warn_if_in_single_mode('after_worker_fork')
694
+
695
+ process_hook :after_worker_fork, nil, block, 'after_worker_fork'
613
696
  end
614
697
 
615
698
  alias_method :after_worker_boot, :after_worker_fork
616
699
 
700
+ # Code to run after puma is booted (works for both: single and clustered)
701
+ #
702
+ # @example
703
+ # on_booted do
704
+ # puts 'After booting...'
705
+ # end
706
+ def on_booted(&block)
707
+ @config.options[:events].on_booted(&block)
708
+ end
709
+
617
710
  # When `fork_worker` is enabled, code to run in Worker 0
618
711
  # before all other workers are re-forked from this process,
619
712
  # after the server has temporarily stopped serving requests
@@ -632,9 +725,42 @@ module Puma
632
725
  # end
633
726
  # @version 5.0.0
634
727
  #
635
- def on_refork(&block)
636
- @options[:before_refork] ||= []
637
- @options[:before_refork] << block
728
+ def on_refork(key = nil, &block)
729
+ process_hook :before_refork, key, block, 'on_refork'
730
+ end
731
+
732
+ # Code to run immediately before a thread starts. The worker does not
733
+ # start new threads until this code finishes.
734
+ #
735
+ # This hook is useful for doing something when a thread
736
+ # starts.
737
+ #
738
+ # This can be called multiple times to add several hooks.
739
+ #
740
+ # @example
741
+ # on_thread_start do
742
+ # puts 'On thread start...'
743
+ # end
744
+ def on_thread_start(&block)
745
+ @options[:before_thread_start] ||= []
746
+ @options[:before_thread_start] << block
747
+ end
748
+
749
+ # Code to run immediately before a thread exits. The worker does not
750
+ # accept new requests until this code finishes.
751
+ #
752
+ # This hook is useful for cleaning up thread local resources when a thread
753
+ # is trimmed.
754
+ #
755
+ # This can be called multiple times to add several hooks.
756
+ #
757
+ # @example
758
+ # on_thread_exit do
759
+ # puts 'On thread exit...'
760
+ # end
761
+ def on_thread_exit(&block)
762
+ @options[:before_thread_exit] ||= []
763
+ @options[:before_thread_exit] << block
638
764
  end
639
765
 
640
766
  # Code to run out-of-band when the worker is idle.
@@ -647,8 +773,7 @@ module Puma
647
773
  #
648
774
  # This can be called multiple times to add several hooks.
649
775
  def out_of_band(&block)
650
- @options[:out_of_band] ||= []
651
- @options[:out_of_band] << block
776
+ process_hook :out_of_band, nil, block, 'out_of_band'
652
777
  end
653
778
 
654
779
  # The directory to operate out of.
@@ -769,7 +894,8 @@ module Puma
769
894
  # not a request timeout, it is to protect against a hung or dead process.
770
895
  # Setting this value will not protect against slow requests.
771
896
  #
772
- # The minimum value is 6 seconds, the default value is 60 seconds.
897
+ # This value must be greater than worker_check_interval.
898
+ # The default value is 60 seconds.
773
899
  #
774
900
  # @note Cluster mode only.
775
901
  # @example
@@ -778,7 +904,7 @@ module Puma
778
904
  #
779
905
  def worker_timeout(timeout)
780
906
  timeout = Integer(timeout)
781
- min = @options.fetch(:worker_check_interval, Puma::ConfigDefault::DefaultWorkerCheckInterval)
907
+ min = @options.fetch(:worker_check_interval, Configuration::DEFAULTS[:worker_check_interval])
782
908
 
783
909
  if timeout <= min
784
910
  raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
@@ -882,13 +1008,16 @@ module Puma
882
1008
  # There are 5 possible values:
883
1009
  #
884
1010
  # 1. **:socket** (the default) - read the peername from the socket using the
885
- # syscall. This is the normal behavior.
1011
+ # syscall. This is the normal behavior. If this fails for any reason (e.g.,
1012
+ # if the peer disconnects between the connection being accepted and the getpeername
1013
+ # system call), Puma will return "0.0.0.0"
886
1014
  # 2. **:localhost** - set the remote address to "127.0.0.1"
887
1015
  # 3. **header: <http_header>**- set the remote address to the value of the
888
1016
  # provided http header. For instance:
889
1017
  # `set_remote_address header: "X-Real-IP"`.
890
1018
  # Only the first word (as separated by spaces or comma) is used, allowing
891
- # headers such as X-Forwarded-For to be used as well.
1019
+ # headers such as X-Forwarded-For to be used as well. If this header is absent,
1020
+ # Puma will fall back to the behavior of :socket
892
1021
  # 4. **proxy_protocol: :v1**- set the remote address to the value read from the
893
1022
  # HAproxy PROXY protocol, version 1. If the request does not have the PROXY
894
1023
  # protocol attached to it, will fall back to :socket
@@ -942,23 +1071,6 @@ module Puma
942
1071
  @options[:fork_worker] = Integer(after_requests)
943
1072
  end
944
1073
 
945
- # When enabled, Puma will GC 4 times before forking workers.
946
- # If available (Ruby 2.7+), we will also call GC.compact.
947
- # Not recommended for non-MRI Rubies.
948
- #
949
- # Based on the work of Koichi Sasada and Aaron Patterson, this option may
950
- # decrease memory utilization of preload-enabled cluster-mode Pumas. It will
951
- # also increase time to boot and fork. See your logs for details on how much
952
- # time this adds to your boot process. For most apps, it will be less than one
953
- # second.
954
- #
955
- # @see Puma::Cluster#nakayoshi_gc
956
- # @version 5.0.0
957
- #
958
- def nakayoshi_fork(enabled=true)
959
- @options[:nakayoshi_fork] = enabled
960
- end
961
-
962
1074
  # The number of requests to attempt inline before sending a client back to
963
1075
  # the reactor to be subject to normal ordering.
964
1076
  #
@@ -989,6 +1101,51 @@ module Puma
989
1101
  @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
990
1102
  end
991
1103
 
1104
+ # Specify how big the request payload should be, in bytes.
1105
+ # This limit is compared against Content-Length HTTP header.
1106
+ # If the payload size (CONTENT_LENGTH) is larger than http_content_length_limit,
1107
+ # HTTP 413 status code is returned.
1108
+ #
1109
+ # When no Content-Length http header is present, it is compared against the
1110
+ # size of the body of the request.
1111
+ #
1112
+ # The default value for http_content_length_limit is nil.
1113
+ def http_content_length_limit(limit)
1114
+ @options[:http_content_length_limit] = limit
1115
+ end
1116
+
1117
+ # Supported http methods, which will replace `Puma::Const::SUPPORTED_HTTP_METHODS`.
1118
+ # The value of `:any` will allows all methods, otherwise, the value must be
1119
+ # an array of strings. Note that methods are all uppercase.
1120
+ #
1121
+ # `Puma::Const::SUPPORTED_HTTP_METHODS` is conservative, if you want a
1122
+ # complete set of methods, the methods defined by the
1123
+ # [IANA Method Registry](https://www.iana.org/assignments/http-methods/http-methods.xhtml)
1124
+ # are pre-defined as the constant `Puma::Const::IANA_HTTP_METHODS`.
1125
+ #
1126
+ # @note If the `methods` value is `:any`, no method check with be performed,
1127
+ # similar to Puma v5 and earlier.
1128
+ #
1129
+ # @example Adds 'PROPFIND' to existing supported methods
1130
+ # supported_http_methods(Puma::Const::SUPPORTED_HTTP_METHODS + ['PROPFIND'])
1131
+ # @example Restricts methods to the array elements
1132
+ # supported_http_methods %w[HEAD GET POST PUT DELETE OPTIONS PROPFIND]
1133
+ # @example Restricts methods to the methods in the IANA Registry
1134
+ # supported_http_methods Puma::Const::IANA_HTTP_METHODS
1135
+ # @example Allows any method
1136
+ # supported_http_methods :any
1137
+ #
1138
+ def supported_http_methods(methods)
1139
+ if methods == :any
1140
+ @options[:supported_http_methods] = :any
1141
+ elsif Array === methods && methods == (ary = methods.grep(String).uniq) &&
1142
+ !ary.empty?
1143
+ @options[:supported_http_methods] = ary
1144
+ else
1145
+ raise "supported_http_methods must be ':any' or a unique array of strings"
1146
+ end
1147
+ end
1148
+
992
1149
  private
993
1150
 
994
1151
  # To avoid adding cert_pem and key_pem as URI params, we store them on the
@@ -1008,5 +1165,29 @@ module Puma
1008
1165
  end
1009
1166
  end
1010
1167
  end
1168
+
1169
+ def process_hook(options_key, key, block, meth)
1170
+ @options[options_key] ||= []
1171
+ if ON_WORKER_KEY.include? key.class
1172
+ @options[options_key] << [block, key.to_sym]
1173
+ elsif key.nil?
1174
+ @options[options_key] << block
1175
+ else
1176
+ raise "'#{meth}' key must be String or Symbol"
1177
+ end
1178
+ end
1179
+
1180
+ def warn_if_in_single_mode(hook_name)
1181
+ return if @options[:silence_fork_callback_warning]
1182
+
1183
+ if (@options[:workers] || 0) == 0
1184
+ log_string =
1185
+ "Warning: You specified code to run in a `#{hook_name}` block, " \
1186
+ "but Puma is not configured to run in cluster mode (worker count > 0 ), " \
1187
+ "so your `#{hook_name}` block did not run"
1188
+
1189
+ LogWriter.stdio.log(log_string)
1190
+ end
1191
+ end
1011
1192
  end
1012
1193
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/const'
3
+ require_relative 'const'
4
4
 
5
5
  module Puma
6
6
  # The implementation of a detailed error logging.
@@ -13,6 +13,8 @@ module Puma
13
13
 
14
14
  REQUEST_FORMAT = %{"%s %s%s" - (%s)}
15
15
 
16
+ LOG_QUEUE = Queue.new
17
+
16
18
  def initialize(ioerr)
17
19
  @ioerr = ioerr
18
20
 
@@ -31,7 +33,7 @@ module Puma
31
33
  # and before all remaining info.
32
34
  #
33
35
  def info(options={})
34
- log title(options)
36
+ internal_write title(options)
35
37
  end
36
38
 
37
39
  # Print occurred error details only if
@@ -53,7 +55,7 @@ module Puma
53
55
  string_block << request_dump(req) if request_parsed?(req)
54
56
  string_block << error.backtrace if error
55
57
 
56
- log string_block.join("\n")
58
+ internal_write string_block.join("\n")
57
59
  end
58
60
 
59
61
  def title(options={})
@@ -93,12 +95,19 @@ module Puma
93
95
  req && req.env[REQUEST_METHOD]
94
96
  end
95
97
 
96
- private
97
-
98
- def log(str)
99
- ioerr.puts str
100
-
101
- ioerr.flush unless ioerr.sync
98
+ def internal_write(str)
99
+ LOG_QUEUE << str
100
+ while (w_str = LOG_QUEUE.pop(true)) do
101
+ begin
102
+ @ioerr.is_a?(IO) and @ioerr.wait_writable(1)
103
+ @ioerr.write "#{w_str}\n"
104
+ @ioerr.flush unless @ioerr.sync
105
+ rescue Errno::EPIPE, Errno::EBADF, IOError, Errno::EINVAL
106
+ # 'Invalid argument' (Errno::EINVAL) may be raised by flush
107
+ end
108
+ end
109
+ rescue ThreadError
102
110
  end
111
+ private :internal_write
103
112
  end
104
113
  end