falcon 0.34.5 → 0.35.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -2
  3. data/Gemfile +2 -0
  4. data/bin/falcon +1 -1
  5. data/bin/falcon-host +1 -1
  6. data/examples/beer/config.ru +25 -23
  7. data/examples/beer/falcon.rb +2 -0
  8. data/examples/hello/config.ru +1 -1
  9. data/examples/hello/falcon.rb +14 -2
  10. data/examples/hello/preload.rb +6 -0
  11. data/examples/trailers/config.ru +33 -0
  12. data/examples/trailers/falcon.rb +7 -0
  13. data/falcon.gemspec +3 -1
  14. data/lib/falcon.rb +0 -4
  15. data/lib/falcon/adapters/response.rb +2 -2
  16. data/lib/falcon/command.rb +3 -53
  17. data/lib/falcon/command/host.rb +22 -39
  18. data/lib/falcon/command/paths.rb +45 -0
  19. data/lib/falcon/{host.rb → command/proxy.rb} +39 -45
  20. data/lib/falcon/command/redirect.rb +72 -0
  21. data/lib/falcon/command/serve.rb +28 -58
  22. data/lib/falcon/command/supervisor.rb +5 -5
  23. data/lib/falcon/command/top.rb +79 -0
  24. data/lib/falcon/command/virtual.rb +18 -53
  25. data/lib/falcon/configuration.rb +1 -1
  26. data/lib/falcon/{configurations/host.rb → configuration/application.rb} +13 -11
  27. data/lib/falcon/{configurations → configuration}/lets_encrypt_tls.rb +0 -0
  28. data/lib/falcon/{configurations → configuration}/proxy.rb +2 -2
  29. data/lib/falcon/{configurations → configuration}/rack.rb +2 -2
  30. data/lib/falcon/{configurations → configuration}/self_signed_tls.rb +0 -0
  31. data/lib/falcon/{configurations → configuration}/supervisor.rb +2 -2
  32. data/lib/falcon/{configurations → configuration}/tls.rb +0 -0
  33. data/lib/falcon/controller/host.rb +58 -0
  34. data/lib/falcon/controller/proxy.rb +102 -0
  35. data/lib/falcon/{service.rb → controller/redirect.rb} +37 -24
  36. data/lib/falcon/controller/serve.rb +112 -0
  37. data/lib/falcon/controller/virtual.rb +89 -0
  38. data/lib/falcon/middleware/proxy.rb +143 -0
  39. data/lib/falcon/{redirection.rb → middleware/redirect.rb} +31 -29
  40. data/lib/falcon/proxy_endpoint.rb +1 -1
  41. data/lib/falcon/service/application.rb +113 -0
  42. data/lib/falcon/service/generic.rb +53 -0
  43. data/lib/falcon/service/supervisor.rb +95 -0
  44. data/lib/falcon/services.rb +32 -5
  45. data/lib/falcon/version.rb +1 -1
  46. data/lib/rack/handler/falcon.rb +2 -1
  47. data/logo-square.afdesign +0 -0
  48. metadata +43 -17
  49. data/lib/falcon/hosts.rb +0 -135
  50. data/lib/falcon/proxy.rb +0 -141
  51. data/lib/falcon/supervisor.rb +0 -106
@@ -18,55 +18,49 @@
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_relative 'service'
21
+ require_relative '../controller/proxy'
22
+ require_relative 'paths'
23
+
24
+ require 'samovar'
22
25
 
23
26
  module Falcon
24
- class Host < Service
25
- def name
26
- "Falcon Host for #{self.authority}"
27
- end
28
-
29
- def authority
30
- @evaluator.authority
31
- end
32
-
33
- def endpoint
34
- @evaluator.endpoint
35
- end
36
-
37
- def ssl_context
38
- @evaluator.ssl_context
39
- end
40
-
41
- def root
42
- @evaluator.root
43
- end
44
-
45
- def bound_endpoint
46
- @evaluator.bound_endpoint
47
- end
48
-
49
- def to_s
50
- "\#<#{self.class} #{@evaluator.authority}>"
51
- end
52
-
53
- def run(container)
54
- if @environment.include?(:server)
55
- bound_endpoint = self.bound_endpoint
56
-
57
- container.run(name: self.name, restart: true) do |task, instance|
58
- Async.logger.info(self) {"Starting application server, binding to #{self.endpoint}..."}
59
-
60
- server = @evaluator.server
61
-
62
- server.run
63
-
64
- task.children.each(&:wait)
27
+ module Command
28
+ class Proxy < Samovar::Command
29
+ self.description = "Proxy to one or more backend hosts."
30
+
31
+ options do
32
+ option '--bind <address>', "Bind to the given hostname/address", default: "https://[::]:443"
33
+ end
34
+
35
+ many :paths
36
+
37
+ include Paths
38
+
39
+ def controller
40
+ Controller::Proxy.new(self)
41
+ end
42
+
43
+ def container_class
44
+ Async::Container.best_container_class
45
+ end
46
+
47
+ def container_options
48
+ {}
49
+ end
50
+
51
+ def call
52
+ Async.logger.info(self) do |buffer|
53
+ buffer.puts "Falcon Proxy v#{VERSION} taking flight!"
54
+ buffer.puts "- Binding to: #{@options[:bind]}"
55
+ buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
56
+ buffer.puts "- To reload: kill -HUP #{Process.pid}"
65
57
  end
