falcon 0.34.5 → 0.35.0

Sign up to get free protection for your applications and to get access to all the features.
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)