puma 5.6.4 → 6.4.2

Sign up to get free protection for your applications and to get access to all the features.
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