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 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