puma 7.2.0-java → 8.0.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/puma/const.rb CHANGED
@@ -100,8 +100,8 @@ module Puma
100
100
  # too taxing on performance.
101
101
  module Const
102
102
 
103
- PUMA_VERSION = VERSION = "7.2.0"
104
- CODE_NAME = "On The Corner"
103
+ PUMA_VERSION = VERSION = "8.0.1"
104
+ CODE_NAME = "Into the Arena"
105
105
 
106
106
  PUMA_SERVER_STRING = ["puma", PUMA_VERSION, CODE_NAME].join(" ").freeze
107
107
 
@@ -16,7 +16,7 @@ module Puma
16
16
  'gc' => nil,
17
17
  'gc-stats' => nil,
18
18
  'halt' => 'SIGQUIT',
19
- 'info' => 'SIGINFO',
19
+ 'info' => Puma.backtrace_signal,
20
20
  'phased-restart' => 'SIGUSR1',
21
21
  'refork' => 'SIGURG',
22
22
  'reload-worker-directory' => nil,
data/lib/puma/detect.rb CHANGED
@@ -35,6 +35,13 @@ module Puma
35
35
  IS_WINDOWS
36
36
  end
37
37
 
38
+ BACKTRACE_SIGNAL =
39
+ if Signal.list.key?("INFO")
40
+ "SIGINFO"
41
+ elsif Signal.list.key?("PWR")
42
+ "SIGPWR"
43
+ end
44
+
38
45
  # @version 5.0.0
39
46
  def self.mri?
40
47
  IS_MRI
@@ -44,4 +51,8 @@ module Puma
44
51
  def self.forkable?
45
52
  HAS_FORK
46
53
  end
54
+
55
+ def self.backtrace_signal
56
+ BACKTRACE_SIGNAL
57
+ end
47
58
  end
data/lib/puma/dsl.rb CHANGED
@@ -155,7 +155,7 @@ module Puma
155
155
  end
156
156
 
157
157
  def default_host
158
- @options[:default_host] || Configuration::DEFAULTS[:tcp_host]
158
+ @options[:default_host] || Configuration.default_tcp_host
159
159
  end
160
160
 
161
161
  def inject(&blk)
@@ -260,7 +260,8 @@ module Puma
260
260
  # accepted protocols. Multiple urls can be bound to, calling +bind+ does
261
261
  # not overwrite previous bindings.
262
262
  #
263
- # The default is "tcp://0.0.0.0:9292".
263
+ # The default is "tcp://[::]:9292" when IPv6 interfaces are available,
264
+ # otherwise "tcp://0.0.0.0:9292".
264
265
  #
265
266
  # You can use query parameters within the url to specify options:
266
267
  #
@@ -279,7 +280,7 @@ module Puma
279
280
  # @example SSL cert for mutual TLS (mTLS)
280
281
  # bind 'ssl://127.0.0.1:9292?key=key.key&cert=cert.pem&ca=ca.pem&verify_mode=force_peer'
281
282
  # @example Disable optimization for low latency
282
- # bind 'tcp://0.0.0.0:9292?low_latency=false'
283
+ # bind 'tcp://[::]:9292?low_latency=false'
283
284
  # @example Socket permissions
284
285
  # bind 'unix:///var/run/puma.sock?umask=0111'
285
286
  #
@@ -349,7 +350,7 @@ module Puma
349
350
 
350
351
  # Define how long persistent connections can be idle before Puma closes them.
351
352
  #
352
- # The default is 20 seconds.
353
+ # The default is 65 seconds.
353
354
  #
354
355
  # @example
355
356
  # persistent_timeout 30
@@ -595,6 +596,29 @@ module Puma
595
596
  @options[:max_threads] = max
596
597
  end
597
598
 
599
+ # Configure the max number of IO threads.
600
+ #
601
+ # When request handlers know the current requests will no longer use a significant amount
602
+ # of CPU, they can mark the current request as IO bound using <tt>env["puma.mark_as_io_bound"]</tt>.
603
+ #
604
+ # Threads marked as IO bound are allowed to go over the max thread limit.
605
+ #
606
+ # @example
607
+ # threads 5
608
+ # max_io_threads 5
609
+ #
610
+ # The above example allows for 5 regular threads and 5 IO threads to process requests concurrently.
611
+ # Any IO thread over the limit is counted as a regular thread, hence the above configuration also
612
+ # allows for 3 regular threads and 7 IO threads for example.
613
+ def max_io_threads(max)
614
+ max = Integer(max)
615
+ if max < 0
616
+ raise "The maximum number of IO threads (#{max}) must be a positive number"
617
+ end
618
+
619
+ @options[:max_io_threads] = max
620
+ end
621
+
598
622
  # Instead of using +bind+ and manually constructing a URI like:
