puma 3.12.1 → 5.3.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.
Potentially problematic release.
This version of puma might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/History.md +1414 -448
- data/LICENSE +23 -20
- data/README.md +131 -60
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +24 -19
- data/docs/compile_options.md +19 -0
- data/docs/deployment.md +38 -13
- data/docs/fork_worker.md +33 -0
- data/docs/jungle/README.md +9 -0
- data/{tools → docs}/jungle/rc.d/README.md +1 -1
- data/{tools → docs}/jungle/rc.d/puma +2 -2
- data/{tools → docs}/jungle/rc.d/puma.conf +0 -0
- data/docs/kubernetes.md +66 -0
- data/docs/nginx.md +1 -1
- data/docs/plugins.md +20 -10
- data/docs/rails_dev_mode.md +29 -0
- data/docs/restart.md +47 -22
- data/docs/signals.md +7 -6
- data/docs/stats.md +142 -0
- data/docs/systemd.md +48 -70
- data/ext/puma_http11/PumaHttp11Service.java +2 -2
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +27 -0
- data/ext/puma_http11/http11_parser.c +84 -109
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +22 -38
- data/ext/puma_http11/http11_parser.rl +4 -2
- data/ext/puma_http11/http11_parser_common.rl +3 -3
- data/ext/puma_http11/mini_ssl.c +254 -91
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +108 -116
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +89 -106
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +92 -22
- data/ext/puma_http11/puma_http11.c +34 -50
- data/lib/puma.rb +54 -0
- data/lib/puma/app/status.rb +68 -49
- data/lib/puma/binder.rb +191 -139
- data/lib/puma/cli.rb +15 -15
- data/lib/puma/client.rb +257 -228
- data/lib/puma/cluster.rb +221 -212
- data/lib/puma/cluster/worker.rb +183 -0
- data/lib/puma/cluster/worker_handle.rb +90 -0
- data/lib/puma/commonlogger.rb +2 -2
- data/lib/puma/configuration.rb +58 -51
- data/lib/puma/const.rb +39 -19
- data/lib/puma/control_cli.rb +109 -67
- data/lib/puma/detect.rb +24 -3
- data/lib/puma/dsl.rb +519 -121
- data/lib/puma/error_logger.rb +104 -0
- data/lib/puma/events.rb +55 -31
- data/lib/puma/io_buffer.rb +7 -5
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/json.rb +96 -0
- data/lib/puma/launcher.rb +178 -68
- data/lib/puma/minissl.rb +147 -48
- data/lib/puma/minissl/context_builder.rb +79 -0
- data/lib/puma/null_io.rb +13 -1
- data/lib/puma/plugin.rb +6 -12
- data/lib/puma/plugin/tmp_restart.rb +2 -0
- data/lib/puma/queue_close.rb +26 -0
- data/lib/puma/rack/builder.rb +2 -4
- data/lib/puma/rack/urlmap.rb +2 -0
- data/lib/puma/rack_default.rb +2 -0
- data/lib/puma/reactor.rb +85 -316
- data/lib/puma/request.rb +467 -0
- data/lib/puma/runner.rb +31 -52
- data/lib/puma/server.rb +282 -680
- data/lib/puma/single.rb +11 -67
- data/lib/puma/state_file.rb +8 -3
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +129 -81
- data/lib/puma/util.rb +13 -6
- data/lib/rack/handler/puma.rb +5 -6
- data/tools/Dockerfile +16 -0
- data/tools/trickletest.rb +0 -1
- metadata +42 -26
- data/ext/puma_http11/io_buffer.c +0 -155
- data/lib/puma/accept_nonblock.rb +0 -23
- data/lib/puma/compat.rb +0 -14
- data/lib/puma/convenient.rb +0 -25
- data/lib/puma/daemon_ext.rb +0 -33
- data/lib/puma/delegation.rb +0 -13
- data/lib/puma/java_io_buffer.rb +0 -47
- data/lib/puma/rack/backports/uri/common_193.rb +0 -33
- data/lib/puma/tcp_logger.rb +0 -41
- data/tools/jungle/README.md +0 -19
- data/tools/jungle/init.d/README.md +0 -61
- data/tools/jungle/init.d/puma +0 -421
- data/tools/jungle/init.d/run-puma +0 -18
- data/tools/jungle/upstart/README.md +0 -61
- data/tools/jungle/upstart/puma-manager.conf +0 -31
- data/tools/jungle/upstart/puma.conf +0 -69
data/lib/puma/app/status.rb
CHANGED
@@ -1,73 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'puma/json'
|
3
|
+
|
1
4
|
module Puma
|
2
5
|
module App
|
6
|
+
# Check out {#call}'s source code to see what actions this web application
|
7
|
+
# can respond to.
|
3
8
|
class Status
|
4
|
-
def initialize(cli)
|
5
|
-
@cli = cli
|
6
|
-
@auth_token = nil
|
7
|
-
end
|
8
9
|
OK_STATUS = '{ "status": "ok" }'.freeze
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
def rack_response(status, body, content_type='application/json')
|
18
|
-
headers = {
|
19
|
-
'Content-Type' => content_type,
|
20
|
-
'Content-Length' => body.bytesize.to_s
|
21
|
-
}
|
22
|
-
|
23
|
-
[status, headers, [body]]
|
11
|
+
# @param launcher [::Puma::Launcher]
|
12
|
+
# @param token [String, nil] the token used for authentication
|
13
|
+
#
|
14
|
+
def initialize(launcher, token = nil)
|
15
|
+
@launcher = launcher
|
16
|
+
@auth_token = token
|
24
17
|
end
|
25
18
|
|
19
|
+
# most commands call methods in `::Puma::Launcher` based on command in
|
20
|
+
# `env['PATH_INFO']`
|
26
21
|
def call(env)
|
27
22
|
unless authenticate(env)
|
28
23
|
return rack_response(403, 'Invalid auth token', 'text/plain')
|
29
24
|
end
|
30
25
|
|
31
|
-
case
|
32
|
-
|
33
|
-
|
34
|
-
|
26
|
+
# resp_type is processed by following case statement, return
|
27
|
+
# is a number (status) or a string used as the body of a 200 response
|
28
|
+
resp_type =
|
29
|
+
case env['PATH_INFO'][/\/([^\/]+)$/, 1]
|
30
|
+
when 'stop'
|
31
|
+
@launcher.stop ; 200
|
35
32
|
|
36
|
-
|
37
|
-
|
38
|
-
return rack_response(200, OK_STATUS)
|
33
|
+
when 'halt'
|
34
|
+
@launcher.halt ; 200
|
39
35
|
|
40
|
-
|
41
|
-
|
42
|
-
return rack_response(200, OK_STATUS)
|
36
|
+
when 'restart'
|
37
|
+
@launcher.restart ; 200
|
43
38
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
39
|
+
when 'phased-restart'
|
40
|
+
@launcher.phased_restart ? 200 : 404
|
41
|
+
|
42
|
+
when 'reload-worker-directory'
|
43
|
+
@launcher.send(:reload_worker_directory) ? 200 : 404
|
44
|
+
|
45
|
+
when 'gc'
|
46
|
+
GC.start ; 200
|
47
|
+
|
48
|
+
when 'gc-stats'
|
49
|
+
Puma::JSON.generate GC.stat
|
50
|
+
|
51
|
+
when 'stats'
|
52
|
+
Puma::JSON.generate @launcher.stats
|
53
|
+
|
54
|
+
when 'thread-backtraces'
|
55
|
+
backtraces = []
|
56
|
+
@launcher.thread_status do |name, backtrace|
|
57
|
+
backtraces << { name: name, backtrace: backtrace }
|
58
|
+
end
|
59
|
+
Puma::JSON.generate backtraces
|
50
60
|
|
51
|
-
when /\/reload-worker-directory$/
|
52
|
-
if !@cli.send(:reload_worker_directory)
|
53
|
-
return rack_response(404, '{ "error": "reload_worker_directory not available" }')
|
54
61
|
else
|
55
|
-
return rack_response(
|
62
|
+
return rack_response(404, "Unsupported action", 'text/plain')
|
56
63
|
end
|
57
64
|
|
58
|
-
|
59
|
-
|
60
|
-
|
65
|
+
case resp_type
|
66
|
+
when String
|
67
|
+
rack_response 200, resp_type
|
68
|
+
when 200
|
69
|
+
rack_response 200, OK_STATUS
|
70
|
+
when 404
|
71
|
+
str = env['PATH_INFO'][/\/(\S+)/, 1].tr '-', '_'
|
72
|
+
rack_response 404, "{ \"error\": \"#{str} not available\" }"
|
73
|
+
end
|
74
|
+
end
|
61
75
|
|
62
|
-
|
63
|
-
json = "{" + GC.stat.map { |k, v| "\"#{k}\": #{v}" }.join(",") + "}"
|
64
|
-
return rack_response(200, json)
|
76
|
+
private
|
65
77
|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
78
|
+
def authenticate(env)
|
79
|
+
return true unless @auth_token
|
80
|
+
env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
|
81
|
+
end
|
82
|
+
|
83
|
+
def rack_response(status, body, content_type='application/json')
|
84
|
+
headers = {
|
85
|
+
'Content-Type' => content_type,
|
86
|
+
'Content-Length' => body.bytesize.to_s
|
87
|
+
}
|
88
|
+
|
89
|
+
[status, headers, [body]]
|
71
90
|
end
|
72
91
|
end
|
73
92
|
end
|
data/lib/puma/binder.rb
CHANGED
@@ -5,14 +5,30 @@ require 'socket'
|
|
5
5
|
|
6
6
|
require 'puma/const'
|
7
7
|
require 'puma/util'
|
8
|
+
require 'puma/configuration'
|
8
9
|
|
9
10
|
module Puma
|
11
|
+
|
12
|
+
if HAS_SSL
|
13
|
+
require 'puma/minissl'
|
14
|
+
require 'puma/minissl/context_builder'
|
15
|
+
|
16
|
+
# Odd bug in 'pure Ruby' nio4r version 2.5.2, which installs with Ruby 2.3.
|
17
|
+
# NIO doesn't create any OpenSSL objects, but it rescues an OpenSSL error.
|
18
|
+
# The bug was that it did not require openssl.
|
19
|
+
# @todo remove when Ruby 2.3 support is dropped
|
20
|
+
#
|
21
|
+
if windows? && RbConfig::CONFIG['ruby_version'] == '2.3.0'
|
22
|
+
require 'openssl'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
10
26
|
class Binder
|
11
27
|
include Puma::Const
|
12
28
|
|
13
|
-
RACK_VERSION = [1,
|
29
|
+
RACK_VERSION = [1,6].freeze
|
14
30
|
|
15
|
-
def initialize(events)
|
31
|
+
def initialize(events, conf = Configuration.new)
|
16
32
|
@events = events
|
17
33
|
@listeners = []
|
18
34
|
@inherited_fds = {}
|
@@ -22,8 +38,8 @@ module Puma
|
|
22
38
|
@proto_env = {
|
23
39
|
"rack.version".freeze => RACK_VERSION,
|
24
40
|
"rack.errors".freeze => events.stderr,
|
25
|
-
"rack.multithread".freeze =>
|
26
|
-
"rack.multiprocess".freeze =>
|
41
|
+
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
42
|
+
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
27
43
|
"rack.run_once".freeze => false,
|
28
44
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
29
45
|
|
@@ -42,7 +58,13 @@ module Puma
|
|
42
58
|
@ios = []
|
43
59
|
end
|
44
60
|
|
45
|
-
attr_reader :
|
61
|
+
attr_reader :ios
|
62
|
+
|
63
|
+
# @version 5.0.0
|
64
|
+
attr_reader :activated_sockets, :envs, :inherited_fds, :listeners, :proto_env, :unix_paths
|
65
|
+
|
66
|
+
# @version 5.0.0
|
67
|
+
attr_writer :ios, :listeners
|
46
68
|
|
47
69
|
def env(sock)
|
48
70
|
@envs.fetch(sock, @proto_env)
|
@@ -50,43 +72,83 @@ module Puma
|
|
50
72
|
|
51
73
|
def close
|
52
74
|
@ios.each { |i| i.close }
|
53
|
-
@unix_paths.each { |i| File.unlink i }
|
54
75
|
end
|
55
76
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
77
|
+
# @!attribute [r] connected_ports
|
78
|
+
# @version 5.0.0
|
79
|
+
def connected_ports
|
80
|
+
ios.map { |io| io.addr[1] }.uniq
|
81
|
+
end
|
82
|
+
|
83
|
+
# @version 5.0.0
|
84
|
+
def create_inherited_fds(env_hash)
|
85
|
+
env_hash.select {|k,v| k =~ /PUMA_INHERIT_\d+/}.each do |_k, v|
|
86
|
+
fd, url = v.split(":", 2)
|
87
|
+
@inherited_fds[url] = fd.to_i
|
88
|
+
end.keys # pass keys back for removal
|
89
|
+
end
|
90
|
+
|
91
|
+
# systemd socket activation.
|
92
|
+
# LISTEN_FDS = number of listening sockets. e.g. 2 means accept on 2 sockets w/descriptors 3 and 4.
|
93
|
+
# LISTEN_PID = PID of the service process, aka us
|
94
|
+
# @see https://www.freedesktop.org/software/systemd/man/systemd-socket-activate.html
|
95
|
+
# @version 5.0.0
|
96
|
+
#
|
97
|
+
def create_activated_fds(env_hash)
|
98
|
+
return [] unless env_hash['LISTEN_FDS'] && env_hash['LISTEN_PID'].to_i == $$
|
99
|
+
env_hash['LISTEN_FDS'].to_i.times do |index|
|
100
|
+
sock = TCPServer.for_fd(socket_activation_fd(index))
|
101
|
+
key = begin # Try to parse as a path
|
102
|
+
[:unix, Socket.unpack_sockaddr_un(sock.getsockname)]
|
103
|
+
rescue ArgumentError # Try to parse as a port/ip
|
104
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
105
|
+
addr = "[#{addr}]" if addr =~ /\:/
|
106
|
+
[:tcp, addr, port]
|
107
|
+
end
|
108
|
+
@activated_sockets[key] = sock
|
109
|
+
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
110
|
+
end
|
111
|
+
["LISTEN_FDS", "LISTEN_PID"] # Signal to remove these keys from ENV
|
112
|
+
end
|
113
|
+
|
114
|
+
# Synthesize binds from systemd socket activation
|
115
|
+
#
|
116
|
+
# When systemd socket activation is enabled, it can be tedious to keep the
|
117
|
+
# binds in sync. This method can synthesize any binds based on the received
|
118
|
+
# activated sockets. Any existing matching binds will be respected.
|
119
|
+
#
|
120
|
+
# When only_matching is true in, all binds that do not match an activated
|
121
|
+
# socket is removed in place.
|
122
|
+
#
|
123
|
+
# It's a noop if no activated sockets were received.
|
124
|
+
def synthesize_binds_from_activated_fs(binds, only_matching)
|
125
|
+
return binds unless activated_sockets.any?
|
126
|
+
|
127
|
+
activated_binds = []
|
128
|
+
|
129
|
+
activated_sockets.keys.each do |proto, addr, port|
|
130
|
+
if port
|
131
|
+
tcp_url = "#{proto}://#{addr}:#{port}"
|
132
|
+
ssl_url = "ssl://#{addr}:#{port}"
|
133
|
+
ssl_url_prefix = "#{ssl_url}?"
|
134
|
+
|
135
|
+
existing = binds.find { |bind| bind == tcp_url || bind == ssl_url || bind.start_with?(ssl_url_prefix) }
|
136
|
+
|
137
|
+
activated_binds << (existing || tcp_url)
|
138
|
+
else
|
139
|
+
# TODO: can there be a SSL bind without a port?
|
140
|
+
activated_binds << "#{proto}://#{addr}"
|
81
141
|
end
|
82
142
|
end
|
83
143
|
|
84
|
-
|
85
|
-
|
144
|
+
if only_matching
|
145
|
+
activated_binds
|
146
|
+
else
|
147
|
+
binds | activated_binds
|
86
148
|
end
|
87
149
|
end
|
88
150
|
|
89
|
-
def parse(binds, logger)
|
151
|
+
def parse(binds, logger, log_msg = 'Listening')
|
90
152
|
binds.each do |str|
|
91
153
|
uri = URI.parse str
|
92
154
|
case uri.scheme
|
@@ -98,23 +160,36 @@ module Puma
|
|
98
160
|
io = inherit_tcp_listener uri.host, uri.port, sock
|
99
161
|
logger.log "* Activated #{str}"
|
100
162
|
else
|
163
|
+
ios_len = @ios.length
|
101
164
|
params = Util.parse_query uri.query
|
102
165
|
|
103
|
-
opt = params.key?('low_latency')
|
166
|
+
opt = params.key?('low_latency') && params['low_latency'] != 'false'
|
104
167
|
bak = params.fetch('backlog', 1024).to_i
|
105
168
|
|
106
169
|
io = add_tcp_listener uri.host, uri.port, opt, bak
|
107
|
-
|
170
|
+
|
171
|
+
@ios[ios_len..-1].each do |i|
|
172
|
+
addr = loc_addr_str i
|
173
|
+
logger.log "* #{log_msg} on http://#{addr}"
|
174
|
+
end
|
108
175
|
end
|
109
176
|
|
110
177
|
@listeners << [str, io] if io
|
111
178
|
when "unix"
|
112
179
|
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
180
|
+
abstract = false
|
181
|
+
if str.start_with? 'unix://@'
|
182
|
+
raise "OS does not support abstract UNIXSockets" unless Puma.abstract_unix_socket?
|
183
|
+
abstract = true
|
184
|
+
path = "@#{path}"
|
185
|
+
end
|
113
186
|
|
114
187
|
if fd = @inherited_fds.delete(str)
|
188
|
+
@unix_paths << path unless abstract
|
115
189
|
io = inherit_unix_listener path, fd
|
116
190
|
logger.log "* Inherited #{str}"
|
117
191
|
elsif sock = @activated_sockets.delete([ :unix, path ])
|
192
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
118
193
|
io = inherit_unix_listener path, sock
|
119
194
|
logger.log "* Activated #{str}"
|
120
195
|
else
|
@@ -138,68 +213,18 @@ module Puma
|
|
138
213
|
end
|
139
214
|
end
|
140
215
|
|
216
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
141
217
|
io = add_unix_listener path, umask, mode, backlog
|
142
|
-
logger.log "*
|
218
|
+
logger.log "* #{log_msg} on #{str}"
|
143
219
|
end
|
144
220
|
|
145
221
|
@listeners << [str, io]
|
146
222
|
when "ssl"
|
147
|
-
params = Util.parse_query uri.query
|
148
|
-
require 'puma/minissl'
|
149
|
-
|
150
|
-
MiniSSL.check
|
151
|
-
|
152
|
-
ctx = MiniSSL::Context.new
|
153
|
-
|
154
|
-
if defined?(JRUBY_VERSION)
|
155
|
-
unless params['keystore']
|
156
|
-
@events.error "Please specify the Java keystore via 'keystore='"
|
157
|
-
end
|
158
|
-
|
159
|
-
ctx.keystore = params['keystore']
|
160
|
-
|
161
|
-
unless params['keystore-pass']
|
162
|
-
@events.error "Please specify the Java keystore password via 'keystore-pass='"
|
163
|
-
end
|
164
|
-
|
165
|
-
ctx.keystore_pass = params['keystore-pass']
|
166
|
-
ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
|
167
|
-
else
|
168
|
-
unless params['key']
|
169
|
-
@events.error "Please specify the SSL key via 'key='"
|
170
|
-
end
|
171
|
-
|
172
|
-
ctx.key = params['key']
|
173
|
-
|
174
|
-
unless params['cert']
|
175
|
-
@events.error "Please specify the SSL cert via 'cert='"
|
176
|
-
end
|
177
223
|
|
178
|
-
|
224
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
179
225
|
|
180
|
-
|
181
|
-
|
182
|
-
@events.error "Please specify the SSL ca via 'ca='"
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
ctx.ca = params['ca'] if params['ca']
|
187
|
-
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
188
|
-
end
|
189
|
-
|
190
|
-
if params['verify_mode']
|
191
|
-
ctx.verify_mode = case params['verify_mode']
|
192
|
-
when "peer"
|
193
|
-
MiniSSL::VERIFY_PEER
|
194
|
-
when "force_peer"
|
195
|
-
MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
196
|
-
when "none"
|
197
|
-
MiniSSL::VERIFY_NONE
|
198
|
-
else
|
199
|
-
@events.error "Please specify a valid verify_mode="
|
200
|
-
MiniSSL::VERIFY_NONE
|
201
|
-
end
|
202
|
-
end
|
226
|
+
params = Util.parse_query uri.query
|
227
|
+
ctx = MiniSSL::ContextBuilder.new(params, @events).context
|
203
228
|
|
204
229
|
if fd = @inherited_fds.delete(str)
|
205
230
|
logger.log "* Inherited #{str}"
|
@@ -208,8 +233,13 @@ module Puma
|
|
208
233
|
io = inherit_ssl_listener sock, ctx
|
209
234
|
logger.log "* Activated #{str}"
|
210
235
|
else
|
236
|
+
ios_len = @ios.length
|
211
237
|
io = add_ssl_listener uri.host, uri.port, ctx
|
212
|
-
|
238
|
+
|
239
|
+
@ios[ios_len..-1].each do |i|
|
240
|
+
addr = loc_addr_str i
|
241
|
+
logger.log "* #{log_msg} on ssl://#{addr}?#{uri.query}"
|
242
|
+
end
|
213
243
|
end
|
214
244
|
|
215
245
|
@listeners << [str, io] if io
|
@@ -237,23 +267,21 @@ module Puma
|
|
237
267
|
end
|
238
268
|
|
239
269
|
# Also close any unused activated sockets
|
240
|
-
@activated_sockets.
|
241
|
-
|
242
|
-
|
243
|
-
sock.
|
244
|
-
|
270
|
+
unless @activated_sockets.empty?
|
271
|
+
fds = @ios.map(&:to_i)
|
272
|
+
@activated_sockets.each do |key, sock|
|
273
|
+
next if fds.include? sock.to_i
|
274
|
+
logger.log "* Closing unused activated socket: #{key.first}://#{key[1..-1].join ':'}"
|
275
|
+
begin
|
276
|
+
sock.close
|
277
|
+
rescue SystemCallError
|
278
|
+
end
|
279
|
+
# We have to unlink a unix socket path that's not being used
|
280
|
+
File.unlink key[1] if key.first == :unix
|
245
281
|
end
|
246
|
-
# We have to unlink a unix socket path that's not being used
|
247
|
-
File.unlink key[1] if key[0] == :unix
|
248
282
|
end
|
249
283
|
end
|
250
284
|
|
251
|
-
def loopback_addresses
|
252
|
-
Socket.ip_address_list.select do |addrinfo|
|
253
|
-
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
254
|
-
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
255
|
-
end
|
256
|
-
|
257
285
|
# Tell the server to listen on host +host+, port +port+.
|
258
286
|
# If +optimize_for_latency+ is true (the default) then clients connecting
|
259
287
|
# will be optimized for latency over throughput.
|
@@ -270,26 +298,19 @@ module Puma
|
|
270
298
|
end
|
271
299
|
|
272
300
|
host = host[1..-2] if host and host[0..0] == '['
|
273
|
-
|
301
|
+
tcp_server = TCPServer.new(host, port)
|
274
302
|
if optimize_for_latency
|
275
|
-
|
303
|
+
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
276
304
|
end
|
277
|
-
|
278
|
-
|
279
|
-
@connected_port = s.addr[1]
|
305
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
306
|
+
tcp_server.listen backlog
|
280
307
|
|
281
|
-
@ios <<
|
282
|
-
|
308
|
+
@ios << tcp_server
|
309
|
+
tcp_server
|
283
310
|
end
|
284
311
|
|
285
|
-
attr_reader :connected_port
|
286
|
-
|
287
312
|
def inherit_tcp_listener(host, port, fd)
|
288
|
-
|
289
|
-
s = fd
|
290
|
-
else
|
291
|
-
s = TCPServer.for_fd(fd)
|
292
|
-
end
|
313
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
293
314
|
|
294
315
|
@ios << s
|
295
316
|
s
|
@@ -297,9 +318,8 @@ module Puma
|
|
297
318
|
|
298
319
|
def add_ssl_listener(host, port, ctx,
|
299
320
|
optimize_for_latency=true, backlog=1024)
|
300
|
-
require 'puma/minissl'
|
301
321
|
|
302
|
-
|
322
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
303
323
|
|
304
324
|
if host == "localhost"
|
305
325
|
loopback_addresses.each do |addr|
|
@@ -316,7 +336,6 @@ module Puma
|
|
316
336
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
317
337
|
s.listen backlog
|
318
338
|
|
319
|
-
|
320
339
|
ssl = MiniSSL::Server.new s, ctx
|
321
340
|
env = @proto_env.dup
|
322
341
|
env[HTTPS_KEY] = HTTPS
|
@@ -327,14 +346,10 @@ module Puma
|
|
327
346
|
end
|
328
347
|
|
329
348
|
def inherit_ssl_listener(fd, ctx)
|
330
|
-
|
331
|
-
|
349
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
350
|
+
|
351
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
332
352
|
|
333
|
-
if fd.kind_of? TCPServer
|
334
|
-
s = fd
|
335
|
-
else
|
336
|
-
s = TCPServer.for_fd(fd)
|
337
|
-
end
|
338
353
|
ssl = MiniSSL::Server.new(s, ctx)
|
339
354
|
|
340
355
|
env = @proto_env.dup
|
@@ -349,8 +364,6 @@ module Puma
|
|
349
364
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
350
365
|
#
|
351
366
|
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
352
|
-
@unix_paths << path
|
353
|
-
|
354
367
|
# Let anyone connect by default
|
355
368
|
umask ||= 0
|
356
369
|
|
@@ -367,8 +380,7 @@ module Puma
|
|
367
380
|
raise "There is already a server bound to: #{path}"
|
368
381
|
end
|
369
382
|
end
|
370
|
-
|
371
|
-
s = UNIXServer.new(path)
|
383
|
+
s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
|
372
384
|
s.listen backlog
|
373
385
|
@ios << s
|
374
386
|
ensure
|
@@ -387,13 +399,8 @@ module Puma
|
|
387
399
|
end
|
388
400
|
|
389
401
|
def inherit_unix_listener(path, fd)
|
390
|
-
|
402
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
|
391
403
|
|
392
|
-
if fd.kind_of? TCPServer
|
393
|
-
s = fd
|
394
|
-
else
|
395
|
-
s = UNIXServer.for_fd fd
|
396
|
-
end
|
397
404
|
@ios << s
|
398
405
|
|
399
406
|
env = @proto_env.dup
|
@@ -403,5 +410,50 @@ module Puma
|
|
403
410
|
s
|
404
411
|
end
|
405
412
|
|
413
|
+
def close_listeners
|
414
|
+
@listeners.each do |l, io|
|
415
|
+
io.close unless io.closed?
|
416
|
+
uri = URI.parse l
|
417
|
+
next unless uri.scheme == 'unix'
|
418
|
+
unix_path = "#{uri.host}#{uri.path}"
|
419
|
+
File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
|
420
|
+
end
|
421
|
+
end
|
422
|
+
|
423
|
+
def redirects_for_restart
|
424
|
+
redirects = @listeners.map { |a| [a[1].to_i, a[1].to_i] }.to_h
|
425
|
+
redirects[:close_others] = true
|
426
|
+
redirects
|
427
|
+
end
|
428
|
+
|
429
|
+
# @version 5.0.0
|
430
|
+
def redirects_for_restart_env
|
431
|
+
@listeners.each_with_object({}).with_index do |(listen, memo), i|
|
432
|
+
memo["PUMA_INHERIT_#{i}"] = "#{listen[1].to_i}:#{listen[0]}"
|
433
|
+
end
|
434
|
+
end
|
435
|
+
|
436
|
+
private
|
437
|
+
|
438
|
+
# @!attribute [r] loopback_addresses
|
439
|
+
def loopback_addresses
|
440
|
+
Socket.ip_address_list.select do |addrinfo|
|
441
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
442
|
+
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
443
|
+
end
|
444
|
+
|
445
|
+
def loc_addr_str(io)
|
446
|
+
loc_addr = io.to_io.local_address
|
447
|
+
if loc_addr.ipv6?
|
448
|
+
"[#{loc_addr.ip_unpack[0]}]:#{loc_addr.ip_unpack[1]}"
|
449
|
+
else
|
450
|
+
loc_addr.ip_unpack.join(':')
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
# @version 5.0.0
|
455
|
+
def socket_activation_fd(int)
|
456
|
+
int + 3 # 3 is the magic number you add to follow the SA protocol
|
457
|
+
end
|
406
458
|
end
|
407
459
|
end
|