Parent

Class/Module Index [+]

Quicksearch

PhusionPassenger::RequestHandler

Constants

BACKLOG_SIZE
HARD_TERMINATION_SIGNAL

Signal which will cause the application to exit immediately.

Attributes

concurrency[R]
connect_password[RW]

A password with which clients must authenticate. Default is unauthenticated.

server_sockets[R]

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.

Public Class Methods

new(owner_pipe, options = {}) click to toggle source

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:

# File lib/phusion_passenger/request_handler.rb, line 76
def initialize(owner_pipe, options = {})
  require_option(options, "app_group_name")
  install_options_as_ivars(self, options,
    "app",
    "app_group_name",
    "connect_password",
    "union_station_core"
  )

  @keepalive = options.fetch("keepalive", true).to_s == "true"
  @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

  #############
  #############

  @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
  @main_loop_running  = false

  #############
end

Public Instance Methods

cleanup() click to toggle source

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 138
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])

    begin
      socket.close if !socket.closed?
    rescue Exception => e
      # Ignore "stream closed" error, which occurs in some unit tests.
      # We catch Exception here instead of IOError because of a Ruby 1.8.7 bug.
      if e.to_s !~ /stream closed/ && e.message.to_s !~ /stream closed/
        raise e
      end
    end
    if type == :unix
      filename = info[:address].sub(/^unix:/, '')
      File.unlink(filename) rescue nil
    end
  end
  @owner_pipe.close rescue nil
end
main_loop() click to toggle source

Enter the request handler's main loop.

# File lib/phusion_passenger/request_handler.rb, line 174
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
      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
main_loop_running?() click to toggle source

Check whether the main loop's currently running.

# File lib/phusion_passenger/request_handler.rb, line 167
def main_loop_running?
  @main_loop_thread_lock.synchronize do
    return @main_loop_running
  end
end
start_main_loop_thread() click to toggle source

Start the main loop in a new thread. This thread will be stopped by cleanup.

# File lib/phusion_passenger/request_handler.rb, line 234
def start_main_loop_thread
  current_generation = @main_loop_generation
  @main_loop_thread = create_thread_and_abort_on_exception do
    main_loop
  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

[Validate]

Generated with the Darkfish Rdoc Generator 2.