puma 5.6.4 → 6.4.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.

Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +372 -6
  3. data/LICENSE +0 -0
  4. data/README.md +79 -29
  5. data/bin/puma-wild +1 -1
  6. data/docs/architecture.md +0 -0
  7. data/docs/compile_options.md +34 -0
  8. data/docs/deployment.md +0 -0
  9. data/docs/fork_worker.md +1 -3
  10. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  11. data/docs/images/puma-connection-flow.png +0 -0
  12. data/docs/images/puma-general-arch.png +0 -0
  13. data/docs/jungle/README.md +0 -0
  14. data/docs/jungle/rc.d/README.md +0 -0
  15. data/docs/jungle/rc.d/puma.conf +0 -0
  16. data/docs/kubernetes.md +12 -0
  17. data/docs/nginx.md +1 -1
  18. data/docs/plugins.md +0 -0
  19. data/docs/rails_dev_mode.md +0 -0
  20. data/docs/restart.md +1 -0
  21. data/docs/signals.md +0 -0
  22. data/docs/stats.md +0 -0
  23. data/docs/systemd.md +3 -6
  24. data/docs/testing_benchmarks_local_files.md +150 -0
  25. data/docs/testing_test_rackup_ci_files.md +36 -0
  26. data/ext/puma_http11/PumaHttp11Service.java +0 -0
  27. data/ext/puma_http11/ext_help.h +0 -0
  28. data/ext/puma_http11/extconf.rb +22 -10
  29. data/ext/puma_http11/http11_parser.c +1 -1
  30. data/ext/puma_http11/http11_parser.h +1 -1
  31. data/ext/puma_http11/http11_parser.java.rl +2 -2
  32. data/ext/puma_http11/http11_parser.rl +2 -2
  33. data/ext/puma_http11/http11_parser_common.rl +2 -2
  34. data/ext/puma_http11/mini_ssl.c +153 -27
  35. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
  36. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  37. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
  38. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +167 -65
  39. data/ext/puma_http11/puma_http11.c +17 -9
  40. data/lib/puma/app/status.rb +7 -4
  41. data/lib/puma/binder.rb +51 -54
  42. data/lib/puma/cli.rb +16 -18
  43. data/lib/puma/client.rb +100 -26
  44. data/lib/puma/cluster/worker.rb +18 -11
  45. data/lib/puma/cluster/worker_handle.rb +4 -1
  46. data/lib/puma/cluster.rb +102 -40
  47. data/lib/puma/commonlogger.rb +21 -14
  48. data/lib/puma/configuration.rb +77 -59
  49. data/lib/puma/const.rb +129 -92
  50. data/lib/puma/control_cli.rb +33 -23
  51. data/lib/puma/detect.rb +7 -4
  52. data/lib/puma/dsl.rb +251 -53
  53. data/lib/puma/error_logger.rb +18 -9
  54. data/lib/puma/events.rb +6 -126
  55. data/lib/puma/io_buffer.rb +39 -4
  56. data/lib/puma/jruby_restart.rb +2 -1
  57. data/lib/puma/json_serialization.rb +0 -0
  58. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  59. data/lib/puma/launcher.rb +113 -175
  60. data/lib/puma/log_writer.rb +147 -0
  61. data/lib/puma/minissl/context_builder.rb +26 -12
  62. data/lib/puma/minissl.rb +113 -15
  63. data/lib/puma/null_io.rb +21 -2
  64. data/lib/puma/plugin/systemd.rb +90 -0
  65. data/lib/puma/plugin/tmp_restart.rb +1 -1
  66. data/lib/puma/plugin.rb +0 -0
  67. data/lib/puma/rack/builder.rb +6 -6
  68. data/lib/puma/rack/urlmap.rb +1 -1
  69. data/lib/puma/rack_default.rb +19 -4
  70. data/lib/puma/reactor.rb +19 -10
  71. data/lib/puma/request.rb +365 -166
  72. data/lib/puma/runner.rb +56 -20
  73. data/lib/puma/sd_notify.rb +149 -0
  74. data/lib/puma/server.rb +137 -87
  75. data/lib/puma/single.rb +13 -11
  76. data/lib/puma/state_file.rb +4 -6
  77. data/lib/puma/thread_pool.rb +57 -19
  78. data/lib/puma/util.rb +12 -14
  79. data/lib/puma.rb +12 -11
  80. data/lib/rack/handler/puma.rb +113 -86
  81. data/tools/Dockerfile +2 -2
  82. data/tools/trickletest.rb +0 -0
  83. metadata +11 -6
  84. data/lib/puma/queue_close.rb +0 -26
  85. data/lib/puma/systemd.rb +0 -46
