class PhusionPassenger::AbstractRequestHandler

and sends it to the request handler.
The web server transforms the HTTP request to the aforementioned format,
NUL = “x00”
notnull ::= “x01” | “x02” | “x02” | … | “xFF”
value ::= notnull+
name ::= notnull+
header ::= name NUL value NUL
headers ::= header*
grammar:
HTTP headers are transformed to a format that satisfies the following
- The verbatim (untransformed) HTTP request body.
- The transformed HTTP headers.
headers.
- A 32-bit big-endian integer, containing the size of the transformed
The format consists of 3 parts:
is based on CGI, and is similar to that of SCGI.
representation do not conform to RFC 2616. Instead, the request format
Incoming “HTTP requests” are not true HTTP requests, i.e. their binary
== Request format
server’s using ApplicationPoolServer instead of StandardApplicationPool.
* It might also be passed to the ApplicationPoolServerExecutable, if the web
as well. This works even if the web server gets killed by SIGKILL.
knows that the web server has exited, and so the request handler will exit
check whether the other side of the pipe has been closed. If so, then it
the readable part of the pipe. AbstractRequestHandler will continuously
server will own that part of the pipe, while AbstractRequestHandler owns
of the pipe will be passed to the web server* via a Unix socket, and the web
This is implemented by using a so-called _owner pipe_. The writable part
we want the request handler to exit if the web server has also exited.
Because only the web server communicates directly with a request handler,
=== Owner pipes
in this section.
administrator maintenance overhead. These decisions are documented
Some design decisions are made because we want to decrease system
== Design decisions
of request handlers for Rails and Rack.
AbstractRequestHandler is an abstract base class for easing the implementation
response back to the HTTP client.
RoR application are forwarded to the web server, which, in turn, sends the
to the request handler by the web server. HTTP responses generated by the
currently loaded Ruby on Rails application. HTTP requests are forwarded
The request handler’s job is to process incoming HTTP requests using the
request dispatcher (i.e. either Rails’s Dispatcher class or Rack).
The request handler is the layer which connects Apache with the underlying application’s

def self.determine_passenger_header

def self.determine_passenger_header
	header = "Phusion Passenger (mod_rails/mod_rack) #{VERSION_STRING}"
	if File.exist?("#{File.dirname(__FILE__)}/../../enterprisey.txt") ||
	   File.exist?("/etc/passenger_enterprisey.txt")
		header << ", Enterprise Edition"
	end
	return header
end

def accept_connection

def accept_connection
	ios = select([@socket, @owner_pipe, @graceful_termination_pipe[0]]).first
	if ios.include?(@socket)
		client = @socket.accept
		client.close_on_exec!
		
		# Some people report that sometimes their Ruby (MRI/REE)
		# processes get stuck with 100% CPU usage. Upon further
		# inspection with strace, it turns out that these Ruby
		# processes are continuously calling lseek() on a socket,
		# which of course returns ESPIPE as error. gdb reveals
		# lseek() is called by fwrite(), which in turn is called
		# by rb_fwrite(). The affected socket is the
		# AbstractRequestHandler client socket.
		#
		# I inspected the MRI source code and didn't find
		# anything that would explain this behavior. This makes
		# me think that it's a glibc bug, but that's very
		# unlikely.
		#
		# The rb_fwrite() implementation takes an entirely
		# different code path if I set 'sync' to true: it will
		# skip fwrite() and use write() instead. So here we set
		# 'sync' to true in the hope that this will work around
		# the problem.
		client.sync = true
		
		# We monkeypatch the 'sync=' method to a no-op so that
		# sync mode can't be disabled.
		def client.sync=(value)
		end
		
		# The real input stream is not seekable (calling _seek_
		# or _rewind_ on it will raise an exception). But some
		# frameworks (e.g. Merb) call _rewind_ if the object
		# responds to it. So we simply undefine _seek_ and
		# _rewind_.
		client.instance_eval do
			undef seek if respond_to?(:seek)
			undef rewind if respond_to?(:rewind)
		end
		
		return client
	else
		# The other end of the owner pipe has been closed, or the
		# graceful termination pipe has been closed. This is our
		# call to gracefully terminate (after having processed all
		# incoming requests).
		return nil
	end
