Spawning of Rack applications.
The following options are accepted:
'app_root'
See PhusionPassenger::SpawnManager#spawn_application for information about the options.
# File lib/phusion_passenger/rack/application_spawner.rb, line 95 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
Spawn an instance of the given Rack application. When successful, an AppProcess object will be returned, which represents the spawned application.
Accepts the same options as PhusionPassenger::SpawnManager#spawn_application.
Raises:
AppInitError: The Rack application raised an exception or called exit() during startup.
SystemCallError, IOError, SocketError: Something went wrong.
# File lib/phusion_passenger/rack/application_spawner.rb, line 59 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
# File lib/phusion_passenger/rack/application_spawner.rb, line 215 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
# File lib/phusion_passenger/rack/application_spawner.rb, line 190 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
Spawns an instance of the Rack application. When successful, an AppProcess object will be returned, which represents the spawned Rack application.
options
will be passed to the request handler's
constructor.
Raises:
AbstractServer::ServerNotStarted: The ApplicationSpawner server hasn't already been started.
ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.
# File lib/phusion_passenger/rack/application_spawner.rb, line 112 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
Overrided from PhusionPassenger::AbstractServer#start.
May raise these additional exceptions:
AppInitError: The Rack application raised an exception or called exit() during startup.
ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.
# File lib/phusion_passenger/rack/application_spawner.rb, line 127 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
# File lib/phusion_passenger/rack/application_spawner.rb, line 162 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