data/lib/puma/dsl.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'puma/const'
3
+ require_relative 'const'
4
+ require_relative 'util'
4
5
 
5
6
  module Puma
6
7
  # The methods that are available for use inside the configuration file.
@@ -31,8 +32,24 @@ module Puma
31
32
  # You can also find many examples being used by the test suite in
32
33
  # +test/config+.
33
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
+ #
34
51
  class DSL
35
- include ConfigDefault
52
+ ON_WORKER_KEY = [String, Symbol].freeze
36
53
 
37
54
  # convenience method so logic can be used in CI
38
55
  # @see ssl_bind
@@ -46,27 +63,60 @@ module Puma
46
63
  else ''
47
64
  end
48
65
 
49
- ca_additions = "&ca=#{opts[:ca]}" if ['peer', 'force_peer'].include?(verify)
66
+ ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify)
50
67
 
68
+ low_latency_str = opts.key?(:low_latency) ? "&low_latency=#{opts[:low_latency]}" : ''
51
69
  backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
52
70
 
53
71
  if defined?(JRUBY_VERSION)
54
- ssl_cipher_list = opts[:ssl_cipher_list] ?
55
- "&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
56
75
 
57
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
58
83
 
59
- "ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
84
+ "ssl://#{host}:#{port}?#{keystore_additions}#{truststore_additions}#{cipher_suites}#{protocols}" \
60
85
  "&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
61
86
  else
62
- ssl_cipher_filter = opts[:ssl_cipher_filter] ?
63
- "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
64
-
65
- v_flags = (ary = opts[:verification_flags]) ?
66
- "&verification_flags=#{Array(ary).join ','}" : 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
67
117
 
68
- "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \
69
- "#{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}"
70
120
  end
71
121
  end
72
122
 
@@ -102,7 +152,7 @@ module Puma
102
152
  end
103
153
 
104
154
  def default_host
105
- @options[:default_host] || Configuration::DefaultTCPHost
155
+ @options[:default_host] || Configuration::DEFAULTS[:tcp_host]
106
156
  end
107
157
 
108
158
  def inject(&blk)
@@ -202,6 +252,7 @@ module Puma
202
252
  #
203
253
  # * Set the socket backlog depth with +backlog+, default is 1024.
204
254
  # * Set up an SSL certificate with +key+ & +cert+.
255
+ # * Set up an SSL certificate for mTLS with +key+, +cert+, +ca+ and +verify_mode+.
205
256
  # * Set whether to optimize for low latency instead of throughput with
206
257
  # +low_latency+, default is to not optimize for low latency. This is done
207
258
  # via +Socket::TCP_NODELAY+.
@@ -211,6 +262,8 @@ module Puma
211
262
  # bind 'unix:///var/run/puma.sock?backlog=512'
212
263
  # @example SSL cert
213
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'
214
267
  # @example Disable optimization for low latency
215
268
  # bind 'tcp://0.0.0.0:9292?low_latency=false'
216
269
  # @example Socket permissions
@@ -262,16 +315,22 @@ module Puma
262
315
  bind URI::Generic.build(scheme: 'tcp', host: host, port: Integer(port)).to_s
263
316
  end
264
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
+
265
324
  # Define how long persistent connections can be idle before Puma closes them.
266
325
  # @see Puma::Server.new
267
326
  def persistent_timeout(seconds)
268
327
  @options[:persistent_timeout] = Integer(seconds)
269
328
  end
270
329
 
271
- # 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.
272
331
  # @see Puma::Server.new
273
- def first_data_timeout(seconds)
274
- @options[:first_data_timeout] = Integer(seconds)
332
+ def idle_timeout(seconds)
333
+ @options[:idle_timeout] = Integer(seconds)
275
334
  end
276
335
 
277
336
  # Work around leaky apps that leave garbage in Thread locals
@@ -367,6 +426,11 @@ module Puma
367
426
  @options[:log_requests] = which
368
427
  end
369
428
 
429
+ # Pass in a custom logging class instance
430
+ def custom_logger(custom_logger)
431
+ @options[:custom_logger] = custom_logger
432
+ end
433
+
370
434
  # Show debugging info
