puma 6.0.2 → 6.2.1

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 83bb232727e7777f406dcacc150db80426972c1e65635f1d81d771fee3585060
4
- data.tar.gz: 16453cd88384851586d3ff6bc274393b44a6a078eaec9932cac795009fc342fb
3
+ metadata.gz: 15b598484e8fbdd9262dfdb0f4cc8ec379b48d8b3f83f6079533d216f37fb12d
4
+ data.tar.gz: d80fc6d21a8ead42565a112d6a4d53313796d729790df9d5be797218f40a7af4
5
5
  SHA512:
6
- metadata.gz: ce357b7686a54c2d0e820d70f24bb427803ba9e7aa24a8b0511624dc4ef850e937b9261575462d6a20a0c69f73d1fefa939a242d8e3e5e9577fdd4e45a862af2
7
- data.tar.gz: 3d9fda444a65d5917872261ff4dff8f3e156aec8a1d06d72cc19201da8f820b0262baac73665bb062355966a8d9415ed5b92d01f4c8ad6e312737141c657e823
6
+ metadata.gz: 3d6a969b599e9e919de4cbead5963379261c4fee1190f361e80945a03ed0d0932e770b62dd37db1388ff473f09f40bca3fe477e82238e234246c0becf70f7553
7
+ data.tar.gz: 2c8be61d5340256eaaca6d093bfad46401199700472a971881dafb7661e0c44fa9bff699e1cd24c031f51b79b3f01854498f80988707933fb6abb1d0fc2bfe05
data/History.md CHANGED
@@ -1,3 +1,45 @@
1
+ ## 6.2.1 / 2023-03-31
2
+
3
+ * Bugfixes
4
+ * Fix java 8 compatibility ([#3109], [#3108])
5
+ * Always write io_buffer when in "enum bodies" branch. ([#3113], [#3112])
6
+ * Fix warn_if_in_single_mode incorrect message ([#3111])
7
+
8
+ ## 6.2.0 / 2023-03-29
9
+
10
+ * Features
11
+ * Ability to supply a custom logger ([#2770], [#2511])
12
+ * Warn when clustered-only hooks are defined in single mode ([#3089])
13
+ * Adds the on_booted event ([#2709])
14
+
15
+ * Bugfixes
16
+ * Loggers - internal_write - catch Errno::EINVAL ([#3091])
17
+ * commonlogger.rb - fix HIJACK time format, use constants, not strings ([#3074])
18
+ * Fixed some edge cases regarding request hijacking ([#3072])
19
+
20
+
21
+ ## 6.1.1 / 2023-02-28
22
+
23
+ * Bugfixes
24
+ * We no longer try to use the systemd plugin for JRuby ([#3079])
25
+ * Allow ::Rack::Handler::Puma.run to work regardless of whether Rack/Rackup are loaded ([#3080])
26
+
27
+ ## 6.1.0 / 2023-02-12
28
+
29
+ * Features
30
+ * WebSocket support via partial hijack ([#3058], [#3007])
31
+ * Add built-in systemd notify support ([#3011])
32
+ * Periodically send status to systemd ([#3006], [#2604])
33
+ * Introduce the ability to return 413: payload too large for requests ([#3040])
34
+ * Log loaded extensions when `PUMA_DEBUG` is set ([#3036], [#3020])
35
+
36
+ * Bugfixes
37
+ * Fix issue with rack 3 compatibility re: rackup ([#3061], [#3057])
38
+ * Allow setting TCP low_latency with SSL listener ([#3065])
39
+
40
+ * Performance
41
+ * Reduce memory usage for large file uploads ([#3062])
42
+
1
43
  ## 6.0.2 / 2023-01-01
2
44
 
3
45
  * Refactor
@@ -1932,6 +1974,32 @@ be added back in a future date when a java Puma::MiniSSL is added.
1932
1974
  * Bugfixes
1933
1975
  * Your bugfix goes here <Most recent on the top, like GitHub> (#Github Number)
1934
1976
 
1977
+ [#3109]:https://github.com/puma/puma/pull/3109 "PR by @ahorek, merged 2023-03-31"
1978
+ [#3108]:https://github.com/puma/puma/issues/3108 "Issue by @treviateo, closed 2023-03-31"
1979
+ [#3113]:https://github.com/puma/puma/pull/3113 "PR by @collinsauve, merged 2023-03-31"
1980
+ [#3112]:https://github.com/puma/puma/issues/3112 "Issue by @dmke, closed 2023-03-31"
1981
+ [#3111]:https://github.com/puma/puma/pull/3111 "PR by @adzap, merged 2023-03-30"
1982
+ [#2770]:https://github.com/puma/puma/pull/2770 "PR by @vzajkov, merged 2023-03-29"
1983
+ [#2511]:https://github.com/puma/puma/issues/2511 "Issue by @jchristie55332, closed 2021-12-12"
1984
+ [#3089]:https://github.com/puma/puma/pull/3089 "PR by @Vuta, merged 2023-03-06"
1985
+ [#2709]:https://github.com/puma/puma/pull/2709 "PR by @rodzyn, merged 2023-02-20"
1986
+ [#3091]:https://github.com/puma/puma/pull/3091 "PR by @MSP-Greg, merged 2023-03-28"
1987
+ [#3074]:https://github.com/puma/puma/pull/3074 "PR by @MSP-Greg, merged 2023-03-14"
1988
+ [#3072]:https://github.com/puma/puma/pull/3072 "PR by @MSP-Greg, merged 2023-02-17"
1989
+ [#3079]:https://github.com/puma/puma/pull/3079 "PR by @mohamedhafez, merged 2023-02-24"
1990
+ [#3080]:https://github.com/puma/puma/pull/3080 "PR by @MSP-Greg, merged 2023-02-16"
1991
+ [#3058]:https://github.com/puma/puma/pull/3058 "PR by @dentarg, merged 2023-01-29"
1992
+ [#3007]:https://github.com/puma/puma/issues/3007 "Issue by @MSP-Greg, closed 2023-01-29"
1993
+ [#3011]:https://github.com/puma/puma/pull/3011 "PR by @joaomarcos96, merged 2023-01-03"
1994
+ [#3006]:https://github.com/puma/puma/pull/3006 "PR by @QWYNG, merged 2023-02-09"
1995
+ [#2604]:https://github.com/puma/puma/issues/2604 "Issue by @dgoetz, closed 2023-02-09"
1996
+ [#3040]:https://github.com/puma/puma/pull/3040 "PR by @shayonj, merged 2023-01-02"
1997
+ [#3036]:https://github.com/puma/puma/pull/3036 "PR by @MSP-Greg, merged 2023-01-13"
1998
+ [#3020]:https://github.com/puma/puma/issues/3020 "Issue by @dentarg, closed 2023-01-13"
1999
+ [#3061]:https://github.com/puma/puma/pull/3061 "PR by @MSP-Greg, merged 2023-02-12"
2000
+ [#3057]:https://github.com/puma/puma/issues/3057 "Issue by @mmarvb8h, closed 2023-02-12"
2001
+ [#3065]:https://github.com/puma/puma/pull/3065 "PR by @MSP-Greg, merged 2023-02-11"
2002
+ [#3062]:https://github.com/puma/puma/pull/3062 "PR by @willkoehler, merged 2023-01-29"
1935
2003
  [#3035]:https://github.com/puma/puma/pull/3035 "PR by @MSP-Greg, merged 2022-12-24"
1936
2004
  [#3033]:https://github.com/puma/puma/issues/3033 "Issue by @jules-w2, closed 2022-12-24"
1937
2005
  [#3016]:https://github.com/puma/puma/pull/3016 "PR by @MSP-Greg, merged 2022-12-24"
data/README.md CHANGED
@@ -157,6 +157,15 @@ before_fork do
157
157
  end
158
158
  ```
159
159
 
160
+ You can also specify a block to be run after puma is booted using `on_booted`:
161
+
162
+ ```ruby
163
+ # config/puma.rb
164
+ on_booted do
165
+ # configuration here
166
+ end
167
+ ```
168
+
160
169
  ### Error handling
161
170
 
162
171
  If puma encounters an error outside of the context of your application, it will respond with a 500 and a simple
@@ -345,11 +354,13 @@ end
345
354
 
346
355
  ## Deployment
347
356
 
348
- Puma has support for Capistrano with an [external gem](https://github.com/seuros/capistrano-puma).
357
+ * Puma has support for Capistrano with an [external gem](https://github.com/seuros/capistrano-puma).
358
+
359
+ * Additionally, Puma has support for built-in daemonization via the [puma-daemon](https://github.com/kigster/puma-daemon) ruby gem. The gem restores the `daemonize` option that was removed from Puma starting version 5, but only for MRI Ruby.
360
+
349
361
 
350
362
  It is common to use process monitors with Puma. Modern process monitors like systemd or rc.d
351
- provide continuous monitoring and restarts for increased
352
- reliability in production environments:
363
+ provide continuous monitoring and restarts for increased reliability in production environments:
353
364
 
354
365
  * [rc.d](docs/jungle/rc.d/README.md)
355
366
  * [systemd](docs/systemd.md)
@@ -365,6 +376,7 @@ Community guides:
365
376
  * [puma-metrics](https://github.com/harmjanblok/puma-metrics) — export Puma metrics to Prometheus
366
377
  * [puma-plugin-statsd](https://github.com/yob/puma-plugin-statsd) — send Puma metrics to statsd
367
378
  * [puma-plugin-systemd](https://github.com/sj26/puma-plugin-systemd) — deeper integration with systemd for notify, status and watchdog
379
+ * [puma-plugin-telemetry](https://github.com/babbel/puma-plugin-telemetry) - telemetry plugin for Puma offering various targets to publish
368
380
 
369
381
  ### Monitoring
370
382
 
data/docs/systemd.md CHANGED
@@ -24,8 +24,7 @@ After=network.target
24
24
 
25
25
  [Service]
26
26
  # Puma supports systemd's `Type=notify` and watchdog service
27
- # monitoring, if the [sd_notify](https://github.com/agis/ruby-sdnotify) gem is installed,
28
- # as of Puma 5.1 or later.
27
+ # monitoring, as of Puma 5.1 or later.
29
28
  # On earlier versions of Puma or JRuby, change this to `Type=simple` and remove
30
29
  # the `WatchdogSec` line.
31
30
  Type=notify
data/lib/puma/binder.rb CHANGED
@@ -158,10 +158,10 @@ module Puma
158
158
  ios_len = @ios.length
159
159
  params = Util.parse_query uri.query
160
160
 
161
- opt = params.key?('low_latency') && params['low_latency'] != 'false'
161
+ low_latency = params.key?('low_latency') && params['low_latency'] != 'false'
162
162
  backlog = params.fetch('backlog', 1024).to_i
163
163
 
164
- io = add_tcp_listener uri.host, uri.port, opt, backlog
164
+ io = add_tcp_listener uri.host, uri.port, low_latency, backlog
165
165
 
166
166
  @ios[ios_len..-1].each do |i|
167
167
  addr = loc_addr_str i
@@ -251,7 +251,8 @@ module Puma
251
251
  else
252
252
  ios_len = @ios.length
253
253
  backlog = params.fetch('backlog', 1024).to_i
254
- io = add_ssl_listener uri.host, uri.port, ctx, optimize_for_latency = true, backlog
254
+ low_latency = params['low_latency'] != 'false'
255
+ io = add_ssl_listener uri.host, uri.port, ctx, low_latency, backlog
255
256
 
256
257
  @ios[ios_len..-1].each do |i|
257
258
  addr = loc_addr_str i
data/lib/puma/cli.rb CHANGED
@@ -93,7 +93,7 @@ module Puma
93
93
  #
94
94
 
95
95
  def setup_options
96
- @conf = Configuration.new do |user_config, file_config|
96
+ @conf = Configuration.new({}, {events: @events}) do |user_config, file_config|
97
97
  @parser = OptionParser.new do |o|
98
98
  o.on "-b", "--bind URI", "URI to bind to (tcp://, unix://, ssl://)" do |arg|
99
99
  user_config.bind arg
data/lib/puma/client.rb CHANGED
@@ -86,6 +86,9 @@ module Puma
86
86
  @requests_served = 0
87
87
  @hijacked = false
88
88
 
89
+ @http_content_length_limit = nil
90
+ @http_content_length_limit_exceeded = false
91
+
89
92
  @peerip = nil
90
93
  @peer_family = nil
91
94
  @listener = nil
@@ -95,12 +98,14 @@ module Puma
95
98
  @body_remain = 0
96
99
 
97
100
  @in_last_chunk = false
101
+
102
+ @read_buffer = +""
98
103
  end
99
104
 
100
105
  attr_reader :env, :to_io, :body, :io, :timeout_at, :ready, :hijacked,
101
- :tempfile, :io_buffer
106
+ :tempfile, :io_buffer, :http_content_length_limit_exceeded
102
107
 
103
- attr_writer :peerip
108
+ attr_writer :peerip, :http_content_length_limit
104
109
 
105
110
  attr_accessor :remote_addr_header, :listener
106
111
 
@@ -151,6 +156,7 @@ module Puma
151
156
  @body_remain = 0
152
157
  @peerip = nil if @remote_addr_header
153
158
  @in_last_chunk = false
159
+ @http_content_length_limit_exceeded = false
154
160
 
155
161
  if @buffer
156
162
  return false unless try_to_parse_proxy_protocol
@@ -210,6 +216,17 @@ module Puma
210
216
  end
211
217
 
212
218
  def try_to_finish
219
+ if env[CONTENT_LENGTH] && above_http_content_limit(env[CONTENT_LENGTH].to_i)
220
+ @http_content_length_limit_exceeded = true
221
+ end
222
+
223
+ if @http_content_length_limit_exceeded
224
+ @buffer = nil
225
+ @body = EmptyBody
226
+ set_ready
227
+ return true
228
+ end
229
+
213
230
  return read_body if in_data_phase
214
231
 
215
232
  begin
@@ -239,6 +256,10 @@ module Puma
239
256
 
240
257
  @parsed_bytes = @parser.execute(@env, @buffer, @parsed_bytes)
241
258
 
259
+ if @parser.finished? && above_http_content_limit(@parser.body.bytesize)
260
+ @http_content_length_limit_exceeded = true
261
+ end
262
+
242
263
  if @parser.finished?
243
264
  return setup_body
244
265
  elsif @parsed_bytes >= MAX_HEADER
@@ -414,7 +435,7 @@ module Puma
414
435
  end
415
436
 
416
437
  begin
417
- chunk = @io.read_nonblock(want)
438
+ chunk = @io.read_nonblock(want, @read_buffer)
418
439
  rescue IO::WaitReadable
419
440
  return false
420
441
  rescue SystemCallError, IOError
@@ -446,7 +467,7 @@ module Puma
446
467
  def read_chunked_body
447
468
  while true
448
469
  begin
449
- chunk = @io.read_nonblock(4096)
470
+ chunk = @io.read_nonblock(4096, @read_buffer)
450
471
  rescue IO::WaitReadable
451
472
  return false
452
473
  rescue SystemCallError, IOError
@@ -594,5 +615,9 @@ module Puma
594
615
  @requests_served += 1
595
616
  @ready = true
596
617
  end
618
+
619
+ def above_http_content_limit(value)
620
+ @http_content_length_limit&.< value
621
+ end
597
622
  end
598
623
  end
@@ -115,6 +115,11 @@ module Puma
115
115
 
116
116
  while restart_server.pop
117
117
  server_thread = server.run
118
+
119
+ if @log_writer.debug? && index == 0
120
+ debug_loaded_extensions "Loaded Extensions - worker 0:"
121
+ end
122
+
118
123
  stat_thread ||= Thread.new(@worker_write) do |io|
119
124
  Puma.set_thread_name "stat pld"
120
125
  base_payload = "p#{Process.pid}"
data/lib/puma/cluster.rb CHANGED
@@ -467,6 +467,7 @@ module Puma
467
467
  @events.fire(:ping!, w)
468
468
  if !booted && @workers.none? {|worker| worker.last_status.empty?}
469
469
  @events.fire_on_booted!
470
+ debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
470
471
  booted = true
471
472
  end
472
473
  end
@@ -476,6 +477,7 @@ module Puma
476
477
  end
477
478
  if in_phased_restart && workers_not_booted.zero?
478
479
  @events.fire_on_booted!
480
+ debug_loaded_extensions("Loaded Extensions - master:") if @log_writer.debug?
479
481
  in_phased_restart = false
480
482
  end
481
483
 
@@ -3,7 +3,7 @@
3
3
  module Puma
4
4
  # Rack::CommonLogger forwards every request to the given +app+, and
5
5
  # logs a line in the
6
- # {Apache common log format}[https://httpd.apache.org/docs/1.3/logs.html#common]
6
+ # {Apache common log format}[https://httpd.apache.org/docs/2.4/logs.html#common]
7
7
  # to the +logger+.
8
8
  #
9
9
  # If +logger+ is nil, CommonLogger will fall back +rack.errors+, which is
@@ -16,7 +16,7 @@ module Puma
16
16
  # (which is called without arguments in order to make the error appear for
17
17
  # sure)
18
18
  class CommonLogger
19
- # Common Log Format: https://httpd.apache.org/docs/1.3/logs.html#common
19
+ # Common Log Format: https://httpd.apache.org/docs/2.4/logs.html#common
20
20
  #
21
21
  # lilith.local - - [07/Aug/2006 23:58:02 -0400] "GET / HTTP/1.1" 500 -
22
22
  #
@@ -25,10 +25,17 @@ module Puma
25
25
 
26
26
  HIJACK_FORMAT = %{%s - %s [%s] "%s %s%s %s" HIJACKED -1 %0.4f\n}
27
27
 
28
- CONTENT_LENGTH = 'Content-Length'.freeze
29
- PATH_INFO = 'PATH_INFO'.freeze
30
- QUERY_STRING = 'QUERY_STRING'.freeze
31
- REQUEST_METHOD = 'REQUEST_METHOD'.freeze
28
+ LOG_TIME_FORMAT = '%d/%b/%Y:%H:%M:%S %z'
29
+
30
+ CONTENT_LENGTH = 'Content-Length' # should be lower case from app,
31
+ # Util::HeaderHash allows mixed
32
+ HTTP_VERSION = Const::HTTP_VERSION
33
+ HTTP_X_FORWARDED_FOR = Const::HTTP_X_FORWARDED_FOR
34
+ PATH_INFO = Const::PATH_INFO
35
+ QUERY_STRING = Const::QUERY_STRING
36
+ REMOTE_ADDR = Const::REMOTE_ADDR
37
+ REMOTE_USER = 'REMOTE_USER'
38
+ REQUEST_METHOD = Const::REQUEST_METHOD
32
39
 
33
40
  def initialize(app, logger=nil)
34
41
  @app = app
@@ -57,13 +64,13 @@ module Puma
57
64
  now = Time.now
58
65
 
59
66
  msg = HIJACK_FORMAT % [
60
- env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
61
- env["REMOTE_USER"] || "-",
62
- now.strftime("%d/%b/%Y %H:%M:%S"),
67
+ env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
68
+ env[REMOTE_USER] || "-",
69
+ now.strftime(LOG_TIME_FORMAT),
63
70
  env[REQUEST_METHOD],
64
71
  env[PATH_INFO],
65
72
  env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
66
- env["HTTP_VERSION"],
73
+ env[HTTP_VERSION],
67
74
  now - began_at ]
68
75
 
69
76
  write(msg)
@@ -74,13 +81,13 @@ module Puma
74
81
  length = extract_content_length(header)
75
82
 
76
83
  msg = FORMAT % [
77
- env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-",
78
- env["REMOTE_USER"] || "-",
79
- now.strftime("%d/%b/%Y:%H:%M:%S %z"),
84
+ env[HTTP_X_FORWARDED_FOR] || env[REMOTE_ADDR] || "-",
85
+ env[REMOTE_USER] || "-",
86
+ now.strftime(LOG_TIME_FORMAT),
80
87
  env[REQUEST_METHOD],
81
88
  env[PATH_INFO],
82
89
  env[QUERY_STRING].empty? ? "" : "?#{env[QUERY_STRING]}",
83
- env["HTTP_VERSION"],
90
+ env[HTTP_VERSION],
84
91
  status.to_s[0..3],
85
92
  length,
86
93
  now - began_at ]
@@ -157,6 +157,7 @@ module Puma
157
157
  reaping_time: 1,
158
158
  remote_address: :socket,
159
159
  silence_single_worker_warning: false,
160
+ silence_fork_callback_warning: false,
160
161
  tag: File.basename(Dir.getwd),
161
162
  tcp_host: '0.0.0.0'.freeze,
162
163
  tcp_port: 9292,
@@ -167,6 +168,7 @@ module Puma
167
168
  worker_shutdown_timeout: 30,
168
169
  worker_timeout: 60,
169
170
  workers: 0,
171
+ http_content_length_limit: nil
170
172
  }
171
173
 
172
174
  def initialize(user_options={}, default_options = {}, &block)
data/lib/puma/const.rb CHANGED
@@ -99,8 +99,8 @@ module Puma
99
99
  # too taxing on performance.
100
100
  module Const
101
101
 
102
- PUMA_VERSION = VERSION = "6.0.2"
103
- CODE_NAME = "Sunflower"
102
+ PUMA_VERSION = VERSION = "6.2.1"
103
+ CODE_NAME = "Speaking of Now"
104
104
 
105
105
  PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
106
106
 
data/lib/puma/detect.rb CHANGED
@@ -17,6 +17,8 @@ module Puma
17
17
  IS_WINDOWS = !!(RUBY_PLATFORM =~ /mswin|ming|cygwin/) ||
18
18
  IS_JRUBY && RUBY_DESCRIPTION.include?('mswin')
19
19
 
20
+ IS_LINUX = !(IS_OSX || IS_WINDOWS)
21
+
20
22
  # @version 5.2.0
21
23
  IS_MRI = (RUBY_ENGINE == 'ruby' || RUBY_ENGINE.nil?)
22
24
 
data/lib/puma/dsl.rb CHANGED
@@ -65,6 +65,7 @@ module Puma
65
65
 
66
66
  ca_additions = "&ca=#{Puma::Util.escape(opts[:ca])}" if ['peer', 'force_peer'].include?(verify)
67
67
 
68
+ low_latency_str = opts.key?(:low_latency) ? "&low_latency=#{opts[:low_latency]}" : ''
68
69
  backlog_str = opts[:backlog] ? "&backlog=#{Integer(opts[:backlog])}" : ''
69
70
 
70
71
  if defined?(JRUBY_VERSION)
@@ -114,7 +115,7 @@ module Puma
114
115
  end
115
116
 
116
117
  "ssl://#{host}:#{port}?#{cert_flags}#{key_flags}#{ssl_cipher_filter}" \
117
- "#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}"
118
+ "#{reuse_flag}&verify_mode=#{verify}#{tls_str}#{ca_additions}#{v_flags}#{backlog_str}#{low_latency_str}"
118
119
  end
119
120
  end
120
121
 
@@ -418,6 +419,11 @@ module Puma
418
419
  @options[:log_requests] = which
419
420
  end
420
421
 
422
+ # Pass in a custom logging class instance
423
+ def custom_logger(custom_logger)
424
+ @options[:custom_logger] = custom_logger
425
+ end
426
+
421
427
  # Show debugging info
422
428
  #
423
429
  def debug
@@ -584,6 +590,11 @@ module Puma
584
590
  @options[:silence_single_worker_warning] = true
585
591
  end
586
592
 
593
+ # Disable warning message when running single mode with callback hook defined.
594
+ def silence_fork_callback_warning
595
+ @options[:silence_fork_callback_warning] = true
596
+ end
597
+
587
598
  # Code to run immediately before master process
588
599
  # forks workers (once on boot). These hooks can block if necessary
589
600
  # to wait for background operations unknown to Puma to finish before
@@ -599,6 +610,8 @@ module Puma
599
610
  # puts "Starting workers..."
600
611
  # end
601
612
  def before_fork(&block)
613
+ warn_if_in_single_mode('before_fork')
614
+
602
615
  @options[:before_fork] ||= []
603
616
  @options[:before_fork] << block
604
617
  end
@@ -614,6 +627,8 @@ module Puma
614
627
  # puts 'Before worker boot...'
615
628
  # end
616
629
  def on_worker_boot(key = nil, &block)
630
+ warn_if_in_single_mode('on_worker_boot')
631
+
617
632
  process_hook :before_worker_boot, key, block, 'on_worker_boot'
618
633
  end
619
634
 
@@ -630,6 +645,8 @@ module Puma
630
645
  # puts 'On worker shutdown...'
631
646
  # end
632
647
  def on_worker_shutdown(key = nil, &block)
648
+ warn_if_in_single_mode('on_worker_shutdown')
649
+
633
650
  process_hook :before_worker_shutdown, key, block, 'on_worker_shutdown'
634
651
  end
635
652
 
@@ -644,6 +661,8 @@ module Puma
644
661
  # puts 'Before worker fork...'
645
662
  # end
646
663
  def on_worker_fork(&block)
664
+ warn_if_in_single_mode('on_worker_fork')
665
+
647
666
  process_hook :before_worker_fork, nil, block, 'on_worker_fork'
648
667
  end
649
668
 
@@ -658,11 +677,23 @@ module Puma
658
677
  # puts 'After worker fork...'
659
678
  # end
660
679
  def after_worker_fork(&block)
680
+ warn_if_in_single_mode('after_worker_fork')
681
+
661
682
  process_hook :after_worker_fork, nil, block, 'after_worker_fork'
662
683
  end
663
684
 
664
685
  alias_method :after_worker_boot, :after_worker_fork
665
686
 
687
+ # Code to run after puma is booted (works for both: single and clustered)
688
+ #
689
+ # @example
690
+ # on_booted do
691
+ # puts 'After booting...'
692
+ # end
693
+ def on_booted(&block)
694
+ @config.options[:events].on_booted(&block)
695
+ end
696
+
666
697
  # When `fork_worker` is enabled, code to run in Worker 0
667
698
  # before all other workers are re-forked from this process,
668
699
  # after the server has temporarily stopped serving requests
@@ -1022,6 +1053,19 @@ module Puma
1022
1053
  @options[:mutate_stdout_and_stderr_to_sync_on_write] = enabled
1023
1054
  end
1024
1055
 
1056
+ # Specify how big the request payload should be, in bytes.
1057
+ # This limit is compared against Content-Length HTTP header.
1058
+ # If the payload size (CONTENT_LENGTH) is larger than http_content_length_limit,
1059
+ # HTTP 413 status code is returned.
1060
+ #
1061
+ # When no Content-Length http header is present, it is compared against the
1062
+ # size of the body of the request.
1063
+ #
1064
+ # The default value for http_content_length_limit is nil.
1065
+ def http_content_length_limit(limit)
1066
+ @options[:http_content_length_limit] = limit
1067
+ end
1068
+
1025
1069
  private
1026
1070
 
1027
1071
  # To avoid adding cert_pem and key_pem as URI params, we store them on the
@@ -1049,7 +1093,20 @@ module Puma
1049
1093
  elsif key.nil?
1050
1094
  @options[options_key] << block
1051
1095
  else
1052
- raise "'#{method}' key must be String or Symbol"
1096
+ raise "'#{meth}' key must be String or Symbol"
1097
+ end
1098
+ end
1099
+
1100
+ def warn_if_in_single_mode(hook_name)
1101
+ return if @options[:silence_fork_callback_warning]
1102
+
1103
+ if (@options[:workers] || 0) == 0
1104
+ log_string =
1105
+ "Warning: You specified code to run in a `#{hook_name}` block, " \
1106
+ "but Puma is not configured to run in cluster mode (worker count > 0 ), " \
1107
+ "so your `#{hook_name}` block did not run"
1108
+
1109
+ LogWriter.stdio.log(log_string)
1053
1110
  end
1054
1111
  end
1055
1112
  end
@@ -102,7 +102,8 @@ module Puma
102
102
  @ioerr.is_a?(IO) and @ioerr.wait_writable(1)
103
103
  @ioerr.write "#{w_str}\n"
104
104
  @ioerr.flush unless @ioerr.sync
105
- rescue Errno::EPIPE, Errno::EBADF, IOError
105
+ rescue Errno::EPIPE, Errno::EBADF, IOError, Errno::EINVAL
106
+ # 'Invalid argument' (Errno::EINVAL) may be raised by flush
106
107
  end
107
108
  end
108
109
  rescue ThreadError
data/lib/puma/launcher.rb CHANGED
@@ -59,6 +59,13 @@ module Puma
59
59
 
60
60
  @environment = conf.environment
61
61
 
62
+ # Load the systemd integration if we detect systemd's NOTIFY_SOCKET.
63
+ # Skip this on JRuby though, because it is incompatible with the systemd
64
+ # integration due to https://github.com/jruby/jruby/issues/6504
65
+ if ENV["NOTIFY_SOCKET"] && !Puma.jruby?
66
+ @config.plugins.create('systemd')
67
+ end
68
+
62
69
  if @config.options[:bind_to_activated_sockets]
63
70
  @config.options[:binds] = @binder.synthesize_binds_from_activated_fs(
64
71
  @config.options[:binds],
@@ -72,6 +79,8 @@ module Puma
72
79
  @log_writer.formatter = LogWriter::PidFormatter.new if clustered?
73
80
  @log_writer.formatter = options[:log_formatter] if @options[:log_formatter]
74
81
 
82
+ @log_writer.custom_logger = options[:custom_logger] if @options[:custom_logger]
83
+
75
84
  generate_restart_data
76
85
 
77
86
  if clustered? && !Puma.forkable?
@@ -180,7 +189,6 @@ module Puma
180
189
 
181
190
  setup_signals
182
191
  set_process_title
183
- integrate_with_systemd
184
192
 
185
193
  # This blocks until the server is stopped
186
194
  @runner.run
@@ -311,27 +319,6 @@ module Puma
311
319
  @runner.reload_worker_directory if @runner.respond_to?(:reload_worker_directory)
312
320
  end
313
321
 
314
- # Puma's systemd integration allows Puma to inform systemd:
315
- # 1. when it has successfully started
316
- # 2. when it is starting shutdown
317
- # 3. periodically for a liveness check with a watchdog thread
318
- def integrate_with_systemd
319
- return unless ENV["NOTIFY_SOCKET"]
320
-
321
- begin
322
- require_relative 'systemd'
323
- rescue LoadError
324
- log "Systemd integration failed. It looks like you're trying to use systemd notify but don't have sd_notify gem installed"
325
- return
326
- end
327
-
328
- log "* Enabling systemd notification integration"
329
-
330
- systemd = Systemd.new(@log_writer, @events)
331
- systemd.hook_events
332
- systemd.start_watchdog
333
- end
334
-
335
322
  def log(str)
336
323
  @log_writer.log(str)
337
324
  end
@@ -28,11 +28,12 @@ module Puma
28
28
  attr_reader :stdout,
29
29
  :stderr
30
30
 
31
- attr_accessor :formatter
31
+ attr_accessor :formatter, :custom_logger
32
32
 
33
33
  # Create a LogWriter that prints to +stdout+ and +stderr+.
34
34
  def initialize(stdout, stderr)
35
35
  @formatter = DefaultFormatter.new
36
+ @custom_logger = nil
36
37
  @stdout = stdout
37
38
  @stderr = stderr
38
39
 
@@ -59,7 +60,11 @@ module Puma
59
60
 
60
61
  # Write +str+ to +@stdout+
61
62
  def log(str)
62
- internal_write "#{@formatter.call str}\n"
63
+ if @custom_logger&.respond_to?(:write)
64
+ @custom_logger.write(format(str))
65
+ else
66
+ internal_write "#{@formatter.call str}\n"
67
+ end
63
68
  end
64
69
 
65
70
  def write(str)
@@ -73,13 +78,18 @@ module Puma
73
78
  @stdout.is_a?(IO) and @stdout.wait_writable(1)
74
79
  @stdout.write w_str
75
80
  @stdout.flush unless @stdout.sync
76
- rescue Errno::EPIPE, Errno::EBADF, IOError
81
+ rescue Errno::EPIPE, Errno::EBADF, IOError, Errno::EINVAL
82
+ # 'Invalid argument' (Errno::EINVAL) may be raised by flush
77
83
  end
78
84
  end
79
85
  rescue ThreadError
80
86
  end
81
87
  private :internal_write
82
88
 
89
+ def debug?
90
+ @debug
91
+ end
92
+
83
93
  def debug(str)
84
94
  log("% #{str}") if @debug
85
95
  end