puma 5.6.4 → 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 (85) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +270 -4
  3. data/LICENSE +0 -0
  4. data/README.md +60 -20
  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 +0 -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 +0 -0
  21. data/docs/signals.md +0 -0
  22. data/docs/stats.md +0 -0
  23. data/docs/systemd.md +1 -2
  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 +18 -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 +93 -26
  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 +166 -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 +49 -52
  42. data/lib/puma/cli.rb +12 -18
  43. data/lib/puma/client.rb +55 -16
  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 +33 -30
  47. data/lib/puma/commonlogger.rb +21 -14
  48. data/lib/puma/configuration.rb +76 -58
  49. data/lib/puma/const.rb +129 -92
  50. data/lib/puma/control_cli.rb +21 -18
  51. data/lib/puma/detect.rb +4 -0
  52. data/lib/puma/dsl.rb +187 -49
  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 +24 -12
  62. data/lib/puma/minissl.rb +108 -15
  63. data/lib/puma/null_io.rb +5 -0
  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 +0 -0
  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 +52 -20
  73. data/lib/puma/sd_notify.rb +149 -0
  74. data/lib/puma/server.rb +73 -73
  75. data/lib/puma/single.rb +13 -11
  76. data/lib/puma/state_file.rb +2 -4
  77. data/lib/puma/thread_pool.rb +23 -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 +0 -0
  82. data/tools/trickletest.rb +0 -0
  83. metadata +10 -5
  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
@@ -367,6 +420,11 @@ module Puma
367
420
  @options[:log_requests] = which
368
421
  end
369
422
 
423
+ # Pass in a custom logging class instance
424
+ def custom_logger(custom_logger)
425
+ @options[:custom_logger] = custom_logger
426
+ end
427
+
370
428
  # Show debugging info
371
429
  #
372
430
  def debug
@@ -448,6 +506,10 @@ module Puma
448
506
  # Puma will assume you are using the +localhost+ gem and try to load the
449
507
  # appropriate files.
450
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.
512
+ #
451
513
  # @example
452
514
  # ssl_bind '127.0.0.1', '9292', {
453
515
  # cert: path_to_cert,
@@ -455,6 +517,7 @@ module Puma
455
517
  # ssl_cipher_filter: cipher_filter, # optional
456
518
  # verify_mode: verify_mode, # default 'none'
457
519
  # verification_flags: flags, # optional, not supported by JRuby
520
+ # reuse: true # optional
458
521
  # }
459
522
  #
460
523
  # @example Using self-signed certificate with the +localhost+ gem:
@@ -464,6 +527,7 @@ module Puma
464
527
  # ssl_bind '127.0.0.1', '9292', {
465
528
  # cert_pem: File.read(path_to_cert),
466
529
  # key_pem: File.read(path_to_key),
530
+ # reuse: {size: 2_000, timeout: 20} # optional
467
531
  # }
468
532
  #
469
533
  # @example For JRuby, two keys are required: +keystore+ & +keystore_pass+
@@ -527,6 +591,11 @@ module Puma
527
591
  @options[:silence_single_worker_warning] = true
528
592
  end
529
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
+
530
599
  # Code to run immediately before master process
531
600
  # forks workers (once on boot). These hooks can block if necessary
532
601
  # to wait for background operations unknown to Puma to finish before
@@ -542,6 +611,8 @@ module Puma
542
611
  # puts "Starting workers..."
543
612
  # end
544
613
  def before_fork(&block)
614
+ warn_if_in_single_mode('before_fork')
615
+
545
616
  @options[:before_fork] ||= []
546
617
  @options[:before_fork] << block
547
618
  end
@@ -556,9 +627,10 @@ module Puma
556
627
  # on_worker_boot do
557
628
  # puts 'Before worker boot...'
558
629
  # end
559
- def on_worker_boot(&block)
560
- @options[:before_worker_boot] ||= []
561
- @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'
562
634
  end
563
635
 
564
636
  # Code to run immediately before a worker shuts
@@ -573,9 +645,10 @@ module Puma
573
645
  # on_worker_shutdown do
574
646
  # puts 'On worker shutdown...'
575
647
  # end
576
- def on_worker_shutdown(&block)
577
- @options[:before_worker_shutdown] ||= []
578
- @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'
579
652
  end
580
653
 
581
654
  # Code to run in the master right before a worker is started. The worker's
@@ -589,8 +662,9 @@ module Puma
589
662
  # puts 'Before worker fork...'
590
663
  # end
591
664
  def on_worker_fork(&block)
592
- @options[:before_worker_fork] ||= []
593
- @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'
594
668
  end
595
669
 
596
670
  # Code to run in the master after a worker has been started. The worker's
@@ -604,12 +678,23 @@ module Puma
604
678
  # puts 'After worker fork...'
605
679
  # end
606
680
  def after_worker_fork(&block)
607
- @options[:after_worker_fork] ||= []
608
- @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'
609
684
  end
610
685
 
