puma 5.2.2 → 6.3.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 (74) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +483 -4
  3. data/README.md +101 -20
  4. data/bin/puma-wild +1 -1
  5. data/docs/architecture.md +50 -16
  6. data/docs/compile_options.md +38 -2
  7. data/docs/deployment.md +53 -67
  8. data/docs/fork_worker.md +1 -3
  9. data/docs/jungle/rc.d/README.md +1 -1
  10. data/docs/kubernetes.md +1 -1
  11. data/docs/nginx.md +1 -1
  12. data/docs/plugins.md +15 -15
  13. data/docs/rails_dev_mode.md +2 -3
  14. data/docs/restart.md +7 -7
  15. data/docs/signals.md +11 -10
  16. data/docs/stats.md +8 -8
  17. data/docs/systemd.md +65 -69
  18. data/docs/testing_benchmarks_local_files.md +150 -0
  19. data/docs/testing_test_rackup_ci_files.md +36 -0
  20. data/ext/puma_http11/extconf.rb +44 -13
  21. data/ext/puma_http11/http11_parser.c +24 -11
  22. data/ext/puma_http11/http11_parser.h +2 -2
  23. data/ext/puma_http11/http11_parser.java.rl +2 -2
  24. data/ext/puma_http11/http11_parser.rl +2 -2
  25. data/ext/puma_http11/http11_parser_common.rl +3 -3
  26. data/ext/puma_http11/mini_ssl.c +150 -23
  27. data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
  28. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +50 -48
  29. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +188 -102
  30. data/ext/puma_http11/puma_http11.c +18 -10
  31. data/lib/puma/app/status.rb +10 -7
  32. data/lib/puma/binder.rb +112 -62
  33. data/lib/puma/cli.rb +24 -20
  34. data/lib/puma/client.rb +162 -36
  35. data/lib/puma/cluster/worker.rb +31 -27
  36. data/lib/puma/cluster/worker_handle.rb +12 -1
  37. data/lib/puma/cluster.rb +102 -61
  38. data/lib/puma/commonlogger.rb +21 -14
  39. data/lib/puma/configuration.rb +78 -54
  40. data/lib/puma/const.rb +135 -97
  41. data/lib/puma/control_cli.rb +25 -20
  42. data/lib/puma/detect.rb +12 -2
  43. data/lib/puma/dsl.rb +308 -58
  44. data/lib/puma/error_logger.rb +20 -11
  45. data/lib/puma/events.rb +6 -126
  46. data/lib/puma/io_buffer.rb +39 -4
  47. data/lib/puma/jruby_restart.rb +2 -1
  48. data/lib/puma/{json.rb → json_serialization.rb} +1 -1
  49. data/lib/puma/launcher/bundle_pruner.rb +104 -0
  50. data/lib/puma/launcher.rb +114 -173
  51. data/lib/puma/log_writer.rb +147 -0
  52. data/lib/puma/minissl/context_builder.rb +30 -16
  53. data/lib/puma/minissl.rb +132 -38
  54. data/lib/puma/null_io.rb +5 -0
  55. data/lib/puma/plugin/systemd.rb +90 -0
  56. data/lib/puma/plugin/tmp_restart.rb +1 -1
  57. data/lib/puma/plugin.rb +2 -2
  58. data/lib/puma/rack/builder.rb +7 -7
  59. data/lib/puma/rack_default.rb +19 -4
  60. data/lib/puma/reactor.rb +19 -10
  61. data/lib/puma/request.rb +373 -153
  62. data/lib/puma/runner.rb +74 -28
  63. data/lib/puma/sd_notify.rb +149 -0
  64. data/lib/puma/server.rb +127 -136
  65. data/lib/puma/single.rb +13 -11
  66. data/lib/puma/state_file.rb +39 -7
  67. data/lib/puma/thread_pool.rb +33 -26
  68. data/lib/puma/util.rb +20 -15
  69. data/lib/puma.rb +28 -11
  70. data/lib/rack/handler/puma.rb +113 -86
  71. data/tools/Dockerfile +1 -1
  72. metadata +15 -10
  73. data/lib/puma/queue_close.rb +0 -26
  74. 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,25 +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)
67
+
68
+ low_latency_str = opts.key?(:low_latency) ? "&low_latency=#{opts[:low_latency]}" : ''
69
+ backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
50
70
 
51
71
  if defined?(JRUBY_VERSION)
52
- ssl_cipher_list = opts[:ssl_cipher_list] ?
53
- "&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
54
75
 
