puma 5.4.0-java → 5.6.0-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.
Files changed (80) hide show
  1. checksums.yaml +4 -4
  2. data/History.md +96 -2
  3. data/LICENSE +0 -0
  4. data/README.md +47 -6
  5. data/bin/puma-wild +0 -0
  6. data/docs/architecture.md +49 -16
  7. data/docs/compile_options.md +4 -2
  8. data/docs/deployment.md +53 -52
  9. data/docs/fork_worker.md +0 -0
  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 +0 -0
  18. data/docs/plugins.md +15 -15
  19. data/docs/rails_dev_mode.md +2 -3
  20. data/docs/restart.md +6 -6
  21. data/docs/signals.md +11 -10
  22. data/docs/stats.md +8 -8
  23. data/docs/systemd.md +64 -67
  24. data/ext/puma_http11/PumaHttp11Service.java +0 -0
  25. data/ext/puma_http11/ext_help.h +0 -0
  26. data/ext/puma_http11/extconf.rb +12 -6
  27. data/ext/puma_http11/http11_parser.c +23 -10
  28. data/ext/puma_http11/http11_parser.h +0 -0
  29. data/ext/puma_http11/http11_parser.java.rl +0 -0
  30. data/ext/puma_http11/http11_parser.rl +0 -0
  31. data/ext/puma_http11/http11_parser_common.rl +1 -1
  32. data/ext/puma_http11/mini_ssl.c +54 -9
  33. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
  34. data/ext/puma_http11/org/jruby/puma/Http11.java +0 -0
  35. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +49 -47
  36. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +28 -43
  37. data/ext/puma_http11/puma_http11.c +1 -1
  38. data/lib/puma/app/status.rb +0 -0
  39. data/lib/puma/binder.rb +46 -4
  40. data/lib/puma/cli.rb +14 -4
  41. data/lib/puma/client.rb +46 -4
  42. data/lib/puma/cluster/worker.rb +7 -17
  43. data/lib/puma/cluster/worker_handle.rb +4 -0
  44. data/lib/puma/cluster.rb +29 -21
  45. data/lib/puma/commonlogger.rb +0 -0
  46. data/lib/puma/configuration.rb +4 -1
  47. data/lib/puma/const.rb +4 -5
  48. data/lib/puma/control_cli.rb +1 -1
  49. data/lib/puma/detect.rb +8 -2
  50. data/lib/puma/dsl.rb +98 -11
  51. data/lib/puma/error_logger.rb +0 -0
  52. data/lib/puma/events.rb +0 -0
  53. data/lib/puma/io_buffer.rb +0 -0
  54. data/lib/puma/jruby_restart.rb +0 -0
  55. data/lib/puma/json_serialization.rb +0 -0
  56. data/lib/puma/launcher.rb +4 -1
  57. data/lib/puma/minissl/context_builder.rb +8 -6
  58. data/lib/puma/minissl.rb +24 -23
  59. data/lib/puma/null_io.rb +0 -0
  60. data/lib/puma/plugin/tmp_restart.rb +0 -0
  61. data/lib/puma/plugin.rb +1 -1
  62. data/lib/puma/puma_http11.jar +0 -0
  63. data/lib/puma/queue_close.rb +0 -0
  64. data/lib/puma/rack/builder.rb +0 -0
  65. data/lib/puma/rack/urlmap.rb +0 -0
  66. data/lib/puma/rack_default.rb +0 -0
  67. data/lib/puma/reactor.rb +0 -0
  68. data/lib/puma/request.rb +47 -29
  69. data/lib/puma/runner.rb +22 -8
  70. data/lib/puma/server.rb +40 -37
  71. data/lib/puma/single.rb +0 -0
  72. data/lib/puma/state_file.rb +41 -7
  73. data/lib/puma/systemd.rb +0 -0
  74. data/lib/puma/thread_pool.rb +2 -2
  75. data/lib/puma/util.rb +7 -0
  76. data/lib/puma.rb +0 -0
  77. data/lib/rack/handler/puma.rb +0 -0
  78. data/tools/Dockerfile +1 -1
  79. data/tools/trickletest.rb +0 -0
  80. metadata +6 -6
