puma 2.2.0 → 8.0.2

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 (136) hide show
  1. checksums.yaml +7 -0
  2. data/History.md +3334 -0
  3. data/LICENSE +23 -20
  4. data/README.md +387 -100
  5. data/bin/puma-wild +25 -0
  6. data/bin/pumactl +1 -1
  7. data/docs/5.0-Upgrade.md +98 -0
  8. data/docs/6.0-Upgrade.md +56 -0
  9. data/docs/7.0-Upgrade.md +52 -0
  10. data/docs/8.0-Upgrade.md +100 -0
  11. data/docs/architecture.md +74 -0
  12. data/docs/compile_options.md +55 -0
  13. data/docs/deployment.md +137 -0
  14. data/docs/fork_worker.md +41 -0
  15. data/docs/grpc.md +62 -0
  16. data/docs/images/favicon.svg +1 -0
  17. data/docs/images/puma-connection-flow-no-reactor.png +0 -0
  18. data/docs/images/puma-connection-flow.png +0 -0
  19. data/docs/images/puma-general-arch.png +0 -0
  20. data/docs/images/running-puma.svg +1 -0
  21. data/docs/images/standard-logo.svg +1 -0
  22. data/docs/java_options.md +54 -0
  23. data/docs/jungle/README.md +9 -0
  24. data/docs/jungle/rc.d/README.md +74 -0
  25. data/docs/jungle/rc.d/puma +61 -0
  26. data/docs/jungle/rc.d/puma.conf +10 -0
  27. data/docs/kubernetes.md +73 -0
  28. data/docs/nginx.md +5 -5
  29. data/docs/plugins.md +42 -0
  30. data/docs/rails_dev_mode.md +28 -0
  31. data/docs/restart.md +65 -0
  32. data/docs/signals.md +98 -0
  33. data/docs/stats.md +148 -0
  34. data/docs/systemd.md +253 -0
  35. data/docs/testing_benchmarks_local_files.md +150 -0
  36. data/docs/testing_test_rackup_ci_files.md +36 -0
  37. data/ext/puma_http11/PumaHttp11Service.java +2 -2
  38. data/ext/puma_http11/extconf.rb +59 -2
  39. data/ext/puma_http11/http11_parser.c +319 -487
  40. data/ext/puma_http11/http11_parser.h +15 -13
  41. data/ext/puma_http11/http11_parser.java.rl +64 -94
  42. data/ext/puma_http11/http11_parser.rl +27 -24
  43. data/ext/puma_http11/http11_parser_common.rl +8 -8
  44. data/ext/puma_http11/mini_ssl.c +696 -38
  45. data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
  46. data/ext/puma_http11/org/jruby/puma/EnvKey.java +241 -0
  47. data/ext/puma_http11/org/jruby/puma/Http11.java +241 -145
  48. data/ext/puma_http11/org/jruby/puma/Http11Parser.java +174 -221
  49. data/ext/puma_http11/org/jruby/puma/MiniSSL.java +413 -193
  50. data/ext/puma_http11/puma_http11.c +183 -175
  51. data/lib/puma/app/status.rb +77 -29
  52. data/lib/puma/binder.rb +349 -119
  53. data/lib/puma/cli.rb +163 -801
  54. data/lib/puma/client.rb +627 -144
  55. data/lib/puma/client_env.rb +171 -0
  56. data/lib/puma/cluster/worker.rb +183 -0
  57. data/lib/puma/cluster/worker_handle.rb +127 -0
  58. data/lib/puma/cluster.rb +634 -0
  59. data/lib/puma/cluster_accept_loop_delay.rb +91 -0
  60. data/lib/puma/commonlogger.rb +115 -0
  61. data/lib/puma/configuration.rb +420 -198
  62. data/lib/puma/const.rb +266 -115
  63. data/lib/puma/control_cli.rb +219 -120
  64. data/lib/puma/detect.rb +56 -2
  65. data/lib/puma/dsl.rb +1562 -0
  66. data/lib/puma/error_logger.rb +115 -0
  67. data/lib/puma/events.rb +44 -60
  68. data/lib/puma/io_buffer.rb +48 -5
  69. data/lib/puma/jruby_restart.rb +2 -51
  70. data/lib/puma/json_serialization.rb +96 -0
  71. data/lib/puma/launcher/bundle_pruner.rb +102 -0
  72. data/lib/puma/launcher.rb +501 -0
  73. data/lib/puma/log_writer.rb +153 -0
  74. data/lib/puma/minissl/context_builder.rb +96 -0
  75. data/lib/puma/minissl.rb +355 -37
  76. data/lib/puma/null_io.rb +79 -12
  77. data/lib/puma/plugin/systemd.rb +90 -0
  78. data/lib/puma/plugin/tmp_restart.rb +36 -0
  79. data/lib/puma/plugin.rb +111 -0
  80. data/lib/puma/rack/builder.rb +297 -0
  81. data/lib/puma/rack/urlmap.rb +93 -0
  82. data/lib/puma/rack_default.rb +21 -4
  83. data/lib/puma/reactor.rb +102 -133
  84. data/lib/puma/response.rb +532 -0
  85. data/lib/puma/runner.rb +211 -0
  86. data/lib/puma/sd_notify.rb +146 -0
  87. data/lib/puma/server.rb +582 -445
  88. data/lib/puma/server_plugin_control.rb +32 -0
  89. data/lib/puma/single.rb +72 -0
  90. data/lib/puma/state_file.rb +69 -0
  91. data/lib/puma/thread_pool.rb +389 -57
  92. data/lib/puma/util.rb +125 -0
  93. data/lib/puma.rb +80 -6
  94. data/lib/rack/handler/puma.rb +125 -47
  95. data/tools/Dockerfile +26 -0
  96. data/tools/trickletest.rb +44 -0
  97. metadata +88 -148
  98. data/COPYING +0 -55
  99. data/Gemfile +0 -10
  100. data/History.txt +0 -302
  101. data/Manifest.txt +0 -60
  102. data/Rakefile +0 -165
  103. data/TODO +0 -5
  104. data/docs/config.md +0 -0
  105. data/ext/puma_http11/ext_help.h +0 -15
  106. data/ext/puma_http11/io_buffer.c +0 -154
  107. data/lib/puma/accept_nonblock.rb +0 -23
  108. data/lib/puma/capistrano.rb +0 -34
  109. data/lib/puma/compat.rb +0 -11
  110. data/lib/puma/daemon_ext.rb +0 -20
  111. data/lib/puma/delegation.rb +0 -11
  112. data/lib/puma/java_io_buffer.rb +0 -45
  113. data/lib/puma/rack_patch.rb +0 -25
  114. data/puma.gemspec +0 -45
  115. data/test/test_app_status.rb +0 -88
  116. data/test/test_cli.rb +0 -171
  117. data/test/test_config.rb +0 -16
  118. data/test/test_http10.rb +0 -27
  119. data/test/test_http11.rb +0 -126
  120. data/test/test_integration.rb +0 -154
  121. data/test/test_iobuffer.rb +0 -38
  122. data/test/test_minissl.rb +0 -25
  123. data/test/test_null_io.rb +0 -31
  124. data/test/test_persistent.rb +0 -238
  125. data/test/test_puma_server.rb +0 -224
  126. data/test/test_rack_handler.rb +0 -10
  127. data/test/test_rack_server.rb +0 -141
  128. data/test/test_thread_pool.rb +0 -146
  129. data/test/test_unix_socket.rb +0 -39
  130. data/test/test_ws.rb +0 -89
  131. data/tools/jungle/init.d/README.md +0 -54
  132. data/tools/jungle/init.d/puma +0 -332
  133. data/tools/jungle/init.d/run-puma +0 -3
  134. data/tools/jungle/upstart/README.md +0 -61
  135. data/tools/jungle/upstart/puma-manager.conf +0 -31
  136. data/tools/jungle/upstart/puma.conf +0 -52
