puma 4.3.3 → 5.3.2
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 +1348 -519
- data/LICENSE +23 -20
- data/README.md +74 -31
- data/bin/puma-wild +3 -9
- data/docs/architecture.md +24 -20
- data/docs/compile_options.md +19 -0
- data/docs/deployment.md +15 -10
- 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 +2 -2
- data/docs/rails_dev_mode.md +29 -0
- data/docs/restart.md +46 -23
- data/docs/signals.md +7 -6
- data/docs/stats.md +142 -0
- data/docs/systemd.md +27 -67
- data/ext/puma_http11/PumaHttp11Service.java +2 -4
- data/ext/puma_http11/ext_help.h +1 -1
- data/ext/puma_http11/extconf.rb +22 -8
- data/ext/puma_http11/http11_parser.c +48 -48
- data/ext/puma_http11/http11_parser.h +1 -1
- data/ext/puma_http11/http11_parser.java.rl +1 -1
- data/ext/puma_http11/http11_parser.rl +4 -2
- data/ext/puma_http11/mini_ssl.c +211 -118
- data/ext/puma_http11/no_ssl/PumaHttp11Service.java +15 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +3 -3
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +5 -7
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +77 -18
- data/ext/puma_http11/puma_http11.c +32 -50
- data/lib/puma.rb +46 -0
- data/lib/puma/app/status.rb +48 -35
- data/lib/puma/binder.rb +177 -103
- data/lib/puma/cli.rb +11 -15
- data/lib/puma/client.rb +83 -76
- data/lib/puma/cluster.rb +184 -198
- 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 +55 -49
- data/lib/puma/const.rb +13 -5
- data/lib/puma/control_cli.rb +93 -76
- data/lib/puma/detect.rb +24 -3
- data/lib/puma/dsl.rb +266 -92
- data/lib/puma/error_logger.rb +104 -0
- data/lib/puma/events.rb +55 -34
- data/lib/puma/io_buffer.rb +9 -2
- data/lib/puma/jruby_restart.rb +0 -58
- data/lib/puma/json.rb +96 -0
- data/lib/puma/launcher.rb +113 -45
- data/lib/puma/minissl.rb +114 -33
- data/lib/puma/minissl/context_builder.rb +6 -3
- data/lib/puma/null_io.rb +13 -1
- data/lib/puma/plugin.rb +1 -10
- data/lib/puma/queue_close.rb +26 -0
- data/lib/puma/rack/builder.rb +0 -4
- data/lib/puma/reactor.rb +85 -369
- data/lib/puma/request.rb +467 -0
- data/lib/puma/runner.rb +29 -58
- data/lib/puma/server.rb +267 -698
- data/lib/puma/single.rb +9 -65
- data/lib/puma/state_file.rb +8 -3
- data/lib/puma/systemd.rb +46 -0
- data/lib/puma/thread_pool.rb +119 -53
- data/lib/puma/util.rb +12 -0
- data/lib/rack/handler/puma.rb +2 -3
- data/tools/{docker/Dockerfile → Dockerfile} +0 -0
- metadata +28 -24
- data/docs/tcp_mode.md +0 -96
- data/ext/puma_http11/io_buffer.c +0 -155
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +0 -72
- data/lib/puma/accept_nonblock.rb +0 -29
- 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.rb
CHANGED
@@ -10,16 +10,62 @@ require 'stringio'
|
|
10
10
|
|
11
11
|
require 'thread'
|
12
12
|
|
13
|
+
require 'puma/puma_http11'
|
14
|
+
require 'puma/detect'
|
15
|
+
require 'puma/json'
|
16
|
+
|
13
17
|
module Puma
|
14
18
|
autoload :Const, 'puma/const'
|
15
19
|
autoload :Server, 'puma/server'
|
16
20
|
autoload :Launcher, 'puma/launcher'
|
17
21
|
|
22
|
+
# at present, MiniSSL::Engine is only defined in extension code (puma_http11),
|
23
|
+
# not in minissl.rb
|
24
|
+
HAS_SSL = const_defined?(:MiniSSL, false) && MiniSSL.const_defined?(:Engine, false)
|
25
|
+
|
26
|
+
HAS_UNIX_SOCKET = Object.const_defined? :UNIXSocket
|
27
|
+
|
28
|
+
if HAS_SSL
|
29
|
+
require 'puma/minissl'
|
30
|
+
else
|
31
|
+
module MiniSSL
|
32
|
+
# this class is defined so that it exists when Puma is compiled
|
33
|
+
# without ssl support, as Server and Reactor use it in rescue statements.
|
34
|
+
class SSLError < StandardError ; end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.ssl?
|
39
|
+
HAS_SSL
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.abstract_unix_socket?
|
43
|
+
@abstract_unix ||=
|
44
|
+
if HAS_UNIX_SOCKET
|
45
|
+
begin
|
46
|
+
::UNIXServer.new("\0puma.temp.unix").close
|
47
|
+
true
|
48
|
+
rescue ArgumentError # darwin
|
49
|
+
false
|
50
|
+
end
|
51
|
+
else
|
52
|
+
false
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# @!attribute [rw] stats_object=
|
18
57
|
def self.stats_object=(val)
|
19
58
|
@get_stats = val
|
20
59
|
end
|
21
60
|
|
61
|
+
# @!attribute [rw] stats_object
|
22
62
|
def self.stats
|
63
|
+
Puma::JSON.generate @get_stats.stats
|
64
|
+
end
|
65
|
+
|
66
|
+
# @!attribute [r] stats_hash
|
67
|
+
# @version 5.0.0
|
68
|
+
def self.stats_hash
|
23
69
|
@get_stats.stats
|
24
70
|
end
|
25
71
|
|
data/lib/puma/app/status.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
2
|
+
require 'puma/json'
|
4
3
|
|
5
4
|
module Puma
|
6
5
|
module App
|
@@ -9,54 +8,68 @@ module Puma
|
|
9
8
|
class Status
|
10
9
|
OK_STATUS = '{ "status": "ok" }'.freeze
|
11
10
|
|
12
|
-
|
13
|
-
|
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
|
14
16
|
@auth_token = token
|
15
17
|
end
|
16
18
|
|
19
|
+
# most commands call methods in `::Puma::Launcher` based on command in
|
20
|
+
# `env['PATH_INFO']`
|
17
21
|
def call(env)
|
18
22
|
unless authenticate(env)
|
19
23
|
return rack_response(403, 'Invalid auth token', 'text/plain')
|
20
24
|
end
|
21
25
|
|
22
|
-
case
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
26
32
|
|
27
|
-
|
28
|
-
|
29
|
-
rack_response(200, OK_STATUS)
|
33
|
+
when 'halt'
|
34
|
+
@launcher.halt ; 200
|
30
35
|
|
31
|
-
|
32
|
-
|
33
|
-
rack_response(200, OK_STATUS)
|
36
|
+
when 'restart'
|
37
|
+
@launcher.restart ; 200
|
34
38
|
|
35
|
-
|
36
|
-
|
37
|
-
rack_response(404, '{ "error": "phased restart not available" }')
|
38
|
-
else
|
39
|
-
rack_response(200, OK_STATUS)
|
40
|
-
end
|
39
|
+
when 'phased-restart'
|
40
|
+
@launcher.phased_restart ? 200 : 404
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
rack_response(404, '{ "error": "reload_worker_directory not available" }')
|
45
|
-
else
|
46
|
-
rack_response(200, OK_STATUS)
|
47
|
-
end
|
42
|
+
when 'reload-worker-directory'
|
43
|
+
@launcher.send(:reload_worker_directory) ? 200 : 404
|
48
44
|
|
49
|
-
|
50
|
-
|
51
|
-
rack_response(200, OK_STATUS)
|
45
|
+
when 'gc'
|
46
|
+
GC.start ; 200
|
52
47
|
|
53
|
-
|
54
|
-
|
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
|
60
|
+
|
61
|
+
else
|
62
|
+
return rack_response(404, "Unsupported action", 'text/plain')
|
63
|
+
end
|
55
64
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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\" }"
|
60
73
|
end
|
61
74
|
end
|
62
75
|
|
data/lib/puma/binder.rb
CHANGED
@@ -5,15 +5,30 @@ require 'socket'
|
|
5
5
|
|
6
6
|
require 'puma/const'
|
7
7
|
require 'puma/util'
|
8
|
-
require 'puma/
|
8
|
+
require 'puma/configuration'
|
9
9
|
|
10
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
|
+
|
11
26
|
class Binder
|
12
27
|
include Puma::Const
|
13
28
|
|
14
|
-
RACK_VERSION = [1,
|
29
|
+
RACK_VERSION = [1,6].freeze
|
15
30
|
|
16
|
-
def initialize(events)
|
31
|
+
def initialize(events, conf = Configuration.new)
|
17
32
|
@events = events
|
18
33
|
@listeners = []
|
19
34
|
@inherited_fds = {}
|
@@ -23,8 +38,8 @@ module Puma
|
|
23
38
|
@proto_env = {
|
24
39
|
"rack.version".freeze => RACK_VERSION,
|
25
40
|
"rack.errors".freeze => events.stderr,
|
26
|
-
"rack.multithread".freeze =>
|
27
|
-
"rack.multiprocess".freeze =>
|
41
|
+
"rack.multithread".freeze => conf.options[:max_threads] > 1,
|
42
|
+
"rack.multiprocess".freeze => conf.options[:workers] >= 1,
|
28
43
|
"rack.run_once".freeze => false,
|
29
44
|
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
30
45
|
|
@@ -45,6 +60,12 @@ module Puma
|
|
45
60
|
|
46
61
|
attr_reader :ios
|
47
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
|
68
|
+
|
48
69
|
def env(sock)
|
49
70
|
@envs.fetch(sock, @proto_env)
|
50
71
|
end
|
@@ -53,40 +74,81 @@ module Puma
|
|
53
74
|
@ios.each { |i| i.close }
|
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]
|
81
107
|
end
|
108
|
+
@activated_sockets[key] = sock
|
109
|
+
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
82
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?
|
83
126
|
|
84
|
-
|
85
|
-
|
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}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
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,33 +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
|
|
108
|
-
@ios.each do |i|
|
109
|
-
|
110
|
-
|
111
|
-
"[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
|
112
|
-
else
|
113
|
-
i.local_address.ip_unpack.join(':')
|
114
|
-
end
|
115
|
-
|
116
|
-
logger.log "* Listening on tcp://#{addr}"
|
171
|
+
@ios[ios_len..-1].each do |i|
|
172
|
+
addr = loc_addr_str i
|
173
|
+
logger.log "* #{log_msg} on http://#{addr}"
|
117
174
|
end
|
118
175
|
end
|
119
176
|
|
120
177
|
@listeners << [str, io] if io
|
121
178
|
when "unix"
|
122
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
|
123
186
|
|
124
187
|
if fd = @inherited_fds.delete(str)
|
188
|
+
@unix_paths << path unless abstract
|
125
189
|
io = inherit_unix_listener path, fd
|
126
190
|
logger.log "* Inherited #{str}"
|
127
191
|
elsif sock = @activated_sockets.delete([ :unix, path ])
|
192
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
128
193
|
io = inherit_unix_listener path, sock
|
129
194
|
logger.log "* Activated #{str}"
|
130
195
|
else
|
@@ -148,12 +213,16 @@ module Puma
|
|
148
213
|
end
|
149
214
|
end
|
150
215
|
|
216
|
+
@unix_paths << path unless abstract || File.exist?(path)
|
151
217
|
io = add_unix_listener path, umask, mode, backlog
|
152
|
-
logger.log "*
|
218
|
+
logger.log "* #{log_msg} on #{str}"
|
153
219
|
end
|
154
220
|
|
155
221
|
@listeners << [str, io]
|
156
222
|
when "ssl"
|
223
|
+
|
224
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
225
|
+
|
157
226
|
params = Util.parse_query uri.query
|
158
227
|
ctx = MiniSSL::ContextBuilder.new(params, @events).context
|
159
228
|
|
@@ -164,8 +233,13 @@ module Puma
|
|
164
233
|
io = inherit_ssl_listener sock, ctx
|
165
234
|
logger.log "* Activated #{str}"
|
166
235
|
else
|
236
|
+
ios_len = @ios.length
|
167
237
|
io = add_ssl_listener uri.host, uri.port, ctx
|
168
|
-
|
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
|
169
243
|
end
|
170
244
|
|
171
245
|
@listeners << [str, io] if io
|
@@ -193,23 +267,21 @@ module Puma
|
|
193
267
|
end
|
194
268
|
|
195
269
|
# Also close any unused activated sockets
|
196
|
-
@activated_sockets.
|
197
|
-
|
198
|
-
|
199
|
-
sock.
|
200
|
-
|
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
|
201
281
|
end
|
202
|
-
# We have to unlink a unix socket path that's not being used
|
203
|
-
File.unlink key[1] if key[0] == :unix
|
204
282
|
end
|
205
283
|
end
|
206
284
|
|
207
|
-
def loopback_addresses
|
208
|
-
Socket.ip_address_list.select do |addrinfo|
|
209
|
-
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
210
|
-
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
211
|
-
end
|
212
|
-
|
213
285
|
# Tell the server to listen on host +host+, port +port+.
|
214
286
|
# If +optimize_for_latency+ is true (the default) then clients connecting
|
215
287
|
# will be optimized for latency over throughput.
|
@@ -226,26 +298,19 @@ module Puma
|
|
226
298
|
end
|
227
299
|
|
228
300
|
host = host[1..-2] if host and host[0..0] == '['
|
229
|
-
|
301
|
+
tcp_server = TCPServer.new(host, port)
|
230
302
|
if optimize_for_latency
|
231
|
-
|
303
|
+
tcp_server.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
232
304
|
end
|
233
|
-
|
234
|
-
|
235
|
-
@connected_port = s.addr[1]
|
305
|
+
tcp_server.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
306
|
+
tcp_server.listen backlog
|
236
307
|
|
237
|
-
@ios <<
|
238
|
-
|
308
|
+
@ios << tcp_server
|
309
|
+
tcp_server
|
239
310
|
end
|
240
311
|
|
241
|
-
attr_reader :connected_port
|
242
|
-
|
243
312
|
def inherit_tcp_listener(host, port, fd)
|
244
|
-
|
245
|
-
s = fd
|
246
|
-
else
|
247
|
-
s = TCPServer.for_fd(fd)
|
248
|
-
end
|
313
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
249
314
|
|
250
315
|
@ios << s
|
251
316
|
s
|
@@ -253,9 +318,8 @@ module Puma
|
|
253
318
|
|
254
319
|
def add_ssl_listener(host, port, ctx,
|
255
320
|
optimize_for_latency=true, backlog=1024)
|
256
|
-
require 'puma/minissl'
|
257
321
|
|
258
|
-
|
322
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
259
323
|
|
260
324
|
if host == "localhost"
|
261
325
|
loopback_addresses.each do |addr|
|
@@ -272,7 +336,6 @@ module Puma
|
|
272
336
|
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
273
337
|
s.listen backlog
|
274
338
|
|
275
|
-
|
276
339
|
ssl = MiniSSL::Server.new s, ctx
|
277
340
|
env = @proto_env.dup
|
278
341
|
env[HTTPS_KEY] = HTTPS
|
@@ -283,14 +346,10 @@ module Puma
|
|
283
346
|
end
|
284
347
|
|
285
348
|
def inherit_ssl_listener(fd, ctx)
|
286
|
-
|
287
|
-
|
349
|
+
raise "Puma compiled without SSL support" unless HAS_SSL
|
350
|
+
|
351
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::TCPServer.for_fd(fd)
|
288
352
|
|
289
|
-
if fd.kind_of? TCPServer
|
290
|
-
s = fd
|
291
|
-
else
|
292
|
-
s = TCPServer.for_fd(fd)
|
293
|
-
end
|
294
353
|
ssl = MiniSSL::Server.new(s, ctx)
|
295
354
|
|
296
355
|
env = @proto_env.dup
|
@@ -305,8 +364,6 @@ module Puma
|
|
305
364
|
# Tell the server to listen on +path+ as a UNIX domain socket.
|
306
365
|
#
|
307
366
|
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
308
|
-
@unix_paths << path unless File.exist? path
|
309
|
-
|
310
367
|
# Let anyone connect by default
|
311
368
|
umask ||= 0
|
312
369
|
|
@@ -323,8 +380,7 @@ module Puma
|
|
323
380
|
raise "There is already a server bound to: #{path}"
|
324
381
|
end
|
325
382
|
end
|
326
|
-
|
327
|
-
s = UNIXServer.new(path)
|
383
|
+
s = UNIXServer.new path.sub(/\A@/, "\0") # check for abstract UNIXSocket
|
328
384
|
s.listen backlog
|
329
385
|
@ios << s
|
330
386
|
ensure
|
@@ -343,13 +399,8 @@ module Puma
|
|
343
399
|
end
|
344
400
|
|
345
401
|
def inherit_unix_listener(path, fd)
|
346
|
-
|
402
|
+
s = fd.kind_of?(::TCPServer) ? fd : ::UNIXServer.for_fd(fd)
|
347
403
|
|
348
|
-
if fd.kind_of? TCPServer
|
349
|
-
s = fd
|
350
|
-
else
|
351
|
-
s = UNIXServer.for_fd fd
|
352
|
-
end
|
353
404
|
@ios << s
|
354
405
|
|
355
406
|
env = @proto_env.dup
|
@@ -361,25 +412,48 @@ module Puma
|
|
361
412
|
|
362
413
|
def close_listeners
|
363
414
|
@listeners.each do |l, io|
|
364
|
-
io.close
|
365
|
-
uri = URI.parse
|
415
|
+
io.close unless io.closed?
|
416
|
+
uri = URI.parse l
|
366
417
|
next unless uri.scheme == 'unix'
|
367
418
|
unix_path = "#{uri.host}#{uri.path}"
|
368
|
-
File.unlink unix_path if @unix_paths.include? unix_path
|
419
|
+
File.unlink unix_path if @unix_paths.include?(unix_path) && File.exist?(unix_path)
|
369
420
|
end
|
370
421
|
end
|
371
422
|
|
372
|
-
def
|
373
|
-
@
|
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
|
374
427
|
end
|
375
428
|
|
376
|
-
|
377
|
-
|
378
|
-
@listeners.
|
379
|
-
|
380
|
-
redirects[io.to_i] = io.to_i
|
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]}"
|
381
433
|
end
|
382
|
-
|
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
|
383
457
|
end
|
384
458
|
end
|
385
459
|
end
|