end

def cleanup

may be called at any time, and it will stop the main loop thread.
If the main loop was started by #start_main_loop_thread, then this method

be called after the main loop has exited.
If the main loop was started by #main_loop, then this method may only

Clean up temporary stuff created by the request handler.
def cleanup
	if @main_loop_thread
		@main_loop_thread.raise(Interrupt.new("Cleaning up"))
		@main_loop_thread.join
	end
	@socket.close rescue nil
	@owner_pipe.close rescue nil
	File.unlink(@socket_name) rescue nil
end

def create_tcp_socket

def create_tcp_socket
	# We use "127.0.0.1" as address in order to force
	# TCPv4 instead of TCPv6.
	@socket = TCPServer.new('127.0.0.1', 0)
	@socket.listen(BACKLOG_SIZE)
	@socket_name = "127.0.0.1:#{@socket.addr[1]}"
	@socket_type = "tcp"
end

def create_unix_socket_on_filesystem

def create_unix_socket_on_filesystem
	done = false
	while !done
		begin
			if defined?(NativeSupport)
				unix_path_max = NativeSupport::UNIX_PATH_MAX
			else
				unix_path_max = 100
			end
			@socket_name = "#{passenger_tmpdir}/backends/backend.#{generate_random_id(:base64)}"
			@socket_name = @socket_name.slice(0, unix_path_max - 1)
			@socket = UNIXServer.new(@socket_name)
			@socket.listen(BACKLOG_SIZE)
			@socket_type = "unix"
			File.chmod(0600, @socket_name)
			
			# The SpawnManager class will set tighter permissions on the
			# socket later on. See sendSpawnCommand in SpawnManager.h.
			
			done = true
		rescue Errno::EADDRINUSE
			# Do nothing, try again with another name.
		end
	end
end

def generate_random_id(method)

is also a valid filename.
Generate a long, cryptographically secure random ID string, which
def generate_random_id(method)
	case method
	when :base64
		require 'base64' unless defined?(Base64)
		data = Base64.encode64(File.read("/dev/urandom", 64))
		data.gsub!("\n", '')
		data.gsub!("+", '')
		data.gsub!("/", '')
		data.gsub!(/==$/, '')
	when :hex
		data = File.read("/dev/urandom", 64).unpack('H*')[0]
	end
	return data
end

def initialize(owner_pipe, options = {})

- memory_limit: Used to set the +memory_limit+ attribute.
Additionally, the following options may be given:

+owner_pipe+ must be the readable part of a pipe IO object.
Create a new RequestHandler with the given owner pipe.
def initialize(owner_pipe, options = {})
	if should_use_unix_sockets?
		create_unix_socket_on_filesystem
	else
		create_tcp_socket
	end
	@socket.close_on_exec!
	@owner_pipe = owner_pipe
	@previous_signal_handlers = {}
	@main_loop_thread_lock = Mutex.new
	@main_loop_thread_cond = ConditionVariable.new
	@memory_limit = options["memory_limit"] || 0
	@iterations = 0
	@processed_requests = 0
end

def install_useful_signal_handlers

def install_useful_signal_handlers
	trappable_signals = Signal.list_trappable
	
	trap(SOFT_TERMINATION_SIGNAL) do
		@graceful_termination_pipe[1].close rescue nil
	end if trappable_signals.has_key?(SOFT_TERMINATION_SIGNAL.sub(/^SIG/, ''))
	
	trap('ABRT') do
		raise SignalException, "SIGABRT"
	end if trappable_signals.has_key?('ABRT')
	
	trap('QUIT') do
		if Kernel.respond_to?(:caller_for_all_threads)
			output = "========== Process #{Process.pid}: backtrace dump ==========\n"
			caller_for_all_threads.each_pair do |thread, stack|
				output << ("-" * 60) << "\n"
				output << "# Thread: #{thread.inspect}, "
				if thread == Thread.main
					output << "[main thread], "
				else
					output << "[current thread], "
				end
				output << "alive = #{thread.alive?}\n"
				output << ("-" * 60) << "\n"
				output << "    " << stack.join("\n    ")
				output << "\n\n"
			end
		else
			output = "========== Process #{Process.pid}: backtrace dump ==========\n"
			output << ("-" * 60) << "\n"
			output << "# Current thread: #{Thread.current.inspect}\n"
			output << ("-" * 60) << "\n"
			output << "    " << caller.join("\n    ")
		end
		STDERR.puts(output)
		STDERR.flush
	end if trappable_signals.has_key?('QUIT')