data/lib/puma/reactor.rb CHANGED
@@ -1,162 +1,131 @@
1
- require 'puma/util'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module Puma
4
+ class UnsupportedBackend < StandardError; end
5
+
6
+ # Monitors a collection of IO objects, calling a block whenever
7
+ # any monitored object either receives data or times out, or when the Reactor shuts down.
8
+ #
9
+ # The waiting/wake up is performed with nio4r, which will use the appropriate backend (libev,
10
+ # Java NIO or just plain IO#select). The call to `NIO::Selector#select` will
11
+ # 'wakeup' any IO object that receives data.
12
+ #
13
+ # This class additionally tracks a timeout for every added object,
14
+ # and wakes up any object when its timeout elapses.
15
+ #
16
+ # The implementation uses a Queue to synchronize adding new objects from the internal select loop.
4
17
  class Reactor
5
- DefaultSleepFor = 5
6
18
 
7
- def initialize(server, app_pool)
8
- @server = server
9
- @events = server.events
10
- @app_pool = app_pool
19
+ # @!attribute [rw] reactor_max
20
+ # Maximum number of clients in the selector. Reset with calls to `Server.stats`.
21
+ attr_accessor :reactor_max
22
+ attr_reader :reactor_size
23
+
24
+ # Create a new Reactor to monitor IO objects added by #add.
25
+ # The provided block will be invoked when an IO has data available to read,
26
+ # its timeout elapses, or when the Reactor shuts down.
27
+ def initialize(backend, &block)
28
+ require 'nio'
29
+ valid_backends = [:auto, *::NIO::Selector.backends]
30
+ unless valid_backends.include?(backend)
31
+ raise ArgumentError.new("unsupported IO selector backend: #{backend} (available backends: #{valid_backends.join(', ')})")
32
+ end
11
33
 
