# frozen_string_literal: true
# Released under the MIT License.
# Copyright, 2018-2024, by Samuel Williams.
# Copyright, 2018, by Mitsutaka Mimura.
require_relative '../server'
require_relative '../endpoint'
require_relative '../configuration'
require_relative '../service/server'
require_relative '../environment/rackup'
require 'async/container'
require 'async/http/client'
require 'samovar'
module Falcon
module Command
# Implements the `falcon serve` command. Designed for *development*.
#
# Manages a {Controller::Serve} instance which is responsible for running applications in a development environment.
class Serve < Samovar::Command
self.description = "Run an HTTP server for development purposes."
# The command line options.
# @attribute [Samovar::Options]
options do
option '-b/--bind
', "Bind to the given hostname/address.", default: "https://localhost:9292"
option '-p/--port ', "Override the specified port.", type: Integer
option '-h/--hostname ', "Specify the hostname which would be used for certificates, etc."
option '-t/--timeout ', "Specify the maximum time to wait for non-blocking operations.", type: Float, default: nil
option '-c/--config ', "Rackup configuration file to load.", default: 'config.ru'
option '--preload ', "Preload the specified path before creating containers."
option '--cache', "Enable the response cache."
option '--forked | --threaded | --hybrid', "Select a specific parallelism model.", key: :container, default: :forked
option '-n/--count ', "Number of instances to start.", default: Async::Container.processor_count, type: Integer
option '--forks ', "Number of forks (hybrid only).", type: Integer
option '--threads ', "Number of threads (hybrid only).", type: Integer
option '--[no]-restart', "Enable/disable automatic restart.", default: true
option '--graceful-stop ', "Duration to wait for graceful stop.", type: Float, default: 1.0
end
def container_options
@options.slice(:count, :forks, :threads, :restart)
end
def endpoint_options
@options.slice(:hostname, :port, :timeout)
end
def environment
Async::Service::Environment.new(Falcon::Environment::Server).with(
Falcon::Environment::Rackup,
root: Dir.pwd,
verbose: self.parent&.verbose?,
cache: @options[:cache],
container_options: self.container_options,
endpoint_options: self.endpoint_options,
rackup_path: @options[:config],
preload: [@options[:preload]].compact,
url: @options[:bind],
name: "server",
endpoint: ->{Endpoint.parse(url, **endpoint_options)}
)
end
def configuration
Configuration.new.tap do |configuration|
configuration.add(self.environment)
end
end
# The container class to use.
def container_class
case @options[:container]
when :threaded
return Async::Container::Threaded
when :forked
return Async::Container::Forked
when :hybrid
return Async::Container::Hybrid
end
end
# The endpoint to bind to.
def endpoint
Endpoint.parse(@options[:bind], **endpoint_options)
end
# The endpoint suitable for a client to connect.
def client_endpoint
Async::HTTP::Endpoint.parse(@options[:bind], **endpoint_options)
end
# Create a new client suitable for accessing the application.
def client
Async::HTTP::Client.new(client_endpoint)
end
# Prepare the environment and run the controller.
def call
Console.logger.info(self) do |buffer|
buffer.puts "Falcon v#{VERSION} taking flight! Using #{self.container_class} #{self.container_options}."
buffer.puts "- Binding to: #{self.endpoint}"
buffer.puts "- To terminate: Ctrl-C or kill #{Process.pid}"
buffer.puts "- To reload configuration: kill -HUP #{Process.pid}"
end
Async::Service::Controller.run(self.configuration, container_class: self.container_class, graceful_stop: @options[:graceful_stop])
end
end
end
end