599
623
  #
600
624
  # bind 'ssl://127.0.0.1:9292?key=key_path&cert=cert_path'
@@ -727,6 +751,44 @@ module Puma
727
751
  @options[:silence_fork_callback_warning] = true
728
752
  end
729
753
 
754
+ # Code to run only in single mode.
755
+ # Runs after all config files are loaded.
756
+ #
757
+ # This can be called multiple times.
758
+ #
759
+ # @note Single mode only.
760
+ #
761
+ # @example
762
+ # single do
763
+ # silence_fork_callback_warning
764
+ # end
765
+ #
766
+ def single(&block)
767
+ raise ArgumentError, "A block must be provided to `single`" unless block
768
+
769
+ @options[:single] ||= []
770
+ @options[:single] << block
771
+ end
772
+
773
+ # Code to run only in cluster mode.
774
+ # Runs after all config files are loaded.
775
+ #
776
+ # This can be called multiple times.
777
+ #
778
+ # @note Cluster mode only.
779
+ #
780
+ # @example
781
+ # cluster do
782
+ # prune_bundler
783
+ # end
784
+ #
785
+ def cluster(&block)
786
+ raise ArgumentError, "A block must be provided to `cluster`" unless block
787
+
788
+ @options[:cluster] ||= []
789
+ @options[:cluster] << block
790
+ end
791
+
730
792
  # Code to run immediately before master process
731
793
  # forks workers (once on boot). These hooks can block if necessary
732
794
  # to wait for background operations unknown to Puma to finish before
@@ -1040,6 +1102,7 @@ module Puma
1040
1102
  # new Bundler context and thus can float around as the release
1041
1103
  # dictates.
1042
1104
  #
1105
+ # @note Cluster mode only.
1043
1106
  # @note This is incompatible with +preload_app!+.
1044
1107
  # @note This is only supported for RubyGems 2.2+
1045
1108
  #
@@ -1145,7 +1208,7 @@ module Puma
1145
1208
 
1146
1209
  # Change the default worker timeout for booting.
1147
1210
  #
1148
- # The default is the value of `worker_timeout`.
1211
+ # The default is 60 seconds.
1149
1212
  #
1150
1213
  # @note Cluster mode only.
1151
1214
  #
@@ -1227,11 +1290,14 @@ module Puma
1227
1290
  # threads will be written to $stdout. This can help figure
1228
1291
  # out why shutdown is hanging.
1229
1292
  #
1230
- def shutdown_debug(val=true)
1231
- @options[:shutdown_debug] = val
1293
+ # If `on_force` is true, the backtraces will be written only
1294
+ # when the shutdown is forced i.e. not graceful.
1295
+ #
1296
+ # @see force_shutdown_after
1297
+ def shutdown_debug(val = true, on_force: false)
1298
+ @options[:shutdown_debug] = val && on_force ? :on_force : val
1232
1299
  end
1233
1300
 
1234
-
1235
1301
  # Maximum delay of worker accept loop.
1236
1302
  #
1237
1303
  # Attempts to route traffic to less-busy workers by causing a busy worker to delay
@@ -28,14 +28,12 @@ module Puma
28
28
 
29
29
  log '* Pruning Bundler environment'
30
30
  home = ENV['GEM_HOME']
31
- bundle_gemfile = Bundler.original_env['BUNDLE_GEMFILE']
32
- bundle_app_config = Bundler.original_env['BUNDLE_APP_CONFIG']
31
+ original_bundle_env = Bundler.original_env.select { |k, v| k.start_with?('BUNDLE_') && v }
33
32
 
34
33
  with_unbundled_env do
35
34
  ENV['GEM_HOME'] = home
36
- ENV['BUNDLE_GEMFILE'] = bundle_gemfile
37
35
  ENV['PUMA_BUNDLER_PRUNED'] = '1'
38
- ENV["BUNDLE_APP_CONFIG"] = bundle_app_config
36
+ original_bundle_env.each { |k, v| ENV[k] = v }
39
37
  args = [Gem.ruby, puma_wild_path, '-I', dirs.join(':')] + @original_argv
40
38
  # Defaults to true which breaks socket activation
41
39
  args += [{:close_others => false}]
data/lib/puma/launcher.rb CHANGED
@@ -476,8 +476,8 @@ module Puma
476
476
  end
477
477
 
478
478
  begin
