puma 4.2.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 +7 -0
- data/History.md +1513 -0
- data/LICENSE +26 -0
- data/README.md +309 -0
- data/bin/puma +10 -0
- data/bin/puma-wild +31 -0
- data/bin/pumactl +12 -0
- data/docs/architecture.md +37 -0
- data/docs/deployment.md +111 -0
- 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/nginx.md +80 -0
- data/docs/plugins.md +28 -0
- data/docs/restart.md +41 -0
- data/docs/signals.md +96 -0
- data/docs/systemd.md +290 -0
- data/ext/puma_http11/PumaHttp11Service.java +19 -0
- data/ext/puma_http11/ext_help.h +15 -0
- data/ext/puma_http11/extconf.rb +23 -0
- data/ext/puma_http11/http11_parser.c +1044 -0
- data/ext/puma_http11/http11_parser.h +65 -0
- data/ext/puma_http11/http11_parser.java.rl +161 -0
- data/ext/puma_http11/http11_parser.rl +147 -0
- data/ext/puma_http11/http11_parser_common.rl +54 -0
- data/ext/puma_http11/io_buffer.c +155 -0
- data/ext/puma_http11/mini_ssl.c +553 -0
- data/ext/puma_http11/org/jruby/puma/Http11.java +234 -0
- data/ext/puma_http11/org/jruby/puma/Http11Parser.java +470 -0
- data/ext/puma_http11/org/jruby/puma/IOBuffer.java +72 -0
- data/ext/puma_http11/org/jruby/puma/MiniSSL.java +363 -0
- data/ext/puma_http11/puma_http11.c +500 -0
- data/lib/puma.rb +31 -0
- data/lib/puma/accept_nonblock.rb +29 -0
- data/lib/puma/app/status.rb +80 -0
- data/lib/puma/binder.rb +439 -0
- data/lib/puma/cli.rb +239 -0
- data/lib/puma/client.rb +494 -0
- data/lib/puma/cluster.rb +555 -0
- data/lib/puma/commonlogger.rb +108 -0
- data/lib/puma/configuration.rb +362 -0
- data/lib/puma/const.rb +235 -0
- data/lib/puma/control_cli.rb +281 -0
- data/lib/puma/convenient.rb +25 -0
- data/lib/puma/delegation.rb +13 -0
- data/lib/puma/detect.rb +15 -0
- data/lib/puma/dsl.rb +738 -0
- data/lib/puma/events.rb +156 -0
- data/lib/puma/io_buffer.rb +4 -0
- data/lib/puma/jruby_restart.rb +84 -0
- data/lib/puma/launcher.rb +478 -0
- data/lib/puma/minissl.rb +278 -0
- data/lib/puma/null_io.rb +44 -0
- data/lib/puma/plugin.rb +120 -0
- data/lib/puma/plugin/tmp_restart.rb +36 -0
- data/lib/puma/rack/builder.rb +301 -0
- data/lib/puma/rack/urlmap.rb +93 -0
- data/lib/puma/rack_default.rb +9 -0
- data/lib/puma/reactor.rb +399 -0
- data/lib/puma/runner.rb +185 -0
- data/lib/puma/server.rb +1033 -0
- data/lib/puma/single.rb +124 -0
- data/lib/puma/state_file.rb +31 -0
- data/lib/puma/tcp_logger.rb +41 -0
- data/lib/puma/thread_pool.rb +328 -0
- data/lib/puma/util.rb +124 -0
- data/lib/rack/handler/puma.rb +115 -0
- data/tools/docker/Dockerfile +16 -0
- data/tools/jungle/README.md +19 -0
- data/tools/jungle/init.d/README.md +61 -0
- data/tools/jungle/init.d/puma +421 -0
- data/tools/jungle/init.d/run-puma +18 -0
- data/tools/jungle/rc.d/README.md +74 -0
- data/tools/jungle/rc.d/puma +61 -0
- data/tools/jungle/rc.d/puma.conf +10 -0
- data/tools/jungle/upstart/README.md +61 -0
- data/tools/jungle/upstart/puma-manager.conf +31 -0
- data/tools/jungle/upstart/puma.conf +69 -0
- data/tools/trickletest.rb +44 -0
- metadata +144 -0
data/lib/puma.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Standard libraries
|
4
|
+
require 'socket'
|
5
|
+
require 'tempfile'
|
6
|
+
require 'time'
|
7
|
+
require 'etc'
|
8
|
+
require 'uri'
|
9
|
+
require 'stringio'
|
10
|
+
|
11
|
+
require 'thread'
|
12
|
+
|
13
|
+
module Puma
|
14
|
+
autoload :Const, 'puma/const'
|
15
|
+
autoload :Server, 'puma/server'
|
16
|
+
autoload :Launcher, 'puma/launcher'
|
17
|
+
|
18
|
+
def self.stats_object=(val)
|
19
|
+
@get_stats = val
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.stats
|
23
|
+
@get_stats.stats
|
24
|
+
end
|
25
|
+
|
26
|
+
# Thread name is new in Ruby 2.3
|
27
|
+
def self.set_thread_name(name)
|
28
|
+
return unless Thread.current.respond_to?(:name=)
|
29
|
+
Thread.current.name = "puma #{name}"
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'openssl'
|
4
|
+
|
5
|
+
module OpenSSL
|
6
|
+
module SSL
|
7
|
+
class SSLServer
|
8
|
+
unless public_method_defined? :accept_nonblock
|
9
|
+
def accept_nonblock
|
10
|
+
sock = @svr.accept_nonblock
|
11
|
+
|
12
|
+
begin
|
13
|
+
ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
|
14
|
+
ssl.sync_close = true
|
15
|
+
ssl.accept if @start_immediately
|
16
|
+
ssl
|
17
|
+
rescue SSLError => ex
|
18
|
+
if ssl
|
19
|
+
ssl.close
|
20
|
+
else
|
21
|
+
sock.close
|
22
|
+
end
|
23
|
+
raise ex
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Puma
|
6
|
+
module App
|
7
|
+
# Check out {#call}'s source code to see what actions this web application
|
8
|
+
# can respond to.
|
9
|
+
class Status
|
10
|
+
OK_STATUS = '{ "status": "ok" }'.freeze
|
11
|
+
|
12
|
+
def initialize(cli, token = nil)
|
13
|
+
@cli = cli
|
14
|
+
@auth_token = token
|
15
|
+
end
|
16
|
+
|
17
|
+
def call(env)
|
18
|
+
unless authenticate(env)
|
19
|
+
return rack_response(403, 'Invalid auth token', 'text/plain')
|
20
|
+
end
|
21
|
+
|
22
|
+
case env['PATH_INFO']
|
23
|
+
when /\/stop$/
|
24
|
+
@cli.stop
|
25
|
+
rack_response(200, OK_STATUS)
|
26
|
+
|
27
|
+
when /\/halt$/
|
28
|
+
@cli.halt
|
29
|
+
rack_response(200, OK_STATUS)
|
30
|
+
|
31
|
+
when /\/restart$/
|
32
|
+
@cli.restart
|
33
|
+
rack_response(200, OK_STATUS)
|
34
|
+
|
35
|
+
when /\/phased-restart$/
|
36
|
+
if !@cli.phased_restart
|
37
|
+
rack_response(404, '{ "error": "phased restart not available" }')
|
38
|
+
else
|
39
|
+
rack_response(200, OK_STATUS)
|
40
|
+
end
|
41
|
+
|
42
|
+
when /\/reload-worker-directory$/
|
43
|
+
if !@cli.send(:reload_worker_directory)
|
44
|
+
rack_response(404, '{ "error": "reload_worker_directory not available" }')
|
45
|
+
else
|
46
|
+
rack_response(200, OK_STATUS)
|
47
|
+
end
|
48
|
+
|
49
|
+
when /\/gc$/
|
50
|
+
GC.start
|
51
|
+
rack_response(200, OK_STATUS)
|
52
|
+
|
53
|
+
when /\/gc-stats$/
|
54
|
+
rack_response(200, GC.stat.to_json)
|
55
|
+
|
56
|
+
when /\/stats$/
|
57
|
+
rack_response(200, @cli.stats)
|
58
|
+
else
|
59
|
+
rack_response 404, "Unsupported action", 'text/plain'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def authenticate(env)
|
66
|
+
return true unless @auth_token
|
67
|
+
env['QUERY_STRING'].to_s.split(/&;/).include?("token=#{@auth_token}")
|
68
|
+
end
|
69
|
+
|
70
|
+
def rack_response(status, body, content_type='application/json')
|
71
|
+
headers = {
|
72
|
+
'Content-Type' => content_type,
|
73
|
+
'Content-Length' => body.bytesize.to_s
|
74
|
+
}
|
75
|
+
|
76
|
+
[status, headers, [body]]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
data/lib/puma/binder.rb
ADDED
@@ -0,0 +1,439 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'uri'
|
4
|
+
require 'socket'
|
5
|
+
|
6
|
+
require 'puma/const'
|
7
|
+
require 'puma/util'
|
8
|
+
|
9
|
+
module Puma
|
10
|
+
class Binder
|
11
|
+
include Puma::Const
|
12
|
+
|
13
|
+
RACK_VERSION = [1,3].freeze
|
14
|
+
|
15
|
+
def initialize(events)
|
16
|
+
@events = events
|
17
|
+
@listeners = []
|
18
|
+
@inherited_fds = {}
|
19
|
+
@activated_sockets = {}
|
20
|
+
@unix_paths = []
|
21
|
+
|
22
|
+
@proto_env = {
|
23
|
+
"rack.version".freeze => RACK_VERSION,
|
24
|
+
"rack.errors".freeze => events.stderr,
|
25
|
+
"rack.multithread".freeze => true,
|
26
|
+
"rack.multiprocess".freeze => false,
|
27
|
+
"rack.run_once".freeze => false,
|
28
|
+
"SCRIPT_NAME".freeze => ENV['SCRIPT_NAME'] || "",
|
29
|
+
|
30
|
+
# I'd like to set a default CONTENT_TYPE here but some things
|
31
|
+
# depend on their not being a default set and inferring
|
32
|
+
# it from the content. And so if i set it here, it won't
|
33
|
+
# infer properly.
|
34
|
+
|
35
|
+
"QUERY_STRING".freeze => "",
|
36
|
+
SERVER_PROTOCOL => HTTP_11,
|
37
|
+
SERVER_SOFTWARE => PUMA_SERVER_STRING,
|
38
|
+
GATEWAY_INTERFACE => CGI_VER
|
39
|
+
}
|
40
|
+
|
41
|
+
@envs = {}
|
42
|
+
@ios = []
|
43
|
+
end
|
44
|
+
|
45
|
+
attr_reader :ios
|
46
|
+
|
47
|
+
def env(sock)
|
48
|
+
@envs.fetch(sock, @proto_env)
|
49
|
+
end
|
50
|
+
|
51
|
+
def close
|
52
|
+
@ios.each { |i| i.close }
|
53
|
+
end
|
54
|
+
|
55
|
+
def import_from_env
|
56
|
+
remove = []
|
57
|
+
|
58
|
+
ENV.each do |k,v|
|
59
|
+
if k =~ /PUMA_INHERIT_\d+/
|
60
|
+
fd, url = v.split(":", 2)
|
61
|
+
@inherited_fds[url] = fd.to_i
|
62
|
+
remove << k
|
63
|
+
elsif k == 'LISTEN_FDS' && ENV['LISTEN_PID'].to_i == $$
|
64
|
+
v.to_i.times do |num|
|
65
|
+
fd = num + 3
|
66
|
+
sock = TCPServer.for_fd(fd)
|
67
|
+
begin
|
68
|
+
key = [ :unix, Socket.unpack_sockaddr_un(sock.getsockname) ]
|
69
|
+
rescue ArgumentError
|
70
|
+
port, addr = Socket.unpack_sockaddr_in(sock.getsockname)
|
71
|
+
if addr =~ /\:/
|
72
|
+
addr = "[#{addr}]"
|
73
|
+
end
|
74
|
+
key = [ :tcp, addr, port ]
|
75
|
+
end
|
76
|
+
@activated_sockets[key] = sock
|
77
|
+
@events.debug "Registered #{key.join ':'} for activation from LISTEN_FDS"
|
78
|
+
end
|
79
|
+
remove << k << 'LISTEN_PID'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
remove.each do |k|
|
84
|
+
ENV.delete k
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def parse(binds, logger)
|
89
|
+
binds.each do |str|
|
90
|
+
uri = URI.parse str
|
91
|
+
case uri.scheme
|
92
|
+
when "tcp"
|
93
|
+
if fd = @inherited_fds.delete(str)
|
94
|
+
io = inherit_tcp_listener uri.host, uri.port, fd
|
95
|
+
logger.log "* Inherited #{str}"
|
96
|
+
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
97
|
+
io = inherit_tcp_listener uri.host, uri.port, sock
|
98
|
+
logger.log "* Activated #{str}"
|
99
|
+
else
|
100
|
+
params = Util.parse_query uri.query
|
101
|
+
|
102
|
+
opt = params.key?('low_latency')
|
103
|
+
bak = params.fetch('backlog', 1024).to_i
|
104
|
+
|
105
|
+
io = add_tcp_listener uri.host, uri.port, opt, bak
|
106
|
+
|
107
|
+
@ios.each do |i|
|
108
|
+
addr = if i.local_address.ipv6?
|
109
|
+
"[#{i.local_address.ip_unpack[0]}]:#{i.local_address.ip_unpack[1]}"
|
110
|
+
else
|
111
|
+
i.local_address.ip_unpack.join(':')
|
112
|
+
end
|
113
|
+
|
114
|
+
logger.log "* Listening on tcp://#{addr}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
@listeners << [str, io] if io
|
119
|
+
when "unix"
|
120
|
+
path = "#{uri.host}#{uri.path}".gsub("%20", " ")
|
121
|
+
|
122
|
+
if fd = @inherited_fds.delete(str)
|
123
|
+
io = inherit_unix_listener path, fd
|
124
|
+
logger.log "* Inherited #{str}"
|
125
|
+
elsif sock = @activated_sockets.delete([ :unix, path ])
|
126
|
+
io = inherit_unix_listener path, sock
|
127
|
+
logger.log "* Activated #{str}"
|
128
|
+
else
|
129
|
+
umask = nil
|
130
|
+
mode = nil
|
131
|
+
backlog = 1024
|
132
|
+
|
133
|
+
if uri.query
|
134
|
+
params = Util.parse_query uri.query
|
135
|
+
if u = params['umask']
|
136
|
+
# Use Integer() to respect the 0 prefix as octal
|
137
|
+
umask = Integer(u)
|
138
|
+
end
|
139
|
+
|
140
|
+
if u = params['mode']
|
141
|
+
mode = Integer('0'+u)
|
142
|
+
end
|
143
|
+
|
144
|
+
if u = params['backlog']
|
145
|
+
backlog = Integer(u)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
io = add_unix_listener path, umask, mode, backlog
|
150
|
+
logger.log "* Listening on #{str}"
|
151
|
+
end
|
152
|
+
|
153
|
+
@listeners << [str, io]
|
154
|
+
when "ssl"
|
155
|
+
params = Util.parse_query uri.query
|
156
|
+
require 'puma/minissl'
|
157
|
+
|
158
|
+
MiniSSL.check
|
159
|
+
|
160
|
+
ctx = MiniSSL::Context.new
|
161
|
+
|
162
|
+
if defined?(JRUBY_VERSION)
|
163
|
+
unless params['keystore']
|
164
|
+
@events.error "Please specify the Java keystore via 'keystore='"
|
165
|
+
end
|
166
|
+
|
167
|
+
ctx.keystore = params['keystore']
|
168
|
+
|
169
|
+
unless params['keystore-pass']
|
170
|
+
@events.error "Please specify the Java keystore password via 'keystore-pass='"
|
171
|
+
end
|
172
|
+
|
173
|
+
ctx.keystore_pass = params['keystore-pass']
|
174
|
+
ctx.ssl_cipher_list = params['ssl_cipher_list'] if params['ssl_cipher_list']
|
175
|
+
else
|
176
|
+
unless params['key']
|
177
|
+
@events.error "Please specify the SSL key via 'key='"
|
178
|
+
end
|
179
|
+
|
180
|
+
ctx.key = params['key']
|
181
|
+
|
182
|
+
unless params['cert']
|
183
|
+
@events.error "Please specify the SSL cert via 'cert='"
|
184
|
+
end
|
185
|
+
|
186
|
+
ctx.cert = params['cert']
|
187
|
+
|
188
|
+
if ['peer', 'force_peer'].include?(params['verify_mode'])
|
189
|
+
unless params['ca']
|
190
|
+
@events.error "Please specify the SSL ca via 'ca='"
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
ctx.ca = params['ca'] if params['ca']
|
195
|
+
ctx.ssl_cipher_filter = params['ssl_cipher_filter'] if params['ssl_cipher_filter']
|
196
|
+
end
|
197
|
+
|
198
|
+
ctx.no_tlsv1 = true if params['no_tlsv1'] == 'true'
|
199
|
+
ctx.no_tlsv1_1 = true if params['no_tlsv1_1'] == 'true'
|
200
|
+
|
201
|
+
if params['verify_mode']
|
202
|
+
ctx.verify_mode = case params['verify_mode']
|
203
|
+
when "peer"
|
204
|
+
MiniSSL::VERIFY_PEER
|
205
|
+
when "force_peer"
|
206
|
+
MiniSSL::VERIFY_PEER | MiniSSL::VERIFY_FAIL_IF_NO_PEER_CERT
|
207
|
+
when "none"
|
208
|
+
MiniSSL::VERIFY_NONE
|
209
|
+
else
|
210
|
+
@events.error "Please specify a valid verify_mode="
|
211
|
+
MiniSSL::VERIFY_NONE
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
if fd = @inherited_fds.delete(str)
|
216
|
+
logger.log "* Inherited #{str}"
|
217
|
+
io = inherit_ssl_listener fd, ctx
|
218
|
+
elsif sock = @activated_sockets.delete([ :tcp, uri.host, uri.port ])
|
219
|
+
io = inherit_ssl_listener sock, ctx
|
220
|
+
logger.log "* Activated #{str}"
|
221
|
+
else
|
222
|
+
io = add_ssl_listener uri.host, uri.port, ctx
|
223
|
+
logger.log "* Listening on #{str}"
|
224
|
+
end
|
225
|
+
|
226
|
+
@listeners << [str, io] if io
|
227
|
+
else
|
228
|
+
logger.error "Invalid URI: #{str}"
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
# If we inherited fds but didn't use them (because of a
|
233
|
+
# configuration change), then be sure to close them.
|
234
|
+
@inherited_fds.each do |str, fd|
|
235
|
+
logger.log "* Closing unused inherited connection: #{str}"
|
236
|
+
|
237
|
+
begin
|
238
|
+
IO.for_fd(fd).close
|
239
|
+
rescue SystemCallError
|
240
|
+
end
|
241
|
+
|
242
|
+
# We have to unlink a unix socket path that's not being used
|
243
|
+
uri = URI.parse str
|
244
|
+
if uri.scheme == "unix"
|
245
|
+
path = "#{uri.host}#{uri.path}"
|
246
|
+
File.unlink path
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
# Also close any unused activated sockets
|
251
|
+
@activated_sockets.each do |key, sock|
|
252
|
+
logger.log "* Closing unused activated socket: #{key.join ':'}"
|
253
|
+
begin
|
254
|
+
sock.close
|
255
|
+
rescue SystemCallError
|
256
|
+
end
|
257
|
+
# We have to unlink a unix socket path that's not being used
|
258
|
+
File.unlink key[1] if key[0] == :unix
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def loopback_addresses
|
263
|
+
Socket.ip_address_list.select do |addrinfo|
|
264
|
+
addrinfo.ipv6_loopback? || addrinfo.ipv4_loopback?
|
265
|
+
end.map { |addrinfo| addrinfo.ip_address }.uniq
|
266
|
+
end
|
267
|
+
|
268
|
+
# Tell the server to listen on host +host+, port +port+.
|
269
|
+
# If +optimize_for_latency+ is true (the default) then clients connecting
|
270
|
+
# will be optimized for latency over throughput.
|
271
|
+
#
|
272
|
+
# +backlog+ indicates how many unaccepted connections the kernel should
|
273
|
+
# allow to accumulate before returning connection refused.
|
274
|
+
#
|
275
|
+
def add_tcp_listener(host, port, optimize_for_latency=true, backlog=1024)
|
276
|
+
if host == "localhost"
|
277
|
+
loopback_addresses.each do |addr|
|
278
|
+
add_tcp_listener addr, port, optimize_for_latency, backlog
|
279
|
+
end
|
280
|
+
return
|
281
|
+
end
|
282
|
+
|
283
|
+
host = host[1..-2] if host and host[0..0] == '['
|
284
|
+
s = TCPServer.new(host, port)
|
285
|
+
if optimize_for_latency
|
286
|
+
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
287
|
+
end
|
288
|
+
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
289
|
+
s.listen backlog
|
290
|
+
@connected_port = s.addr[1]
|
291
|
+
|
292
|
+
@ios << s
|
293
|
+
s
|
294
|
+
end
|
295
|
+
|
296
|
+
attr_reader :connected_port
|
297
|
+
|
298
|
+
def inherit_tcp_listener(host, port, fd)
|
299
|
+
if fd.kind_of? TCPServer
|
300
|
+
s = fd
|
301
|
+
else
|
302
|
+
s = TCPServer.for_fd(fd)
|
303
|
+
end
|
304
|
+
|
305
|
+
@ios << s
|
306
|
+
s
|
307
|
+
end
|
308
|
+
|
309
|
+
def add_ssl_listener(host, port, ctx,
|
310
|
+
optimize_for_latency=true, backlog=1024)
|
311
|
+
require 'puma/minissl'
|
312
|
+
|
313
|
+
MiniSSL.check
|
314
|
+
|
315
|
+
if host == "localhost"
|
316
|
+
loopback_addresses.each do |addr|
|
317
|
+
add_ssl_listener addr, port, ctx, optimize_for_latency, backlog
|
318
|
+
end
|
319
|
+
return
|
320
|
+
end
|
321
|
+
|
322
|
+
host = host[1..-2] if host[0..0] == '['
|
323
|
+
s = TCPServer.new(host, port)
|
324
|
+
if optimize_for_latency
|
325
|
+
s.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
|
326
|
+
end
|
327
|
+
s.setsockopt(Socket::SOL_SOCKET,Socket::SO_REUSEADDR, true)
|
328
|
+
s.listen backlog
|
329
|
+
|
330
|
+
|
331
|
+
ssl = MiniSSL::Server.new s, ctx
|
332
|
+
env = @proto_env.dup
|
333
|
+
env[HTTPS_KEY] = HTTPS
|
334
|
+
@envs[ssl] = env
|
335
|
+
|
336
|
+
@ios << ssl
|
337
|
+
s
|
338
|
+
end
|
339
|
+
|
340
|
+
def inherit_ssl_listener(fd, ctx)
|
341
|
+
require 'puma/minissl'
|
342
|
+
MiniSSL.check
|
343
|
+
|
344
|
+
if fd.kind_of? TCPServer
|
345
|
+
s = fd
|
346
|
+
else
|
347
|
+
s = TCPServer.for_fd(fd)
|
348
|
+
end
|
349
|
+
ssl = MiniSSL::Server.new(s, ctx)
|
350
|
+
|
351
|
+
env = @proto_env.dup
|
352
|
+
env[HTTPS_KEY] = HTTPS
|
353
|
+
@envs[ssl] = env
|
354
|
+
|
355
|
+
@ios << ssl
|
356
|
+
|
357
|
+
s
|
358
|
+
end
|
359
|
+
|
360
|
+
# Tell the server to listen on +path+ as a UNIX domain socket.
|
361
|
+
#
|
362
|
+
def add_unix_listener(path, umask=nil, mode=nil, backlog=1024)
|
363
|
+
@unix_paths << path
|
364
|
+
|
365
|
+
# Let anyone connect by default
|
366
|
+
umask ||= 0
|
367
|
+
|
368
|
+
begin
|
369
|
+
old_mask = File.umask(umask)
|
370
|
+
|
371
|
+
if File.exist? path
|
372
|
+
begin
|
373
|
+
old = UNIXSocket.new path
|
374
|
+
rescue SystemCallError, IOError
|
375
|
+
File.unlink path
|
376
|
+
else
|
377
|
+
old.close
|
378
|
+
raise "There is already a server bound to: #{path}"
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
s = UNIXServer.new(path)
|
383
|
+
s.listen backlog
|
384
|
+
@ios << s
|
385
|
+
ensure
|
386
|
+
File.umask old_mask
|
387
|
+
end
|
388
|
+
|
389
|
+
if mode
|
390
|
+
File.chmod mode, path
|
391
|
+
end
|
392
|
+
|
393
|
+
env = @proto_env.dup
|
394
|
+
env[REMOTE_ADDR] = "127.0.0.1"
|
395
|
+
@envs[s] = env
|
396
|
+
|
397
|
+
s
|
398
|
+
end
|
399
|
+
|
400
|
+
def inherit_unix_listener(path, fd)
|
401
|
+
@unix_paths << path
|
402
|
+
|
403
|
+
if fd.kind_of? TCPServer
|
404
|
+
s = fd
|
405
|
+
else
|
406
|
+
s = UNIXServer.for_fd fd
|
407
|
+
end
|
408
|
+
@ios << s
|
409
|
+
|
410
|
+
env = @proto_env.dup
|
411
|
+
env[REMOTE_ADDR] = "127.0.0.1"
|
412
|
+
@envs[s] = env
|
413
|
+
|
414
|
+
s
|
415
|
+
end
|
416
|
+
|
417
|
+
def close_listeners
|
418
|
+
@listeners.each do |l, io|
|
419
|
+
io.close
|
420
|
+
uri = URI.parse(l)
|
421
|
+
next unless uri.scheme == 'unix'
|
422
|
+
File.unlink("#{uri.host}#{uri.path}")
|
423
|
+
end
|
424
|
+
end
|
425
|
+
|
426
|
+
def close_unix_paths
|
427
|
+
@unix_paths.each { |up| File.unlink(up) if File.exist? up }
|
428
|
+
end
|
429
|
+
|
430
|
+
def redirects_for_restart
|
431
|
+
redirects = {:close_others => true}
|
432
|
+
@listeners.each_with_index do |(l, io), i|
|
433
|
+
ENV["PUMA_INHERIT_#{i}"] = "#{io.to_i}:#{l}"
|
434
|
+
redirects[io.to_i] = io.to_i
|
435
|
+
end
|
436
|
+
redirects
|
437
|
+
end
|
438
|
+
end
|
439
|
+
end
|