data/lib/puma/runner.rb CHANGED
@@ -15,6 +15,16 @@ module Puma
15
15
  @app = nil
16
16
  @control = nil
17
17
  @started_at = Time.now
18
+ @wakeup = nil
19
+ end
20
+
21
+ def wakeup!
22
+ return unless @wakeup
23
+
24
+ @wakeup.write "!" unless @wakeup.closed?
25
+
26
+ rescue SystemCallError, IOError
27
+ Puma::Util.purge_interrupt_queue
18
28
  end
19
29
 
20
30
  def development?
@@ -59,7 +69,7 @@ module Puma
59
69
 
60
70
  control.binder.parse [str], self, 'Starting control server'
61
71
 
62
- control.run thread_name: 'control'
72
+ control.run thread_name: 'ctl'
63
73
  @control = control
64
74
  end
65
75
 
@@ -84,12 +94,13 @@ module Puma
84
94
  def output_header(mode)
85
95
  min_t = @options[:min_threads]
86
96
  max_t = @options[:max_threads]
97
+ environment = @options[:environment]
87
98
 
88
99
  log "Puma starting in #{mode} mode..."
89
100
  log "* Puma version: #{Puma::Const::PUMA_VERSION} (#{ruby_engine}) (\"#{Puma::Const::CODE_NAME}\")"
90
101
  log "* Min threads: #{min_t}"
91
102
  log "* Max threads: #{max_t}"
92
- log "* Environment: #{ENV['RACK_ENV']}"
103
+ log "* Environment: #{environment}"
93
104
 
94
105
  if mode == "cluster"
95
106
  log "* Master PID: #{Process.pid}"
@@ -108,9 +119,7 @@ module Puma
108
119
  append = @options[:redirect_append]
109
120
 
110
121
  if stdout
111
- unless Dir.exist?(File.dirname(stdout))
112
- raise "Cannot redirect STDOUT to #{stdout}"
113
- end
122
+ ensure_output_directory_exists(stdout, 'STDOUT')
114
123
 
115
124
  STDOUT.reopen stdout, (append ? "a" : "w")
116
125
  STDOUT.puts "=== puma startup: #{Time.now} ==="
@@ -118,9 +127,7 @@ module Puma
118
127
  end
119
128
 
120
129
  if stderr
121
- unless Dir.exist?(File.dirname(stderr))
122
- raise "Cannot redirect STDERR to #{stderr}"
123
- end
130
+ ensure_output_directory_exists(stderr, 'STDERR')
124
131
 
125
132
  STDERR.reopen stderr, (append ? "a" : "w")
126
133
  STDERR.puts "=== puma startup: #{Time.now} ==="
@@ -159,5 +166,12 @@ module Puma
159
166
  server.inherit_binder @launcher.binder
160
167
  server
161
168
  end
169
+
170
+ private
171
+ def ensure_output_directory_exists(path, io_name)
172
+ unless Dir.exist?(File.dirname(path))
173
+ raise "Cannot redirect #{io_name} to #{path}"
174
+ end
175
+ end
162
176
  end
163
177
  end
data/lib/puma/server.rb CHANGED
@@ -146,7 +146,7 @@ module Puma
146
146
  begin
147
147
  skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if skt.kind_of? TCPSocket
148
148
  rescue IOError, SystemCallError
149
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
149
+ Puma::Util.purge_interrupt_queue
150
150
  end
151
151
  end
152
152
 
@@ -155,7 +155,7 @@ module Puma
155
155
  begin
156
156
  skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if skt.kind_of? TCPSocket
157
157
  rescue IOError, SystemCallError
158
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
158
+ Puma::Util.purge_interrupt_queue
159
159
  end
160
160
  end
161
161
  else
@@ -176,7 +176,7 @@ module Puma
176
176
  begin
177
177
  tcp_info = skt.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
178
178
  rescue IOError, SystemCallError
179
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
179
+ Puma::Util.purge_interrupt_queue
180
180
  @precheck_closing = false
181
181
  false
182
182
  else
@@ -220,7 +220,7 @@ module Puma
220
220
  # up in the background to handle requests. Otherwise requests