55
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
56
83
 
57
- "ssl://#{host}:#{port}?#{keystore_additions}#{ssl_cipher_list}" \
58
- "&verify_mode=#{verify}#{tls_str}#{ca_additions}"
84
+ "ssl://#{host}:#{port}?#{keystore_additions}#{truststore_additions}#{cipher_suites}#{protocols}" \
85
+ "&verify_mode=#{verify}#{tls_str}#{ca_additions}#{backlog_str}"
59
86
  else
60
- ssl_cipher_filter = opts[:ssl_cipher_filter] ?
61
- "&ssl_cipher_filter=#{opts[:ssl_cipher_filter]}" : nil
62
-
63
- v_flags = (ary = opts[:verification_flags]) ?
64
- "&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
65
117
 
66
- "ssl://#{host}:#{port}?cert=#{opts[:cert]}&key=#{opts[:key]}" \
67
- "#{ssl_cipher_filter}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}"
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}"
68
120
  end
69
121
  end
70
122
 
@@ -100,7 +152,7 @@ module Puma
100
152
  end
101
153
 
102
154
  def default_host
103
- @options[:default_host] || Configuration::DefaultTCPHost
155
+ @options[:default_host] || Configuration::DEFAULTS[:tcp_host]
104
156
  end
105
157
 
106
158
  def inject(&blk)
@@ -191,7 +243,7 @@ module Puma
191
243
  end
192
244
 
193
245
  # Bind the server to +url+. "tcp://", "unix://" and "ssl://" are the only
194
- # accepted protocols. Multiple urls can be bound to, calling `bind` does
246
+ # accepted protocols. Multiple urls can be bound to, calling +bind+ does
195
247
  # not overwrite previous bindings.
196
248
  #
197
249
  # The default is "tcp://0.0.0.0:9292".
@@ -200,8 +252,9 @@ module Puma
200
252
  #
201
253
  # * Set the socket backlog depth with +backlog+, default is 1024.
202
254
  # * Set up an SSL certificate with +key+ & +cert+.
255
+ # * Set up an SSL certificate for mTLS with +key+, +cert+, +ca+ and +verify_mode+.
203
256
  # * Set whether to optimize for low latency instead of throughput with
204
- # +low_latency+, default is to optimize for low latency. This is done
257
+ # +low_latency+, default is to not optimize for low latency. This is done
205
258
  # via +Socket::TCP_NODELAY+.
206
259
  # * Set socket permissions with +umask+.
207
260
  #
@@ -209,6 +262,8 @@ module Puma
209
262
  # bind 'unix:///var/run/puma.sock?backlog=512'
210
263
  # @example SSL cert
211
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'
212
267
  # @example Disable optimization for low latency
213
268
  # bind 'tcp://0.0.0.0:9292?low_latency=false'
214
269
  # @example Socket permissions
@@ -365,6 +420,11 @@ module Puma
365
420
  @options[:log_requests] = which
366
421
  end
367
422
 
423
+ # Pass in a custom logging class instance
424
+ def custom_logger(custom_logger)
425
+ @options[:custom_logger] = custom_logger
426
+ end
427
+
368
428
  # Show debugging info
369
429
  #
370
430
  def debug
@@ -381,6 +441,13 @@ module Puma
381
441
  @options[:rackup] ||= path.to_s
382
442
  end
383
443
 
444
+ # Allows setting `env['rack.url_scheme']`.
445
+ # Only necessary if X-Forwarded-Proto is not being set by your proxy
446
+ # Normal values are 'http' or 'https'.
447
+ def rack_url_scheme(scheme=nil)
448
+ @options[:rack_url_scheme] = scheme
449
+ end
450
+
384
451
  def early_hints(answer=true)
385
452
  @options[:early_hints] = answer
386
453
  end
@@ -429,8 +496,19 @@ module Puma
429
496
  @options[:max_threads] = max
430
497
  end
431
498
 
432
- # Instead of `bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'` you
433
- # can also use the this method.
499
+ # Instead of using +bind+ and manually constructing a URI like:
500
+ #
501
+ # bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'
502
+ #
503
+ # you can use the this method.
504
+ #
505
+ # When binding on localhost you don't need to specify +cert+ and +key+,
506
+ # Puma will assume you are using the +localhost+ gem and try to load the
507
+ # appropriate files.
508
+ #
509
+ # When using the options hash parameter, the `reuse:` value is either
510
+ # `true`, which sets reuse 'on' with default values, or a hash, with `:size`
511
+ # and/or `:timeout` keys, each with integer values.
434
512
  #