479
- unless Puma.jruby? # INFO in use by JVM already
480
- Signal.trap "SIGINFO" do
479
+ if Puma.backtrace_signal
480
+ Signal.trap Puma.backtrace_signal do
481
481
  thread_status do |name, backtrace|
482
482
  @log_writer.log(name)
483
483
  @log_writer.log(backtrace.map { |bt| " #{bt}" })
@@ -485,8 +485,7 @@ module Puma
485
485
  end
486
486
  end
487
487
  rescue Exception
488
- # Not going to log this one, as SIGINFO is *BSD only and would be pretty annoying
489
- # to see this constantly on Linux.
488
+ log "*** SIGINFO/SIGPWR not implemented, signal based backtrace unavailable!"
490
489
  end
491
490
  end
492
491
 
@@ -90,8 +90,14 @@ module Puma
90
90
  @debug
91
91
  end
92
92
 
93
- def debug(str)
94
- log("% #{str}") if @debug
93
+ def debug(str=nil, &block)
94
+ if @debug
95
+ if block_given?
96
+ log("% #{yield}")
97
+ else
98
+ log("% #{str}")
99
+ end
100
+ end
95
101
  end
96
102
 
97
103
  # Write +str+ to +@stderr+
Binary file
@@ -12,7 +12,7 @@ module Puma
12
12
  # #handle_request, which is called in Server#process_client.
13
13
  # @version 5.0.3
14
14
  #
15
- module Request # :nodoc:
15
+ module Response # :nodoc:
16
16
 
17
17
  # Single element array body: smaller bodies are written to io_buffer first,
18
18
  # then a single write from io_buffer. Larger sizes are written separately.
@@ -46,10 +46,11 @@ module Puma
46
46
  # elsewhere, i.e. the connection has been hijacked by the Rack application.
47
47
  #
48
48
  # Finally, it'll return +true+ on keep-alive connections.
49
+ # @param processor [Puma::ThreadPool::ProcessorThread]
49
50
  # @param client [Puma::Client]
50
51
  # @param requests [Integer]
51
52
  # @return [:close, :keep_alive, :async]
52
- def handle_request(client, requests)
53
+ def handle_request(processor, client, requests)
53
54
  env = client.env
54
55
  io_buffer = client.io_buffer
55
56
  socket = client.io # io may be a MiniSSL::Socket
@@ -58,24 +59,6 @@ module Puma
58
59
 
59
60
  return :close if closed_socket?(socket)
60
61
 
61
- if client.http_content_length_limit_exceeded
62
- return prepare_response(413, {}, ["Payload Too Large"], requests, client)
63
- end
64
-
65
- normalize_env env, client
66
-
67
- env[PUMA_SOCKET] = socket
68
-
69
- if env[HTTPS_KEY] && socket.peercert
70
- env[PUMA_PEERCERT] = socket.peercert
71
- end
72
-
73
- env[HIJACK_P] = true
74
- env[HIJACK] = client.method :full_hijack
75
-
76
- env[RACK_INPUT] = client.body
77
- env[RACK_URL_SCHEME] ||= default_server_port(env) == PORT_443 ? HTTPS : HTTP
78
-
79
62
  if @early_hints
80
63
  env[EARLY_HINTS] = lambda { |headers|
81
64
  begin
@@ -89,22 +72,11 @@ module Puma
89
72
  }
90
73
  end
91
74
 
92
- req_env_post_parse env
93
-
94
- # A rack extension. If the app writes #call'ables to this
95
- # array, we will invoke them when the request is done.
96
- #
97
- env[RACK_AFTER_REPLY] ||= []
98
- env[RACK_RESPONSE_FINISHED] ||= []
75
+ env["puma.mark_as_io_bound"] = -> { processor.mark_as_io_thread! }
99
76
 
100
77
  begin
101
- if @supported_http_methods == :any || @supported_http_methods.key?(env[REQUEST_METHOD])
102
- status, headers, app_body = @thread_pool.with_force_shutdown do
103
- @app.call(env)
104
- end
105
- else
106
- @log_writer.log "Unsupported HTTP method used: #{env[REQUEST_METHOD]}"
107
- status, headers, app_body = [501, {}, ["#{env[REQUEST_METHOD]} method is not supported"]]
78
+ status, headers, app_body = @thread_pool.with_force_shutdown do
79
+ @app.call(env)
108
80
  end
109
81
 
110
82
  # app_body needs to always be closed, hold value in case lowlevel_error
@@ -136,7 +108,6 @@ module Puma
136
108
  prepare_response(status, headers, res_body, requests, client)
137
109
  ensure
138
110
  io_buffer.reset