221
221
  # are handled synchronously.
222
222
  #
223
- def run(background=true, thread_name: 'server')
223
+ def run(background=true, thread_name: 'srv')
224
224
  BasicSocket.do_not_reverse_lookup = true
225
225
 
226
226
  @events.fire :state, :booting
@@ -315,14 +315,15 @@ module Puma
315
315
  queue_requests = @queue_requests
316
316
  drain = @options[:drain_on_shutdown] ? 0 : nil
317
317
 
318
- remote_addr_value = nil
319
- remote_addr_header = nil
320
-
321
- case @options[:remote_address]
318
+ addr_send_name, addr_value = case @options[:remote_address]
322
319
  when :value
323
- remote_addr_value = @options[:remote_address_value]
320
+ [:peerip=, @options[:remote_address_value]]
324
321
  when :header
325
- remote_addr_header = @options[:remote_address_header]
322
+ [:remote_addr_header=, @options[:remote_address_header]]
323
+ when :proxy_protocol
324
+ [:expect_proxy_proto=, @options[:remote_address_proxy_protocol]]
325
+ else
326
+ [nil, nil]
326
327
  end
327
328
 
328
329
  while @status == :run || (drain && shutting_down?)
@@ -342,17 +343,16 @@ module Puma
342
343
  next
343
344
  end
344
345
  drain += 1 if shutting_down?
345
- client = Client.new io, @binder.env(sock)
346
- client.listener = sock
347
- if remote_addr_value
348
- client.peerip = remote_addr_value
349
- elsif remote_addr_header
350
- client.remote_addr_header = remote_addr_header
351
- end
352
- pool << client
346
+ pool << Client.new(io, @binder.env(sock)).tap { |c|
347
+ c.listener = sock
348
+ c.send(addr_send_name, addr_value) if addr_value
349
+ }
353
350
  end
354
351
  end
355
- rescue Object => e
352
+ rescue IOError, Errno::EBADF
353
+ # In the case that any of the sockets are unexpectedly close.
354
+ raise
355
+ rescue StandardError => e
356
356
  @events.unknown_error e, nil, "Listen loop"
357
357
  end
358
358
  end
@@ -368,13 +368,14 @@ module Puma
368
368
  rescue Exception => e
369
369
  @events.unknown_error e, nil, "Exception handling servers"
370
370
  ensure
371
- begin
372
- @check.close unless @check.closed?
373
- rescue Errno::EBADF, RuntimeError
374
- # RuntimeError is Ruby 2.2 issue, can't modify frozen IOError
375
- # Errno::EBADF is infrequently raised
371
+ # RuntimeError is Ruby 2.2 issue, can't modify frozen IOError
372
+ # Errno::EBADF is infrequently raised
373
+ [@check, @notify].each do |io|
374
+ begin
375
+ io.close unless io.closed?
376
+ rescue Errno::EBADF, RuntimeError
377
+ end
376
378
  end
377
- @notify.close
378
379
  @notify = nil
379
380
  @check = nil
380
381
  end
@@ -475,7 +476,7 @@ module Puma
475
476
  end
476
477
  true
477
478
  rescue StandardError => e
478
- client_error(e, client)
479
+ client_error(e, client, buffer, requests)
479
480
  # The ensure tries to close +client+ down
480
481
  requests > 0
481
482
  ensure
@@ -484,7 +485,7 @@ module Puma
484
485
  begin
485
486
  client.close if close_socket
486
487
  rescue IOError, SystemCallError
487
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
488
+ Puma::Util.purge_interrupt_queue
488
489
  # Already closed
489
490
  rescue StandardError => e
490
491
  @events.unknown_error e, nil, "Client"
@@ -503,34 +504,36 @@ module Puma
503
504
  # :nocov:
504
505
 
505
506
  # Handle various error types thrown by Client I/O operations.
506
- def client_error(e, client)
507
+ def client_error(e, client, buffer = ::Puma::IOBuffer.new, requests = 1)
507
508
  # Swallow, do not log
508
509
  return if [ConnectionError, EOFError].include?(e.class)
509
510
 
