falcon 0.43.0 → 0.45.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 (45) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +1 -1
  3. data/changes.md +36 -0
  4. data/lib/falcon/command/host.rb +6 -32
  5. data/lib/falcon/command/proxy.rb +20 -15
  6. data/lib/falcon/command/redirect.rb +21 -15
  7. data/lib/falcon/command/serve.rb +44 -65
  8. data/lib/falcon/command/top.rb +1 -1
  9. data/lib/falcon/command/virtual.rb +15 -23
  10. data/lib/falcon/configuration.rb +26 -124
  11. data/lib/falcon/environment/application.rb +60 -0
  12. data/lib/falcon/environment/lets_encrypt_tls.rb +34 -0
  13. data/lib/falcon/environment/proxy.rb +109 -0
  14. data/lib/falcon/environment/rack.rb +20 -0
  15. data/lib/falcon/environment/rackup.rb +26 -0
  16. data/lib/falcon/environment/redirect.rb +50 -0
  17. data/lib/falcon/environment/self_signed_tls.rb +45 -0
  18. data/lib/falcon/environment/server.rb +69 -0
  19. data/lib/falcon/environment/supervisor.rb +40 -0
  20. data/lib/falcon/environment/tls.rb +97 -0
  21. data/lib/falcon/{environments.rb → environment.rb} +3 -4
  22. data/lib/falcon/service/server.rb +84 -0
  23. data/lib/falcon/service/supervisor.rb +4 -3
  24. data/lib/falcon/{controller → service}/virtual.rb +71 -18
  25. data/lib/falcon/version.rb +2 -2
  26. data/license.md +2 -0
  27. data.tar.gz.sig +0 -0
  28. metadata +28 -30
  29. metadata.gz.sig +0 -0
  30. data/lib/.DS_Store +0 -0
  31. data/lib/falcon/controller/host.rb +0 -55
  32. data/lib/falcon/controller/proxy.rb +0 -109
  33. data/lib/falcon/controller/redirect.rb +0 -59
  34. data/lib/falcon/controller/serve.rb +0 -110
  35. data/lib/falcon/environments/application.rb +0 -56
  36. data/lib/falcon/environments/lets_encrypt_tls.rb +0 -30
  37. data/lib/falcon/environments/proxy.rb +0 -22
  38. data/lib/falcon/environments/rack.rb +0 -33
  39. data/lib/falcon/environments/self_signed_tls.rb +0 -38
  40. data/lib/falcon/environments/supervisor.rb +0 -34
  41. data/lib/falcon/environments/tls.rb +0 -86
  42. data/lib/falcon/service/application.rb +0 -99
  43. data/lib/falcon/service/generic.rb +0 -61
  44. data/lib/falcon/service/proxy.rb +0 -49
  45. data/lib/falcon/services.rb +0 -82