371
435
  #
372
436
  def debug
@@ -448,6 +512,16 @@ module Puma
448
512
  # Puma will assume you are using the +localhost+ gem and try to load the
449
513
  # appropriate files.
450
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
+ #
451
525
  # @example
452
526
  # ssl_bind '127.0.0.1', '9292', {
453
527
  # cert: path_to_cert,
@@ -455,6 +529,7 @@ module Puma
455
529
  # ssl_cipher_filter: cipher_filter, # optional
456
530
  # verify_mode: verify_mode, # default 'none'
457
531
  # verification_flags: flags, # optional, not supported by JRuby
532
+ # reuse: true # optional
458
533
  # }
459
534
  #
460
535
  # @example Using self-signed certificate with the +localhost+ gem:
@@ -464,6 +539,7 @@ module Puma
464
539
  # ssl_bind '127.0.0.1', '9292', {
465
540
  # cert_pem: File.read(path_to_cert),
466
541
  # key_pem: File.read(path_to_key),
542
+ # reuse: {size: 2_000, timeout: 20} # optional
467
543
  # }
468
544
  #
469
545
  # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
@@ -527,6 +603,11 @@ module Puma
527
603
  @options[:silence_single_worker_warning] = true
528
604
  end
529
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
+
530
611
  # Code to run immediately before master process
531
612
  # forks workers (once on boot). These hooks can block if necessary
532
613
  # to wait for background operations unknown to Puma to finish before
@@ -542,6 +623,8 @@ module Puma
542
623
  # puts "Starting workers..."
543
624
  # end
544
625
  def before_fork(&block)
626
+ warn_if_in_single_mode('before_fork')
627
+
545
628
  @options[:before_fork] ||= []
546
629
  @options[:before_fork] << block
547
630
  end
@@ -556,9 +639,10 @@ module Puma
556
639
  # on_worker_boot do
557
640
  # puts 'Before worker boot...'
558
641
  # end
559
- def on_worker_boot(&block)
560
- @options[:before_worker_boot] ||= []
561
- @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'
562
646
  end
563
647
 
564
648
  # Code to run immediately before a worker shuts
@@ -573,9 +657,10 @@ module Puma
573
657
  # on_worker_shutdown do
574
658
  # puts 'On worker shutdown...'
575
659
  # end
576
- def on_worker_shutdown(&block)
577
- @options[:before_worker_shutdown] ||= []
578
- @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'
579
664
  end
580
665
 
581
666
  # Code to run in the master right before a worker is started. The worker's
@@ -589,8 +674,9 @@ module Puma
589
674
  # puts 'Before worker fork...'
590
675
  # end
591
676
  def on_worker_fork(&block)
592
- @options[:before_worker_fork] ||= []
593
- @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'
594
680
  end
595
681
 
596
682
  # Code to run in the master after a worker has been started. The worker's
@@ -604,12 +690,23 @@ module Puma
604
690
  # puts 'After worker fork...'
605
691
  # end
606
692
  def after_worker_fork(&block)
607
- @options[:after_worker_fork] ||= []
608
- @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'
609
696
  end
610
697
 
611
698
  alias_method :after_worker_boot, :after_worker_fork
612
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
+
613
710
  # When `fork_worker` is enabled, code to run in Worker 0
614
711
  # before all other workers are re-forked from this process,
615
712
  # after the server has temporarily stopped serving requests
@@ -628,9 +725,53 @@ module Puma
628
725
  # end
629
726
  # @version 5.0.0
630
727
  #
