class PhusionPassenger::Rack::ApplicationSpawner
Spawning of Rack applications.
def self.load_rack_app
def self.load_rack_app # Load Rack inside the spawned child process so that the spawn manager # itself doesn't preload Rack. This is necessary because some broken # Rails apps explicitly specify a Rack version as dependency. require 'rack' rackup_file = ENV["RACKUP_FILE"] || "config.ru" rackup_code = ::File.open(rackup_file, 'rb') do |f| f.read end eval("Rack::Builder.new {( #{rackup_code}\n )}.to_app", TOPLEVEL_BINDING, rackup_file) end
def self.spawn_application(options = {})
exit() during startup.
- AppInitError: The Rack application raised an exception or called
Raises:
Accepts the same options as SpawnManager#spawn_application.
application.
AppProcess object will be returned, which represents the spawned
Spawn an instance of the given Rack application. When successful, an
def self.spawn_application(options = {}) options = sanitize_spawn_options(options) a, b = UNIXSocket.pair pid = safe_fork(self.class.to_s, true) do a.close file_descriptors_to_leave_open = [0, 1, 2, b.fileno] NativeSupport.close_all_file_descriptors(file_descriptors_to_leave_open) close_all_io_objects_for_fds(file_descriptors_to_leave_open) channel = MessageChannel.new(b) app = nil success = report_app_init_status(channel) do prepare_app_process('config.ru', options) app = load_rack_app after_loading_app_code(options) end if success start_request_handler(channel, app, false, options) end end b.close Process.waitpid(pid) rescue nil channel = MessageChannel.new(a) unmarshal_and_raise_errors(channel, options["print_exceptions"], "rack") # No exception was raised, so spawning succeeded. return AppProcess.read_from_channel(channel) end
def self.start_request_handler(channel, app, forked, options)
def self.start_request_handler(channel, app, forked, options) app_root = options["app_root"] $0 = "Rack: #{options['app_group_name']}" reader, writer = IO.pipe begin reader.close_on_exec! handler = RequestHandler.new(reader, app, options) app_process = AppProcess.new(app_root, Process.pid, writer, handler.server_sockets) app_process.write_to_channel(channel) writer.close channel.close before_handling_requests(forked, options) handler.main_loop ensure channel.close rescue nil writer.close rescue nil handler.cleanup rescue nil after_handling_requests end end
def before_fork # :nodoc:
Overrided method.
def before_fork # :nodoc: if GC.copy_on_write_friendly? # Garbage collect now so that the child process doesn't have to # do that (to prevent making pages dirty). GC.start end end
def handle_spawn_application(client, *options)
def handle_spawn_application(client, *options) options = sanitize_spawn_options(Hash[*options]) a, b = UNIXSocket.pair safe_fork('application', true) do begin a.close client.close options = @options.merge(options) self.class.send(:start_request_handler, MessageChannel.new(b), @app, true, options) rescue SignalException => e if e.message != AbstractRequestHandler::HARD_TERMINATION_SIGNAL && e.message != AbstractRequestHandler::SOFT_TERMINATION_SIGNAL raise end end end b.close worker_channel = MessageChannel.new(a) app_process = AppProcess.read_from_channel(worker_channel) app_process.write_to_channel(client) ensure a.close if a b.close if b && !b.closed? app_process.close if app_process end
def initialize(options)
- 'app_root'
The following options are accepted:
def initialize(options) super() @options = sanitize_spawn_options(options) @app_root = @options["app_root"] @canonicalized_app_root = canonicalize_path(@app_root) self.max_idle_time = DEFAULT_APP_SPAWNER_MAX_IDLE_TIME define_message_handler(:spawn_application, :handle_spawn_application) end
def initialize_server # :nodoc:
Overrided method.
def initialize_server # :nodoc: report_app_init_status(MessageChannel.new(@owner_socket)) do $0 = "Passenger ApplicationSpawner: #{@options['app_group_name']}" prepare_app_process('config.ru', @options) @app = self.class.send(:load_rack_app) after_loading_app_code(@options) end end
def spawn_application(options = {})
- AbstractServer::ServerNotStarted: The ApplicationSpawner server hasn't already been started.
Raises:
+options+ will be passed to the request handler's constructor.
will be returned, which represents the spawned Rack application.
Spawns an instance of the Rack application. When successful, an AppProcess object
def spawn_application(options = {}) connect do |channel| channel.write("spawn_application", *options.to_a.flatten) return AppProcess.read_from_channel(channel) end rescue SystemCallError, IOError, SocketError => e raise Error, "The application spawner server exited unexpectedly: #{e}" end
def start
or called exit() during startup.
- AppInitError: The Rack application raised an exception
May raise these additional exceptions:
Overrided from AbstractServer#start.
def start super begin channel = MessageChannel.new(@owner_socket) unmarshal_and_raise_errors(channel, @options["print_exceptions"]) rescue IOError, SystemCallError, SocketError => e stop if started? raise Error, "The application spawner server exited unexpectedly: #{e}" rescue stop if started? raise end end