435
513
  # @example
436
514
  # ssl_bind '127.0.0.1', '9292', {
@@ -439,15 +517,28 @@ module Puma
439
517
  # ssl_cipher_filter: cipher_filter, # optional
440
518
  # verify_mode: verify_mode, # default 'none'
441
519
  # verification_flags: flags, # optional, not supported by JRuby
520
+ # reuse: true # optional
442
521
  # }
443
- # @example For JRuby, two keys are required: keystore & keystore_pass.
522
+ #
523
+ # @example Using self-signed certificate with the +localhost+ gem:
524
+ # ssl_bind '127.0.0.1', '9292'
525
+ #
526
+ # @example Alternatively, you can provide +cert_pem+ and +key_pem+:
527
+ # ssl_bind '127.0.0.1', '9292', {
528
+ # cert_pem: File.read(path_to_cert),
529
+ # key_pem: File.read(path_to_key),
530
+ # reuse: {size: 2_000, timeout: 20} # optional
531
+ # }
532
+ #
533
+ # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
444
534
  # ssl_bind '127.0.0.1', '9292', {
445
535
  # keystore: path_to_keystore,
446
536
  # keystore_pass: password,
447
537
  # ssl_cipher_list: cipher_list, # optional
448
538
  # verify_mode: verify_mode # default 'none'
449
539
  # }
450
- def ssl_bind(host, port, opts)
540
+ def ssl_bind(host, port, opts = {})
541
+ add_pem_values_to_options_store(opts)
451
542
  bind self.class.ssl_bind_str(host, port, opts)
452
543
  end
453
544
 
@@ -482,6 +573,29 @@ module Puma
482
573
  @options[:workers] = count.to_i
483
574
  end
484
575
 
576
+ # Disable warning message when running in cluster mode with a single worker.
577
+ #
578
+ # Cluster mode has some overhead of running an additional 'control' process
579
+ # in order to manage the cluster. If only running a single worker it is
580
+ # likely not worth paying that overhead vs running in single mode with
581
+ # additional threads instead.
582
+ #
583
+ # There are some scenarios where running cluster mode with a single worker
584
+ # may still be warranted and valid under certain deployment scenarios, see
585
+ # https://github.com/puma/puma/issues/2534
586
+ #
587
+ # Moving from workers = 1 to workers = 0 will save 10-30% of memory use.
588
+ #
589
+ # @note Cluster mode only.
590
+ def silence_single_worker_warning
591
+ @options[:silence_single_worker_warning] = true
592
+ end
593
+
594
+ # Disable warning message when running single mode with callback hook defined.
595
+ def silence_fork_callback_warning
596
+ @options[:silence_fork_callback_warning] = true
597
+ end
598
+
485
599
  # Code to run immediately before master process
486
600
  # forks workers (once on boot). These hooks can block if necessary
487
601
  # to wait for background operations unknown to Puma to finish before
@@ -497,6 +611,8 @@ module Puma
497
611
  # puts "Starting workers..."
498
612
  # end
499
613
  def before_fork(&block)
614
+ warn_if_in_single_mode('before_fork')
615
+
500
616
  @options[:before_fork] ||= []
501
617
  @options[:before_fork] << block
502
618
  end
@@ -511,9 +627,10 @@ module Puma
511
627
  # on_worker_boot do
512
628
  # puts 'Before worker boot...'
513
629
  # end
514
- def on_worker_boot(&block)
515
- @options[:before_worker_boot] ||= []
516
- @options[:before_worker_boot] << block
630
+ def on_worker_boot(key = nil, &block)
631
+ warn_if_in_single_mode('on_worker_boot')
632
+
633
+ process_hook :before_worker_boot, key, block, 'on_worker_boot'
517
634
  end
518
635
 
519
636
  # Code to run immediately before a worker shuts
@@ -528,9 +645,10 @@ module Puma
528
645
  # on_worker_shutdown do
529
646
  # puts 'On worker shutdown...'
530
647
  # end
531
- def on_worker_shutdown(&block)
532
- @options[:before_worker_shutdown] ||= []
533
- @options[:before_worker_shutdown] << block
648
+ def on_worker_shutdown(key = nil, &block)
649
+ warn_if_in_single_mode('on_worker_shutdown')
650
+
651
+ process_hook :before_worker_shutdown, key, block, 'on_worker_shutdown'
534
652
  end
