class PhusionPassenger::RequestHandler
Constants
Attributes
A password with which clients must authenticate. Default is unauthenticated.
A hash containing all server sockets that this request handler listens on. The hash is in the form of:
{ name1 => [socket_address1, socket_type1, socket1], name2 => [socket_address2, socket_type2, socket2], ... }
name
is a Symbol. socket_addressx
is the address
of the socket, socket_typex
is the socket's type (either
'unix' or 'tcp') and socketx
is the actual
socket IO objec. There's guaranteed to be at
least one server socket, namely one with the name :main
.
If a soft termination signal was received, then the main loop will quit the given amount of seconds after the last time a connection was accepted. Defaults to 3 seconds.
Public Class Methods
Create a new RequestHandler with the
given owner pipe. owner_pipe
must be the readable part of a
pipe IO object.
Additionally, the following options may be given:
-
detach_key
-
pool_account_username
-
pool_account_password_base64
# File lib/phusion_passenger/request_handler.rb, line 87 def initialize(owner_pipe, options = {}) require_option(options, "app_group_name") install_options_as_ivars(self, options, "app", "app_group_name", "connect_password", "detach_key", "union_station_core", "pool_account_username" ) @force_http_session = ENV["_PASSENGER_FORCE_HTTP_SESSION"] == "true" if @force_http_session @connect_password = nil end @thread_handler = options["thread_handler"] || ThreadHandler @concurrency = 1 if options["pool_account_password_base64"] @pool_account_password = options["pool_account_password_base64"].unpack('m').first end ############# ############# @server_sockets = {} if should_use_unix_sockets? @main_socket_address, @main_socket = create_unix_socket_on_filesystem(options) else @main_socket_address, @main_socket = create_tcp_socket end @server_sockets[:main] = { :address => @main_socket_address, :socket => @main_socket, :protocol => @force_http_session ? :http_session : :session, :concurrency => @concurrency } @http_socket_address, @http_socket = create_tcp_socket @server_sockets[:http] = { :address => @http_socket_address, :socket => @http_socket, :protocol => :http, :concurrency => 1 } @owner_pipe = owner_pipe @options = options @previous_signal_handlers = {} @main_loop_generation = 0 @main_loop_thread_lock = Mutex.new @main_loop_thread_cond = ConditionVariable.new @threads = [] @threads_mutex = Mutex.new @soft_termination_linger_time = 3 @main_loop_running = false ############# end
Public Instance Methods
Clean up temporary stuff created by the request handler.
If the main loop was started by main_loop, then this method may only be called after the main loop has exited.
If the main loop was started by start_main_loop_thread, then this method may be called at any time, and it will stop the main loop thread.
# File lib/phusion_passenger/request_handler.rb, line 154 def cleanup if @main_loop_thread @main_loop_thread_lock.synchronize do @graceful_termination_pipe[1].close rescue nil end @main_loop_thread.join end @server_sockets.each_value do |info| socket = info[:socket] type = get_socket_address_type(info[:address]) socket.close if !socket.closed? if type == :unix filename = info[:address].sub(/^unix:/, '') File.unlink(filename) rescue nil end end @owner_pipe.close rescue nil end
Enter the request handler's main loop.
# File lib/phusion_passenger/request_handler.rb, line 182 def main_loop debug("Entering request handler main loop") reset_signal_handlers begin @graceful_termination_pipe = IO.pipe @graceful_termination_pipe[0].close_on_exec! @graceful_termination_pipe[1].close_on_exec! @main_loop_thread_lock.synchronize do @main_loop_generation += 1 @main_loop_running = true @main_loop_thread_cond.broadcast @select_timeout = nil @selectable_sockets = [] @server_sockets.each_value do |value| socket = value[2] @selectable_sockets << socket if socket end @selectable_sockets << @owner_pipe @selectable_sockets << @graceful_termination_pipe[0] end install_useful_signal_handlers start_threads wait_until_termination_requested wait_until_all_threads_are_idle terminate_threads debug("Request handler main loop exited normally") rescue EOFError # Exit main loop. trace(2, "Request handler main loop interrupted by EOFError exception") rescue Interrupt # Exit main loop. trace(2, "Request handler main loop interrupted by Interrupt exception") rescue SignalException => signal trace(2, "Request handler main loop interrupted by SignalException") if signal.message != HARD_TERMINATION_SIGNAL && signal.message != SOFT_TERMINATION_SIGNAL raise end rescue Exception => e trace(2, "Request handler main loop interrupted by #{e.class} exception") raise ensure debug("Exiting request handler main loop") revert_signal_handlers @main_loop_thread_lock.synchronize do @graceful_termination_pipe[1].close rescue nil @graceful_termination_pipe[0].close rescue nil @selectable_sockets = [] @main_loop_generation += 1 @main_loop_running = false @main_loop_thread_cond.broadcast end end end
Check whether the main loop's currently running.
# File lib/phusion_passenger/request_handler.rb, line 175 def main_loop_running? @main_loop_thread_lock.synchronize do return @main_loop_running end end
Remove this request handler from the application pool so that no new connections will come in. Then make the main loop quit a few seconds after the last time a connection came in. This all is to ensure that no connections come in while we're shutting down.
May only be called while the main loop is running. May be called from any thread.
# File lib/phusion_passenger/request_handler.rb, line 266 def soft_shutdown @soft_termination_linger_thread ||= Thread.new do debug("Soft termination initiated") if @detach_key && @pool_account_username && @pool_account_password client = MessageClient.new(@pool_account_username, @pool_account_password) begin client.pool_detach_process_by_key(@detach_key) ensure client.close end end wait_until_all_threads_are_idle debug("Soft terminating in #{@soft_termination_linger_time} seconds") sleep @soft_termination_linger_time @graceful_termination_pipe[1].close rescue nil end end
Start the main loop in a new thread. This thread will be stopped by cleanup.
# File lib/phusion_passenger/request_handler.rb, line 243 def start_main_loop_thread current_generation = @main_loop_generation @main_loop_thread = Thread.new do begin main_loop rescue Exception => e print_exception(self.class, e) end end @main_loop_thread_lock.synchronize do while @main_loop_generation == current_generation @main_loop_thread_cond.wait(@main_loop_thread_lock) end end end