611
686
  alias_method :after_worker_boot, :after_worker_fork
612
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
+
613
698
  # When `fork_worker` is enabled, code to run in Worker 0
614
699
  # before all other workers are re-forked from this process,
615
700
  # after the server has temporarily stopped serving requests
@@ -628,9 +713,8 @@ module Puma
628
713
  # end
629
714
  # @version 5.0.0
630
715
  #
631
- def on_refork(&block)
632
- @options[:before_refork] ||= []
633
- @options[:before_refork] << block
716
+ def on_refork(key = nil, &block)
717
+ process_hook :before_refork, key, block, 'on_refork'
634
718
  end
635
719
 
636
720
  # Code to run out-of-band when the worker is idle.
@@ -643,8 +727,7 @@ module Puma
643
727
  #
644
728
  # This can be called multiple times to add several hooks.
645
729
  def out_of_band(&block)
646
- @options[:out_of_band] ||= []
647
- @options[:out_of_band] << block
730
+ process_hook :out_of_band, nil, block, 'out_of_band'
648
731
  end
649
732
 
650
733
  # The directory to operate out of.
@@ -774,7 +857,7 @@ module Puma
774
857
  #
775
858
  def worker_timeout(timeout)
776
859
  timeout = Integer(timeout)
777
- min = @options.fetch(:worker_check_interval, Puma::ConfigDefault::DefaultWorkerCheckInterval)
860
+ min = @options.fetch(:worker_check_interval, Configuration::DEFAULTS[:worker_check_interval])
778
861
 
779
862
  if timeout <= min
780
863
  raise "The minimum worker_timeout must be greater than the worker reporting interval (#{min})"
@@ -878,13 +961,16 @@ module Puma
878
961
  # There are 5 possible values:
879
962
  #
880
963
  # 1. **:socket** (the default) - read the peername from the socket using the
881
- # 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"
882
967
  # 2. **:localhost** - set the remote address to "127.0.0.1"
883
968
  # 3. **header: <http_header>**- set the remote address to the value of the
884
969
  # provided http header. For instance:
885
970
  # `set_remote_address header: "X-Real-IP"`.
886
971
  # 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.
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
888
974
  # 4. **proxy_protocol: :v1**- set the remote address to the value read from the
889
975
  # HAproxy PROXY protocol, version 1. If the request does not have the PROXY
890
976
  # protocol attached to it, will fall back to :socket
@@ -938,23 +1024,6 @@ module Puma
938
1024
  @options[:fork_worker] = Integer(after_requests)
939
1025
  end
940
1026
 
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
1027
  # The number of requests to attempt inline before sending a client back to
959
1028
  # the reactor to be subject to normal ordering.
960
1029
  #
@@ -985,6 +1054,51 @@ module Puma
985
1054
  @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
986
1055
  end
987
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
+
988
1102
  private
989
1103
 
990
1104
  # To avoid adding cert_pem and key_pem as URI params, we store them on the
@@ -1004,5 +1118,29 @@ module Puma
1004
1118
  end
1005
1119
  end
1006
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
1007
1145
  end
1008
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
 
@@ -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
data/lib/puma/events.rb CHANGED
@@ -1,52 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "puma/null_io"
4
- require 'puma/error_logger'
5
- require 'stringio'
6
-
7
3
  module Puma
8
- # The default implement of an event sink object used by Server
9
- # for when certain kinds of events occur in the life of the server.
10
- #
11
- # The methods available are the events that the Server fires.
12
- #
13
- class Events
14
- class DefaultFormatter
15
- def call(str)
16
- str
17
- end
18
- end
19
-
20
- class PidFormatter
21
- def call(str)
22
- "[#{$$}] #{str}"
23
- end
24
- end
25
-
26
- # Create an Events object that prints to +stdout+ and +stderr+.
27
- #
28
- def initialize(stdout, stderr)
29
- @formatter = DefaultFormatter.new
30
- @stdout = stdout
31
- @stderr = stderr
32
4
 
33
- @debug = ENV.key? 'PUMA_DEBUG'
34
- @error_logger = ErrorLogger.new(@stderr)
5
+ # This is an event sink used by `Puma::Server` to handle
6
+ # lifecycle events such as :on_booted, :on_restart, and :on_stopped.
7
+ # Using `Puma::DSL` it is possible to register callback hooks
8
+ # for each event type.
9
+ class Events
35
10
 
11
+ def initialize
36
12
  @hooks = Hash.new { |h,k| h[k] = [] }
37
13
  end
38
14
 
39
- attr_reader :stdout, :stderr
40
- attr_accessor :formatter
41
-
42
15
  # Fire callbacks for the named hook
43
- #
44
16
  def fire(hook, *args)
45
17
  @hooks[hook].each { |t| t.call(*args) }
46
18
  end
47
19
 
48
20
  # Register a callback for a given hook
49
- #
50
21
  def register(hook, obj=nil, &blk)
51
22
  if obj and blk
52
23
  raise "Specify either an object or a block, not both"