631
- def on_refork(&block)
632
- @options[:before_refork] ||= []
633
- @options[:before_refork] << block
728
+ def on_refork(key = nil, &block)
729
+ process_hook :before_refork, key, block, 'on_refork'
730
+ end
731
+
732
+ # Provide a block to be executed just before a thread is added to the thread
733
+ # pool. Be careful: while the block executes, thread creation is delayed, and
734
+ # probably a request will have to wait too! The new thread will not be added to
735
+ # the threadpool until the provided block returns.
736
+ #
737
+ # Return values are ignored.
738
+ # Raising an exception will log a warning.
739
+ #
740
+ # This hook is useful for doing something when the thread pool grows.
741
+ #
742
+ # This can be called multiple times to add several hooks.
743
+ #
744
+ # @example
745
+ # on_thread_start do
746
+ # puts 'On thread start...'
747
+ # end
748
+ def on_thread_start(&block)
749
+ @options[:before_thread_start] ||= []
750
+ @options[:before_thread_start] << block
751
+ end
752
+
753
+ # Provide a block to be executed after a thread is trimmed from the thread
754
+ # pool. Be careful: while this block executes, Puma's main loop is
755
+ # blocked, so no new requests will be picked up.
756
+ #
757
+ # This hook only runs when a thread in the threadpool is trimmed by Puma.
758
+ # It does not run when a thread dies due to exceptions or any other cause.
759
+ #
760
+ # Return values are ignored.
761
+ # Raising an exception will log a warning.
762
+ #
763
+ # This hook is useful for cleaning up thread local resources when a thread
764
+ # is trimmed.
765
+ #
766
+ # This can be called multiple times to add several hooks.
767
+ #
768
+ # @example
769
+ # on_thread_exit do
770
+ # puts 'On thread exit...'
771
+ # end
772
+ def on_thread_exit(&block)
773
+ @options[:before_thread_exit] ||= []
774
+ @options[:before_thread_exit] << block
634
775
  end
635
776
 
636
777
  # Code to run out-of-band when the worker is idle.
@@ -643,8 +784,7 @@ module Puma
643
784
  #
644
785
  # This can be called multiple times to add several hooks.
645
786
  def out_of_band(&block)
646
- @options[:out_of_band] ||= []
647
- @options[:out_of_band] << block
787
+ process_hook :out_of_band, nil, block, 'out_of_band'
648
788
  end
649
789
 
650
790
  # The directory to operate out of.
@@ -765,7 +905,8 @@ module Puma
765
905
  # not a request timeout, it is to protect against a hung or dead process.
766
906
  # Setting this value will not protect against slow requests.
767
907
  #
768
- # The minimum value is 6 seconds, the default value is 60 seconds.
908
+ # This value must be greater than worker_check_interval.
909
+ # The default value is 60 seconds.
769
910
  #
770
911
  # @note Cluster mode only.
771
912
  # @example
@@ -774,7 +915,7 @@ module Puma
774
915
  #
775
916
  def worker_timeout(timeout)
776
917
  timeout = Integer(timeout)
777
- min = @options.fetch(:worker_check_interval, Puma::ConfigDefault::DefaultWorkerCheckInterval)
918
+ min = @options.fetch(:worker_check_interval, Configuration::DEFAULTS[:worker_check_interval])
778
919
 
779
920
  if timeout <= min
780
921
  raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
@@ -878,13 +1019,16 @@ module Puma
878
1019
  # There are 5 possible values:
879
1020
  #
880
1021
  # 1. **:socket** (the default) - read the peername from the socket using the
881
- # syscall. This is the normal behavior.
1022
+ # syscall. This is the normal behavior. If this fails for any reason (e.g.,
1023
+ # if the peer disconnects between the connection being accepted and the getpeername
1024
+ # system call), Puma will return "0.0.0.0"
882
1025
  # 2. **:localhost** - set the remote address to "127.0.0.1"
883
1026
  # 3. **header: <http_header>**- set the remote address to the value of the
884
1027
  # provided http header. For instance:
885
1028
  # `set_remote_address header: "X-Real-IP"`.
886
1029
  # Only the first word (as separated by spaces or comma) is used, allowing
887
- # headers such as X-Forwarded-For to be used as well.
1030
+ # headers such as X-Forwarded-For to be used as well. If this header is absent,
1031
+ # Puma will fall back to the behavior of :socket
888
1032
  # 4. **proxy_protocol: :v1**- set the remote address to the value read from the
889
1033
  # HAproxy PROXY protocol, version 1. If the request does not have the PROXY
890
1034
  # protocol attached to it, will fall back to :socket
@@ -938,23 +1082,6 @@ module Puma
938
1082
  @options[:fork_worker] = Integer(after_requests)
939
1083
  end
940
1084
 
941
- # When enabled, Puma will GC 4 times before forking workers.
942
- # If available (Ruby 2.7+), we will also call GC.compact.
943
- # Not recommended for non-MRI Rubies.
944
- #
945
- # Based on the work of Koichi Sasada and Aaron Patterson, this option may
946
- # decrease memory utilization of preload-enabled cluster-mode Pumas. It will
947
- # also increase time to boot and fork. See your logs for details on how much
948
- # time this adds to your boot process. For most apps, it will be less than one
949
- # second.
950
- #
951
- # @see Puma::Cluster#nakayoshi_gc
952
- # @version 5.0.0
953
- #
954
- def nakayoshi_fork(enabled=true)
955
- @options[:nakayoshi_fork] = enabled
956
- end
957
-
958
1085
  # The number of requests to attempt inline before sending a client back to
