falcon 0.32.1 → 0.33.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +0 -1
- data/.travis.yml +6 -1
- data/examples/beer/config.ru +2 -2
- data/examples/beer/falcon.rb +3 -3
- data/examples/benchmark/falcon.rb +3 -1
- data/examples/hello/falcon.rb +3 -3
- data/falcon.gemspec +1 -2
- data/lib/falcon/command.rb +5 -4
- data/lib/falcon/command/host.rb +11 -6
- data/lib/falcon/command/supervisor.rb +74 -0
- data/lib/falcon/command/virtual.rb +19 -1
- data/lib/falcon/configuration.rb +93 -146
- data/lib/falcon/configurations/host.rb +44 -0
- data/lib/falcon/configurations/lets_encrypt_tls.rb +28 -0
- data/lib/falcon/configurations/proxy.rb +25 -0
- data/lib/falcon/configurations/rack.rb +31 -0
- data/lib/falcon/configurations/self_signed_tls.rb +45 -0
- data/lib/falcon/configurations/supervisor.rb +35 -0
- data/lib/falcon/configurations/tls.rb +44 -0
- data/lib/falcon/host.rb +7 -32
- data/lib/falcon/hosts.rb +2 -6
- data/lib/falcon/proxy_endpoint.rb +69 -0
- data/lib/falcon/service.rb +53 -0
- data/lib/falcon/services.rb +49 -0
- data/lib/falcon/supervisor.rb +106 -0
- data/lib/falcon/version.rb +1 -1
- metadata +16 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75d7948a19515712232d9eb7318bbfb3f890da83e93769d7bd3c5f11ef8fd56b
|
4
|
+
data.tar.gz: bb70af1d70fa8d51efeeda865416d2eb284baf364cce93717dde95e5be415399
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ecd5fd3744fc9f145cbd2dd754fb8e4082eb4066492658fb8941316ca90356cd0461438f7a44cef4b0497c5d0fe0a90dcb4135e461348900dad05d68ef23c31d
|
7
|
+
data.tar.gz: bbf427f717117d135480ca337bc376a949ea5ebb0df28aa02e4d1e6d6231e86aaf69b21c243e656042ecdfc66cb055e44a5308b94612f89b8ce24f3eaa6adf78
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -7,6 +7,9 @@ addons:
|
|
7
7
|
packages:
|
8
8
|
- wrk
|
9
9
|
- apache2-utils
|
10
|
+
homebrew:
|
11
|
+
packages:
|
12
|
+
- wrk
|
10
13
|
|
11
14
|
after_success:
|
12
15
|
- bundle exec rake benchmark:compare
|
@@ -21,11 +24,13 @@ matrix:
|
|
21
24
|
- rvm: 2.6
|
22
25
|
gemfile: gems/rack3.gemfile
|
23
26
|
- rvm: 2.6
|
24
|
-
env: COVERAGE=
|
27
|
+
env: COVERAGE=Summary,Coveralls
|
25
28
|
- rvm: truffleruby
|
26
29
|
- rvm: jruby-head
|
27
30
|
env: JRUBY_OPTS="--debug -X+O"
|
28
31
|
- rvm: ruby-head
|
32
|
+
- rvm: 2.6
|
33
|
+
os: osx
|
29
34
|
allow_failures:
|
30
35
|
- rvm: truffleruby
|
31
36
|
- rvm: ruby-head
|
data/examples/beer/config.ru
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
#!/usr/bin/env falcon --verbose serve -c
|
2
2
|
|
3
3
|
require 'rack'
|
4
|
-
require '
|
4
|
+
require 'cgi'
|
5
5
|
|
6
6
|
def bottles(n)
|
7
7
|
n == 1 ? "#{n} bottle" : "#{n} bottles"
|
@@ -42,7 +42,7 @@ run lambda {|env|
|
|
42
42
|
|
43
43
|
code = File.read(__FILE__)
|
44
44
|
body.write("<h1>Source Code</h1>")
|
45
|
-
body.write("<pre><code>#{
|
45
|
+
body.write("<pre><code>#{CGI.escapeHTML code}</code></pre>")
|
46
46
|
body.write("</body></html>")
|
47
47
|
rescue
|
48
48
|
puts "Remote end closed connection: #{$!}"
|
data/examples/beer/falcon.rb
CHANGED
data/examples/hello/falcon.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
#!/usr/bin/env -S falcon host
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
3
|
+
load :rack, :self_signed_tls, :supervisor
|
4
|
+
|
5
|
+
rack 'hello.localhost', :self_signed_tls
|
6
6
|
|
7
7
|
# service 'jobs' do
|
8
8
|
# shell ['rake', 'background:jobs:process']
|
data/falcon.gemspec
CHANGED
@@ -28,9 +28,8 @@ Gem::Specification.new do |spec|
|
|
28
28
|
|
29
29
|
spec.add_dependency 'samovar', "~> 2.1"
|
30
30
|
spec.add_dependency 'localhost', "~> 1.1"
|
31
|
-
spec.add_dependency 'build-environment', '~> 1.
|
31
|
+
spec.add_dependency 'build-environment', '~> 1.11'
|
32
32
|
|
33
|
-
spec.add_development_dependency "trenni"
|
34
33
|
spec.add_development_dependency "async-rspec", "~> 1.7"
|
35
34
|
spec.add_development_dependency "async-websocket", "~> 0.12.0"
|
36
35
|
spec.add_development_dependency "async-process", "~> 1.1"
|
data/lib/falcon/command.rb
CHANGED
@@ -21,11 +21,11 @@
|
|
21
21
|
require_relative 'command/serve'
|
22
22
|
require_relative 'command/virtual'
|
23
23
|
require_relative 'command/host'
|
24
|
+
require_relative 'command/supervisor'
|
24
25
|
|
25
26
|
require_relative 'version'
|
26
27
|
|
27
28
|
require 'samovar'
|
28
|
-
require 'logger'
|
29
29
|
|
30
30
|
module Falcon
|
31
31
|
module Command
|
@@ -46,6 +46,7 @@ module Falcon
|
|
46
46
|
'serve' => Serve,
|
47
47
|
'virtual' => Virtual,
|
48
48
|
'host' => Host,
|
49
|
+
'supervisor' => Supervisor
|
49
50
|
}, default: 'serve'
|
50
51
|
|
51
52
|
def verbose?
|
@@ -58,11 +59,11 @@ module Falcon
|
|
58
59
|
|
59
60
|
def call
|
60
61
|
if verbose?
|
61
|
-
Async.logger.
|
62
|
+
Async.logger.debug!
|
62
63
|
elsif quiet?
|
63
|
-
Async.logger.
|
64
|
+
Async.logger.warn!
|
64
65
|
else
|
65
|
-
Async.logger.
|
66
|
+
Async.logger.info!
|
66
67
|
end
|
67
68
|
|
68
69
|
if @options[:version]
|
data/lib/falcon/command/host.rb
CHANGED
@@ -20,8 +20,9 @@
|
|
20
20
|
|
21
21
|
require_relative '../server'
|
22
22
|
require_relative '../endpoint'
|
23
|
-
require_relative '../hosts'
|
24
23
|
require_relative '../configuration'
|
24
|
+
require_relative '../hosts'
|
25
|
+
require_relative '../services'
|
25
26
|
|
26
27
|
require 'async/container'
|
27
28
|
require 'async/container/controller'
|
@@ -57,16 +58,20 @@ module Falcon
|
|
57
58
|
|
58
59
|
assume_privileges(@path)
|
59
60
|
|
60
|
-
|
61
|
-
|
61
|
+
hosts = Hosts.new(configuration)
|
62
|
+
hosts.each do |host|
|
63
|
+
host.run(container)
|
64
|
+
end
|
65
|
+
|
66
|
+
services = Services.new(configuration)
|
67
|
+
services.each do |service|
|
68
|
+
service.run(container)
|
62
69
|
end
|
63
70
|
|
64
71
|
return container
|
65
72
|
end
|
66
73
|
|
67
|
-
def call
|
68
|
-
container = Async::Container::Forked.new
|
69
|
-
|
74
|
+
def call(container = Async::Container.new)
|
70
75
|
container = run(container, parent.verbose?)
|
71
76
|
|
72
77
|
container.wait(true)
|
@@ -0,0 +1,74 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'samovar'
|
22
|
+
require 'async'
|
23
|
+
require 'json'
|
24
|
+
require 'async/io/unix_endpoint'
|
25
|
+
|
26
|
+
module Falcon
|
27
|
+
module Command
|
28
|
+
class Supervisor < Samovar::Command
|
29
|
+
self.description = "Control and query a specific host."
|
30
|
+
|
31
|
+
options do
|
32
|
+
option "--path <path>", "The control IPC path.", default: "supervisor.ipc"
|
33
|
+
end
|
34
|
+
|
35
|
+
class Restart < Samovar::Command
|
36
|
+
self.description = "Restart the process group."
|
37
|
+
|
38
|
+
def call(stream)
|
39
|
+
stream.puts({please: 'restart'}.to_json, separator: "\0")
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
class Statistics < Samovar::Command
|
44
|
+
self.description = "Show statistics about the process group."
|
45
|
+
|
46
|
+
def call(stream)
|
47
|
+
stream.puts({please: 'statistics'}.to_json, separator: "\0")
|
48
|
+
response = JSON.parse(stream.gets("\0"), symbolize_names: true)
|
49
|
+
|
50
|
+
pp response
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
nested :command, {
|
55
|
+
'restart' => Restart,
|
56
|
+
'statistics' => Statistics,
|
57
|
+
}, default: 'statistics'
|
58
|
+
|
59
|
+
def endpoint
|
60
|
+
Async::IO::Endpoint.unix(@options[:path])
|
61
|
+
end
|
62
|
+
|
63
|
+
def call
|
64
|
+
Async do
|
65
|
+
endpoint.connect do |socket|
|
66
|
+
stream = Async::IO::Stream.new(socket)
|
67
|
+
|
68
|
+
@command.call(stream)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -47,16 +47,34 @@ module Falcon
|
|
47
47
|
|
48
48
|
many :paths
|
49
49
|
|
50
|
+
def assume_privileges(path)
|
51
|
+
stat = File.stat(path)
|
52
|
+
|
53
|
+
Process::GID.change_privilege(stat.gid)
|
54
|
+
Process::UID.change_privilege(stat.uid)
|
55
|
+
end
|
56
|
+
|
57
|
+
def spawn(path, container)
|
58
|
+
container.spawn(name: self.name, restart: true) do |instance|
|
59
|
+
assume_privileges(path)
|
60
|
+
|
61
|
+
instance.exec("bundle", "exec", path)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
50
65
|
def run(verbose = false)
|
51
66
|
configuration = Configuration.new(verbose)
|
67
|
+
container = Async::Container.new
|
52
68
|
|
53
69
|
@paths.each do |path|
|
54
70
|
configuration.load_file(path)
|
71
|
+
spawn(path, container)
|
55
72
|
end
|
56
73
|
|
57
74
|
hosts = Hosts.new(configuration)
|
75
|
+
hosts.run(container, **@options)
|
58
76
|
|
59
|
-
return
|
77
|
+
return container
|
60
78
|
end
|
61
79
|
|
62
80
|
def call
|
data/lib/falcon/configuration.rb
CHANGED
@@ -19,185 +19,132 @@
|
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
21
|
require 'build/environment'
|
22
|
-
require 'async/io/unix_endpoint'
|
23
22
|
|
24
23
|
module Falcon
|
25
|
-
class
|
26
|
-
def initialize(
|
27
|
-
|
28
|
-
|
29
|
-
@endpoint = endpoint
|
30
|
-
end
|
31
|
-
|
32
|
-
attr :endpoint
|
33
|
-
|
34
|
-
def protocol
|
35
|
-
@options[:protocol]
|
36
|
-
end
|
37
|
-
|
38
|
-
def scheme
|
39
|
-
@options[:scheme]
|
40
|
-
end
|
41
|
-
|
42
|
-
def authority
|
43
|
-
@options[:authority]
|
24
|
+
class Configuration
|
25
|
+
def initialize(verbose = false)
|
26
|
+
@environments = {}
|
44
27
|
end
|
45
28
|
|
46
|
-
|
47
|
-
@endpoint.connect(&block)
|
48
|
-
end
|
29
|
+
attr :environments
|
49
30
|
|
50
|
-
def
|
51
|
-
|
31
|
+
def each(key = :authority)
|
32
|
+
return to_enum(key) unless block_given?
|
33
|
+
|
34
|
+
@environments.each do |name, environment|
|
35
|
+
environment = environment.flatten
|
36
|
+
|
37
|
+
if environment.include?(key)
|
38
|
+
yield environment
|
39
|
+
end
|
40
|
+
end
|
52
41
|
end
|
53
42
|
|
54
|
-
def
|
55
|
-
|
43
|
+
def add(environment)
|
44
|
+
name = environment.name
|
56
45
|
|
57
|
-
|
58
|
-
|
46
|
+
unless name
|
47
|
+
raise ArgumentError, "Environment name is nil #{environment.inspect}"
|
59
48
|
end
|
49
|
+
|
50
|
+
environment = environment.flatten
|
51
|
+
|
52
|
+
raise KeyError.new("#{name.inspect} is already set", key: name) if @environments.key?(name)
|
53
|
+
|
54
|
+
@environments[name] = environment
|
60
55
|
end
|
61
56
|
|
62
|
-
def
|
63
|
-
|
57
|
+
def load_file(path)
|
58
|
+
Loader.load_file(self, path)
|
64
59
|
end
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
add(:ssl) do
|
73
|
-
ssl_session_id {"falcon"}
|
60
|
+
|
61
|
+
class Loader
|
62
|
+
def initialize(configuration, root = nil)
|
63
|
+
@loaded = {}
|
64
|
+
@configuration = configuration
|
65
|
+
@environments = {}
|
66
|
+
@root = root
|
74
67
|
end
|
75
68
|
|
76
|
-
|
77
|
-
|
78
|
-
ssl_certificate {OpenSSL::X509::Certificate.new(File.read(ssl_certificate_path))}
|
79
|
-
|
80
|
-
ssl_private_key_path {File.expand_path("ssl/private.key", root)}
|
81
|
-
ssl_private_key {OpenSSL::PKey::RSA.new(File.read(ssl_private_key_path))}
|
82
|
-
|
83
|
-
ssl_context do
|
84
|
-
OpenSSL::SSL::SSLContext.new.tap do |context|
|
85
|
-
context.cert = ssl_certificate
|
86
|
-
context.key = ssl_private_key
|
87
|
-
|
88
|
-
context.session_id_context = ssl_session_id
|
89
|
-
|
90
|
-
context.set_params(
|
91
|
-
verify_mode: OpenSSL::SSL::VERIFY_NONE,
|
92
|
-
)
|
93
|
-
|
94
|
-
context.setup
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
69
|
+
attr :path
|
70
|
+
attr :configuration
|
98
71
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
72
|
+
def self.load_file(configuration, path)
|
73
|
+
path = File.realpath(path)
|
74
|
+
root = File.dirname(path)
|
75
|
+
|
76
|
+
loader = self.new(configuration, root)
|
77
|
+
|
78
|
+
loader.instance_eval(File.read(path), path)
|
103
79
|
end
|
104
80
|
|
105
|
-
|
106
|
-
|
107
|
-
|
81
|
+
def load(*features)
|
82
|
+
features.each do |feature|
|
83
|
+
next if @loaded.include?(feature)
|
84
|
+
|
85
|
+
relative_path = File.join(__dir__, "configurations", "#{feature}.rb")
|
86
|
+
|
87
|
+
self.instance_eval(File.read(relative_path), relative_path)
|
108
88
|
|
109
|
-
|
110
|
-
context.alpn_select_cb = lambda do |protocols|
|
111
|
-
if protocols.include? "h2"
|
112
|
-
return "h2"
|
113
|
-
elsif protocols.include? "http/1.1"
|
114
|
-
return "http/1.1"
|
115
|
-
elsif protocols.include? "http/1.0"
|
116
|
-
return "http/1.0"
|
117
|
-
else
|
118
|
-
return nil
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
context.session_id_context = "falcon"
|
123
|
-
end
|
89
|
+
@loaded[feature] = relative_path
|
124
90
|
end
|
125
91
|
end
|
126
92
|
|
127
|
-
add(
|
128
|
-
|
129
|
-
end
|
130
|
-
|
131
|
-
add(:rack, :host) do
|
132
|
-
config_path {::File.expand_path("config.ru", root)}
|
93
|
+
def add(name, *parents, &block)
|
94
|
+
raise KeyError.new("#{name} is already set", key: name) if @environments.key?(name)
|
133
95
|
|
134
|
-
|
135
|
-
::Falcon::Server.middleware(
|
136
|
-
::Rack::Builder.parse_file(config_path).first, verbose: verbose
|
137
|
-
)
|
138
|
-
end
|
96
|
+
environments = parents.map{|name| @environments.fetch(name)}
|
139
97
|
|
140
|
-
|
141
|
-
scheme 'https'
|
142
|
-
ipc_path {::File.expand_path("server.ipc", root)}
|
98
|
+
parent = Build::Environment.combine(*environments)
|
143
99
|
|
144
|
-
|
145
|
-
|
100
|
+
@environments[name] = merge(name, *parents, &block)
|
101
|
+
end
|
146
102
|
|
147
|
-
|
148
|
-
|
149
|
-
Async::IO::SharedEndpoint.bound(endpoint)
|
150
|
-
end.wait
|
151
|
-
end
|
103
|
+
def host(name, *parents, &block)
|
104
|
+
environment = merge(name, :host, *parents, &block)
|
152
105
|
|
153
|
-
|
154
|
-
|
155
|
-
|
106
|
+
environment[:root] = @root
|
107
|
+
environment[:authority] = name
|
108
|
+
|
109
|
+
@configuration.add(environment.flatten)
|
156
110
|
end
|
157
|
-
end
|
158
|
-
|
159
|
-
attr :environments
|
160
|
-
|
161
|
-
def add(name, *parents, &block)
|
162
|
-
raise KeyError.new("#{name} is already set", key: name) if @environments.key?(name)
|
163
|
-
|
164
|
-
environments = parents.map{|name| @environments.fetch(name)}
|
165
|
-
|
166
|
-
parent = Build::Environment.combine(*environments)
|
167
|
-
|
168
|
-
@environments[name] = Build::Environment.new(parent, name: name, &block)
|
169
|
-
end
|
170
|
-
|
171
|
-
def each
|
172
|
-
return to_enum unless block_given?
|
173
111
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
def host(name, *parents, &block)
|
182
|
-
add(name, :host, *parents, &block).tap do |environment|
|
112
|
+
def proxy(name, *parents, &block)
|
113
|
+
environment = merge(name, :proxy, *parents, &block)
|
114
|
+
|
115
|
+
environment[:root] = @root
|
183
116
|
environment[:authority] = name
|
117
|
+
|
118
|
+
@configuration.add(environment.flatten)
|
184
119
|
end
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
120
|
+
|
121
|
+
def rack(name, *parents, &block)
|
122
|
+
environment = merge(name, :rack, *parents, &block)
|
123
|
+
|
124
|
+
environment[:root] = @root
|
189
125
|
environment[:authority] = name
|
126
|
+
|
127
|
+
@configuration.add(environment.flatten)
|
190
128
|
end
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
129
|
+
|
130
|
+
def supervisor
|
131
|
+
name = File.join(@root, "supervisor")
|
132
|
+
environment = merge(name, :supervisor)
|
133
|
+
|
134
|
+
environment[:root] = @root
|
135
|
+
|
136
|
+
@configuration.add(environment.flatten)
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def merge(name, *parents, &block)
|
142
|
+
environments = parents.map{|name| @environments.fetch(name)}
|
143
|
+
|
144
|
+
parent = Build::Environment.combine(*environments)
|
145
|
+
|
146
|
+
return Build::Environment.new(parent, name: name, &block)
|
196
147
|
end
|
197
|
-
end
|
198
|
-
|
199
|
-
def load_file(path)
|
200
|
-
self.instance_eval(File.read(path), File.realpath(path))
|
201
148
|
end
|
202
149
|
end
|
203
150
|
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative '../proxy_endpoint'
|
22
|
+
require_relative '../server'
|
23
|
+
|
24
|
+
add(:host) do
|
25
|
+
middleware do
|
26
|
+
::Protocol::HTTP::Middleware::HelloWorld
|
27
|
+
end
|
28
|
+
|
29
|
+
ipc_path {::File.expand_path("server.ipc", root)}
|
30
|
+
protocol {Async::HTTP::Protocol::HTTP2}
|
31
|
+
scheme 'https'
|
32
|
+
|
33
|
+
endpoint {::Falcon::ProxyEndpoint.unix(ipc_path, protocol: protocol, scheme: scheme, authority: authority)}
|
34
|
+
|
35
|
+
bound_endpoint do
|
36
|
+
Async::Reactor.run do
|
37
|
+
Async::IO::SharedEndpoint.bound(endpoint)
|
38
|
+
end.wait
|
39
|
+
end
|
40
|
+
|
41
|
+
server do
|
42
|
+
::Falcon::Server.new(middleware, bound_endpoint, protocol, scheme)
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
load(:tls)
|
22
|
+
|
23
|
+
add(:lets_encrypt_tls, :tls) do
|
24
|
+
lets_encrypt_root '/etc/letsencrypt/live'
|
25
|
+
|
26
|
+
ssl_certificate_path {File.join(lets_encrypt_root, authority, "fullchain.pem")}
|
27
|
+
ssl_private_key_path {File.join(lets_encrypt_root, authority, "privkey.pem")}
|
28
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
load(:host)
|
22
|
+
|
23
|
+
add(:proxy, :host) do
|
24
|
+
endpoint {::Async::HTTP::Endpoint.parse(url)}
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
load :host
|
22
|
+
|
23
|
+
add(:rack, :host) do
|
24
|
+
config_path {::File.expand_path("config.ru", root)}
|
25
|
+
|
26
|
+
middleware do
|
27
|
+
::Falcon::Server.middleware(
|
28
|
+
::Rack::Builder.parse_file(config_path).first, verbose: verbose
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'localhost/authority'
|
22
|
+
|
23
|
+
add(:self_signed_tls) do
|
24
|
+
ssl_session_id {"falcon"}
|
25
|
+
|
26
|
+
ssl_context do
|
27
|
+
contexts = Localhost::Authority.fetch(authority)
|
28
|
+
|
29
|
+
contexts.server_context.tap do |context|
|
30
|
+
context.alpn_select_cb = lambda do |protocols|
|
31
|
+
if protocols.include? "h2"
|
32
|
+
return "h2"
|
33
|
+
elsif protocols.include? "http/1.1"
|
34
|
+
return "http/1.1"
|
35
|
+
elsif protocols.include? "http/1.0"
|
36
|
+
return "http/1.0"
|
37
|
+
else
|
38
|
+
return nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context.session_id_context = ssl_session_id
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative '../supervisor'
|
22
|
+
|
23
|
+
add(:supervisor) do
|
24
|
+
start true
|
25
|
+
|
26
|
+
name "supervisor"
|
27
|
+
|
28
|
+
ipc_path {::File.expand_path("supervisor.ipc", root)}
|
29
|
+
|
30
|
+
endpoint {Async::IO::Endpoint.unix(ipc_path)}
|
31
|
+
|
32
|
+
service do
|
33
|
+
::Falcon::Supervisor.new(endpoint)
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
add(:tls) do
|
22
|
+
ssl_session_id {"falcon"}
|
23
|
+
|
24
|
+
ssl_certificate_path {File.expand_path("ssl/certificate.pem", root)}
|
25
|
+
ssl_certificate {OpenSSL::X509::Certificate.new(File.read(ssl_certificate_path))}
|
26
|
+
|
27
|
+
ssl_private_key_path {File.expand_path("ssl/private.key", root)}
|
28
|
+
ssl_private_key {OpenSSL::PKey::RSA.new(File.read(ssl_private_key_path))}
|
29
|
+
|
30
|
+
ssl_context do
|
31
|
+
OpenSSL::SSL::SSLContext.new.tap do |context|
|
32
|
+
context.cert = ssl_certificate
|
33
|
+
context.key = ssl_private_key
|
34
|
+
|
35
|
+
context.session_id_context = ssl_session_id
|
36
|
+
|
37
|
+
context.set_params(
|
38
|
+
verify_mode: OpenSSL::SSL::VERIFY_NONE,
|
39
|
+
)
|
40
|
+
|
41
|
+
context.setup
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/falcon/host.rb
CHANGED
@@ -18,22 +18,10 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
require_relative 'proxy'
|
24
|
-
require_relative 'redirection'
|
25
|
-
|
26
|
-
require 'async/container'
|
27
|
-
require 'async/container/controller'
|
28
|
-
require 'async/http/endpoint'
|
21
|
+
require_relative 'service'
|
29
22
|
|
30
23
|
module Falcon
|
31
|
-
class Host
|
32
|
-
def initialize(environment)
|
33
|
-
@environment = environment.flatten
|
34
|
-
@evaluator = @environment.evaluator
|
35
|
-
end
|
36
|
-
|
24
|
+
class Host < Service
|
37
25
|
def name
|
38
26
|
"Falcon Host for #{self.authority}"
|
39
27
|
end
|
@@ -62,29 +50,12 @@ module Falcon
|
|
62
50
|
"\#<#{self.class} #{@evaluator.authority}>"
|
63
51
|
end
|
64
52
|
|
65
|
-
def assume_privileges(path)
|
66
|
-
stat = File.stat(path)
|
67
|
-
|
68
|
-
Process::GID.change_privilege(stat.gid)
|
69
|
-
Process::UID.change_privilege(stat.uid)
|
70
|
-
end
|
71
|
-
|
72
|
-
def spawn(container)
|
73
|
-
container.spawn(name: self.name, restart: true) do |instance|
|
74
|
-
path = File.join(self.root, "falcon.rb")
|
75
|
-
|
76
|
-
assume_privileges(path)
|
77
|
-
|
78
|
-
instance.exec("bundle", "exec", path)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
53
|
def run(container)
|
83
54
|
if @environment.include?(:server)
|
84
55
|
bound_endpoint = self.bound_endpoint
|
85
56
|
|
86
57
|
container.run(name: self.name, restart: true) do |task, instance|
|
87
|
-
Async.logger.info(self) {"Starting application server..."}
|
58
|
+
Async.logger.info(self) {"Starting application server, binding to #{self.endpoint}..."}
|
88
59
|
|
89
60
|
server = @evaluator.server
|
90
61
|
|
@@ -92,6 +63,10 @@ module Falcon
|
|
92
63
|
|
93
64
|
task.children.each(&:wait)
|
94
65
|
end
|
66
|
+
|
67
|
+
container.attach do
|
68
|
+
bound_endpoint.close
|
69
|
+
end
|
95
70
|
end
|
96
71
|
end
|
97
72
|
end
|
data/lib/falcon/hosts.rb
CHANGED
@@ -38,13 +38,13 @@ module Falcon
|
|
38
38
|
@server_context = nil
|
39
39
|
@server_endpoint = nil
|
40
40
|
|
41
|
-
configuration.each do |environment|
|
41
|
+
configuration.each(:authority) do |environment|
|
42
42
|
add(Host.new(environment))
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
46
46
|
def each(&block)
|
47
|
-
@named.
|
47
|
+
@named.each_value(&block)
|
48
48
|
end
|
49
49
|
|
50
50
|
def endpoint
|
@@ -100,10 +100,6 @@ module Falcon
|
|
100
100
|
end
|
101
101
|
|
102
102
|
def run(container = Async::Container.new, **options)
|
103
|
-
@named.each do |name, host|
|
104
|
-
host.spawn(container)
|
105
|
-
end
|
106
|
-
|
107
103
|
secure_endpoint = Async::HTTP::Endpoint.parse(options[:bind_secure], ssl_context: self.ssl_context)
|
108
104
|
insecure_endpoint = Async::HTTP::Endpoint.parse(options[:bind_insecure])
|
109
105
|
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'async/io/unix_endpoint'
|
22
|
+
|
23
|
+
module Falcon
|
24
|
+
class ProxyEndpoint < Async::IO::Endpoint
|
25
|
+
def initialize(endpoint, **options)
|
26
|
+
super(**options)
|
27
|
+
|
28
|
+
@endpoint = endpoint
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_s
|
32
|
+
"\#<#{self.class} endpoint=#{@endpoint}>"
|
33
|
+
end
|
34
|
+
|
35
|
+
attr :endpoint
|
36
|
+
|
37
|
+
def protocol
|
38
|
+
@options[:protocol]
|
39
|
+
end
|
40
|
+
|
41
|
+
def scheme
|
42
|
+
@options[:scheme]
|
43
|
+
end
|
44
|
+
|
45
|
+
def authority
|
46
|
+
@options[:authority]
|
47
|
+
end
|
48
|
+
|
49
|
+
def connect(&block)
|
50
|
+
@endpoint.connect(&block)
|
51
|
+
end
|
52
|
+
|
53
|
+
def bind(&block)
|
54
|
+
@endpoint.bind(&block)
|
55
|
+
end
|
56
|
+
|
57
|
+
def each
|
58
|
+
return to_enum unless block_given?
|
59
|
+
|
60
|
+
@endpoint.each do |endpoint|
|
61
|
+
yield self.class.new(endpoint, @options)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.unix(path, **options)
|
66
|
+
self.new(::Async::IO::Endpoint.unix(path), **options)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'async/io/endpoint'
|
22
|
+
|
23
|
+
require_relative 'proxy'
|
24
|
+
require_relative 'redirection'
|
25
|
+
|
26
|
+
require 'async/container'
|
27
|
+
require 'async/container/controller'
|
28
|
+
require 'async/http/endpoint'
|
29
|
+
|
30
|
+
module Falcon
|
31
|
+
class Service
|
32
|
+
def initialize(environment)
|
33
|
+
@environment = environment
|
34
|
+
@evaluator = @environment.evaluator
|
35
|
+
end
|
36
|
+
|
37
|
+
def name
|
38
|
+
@evaluator.name
|
39
|
+
end
|
40
|
+
|
41
|
+
def run(container)
|
42
|
+
container.run(name: self.name, count: 1, restart: true) do |task, instance|
|
43
|
+
Async.logger.info(self) {"Starting #{self.name}..."}
|
44
|
+
|
45
|
+
if service = @evaluator.service
|
46
|
+
service.run
|
47
|
+
else
|
48
|
+
Async.logger.error(self) {"Could not determine how to start service: #{@environment.inspect}"}
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require_relative 'service'
|
22
|
+
|
23
|
+
module Falcon
|
24
|
+
class Services
|
25
|
+
def initialize(configuration)
|
26
|
+
@named = {}
|
27
|
+
|
28
|
+
configuration.each(:start) do |environment|
|
29
|
+
add(Service.new(environment))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def each(&block)
|
34
|
+
@named.each_value(&block)
|
35
|
+
end
|
36
|
+
|
37
|
+
def add(service)
|
38
|
+
@named[service.name] = service
|
39
|
+
end
|
40
|
+
|
41
|
+
def run(container = Async::Container.new, **options)
|
42
|
+
@named.each do |name, service|
|
43
|
+
service.spawn(container)
|
44
|
+
end
|
45
|
+
|
46
|
+
return container
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'async/io/endpoint'
|
22
|
+
|
23
|
+
require_relative 'proxy'
|
24
|
+
require_relative 'redirection'
|
25
|
+
|
26
|
+
require 'async/container'
|
27
|
+
require 'async/container/controller'
|
28
|
+
require 'async/http/endpoint'
|
29
|
+
|
30
|
+
module Falcon
|
31
|
+
class Supervisor
|
32
|
+
class Statistics
|
33
|
+
PS = "ps"
|
34
|
+
|
35
|
+
def initialize(pgid: Process.ppid, ps: PS)
|
36
|
+
@ppid = pgid
|
37
|
+
@ps = ps
|
38
|
+
end
|
39
|
+
|
40
|
+
# pid: Process Identifier
|
41
|
+
# pmem: Percentage Memory used.
|
42
|
+
# pcpu: Percentage Processor used.
|
43
|
+
# time: The process time used (executing on CPU).
|
44
|
+
# vsz: Virtual Size in kilobytes
|
45
|
+
# rss: Resident Set Size in kilobytes
|
46
|
+
# etime: The process elapsed time.
|
47
|
+
# command: The name of the process.
|
48
|
+
COLUMNS = "pid,pmem,pcpu,time,vsz,rss,etime,command"
|
49
|
+
|
50
|
+
def capture
|
51
|
+
input, output = IO.pipe
|
52
|
+
|
53
|
+
system(@ps, "--ppid", @ppid.to_s, "-o", COLUMNS, out: output, pgroup: true)
|
54
|
+
output.close
|
55
|
+
|
56
|
+
header, *lines = input.readlines.map(&:strip)
|
57
|
+
|
58
|
+
keys = header.split(/\s+/).map(&:downcase)
|
59
|
+
|
60
|
+
processes = lines.map do |line|
|
61
|
+
keys.zip(line.split(/\s+/, keys.count)).to_h
|
62
|
+
end
|
63
|
+
|
64
|
+
return processes
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def initialize(endpoint)
|
69
|
+
@endpoint = endpoint
|
70
|
+
end
|
71
|
+
|
72
|
+
def restart(message)
|
73
|
+
signal = message[:signal] || :INT
|
74
|
+
|
75
|
+
# Sepukku:
|
76
|
+
Process.kill(signal, -Process.getpgrp)
|
77
|
+
end
|
78
|
+
|
79
|
+
def statistics(message)
|
80
|
+
statistics = Statistics.new
|
81
|
+
|
82
|
+
statistics.capture
|
83
|
+
end
|
84
|
+
|
85
|
+
def handle(message)
|
86
|
+
case message[:please]
|
87
|
+
when 'restart'
|
88
|
+
self.restart(message)
|
89
|
+
when 'statistics'
|
90
|
+
self.statistics(message)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def run
|
95
|
+
Async.logger.info("Binding to #{@endpoint}")
|
96
|
+
@endpoint.accept do |peer|
|
97
|
+
stream = Async::IO::Stream.new(peer)
|
98
|
+
|
99
|
+
while message = stream.gets("\0")
|
100
|
+
response = handle(JSON.parse(message, symbolize_names: true))
|
101
|
+
stream.puts(response.to_json, separator: "\0")
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
data/lib/falcon/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: falcon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.33.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-06-
|
11
|
+
date: 2019-06-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: async
|
@@ -114,28 +114,14 @@ dependencies:
|
|
114
114
|
requirements:
|
115
115
|
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '1.
|
117
|
+
version: '1.11'
|
118
118
|
type: :runtime
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
122
|
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '1.
|
125
|
-
- !ruby/object:Gem::Dependency
|
126
|
-
name: trenni
|
127
|
-
requirement: !ruby/object:Gem::Requirement
|
128
|
-
requirements:
|
129
|
-
- - ">="
|
130
|
-
- !ruby/object:Gem::Version
|
131
|
-
version: '0'
|
132
|
-
type: :development
|
133
|
-
prerelease: false
|
134
|
-
version_requirements: !ruby/object:Gem::Requirement
|
135
|
-
requirements:
|
136
|
-
- - ">="
|
137
|
-
- !ruby/object:Gem::Version
|
138
|
-
version: '0'
|
124
|
+
version: '1.11'
|
139
125
|
- !ruby/object:Gem::Dependency
|
140
126
|
name: async-rspec
|
141
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -280,14 +266,26 @@ files:
|
|
280
266
|
- lib/falcon/command.rb
|
281
267
|
- lib/falcon/command/host.rb
|
282
268
|
- lib/falcon/command/serve.rb
|
269
|
+
- lib/falcon/command/supervisor.rb
|
283
270
|
- lib/falcon/command/virtual.rb
|
284
271
|
- lib/falcon/configuration.rb
|
272
|
+
- lib/falcon/configurations/host.rb
|
273
|
+
- lib/falcon/configurations/lets_encrypt_tls.rb
|
274
|
+
- lib/falcon/configurations/proxy.rb
|
275
|
+
- lib/falcon/configurations/rack.rb
|
276
|
+
- lib/falcon/configurations/self_signed_tls.rb
|
277
|
+
- lib/falcon/configurations/supervisor.rb
|
278
|
+
- lib/falcon/configurations/tls.rb
|
285
279
|
- lib/falcon/endpoint.rb
|
286
280
|
- lib/falcon/host.rb
|
287
281
|
- lib/falcon/hosts.rb
|
288
282
|
- lib/falcon/proxy.rb
|
283
|
+
- lib/falcon/proxy_endpoint.rb
|
289
284
|
- lib/falcon/redirection.rb
|
290
285
|
- lib/falcon/server.rb
|
286
|
+
- lib/falcon/service.rb
|
287
|
+
- lib/falcon/services.rb
|
288
|
+
- lib/falcon/supervisor.rb
|
291
289
|
- lib/falcon/verbose.rb
|
292
290
|
- lib/falcon/version.rb
|
293
291
|
- lib/rack/handler/falcon.rb
|