falcon 0.32.1 → 0.33.0
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.
- 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
|