959
1086
  # the reactor to be subject to normal ordering.
960
1087
  #
@@ -985,6 +1112,51 @@ module Puma
985
1112
  @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
986
1113
  end
987
1114
 
1115
+ # Specify how big the request payload should be, in bytes.
1116
+ # This limit is compared against Content-Length HTTP header.
1117
+ # If the payload size (CONTENT_LENGTH) is larger than http_content_length_limit,
1118
+ # HTTP 413 status code is returned.
1119
+ #
1120
+ # When no Content-Length http header is present, it is compared against the
1121
+ # size of the body of the request.
1122
+ #
1123
+ # The default value for http_content_length_limit is nil.
1124
+ def http_content_length_limit(limit)
1125
+ @options[:http_content_length_limit] = limit
1126
+ end
1127
+
1128
+ # Supported http methods, which will replace `Puma::Const::SUPPORTED_HTTP_METHODS`.
1129
+ # The value of `:any` will allows all methods, otherwise, the value must be
1130
+ # an array of strings. Note that methods are all uppercase.
1131
+ #
1132
+ # `Puma::Const::SUPPORTED_HTTP_METHODS` is conservative, if you want a
1133
+ # complete set of methods, the methods defined by the
1134
+ # [IANA Method Registry](https://www.iana.org/assignments/http-methods/http-methods.xhtml)
1135
+ # are pre-defined as the constant `Puma::Const::IANA_HTTP_METHODS`.
1136
+ #
1137
+ # @note If the `methods` value is `:any`, no method check with be performed,
1138
+ # similar to Puma v5 and earlier.
1139
+ #
1140
+ # @example Adds 'PROPFIND' to existing supported methods
1141
+ # supported_http_methods(Puma::Const::SUPPORTED_HTTP_METHODS + ['PROPFIND'])
1142
+ # @example Restricts methods to the array elements
1143
+ # supported_http_methods %w[HEAD GET POST PUT DELETE OPTIONS PROPFIND]
1144
+ # @example Restricts methods to the methods in the IANA Registry
1145
+ # supported_http_methods Puma::Const::IANA_HTTP_METHODS
1146
+ # @example Allows any method
1147
+ # supported_http_methods :any
1148
+ #
1149
+ def supported_http_methods(methods)
1150
+ if methods == :any
1151
+ @options[:supported_http_methods] = :any
1152
+ elsif Array === methods && methods == (ary = methods.grep(String).uniq) &&
1153
+ !ary.empty?
1154
+ @options[:supported_http_methods] = ary
1155
+ else
1156
+ raise "supported_http_methods must be ':any' or a unique array of strings"
1157
+ end
1158
+ end
1159
+
988
1160
  private
989
1161
 
990
1162
  # To avoid adding cert_pem and key_pem as URI params, we store them on the
@@ -1004,5 +1176,31 @@ module Puma
1004
1176
  end
1005
1177
  end
1006
1178
  end
1179
+
1180
+ def process_hook(options_key, key, block, meth)
1181
+ @options[options_key] ||= []
1182
+ if ON_WORKER_KEY.include? key.class
1183
+ @options[options_key] << [block, key.to_sym]
1184
+ elsif key.nil?
1185
+ @options[options_key] << block
1186
+ else
1187
+ raise "'#{meth}' key must be String or Symbol"
1188
+ end
1189
+ end
1190
+
1191
+ def warn_if_in_single_mode(hook_name)
1192
+ return if @options[:silence_fork_callback_warning]
1193
+ # user_options (CLI) have precedence over config file
1194
+ workers_val = @config.options.user_options[:workers] || @options[:workers] ||
1195
+ @config.puma_default_options[:workers] || 0
1196
+ if workers_val == 0
1197
+ log_string =
1198
+ "Warning: You specified code to run in a `#{hook_name}` block, " \
1199
+ "but Puma is not configured to run in cluster mode (worker count > 0 ), " \
1200
+ "so your `#{hook_name}` block did not run"
1201
+
1202
+ LogWriter.stdio.log(log_string)
1203
+ end
1204
+ end
1007
1205
  end
1008
1206
  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