12
- @mutex = Mutex.new
13
- @ready, @trigger = Puma::Util.pipe
14
- @input = []
15
- @sleep_for = DefaultSleepFor
34
+ @selector = ::NIO::Selector.new(NIO::Selector.backends.delete(backend))
35
+ @input = Queue.new
16
36
  @timeouts = []
17
-
18
- @sockets = [@ready]
37
+ @block = block
38
+ @reactor_size = 0
39
+ @reactor_max = 0
19
40
  end
20
41
 
21
- def run
22
- sockets = @sockets
23
-
24
- while true
25
- ready = IO.select sockets, nil, nil, @sleep_for
26
-
27
- if ready and reads = ready[0]
28
- reads.each do |c|
29
- if c == @ready
30
- @mutex.synchronize do
31
- case @ready.read(1)
32
- when "*"
33
- sockets += @input
34
- @input.clear
35
- when "c"
36
- sockets.delete_if do |s|
37
- if s == @ready
38
- false
39
- else
40
- s.close
41
- true
42
- end
43
- end
44
- when "!"
45
- return
46
- end
47
- end
48
- else
49
- # We have to be sure to remove it from the timeout
50
- # list or we'll accidentally close the socket when
51
- # it's in use!
52
- if c.timeout_at
53
- @mutex.synchronize do
54
- @timeouts.delete c
55
- end
56
- end
57
-
58
- begin
59
- if c.try_to_finish
60
- @app_pool << c
61
- sockets.delete c
62
- end
63
-
64
- # The client doesn't know HTTP well
65
- rescue HttpParserError => e
66
- c.write_400
67
- c.close
68
-
69
- sockets.delete c
70
-
71
- @events.parse_error @server, c.env, e
72
- rescue StandardError => e
73
- c.write_500
74
- c.close
75
-
76
- sockets.delete c
77
- end
78
- end
79
- end
80
- end
81
-
82
- unless @timeouts.empty?
83
- @mutex.synchronize do
84
- now = Time.now
85
-
86
- while @timeouts.first.timeout_at < now
87
- c = @timeouts.shift
88
- sockets.delete c
89
- c.close
90
-
91
- break if @timeouts.empty?
92
- end
93
-
94
- calculate_sleep
95
- end
42
+ # Run the internal select loop, using a background thread by default.
43
+ def run(background=true)
44
+ if background
45
+ @thread = Thread.new do
46
+ Puma.set_thread_name "reactor"
47
+ select_loop
96
48
  end