139
- uncork_socket client.io
140
111
  app_body.close if app_body.respond_to? :close
141
112
  client&.tempfile_close
142
113
  if after_reply = env[RACK_AFTER_REPLY]
@@ -245,7 +216,8 @@ module Puma
245
216
 
246
217
  io_buffer << LINE_END
247
218
  fast_write_str socket, io_buffer.read_and_reset
248
- socket.flush
219
+
220
+ uncork_socket socket.flush
249
221
  return keep_alive ? :keep_alive : :close
250
222
  end
251
223
  else
@@ -264,34 +236,18 @@ module Puma
264
236
  # response_hijack.call
265
237
  if response_hijack
266
238
  fast_write_str socket, io_buffer.read_and_reset
267
- uncork_socket socket
239
+ uncork_socket socket.flush
268
240
  response_hijack.call socket
269
241
  return :async
270
242
  end
271
243
 
272
244
  fast_write_response socket, body, io_buffer, chunked, content_length.to_i
273
245
  body.close if close_body
246
+
274
247
  # if we're shutting down, close keep_alive connections
275
248
  !shutting_down? && keep_alive ? :keep_alive : :close
276
249
  end
277
250
 
278
- HTTP_ON_VALUES = { "on" => true, HTTPS => true }
279
- private_constant :HTTP_ON_VALUES
280
-
281
- # @param env [Hash] see Puma::Client#env, from request
282
- # @return [Puma::Const::PORT_443,Puma::Const::PORT_80]
283
- #
284
- def default_server_port(env)
285
- if HTTP_ON_VALUES[env[HTTPS_KEY]] ||
286
- env[HTTP_X_FORWARDED_PROTO]&.start_with?(HTTPS) ||
287
- env[HTTP_X_FORWARDED_SCHEME] == HTTPS ||
288
- env[HTTP_X_FORWARDED_SSL] == "on"
289
- PORT_443
290
- else
291
- PORT_80
292
- end
293
- end
294
-
295
251
  # Used to write 'early hints', 'no body' responses, 'hijacked' responses,
296
252
  # and body segments (called by `fast_write_response`).
297
253
  # Writes a string to a socket (normally `Client#io`) using `write_nonblock`.
@@ -406,6 +362,7 @@ module Puma
406
362
  end
407
363
  end
408
364
  socket.flush
365
+ uncork_socket socket
409
366
  rescue Errno::EAGAIN, Errno::EWOULDBLOCK
410
367
  raise ConnectionError, SOCKET_WRITE_ERR_MSG
411
368
  rescue Errno::EPIPE, SystemCallError, IOError
@@ -414,85 +371,6 @@ module Puma
414
371
 
415
372
  private :fast_write_str, :fast_write_response
416
373
 
417
- # Given a Hash +env+ for the request read from +client+, add
418
- # and fixup keys to comply with Rack's env guidelines.
419
- # @param env [Hash] see Puma::Client#env, from request
420
- # @param client [Puma::Client] only needed for Client#peerip
421
- #
422
- def normalize_env(env, client)
423
- if host = env[HTTP_HOST]
424
- # host can be a hostname, ipv4 or bracketed ipv6. Followed by an optional port.
425
- if colon = host.rindex("]:") # IPV6 with port
426
- env[SERVER_NAME] = host[0, colon+1]
427
- env[SERVER_PORT] = host[colon+2, host.bytesize]
428
- elsif !host.start_with?("[") && colon = host.index(":") # not hostname or IPV4 with port
429
- env[SERVER_NAME] = host[0, colon]
430
- env[SERVER_PORT] = host[colon+1, host.bytesize]
431
- else
432
- env[SERVER_NAME] = host
433
- env[SERVER_PORT] = default_server_port(env)
434
- end
435
- else
436
- env[SERVER_NAME] = LOCALHOST
437
- env[SERVER_PORT] = default_server_port(env)
438
- end
439
-
440
- unless env[REQUEST_PATH]
441
- # it might be a dumbass full host request header
442
- uri = begin
443
- URI.parse(env[REQUEST_URI])
444
- rescue URI::InvalidURIError
445
- raise Puma::HttpParserError
446
- end
447
- env[REQUEST_PATH] = uri.path
448
-
449
- # A nil env value will cause a LintError (and fatal errors elsewhere),
450
- # so only set the env value if there actually is a value.
451
- env[QUERY_STRING] = uri.query if uri.query
452
- end
453
-
454
- env[PATH_INFO] = env[REQUEST_PATH].to_s # #to_s in case it's nil
455
-
456
- # From https://www.ietf.org/rfc/rfc3875 :
457
- # "Script authors should be aware that the REMOTE_ADDR and
458
- # REMOTE_HOST meta-variables (see sections 4.1.8 and 4.1.9)
459
- # may not identify the ultimate source of the request.
460
- # They identify the client for the immediate request to the
461
- # server; that client may be a proxy, gateway, or other
462
- # intermediary acting on behalf of the actual source client."
463
- #
464
-
465
- unless env.key?(REMOTE_ADDR)
466
- begin
467
- addr = client.peerip
468
- rescue Errno::ENOTCONN
469
- # Client disconnects can result in an inability to get the
470
- # peeraddr from the socket; default to unspec.
471
- if client.peer_family == Socket::AF_INET6
472
- addr = UNSPECIFIED_IPV6
473
- else
474
- addr = UNSPECIFIED_IPV4
475
- end
476
- end
477
-
478
- # Set unix socket addrs to localhost
479
- if addr.empty?
480
- if client.peer_family == Socket::AF_INET6
481
- addr = LOCALHOST_IPV6
482
- else
483
- addr = LOCALHOST_IPV4
484
- end
485
- end
486
-
487
- env[REMOTE_ADDR] = addr
488
- end
489
-
490
- # The legacy HTTP_VERSION header can be sent as a client header.
491
- # Rack v4 may remove using HTTP_VERSION. If so, remove this line.
492
- env[HTTP_VERSION] = env[SERVER_PROTOCOL] if @env_set_http_version
493
- end
494
- private :normalize_env
495
-
496
374
  # @param header_key [#to_s]