510
- lowlevel_error(e, client.env)
511
511
  case e
512
512
  when MiniSSL::SSLError
513
513
  @events.ssl_error e, client.io
514
514
  when HttpParserError
515
- client.write_error(400)
515
+ status, headers, res_body = lowlevel_error(e, client.env, 400)
516
+ write_response(status, headers, res_body, buffer, requests, client)
516
517
  @events.parse_error e, client
517
518
  else
518
- client.write_error(500)
519
+ status, headers, res_body = lowlevel_error(e, client.env)
520
+ write_response(status, headers, res_body, buffer, requests, client)
519
521
  @events.unknown_error e, nil, "Read"
520
522
  end
521
523
  end
522
524
 
523
525
  # A fallback rack response if +@app+ raises as exception.
524
526
  #
525
- def lowlevel_error(e, env, status=500)
527
+ def lowlevel_error(e, env, status = 500)
526
528
  if handler = @options[:lowlevel_error_handler]
527
529
  if handler.arity == 1
528
- return handler.call(e)
530
+ handler_status, headers, res_body = handler.call(e)
529
531
  elsif handler.arity == 2
530
- return handler.call(e, env)
532
+ handler_status, headers, res_body = handler.call(e, env)
531
533
  else
532
- return handler.call(e, env, status)
534
+ handler_status, headers, res_body = handler.call(e, env, status)
533
535
  end
536
+ return [handler_status || status, headers || {}, res_body || []]
534
537
  end
535
538
 
536
539
  if @leak_stack_on_error
@@ -576,11 +579,11 @@ module Puma
576
579
  @notify << message
577
580
  rescue IOError, NoMethodError, Errno::EPIPE
578
581
  # The server, in another thread, is shutting down
579
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
582
+ Puma::Util.purge_interrupt_queue
580
583
  rescue RuntimeError => e
581
584
  # Temporary workaround for https://bugs.ruby-lang.org/issues/13239
582
585
  if e.message.include?('IOError')
583
- Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
586
+ Puma::Util.purge_interrupt_queue
584
587
  else
585
588
  raise e
586
589
  end
data/lib/puma/single.rb CHANGED
File without changes
@@ -1,15 +1,40 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'yaml'
4
-
5
3
  module Puma
4
+
5
+ # Puma::Launcher uses StateFile to write a yaml file for use with Puma::ControlCLI.
6
+ #
7
+ # In previous versions of Puma, YAML was used to read/write the state file.
8
+ # Since Puma is similar to Bundler/RubyGems in that it may load before one's app
9
+ # does, minimizing the dependencies that may be shared with the app is desired.
10
+ #
11
+ # At present, it only works with numeric and string values. It is still a valid
12
+ # yaml file, and the CI tests parse it with Psych.
13
+ #
6
14
  class StateFile
15
+
16
+ ALLOWED_FIELDS = %w!control_url control_auth_token pid running_from!
17
+
18
+ # @deprecated 6.0.0
19
+ FIELDS = ALLOWED_FIELDS
20
+
7
21
  def initialize
8
22
  @options = {}
9
23
  end
10
24
 
11
25
  def save(path, permission = nil)
12
- contents =YAML.dump @options
26
+ contents = "---\n".dup
27
+ @options.each do |k,v|
28
+ next unless ALLOWED_FIELDS.include? k
29
+ case v
30
+ when Numeric
31
+ contents << "#{k}: #{v}\n"
32
+ when String
33
+ next if v.strip.empty?
34
+ contents << (k == 'running_from' || v.to_s.include?(' ') ?
35
+ "#{k}: \"#{v}\"\n" : "#{k}: #{v}\n")
36
+ end
37
+ end
13
38
  if permission
14
39
  File.write path, contents, mode: 'wb:UTF-8'
15
40
  else
@@ -18,12 +43,21 @@ module Puma
18
43
  end
19
44
 
20
45
  def load(path)