66
58
 
67
- container.attach do
68
- bound_endpoint.close
69
- end
59
+ self.controller.run
60
+ end
61
+
62
+ def endpoint(**options)
63
+ Async::HTTP::Endpoint.parse(@options[:bind], **options)
70
64
  end
71
65
  end
72
66
  end
@@ -0,0 +1,72 @@
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 '../controller/redirect'
22
+ require_relative 'paths'
23
+
24
+ require 'samovar'
25
+
26
+ module Falcon
27
+ module Command
28
+ class Redirect < Samovar::Command
29
+ self.description = "Redirect from insecure HTTP to secure HTTP."
30
+
31
+ options do
32
+ option '--bind <address>', "Bind to the given hostname/address", default: "http://[::]:80"
33
+ option '--redirect <address>', "Redirect using this address as a template.", default: "https://[::]:443"
34
+ end
35
+
36
+ many :paths
37
+
38
+ include Paths
39
+
40
+ def controller
41
+ Controller::Redirect.new(self)
42
+ end
43
+
44
+ def container_class
45
+ Async::Container.best_container_class
46
+ end
47
+
48
+ def container_options
49
+ {}
50
+ end
51
+
52
+ def call
53
+ Async.logger.info(self) do |buffer|
54
+ buffer.puts "Falcon Redirect v#{VERSION} taking flight!"
55
+ buffer.puts "- Binding to: #{@options[:bind]}"
56
+ buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
57
+ buffer.puts "- To reload: kill -HUP #{Process.pid}"
58
+ end
59
+
60
+ self.controller.run
61
+ end
62
+
63
+ def endpoint(**options)
64
+ Async::HTTP::Endpoint.parse(@options[:bind], **options)
65
+ end
66
+
67
+ def redirect_endpoint(**options)
68
+ Async::HTTP::Endpoint.parse(@options[:redirect], **options)
69
+ end
70
+ end
71
+ end
72
+ end
@@ -20,8 +20,10 @@
20
20
 
21
21
  require_relative '../server'
22
22
  require_relative '../endpoint'
23
+ require_relative '../controller/serve'
23
24
 
24
25
  require 'async/container'
26
+
25
27
  require 'async/io/trap'
26
28
  require 'async/io/host_endpoint'
27
29
  require 'async/io/shared_endpoint'
@@ -40,16 +42,16 @@ module Falcon
40
42
  self.description = "Run an HTTP server."
41
43
 
42
44
  options do
43
- option '-b/--bind <address>', "Bind to the given hostname/address", default: "https://localhost:9292"
45
+ option '-b/--bind <address>', "Bind to the given hostname/address.", default: "https://localhost:9292"
44
46
 
45
- option '-p/--port <number>', "Override the specified port", type: Integer
47
+ option '-p/--port <number>', "Override the specified port.", type: Integer
46
48
  option '-h/--hostname <hostname>', "Specify the hostname which would be used for certificates, etc."
47
- option '-t/--timeout <duration>', "Specify the maximum time to wait for blocking operations.", type: Float, default: nil
49
+ option '-t/--timeout <duration>', "Specify the maximum time to wait for non-blocking operations.", type: Float, default: nil
48
50
 
49
- option '-c/--config <path>', "Rackup configuration file to load", default: 'config.ru'
50
- option '--preload', "Preload the bundle before creating containers"
51
+ option '-c/--config <path>', "Rackup configuration file to load.", default: 'config.ru'
52
+ option '--preload', "Preload the bundle before creating containers."
51
53
 
52
- option '--forked | --threaded | --hybrid', "Select a specific parallelism model", key: :container, default: :forked
54
+ option '--forked | --threaded | --hybrid', "Select a specific parallelism model.", key: :container, default: :forked
53
55
 