497
375
  # @return [Boolean]
498
376
  #
@@ -506,56 +384,6 @@ module Puma
506
384
  def illegal_header_value?(header_value)
507
385
  !!(ILLEGAL_HEADER_VALUE_REGEX =~ header_value.to_s)
508
386
  end
509
- private :illegal_header_key?, :illegal_header_value?
510
-
511
- # Fixup any headers with `,` in the name to have `_` now. We emit
512
- # headers with `,` in them during the parse phase to avoid ambiguity
513
- # with the `-` to `_` conversion for critical headers. But here for
514
- # compatibility, we'll convert them back. This code is written to
515
- # avoid allocation in the common case (ie there are no headers
516
- # with `,` in their names), that's why it has the extra conditionals.
517
- #
518
- # @note If a normalized version of a `,` header already exists, we ignore
519
- # the `,` version. This prevents clobbering headers managed by proxies
520
- # but not by clients (Like X-Forwarded-For).
521
- #
522
- # @param env [Hash] see Puma::Client#env, from request, modifies in place
523
- # @version 5.0.3
524
- #
525
- def req_env_post_parse(env)
526
- to_delete = nil
527
- to_add = nil
528
-
529
- env.each do |k,v|
530
- if k.start_with?("HTTP_") && k.include?(",") && !UNMASKABLE_HEADERS.key?(k)
531
- if to_delete
532
- to_delete << k
533
- else
534
- to_delete = [k]
535
- end
536
-
537
- new_k = k.tr(",", "_")
538
- if env.key?(new_k)
539
- next
540
- end
541
-
542
- unless to_add
543
- to_add = {}
544
- end
545
-
546
- to_add[new_k] = v
547
- end
548
- end
549
-
550
- if to_delete # rubocop:disable Style/SafeNavigation
551
- to_delete.each { |k| env.delete(k) }
552
- end
553
-
554
- if to_add
555
- env.merge! to_add
556
- end
557
- end
558
- private :req_env_post_parse
559
387
 
560
388
  # Used in the lambda for env[ `Puma::Const::EARLY_HINTS` ]
561
389
  # @param headers [Hash] the headers returned by the Rack application
@@ -652,7 +480,8 @@ module Puma
652
480
  headers.each do |k, vs|
653
481
  next if illegal_header_key?(k)
654
482
 
655
- case k.downcase
483
+ key = k.downcase
484
+ case key
656
485
  when CONTENT_LENGTH2
657
486
  next if illegal_header_value?(vs)
658
487
  # nil.to_i is 0, nil&.to_i is nil
@@ -679,10 +508,10 @@ module Puma
679
508
  if ary
680
509
  ary.each do |v|
681
510
  next if illegal_header_value?(v)
682
- io_buffer.append k.downcase, colon, v, line_ending
511
+ io_buffer.append key, colon, v, line_ending
683
512
  end
684
513
  else
685
- io_buffer.append k.downcase, colon, line_ending
514
+ io_buffer.append key, colon, line_ending
686
515
  end
687
516
  end
688
517