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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c80289856ac260e9a3e423465926308c76630cdb6d9d6940f147e2f643af638a
4
- data.tar.gz: 0dc95d7527f64b6bc8ae6de7bfaa5f9eb5c36c26a40d6b4d318d2c63a354f4c2
3
+ metadata.gz: 75d7948a19515712232d9eb7318bbfb3f890da83e93769d7bd3c5f11ef8fd56b
4
+ data.tar.gz: bb70af1d70fa8d51efeeda865416d2eb284baf364cce93717dde95e5be415399
5
5
  SHA512:
6
- metadata.gz: 8cde639a3858513ccdd80c8192d9158f75973375ecf2d66b468d18943da3e6f490cb9edaf3c94cb96b5494e140287c71545f41c806fbd4bba28e88f5f77ad43a
7
- data.tar.gz: edb463f74426954aa1a71788060cdda534d4ce9ae147a4f42899f1251e669644ed5be5ab4e9e22d7f17f24158d4d9ffedc1659f38a4cbaa3c0d3695a41961fa6
6
+ metadata.gz: ecd5fd3744fc9f145cbd2dd754fb8e4082eb4066492658fb8941316ca90356cd0461438f7a44cef4b0497c5d0fe0a90dcb4135e461348900dad05d68ef23c31d
7
+ data.tar.gz: bbf427f717117d135480ca337bc376a949ea5ebb0df28aa02e4d1e6d6231e86aaf69b21c243e656042ecdfc66cb055e44a5308b94612f89b8ce24f3eaa6adf78
data/.gitignore CHANGED
@@ -10,6 +10,5 @@
10
10
  /spec/reports/
11
11
  /tmp/
12
12
 
13
- # rspec failure tracking
14
13
  .rspec_status
15
14
  .covered.db
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=BriefSummary,Coveralls
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
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env falcon --verbose serve -c
2
2
 
3
3
  require 'rack'
4
- require 'trenni'
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>#{Trenni::Markup.escape_string code}</code></pre>")
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: #{$!}"
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env -S falcon host
2
2
 
3
- host 'beer.localhost', :rack, :self_signed do
4
- root __dir__
5
- end
3
+ load :rack, :self_signed_tls, :supervisor
4
+
5
+ rack 'beer.localhost', :self_signed_tls
@@ -1,3 +1,5 @@
1
1
  #!/usr/bin/env -S ./bin/falcon virtual
2
2
 
3
- host 'benchmark.local', :self_signed
3
+ load :rack, :self_signed_tls, :supervisor
4
+
5
+ rack 'benchmark.local', :self_signed_tls
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env -S falcon host
2
2
 
3
- host 'hello.localhost', :rack, :self_signed do
4
- root __dir__
5
- end
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.6'
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"
@@ -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.level = Logger::DEBUG
62
+ Async.logger.debug!
62
63
  elsif quiet?
63
- Async.logger.level = Logger::WARN
64
+ Async.logger.warn!
64
65
  else
65
- Async.logger.level = Logger::INFO
66
+ Async.logger.info!
66
67
  end
67
68
 
68
69
  if @options[:version]
@@ -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
- configuration.each do |environment|
61
- Falcon::Host.new(environment).run(container)
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 hosts.run(@options)
77
+ return container
60
78
  end
61
79
 
62
80
  def call
@@ -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 ProxyEndpoint < Async::IO::Endpoint
26
- def initialize(endpoint, **options)
27
- super(**options)
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
- def connect(&block)
47
- @endpoint.connect(&block)
48
- end
29
+ attr :environments
49
30
 
50
- def bind(&block)
51
- @endpoint.bind(&block)
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 each
55
- return to_enum unless block_given?
43
+ def add(environment)
44
+ name = environment.name
56
45
 
57
- @endpoint.each do |endpoint|
58
- yield self.class.new(endpoint, @options)
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 self.unix(path, **options)
63
- self.new(::Async::IO::Endpoint.unix(path), **options)
57
+ def load_file(path)
58
+ Loader.load_file(self, path)
64
59
  end
65
- end
66
-
67
- class Configuration
68
- def initialize(verbose = false)
69
- @environments = {}
70
- @verbose = verbose
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
- add(:host, :ssl) do
77
- ssl_certificate_path {File.expand_path("ssl/certificate.pem", root)}
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
- add(:lets_encrypt, :ssl) do
100
- lets_encrypt_root '/etc/letsencrypt/live'
101
- ssl_certificate_path {File.join(lets_encrypt_root, authority, "fullchain.pem")}
102
- ssl_private_key_path {File.join(lets_encrypt_root, authority, "privkey.pem")}
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
- add(:self_signed, :ssl) do
106
- ssl_context do
107
- contexts = Localhost::Authority.fetch(authority)
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
- contexts.server_context.tap do |context|
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(:proxy, :host) do
128
- endpoint {::Async::HTTP::Endpoint.parse(url)}
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
- middleware do
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
- authority 'localhost'
141
- scheme 'https'
142
- ipc_path {::File.expand_path("server.ipc", root)}
98
+ parent = Build::Environment.combine(*environments)
143
99
 
144
- endpoint {ProxyEndpoint.unix(ipc_path, protocol: Async::HTTP::Protocol::HTTP2, scheme: scheme, authority: authority)}
145
- protocol {endpoint.protocol}
100
+ @environments[name] = merge(name, *parents, &block)
101
+ end
146
102
 
147
- bound_endpoint do
148
- Async::Reactor.run do
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
- server do
154
- ::Falcon::Server.new(middleware, bound_endpoint, protocol, scheme)
155
- end
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
- @environments.each do |name, environment|
175
- if environment.include?(:authority)
176
- yield environment
177
- end
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
- end
186
-
187
- def proxy(name, *parents, &block)
188
- add(name, :proxy, *parents, &block).tap do |environment|
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
- end
192
-
193
- def rack(name, *parents, &block)
194
- add(name, :rack, *parents, &block).tap do |environment|
195
- environment[:authority] = name
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
- 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'
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.each(&block)
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
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Falcon
22
- VERSION = "0.32.1"
22
+ VERSION = "0.33.0"
23
23
  end
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.32.1
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-08 00:00:00.000000000 Z
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.6'
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.6'
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