535
653
 
536
654
  # Code to run in the master right before a worker is started. The worker's
@@ -544,8 +662,9 @@ module Puma
544
662
  # puts 'Before worker fork...'
545
663
  # end
546
664
  def on_worker_fork(&block)
547
- @options[:before_worker_fork] ||= []
548
- @options[:before_worker_fork] << block
665
+ warn_if_in_single_mode('on_worker_fork')
666
+
667
+ process_hook :before_worker_fork, nil, block, 'on_worker_fork'
549
668
  end
550
669
 
551
670
  # Code to run in the master after a worker has been started. The worker's
@@ -559,12 +678,23 @@ module Puma
559
678
  # puts 'After worker fork...'
560
679
  # end
561
680
  def after_worker_fork(&block)
562
- @options[:after_worker_fork] ||= []
563
- @options[:after_worker_fork] = block
681
+ warn_if_in_single_mode('after_worker_fork')
682
+
683
+ process_hook :after_worker_fork, nil, block, 'after_worker_fork'
564
684
  end
565
685
 
566
686
  alias_method :after_worker_boot, :after_worker_fork
567
687
 
688
+ # Code to run after puma is booted (works for both: single and clustered)
689
+ #
690
+ # @example
691
+ # on_booted do
692
+ # puts 'After booting...'
693
+ # end
694
+ def on_booted(&block)
695
+ @config.options[:events].on_booted(&block)
696
+ end
697
+
568
698
  # When `fork_worker` is enabled, code to run in Worker 0
569
699
  # before all other workers are re-forked from this process,
570
700
  # after the server has temporarily stopped serving requests
@@ -583,9 +713,8 @@ module Puma
583
713
  # end
584
714
  # @version 5.0.0
585
715
  #
586
- def on_refork(&block)
587
- @options[:before_refork] ||= []
588
- @options[:before_refork] << block
716
+ def on_refork(key = nil, &block)
717
+ process_hook :before_refork, key, block, 'on_refork'
589
718
  end
590
719
 
591
720
  # Code to run out-of-band when the worker is idle.
@@ -598,8 +727,7 @@ module Puma
598
727
  #
599
728
  # This can be called multiple times to add several hooks.
600
729
  def out_of_band(&block)
601
- @options[:out_of_band] ||= []
602
- @options[:out_of_band] << block
730
+ process_hook :out_of_band, nil, block, 'out_of_band'
603
731
  end
604
732
 
605
733
  # The directory to operate out of.
@@ -702,6 +830,19 @@ module Puma
702
830
  @options[:tag] = string.to_s
703
831
  end
704
832
 
833
+ # Change the default interval for checking workers.
834
+ #
835
+ # The default value is 5 seconds.
836
+ #
837
+ # @note Cluster mode only.
838
+ # @example
839
+ # worker_check_interval 5
840
+ # @see Puma::Cluster#check_workers
841
+ #
842
+ def worker_check_interval(interval)
843
+ @options[:worker_check_interval] = Integer(interval)
844
+ end
845
+
705
846
  # Verifies that all workers have checked in to the master process within
706
847
  # the given timeout. If not the worker process will be restarted. This is
707
848
  # not a request timeout, it is to protect against a hung or dead process.
@@ -716,7 +857,7 @@ module Puma
716
857
  #
717
858
  def worker_timeout(timeout)
718
859
  timeout = Integer(timeout)
719
- min = Const::WORKER_CHECK_INTERVAL
860
+ min = @options.fetch(:worker_check_interval, Configuration::DEFAULTS[:worker_check_interval])
720
861
 
721
862
  if timeout <= min
722
863
  raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
@@ -748,6 +889,30 @@ module Puma
748
889
  @options[:worker_shutdown_timeout] = Integer(timeout)
749
890
  end
750
891
 
892
+ # Set the strategy for worker culling.
893
+ #
894
+ # There are two possible values:
895
+ #
896
+ # 1. **:youngest** - the youngest workers (i.e. the workers that were
897
+ # the most recently started) will be culled.
898
+ # 2. **:oldest** - the oldest workers (i.e. the workers that were started
899
+ # the longest time ago) will be culled.
900
+ #
901
+ # @note Cluster mode only.
902
+ # @example
903
+ # worker_culling_strategy :oldest
904
+ # @see Puma::Cluster#cull_workers
905
+ #
906
+ def worker_culling_strategy(strategy)
907
+ stategy = strategy.to_sym
908
+
909
+ if ![:youngest, :oldest].include?(strategy)
910
+ raise "Invalid value for worker_culling_strategy - #{stategy}"
911
+ end
912
+
913
+ @options[:worker_culling_strategy] = strategy
914
+ end
915
+
751
916
  # When set to true (the default), workers accept all requests
