diff --git a/examples/hello/config.ru b/examples/hello/config.ru index a82a4bc6..8abcc1ec 100755 --- a/examples/hello/config.ru +++ b/examples/hello/config.ru @@ -3,7 +3,7 @@ require 'async' -Console.logger.debug! +# Console.logger.debug! class RequestLogger def initialize(app) diff --git a/examples/hello/preload.rb b/examples/hello/preload.rb index dda339a8..f1d7ce07 100644 --- a/examples/hello/preload.rb +++ b/examples/hello/preload.rb @@ -3,4 +3,4 @@ # Released under the MIT License. # Copyright, 2020-2023, by Samuel Williams. -$stderr.puts "Preloading..." +# $stderr.puts "Preloading..." diff --git a/lib/falcon/command/proxy.rb b/lib/falcon/command/proxy.rb index 749c9919..407c7f61 100644 --- a/lib/falcon/command/proxy.rb +++ b/lib/falcon/command/proxy.rb @@ -31,19 +31,37 @@ class Proxy < Samovar::Command include Paths - def environment + def environment(**options) Async::Service::Environment.new(Falcon::Service::Proxy::Environment).with( root: Dir.pwd, verbose: self.parent&.verbose?, - name: "proxy", - url: @options[:bind], + timeout: @options[:timeout], + **options ) end + def host_map(environments) + hosts = {} + + environments.each do |environment| + next unless environment.implements?(Falcon::Service::Application::Environment) + evaluator = environment.evaluator + hosts[evaluator.authority] = evaluator + end + + Console.info(self) {"Hosts: #{hosts}"} + + return hosts + end + def configuration + configuration = super + hosts = host_map(configuration.environments) + Configuration.new.tap do |configuration| - configuration.add(self.environment) + environment = self.environment(hosts: hosts) + configuration.add(environment) end end @@ -59,6 +77,10 @@ def call buffer.puts "- Binding to: #{@options[:bind]}" buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}" buffer.puts "- To reload: kill -HUP #{Process.pid}" + + self.resolved_paths.each do |path| + buffer.puts "- Loading configuration from #{path}" + end end Async::Service::Controller.run(self.configuration, container_class: self.container_class) diff --git a/lib/falcon/command/redirect.rb b/lib/falcon/command/redirect.rb index 5047671a..9bbded93 100644 --- a/lib/falcon/command/redirect.rb +++ b/lib/falcon/command/redirect.rb @@ -29,19 +29,38 @@ class Redirect < Samovar::Command include Paths - def environment - Async::Service::Environment.new(Falcon::Service::Proxy::Environment).with( + def environment(**options) + Async::Service::Environment.new(Falcon::Service::Redirect::Environment).with( root: Dir.pwd, verbose: self.parent&.verbose?, - name: "proxy", - url: @options[:bind], + redirect_url: @options[:redirect], + timeout: @options[:timeout], + **options ) end + def host_map(environments) + hosts = {} + + environments.each do |environment| + next unless environment.implements?(Falcon::Service::Application::Environment) + evaluator = environment.evaluator + hosts[evaluator.authority] = evaluator + end + + Console.info(self) {"Hosts: #{hosts}"} + + return hosts + end + def configuration + configuration = super + hosts = host_map(configuration.environments) + Configuration.new.tap do |configuration| - configuration.add(self.environment) + environment = self.environment(hosts: hosts) + configuration.add(environment) end end @@ -57,6 +76,10 @@ def call buffer.puts "- Binding to: #{@options[:bind]}" buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}" buffer.puts "- To reload: kill -HUP #{Process.pid}" + + self.resolved_paths.each do |path| + buffer.puts "- Loading configuration from #{path}" + end end Async::Service::Controller.run(self.configuration, container_class: self.container_class) diff --git a/lib/falcon/command/virtual.rb b/lib/falcon/command/virtual.rb index 3d56a25f..1f16d1c4 100644 --- a/lib/falcon/command/virtual.rb +++ b/lib/falcon/command/virtual.rb @@ -56,6 +56,26 @@ def call Async::Service::Controller.run(self.configuration) end + + # The insecure endpoint for connecting to the {Redirect} instance. + def insecure_endpoint(**options) + Async::HTTP::Endpoint.parse(@options[:bind_insecure], **options) + end + + # The secure endpoint for connecting to the {Proxy} instance. + def secure_endpoint(**options) + Async::HTTP::Endpoint.parse(@options[:bind_secure], **options) + end + + # An endpoint suitable for connecting to the specified hostname. + def host_endpoint(hostname, **options) + endpoint = secure_endpoint(**options) + + url = URI.parse(@options[:bind_secure]) + url.hostname = hostname + + return Async::HTTP::Endpoint.new(url, hostname: endpoint.hostname) + end end end end diff --git a/lib/falcon/service/application.rb b/lib/falcon/service/application.rb index 2af574c5..892b9e14 100644 --- a/lib/falcon/service/application.rb +++ b/lib/falcon/service/application.rb @@ -66,66 +66,6 @@ def count nil end end - - def initialize(...) - super - - @bound_endpoint = nil - end - - # Prepare the bound endpoint for the application instances. - # Invoke {preload!} to load shared resources into the parent process. - def start - endpoint = @evaluator.endpoint - - Console.logger.info(self) {"Binding to #{endpoint}..."} - - @bound_endpoint = Async::Reactor.run do - Async::IO::SharedEndpoint.bound(endpoint) - end.wait - - preload! - - super - end - - # Setup instances of the application into the container. - # @parameter container [Async::Container::Generic] - def setup(container) - protocol = self.protocol - scheme = self.scheme - - run_options = { - name: self.name, - restart: true, - } - - run_options[:count] = count unless count.nil? - - container.run(**run_options) do |instance| - Async do |task| - Console.logger.info(self) {"Starting application server for #{self.root}..."} - - server = Server.new(self.middleware, @bound_endpoint, protocol: protocol, scheme: scheme) - - server.run - - instance.ready! - - task.children.each(&:wait) - end - end - - super - end - - # Close the bound endpoint. - def stop - @bound_endpoint&.close - @bound_endpoint = nil - - super - end end end end diff --git a/lib/falcon/service/proxy.rb b/lib/falcon/service/proxy.rb index 9b4b05fc..8b1b56a1 100644 --- a/lib/falcon/service/proxy.rb +++ b/lib/falcon/service/proxy.rb @@ -22,6 +22,10 @@ def service_class Proxy end + def name + service_class.name + end + # The host that this proxy will receive connections for. def url "https://[::]:443" @@ -63,14 +67,16 @@ def hosts # @parameter socket [OpenSSL::SSL::SSLSocket] The incoming connection. # @parameter hostname [String] The negotiated hostname. def host_context(socket, hostname) - if host = self.hosts[hostname] + hosts = self.hosts + + if host = hosts[hostname] Console.logger.debug(self) {"Resolving #{hostname} -> #{host}"} socket.hostname = hostname return host.ssl_context else - Console.logger.warn(self) {"Unable to resolve #{hostname}!"} + Console.logger.warn(self, hosts: hosts.keys) {"Unable to resolve #{hostname}!"} return nil end diff --git a/lib/falcon/service/redirect.rb b/lib/falcon/service/redirect.rb index 0b631285..1a3f6167 100644 --- a/lib/falcon/service/redirect.rb +++ b/lib/falcon/service/redirect.rb @@ -33,12 +33,6 @@ def middleware Middleware::Redirect.new(Middleware::NotFound, hosts, redirect_endpoint) end end - - # services.each do |service| - # if service.is_a?(Service::Proxy) - # @hosts[service.authority] = service - # end - # end end end end diff --git a/lib/falcon/service/server.rb b/lib/falcon/service/server.rb index 5f2ea7c5..0b6079ae 100644 --- a/lib/falcon/service/server.rb +++ b/lib/falcon/service/server.rb @@ -18,6 +18,10 @@ def service_class Server end + def name + "#{service_class.name} (#{url})" + end + # Options to use when creating the container. def container_options {restart: true} @@ -28,11 +32,16 @@ def url "http://[::]:9292" end + def timeout + nil + end + # The upstream endpoint that will handle incoming requests. # @returns [Async::HTTP::Endpoint] def endpoint ::Async::HTTP::Endpoint.parse(url).with( reuse_address: true, + timeout: timeout, ) end @@ -81,15 +90,15 @@ def preload! # Prepare the bound endpoint for the server. def start - endpoint = @evaluator.endpoint + @endpoint = @evaluator.endpoint Sync do - @bound_endpoint = endpoint.bound + @bound_endpoint = @endpoint.bound end preload! - Console.logger.info(self) {"Starting #{name} on #{@endpoint.to_url}"} + Console.logger.info(self) {"Starting #{name} on #{@endpoint}"} super end @@ -121,6 +130,8 @@ def stop(...) @bound_endpoint = nil end + @endpoint = nil + super end end diff --git a/lib/falcon/service/virtual.rb b/lib/falcon/service/virtual.rb index 7268b552..2b499ccd 100644 --- a/lib/falcon/service/virtual.rb +++ b/lib/falcon/service/virtual.rb @@ -19,6 +19,10 @@ def service_class Virtual end + def name + service_class.name + end + # All the falcon application configuration paths. # @returns [Array(String)] Paths to the falcon application configuration files. def configuration_paths diff --git a/test/falcon/command/virtual.rb b/test/falcon/command/virtual.rb index 06ba80dc..9b5f8a0f 100644 --- a/test/falcon/command/virtual.rb +++ b/test/falcon/command/virtual.rb @@ -48,7 +48,7 @@ def around Async do response = insecure_client.call(request) - expect(response).to be_redirection + expect(response).to be(:redirection?) expect(response.headers['location']).to be == "https://hello.localhost:8443/index" response.close