puma 5.5.2 → 6.3.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 +336 -3
- data/README.md +61 -16
- data/bin/puma-wild +1 -1
- data/docs/architecture.md +4 -4
- data/docs/compile_options.md +34 -0
- data/docs/fork_worker.md +1 -3
- data/docs/nginx.md +1 -1
- data/docs/signals.md +1 -0
- data/docs/systemd.md +1 -2
- data/docs/testing_benchmarks_local_files.md +150 -0
- data/docs/testing_test_rackup_ci_files.md +36 -0
- data/ext/puma_http11/extconf.rb +28 -14
- data/ext/puma_http11/http11_parser.c +1 -1
- 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 +2 -2
- data/ext/puma_http11/mini_ssl.c +135 -23
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +1 -1
- 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 +7 -4
- data/lib/puma/binder.rb +62 -51
- data/lib/puma/cli.rb +19 -20
- data/lib/puma/client.rb +108 -26
- data/lib/puma/cluster/worker.rb +23 -16
- data/lib/puma/cluster/worker_handle.rb +8 -1
- data/lib/puma/cluster.rb +62 -41
- data/lib/puma/commonlogger.rb +21 -14
- data/lib/puma/configuration.rb +76 -55
- data/lib/puma/const.rb +133 -97
- data/lib/puma/control_cli.rb +21 -18
- data/lib/puma/detect.rb +12 -2
- data/lib/puma/dsl.rb +270 -55
- data/lib/puma/error_logger.rb +18 -9
- data/lib/puma/events.rb +6 -126
- data/lib/puma/io_buffer.rb +39 -4
- data/lib/puma/jruby_restart.rb +2 -1
- data/lib/puma/launcher/bundle_pruner.rb +104 -0
- data/lib/puma/launcher.rb +114 -175
- data/lib/puma/log_writer.rb +147 -0
- data/lib/puma/minissl/context_builder.rb +30 -16
- data/lib/puma/minissl.rb +126 -17
- data/lib/puma/null_io.rb +5 -0
- data/lib/puma/plugin/systemd.rb +90 -0
- data/lib/puma/plugin/tmp_restart.rb +1 -1
- data/lib/puma/plugin.rb +1 -1
- data/lib/puma/rack/builder.rb +6 -6
- data/lib/puma/rack_default.rb +19 -4
- data/lib/puma/reactor.rb +19 -10
- data/lib/puma/request.rb +365 -161
- data/lib/puma/runner.rb +55 -22
- data/lib/puma/sd_notify.rb +149 -0
- data/lib/puma/server.rb +91 -94
- data/lib/puma/single.rb +13 -11
- data/lib/puma/state_file.rb +39 -7
- data/lib/puma/thread_pool.rb +25 -21
- data/lib/puma/util.rb +12 -14
- data/lib/puma.rb +12 -11
- data/lib/rack/handler/puma.rb +113 -86
- data/tools/Dockerfile +1 -1
- metadata +11 -6
- data/lib/puma/queue_close.rb +0 -26
- data/lib/puma/systemd.rb +0 -46
data/lib/puma/runner.rb
CHANGED
@@ -1,23 +1,29 @@
|
|
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
|
18
20
|
@wakeup = nil
|
19
21
|
end
|
20
22
|
|
23
|
+
# Returns the hash of configuration options.
|
24
|
+
# @return [Puma::UserFileDefaultOptions]
|
25
|
+
attr_reader :options
|
26
|
+
|
21
27
|
def wakeup!
|
22
28
|
return unless @wakeup
|
23
29
|
|
@@ -36,27 +42,27 @@ module Puma
|
|
36
42
|
end
|
37
43
|
|
38
44
|
def log(str)
|
39
|
-
@
|
45
|
+
@log_writer.log str
|
40
46
|
end
|
41
47
|
|
42
48
|
# @version 5.0.0
|
43
49
|
def stop_control
|
44
|
-
@control
|
50
|
+
@control&.stop true
|
45
51
|
end
|
46
52
|
|
47
53
|
def error(str)
|
48
|
-
@
|
54
|
+
@log_writer.error str
|
49
55
|
end
|
50
56
|
|
51
57
|
def debug(str)
|
52
|
-
@
|
58
|
+
@log_writer.log "- #{str}" if @options[:debug]
|
53
59
|
end
|
54
60
|
|
55
61
|
def start_control
|
56
62
|
str = @options[:control_url]
|
57
63
|
return unless str
|
58
64
|
|
59
|
-
|
65
|
+
require_relative 'app/status'
|
60
66
|
|
61
67
|
if token = @options[:control_auth_token]
|
62
68
|
token = nil if token.empty? || token == 'none'
|
@@ -64,12 +70,14 @@ module Puma
|
|
64
70
|
|
65
71
|
app = Puma::App::Status.new @launcher, token
|
66
72
|
|
67
|
-
|
68
|
-
|
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 }
|
69
77
|
|
70
|
-
control.binder.parse [str],
|
78
|
+
control.binder.parse [str], nil, 'Starting control server'
|
71
79
|
|
72
|
-
control.run thread_name: '
|
80
|
+
control.run thread_name: 'ctl'
|
73
81
|
@control = control
|
74
82
|
end
|
75
83
|
|
@@ -94,12 +102,13 @@ module Puma
|
|
94
102
|
def output_header(mode)
|
95
103
|
min_t = @options[:min_threads]
|
96
104
|
max_t = @options[:max_threads]
|
105
|
+
environment = @options[:environment]
|
97
106
|
|
98
107
|
log "Puma starting in #{mode} mode..."
|
99
108
|
log "* Puma version: #{Puma::Const::PUMA_VERSION} (#{ruby_engine}) (\"#{Puma::Const::CODE_NAME}\")"
|
100
109
|
log "* Min threads: #{min_t}"
|
101
110
|
log "* Max threads: #{max_t}"
|
102
|
-
log "* Environment: #{
|
111
|
+
log "* Environment: #{environment}"
|
103
112
|
|
104
113
|
if mode == "cluster"
|
105
114
|
log "* Master PID: #{Process.pid}"
|
@@ -140,29 +149,29 @@ module Puma
|
|
140
149
|
end
|
141
150
|
|
142
151
|
def load_and_bind
|
143
|
-
unless @
|
152
|
+
unless @config.app_configured?
|
144
153
|
error "No application configured, nothing to run"
|
145
154
|
exit 1
|
146
155
|
end
|
147
156
|
|
148
157
|
begin
|
149
|
-
@app = @
|
158
|
+
@app = @config.app
|
150
159
|
rescue Exception => e
|
151
160
|
log "! Unable to load application: #{e.class}: #{e.message}"
|
152
161
|
raise e
|
153
162
|
end
|
154
163
|
|
155
|
-
@launcher.binder.parse @options[:binds]
|
164
|
+
@launcher.binder.parse @options[:binds]
|
156
165
|
end
|
157
166
|
|
158
167
|
# @!attribute [r] app
|
159
168
|
def app
|
160
|
-
@app ||= @
|
169
|
+
@app ||= @config.app
|
161
170
|
end
|
162
171
|
|
163
172
|
def start_server
|
164
|
-
server = Puma::Server.new
|
165
|
-
server.inherit_binder
|
173
|
+
server = Puma::Server.new(app, @events, @options)
|
174
|
+
server.inherit_binder(@launcher.binder)
|
166
175
|
server
|
167
176
|
end
|
168
177
|
|
@@ -172,5 +181,29 @@ module Puma
|
|
172
181
|
raise "Cannot redirect #{io_name} to #{path}"
|
173
182
|
end
|
174
183
|
end
|
184
|
+
|
185
|
+
def utc_iso8601(val)
|
186
|
+
"#{val.utc.strftime '%FT%T'}Z"
|
187
|
+
end
|
188
|
+
|
189
|
+
def stats
|
190
|
+
{
|
191
|
+
versions: {
|
192
|
+
puma: Puma::Const::PUMA_VERSION,
|
193
|
+
ruby: {
|
194
|
+
engine: RUBY_ENGINE,
|
195
|
+
version: RUBY_VERSION,
|
196
|
+
patchlevel: RUBY_PATCHLEVEL
|
197
|
+
}
|
198
|
+
}
|
199
|
+
}
|
200
|
+
end
|
201
|
+
|
202
|
+
# this method call should always be guarded by `@log_writer.debug?`
|
203
|
+
def debug_loaded_extensions(str)
|
204
|
+
@log_writer.debug "────────────────────────────────── #{str}"
|
205
|
+
re_ext = /\.#{RbConfig::CONFIG['DLEXT']}\z/i
|
206
|
+
$LOADED_FEATURES.grep(re_ext).each { |f| @log_writer.debug(" #{f}") }
|
207
|
+
end
|
175
208
|
end
|
176
209
|
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "socket"
|
4
|
+
|
5
|
+
module Puma
|
6
|
+
# The MIT License
|
7
|
+
#
|
8
|
+
# Copyright (c) 2017-2022 Agis Anastasopoulos
|
9
|
+
#
|
10
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
11
|
+
# this software and associated documentation files (the "Software"), to deal in
|
12
|
+
# the Software without restriction, including without limitation the rights to
|
13
|
+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
14
|
+
# the Software, and to permit persons to whom the Software is furnished to do so,
|
15
|
+
# subject to the following conditions:
|
16
|
+
#
|
17
|
+
# The above copyright notice and this permission notice shall be included in all
|
18
|
+
# copies or substantial portions of the Software.
|
19
|
+
#
|
20
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
21
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
22
|
+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
23
|
+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
24
|
+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
25
|
+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
26
|
+
#
|
27
|
+
# This is a copy of https://github.com/agis/ruby-sdnotify as of commit cca575c
|
28
|
+
# The only changes made was "rehoming" it within the Puma module to avoid
|
29
|
+
# namespace collisions and applying standard's code formatting style.
|
30
|
+
#
|
31
|
+
# SdNotify is a pure-Ruby implementation of sd_notify(3). It can be used to
|
32
|
+
# notify systemd about state changes. Methods of this package are no-op on
|
33
|
+
# non-systemd systems (eg. Darwin).
|
34
|
+
#
|
35
|
+
# The API maps closely to the original implementation of sd_notify(3),
|
36
|
+
# therefore be sure to check the official man pages prior to using SdNotify.
|
37
|
+
#
|
38
|
+
# @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
39
|
+
module SdNotify
|
40
|
+
# Exception raised when there's an error writing to the notification socket
|
41
|
+
class NotifyError < RuntimeError; end
|
42
|
+
|
43
|
+
READY = "READY=1"
|
44
|
+
RELOADING = "RELOADING=1"
|
45
|
+
STOPPING = "STOPPING=1"
|
46
|
+
STATUS = "STATUS="
|
47
|
+
ERRNO = "ERRNO="
|
48
|
+
MAINPID = "MAINPID="
|
49
|
+
WATCHDOG = "WATCHDOG=1"
|
50
|
+
FDSTORE = "FDSTORE=1"
|
51
|
+
|
52
|
+
def self.ready(unset_env=false)
|
53
|
+
notify(READY, unset_env)
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.reloading(unset_env=false)
|
57
|
+
notify(RELOADING, unset_env)
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.stopping(unset_env=false)
|
61
|
+
notify(STOPPING, unset_env)
|
62
|
+
end
|
63
|
+
|
64
|
+
# @param status [String] a custom status string that describes the current
|
65
|
+
# state of the service
|
66
|
+
def self.status(status, unset_env=false)
|
67
|
+
notify("#{STATUS}#{status}", unset_env)
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param errno [Integer]
|
71
|
+
def self.errno(errno, unset_env=false)
|
72
|
+
notify("#{ERRNO}#{errno}", unset_env)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param pid [Integer]
|
76
|
+
def self.mainpid(pid, unset_env=false)
|
77
|
+
notify("#{MAINPID}#{pid}", unset_env)
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.watchdog(unset_env=false)
|
81
|
+
notify(WATCHDOG, unset_env)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.fdstore(unset_env=false)
|
85
|
+
notify(FDSTORE, unset_env)
|
86
|
+
end
|
87
|
+
|
88
|
+
# @param [Boolean] true if the service manager expects watchdog keep-alive
|
89
|
+
# notification messages to be sent from this process.
|
90
|
+
#
|
91
|
+
# If the $WATCHDOG_USEC environment variable is set,
|
92
|
+
# and the $WATCHDOG_PID variable is unset or set to the PID of the current
|
93
|
+
# process
|
94
|
+
#
|
95
|
+
# @note Unlike sd_watchdog_enabled(3), this method does not mutate the
|
96
|
+
# environment.
|
97
|
+
def self.watchdog?
|
98
|
+
wd_usec = ENV["WATCHDOG_USEC"]
|
99
|
+
wd_pid = ENV["WATCHDOG_PID"]
|
100
|
+
|
101
|
+
return false if !wd_usec
|
102
|
+
|
103
|
+
begin
|
104
|
+
wd_usec = Integer(wd_usec)
|
105
|
+
rescue
|
106
|
+
return false
|
107
|
+
end
|
108
|
+
|
109
|
+
return false if wd_usec <= 0
|
110
|
+
return true if !wd_pid || wd_pid == $$.to_s
|
111
|
+
|
112
|
+
false
|
113
|
+
end
|
114
|
+
|
115
|
+
# Notify systemd with the provided state, via the notification socket, if
|
116
|
+
# any.
|
117
|
+
#
|
118
|
+
# Generally this method will be used indirectly through the other methods
|
119
|
+
# of the library.
|
120
|
+
#
|
121
|
+
# @param state [String]
|
122
|
+
# @param unset_env [Boolean]
|
123
|
+
#
|
124
|
+
# @return [Fixnum, nil] the number of bytes written to the notification
|
125
|
+
# socket or nil if there was no socket to report to (eg. the program wasn't
|
126
|
+
# started by systemd)
|
127
|
+
#
|
128
|
+
# @raise [NotifyError] if there was an error communicating with the systemd
|
129
|
+
# socket
|
130
|
+
#
|
131
|
+
# @see https://www.freedesktop.org/software/systemd/man/sd_notify.html
|
132
|
+
def self.notify(state, unset_env=false)
|
133
|
+
sock = ENV["NOTIFY_SOCKET"]
|
134
|
+
|
135
|
+
return nil if !sock
|
136
|
+
|
137
|
+
ENV.delete("NOTIFY_SOCKET") if unset_env
|
138
|
+
|
139
|
+
begin
|
140
|
+
Addrinfo.unix(sock, :DGRAM).connect do |s|
|
141
|
+
s.close_on_exec = true
|
142
|
+
s.write(state)
|
143
|
+
end
|
144
|
+
rescue StandardError => e
|
145
|
+
raise NotifyError, "#{e.class}: #{e.message}", e.backtrace
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/lib/puma/server.rb
CHANGED
@@ -2,19 +2,19 @@
|
|
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 'request'
|
15
15
|
|
16
16
|
require 'socket'
|
17
|
-
require 'io/wait'
|
17
|
+
require 'io/wait' unless Puma::HAS_NATIVE_IO_WAIT
|
18
18
|
require 'forwardable'
|
19
19
|
|
20
20
|
module Puma
|
@@ -30,12 +30,12 @@ module Puma
|
|
30
30
|
#
|
31
31
|
# Each `Puma::Server` will have one reactor and one thread pool.
|
32
32
|
class Server
|
33
|
-
|
34
33
|
include Puma::Const
|
35
34
|
include Request
|
36
35
|
extend Forwardable
|
37
36
|
|
38
37
|
attr_reader :thread
|
38
|
+
attr_reader :log_writer
|
39
39
|
attr_reader :events
|
40
40
|
attr_reader :min_threads, :max_threads # for #stats
|
41
41
|
attr_reader :requests_count # @version 5.0.0
|
@@ -45,23 +45,19 @@ module Puma
|
|
45
45
|
:leak_stack_on_error,
|
46
46
|
:persistent_timeout, :reaping_time
|
47
47
|
|
48
|
-
# @deprecated v6.0.0
|
49
|
-
attr_writer :auto_trim_time, :early_hints, :first_data_timeout,
|
50
|
-
:leak_stack_on_error, :min_threads, :max_threads,
|
51
|
-
:persistent_timeout, :reaping_time
|
52
|
-
|
53
48
|
attr_accessor :app
|
54
49
|
attr_accessor :binder
|
55
50
|
|
56
51
|
def_delegators :@binder, :add_tcp_listener, :add_ssl_listener,
|
57
52
|
:add_unix_listener, :connected_ports
|
58
53
|
|
59
|
-
|
54
|
+
THREAD_LOCAL_KEY = :puma_server
|
60
55
|
|
61
56
|
# Create a server for the rack app +app+.
|
62
57
|
#
|
63
|
-
# +
|
64
|
-
#
|
58
|
+
# +log_writer+ is a Puma::LogWriter object used to log info and error messages.
|
59
|
+
#
|
60
|
+
# +events+ is a Puma::Events object used to notify application status events.
|
65
61
|
#
|
66
62
|
# Server#run returns a thread that you can join on to wait for the server
|
67
63
|
# to do its work.
|
@@ -70,34 +66,53 @@ module Puma
|
|
70
66
|
# and have default values set via +fetch+. Normally the values are set via
|
71
67
|
# `::Puma::Configuration.puma_default_options`.
|
72
68
|
#
|
73
|
-
|
69
|
+
# @note The `events` parameter is set to nil, and set to `Events.new` in code.
|
70
|
+
# Often `options` needs to be passed, but `events` does not. Using nil allows
|
71
|
+
# calling code to not require events.rb.
|
72
|
+
#
|
73
|
+
def initialize(app, events = nil, options = {})
|
74
74
|
@app = app
|
75
|
-
@events = events
|
75
|
+
@events = events || Events.new
|
76
76
|
|
77
77
|
@check, @notify = nil
|
78
78
|
@status = :stop
|
79
79
|
|
80
|
-
@auto_trim_time = 30
|
81
|
-
@reaping_time = 1
|
82
|
-
|
83
80
|
@thread = nil
|
84
81
|
@thread_pool = nil
|
85
82
|
|
86
|
-
@options = options
|
83
|
+
@options = if options.is_a?(UserFileDefaultOptions)
|
84
|
+
options
|
85
|
+
else
|
86
|
+
UserFileDefaultOptions.new(options, Configuration::DEFAULTS)
|
87
|
+
end
|
87
88
|
|
88
|
-
@
|
89
|
-
@
|
90
|
-
@
|
91
|
-
@
|
92
|
-
@
|
93
|
-
@
|
94
|
-
@
|
95
|
-
@
|
89
|
+
@log_writer = @options.fetch :log_writer, LogWriter.stdio
|
90
|
+
@early_hints = @options[:early_hints]
|
91
|
+
@first_data_timeout = @options[:first_data_timeout]
|
92
|
+
@min_threads = @options[:min_threads]
|
93
|
+
@max_threads = @options[:max_threads]
|
94
|
+
@persistent_timeout = @options[:persistent_timeout]
|
95
|
+
@queue_requests = @options[:queue_requests]
|
96
|
+
@max_fast_inline = @options[:max_fast_inline]
|
97
|
+
@io_selector_backend = @options[:io_selector_backend]
|
98
|
+
@http_content_length_limit = @options[:http_content_length_limit]
|
99
|
+
|
100
|
+
# make this a hash, since we prefer `key?` over `include?`
|
101
|
+
@supported_http_methods =
|
102
|
+
if @options[:supported_http_methods] == :any
|
103
|
+
:any
|
104
|
+
else
|
105
|
+
if (ary = @options[:supported_http_methods])
|
106
|
+
ary
|
107
|
+
else
|
108
|
+
SUPPORTED_HTTP_METHODS
|
109
|
+
end.sort.product([nil]).to_h.freeze
|
110
|
+
end
|
96
111
|
|
97
112
|
temp = !!(@options[:environment] =~ /\A(development|test)\z/)
|
98
113
|
@leak_stack_on_error = @options[:environment] ? temp : true
|
99
114
|
|
100
|
-
@binder = Binder.new(
|
115
|
+
@binder = Binder.new(log_writer)
|
101
116
|
|
102
117
|
ENV['RACK_ENV'] ||= "development"
|
103
118
|
|
@@ -115,7 +130,7 @@ module Puma
|
|
115
130
|
class << self
|
116
131
|
# @!attribute [r] current
|
117
132
|
def current
|
118
|
-
Thread.current[
|
133
|
+
Thread.current[THREAD_LOCAL_KEY]
|
119
134
|
end
|
120
135
|
|
121
136
|
# :nodoc:
|
@@ -193,12 +208,12 @@ module Puma
|
|
193
208
|
|
194
209
|
# @!attribute [r] backlog
|
195
210
|
def backlog
|
196
|
-
@thread_pool
|
211
|
+
@thread_pool&.backlog
|
197
212
|
end
|
198
213
|
|
199
214
|
# @!attribute [r] running
|
200
215
|
def running
|
201
|
-
@thread_pool
|
216
|
+
@thread_pool&.spawned
|
202
217
|
end
|
203
218
|
|
204
219
|
|
@@ -211,7 +226,7 @@ module Puma
|
|
211
226
|
# value would be 4 until it finishes processing.
|
212
227
|
# @!attribute [r] pool_capacity
|
213
228
|
def pool_capacity
|
214
|
-
@thread_pool
|
229
|
+
@thread_pool&.pool_capacity
|
215
230
|
end
|
216
231
|
|
217
232
|
# Runs the server.
|
@@ -220,36 +235,23 @@ module Puma
|
|
220
235
|
# up in the background to handle requests. Otherwise requests
|
221
236
|
# are handled synchronously.
|
222
237
|
#
|
223
|
-
def run(background=true, thread_name: '
|
238
|
+
def run(background=true, thread_name: 'srv')
|
224
239
|
BasicSocket.do_not_reverse_lookup = true
|
225
240
|
|
226
241
|
@events.fire :state, :booting
|
227
242
|
|
228
243
|
@status = :run
|
229
244
|
|
230
|
-
@thread_pool = ThreadPool.new(
|
231
|
-
thread_name,
|
232
|
-
@min_threads,
|
233
|
-
@max_threads,
|
234
|
-
::Puma::IOBuffer,
|
235
|
-
&method(:process_client)
|
236
|
-
)
|
237
|
-
|
238
|
-
@thread_pool.out_of_band_hook = @options[:out_of_band]
|
239
|
-
@thread_pool.clean_thread_locals = @options[:clean_thread_locals]
|
245
|
+
@thread_pool = ThreadPool.new(thread_name, @options) { |client| process_client client }
|
240
246
|
|
241
247
|
if @queue_requests
|
242
|
-
@reactor = Reactor.new(@io_selector_backend
|
248
|
+
@reactor = Reactor.new(@io_selector_backend) { |c| reactor_wakeup c }
|
243
249
|
@reactor.run
|
244
250
|
end
|
245
251
|
|
246
|
-
if @reaping_time
|
247
|
-
@thread_pool.auto_reap!(@reaping_time)
|
248
|
-
end
|
249
252
|
|
250
|
-
if @
|
251
|
-
|
252
|
-
end
|
253
|
+
@thread_pool.auto_reap! if @options[:reaping_time]
|
254
|
+
@thread_pool.auto_trim! if @options[:auto_trim_time]
|
253
255
|
|
254
256
|
@check, @notify = Puma::Util.pipe unless @notify
|
255
257
|
|
@@ -315,16 +317,15 @@ module Puma
|
|
315
317
|
queue_requests = @queue_requests
|
316
318
|
drain = @options[:drain_on_shutdown] ? 0 : nil
|
317
319
|
|
318
|
-
|
319
|
-
remote_addr_header = nil
|
320
|
-
|
321
|
-
case @options[:remote_address]
|
320
|
+
addr_send_name, addr_value = case @options[:remote_address]
|
322
321
|
when :value
|
323
|
-
|
322
|
+
[:peerip=, @options[:remote_address_value]]
|
324
323
|
when :header
|
325
|
-
remote_addr_header
|
324
|
+
[:remote_addr_header=, @options[:remote_address_header]]
|
326
325
|
when :proxy_protocol
|
327
|
-
|
326
|
+
[:expect_proxy_proto=, @options[:remote_address_proxy_protocol]]
|
327
|
+
else
|
328
|
+
[nil, nil]
|
328
329
|
end
|
329
330
|
|
330
331
|
while @status == :run || (drain && shutting_down?)
|
@@ -344,27 +345,22 @@ module Puma
|
|
344
345
|
next
|
345
346
|
end
|
346
347
|
drain += 1 if shutting_down?
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
client.remote_addr_header = remote_addr_header
|
353
|
-
elsif remote_addr_proxy_protocol
|
354
|
-
client.expect_proxy_proto = remote_addr_proxy_protocol
|
355
|
-
end
|
356
|
-
pool << client
|
348
|
+
pool << Client.new(io, @binder.env(sock)).tap { |c|
|
349
|
+
c.listener = sock
|
350
|
+
c.http_content_length_limit = @http_content_length_limit
|
351
|
+
c.send(addr_send_name, addr_value) if addr_value
|
352
|
+
}
|
357
353
|
end
|
358
354
|
end
|
359
355
|
rescue IOError, Errno::EBADF
|
360
356
|
# In the case that any of the sockets are unexpectedly close.
|
361
357
|
raise
|
362
358
|
rescue StandardError => e
|
363
|
-
@
|
359
|
+
@log_writer.unknown_error e, nil, "Listen loop"
|
364
360
|
end
|
365
361
|
end
|
366
362
|
|
367
|
-
@
|
363
|
+
@log_writer.debug "Drained #{drain} additional connections." if drain
|
368
364
|
@events.fire :state, @status
|
369
365
|
|
370
366
|
if queue_requests
|
@@ -373,15 +369,15 @@ module Puma
|
|
373
369
|
end
|
374
370
|
graceful_shutdown if @status == :stop || @status == :restart
|
375
371
|
rescue Exception => e
|
376
|
-
@
|
372
|
+
@log_writer.unknown_error e, nil, "Exception handling servers"
|
377
373
|
ensure
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
374
|
+
# Errno::EBADF is infrequently raised
|
375
|
+
[@check, @notify].each do |io|
|
376
|
+
begin
|
377
|
+
io.close unless io.closed?
|
378
|
+
rescue Errno::EBADF
|
379
|
+
end
|
383
380
|
end
|
384
|
-
@notify.close
|
385
381
|
@notify = nil
|
386
382
|
@check = nil
|
387
383
|
end
|
@@ -418,9 +414,9 @@ module Puma
|
|
418
414
|
# returning.
|
419
415
|
#
|
420
416
|
# Return true if one or more requests were processed.
|
421
|
-
def process_client(client
|
417
|
+
def process_client(client)
|
422
418
|
# Advertise this server into the thread
|
423
|
-
Thread.current[
|
419
|
+
Thread.current[THREAD_LOCAL_KEY] = self
|
424
420
|
|
425
421
|
clean_thread_locals = @options[:clean_thread_locals]
|
426
422
|
close_socket = true
|
@@ -444,15 +440,13 @@ module Puma
|
|
444
440
|
|
445
441
|
while true
|
446
442
|
@requests_count += 1
|
447
|
-
case handle_request(client,
|
443
|
+
case handle_request(client, requests + 1)
|
448
444
|
when false
|
449
445
|
break
|
450
446
|
when :async
|
451
447
|
close_socket = false
|
452
448
|
break
|
453
449
|
when true
|
454
|
-
buffer.reset
|
455
|
-
|
456
450
|
ThreadPool.clean_thread_locals if clean_thread_locals
|
457
451
|
|
458
452
|
requests += 1
|
@@ -486,7 +480,7 @@ module Puma
|
|
486
480
|
# The ensure tries to close +client+ down
|
487
481
|
requests > 0
|
488
482
|
ensure
|
489
|
-
|
483
|
+
client.io_buffer.reset
|
490
484
|
|
491
485
|
begin
|
492
486
|
client.close if close_socket
|
@@ -494,7 +488,7 @@ module Puma
|
|
494
488
|
Puma::Util.purge_interrupt_queue
|
495
489
|
# Already closed
|
496
490
|
rescue StandardError => e
|
497
|
-
@
|
491
|
+
@log_writer.unknown_error e, nil, "Client"
|
498
492
|
end
|
499
493
|
end
|
500
494
|
end
|
@@ -517,13 +511,16 @@ module Puma
|
|
517
511
|
lowlevel_error(e, client.env)
|
518
512
|
case e
|
519
513
|
when MiniSSL::SSLError
|
520
|
-
@
|
514
|
+
@log_writer.ssl_error e, client.io
|
521
515
|
when HttpParserError
|
522
516
|
client.write_error(400)
|
523
|
-
@
|
517
|
+
@log_writer.parse_error e, client
|
518
|
+
when HttpParserError501
|
519
|
+
client.write_error(501)
|
520
|
+
@log_writer.parse_error e, client
|
524
521
|
else
|
525
522
|
client.write_error(500)
|
526
|
-
@
|
523
|
+
@log_writer.unknown_error e, nil, "Read"
|
527
524
|
end
|
528
525
|
end
|
529
526
|
|
@@ -581,7 +578,7 @@ module Puma
|
|
581
578
|
|
582
579
|
def notify_safely(message)
|
583
580
|
@notify << message
|
584
|
-
rescue IOError, NoMethodError, Errno::EPIPE
|
581
|
+
rescue IOError, NoMethodError, Errno::EPIPE, Errno::EBADF
|
585
582
|
# The server, in another thread, is shutting down
|
586
583
|
Puma::Util.purge_interrupt_queue
|
587
584
|
rescue RuntimeError => e
|