puma 5.3.2 → 6.0.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.
- checksums.yaml +4 -4
- data/History.md +284 -11
- data/LICENSE +0 -0
- data/README.md +61 -16
- data/bin/puma-wild +1 -1
- data/docs/architecture.md +49 -16
- data/docs/compile_options.md +38 -2
- data/docs/deployment.md +53 -67
- data/docs/fork_worker.md +1 -3
- data/docs/images/puma-connection-flow-no-reactor.png +0 -0
- data/docs/images/puma-connection-flow.png +0 -0
- data/docs/images/puma-general-arch.png +0 -0
- data/docs/jungle/README.md +0 -0
- data/docs/jungle/rc.d/README.md +0 -0
- data/docs/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +0 -0
- data/docs/nginx.md +0 -0
- data/docs/plugins.md +15 -15
- data/docs/rails_dev_mode.md +2 -3
- data/docs/restart.md +6 -6
- data/docs/signals.md +11 -10
- data/docs/stats.md +8 -8
- data/docs/systemd.md +64 -67
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/ext_help.h +0 -0
- data/ext/puma_http11/extconf.rb +44 -13
- data/ext/puma_http11/http11_parser.c +24 -11
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +2 -2
- data/ext/puma_http11/http11_parser.rl +2 -2
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +122 -23
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +0 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +50 -48
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +188 -102
- data/ext/puma_http11/puma_http11.c +18 -10
- data/lib/puma/app/status.rb +9 -6
- data/lib/puma/binder.rb +81 -42
- data/lib/puma/cli.rb +23 -19
- data/lib/puma/client.rb +124 -30
- data/lib/puma/cluster/worker.rb +21 -29
- data/lib/puma/cluster/worker_handle.rb +8 -1
- data/lib/puma/cluster.rb +57 -48
- data/lib/puma/commonlogger.rb +0 -0
- data/lib/puma/configuration.rb +74 -55
- data/lib/puma/const.rb +21 -24
- data/lib/puma/control_cli.rb +22 -19
- data/lib/puma/detect.rb +10 -2
- data/lib/puma/dsl.rb +196 -57
- data/lib/puma/error_logger.rb +17 -9
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +29 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/{json.rb → json_serialization.rb} +1 -1
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +108 -154
- data/lib/puma/log_writer.rb +137 -0
- data/lib/puma/minissl/context_builder.rb +29 -16
- data/lib/puma/minissl.rb +115 -38
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +2 -2
- data/lib/puma/rack/builder.rb +5 -5
- data/lib/puma/rack/urlmap.rb +0 -0
- data/lib/puma/rack_default.rb +1 -1
- data/lib/puma/reactor.rb +3 -3
- data/lib/puma/request.rb +293 -153
- data/lib/puma/runner.rb +63 -28
- data/lib/puma/server.rb +83 -88
- data/lib/puma/single.rb +10 -10
- data/lib/puma/state_file.rb +39 -7
- data/lib/puma/systemd.rb +3 -2
- data/lib/puma/thread_pool.rb +22 -17
- data/lib/puma/util.rb +20 -15
- data/lib/puma.rb +12 -9
- data/lib/rack/handler/puma.rb +9 -9
- data/tools/Dockerfile +1 -1
- data/tools/trickletest.rb +0 -0
- metadata +13 -9
- data/lib/puma/queue_close.rb +0 -26
data/lib/puma/runner.rb
CHANGED
@@ -1,20 +1,36 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
3
|
+
require_relative 'server'
|
4
|
+
require_relative 'const'
|
5
5
|
|
6
6
|
module Puma
|
7
7
|
# Generic class that is used by `Puma::Cluster` and `Puma::Single` to
|
8
8
|
# serve requests. This class spawns a new instance of `Puma::Server` via
|
9
9
|
# a call to `start_server`.
|
10
10
|
class Runner
|
11
|
-
def initialize(
|
12
|
-
@launcher =
|
13
|
-
@
|
14
|
-
@
|
11
|
+
def initialize(launcher)
|
12
|
+
@launcher = launcher
|
13
|
+
@log_writer = launcher.log_writer
|
14
|
+
@events = launcher.events
|
15
|
+
@config = launcher.config
|
16
|
+
@options = launcher.options
|
15
17
|
@app = nil
|
16
18
|
@control = nil
|
17
19
|
@started_at = Time.now
|
20
|
+
@wakeup = nil
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns the hash of configuration options.
|
24
|
+
# @return [Puma::UserFileDefaultOptions]
|
25
|
+
attr_reader :options
|
26
|
+
|
27
|
+
def wakeup!
|
28
|
+
return unless @wakeup
|
29
|
+
|
30
|
+
@wakeup.write "!" unless @wakeup.closed?
|
31
|
+
|
32
|
+
rescue SystemCallError, IOError
|
33
|
+
Puma::Util.purge_interrupt_queue
|
18
34
|
end
|
19
35
|
|
20
36
|
def development?
|
@@ -26,27 +42,27 @@ module Puma
|
|
26
42
|
end
|
27
43
|
|
28
44
|
def log(str)
|
29
|
-
@
|
45
|
+
@log_writer.log str
|
30
46
|
end
|
31
47
|
|
32
48
|
# @version 5.0.0
|
33
49
|
def stop_control
|
34
|
-
@control
|
50
|
+
@control&.stop true
|
35
51
|
end
|
36
52
|
|
37
53
|
def error(str)
|
38
|
-
@
|
54
|
+
@log_writer.error str
|
39
55
|
end
|
40
56
|
|
41
57
|
def debug(str)
|
42
|
-
@
|
58
|
+
@log_writer.log "- #{str}" if @options[:debug]
|
43
59
|
end
|
44
60
|
|
45
61
|
def start_control
|
46
62
|
str = @options[:control_url]
|
47
63
|
return unless str
|
48
64
|
|
49
|
-
|
65
|
+
require_relative 'app/status'
|
50
66
|
|
51
67
|
if token = @options[:control_auth_token]
|
52
68
|
token = nil if token.empty? || token == 'none'
|
@@ -54,12 +70,14 @@ module Puma
|
|
54
70
|
|
55
71
|
app = Puma::App::Status.new @launcher, token
|
56
72
|
|
57
|
-
|
58
|
-
|
73
|
+
# A Reactor is not created aand nio4r is not loaded when 'queue_requests: false'
|
74
|
+
# Use `nil` for events, no hooks in control server
|
75
|
+
control = Puma::Server.new app, nil,
|
76
|
+
{ min_threads: 0, max_threads: 1, queue_requests: false, log_writer: @log_writer }
|
59
77
|
|
60
|
-
control.binder.parse [str],
|
78
|
+
control.binder.parse [str], nil, 'Starting control server'
|
61
79
|
|
62
|
-
control.run thread_name: '
|
80
|
+
control.run thread_name: 'ctl'
|
63
81
|
@control = control
|
64
82
|
end
|
65
83
|
|
@@ -84,12 +102,13 @@ module Puma
|
|
84
102
|
def output_header(mode)
|
85
103
|
min_t = @options[:min_threads]
|
86
104
|
max_t = @options[:max_threads]
|
105
|
+
environment = @options[:environment]
|
87
106
|
|
88
107
|
log "Puma starting in #{mode} mode..."
|
89
108
|
log "* Puma version: #{Puma::Const::PUMA_VERSION} (#{ruby_engine}) (\"#{Puma::Const::CODE_NAME}\")"
|
90
109
|
log "* Min threads: #{min_t}"
|
91
110
|
log "* Max threads: #{max_t}"
|
92
|
-
log "* Environment: #{
|
111
|
+
log "* Environment: #{environment}"
|
93
112
|
|
94
113
|
if mode == "cluster"
|
95
114
|
log "* Master PID: #{Process.pid}"
|
@@ -108,9 +127,7 @@ module Puma
|
|
108
127
|
append = @options[:redirect_append]
|
109
128
|
|
110
129
|
if stdout
|
111
|
-
|
112
|
-
raise "Cannot redirect STDOUT to #{stdout}"
|
113
|
-
end
|
130
|
+
ensure_output_directory_exists(stdout, 'STDOUT')
|
114
131
|
|
115
132
|
STDOUT.reopen stdout, (append ? "a" : "w")
|
116
133
|
STDOUT.puts "=== puma startup: #{Time.now} ==="
|
@@ -118,9 +135,7 @@ module Puma
|
|
118
135
|
end
|
119
136
|
|
120
137
|
if stderr
|
121
|
-
|
122
|
-
raise "Cannot redirect STDERR to #{stderr}"
|
123
|
-
end
|
138
|
+
ensure_output_directory_exists(stderr, 'STDERR')
|
124
139
|
|
125
140
|
STDERR.reopen stderr, (append ? "a" : "w")
|
126
141
|
STDERR.puts "=== puma startup: #{Time.now} ==="
|
@@ -134,30 +149,50 @@ module Puma
|
|
134
149
|
end
|
135
150
|
|
136
151
|
def load_and_bind
|
137
|
-
unless @
|
152
|
+
unless @config.app_configured?
|
138
153
|
error "No application configured, nothing to run"
|
139
154
|
exit 1
|
140
155
|
end
|
141
156
|
|
142
157
|
begin
|
143
|
-
@app = @
|
158
|
+
@app = @config.app
|
144
159
|
rescue Exception => e
|
145
160
|
log "! Unable to load application: #{e.class}: #{e.message}"
|
146
161
|
raise e
|
147
162
|
end
|
148
163
|
|
149
|
-
@launcher.binder.parse @options[:binds]
|
164
|
+
@launcher.binder.parse @options[:binds]
|
150
165
|
end
|
151
166
|
|
152
167
|
# @!attribute [r] app
|
153
168
|
def app
|
154
|
-
@app ||= @
|
169
|
+
@app ||= @config.app
|
155
170
|
end
|
156
171
|
|
157
172
|
def start_server
|
158
|
-
server = Puma::Server.new
|
159
|
-
server.inherit_binder
|
173
|
+
server = Puma::Server.new(app, @events, @options)
|
174
|
+
server.inherit_binder(@launcher.binder)
|
160
175
|
server
|
161
176
|
end
|
177
|
+
|
178
|
+
private
|
179
|
+
def ensure_output_directory_exists(path, io_name)
|
180
|
+
unless Dir.exist?(File.dirname(path))
|
181
|
+
raise "Cannot redirect #{io_name} to #{path}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def stats
|
186
|
+
{
|
187
|
+
versions: {
|
188
|
+
puma: Puma::Const::PUMA_VERSION,
|
189
|
+
ruby: {
|
190
|
+
engine: RUBY_ENGINE,
|
191
|
+
version: RUBY_VERSION,
|
192
|
+
patchlevel: RUBY_PATCHLEVEL
|
193
|
+
}
|
194
|
+
}
|
195
|
+
}
|
196
|
+
end
|
162
197
|
end
|
163
198
|
end
|
data/lib/puma/server.rb
CHANGED
@@ -2,18 +2,20 @@
|
|
2
2
|
|
3
3
|
require 'stringio'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
5
|
+
require_relative 'thread_pool'
|
6
|
+
require_relative 'const'
|
7
|
+
require_relative 'log_writer'
|
8
|
+
require_relative 'events'
|
9
|
+
require_relative 'null_io'
|
10
|
+
require_relative 'reactor'
|
11
|
+
require_relative 'client'
|
12
|
+
require_relative 'binder'
|
13
|
+
require_relative 'util'
|
14
|
+
require_relative 'io_buffer'
|
15
|
+
require_relative 'request'
|
15
16
|
|
16
17
|
require 'socket'
|
18
|
+
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
17
19
|
require 'forwardable'
|
18
20
|
|
19
21
|
module Puma
|
@@ -29,12 +31,12 @@ module Puma
|
|
29
31
|
#
|
30
32
|
# Each `Puma::Server` will have one reactor and one thread pool.
|
31
33
|
class Server
|
32
|
-
|
33
34
|
include Puma::Const
|
34
35
|
include Request
|
35
36
|
extend Forwardable
|
36
37
|
|
37
38
|
attr_reader :thread
|
39
|
+
attr_reader :log_writer
|
38
40
|
attr_reader :events
|
39
41
|
attr_reader :min_threads, :max_threads # for #stats
|
40
42
|
attr_reader :requests_count # @version 5.0.0
|
@@ -44,11 +46,6 @@ module Puma
|
|
44
46
|
:leak_stack_on_error,
|
45
47
|
:persistent_timeout, :reaping_time
|
46
48
|
|
47
|
-
# @deprecated v6.0.0
|
48
|
-
attr_writer :auto_trim_time, :early_hints, :first_data_timeout,
|
49
|
-
:leak_stack_on_error, :min_threads, :max_threads,
|
50
|
-
:persistent_timeout, :reaping_time
|
51
|
-
|
52
49
|
attr_accessor :app
|
53
50
|
attr_accessor :binder
|
54
51
|
|
@@ -59,8 +56,9 @@ module Puma
|
|
59
56
|
|
60
57
|
# Create a server for the rack app +app+.
|
61
58
|
#
|
62
|
-
# +
|
63
|
-
#
|
59
|
+
# +log_writer+ is a Puma::LogWriter object used to log info and error messages.
|
60
|
+
#
|
61
|
+
# +events+ is a Puma::Events object used to notify application status events.
|
64
62
|
#
|
65
63
|
# Server#run returns a thread that you can join on to wait for the server
|
66
64
|
# to do its work.
|
@@ -69,34 +67,40 @@ module Puma
|
|
69
67
|
# and have default values set via +fetch+. Normally the values are set via
|
70
68
|
# `::Puma::Configuration.puma_default_options`.
|
71
69
|
#
|
72
|
-
|
70
|
+
# @note The `events` parameter is set to nil, and set to `Events.new` in code.
|
71
|
+
# Often `options` needs to be passed, but `events` does not. Using nil allows
|
72
|
+
# calling code to not require events.rb.
|
73
|
+
#
|
74
|
+
def initialize(app, events = nil, options = {})
|
73
75
|
@app = app
|
74
|
-
@events = events
|
76
|
+
@events = events || Events.new
|
75
77
|
|
76
78
|
@check, @notify = nil
|
77
79
|
@status = :stop
|
78
80
|
|
79
|
-
@auto_trim_time = 30
|
80
|
-
@reaping_time = 1
|
81
|
-
|
82
81
|
@thread = nil
|
83
82
|
@thread_pool = nil
|
84
83
|
|
85
|
-
@options = options
|
84
|
+
@options = if options.is_a?(UserFileDefaultOptions)
|
85
|
+
options
|
86
|
+
else
|
87
|
+
UserFileDefaultOptions.new(options, Configuration::DEFAULTS)
|
88
|
+
end
|
86
89
|
|
87
|
-
@
|
88
|
-
@
|
89
|
-
@
|
90
|
-
@
|
91
|
-
@
|
92
|
-
@
|
93
|
-
@
|
94
|
-
@
|
90
|
+
@log_writer = @options.fetch :log_writer, LogWriter.stdio
|
91
|
+
@early_hints = @options[:early_hints]
|
92
|
+
@first_data_timeout = @options[:first_data_timeout]
|
93
|
+
@min_threads = @options[:min_threads]
|
94
|
+
@max_threads = @options[:max_threads]
|
95
|
+
@persistent_timeout = @options[:persistent_timeout]
|
96
|
+
@queue_requests = @options[:queue_requests]
|
97
|
+
@max_fast_inline = @options[:max_fast_inline]
|
98
|
+
@io_selector_backend = @options[:io_selector_backend]
|
95
99
|
|
96
100
|
temp = !!(@options[:environment] =~ /\A(development|test)\z/)
|
97
101
|
@leak_stack_on_error = @options[:environment] ? temp : true
|
98
102
|
|
99
|
-
@binder = Binder.new(
|
103
|
+
@binder = Binder.new(log_writer)
|
100
104
|
|
101
105
|
ENV['RACK_ENV'] ||= "development"
|
102
106
|
|
@@ -145,7 +149,7 @@ module Puma
|
|
145
149
|
begin
|
146
150
|
skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 1) if skt.kind_of? TCPSocket
|
147
151
|
rescue IOError, SystemCallError
|
148
|
-
|
152
|
+
Puma::Util.purge_interrupt_queue
|
149
153
|
end
|
150
154
|
end
|
151
155
|
|
@@ -154,7 +158,7 @@ module Puma
|
|
154
158
|
begin
|
155
159
|
skt.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_CORK, 0) if skt.kind_of? TCPSocket
|
156
160
|
rescue IOError, SystemCallError
|
157
|
-
|
161
|
+
Puma::Util.purge_interrupt_queue
|
158
162
|
end
|
159
163
|
end
|
160
164
|
else
|
@@ -175,7 +179,7 @@ module Puma
|
|
175
179
|
begin
|
176
180
|
tcp_info = skt.getsockopt(Socket::IPPROTO_TCP, Socket::TCP_INFO)
|
177
181
|
rescue IOError, SystemCallError
|
178
|
-
|
182
|
+
Puma::Util.purge_interrupt_queue
|
179
183
|
@precheck_closing = false
|
180
184
|
false
|
181
185
|
else
|
@@ -192,12 +196,12 @@ module Puma
|
|
192
196
|
|
193
197
|
# @!attribute [r] backlog
|
194
198
|
def backlog
|
195
|
-
@thread_pool
|
199
|
+
@thread_pool&.backlog
|
196
200
|
end
|
197
201
|
|
198
202
|
# @!attribute [r] running
|
199
203
|
def running
|
200
|
-
@thread_pool
|
204
|
+
@thread_pool&.spawned
|
201
205
|
end
|
202
206
|
|
203
207
|
|
@@ -210,7 +214,7 @@ module Puma
|
|
210
214
|
# value would be 4 until it finishes processing.
|
211
215
|
# @!attribute [r] pool_capacity
|
212
216
|
def pool_capacity
|
213
|
-
@thread_pool
|
217
|
+
@thread_pool&.pool_capacity
|
214
218
|
end
|
215
219
|
|
216
220
|
# Runs the server.
|
@@ -219,35 +223,23 @@ module Puma
|
|
219
223
|
# up in the background to handle requests. Otherwise requests
|
220
224
|
# are handled synchronously.
|
221
225
|
#
|
222
|
-
def run(background=true, thread_name: '
|
226
|
+
def run(background=true, thread_name: 'srv')
|
223
227
|
BasicSocket.do_not_reverse_lookup = true
|
224
228
|
|
225
229
|
@events.fire :state, :booting
|
226
230
|
|
227
231
|
@status = :run
|
228
232
|
|
229
|
-
@thread_pool = ThreadPool.new(
|
230
|
-
@min_threads,
|
231
|
-
@max_threads,
|
232
|
-
::Puma::IOBuffer,
|
233
|
-
&method(:process_client)
|
234
|
-
)
|
235
|
-
|
236
|
-
@thread_pool.out_of_band_hook = @options[:out_of_band]
|
237
|
-
@thread_pool.clean_thread_locals = @options[:clean_thread_locals]
|
233
|
+
@thread_pool = ThreadPool.new(thread_name, @options) { |a, b| process_client a, b }
|
238
234
|
|
239
235
|
if @queue_requests
|
240
|
-
@reactor = Reactor.new(@io_selector_backend
|
236
|
+
@reactor = Reactor.new(@io_selector_backend) { |c| reactor_wakeup c }
|
241
237
|
@reactor.run
|
242
238
|
end
|
243
239
|
|
244
|
-
if @reaping_time
|
245
|
-
@thread_pool.auto_reap!(@reaping_time)
|
246
|
-
end
|
247
240
|
|
248
|
-
if @
|
249
|
-
|
250
|
-
end
|
241
|
+
@thread_pool.auto_reap! if @options[:reaping_time]
|
242
|
+
@thread_pool.auto_trim! if @options[:auto_trim_time]
|
251
243
|
|
252
244
|
@check, @notify = Puma::Util.pipe unless @notify
|
253
245
|
|
@@ -313,14 +305,15 @@ module Puma
|
|
313
305
|
queue_requests = @queue_requests
|
314
306
|
drain = @options[:drain_on_shutdown] ? 0 : nil
|
315
307
|
|
316
|
-
|
317
|
-
remote_addr_header = nil
|
318
|
-
|
319
|
-
case @options[:remote_address]
|
308
|
+
addr_send_name, addr_value = case @options[:remote_address]
|
320
309
|
when :value
|
321
|
-
|
310
|
+
[:peerip=, @options[:remote_address_value]]
|
322
311
|
when :header
|
323
|
-
remote_addr_header
|
312
|
+
[:remote_addr_header=, @options[:remote_address_header]]
|
313
|
+
when :proxy_protocol
|
314
|
+
[:expect_proxy_proto=, @options[:remote_address_proxy_protocol]]
|
315
|
+
else
|
316
|
+
[nil, nil]
|
324
317
|
end
|
325
318
|
|
326
319
|
while @status == :run || (drain && shutting_down?)
|
@@ -340,22 +333,21 @@ module Puma
|
|
340
333
|
next
|
341
334
|
end
|
342
335
|
drain += 1 if shutting_down?
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
elsif remote_addr_header
|
348
|
-
client.remote_addr_header = remote_addr_header
|
349
|
-
end
|
350
|
-
pool << client
|
336
|
+
pool << Client.new(io, @binder.env(sock)).tap { |c|
|
337
|
+
c.listener = sock
|
338
|
+
c.send(addr_send_name, addr_value) if addr_value
|
339
|
+
}
|
351
340
|
end
|
352
341
|
end
|
353
|
-
rescue
|
354
|
-
|
342
|
+
rescue IOError, Errno::EBADF
|
343
|
+
# In the case that any of the sockets are unexpectedly close.
|
344
|
+
raise
|
345
|
+
rescue StandardError => e
|
346
|
+
@log_writer.unknown_error e, nil, "Listen loop"
|
355
347
|
end
|
356
348
|
end
|
357
349
|
|
358
|
-
@
|
350
|
+
@log_writer.debug "Drained #{drain} additional connections." if drain
|
359
351
|
@events.fire :state, @status
|
360
352
|
|
361
353
|
if queue_requests
|
@@ -364,15 +356,15 @@ module Puma
|
|
364
356
|
end
|
365
357
|
graceful_shutdown if @status == :stop || @status == :restart
|
366
358
|
rescue Exception => e
|
367
|
-
@
|
359
|
+
@log_writer.unknown_error e, nil, "Exception handling servers"
|
368
360
|
ensure
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
361
|
+
# Errno::EBADF is infrequently raised
|
362
|
+
[@check, @notify].each do |io|
|
363
|
+
begin
|
364
|
+
io.close unless io.closed?
|
365
|
+
rescue Errno::EBADF
|
366
|
+
end
|
374
367
|
end
|
375
|
-
@notify.close
|
376
368
|
@notify = nil
|
377
369
|
@check = nil
|
378
370
|
end
|
@@ -396,7 +388,7 @@ module Puma
|
|
396
388
|
return true
|
397
389
|
end
|
398
390
|
|
399
|
-
|
391
|
+
false
|
400
392
|
end
|
401
393
|
|
402
394
|
# Given a connection on +client+, handle the incoming requests,
|
@@ -482,10 +474,10 @@ module Puma
|
|
482
474
|
begin
|
483
475
|
client.close if close_socket
|
484
476
|
rescue IOError, SystemCallError
|
485
|
-
|
477
|
+
Puma::Util.purge_interrupt_queue
|
486
478
|
# Already closed
|
487
479
|
rescue StandardError => e
|
488
|
-
@
|
480
|
+
@log_writer.unknown_error e, nil, "Client"
|
489
481
|
end
|
490
482
|
end
|
491
483
|
end
|
@@ -508,13 +500,16 @@ module Puma
|
|
508
500
|
lowlevel_error(e, client.env)
|
509
501
|
case e
|
510
502
|
when MiniSSL::SSLError
|
511
|
-
@
|
503
|
+
@log_writer.ssl_error e, client.io
|
512
504
|
when HttpParserError
|
513
505
|
client.write_error(400)
|
514
|
-
@
|
506
|
+
@log_writer.parse_error e, client
|
507
|
+
when HttpParserError501
|
508
|
+
client.write_error(501)
|
509
|
+
@log_writer.parse_error e, client
|
515
510
|
else
|
516
511
|
client.write_error(500)
|
517
|
-
@
|
512
|
+
@log_writer.unknown_error e, nil, "Read"
|
518
513
|
end
|
519
514
|
end
|
520
515
|
|
@@ -574,11 +569,11 @@ module Puma
|
|
574
569
|
@notify << message
|
575
570
|
rescue IOError, NoMethodError, Errno::EPIPE
|
576
571
|
# The server, in another thread, is shutting down
|
577
|
-
|
572
|
+
Puma::Util.purge_interrupt_queue
|
578
573
|
rescue RuntimeError => e
|
579
574
|
# Temporary workaround for https://bugs.ruby-lang.org/issues/13239
|
580
575
|
if e.message.include?('IOError')
|
581
|
-
|
576
|
+
Puma::Util.purge_interrupt_queue
|
582
577
|
else
|
583
578
|
raise e
|
584
579
|
end
|
data/lib/puma/single.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
require_relative 'runner'
|
4
|
+
require_relative 'detect'
|
5
|
+
require_relative 'plugin'
|
6
6
|
|
7
7
|
module Puma
|
8
8
|
# This class is instantiated by the `Puma::Launcher` and used
|
@@ -17,25 +17,25 @@ module Puma
|
|
17
17
|
def stats
|
18
18
|
{
|
19
19
|
started_at: @started_at.utc.iso8601
|
20
|
-
}.merge(@server.stats)
|
20
|
+
}.merge(@server.stats).merge(super)
|
21
21
|
end
|
22
22
|
|
23
23
|
def restart
|
24
|
-
@server
|
24
|
+
@server&.begin_restart
|
25
25
|
end
|
26
26
|
|
27
27
|
def stop
|
28
|
-
@server
|
28
|
+
@server&.stop false
|
29
29
|
end
|
30
30
|
|
31
31
|
def halt
|
32
|
-
@server
|
32
|
+
@server&.halt
|
33
33
|
end
|
34
34
|
|
35
35
|
def stop_blocked
|
36
36
|
log "- Gracefully stopping, waiting for requests to finish"
|
37
|
-
@control
|
38
|
-
@server
|
37
|
+
@control&.stop true
|
38
|
+
@server&.stop true
|
39
39
|
end
|
40
40
|
|
41
41
|
def run
|
@@ -55,7 +55,7 @@ module Puma
|
|
55
55
|
log "Use Ctrl-C to stop"
|
56
56
|
redirect_io
|
57
57
|
|
58
|
-
@
|
58
|
+
@events.fire_on_booted!
|
59
59
|
|
60
60
|
begin
|
61
61
|
server_thread.join
|
data/lib/puma/state_file.rb
CHANGED
@@ -1,15 +1,37 @@
|
|
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
|
+
|
7
18
|
def initialize
|
8
19
|
@options = {}
|
9
20
|
end
|
10
21
|
|
11
22
|
def save(path, permission = nil)
|
12
|
-
contents =
|
23
|
+
contents = +"---\n"
|
24
|
+
@options.each do |k,v|
|
25
|
+
next unless ALLOWED_FIELDS.include? k
|
26
|
+
case v
|
27
|
+
when Numeric
|
28
|
+
contents << "#{k}: #{v}\n"
|
29
|
+
when String
|
30
|
+
next if v.strip.empty?
|
31
|
+
contents << (k == 'running_from' || v.to_s.include?(' ') ?
|
32
|
+
"#{k}: \"#{v}\"\n" : "#{k}: #{v}\n")
|
33
|
+
end
|
34
|
+
end
|
13
35
|
if permission
|
14
36
|
File.write path, contents, mode: 'wb:UTF-8'
|
15
37
|
else
|
@@ -18,12 +40,22 @@ module Puma
|
|
18
40
|
end
|
19
41
|
|
20
42
|
def load(path)
|
21
|
-
|
43
|
+
File.read(path).lines.each do |line|
|
44
|
+
next if line.start_with? '#'
|
45
|
+
k,v = line.split ':', 2
|
46
|
+
next unless v && ALLOWED_FIELDS.include?(k)
|
47
|
+
v = v.strip
|
48
|
+
@options[k] =
|
49
|
+
case v
|
50
|
+
when '' then nil
|
51
|
+
when /\A\d+\z/ then v.to_i
|
52
|
+
when /\A\d+\.\d+\z/ then v.to_f
|
53
|
+
else v.gsub(/\A"|"\z/, '')
|
54
|
+
end
|
55
|
+
end
|
22
56
|
end
|
23
57
|
|
24
|
-
|
25
|
-
|
26
|
-
FIELDS.each do |f|
|
58
|
+
ALLOWED_FIELDS.each do |f|
|
27
59
|
define_method f do
|
28
60
|
@options[f]
|
29
61
|
end
|
data/lib/puma/systemd.rb
CHANGED
@@ -4,7 +4,8 @@ require 'sd_notify'
|
|
4
4
|
|
5
5
|
module Puma
|
6
6
|
class Systemd
|
7
|
-
def initialize(events)
|
7
|
+
def initialize(log_writer, events)
|
8
|
+
@log_writer = log_writer
|
8
9
|
@events = events
|
9
10
|
end
|
10
11
|
|
@@ -40,7 +41,7 @@ module Puma
|
|
40
41
|
end
|
41
42
|
|
42
43
|
def log(str)
|
43
|
-
@
|
44
|
+
@log_writer.log(str)
|
44
45
|
end
|
45
46
|
end
|
46
47
|
end
|