49
+ else
50
+ select_loop
97
51
  end
98
- ensure
99
- @trigger.close
100
- @ready.close
101
52
  end
102
53
 
103
- def run_in_thread
104
- @thread = Thread.new {
105
- while true
106
- begin
107
- run
108
- break
109
- rescue StandardError => e
110
- STDERR.puts "Error in reactor loop escaped: #{e.message} (#{e.class})"
111
- puts e.backtrace
112
- end
113
- end
114
- }
54
+ # Add a new client to monitor.
55
+ # The object must respond to #timeout and #timeout_at.
56
+ # Returns false if the reactor is already shut down.
57
+ def add(client)
58
+ @input << client
59
+ @selector.wakeup
60
+ true
61
+ rescue ClosedQueueError, IOError # Ignore if selector is already closed
62
+ false
115
63
  end
116
64
 
117
- def calculate_sleep
118
- if @timeouts.empty?
119
- @sleep_for = DefaultSleepFor
120
- else
121
- diff = @timeouts.first.timeout_at.to_f - Time.now.to_f
122
-
123
- if diff < 0.0
124
- @sleep_for = 0
125
- else
126
- @sleep_for = diff
127
- end
65
+ # Shutdown the reactor, blocking until the background thread is finished.
66
+ def shutdown
67
+ @input.close
68
+ begin
69
+ @selector.wakeup
70
+ rescue IOError # Ignore if selector is already closed
128
71
  end
72
+ @thread&.join
129
73
  end
130
74
 
131
- def add(c)
132
- @mutex.synchronize do
133
- @input << c
134
- @trigger << "*"
75
+ private
76
+
77
+ def select_loop
78
+ begin
79
+ until @input.closed? && @input.empty?
80
+ # Wakeup any registered object that receives incoming data.
81
+ # Block until the earliest timeout or Selector#wakeup is called.
82
+ timeout = (earliest = @timeouts.first) && earliest.timeout
83
+ @selector.select(timeout) do |monitor|
84
+ wakeup!(monitor.value)
85
+ end
135
86
 
136
- if c.timeout_at
137
- @timeouts << c
138
- @timeouts.sort! { |a,b| a.timeout_at <=> b.timeout_at }
87
+ # Wakeup all objects that timed out.
88
+ timed_out = @timeouts.take_while { |client| client.timeout == 0 }
89
+ timed_out.each { |client| wakeup!(client) }
139
90
 
140
- calculate_sleep
91
+ unless @input.empty?
92
+ until @input.empty?
93
+ client = @input.pop
94
+ register(client) if client.io_ok?
95
+ end
96
+ @timeouts.sort_by!(&:timeout_at)
97
+ end
141
98
  end
99
+ rescue StandardError => e
100
+ STDERR.puts "Error in reactor loop escaped: #{e.message} (#{e.class})"
101
+ STDERR.puts e.backtrace
102
+
103
+ retry
142
104
  end
105
+
106
+ # Wakeup all remaining objects on shutdown.
107
+ @timeouts.each(&@block)
108
+ @selector.close
143
109
  end
144
110
 
145
- # Close all watched sockets and clear them from being watched
146
- def clear!
147
- begin
148
- @trigger << "c"
149
- rescue IOError
150
- end
111
+ # Start monitoring the object.
112
+ def register(client)
113
+ @selector.register(client.to_io, :r).value = client
114
+ @reactor_size += 1
115
+ @reactor_max = @reactor_size if @reactor_max < @reactor_size
116
+ @timeouts << client
117
+ rescue ArgumentError
118
+ # unreadable clients raise error when processed by NIO
151
119
  end
152
120
 
153
- def shutdown
154
- begin
155
- @trigger << "!"
156
- rescue IOError
121
+ # 'Wake up' a monitored object by calling the provided block.
122
+ # Stop monitoring the object if the block returns `true`.
123
+ def wakeup!(client)
124
+ if @block.call client
125
+ @selector.deregister client.to_io
126
+ @reactor_size -= 1
127
+ @timeouts.delete client
157
128
  end
158
-
159
- @thread.join
160
129
  end
161
130
  end
162
131
  end