54
56
  option '-n/--count <count>', "Number of instances to start.", default: Async::Container.processor_count, type: Integer
55
57
 
@@ -68,7 +70,11 @@ module Falcon
68
70
  end
69
71
  end
70
72
 
71
- def load_app(verbose)
73
+ def verbose?
74
+ @parent&.verbose?
75
+ end
76
+
77
+ def load_app(verbose = self.verbose?)
72
78
  rack_app, options = Rack::Builder.parse_file(@options[:config])
73
79
 
74
80
  return Server.middleware(rack_app, verbose: verbose), options
@@ -95,6 +101,10 @@ module Falcon
95
101
  slice_options(:hostname, :port, :reuse_port, :timeout)
96
102
  end
97
103
 
104
+ def endpoint
105
+ Endpoint.parse(@options[:bind], **endpoint_options)
106
+ end
107
+
98
108
  def client_endpoint
99
109
  Async::HTTP::Endpoint.parse(@options[:bind], **endpoint_options)
100
110
  end
@@ -103,62 +113,22 @@ module Falcon
103
113
  Async::HTTP::Client.new(client_endpoint)
104
114
  end
105
115
 
106
- def run(verbose = false)
107
- if @options[:preload]
108
- Bundler.require(:preload)
109
- end
110
-
111
- endpoint = Endpoint.parse(@options[:bind], **endpoint_options)
112
-
113
- bound_endpoint = Async::Reactor.run do
114
- Async::IO::SharedEndpoint.bound(endpoint)
115
- end.wait
116
-
117
- Async.logger.info(endpoint) do |buffer|
118
- buffer.puts "Falcon v#{VERSION} taking flight! Using #{container_class} #{container_options}"
116
+ def controller
117
+ Controller::Serve.new(self)
118
+ end
119
+
120
+ def call
121
+ Async.logger.info(self.endpoint) do |buffer|
122
+ buffer.puts "Falcon v#{VERSION} taking flight! Using #{self.container_class} #{self.container_options}."
119
123
  buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
124
+ buffer.puts "- To reload configuration: kill -HUP #{Process.pid}"
120
125
  end
121
126
 
122
- debug_trap = Async::IO::Trap.new(:USR1)
123
- debug_trap.ignore!
124
-
125
- container = container_class.new
126
-
127
- container.attach do
128
- bound_endpoint.close
129
- end
130
-
131
- container.run(name: "Falcon Server", restart: true, **container_options) do |task, instance|
132
- task.async do
133
- if debug_trap.install!
134
- Async.logger.info(instance) do
135
- "- Per-process status: kill -USR1 #{Process.pid}"
136
- end
137
- end
138
-
139
- debug_trap.trap do
140
- Async.logger.info(self) do |buffer|
141
- task.reactor.print_hierarchy(buffer)
142
- end
143
- end
144
- end
145
-
146
- app, _ = load_app(verbose)
147
-
148
- server = Falcon::Server.new(app, bound_endpoint, endpoint.protocol, endpoint.scheme)
149
-
150
- server.run
151
-
152
- task.children.each(&:wait)
127
+ if @options[:preload]
128
+ Bundler.require(:preload)
153
129
  end
154
130
 
155
- return container
156
- end
157
-
158
- def call
159
- container = run(parent.verbose?)
160
-
161
- container.wait
131
+ self.controller.run
162
132
  end
163
133
  end
164
134
  end
@@ -40,11 +40,11 @@ module Falcon
40
40
  end
41
41
  end
42
42
 
43
- class Statistics < Samovar::Command
44
- self.description = "Show statistics about the process group."
43
+ class Metrics < Samovar::Command
44
+ self.description = "Show metrics about the falcon processes."
45
45
 
46
46
  def call(stream)
47
- stream.puts({please: 'statistics'}.to_json, separator: "\0")
47
+ stream.puts({please: 'metrics'}.to_json, separator: "\0")
48
48
  response = JSON.parse(stream.gets("\0"), symbolize_names: true)
49
49
 
50
50
  pp response
@@ -53,8 +53,8 @@ module Falcon
53
53
 
54
54
  nested :command, {
55
55
  'restart' => Restart,
56
- 'statistics' => Statistics,
57
- }, default: 'statistics'
56
+ 'metrics' => Metrics,
57
+ }, default: 'metrics'
58
58
 
59
59
  def endpoint
60
60
  Async::IO::Endpoint.unix(@options[:path])