@@ -59,79 +30,6 @@ module Puma
59
30
  h
60
31
  end
61
32
 
62
- # Write +str+ to +@stdout+
63
- #
64
- def log(str)
65
- @stdout.puts format(str) if @stdout.respond_to? :puts
66
-
67
- @stdout.flush unless @stdout.sync
68
- rescue Errno::EPIPE
69
- end
70
-
71
- def write(str)
72
- @stdout.write format(str)
73
- end
74
-
75
- def debug(str)
76
- log("% #{str}") if @debug
77
- end
78
-
79
- # Write +str+ to +@stderr+
80
- #
81
- def error(str)
82
- @error_logger.info(text: format("ERROR: #{str}"))
83
- exit 1
84
- end
85
-
86
- def format(str)
87
- formatter.call(str)
88
- end
89
-
90
- # An HTTP connection error has occurred.
91
- # +error+ a connection exception, +req+ the request,
92
- # and +text+ additional info
93
- # @version 5.0.0
94
- #
95
- def connection_error(error, req, text="HTTP connection error")
96
- @error_logger.info(error: error, req: req, text: text)
97
- end
98
-
99
- # An HTTP parse error has occurred.
100
- # +error+ a parsing exception,
101
- # and +req+ the request.
102
- #
103
- def parse_error(error, req)
104
- @error_logger.info(error: error, req: req, text: 'HTTP parse error, malformed request')
105
- end
106
-
107
- # An SSL error has occurred.
108
- # @param error <Puma::MiniSSL::SSLError>
109
- # @param ssl_socket <Puma::MiniSSL::Socket>
110
- #
111
- def ssl_error(error, ssl_socket)
112
- peeraddr = ssl_socket.peeraddr.last rescue "<unknown>"
113
- peercert = ssl_socket.peercert
114
- subject = peercert ? peercert.subject : nil
115
- @error_logger.info(error: error, text: "SSL error, peer: #{peeraddr}, peer cert: #{subject}")
116
- end
117
-
118
- # An unknown error has occurred.
119
- # +error+ an exception object, +req+ the request,
120
- # and +text+ additional info
121
- #
122
- def unknown_error(error, req=nil, text="Unknown error")
123
- @error_logger.info(error: error, req: req, text: text)
124
- end
125
-
126
- # Log occurred error debug dump.
127
- # +error+ an exception object, +req+ the request,
128
- # and +text+ additional info
129
- # @version 5.0.0
130
- #
131
- def debug_error(error, req=nil, text="")
132
- @error_logger.debug(error: error, req: req, text: text)
133
- end
134
-
135
33
  def on_booted(&block)
136
34
  register(:on_booted, &block)
137
35
  end
@@ -155,23 +53,5 @@ module Puma
155
53
  def fire_on_stopped!
156
54
  fire(:on_stopped)
157
55
  end
158
-
159
- DEFAULT = new(STDOUT, STDERR)
160
-
161
- # Returns an Events object which writes its status to 2 StringIO
162
- # objects.
163
- #
164
- def self.strings
165
- Events.new StringIO.new, StringIO.new
166
- end
167
-
168
- def self.stdio
169
- Events.new $stdout, $stderr
170
- end
171
-
172
- def self.null
173
- n = NullIO.new
174
- Events.new n, n
175
- end
176
56
  end
177
57
  end
@@ -1,11 +1,46 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'stringio'
4
+
3
5
  module Puma
4
- class IOBuffer < String
5
- def append(*args)
6
- args.each { |a| concat(a) }
6
+ class IOBuffer < StringIO
7
+ def initialize
8
+ super.binmode
9
+ end
10
+
11
+ def empty?
12
+ length.zero?
7
13
  end
8
14
 
9
- alias reset clear
15
+ def reset
16
+ truncate 0
17
+ rewind
18
+ end
19
+
20
+ def to_s
21
+ rewind
22
+ read
23
+ end
24
+
25
+ # Read & Reset - returns contents and resets
26
+ # @return [String] StringIO contents
27
+ def read_and_reset
28
+ rewind
29
+ str = read
30
+ truncate 0
31
+ rewind
32
+ str
33
+ end
34
+
35
+ alias_method :clear, :reset
36
+
37
+ # before Ruby 2.5, `write` would only take one argument
38
+ if RUBY_VERSION >= '2.5' && RUBY_ENGINE != 'truffleruby'
39
+ alias_method :append, :write
40
+ else
41
+ def append(*strs)
42
+ strs.each { |str| write str }
43
+ end
44
+ end
10
45
  end
11
46
  end
@@ -16,7 +16,8 @@ module Puma
16
16
  def self.chdir_exec(dir, argv)
17
17
  chdir(dir)
18
18
  cmd = argv.first
19
- argv = ([:string] * argv.size).zip(argv).flatten
19
+ argv = ([:string] * argv.size).zip(argv)
20
+ argv.flatten!
20
21
  argv << :string
21
22
  argv << nil
22
23
  execlp(cmd, *argv)
File without changes