@@ -1,56 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
5
- # Copyright, 2020, by Daniel Evans.
6
-
7
- require_relative '../proxy_endpoint'
8
- require_relative '../server'
9
-
10
- require_relative '../service/application'
11
-
12
- # A general application environment.
13
- # Suitable for use with any {Protocol::HTTP::Middleware}.
14
- #
15
- # @scope Falcon Environments
16
- # @name application
17
- environment(:application) do
18
- # The middleware stack for the application.
19
- # @attribute [Protocol::HTTP::Middleware]
20
- middleware do
21
- ::Protocol::HTTP::Middleware::HelloWorld
22
- end
23
-
24
- # The scheme to use to communicate with the application.
25
- # @attribute [String]
26
- scheme 'https'
27
-
28
- # The protocol to use to communicate with the application.
29
- #
30
- # Typically one of {Async::HTTP::Protocol::HTTP1} or {Async::HTTP::Protocl::HTTP2}.
31
- #
32
- # @attribute [Async::HTTP::Protocol]
33
- protocol {Async::HTTP::Protocol::HTTP2}
34
-
35
- # The IPC path to use for communication with the application.
36
- # @attribute [String]
37
- ipc_path {::File.expand_path("application.ipc", root)}
38
-
39
- # The endpoint that will be used for communicating with the application server.
40
- # @attribute [Async::IO::Endpoint]
41
- endpoint do
42
- ::Falcon::ProxyEndpoint.unix(ipc_path,
43
- protocol: protocol,
44
- scheme: scheme,
45
- authority: authority
46
- )
47
- end
48
-
49
- # The service class to use for the application.
50
- # @attribute [Class]
51
- service ::Falcon::Service::Application
52
-
53
- # Number of instances to start.
54
- # @attribute [Integer | nil]
55
- count nil
56
- end
@@ -1,30 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2020-2023, by Samuel Williams.
5
-
6
- load(:tls)
7
-
8
- # A Lets Encrypt SSL context environment.
9
- #
10
- # Derived from {.tls}.
11
- #
12
- # @scope Falcon Environments
13
- # @name lets_encrypt_tls
14
- environment(:lets_encrypt_tls, :tls) do
15
- # The Lets Encrypt certificate store path.
16
- # @parameter [String]
17
- lets_encrypt_root '/etc/letsencrypt/live'
18
-
19
- # The public certificate path.
20
- # @attribute [String]
21
- ssl_certificate_path do
22
- File.join(lets_encrypt_root, authority, "fullchain.pem")
23
- end
24
-
25
- # The private key path.
26
- # @attribute [String]
27
- ssl_private_key_path do
28
- File.join(lets_encrypt_root, authority, "privkey.pem")
29
- end
30
- end
@@ -1,22 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
5
-
6
- require_relative '../service/proxy'
7
-
8
- # A HTTP proxy environment.
9
- #
10
- # Derived from {.application}.
11
- #
12
- # @scope Falcon Environments
13
- # @name rack
14
- environment(:proxy) do
15
- # The upstream endpoint that will handle incoming requests.
16
- # @attribute [Async::HTTP::Endpoint]
17
- endpoint {::Async::HTTP::Endpoint.parse(url)}
18
-
19
- # The service class to use for the proxy.
20
- # @attribute [Class]
21
- service ::Falcon::Service::Proxy
22
- end
@@ -1,33 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
5
-
6
- load :application
7
-
8
- # A rack application environment.
9
- #
10
- # Derived from {.application}.
11
- #
12
- # @scope Falcon Environments
13
- # @name rack
14
- environment(:rack, :application) do
15
- # The rack configuration path.
16
- # @attribute [String]
17
- config_path {::File.expand_path("config.ru", root)}
18
-
19
- # Whether to enable the application layer cache.
20
- # @attribute [String]
21
- cache false
22
-
23
- # The middleware stack for the rack application.
24
- # @attribute [Protocol::HTTP::Middleware]
25
- middleware do
26
- app, _ = ::Rack::Builder.parse_file(config_path)
27
-
28
- ::Falcon::Server.middleware(app,
29
- verbose: verbose,
30
- cache: cache
31
- )
32
- end
33
- end
@@ -1,38 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
5
-
6
- require 'localhost/authority'
7
-
8
- # A self-signed SSL context environment.
9
- #
10
- # @scope Falcon Environments
11
- # @name self_signed_tls
12
- environment(:self_signed_tls) do
13
- # The default session identifier for the session cache.
14
- # @attribute [String]
15
- ssl_session_id {"falcon"}
16
-
17
- # The SSL context to use for incoming connections.
18
- # @attribute [OpenSSL::SSL::SSLContext]
19
- ssl_context do
20
- contexts = Localhost::Authority.fetch(authority)
21
-
22
- contexts.server_context.tap do |context|
23
- context.alpn_select_cb = lambda do |protocols|
24
- if protocols.include? "h2"
25
- return "h2"
26
- elsif protocols.include? "http/1.1"
27
- return "http/1.1"
28
- elsif protocols.include? "http/1.0"
29
- return "http/1.0"
30
- else
31
- return nil
32
- end
33
- end
34
-
35
- context.session_id_context = ssl_session_id
36
- end
37
- end
38
- end
@@ -1,34 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
5
-
6
- require_relative '../service/supervisor'
7
-
8
- # A application process monitor environment.
9
- #
10
- # @scope Falcon Environments
11
- # @name supervisor
12
- environment(:supervisor) do
13
- # The name of the supervisor
14
- # @attribute [String]
15
- name "supervisor"
16
-
17
- # The IPC path to use for communication with the supervisor.
18
- # @attribute [String]
19
- ipc_path do
20
- ::File.expand_path("supervisor.ipc", root)
21
- end
22
-
23
- # The endpoint the supervisor will bind to.
24
- # @attribute [Async::IO::Endpoint]
25
- endpoint do
26
- Async::IO::Endpoint.unix(ipc_path)
27
- end
28
-
29
- # The service class to use for the supervisor.
30
- # @attribute [Class]
31
- service do
32
- ::Falcon::Service::Supervisor
33
- end
34
- end
@@ -1,86 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
5
-
6
- require_relative '../controller/proxy'
7
- require_relative '../tls'
8
-
9
- # A general SSL context environment.
10
- #
11
- # @scope Falcon Environments
12
- # @name tls
13
- environment(:tls) do
14
- # The default session identifier for the session cache.
15
- # @attribute [String]
16
- ssl_session_id "falcon"
17
-
18
- # The supported ciphers.
19
- # @attribute [Array(String)]
20
- ssl_ciphers Falcon::TLS::SERVER_CIPHERS
21
-
22
- # The public certificate path.
23
- # @attribute [String]
24
- ssl_certificate_path do
25
- File.expand_path("ssl/certificate.pem", root)
26
- end
27
-
28
- # The list of certificates loaded from that path.
29
- # @attribute [Array(OpenSSL::X509::Certificate)]
30
- ssl_certificates do
31
- OpenSSL::X509::Certificate.load_file(ssl_certificate_path)
32
- end
33
-
34
- # The main certificate.
35
- # @attribute [OpenSSL::X509::Certificate]
36
- ssl_certificate {ssl_certificates[0]}
37
-
38
- # The certificate chain.
39
- # @attribute [Array(OpenSSL::X509::Certificate)]
40
- ssl_certificate_chain {ssl_certificates[1..-1]}
41
-
42
- # The private key path.
43
- # @attribute [String]
44
- ssl_private_key_path do
45
- File.expand_path("ssl/private.key", root)
46
- end
47
-
48
- # The private key.
49
- # @attribute [OpenSSL::PKey::RSA]
50
- ssl_private_key do
51
- OpenSSL::PKey::RSA.new(File.read(ssl_private_key_path))
52
- end
53
-
54
- # The SSL context to use for incoming connections.
55
- # @attribute [OpenSSL::SSL::SSLContext]
56
- ssl_context do
57
- OpenSSL::SSL::SSLContext.new.tap do |context|
58
- context.add_certificate(ssl_certificate, ssl_private_key, ssl_certificate_chain)
59
-
60
- context.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT
61
- context.session_id_context = ssl_session_id
62
-
63
- context.alpn_select_cb = lambda do |protocols|
64
- if protocols.include? "h2"
65
- return "h2"
66
- elsif protocols.include? "http/1.1"
67
- return "http/1.1"
68
- elsif protocols.include? "http/1.0"
69
- return "http/1.0"
70
- else
71
- return nil
72
- end
73
- end
74
-
75
- # TODO Ruby 2.4 requires using ssl_version.
76
- context.ssl_version = :TLSv1_2_server
77
-
78
- context.set_params(
79
- ciphers: ssl_ciphers,
80
- verify_mode: OpenSSL::SSL::VERIFY_NONE,
81
- )
82
-
83
- context.setup
84
- end
85
- end
86
- end
@@ -1,99 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
5
- # Copyright, 2020, by Daniel Evans.
6
-
7
- require_relative 'proxy'
8
-
9
- require 'async/http/endpoint'
10
- require 'async/io/shared_endpoint'
11
-
12
- module Falcon
13
- module Service
14
- # Implements an application server using an internal clear-text proxy.
15
- class Application < Proxy
16
- def initialize(environment)
17
- super
18
-
19
- @bound_endpoint = nil
20
- end
21
-
22
- # The middleware that will be served by this application.
23
- # @returns [Protocol::HTTP::Middleware]
24
- def middleware
25
- # In a multi-threaded container, we don't want to modify the shared evaluator's cache, so we create a new evaluator:
26
- @environment.evaluator.middleware
27
- end
28
-
29
- # Number of instances to start.
30
- # @returns [Integer | nil]
31
- def count
32
- @environment.evaluator.count
33
- end
34
-
35
- # Preload any resources specified by the environment.
36
- def preload!
37
- if scripts = @evaluator.preload
38
- scripts.each do |path|
39
- Console.logger.info(self) {"Preloading #{path}..."}
40
- full_path = File.expand_path(path, self.root)
41
- load(full_path)
42
- end
43
- end
44
- end
45
-
46
- # Prepare the bound endpoint for the application instances.
47
- # Invoke {preload!} to load shared resources into the parent process.
48
- def start
49
- Console.logger.info(self) {"Binding to #{self.endpoint}..."}
50
-
51
- @bound_endpoint = Async::Reactor.run do
52
- Async::IO::SharedEndpoint.bound(self.endpoint)
53
- end.wait
54
-
55
- preload!
56
-
57
- super
58
- end
59
-
60
- # Setup instances of the application into the container.
61
- # @parameter container [Async::Container::Generic]
62
- def setup(container)
63
- protocol = self.protocol
64
- scheme = self.scheme
65
-
66
- run_options = {
67
- name: self.name,
68
- restart: true,
69
- }
70
-
71
- run_options[:count] = count unless count.nil?
72
-
73
- container.run(**run_options) do |instance|
74
- Async do |task|
75
- Console.logger.info(self) {"Starting application server for #{self.root}..."}
76
-
77
- server = Server.new(self.middleware, @bound_endpoint, protocol: protocol, scheme: scheme)
78
-
79
- server.run
80
-
81
- instance.ready!
82
-
83
- task.children.each(&:wait)
84
- end
85
- end
86
-
87
- super
88
- end
89
-
90
- # Close the bound endpoint.
91
- def stop
92
- @bound_endpoint&.close
93
- @bound_endpoint = nil
94
-
95
- super
96
- end
97
- end
98
- end
99
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
5
-
6
- module Falcon
7
- module Service
8
- # Captures the stateful behaviour of a specific service.
9
- # Specifies the interfaces required by derived classes.
10
- #
11
- # Designed to be invoked within an {Async::Controller::Container}.
12
- class Generic
13
- # Convert the given environment into a service if possible.
14
- # @parameter environment [Build::Environment] The environment to use to construct the service.
15
- def self.wrap(environment)
16
- evaluator = environment.evaluator
17
- service = evaluator.service || self
18
-
19
- return service.new(environment)
20
- end
21
-
22
- # Initialize the service from the given environment.
23
- # @parameter environment [Build::Environment]
24
- def initialize(environment)
25
- @environment = environment
26
- @evaluator = @environment.evaluator
27
- end
28
-
29
- # Whether the service environment contains the specified keys.
30
- # This is used for matching environment configuration to service behaviour.
31
- def include?(keys)
32
- keys.all?{|key| @environment.include?(key)}
33
- end
34
-
35
- # The name of the service.
36
- # e.g. `myapp.com`.
37
- def name
38
- @evaluator.name
39
- end
40
-
41
- # The logger to use for this service.
42
- # @returns [Console::Logger]
43
- def logger
44
- return Console.logger # .with(name: name)
45
- end
46
-
47
- # Start the service.
48
- def start
49
- end
50
-
51
- # Setup the service into the specified container.
52
- # @parameter container [Async::Container::Generic]
53
- def setup(container)
54
- end
55
-
56
- # Stop the service.
57
- def stop
58
- end
59
- end
60
- end
61
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2020-2023, by Samuel Williams.
5
-
6
- require_relative 'generic'
7
-
8
- require 'async/http/endpoint'
9
- require 'async/io/shared_endpoint'
10
-
11
- module Falcon
12
- module Service
13
- class Proxy < Generic
14
- def name
15
- "#{self.class} for #{self.authority}"
16
- end
17
-
18
- # The host that this proxy will receive connections for.
19
- def authority
20
- @evaluator.authority
21
- end
22
-
23
- # The upstream endpoint that this proxy will connect to.
24
- def endpoint
25
- @evaluator.endpoint
26
- end
27
-
28
- # The {OpenSSL::SSL::SSLContext} that will be used for incoming connections.
29
- def ssl_context
30
- @evaluator.ssl_context
31
- end
32
-
33
- # The root
34
- def root
35
- @evaluator.root
36
- end
37
-
38
- # The protocol this proxy will use to talk to the upstream host.
39
- def protocol
40
- endpoint.protocol
41
- end
42
-
43
- # The scheme this proxy will use to talk to the upstream host.
44
- def scheme
45
- endpoint.scheme
46
- end
47
- end
48
- end
49
- end
@@ -1,82 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Released under the MIT License.
4
- # Copyright, 2019-2023, by Samuel Williams.
5
-
6
- require_relative 'service/generic'
7
-
8
- module Falcon
9
- # Represents one or more services associated with a host.
10
- #
11
- # The services model allows falcon to manage one more more service associated with a given host. Some examples of services include:
12
- #
13
- # - Rack applications wrapped by {Service::Application}.
14
- # - Host supervisor implemented in {Service::Supervisor}.
15
- # - Proxy services wrapped by {Service::Proxy}.
16
- #
17
- # The list of services is typically generated from the user supplied `falcon.rb` configuration file, which is loaded into an immutable {Configuration} instance, which is mapped into a list of services.
18
- class Services
19
- # Initialize the services from the given configuration.
20
- #
21
- # @parameter configuration [Configuration]
22
- def initialize(configuration)
23
- @named = {}
24
-
25
- configuration.each(:service) do |environment|
26
- service = Service::Generic.wrap(environment)
27
-
28
- add(service)
29
- end
30
- end
31
-
32
- # Enumerate all named services.
33
- def each(&block)
34
- @named.each_value(&block)
35
- end
36
-
37
- # Add a named service.
38
- #
39
- # @parameter service [Service]
40
- def add(service)
41
- @named[service.name] = service
42
- end
43
-
44
- # Start all named services.
45
- def start
46
- @named.each do |name, service|
47
- Console.logger.debug(self) {"Starting #{name}..."}
48
- service.start
49
- end
50
- end
51
-
52
- # Setup all named services into the given container.
53
- #
54
- # @parameter container [Async::Container::Generic]
55
- def setup(container)
56
- @named.each do |name, service|
57
- Console.logger.debug(self) {"Setup #{name} into #{container}..."}
58
- service.setup(container)
59
- end
60
-
61
- return container
62
- end
63
-
64
- # Stop all named services.
65
- def stop
66
- failed = false
67
-
68
- @named.each do |name, service|
69
- Console.logger.debug(self) {"Stopping #{name}..."}
70
-
71
- begin
72
- service.stop
73
- rescue => error
74
- failed = true
75
- Console.logger.error(self, error)
76
- end
77
- end
78
-
79
- return failed
80
- end
81
- end
82
- end