@@ -0,0 +1,79 @@
1
+ # Copyright, 2017, 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 'serve'
22
+ require_relative 'host'
23
+ require_relative 'virtual'
24
+ require_relative 'proxy'
25
+ require_relative 'redirect'
26
+ require_relative 'supervisor'
27
+
28
+ require_relative '../version'
29
+
30
+ require 'samovar'
31
+
32
+ module Falcon
33
+ module Command
34
+ class Top < Samovar::Command
35
+ self.description = "An asynchronous HTTP server."
36
+
37
+ options do
38
+ option '--verbose | --quiet', "Verbosity of output for debugging.", key: :logging
39
+ option '-h/--help', "Print out help information."
40
+ option '-v/--version', "Print out the application version."
41
+ end
42
+
43
+ nested :command, {
44
+ 'serve' => Serve,
45
+ 'host' => Host,
46
+ 'virtual' => Virtual,
47
+ 'proxy' => Proxy,
48
+ 'redirect' => Redirect,
49
+ 'supervisor' => Supervisor,
50
+ }, default: 'serve'
51
+
52
+ def verbose?
53
+ @options[:logging] == :verbose
54
+ end
55
+
56
+ def quiet?
57
+ @options[:logging] == :quiet
58
+ end
59
+
60
+ def call
61
+ # if verbose?
62
+ # Async.logger.debug!
63
+ # elsif quiet?
64
+ # Async.logger.warn!
65
+ # else
66
+ # Async.logger.info!
67
+ # end
68
+ #
69
+ if @options[:version]
70
+ puts "#{self.name} v#{Falcon::VERSION}"
71
+ elsif @options[:help]
72
+ self.print_usage
73
+ else
74
+ @command.call
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -18,80 +18,45 @@
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_relative '../server'
22
- require_relative '../endpoint'
23
- require_relative '../hosts'
24
- require_relative '../configuration'
25
-
26
- require 'async/container'
27
- require 'async/container/controller'
28
-
29
- require 'async/io/host_endpoint'
30
- require 'async/io/shared_endpoint'
31
- require 'async/io/ssl_endpoint'
21
+ require_relative '../controller/virtual'
22
+ require_relative 'paths'
32
23
 
33
24
  require 'samovar'
34
25
 
35
- require 'rack/builder'
36
- require 'rack/server'
37
-
38
26
  module Falcon
39
27
  module Command
40
28
  class Virtual < Samovar::Command
41
29
  self.description = "Run one or more virtual hosts with a front-end proxy."
42
30
 
43
31
  options do
44
- option '--bind-insecure <address>', "Bind redirection to the given hostname/address", default: "http://[::]"
45
- option '--bind-secure <address>', "Bind proxy to the given hostname/address", default: "https://[::]"
32
+ option '--bind-insecure <address>', "Bind redirection to the given hostname/address", default: "http://[::]:8080"
33
+ option '--bind-secure <address>', "Bind proxy to the given hostname/address", default: "https://[::]:8443"
46
34
  end
47
35
 
48
36
  many :paths
49
37
 
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
-
56
- home = Etc.getpwuid(stat.uid).dir
57
-
58
- return {
59
- 'HOME' => home,
60
- }
38
+ include Paths
39
+
40
+ def controller
41
+ Controller::Virtual.new(self)
61
42
  end
62
43
 
63
- def spawn(path, container, **options)
64
- container.spawn(name: self.name, restart: true) do |instance|
65
- env = assume_privileges(path)
66
-
67
- instance.exec(env, "bundle", "exec", path, **options)
68
- end
44
+ def bind_secure
45
+ @options[:bind_secure]
69
46
  end
70
47
 
71
- def run(verbose = false)
72
- configuration = Configuration.new(verbose)
73
- container = Async::Container.new
74
-
75
- @paths.each do |path|
76
- path = File.expand_path(path)
77
- root = File.dirname(path)
78
-
79
- configuration.load_file(path)
80
-
81
- spawn(path, container, chdir: root)
82
- end
83
-
84
- hosts = Hosts.new(configuration)
85
-
86
- hosts.run(container, **@options)
87
-
88
- return container
48
+ def bind_insecure
49
+ @options[:bind_insecure]
89
50
  end
90
51
 
91
52
  def call
92
- container = run(parent.verbose?)
53
+ Async.logger.info(self) do |buffer|
54
+ buffer.puts "Falcon Virtual v#{VERSION} taking flight!"
55
+ buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
56
+ buffer.puts "- To reload all sites: kill -HUP #{Process.pid}"
57
+ end
93
58
 
94
- container.wait
59
+ self.controller.run
95
60
  end
96
61
 
97
62
  def insecure_endpoint(**options)