21
- @options = YAML.load File.read(path)
46
+ File.read(path).lines.each do |line|
47
+ next if line.start_with? '#'
48
+ k,v = line.split ':', 2
49
+ next unless v && ALLOWED_FIELDS.include?(k)
50
+ v = v.strip
51
+ @options[k] =
52
+ case v
53
+ when /\A\d+\z/ then v.to_i
54
+ when /\A\d+\.\d+\z/ then v.to_f
55
+ else v.gsub(/\A"|"\z/, '')
56
+ end
57
+ end
22
58
  end
23
59
 
24
- FIELDS = %w!control_url control_auth_token pid running_from!
25
-
26
- FIELDS.each do |f|
60
+ ALLOWED_FIELDS.each do |f|
27
61
  define_method f do
28
62
  @options[f]
29
63
  end
data/lib/puma/systemd.rb CHANGED
File without changes
@@ -72,7 +72,7 @@ module Puma
72
72
  attr_accessor :out_of_band_hook # @version 5.0.0
73
73
 
74
74
  def self.clean_thread_locals
75
- Thread.current.keys.each do |key| # rubocop: disable Performance/HashEachMethods
75
+ Thread.current.keys.each do |key| # rubocop: disable Style/HashEachMethods
76
76
  Thread.current[key] = nil unless key == :__recursive_key__
77
77
  end
78
78
  end
@@ -102,7 +102,7 @@ module Puma
102
102
  @spawned += 1
103
103
 
104
104
  th = Thread.new(@spawned) do |spawned|
105
- Puma.set_thread_name '%s threadpool %03i' % [@name, spawned]
105
+ Puma.set_thread_name '%s tp %03i' % [@name, spawned]
106
106
  todo = @todo
107
107
  block = @block
108
108
  mutex = @mutex
data/lib/puma/util.rb CHANGED
@@ -10,6 +10,13 @@ module Puma
10
10
  IO.pipe
11
11
  end
12
12
 
13
+ # An instance method on Thread has been provided to address https://bugs.ruby-lang.org/issues/13632,
14
+ # which currently effects some older versions of Ruby: 2.2.7 2.2.8 2.2.9 2.2.10 2.3.4 2.4.1
15
+ # Additional context: https://github.com/puma/puma/pull/1345
16
+ def purge_interrupt_queue
17
+ Thread.current.purge_interrupt_queue if Thread.current.respond_to? :purge_interrupt_queue
18
+ end
19
+
13
20
  # Unescapes a URI escaped string with +encoding+. +encoding+ will be the
14
21
  # target encoding of the string returned, and it defaults to UTF-8
15
22
  if defined?(::Encoding)
data/lib/puma.rb CHANGED
File without changes
File without changes
data/tools/Dockerfile CHANGED
@@ -1,6 +1,6 @@
1
1
  # Use this Dockerfile to create minimal reproductions of issues
2
2
 
3
- FROM ruby:2.6
3
+ FROM ruby:3.1
4
4
 
5
5
  # throw errors if Gemfile has been modified since Gemfile.lock
6
6
  RUN bundle config --global frozen 1
data/tools/trickletest.rb CHANGED
File without changes
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puma
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.4.0
4
+ version: 5.6.0
5
5
  platform: java
6
6
  authors:
7
7
  - Evan Phoenix
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-29 00:00:00.000000000 Z
11
+ date: 1980-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -24,9 +24,9 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.0'
27
- description: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server
27
+ description: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server
28
28
  for Ruby/Rack applications. Puma is intended for use in both development and production
29
- environments. It's great for highly concurrent Ruby implementations such as Rubinius
29
+ environments. It's great for highly parallel Ruby implementations such as Rubinius
30
30
  and JRuby as well as as providing process worker support to support CRuby well.
31
31
  email:
32
32
  - evan@phx.io
@@ -140,9 +140,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
140
140
  - !ruby/object:Gem::Version
141
141
  version: '0'
142
142
  requirements: []
143
- rubygems_version: 3.1.6
143
+ rubygems_version: 3.2.29
144
144
  signing_key:
145
145
  specification_version: 4
146
- summary: Puma is a simple, fast, threaded, and highly concurrent HTTP 1.1 server for
146
+ summary: Puma is a simple, fast, threaded, and highly parallel HTTP 1.1 server for
147
147
  Ruby/Rack applications
148
148
  test_files: []