752
917
  # and queue them before passing them to the handlers.
753
918
  # When set to false, each worker process accepts exactly as
@@ -793,17 +958,23 @@ module Puma
793
958
  # a kernel syscall is required which for very fast rack handlers
794
959
  # slows down the handling significantly.
795
960
  #
796
- # There are 4 possible values:
961
+ # There are 5 possible values:
797
962
  #
798
963
  # 1. **:socket** (the default) - read the peername from the socket using the
799
- # syscall. This is the normal behavior.
964
+ # syscall. This is the normal behavior. If this fails for any reason (e.g.,
965
+ # if the peer disconnects between the connection being accepted and the getpeername
966
+ # system call), Puma will return "0.0.0.0"
800
967
  # 2. **:localhost** - set the remote address to "127.0.0.1"
801
968
  # 3. **header: <http_header>**- set the remote address to the value of the
802
969
  # provided http header. For instance:
803
970
  # `set_remote_address header: "X-Real-IP"`.
804
971
  # Only the first word (as separated by spaces or comma) is used, allowing
805
- # headers such as X-Forwarded-For to be used as well.
806
- # 4. **\<Any string\>** - this allows you to hardcode remote address to any value
972
+ # headers such as X-Forwarded-For to be used as well. If this header is absent,
973
+ # Puma will fall back to the behavior of :socket
974
+ # 4. **proxy_protocol: :v1**- set the remote address to the value read from the
975
+ # HAproxy PROXY protocol, version 1. If the request does not have the PROXY
976
+ # protocol attached to it, will fall back to :socket
977
+ # 5. **\<Any string\>** - this allows you to hardcode remote address to any value
807
978
  # you wish. Because Puma never uses this field anyway, it's format is
808
979
  # entirely in your hands.
809
980
  #
@@ -821,6 +992,13 @@ module Puma
821
992
  if hdr = val[:header]
822
993
  @options[:remote_address] = :header
823
994
  @options[:remote_address_header] = "HTTP_" + hdr.upcase.tr("-", "_")
995
+ elsif protocol_version = val[:proxy_protocol]
996
+ @options[:remote_address] = :proxy_protocol
997
+ protocol_version = protocol_version.downcase.to_sym
998
+ unless [:v1].include?(protocol_version)
999
+ raise "Invalid value for proxy_protocol - #{protocol_version.inspect}"
1000
+ end
1001
+ @options[:remote_address_proxy_protocol] = protocol_version
824
1002
  else
825
1003
  raise "Invalid value for set_remote_address - #{val.inspect}"
826
1004
  end
@@ -846,23 +1024,6 @@ module Puma
846
1024
  @options[:fork_worker] = Integer(after_requests)
847
1025
  end
848
1026
 
849
- # When enabled, Puma will GC 4 times before forking workers.
850
- # If available (Ruby 2.7+), we will also call GC.compact.
851
- # Not recommended for non-MRI Rubies.
852
- #
853
- # Based on the work of Koichi Sasada and Aaron Patterson, this option may
854
- # decrease memory utilization of preload-enabled cluster-mode Pumas. It will
855
- # also increase time to boot and fork. See your logs for details on how much
856
- # time this adds to your boot process. For most apps, it will be less than one
857
- # second.
858
- #
859
- # @see Puma::Cluster#nakayoshi_gc
860
- # @version 5.0.0
861
- #
862
- def nakayoshi_fork(enabled=true)
863
- @options[:nakayoshi_fork] = enabled
864
- end
865
-
866
1027
  # The number of requests to attempt inline before sending a client back to
867
1028
  # the reactor to be subject to normal ordering.
868
1029
  #
@@ -892,5 +1053,94 @@ module Puma
892
1053
  def mutate_stdout_and_stderr_to_sync_on_write(enabled=true)
893
1054
  @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
894
1055
  end