end

def main_loop

Enter the request handler's main loop.
def 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_running = true
			@main_loop_thread_cond.broadcast
		end
		
		install_useful_signal_handlers
		
		while true
			@iterations += 1
			client = accept_connection
			if client.nil?
				break
			end
			begin
				headers, input = parse_request(client)
				if headers
					if headers[REQUEST_METHOD] == PING
						process_ping(headers, input, client)
					else
						process_request(headers, input, client)
					end
				end
			rescue IOError, SocketError, SystemCallError => e
				print_exception("Passenger RequestHandler", e)
			ensure
				# 'input' is the same as 'client' so we don't
				# need to close that.
				# The 'close_write' here prevents forked child
				# processes from unintentionally keeping the
				# connection open.
				client.close_write rescue nil
				client.close rescue nil
			end
			@processed_requests += 1
		end
	rescue EOFError
		# Exit main loop.
	rescue Interrupt
		# Exit main loop.
	rescue SignalException => signal
		if signal.message != HARD_TERMINATION_SIGNAL &&
		   signal.message != SOFT_TERMINATION_SIGNAL
			raise
		end
	ensure
		@graceful_termination_pipe[0].close rescue nil
		@graceful_termination_pipe[1].close rescue nil
		revert_signal_handlers
		@main_loop_thread_lock.synchronize do
			@main_loop_running = false
			@main_loop_thread_cond.broadcast
		end
	end
end

def main_loop_running?

Check whether the main loop's currently running.
def main_loop_running?
	return @main_loop_running
end

def parse_request(socket)

Returns nil if end-of-stream was encountered.

reading HTTP POST data.
the request headers, while _input_stream_ is an IO object for
a pair [headers, input_stream]. _headers_ is a Hash containing
Read the next request from the given socket, and return
def parse_request(socket)
	channel = MessageChannel.new(socket)
	headers_data = channel.read_scalar(MAX_HEADER_SIZE)
	if headers_data.nil?
		return
	end
	headers = Hash[*headers_data.split(NULL)]
	headers[CONTENT_LENGTH] = headers[HTTP_CONTENT_LENGTH]
	return [headers, socket]
rescue SecurityError => e
	STDERR.puts("*** Passenger RequestHandler: HTTP header size exceeded maximum.")
	STDERR.flush
	print_exception("Passenger RequestHandler", e)
end

def process_ping(env, input, output)

def process_ping(env, input, output)
	output.write("pong")
end

def reset_signal_handlers

will be put back by calling revert_signal_handlers.
special handlers for a few signals. The previous signal handlers
Reset signal handlers to their default handler, and install some
def reset_signal_handlers
	Signal.list_trappable.each_key do |signal|
		begin
			prev_handler = trap(signal, DEFAULT)
			if prev_handler != DEFAULT
				@previous_signal_handlers[signal] = prev_handler
			end
		rescue ArgumentError
			# Signal cannot be trapped; ignore it.
		end
	end
	trap('HUP', IGNORE)
end

def revert_signal_handlers

def revert_signal_handlers
	@previous_signal_handlers.each_pair do |signal, handler|
		trap(signal, handler)
	end
end

def should_use_unix_sockets?

def should_use_unix_sockets?
	# There seems to be a bug in MacOS X w.r.t. Unix sockets.
	# When the Unix socket subsystem is under high stress, a
	# recv()/read() on a Unix socket can return 0 even when EOF is
	# not reached. We work around this by using TCP sockets on
	# MacOS X.
	return RUBY_PLATFORM !~ /darwin/
end

def start_main_loop_thread

Start the main loop in a new thread. This thread will be stopped by #cleanup.
def start_main_loop_thread
	@main_loop_thread = Thread.new do
		main_loop
	end
	@main_loop_thread_lock.synchronize do
		while !@main_loop_running
			@main_loop_thread_cond.wait(@main_loop_thread_lock)
		end
	end
end