diff --git a/lib/falcon/command/proxy.rb b/lib/falcon/command/proxy.rb
new file mode 100644
index 00000000..749c9919
--- /dev/null
+++ b/lib/falcon/command/proxy.rb
@@ -0,0 +1,73 @@
+# frozen_string_literal: true
+
+# Released under the MIT License.
+# Copyright, 2020-2023, by Samuel Williams.
+
+require_relative '../service/proxy'
+require_relative 'paths'
+
+require 'samovar'
+
+module Falcon
+ module Command
+ # Implements the `falcon proxy` command.
+ #
+ # Manages a {Controller::Proxy} instance which is responsible for proxing incoming requests.
+ class Proxy < Samovar::Command
+ self.description = "Proxy to one or more backend hosts."
+
+ # The command line options.
+ # @attribute [Samovar::Options]
+ options do
+ option '--bind
', "Bind to the given hostname/address", default: "https://[::]:443"
+
+ option '-t/--timeout ', "Specify the maximum time to wait for non-blocking operations.", type: Float, default: nil
+ end
+
+ # One or more paths to the configuration files.
+ # @name paths
+ # @attribute [Array(String)]
+ many :paths
+
+ include Paths
+
+ def environment
+ Async::Service::Environment.new(Falcon::Service::Proxy::Environment).with(
+ root: Dir.pwd,
+ verbose: self.parent&.verbose?,
+ name: "proxy",
+
+ url: @options[:bind],
+ )
+ end
+
+ def configuration
+ Configuration.new.tap do |configuration|
+ configuration.add(self.environment)
+ end
+ end
+
+ # The container class to use.
+ def container_class
+ Async::Container.best_container_class
+ end
+
+ # Prepare the environment and run the controller.
+ def call
+ Console.logger.info(self) do |buffer|
+ buffer.puts "Falcon Proxy v#{VERSION} taking flight!"
+ buffer.puts "- Binding to: #{@options[:bind]}"
+ buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
+ buffer.puts "- To reload: kill -HUP #{Process.pid}"
+ end
+
+ Async::Service::Controller.run(self.configuration, container_class: self.container_class)
+ end
+
+ # The endpoint to bind to.
+ def endpoint(**options)
+ Async::HTTP::Endpoint.parse(@options[:bind], timeout: @options[:timeout], **options)
+ end
+ end
+ end
+end
diff --git a/lib/falcon/command/redirect.rb b/lib/falcon/command/redirect.rb
new file mode 100644
index 00000000..5047671a
--- /dev/null
+++ b/lib/falcon/command/redirect.rb
@@ -0,0 +1,76 @@
+# frozen_string_literal: true
+
+# Released under the MIT License.
+# Copyright, 2020-2023, by Samuel Williams.
+
+require_relative '../service/redirect'
+require_relative 'paths'
+
+require 'samovar'
+
+module Falcon
+ module Command
+ class Redirect < Samovar::Command
+ self.description = "Redirect from insecure HTTP to secure HTTP."
+
+ # The command line options.
+ # @attribute [Samovar::Options]
+ options do
+ option '--bind ', "Bind to the given hostname/address", default: "http://[::]:80"
+ option '--redirect ', "Redirect using this address as a template.", default: "https://[::]:443"
+
+ option '-t/--timeout ', "Specify the maximum time to wait for non-blocking operations.", type: Float, default: nil
+ end
+
+ # One or more paths to the configuration files.
+ # @name paths
+ # @attribute [Array(String)]
+ many :paths
+
+ include Paths
+
+ def environment
+ Async::Service::Environment.new(Falcon::Service::Proxy::Environment).with(
+ root: Dir.pwd,
+ verbose: self.parent&.verbose?,
+ name: "proxy",
+
+ url: @options[:bind],
+ )
+ end
+
+ def configuration
+ Configuration.new.tap do |configuration|
+ configuration.add(self.environment)
+ end
+ end
+
+ # The container class to use.
+ def container_class
+ Async::Container.best_container_class
+ end
+
+ # Prepare the environment and run the controller.
+ def call
+ Console.logger.info(self) do |buffer|
+ buffer.puts "Falcon Redirect v#{VERSION} taking flight!"
+ buffer.puts "- Binding to: #{@options[:bind]}"
+ buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
+ buffer.puts "- To reload: kill -HUP #{Process.pid}"
+ end
+
+ Async::Service::Controller.run(self.configuration, container_class: self.container_class)
+ end
+
+ # The endpoint to bind to.
+ def endpoint(**options)
+ Async::HTTP::Endpoint.parse(@options[:bind], timeout: @options[:timeout], **options)
+ end
+
+ # The template endpoint to redirect to.
+ def redirect_endpoint(**options)
+ Async::HTTP::Endpoint.parse(@options[:redirect], **options)
+ end
+ end
+ end
+end
diff --git a/lib/falcon/command/serve.rb b/lib/falcon/command/serve.rb
index 2c71ffdb..4918d0c6 100644
--- a/lib/falcon/command/serve.rb
+++ b/lib/falcon/command/serve.rb
@@ -62,11 +62,11 @@ def environment
rackup_path: @options[:config],
preload: [@options[:preload]].compact,
- bind: @options[:bind],
+ url: @options[:bind],
name: "server",
- endpoint: ->{Endpoint.parse(bind, **endpoint_options)}
+ endpoint: ->{Endpoint.parse(url, **endpoint_options)}
)
end
diff --git a/test/falcon/command/top.rb b/test/falcon/command/top.rb
index d7de5ba5..2127c971 100644
--- a/test/falcon/command/top.rb
+++ b/test/falcon/command/top.rb
@@ -16,7 +16,7 @@
]
serve = top.command
- controller = Async::Service::Controller.new(serve.configuration)
+ controller = Async::Service::Controller.new(serve.configuration.services.to_a)
controller.start
Async do