1056
+
1057
+ # Specify how big the request payload should be, in bytes.
1058
+ # This limit is compared against Content-Length HTTP header.
1059
+ # If the payload size (CONTENT_LENGTH) is larger than http_content_length_limit,
1060
+ # HTTP 413 status code is returned.
1061
+ #
1062
+ # When no Content-Length http header is present, it is compared against the
1063
+ # size of the body of the request.
1064
+ #
1065
+ # The default value for http_content_length_limit is nil.
1066
+ def http_content_length_limit(limit)
1067
+ @options[:http_content_length_limit] = limit
1068
+ end
1069
+
1070
+ # Supported http methods, which will replace `Puma::Const::SUPPORTED_HTTP_METHODS`.
1071
+ # The value of `:any` will allows all methods, otherwise, the value must be
1072
+ # an array of strings. Note that methods are all uppercase.
1073
+ #
1074
+ # `Puma::Const::SUPPORTED_HTTP_METHODS` is conservative, if you want a
1075
+ # complete set of methods, the methods defined by the
1076
+ # [IANA Method Registry](https://www.iana.org/assignments/http-methods/http-methods.xhtml)
1077
+ # are pre-defined as the constant `Puma::Const::IANA_HTTP_METHODS`.
1078
+ #
1079
+ # @note If the `methods` value is `:any`, no method check with be performed,
1080
+ # similar to Puma v5 and earlier.
1081
+ #
1082
+ # @example Adds 'PROPFIND' to existing supported methods
1083
+ # supported_http_methods(Puma::Const::SUPPORTED_HTTP_METHODS + ['PROPFIND'])
1084
+ # @example Restricts methods to the array elements
1085
+ # supported_http_methods %w[HEAD GET POST PUT DELETE OPTIONS PROPFIND]
1086
+ # @example Restricts methods to the methods in the IANA Registry
1087
+ # supported_http_methods Puma::Const::IANA_HTTP_METHODS
1088
+ # @example Allows any method
1089
+ # supported_http_methods :any
1090
+ #
1091
+ def supported_http_methods(methods)
1092
+ if methods == :any
1093
+ @options[:supported_http_methods] = :any
1094
+ elsif Array === methods && methods == (ary = methods.grep(String).uniq) &&
1095
+ !ary.empty?
1096
+ @options[:supported_http_methods] = ary
1097
+ else
1098
+ raise "supported_http_methods must be ':any' or a unique array of strings"
1099
+ end
1100
+ end
1101
+
1102
+ private
1103
+
1104
+ # To avoid adding cert_pem and key_pem as URI params, we store them on the
1105
+ # options[:store] from where Puma binder knows how to find and extract them.
1106
+ def add_pem_values_to_options_store(opts)
1107
+ return if defined?(JRUBY_VERSION)
1108
+
1109
+ @options[:store] ||= []
1110
+
1111
+ # Store cert_pem and key_pem to options[:store] if present
1112
+ [:cert, :key].each do |v|
1113
+ opt_key = :"#{v}_pem"
1114
+ if opts[opt_key]
1115
+ index = @options[:store].length
1116
+ @options[:store] << opts[opt_key]
1117
+ opts[v] = "store:#{index}"
1118
+ end
1119
+ end
1120
+ end
1121
+
1122
+ def process_hook(options_key, key, block, meth)
1123
+ @options[options_key] ||= []
1124
+ if ON_WORKER_KEY.include? key.class
1125
+ @options[options_key] << [block, key.to_sym]
1126
+ elsif key.nil?
1127
+ @options[options_key] << block
1128
+ else
1129
+ raise "'#{meth}' key must be String or Symbol"
1130
+ end
1131
+ end
1132
+
1133
+ def warn_if_in_single_mode(hook_name)
1134
+ return if @options[:silence_fork_callback_warning]
1135
+
1136
+ if (@options[:workers] || 0) == 0
1137
+ log_string =
1138
+ "Warning: You specified code to run in a `#{hook_name}` block, " \
1139
+ "but Puma is not configured to run in cluster mode (worker count > 0 ), " \
1140
+ "so your `#{hook_name}` block did not run"
1141
+
1142
+ LogWriter.stdio.log(log_string)
1143
+ end
1144
+ end
895
1145
  end
896
1146
  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
 
@@ -23,7 +25,7 @@ module Puma
23
25
  new $stderr
24
26
  end
25
27
 
26
- # Print occured error details.
28
+ # Print occurred error details.
27
29
  # +options+ hash with additional options:
28
30
  # - +error+ is an exception object
29
31
  # - +req+ the http request
@@ -31,10 +33,10 @@ 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
- # Print occured error details only if
39
+ # Print occurred error details only if
38
40
  # environment variable PUMA_DEBUG is defined.
39
41
  # +options+ hash with additional options:
40
42
  # - +error+ is an exception object
@@ -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