<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>ext/common/Base64.cpp</filename>
    </added>
    <added>
      <filename>ext/common/Base64.h</filename>
    </added>
    <added>
      <filename>ext/common/CachedFileStat.hpp</filename>
    </added>
    <added>
      <filename>ext/common/FileChangeChecker.h</filename>
    </added>
    <added>
      <filename>ext/common/StringListCreator.h</filename>
    </added>
    <added>
      <filename>ext/common/Timer.h</filename>
    </added>
    <added>
      <filename>test/Base64Test.cpp</filename>
    </added>
    <added>
      <filename>test/FileChangeCheckerTest.cpp</filename>
    </added>
    <added>
      <filename>test/support/Support.cpp</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -7,6 +7,7 @@ scgi_demo
 *.bundle
 *.pyc
 *.a
+*.dSYM
 ext/apache2/*.la
 ext/apache2/*.lo
 ext/apache2/*.slo
@@ -25,11 +26,9 @@ test/CxxTests
 test/ApplicationPoolServerExecutable
 test/oxt/oxt_test_main
 test/stub/railsapp/app/controllers/bar_controller.rb
-test/stub/*/log/*
 test/stub/apache2/*.log
 test/stub/apache2/*.pid
 test/stub/apache2/httpd.conf
-test/stub/mycook/log
 test/stub/zsfa/mycook
 test/stub/zsfa/foo
 pkg</diff>
      <filename>.gitignore</filename>
    </modified>
    <modified>
      <diff>@@ -34,7 +34,11 @@ The most important directories are:
 [ ext/phusion_passenger ]
   Native extensions for Ruby, used by the spawn server.
 [ ext/apache2 ]
-  The Phusion Passenger Apache 2 module (mod_passenger).
+  Apache 2-specific source code.
+[ ext/nginx ]
+  Nginx-specific source code.
+[ ext/common ]
+  Source code shared by the Apache and Nginx modules.
 [ bin ]
   Executables.
 [ doc ]
@@ -47,8 +51,6 @@ The most important directories are:
   Stub code, used in the tests.
 
 Less important directories:
-[ lib/rake ]
-  Rake tasks.
 [ ext/boost ]
   A stripped-down and customized version of the Boost C++ library
   (www.boost.org).
@@ -59,8 +61,16 @@ Less important directories:
   for C++. Boost was modified to make use of the functionality provided by OXT.
 [ benchmark ]
   Benchmark tools.
+[ debian ]
+  Debian packaging files.
+[ man ]
+  Man pages.
 [ misc ]
   Miscellaneous tools.
+[ misc/rake ]
+  Rake tasks.
+[ vendor ]
+  Various vendored libraries.
 
 == Tests
 </diff>
      <filename>DEVELOPERS.TXT</filename>
    </modified>
    <modified>
      <diff>@@ -36,6 +36,7 @@ OPTIMIZE = [&quot;yes&quot;, &quot;on&quot;, &quot;true&quot;].include?(ENV['OPTIMIZE'])
 
 include PlatformInfo
 
+CC  = &quot;gcc&quot;
 CXX = &quot;g++&quot;
 LIBEXT = PlatformInfo.library_extension
 if OPTIMIZE
@@ -45,11 +46,11 @@ else
 end
 
 # Extra compiler flags that should always be passed to the C/C++ compiler.
-# Should be included last in the command string.
+# Should be included last in the command string, even after PlatformInfo.portability_cflags.
 EXTRA_CXXFLAGS = &quot;-Wall #{OPTIMIZATION_FLAGS}&quot;
 
 # Extra linker flags that should always be passed to the linker.
-# Should be included last in the command string.
+# Should be included last in the command string, even after PlatformInfo.portability_ldflags.
 EXTRA_LDFLAGS  = &quot;&quot;
 
 
@@ -150,7 +151,7 @@ def define_common_library_task(output_dir, extra_compiler_flags = nil,
                                with_application_pool_server_exe = false,
                                boost_oxt_library = nil,
                                extra_compiler_flags_for_server_exe = nil,
-                               extra_linker_flags = nil)
+                               extra_linker_flags_for_server_exe = nil)
 	static_library = &quot;#{output_dir}/libpassenger_common.a&quot;
 	objects_output_dir = &quot;#{output_dir}/libpassenger_common&quot;
 	targets = [static_library]
@@ -159,7 +160,8 @@ def define_common_library_task(output_dir, extra_compiler_flags = nil,
 	flags =  &quot;-Iext -Iext/common #{extra_compiler_flags} &quot;
 	flags &lt;&lt; &quot;#{PlatformInfo.portability_cflags} #{EXTRA_CXXFLAGS}&quot;
 	common_object_files = []
-	['Utils.cpp', 'Logging.cpp', 'SystemTime.cpp', 'CachedFileStat.cpp'].each do |source_file|
+	['Utils.cpp', 'Logging.cpp', 'SystemTime.cpp', 'CachedFileStat.cpp',
+	 'Base64.cpp'].each do |source_file|
 		object_name = source_file.sub(/\.cpp$/, '.o')
 		object_file = &quot;#{objects_output_dir}/#{object_name}&quot;
 		header_file = source_file.sub(/\.cpp$/, '.h')
@@ -185,12 +187,14 @@ def define_common_library_task(output_dir, extra_compiler_flags = nil,
 			'ext/common/ApplicationPool.h',
 			'ext/common/Application.h',
 			'ext/common/StandardApplicationPool.h',
+			'ext/common/ApplicationPoolStatusReporter.h',
 			'ext/common/MessageChannel.h',
 			'ext/common/SpawnManager.h',
 			'ext/common/PoolOptions.h',
-			'ext/common/FileChecker.h',
+			'ext/common/StringListCreator.h',
+			'ext/common/FileChangeChecker.h',
 			'ext/common/SystemTime.h',
-			'ext/common/CachedFileStat.h',
+			'ext/common/CachedFileStat.hpp',
 			boost_oxt_library,
 			static_library
 		]) do
@@ -203,7 +207,7 @@ def define_common_library_task(output_dir, extra_compiler_flags = nil,
 				&quot;#{EXTRA_CXXFLAGS} &quot; &lt;&lt;
 				&quot;#{static_library} &quot; &lt;&lt;
 				&quot;#{boost_oxt_library} &quot; &lt;&lt;
-				&quot;#{extra_linker_flags} &quot; &lt;&lt;
+				&quot;#{extra_linker_flags_for_server_exe} &quot; &lt;&lt;
 				&quot;#{PlatformInfo.portability_ldflags} &quot; &lt;&lt;
 				EXTRA_LDFLAGS
 			)
@@ -242,7 +246,9 @@ end
 			ext/common/Application.h
 			ext/common/MessageChannel.h
 			ext/common/PoolOptions.h
+			ext/common/StringListCreator.h
 			ext/common/Version.h
+			ext/common/Timer.h
 			ext/common/Utils.h)
 	}
 	APACHE2_OBJECTS = APACHE2_INPUT_FILES.keys
@@ -253,8 +259,7 @@ end
 		PlatformInfo.apache2_module_cflags)
 	APACHE2_COMMON_LIBRARY    = define_common_library_task(&quot;ext/apache2&quot;,
 		PlatformInfo.apache2_module_cflags,
-		true, APACHE2_BOOST_OXT_LIBRARY, nil,
-		PlatformInfo.apache2_module_ldflags)
+		true, APACHE2_BOOST_OXT_LIBRARY)
 	
 	
 	desc &quot;Build Apache 2 module&quot;
@@ -382,6 +387,9 @@ end
 	TEST_CXX_OBJECTS = {
 		'test/CxxTestMain.o' =&gt; %w(
 			test/CxxTestMain.cpp),
+		'test/support/Support.o' =&gt; %w(
+			test/support/Support.cpp
+			test/support/Support.h),
 		'test/MessageChannelTest.o' =&gt; %w(
 			test/MessageChannelTest.cpp
 			ext/common/MessageChannel.h),
@@ -389,12 +397,14 @@ end
 			test/SpawnManagerTest.cpp
 			ext/common/SpawnManager.h
 			ext/common/PoolOptions.h
+			ext/common/StringListCreator.h
 			ext/common/Application.h
 			ext/common/MessageChannel.h),
 		'test/ApplicationPoolServerTest.o' =&gt; %w(
 			test/ApplicationPoolServerTest.cpp
 			ext/common/ApplicationPoolServer.h
 			ext/common/PoolOptions.h
+			ext/common/StringListCreator.h
 			ext/common/MessageChannel.h),
 		'test/ApplicationPoolServer_ApplicationPoolTest.o' =&gt; %w(
 			test/ApplicationPoolServer_ApplicationPoolTest.cpp
@@ -403,6 +413,7 @@ end
 			ext/common/ApplicationPool.h
 			ext/common/SpawnManager.h
 			ext/common/PoolOptions.h
+			ext/common/StringListCreator.h
 			ext/common/Application.h
 			ext/common/MessageChannel.h),
 		'test/StandardApplicationPoolTest.o' =&gt; %w(
@@ -412,14 +423,21 @@ end
 			ext/common/StandardApplicationPool.h
 			ext/common/SpawnManager.h
 			ext/common/PoolOptions.h
-			ext/common/FileChecker.h
+			ext/common/StringListCreator.h
+			ext/common/FileChangeChecker.h
+			ext/common/CachedFileStat.hpp
 			ext/common/Application.h),
 		'test/PoolOptionsTest.o' =&gt; %w(
 			test/PoolOptionsTest.cpp
-			ext/common/PoolOptions.h),
-		'test/StaticString.o' =&gt; %w(
+			ext/common/PoolOptions.h
+			ext/common/StringListCreator.h),
+		'test/StaticStringTest.o' =&gt; %w(
 			test/StaticStringTest.cpp
 			ext/common/StaticString.h),
+		'test/Base64Test.o' =&gt; %w(
+			test/Base64Test.cpp
+			ext/common/Base64.h
+			ext/common/Base64.cpp),
 		'test/ScgiRequestParserTest.o' =&gt; %w(
 			test/ScgiRequestParserTest.cpp
 			ext/nginx/ScgiRequestParser.h
@@ -427,17 +445,17 @@ end
 		'test/HttpStatusExtractorTest.o' =&gt; %w(
 			test/HttpStatusExtractorTest.cpp
 			ext/nginx/HttpStatusExtractor.h),
-		'test/FileCheckerTest.o' =&gt; %w(
-			test/FileCheckerTest.cpp
-			ext/common/FileChecker.h
-			ext/common/CachedFileStat.h),
+		'test/FileChangeCheckerTest.o' =&gt; %w(
+			test/FileChangeCheckerTest.cpp
+			ext/common/FileChangeChecker.h
+			ext/common/CachedFileStat.hpp),
 		'test/SystemTimeTest.o' =&gt; %w(
 			test/SystemTimeTest.cpp
 			ext/common/SystemTime.h
 			ext/common/SystemTime.cpp),
 		'test/CachedFileStatTest.o' =&gt; %w(
 			test/CachedFileStatTest.cpp
-			ext/common/CachedFileStat.h
+			ext/common/CachedFileStat.hpp
 			ext/common/CachedFileStat.cpp),
 		'test/UtilsTest.o' =&gt; %w(
 			test/UtilsTest.cpp
@@ -518,7 +536,7 @@ end
 	end
 	
 	TEST_OXT_OBJECTS.each_pair do |target, sources|
-		file &quot;test/oxt/#{target}&quot; =&gt; sources.map{ |x| &quot;test/oxt/#{x}&quot; } do
+		file &quot;test/oxt/#{target}&quot; =&gt; sources.map{ |x| &quot;test/oxt/#{x}&quot; } + ['test/support/Support.h'] do
 			Dir.chdir('test/oxt') do
 				puts &quot;### In test/oxt:&quot;
 				compile_cxx sources[0], TEST_OXT_CFLAGS
@@ -668,7 +686,7 @@ spec = Gem::Specification.new do |s|
 		'doc/*/*/*/*/*/*',
 		'man/*',
 		'debian/*',
-		'ext/common/*.{cpp,c,h}',
+		'ext/common/*.{cpp,c,h,hpp}',
 		'ext/apache2/*.{cpp,h,c,TXT}',
 		'ext/nginx/*.{c,cpp,h}',
 		'ext/nginx/config',
@@ -683,21 +701,12 @@ spec = Gem::Specification.new do |s|
 		'misc/*/*',
 		'vendor/**/*',
 		'test/*.{rb,cpp,example}',
-		'test/support/*',
+		'test/support/*.{cpp,h,rb}',
 		'test/oxt/*.cpp',
-		'test/ruby/*',
-		'test/ruby/*/*',
-		'test/integration_tests/*',
-		'test/stub/*',
-		'test/stub/*/*',
-		'test/stub/*/*/*',
-		'test/stub/*/*/*/*',
-		'test/stub/*/*/*/*/*',
-		'test/stub/*/*/*/*/*/*',
-		'test/stub/*/*/*/*/*/*/*'
-	] - Dir['test/stub/*/log/*'] \
-	  - Dir['test/stub/*/tmp/*/*'] \
-	  - Dir['test/stub/apache2/*.{pid,lock,log}']
+		'test/ruby/**/*',
+		'test/integration_tests/**/*',
+		'test/stub/**/*'
+	]
 	s.executables = [
 		'passenger-spawn-server',
 		'passenger-install-apache2-module',</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -49,8 +49,7 @@ class Installer &lt; PhusionPassenger::AbstractInstaller
 			Dependencies::RubyGems,
 			Dependencies::Rake,
 			Dependencies::Apache2,
-			Dependencies::Apache2_DevHeaders,
-			Dependencies::FastThread
+			Dependencies::Apache2_DevHeaders
 		]
 		if Dependencies.fastthread_required?
 			result &lt;&lt; Dependencies::FastThread</diff>
      <filename>bin/passenger-install-apache2-module</filename>
    </modified>
    <modified>
      <diff>@@ -36,18 +36,21 @@ include PlatformInfo
 class Installer &lt; PhusionPassenger::AbstractInstaller
 	include PhusionPassenger
 	
-	NGINX_VERSION = &quot;0.6.36&quot;
+	NGINX_VERSION = &quot;0.6.37&quot;
 	
 	def dependencies
-		return [
+		result = [
 			Dependencies::GCC,
 			Dependencies::Ruby_DevHeaders,
 			Dependencies::Ruby_OpenSSL,
 			Dependencies::RubyGems,
 			Dependencies::Rake,
-			Dependencies::FastThread,
 			Dependencies::Zlib_Dev
 		]
+		if Dependencies.fastthread_required?
+			result &lt;&lt; Dependencies::FastThread
+		end
+		return result
 	end
 	
 	def users_guide
@@ -59,7 +62,7 @@ class Installer &lt; PhusionPassenger::AbstractInstaller
 		show_welcome_screen
 		check_dependencies || exit(1)
 		
-		check_write_permission_to_passenger_root || exit(1)
+		check_whether_we_can_write_to(PASSENGER_ROOT) || exit(1)
 		
 		download_and_install = should_we_download_and_install_nginx_automatically?
 		if pcre_is_installed?
@@ -102,21 +105,6 @@ private
 		wait
 	end
 	
-	def check_write_permission_to_passenger_root
-		File.new(&quot;__test__.txt&quot;, &quot;w&quot;).close
-		return true
-	rescue
-		new_screen
-		if Process.uid == 0
-			render_template 'no_write_permission_to_passenger_root'
-		else
-			render_template 'run_installer_as_root'
-		end
-		return false
-	ensure
-		File.unlink(&quot;__test__.txt&quot;) rescue nil
-	end
-	
 	def compile_passenger_support_files
 		new_screen
 		color_puts &quot;&lt;banner&gt;Compiling Passenger support files...&lt;/banner&gt;&quot;</diff>
      <filename>bin/passenger-install-nginx-module</filename>
    </modified>
    <modified>
      <diff>@@ -22,9 +22,10 @@
 #  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 #  THE SOFTWARE.
 
-$LOAD_PATH &lt;&lt; File.expand_path(File.dirname(__FILE__) + &quot;/../lib&quot;)
-$LOAD_PATH &lt;&lt; File.expand_path(File.dirname(__FILE__) + &quot;/../ext&quot;)
+$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + &quot;/../lib&quot;))
+$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + &quot;/../ext&quot;))
 require 'phusion_passenger/admin_tools/control_process'
+require 'optparse'
 
 include PhusionPassenger::AdminTools
 
@@ -32,30 +33,78 @@ include PhusionPassenger::AdminTools
 RESET   = &quot;\e[0m&quot;
 BOLD    = &quot;\e[1m&quot;
 YELLOW  = &quot;\e[33m&quot;
+BLACK_BG = &quot;\e[40m&quot;
 BLUE_BG = &quot;\e[44m&quot;
 
-def show_status(control_process, format = :text)
-	begin
-		text = control_process.status
-	rescue SystemCallError =&gt; e
-		STDERR.puts &quot;*** ERROR: Cannot query status for Passenger instance #{control_process.pid}:&quot;
-		STDERR.puts e.to_s
-		exit 2
+def show_status(control_process, options = {})
+	case options[:show]
+	when 'pool'
+		begin
+			text = control_process.status
+		rescue SystemCallError =&gt; e
+			STDERR.puts &quot;*** ERROR: Cannot query status for Passenger instance #{control_process.pid}:&quot;
+			STDERR.puts e.to_s
+			exit 2
+		end
+		
+		# Colorize output
+		text.gsub!(/^(----)(.*)$/, YELLOW + BLUE_BG + BOLD + '\1\2' + RESET)
+		
+		puts text
+		
+	when 'backtraces'
+		begin
+			text = control_process.backtraces
+		rescue SystemCallError =&gt; e
+			STDERR.puts &quot;*** ERROR: Cannot query status for Passenger instance #{control_process.pid}:&quot;
+			STDERR.puts e.to_s
+			exit 2
+		end
+		
+		# Colorize output
+		text.gsub!(/^(Thread .*:)$/, BLACK_BG + YELLOW + '\1' + RESET)
+		text.gsub!(/^( +in '.*? )(.*?)\(/, '\1' + BOLD + '\2' + RESET + '(')
+		
+		puts text
 	end
-	# Colorize output
-	text.gsub!(/^(----)(.*)$/, YELLOW + BLUE_BG + BOLD + '\1\2' + RESET)
-	text.gsub!(/^( +in '.*? )(.*?)\(/, '\1' + BOLD + '\2' + RESET + '(')
-	puts text
 end
 
 def start
+	options = { :show =&gt; 'pool' }
+	parser = OptionParser.new do |opts|
+		opts.banner = &quot;Usage: passenger-status [options] [Phusion Passenger's PID]&quot;
+		opts.separator &quot;&quot;
+		opts.separator &quot;Tool for inspecting Phusion Passenger's internal status.&quot;
+		opts.separator &quot;&quot;
+
+		opts.separator &quot;Options:&quot;
+		opts.on(&quot;--show=pool|backtraces&quot;, String,
+		        &quot;Whether to show the pool's contents or\n&quot; &lt;&lt;
+		        &quot;#{' ' * 37}the backtraces of all threads.&quot;) do |what|
+			if what !~ /\A(pool|backtraces)\Z/
+				STDERR.puts &quot;Invalid argument for --show.&quot;
+				exit 1
+			else
+				options[:show] = what
+			end
+		end
+	end
+	begin
+		parser.parse!
+	rescue OptionParser::ParseError =&gt; e
+		puts e
+		puts
+		puts &quot;Please see '--help' for valid options.&quot;
+		exit 1
+	end
+	
 	if ARGV.empty?
 		control_processes = ControlProcess.list
 		if control_processes.empty?
 			STDERR.puts(&quot;ERROR: Phusion Passenger doesn't seem to be running.&quot;)
 			exit 2
 		elsif control_processes.size == 1
-			show_status(control_processes.first)
+			show_status(control_processes.first, options)
 		else
 			puts &quot;It appears that multiple Passenger instances are running. Please select a&quot;
 			puts &quot;specific one by running:&quot;
@@ -69,7 +118,7 @@ def start
 			exit 1
 		end
 	else
-		show_status(ControlProcess.for_pid(ARGV[0].to_i))
+		show_status(ControlProcess.for_pid(ARGV[0].to_i), options)
 	end
 end
 </diff>
      <filename>bin/passenger-status</filename>
    </modified>
    <modified>
      <diff>@@ -22,8 +22,8 @@
 #  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 #  THE SOFTWARE.
 
-$LOAD_PATH &lt;&lt; File.expand_path(File.dirname(__FILE__) + &quot;/../lib&quot;)
-$LOAD_PATH &lt;&lt; File.expand_path(File.dirname(__FILE__) + &quot;/../ext&quot;)
+$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + &quot;/../lib&quot;))
+$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + &quot;/../ext&quot;))
 require 'rubygems'
 require 'optparse'
 require 'socket'</diff>
      <filename>bin/passenger-stress-test</filename>
    </modified>
    <modified>
      <diff>@@ -65,12 +65,6 @@ explicitly define some special types:
     application instance in this domain may process. After having processed this
     many requests, the application instance will be shut down.
     A value of 0 indicates that there is no maximum.
-  
-  * restart_file_checker
-    An object which monitors the restart.txt file, which belongs to this
-    application root, for changes. This object has the method changed(),
-    which rteurns whether restart.txt's timestamp has changed since the last
-    check.
 
 - AppContainer
   A compound type (class) which contains an application instance, as well as
@@ -99,7 +93,7 @@ explicitly define some special types:
   A structure containing additional information used by the spawn manager's
   spawning process, as well as by the get() function.
   
-  A PoolOptions has the following members:
+  A PoolOptions has at least the following members:
   * max_requests (unsigned integer) - The maximum number of requests that the
     application instance may process. After having processed this many requests,
     the application instance will be shut down. A value of 0 indicates that there
@@ -107,6 +101,9 @@ explicitly define some special types:
   * use_global_queue (boolean) - Whether to use a global queue for all
     application instances, or a queue that's private to the application instance.
     The users guide explains this feature in more detail.
+  * restart_dir (string) - The directory in which the algorithm should look for
+    restart.txt and always_restart.txt. The existance and modification times of
+    these files tell the algorithm whether an application should be restarted.
 
 === Special functions
 
@@ -226,17 +223,18 @@ function get(app_root, options):
 function spawn_or_use_existing(app_root, options):
 	domain = domains[app_root]
 	
-	if (domain != nil) and (needs_restart(app_root, domain)):
-		for all container in domain.instances:
-			if container.sessions == 0:
-				inactive_apps.remove(container.ia_iterator)
-			else:
-				active--
-			domain.instances.remove(container.iterator)
-			count--
-		domains.remove(app_root)
-		list = nil
+	if needs_restart(app_root, options):
+		if (domain != nil):
+			for all container in domain.instances:
+				if container.sessions == 0:
+					inactive_apps.remove(container.ia_iterator)
+				else:
+					active--
+				domain.instances.remove(container.iterator)
+				count--
+			domains.remove(app_root)
 		Tell spawn server to reload code for app_root.
+		domain = nil
 	
 	if domain != nil:
 		# There are apps for this app root.
@@ -320,7 +318,6 @@ function spawn_or_use_existing(app_root, options):
 		if domain == nil:
 			domain = new Domain
 			initialize domain.instances
-			initialize domain.restart_file_checker with &quot;$app_root/tmp/restart.txt&quot;
 			domain.size = 1
 			domain.max_requests = options.max_requests
 			domains[app_root] = domain
@@ -362,10 +359,17 @@ function session_has_been_closed(container):
 					active--
 
 
-function needs_restart(app_root, domain):
-	always_restart_file = &quot;$app_root/tmp/always_restart.txt&quot;
-	return (file_exists(always_restart_file)) or
-	       (domain.restart_file_checker.changed())
+function needs_restart(app_root, options):
+	if (options.restart_dir is not set):
+		restart_dir = app_root + &quot;/tmp&quot;
+	else if (options.restart_dir is an absolute path):
+		restart_dir = options.restart_dir
+	else:
+		restart_dir = app_root + &quot;/&quot; + options.restart_dir
+	
+	return (file_exists(&quot;$restart_dir/always_restart.txt&quot;)) or
+	       (we haven't seen &quot;$restart_dir/restart.txt&quot; before) or
+	       (&quot;$restart_dir/restart.txt&quot; changed since the last time we checked)
 
 
 # The following thread will be responsible for cleaning up idle application</diff>
      <filename>doc/ApplicationPool algorithm.txt</filename>
    </modified>
    <modified>
      <diff>@@ -24,35 +24,26 @@ using the commandline.
 Phusion Passenger works on any POSIX-compliant operating system. In other
 words: practically any operating system on earth, except Microsoft Windows.
 
-Phusion Passenger has been tested on:
-
-- Ubuntu Linux 6.06 (x86)
-- Ubuntu Linux 7.10 (x86)
-- Ubuntu Linux 8.04 (x86)
-- Debian Sarge (x86)
-- Debian Etch (x86)
-- Debian Lenny/Sid (x86)
-- CentOS 5 (x86)
-- Red Hat Enterprise Linux 5 (x86)
-- Gentoo, March 14 2008 (AMD64)
-- FreeBSD 6.1-RELEASE (x86)
-- MacOS X Tiger (x86)
-- MacOS X Leopard (x86)
-
-Other operating systems have not been tested, but Phusion Passenger will probably
-work fine on them. Please
+Phusion Passenger is confirmed on a large number of operating systems and Linux
+distributions, including, but not limited to, Ubuntu, Debian, CentOS/Fedora/RHEL,
+Gentoo, Mac OS X, FreeBSD and Solaris. Both 32-bit and 64-bit platforms are supported.
+
+The only POSIX-compliant operating system on which Phusion Passenger for Apache is
+known not to work at this time, is OpenBSD. Please use Phusion Passenger for Nginx
+instead.
+
+If Phusion Passenger does not work on your platform then please
 link:http://code.google.com/p/phusion-passenger/issues/list[report a bug]
 or
-link:http://groups.google.com/group/phusion-passenger[join our discussion list]
-if it doesn't.
+link:http://groups.google.com/group/phusion-passenger[join our discussion list].
 
 
-== Installing Phusion Passenger ==
+== Installing, upgrading and uninstalling Phusion Passenger ==
 
 === Generic installation instructions ===
 
 [[install_passenger]]
-==== Overview of download and installation methods ====
+==== Overview of installation methods ====
 
 There are three ways to install Phusion Passenger:
 
@@ -61,39 +52,73 @@ There are three ways to install Phusion Passenger:
    Passenger website].
 2. By downloading the source tarball from the Phusion Passenger website
    ('passenger-x.x.x.tar.gz').
-3. By downloading a native Linux package (e.g. Debian package) from the
-   Phusion Passenger website.
-
-In our opinion, installing the gem or the native package is easiest.
+3. By installing a native Linux package (e.g. Debian package).
 
-Phusion Passenger provides an easy-to-use installer for installing the Phusion
-Passenger Apache module, in case you choose to install via the gem or the
-source tarball.
+The following sections will explain each installation method. Please read the
+section for the installation method that you prefer. In our opinion, installing
+the gem or the native package is easiest. For these two installation methods,
+Phusion Passenger provides an easy-to-use installer.
 
-TIP: You might have to run the installation commands in the following sections
-as 'root'. If the installer fails because of permission errors, it will tell
-you.
-
-==== Preparation
+==== Preparation (gem and source tarball only)
 If you want to install Phusion Passenger via the gem or the source tarball,
-then some preparation might be required. You can skip this subsection if
+then some preparations might be required. You can skip this subsection if
 you're installing Phusion Passenger via a native Linux package, because no
 compilation is necessary.
 
+===== Switching to a root command prompt =====
+
+Before installing, you will probably need to switch to the `root` user first.
+When you install Phusion Passenger via a gem or a source tarball, some Phusion
+Passenger files have to be compiled, which requires write access to the
+directory in which the Phusion Passenger files are located. On Unix systems,
+the root user is the user who has write access to the entire system. So unless
+you know that your normal user account has write access to the Phusion Passenger
+directory, you should switch to root before installing Phusion Passenger.
+
+You can switch to root by typing the following command:
+
+-------------------------
+sudo -s
+-------------------------
+
+This will open a command prompt as the root user, from which you can proceed
+with installing Phusion Passenger.
+
+If your system does not have 'sudo' installed, please type the following command instead, which should do the same thing:
+
+-------------------------
+su
+-------------------------
+
 [[specifying_correct_apache_install]]
 ===== Specifying the correct Apache installation =====
 
-If your system has multiple Apache installations (this is likely the case on
-MacOS X), then you will need to tell the Phusion Passenger installer which one
-to use. If you only have one Apache installation (the case on most Linux
-systems), then you can skip this section because Phusion Passenger will
-automatically detect it.
-
-Every Apache installation has its own `apxs` program. You will need to tell
-Phusion Passenger the location of this program, by specifying the `APXS2`
-environment variable. Suppose that you want to use the Apache installation in
-'/opt/apache2'. Then, assuming that the corresponding `apxs` program is located
-'/opt/apache2/bin/apxs', type:
+The Phusion Passenger installer will attempt to automatically detect Apache,
+and compile Phusion Passenger against that Apache version. It does this by
+looking for the `apxs` or `apxs2` command in the PATH environment variable.
+Apxs is an integral part of any Apache installation.
+
+However, some systems have multiple Apache installations. This is likely
+the case on MacOS X: the OS ships with Apache, but users tend to install
+another Apache version seperately, e.g. via MacPorts. If your system has
+multiple Apache installations, then you will need to tell the Phusion Passenger
+installer which one to use. It is very important that you specify the
+correct Apache installation, because if you load Phusion Passenger in an
+Apache installation that it wasn't compiled against, then it will likely
+crash.
+
+On yet other systems, Apache is installed in a non-standard location,
+preventing the Phusion Passenger installer from detecting Apache. This
+is most likely the case on systems on which Apache was installed by hand
+from source, i.e. as opposed to installed through the system's native
+package manager. If this is the case, then you will also have to tell
+the installer where it can find Apache.
+
+To do so, set the `APXS2` environment variable to the full path of the
+correct `apxs` or `apxs2` command. Suppose that you want to use the Apache
+installation in '/opt/apache2'. Then, assuming that the corresponding
+`apxs` program's path is '/opt/apache2/bin/apxs', type:
+
 ----------------------------------
 export APXS2=/opt/apache2/bin/apxs
 ----------------------------------
@@ -101,6 +126,14 @@ export APXS2=/opt/apache2/bin/apxs
 NOTE: On some systems, the `apxs` program might be called `apxs2`, and it might
 be located in the `sbin` folder instead of the `bin` folder.
 
+.Environment variables and 'sudo'
+NOTE: By default, the 'sudo' command will erase any environment variables that it
+doesn't recognize, prior to executing the given command. So if you set APXS2 as a
+normal user, then run `sudo passenger-install-apache2-module` (which is the command
+for the Phusion Passenger installer), then the installer will not receive the
+environment variable value that you set. To solve this problem, please become root
+prior to setting any environment variables, as described in the previous subsection.
+
 [[specifying_ruby_installation]]
 ===== Specifying the correct Ruby installation =====
 
@@ -169,6 +202,17 @@ sudo apt-get update
 sudo apt-get install libapache2-mod-passenger
 ------------------------------------------------------
 
+==== What does the installer do? ====
+
+Although we call it an ``installer'', it doesn't actually install anything.
+The installer checks whether all required dependencies are installed,
+compiles Phusion Passenger for you, and tells you how to modify the Apache
+configuration file, but it doesn't copy any files around.
+
+`passenger-install-apache2-module` is actually just a user-friendly frontend
+around the command `rake apache2`, which performs the actual compilation of
+Phusion Passenger.
+
 
 === Operating system-specific instructions and information ===
 
@@ -188,6 +232,105 @@ J Aaron Farr has written a link:http://cubiclemuses.com/cm/articles/2009/04/09/r
 about setting up Ruby on Rails and Phusion Passenger on OpenSolaris and EC2.
 
 
+=== Upgrading or downgrading Phusion Passenger ===
+
+==== Via a gem or a source tarball ====
+
+To ugrade or downgrade Phusion Passenger via the gem or the source tarball, install the newer
+or older version as you normally would; that is, install the gem or unpack the tarball, and
+run `passenger-install-apache2-module`. Eventually `passenger-install-apache2-module` will tell
+you to copy &amp; paste some settings into the Apache configuration file; something that looks along
+the lines of:
+
+-----------------------------------
+LoadModule passenger_module ...
+PassengerRoot ...
+PassengerRuby ...
+-----------------------------------
+
+Because you already have Phusion Passenger installed, you already have the same settings
+in your Apache configuration file, just with different values. Replace the old settings with
+the new ones that the installer outputs.
+
+When you're done, restart Apache.
+
+==== Via a native Linux package
+
+There are no special instructions required to upgrade or downgrade Phusion Passenger
+via a native Linux package.
+
+=== Unloading (disabling) Phusion Passenger from Apache without uninstalling it ===
+
+You can temporarily unload (disable) Phusion Passenger from Apache, without
+uninstalling the Phusion Passenger files, so that Apache behaves as if Phusion
+Passenger was never installed in the first place. This might be useful to you if,
+for example, you seem to be experiencing a problem caused by Phusion Passenger,
+but you want to make sure whether that's actually the case, without having
+to through the hassle of uninstalling Phusion Passenger completely.
+
+To unload Phusion Passenger from Apache, edit your Apache configuration file(s)
+and comment out:
+
+- all Phusion Passenger configuration directives.
+- the 'LoadModule passenger_module' directive.
+
+For example, if your configuration file looks like this...
+
+-----------------------------------
+Listen *:80
+NameVirtualHosts *:80
+....
+
+LoadModule passenger_module /somewhere/passenger-x.x.x/ext/apache2/mod_passenger.so
+
+PassengerRuby /usr/bin/ruby
+PassengerRoot /somewhere/passenger/x.x.x
+PassengerMaxPoolSize 10
+
+&lt;VirtualHost *:80&gt;
+    ServerName www.foo.com
+    DocumentRoot /webapps/foo/public
+    RailsBaseURI /rails
+&lt;/VirtualHost&gt;
+-----------------------------------
+
+...then comment out the relevant directives, so that it looks like this:
+
+-----------------------------------
+Listen *:80
+NameVirtualHosts *:80
+....
+
+# LoadModule passenger_module /somewhere/passenger-x.x.x/ext/apache2/mod_passenger.so
+
+# PassengerRuby /usr/bin/ruby
+# PassengerRoot /somewhere/passenger/x.x.x
+# PassengerMaxPoolSize 10
+
+&lt;VirtualHost *:80&gt;
+    ServerName www.foo.com
+    DocumentRoot /webapps/foo/public
+    # RailsBaseURI /rails
+&lt;/VirtualHost&gt;
+-----------------------------------
+
+After you've done this, save the file and restart Apache.
+
+=== Uninstalling Phusion Passenger ===
+
+To uninstall Phusion Passenger, please first remove all Phusion Passenger
+configuration directives from your Apache configuration file(s). After you've
+done this, you need to remove the Phusion Passenger files.
+
+- If you installed Phusion Passenger via a gem, then type `gem uninstall passenger`.
+  You might have to run this as root.
+- If you installed Phusion Passenger via a source tarball, then remove the directory
+  in which you placed the extracted Phusion Passenger files. This directory is the
+  same as the one pointed to the by 'PassengerRoot' configuration directive.
+- If you installed Phusion Passenger via a Debian package, then remove type
+  `sudo apt-get remove libapache2-mod-passenger`.
+
+
 == Deploying a Ruby on Rails application ==
 
 Suppose you have a Ruby on Rails application in '/webapps/mycook', and you own
@@ -274,7 +417,7 @@ There are two ways to restart the application:
 1. By restarting Apache.
 2. By creating or modifying the file 'tmp/restart.txt' in the Rails
    application's &lt;&lt;application_root,root folder&gt;&gt;. Phusion Passenger will
-   automatically restart the application.
+   automatically restart the application during the next request.
 
 For example, to restart our example MyCook application, we type this in the
 command line:
@@ -282,6 +425,11 @@ command line:
 touch /webapps/mycook/tmp/restart.txt
 -------------------------------------------
 
+Please note that, unlike earlier versions of Phusion Passenger, 'restart.txt'
+is not automatically deleted. Phusion Passenger checks whether the timestamp
+of this file has changed in order to determine whether the application should
+be restarted.
+
 === Migrations ===
 
 Phusion Passenger is not related to Ruby on Rails migrations in any way. To
@@ -347,7 +495,7 @@ $ some_awesome_editor config.ru
 ...type in some source code...
 $ cat config.ru
 app = proc do |env|
-    return [200, { &quot;Content-Type&quot; =&gt; &quot;text/html&quot; }, &quot;hello &lt;b&gt;world&lt;/b&gt;&quot;]
+    [200, { &quot;Content-Type&quot; =&gt; &quot;text/html&quot; }, [&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;]]
 end
 run app
 -------------------------------------------
@@ -631,10 +779,10 @@ follows:
 &lt;VirtualHost *:80&gt;
     ServerName www.foo.com
     DocumentRoot /apps/foo/public
-    &lt;Location /wordpress&gt;
+    &lt;Directory /apps/foo/public/wordpress&gt;
         PassengerEnabled off
         AllowOverride all      # &lt;-- Makes Wordpress's .htaccess file work.
-    &lt;/Location&gt;
+    &lt;/Directory&gt;
 &lt;/VirtualHost&gt;
 ------------------------------------
 
@@ -649,10 +797,11 @@ This way, Phusion Passenger will not interfere with Wordpress.
 
 In each place, it may be specified at most once. The default value is 'on'.
 
+[[PassengerTempDir]]
 === PassengerTempDir &lt;directory&gt; ===
 Specifies the directory that Phusion Passenger should use for storing temporary
-files. This includes things such as Unix socket files, buffered file uploads,
-etc.
+files. This includes things such as Unix socket files, buffered file uploads
+(see also &lt;&lt;PassengerUploadBufferDir,PassengerUploadBufferDir&gt;&gt;), etc.
 
 This option may be specified once, in the global server configuration. The
 default temp directory that Phusion Passenger uses is '/tmp'.
@@ -678,6 +827,36 @@ sudo -E passenger-status
 # The -E option tells 'sudo' to preserve environment variables.
 ----------------------------------------------------------
 
+[[PassengerUploadBufferDir]]
+=== PassengerUploadBufferDir &lt;directory&gt; ===
+Phusion Passenger buffers large file uploads to disk in order prevent slow file
+uploads from blocking web applications. By default, a subdirectory in the
+system's temporary files directory (or a subdirectory in the directory specified
+in &lt;&lt;PassengerTempDir,PassengerTempDir&gt;&gt;, if set) is automatically created for
+storing these buffered file uploads.
+
+This configuration directive allows you to specify a different directory for storing
+buffered file uploads. If you've specified such a directory (as opposed to using
+Phusion Passenger's default) then you *must* ensure that this directory exists.
+
+This configuration directive is also useful if you're using apache2-mpm-itk.
+The buffered file upload directory that Phusion Passenger creates by default has
+very strict permissions: it can only be accessed by the Apache worker processes.
+However, Phusion Passenger assumes that all Apache worker processes are running
+as the same user. apache2-mpm-itk breaks this assumption by running multiple
+Apache worker processes as different users. So if you're using apace2-mpm-itk,
+you should set this option to a directory that is writable by all Apache worker
+processes, such as '/tmp'.
+
+You may specify 'PassengerUploadBufferDir' in the following places:
+
+ * In the global server configuration.
+ * In a virtual host configuration block.
+ * In a `&lt;Directory&gt;` or `&lt;Location&gt;` block.
+ * In '.htaccess', if `AllowOverrides Options` is enabled.
+
+In each place, it may be specified at most once.
+
 === PassengerRestartDir &lt;directory&gt; ===
 As described in the deployment chapters of this document, Phusion Passenger
 checks the file 'tmp/restart.txt' in the applications'
@@ -1419,6 +1598,94 @@ try drogus's link:http://github.com/drogus/apache-upload-progress-module/tree/ma
 Apache upload progress module] instead.
 
 
+== Under the hood ==
+Phusion Passenger hides a lot of complexity for the end user (i.e. the web server
+system administrator), but sometimes it is desirable to know what is going on.
+This section describes a few things that Phusion Passenger does under the hood.
+
+=== Static assets serving ===
+Phusion Passenger accelerates serving of static files. This means that, if an URI
+maps to a file that exists, then Phusion Passenger will let Apache serve that file
+directly, without hitting the web application.
+
+Phusion Passenger does all this without the need for any mod_rewrite rules. People
+who are switching from an old Mongrel-based setup might have mod_rewrite rules such
+as these:
+
+------------------------------------------------------------
+# Check whether this request has a corresponding file; if that
+# exists, let Apache serve it, otherwise forward the request to
+# Mongrel.
+RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f
+RewriteRule ^(.*)$ balancer://mongrel%{REQUEST_URI} [P,QSA,L]
+------------------------------------------------------------
+
+These kind of mod_rewrite rules are no longer required, and you can safely remove
+them.
+
+=== Page caching support ===
+For each HTTP request, Phusion Passenger will automatically look for a corresponding
+page cache file, and serve that if it exists. It does this by appending &quot;.html&quot; to
+the filename that the URI normally maps to, and checking whether that file exists.
+This check occurs after checking whether the original mapped filename exists (as part
+of static asset serving). All this is done without the need for special mod_rewrite
+rules.
+
+For example, suppose that the browser requests '/foo/bar'.
+
+1. Phusion Passenger will first check whether this URI maps to a static file, i.e.
+   whether the file 'foo/bar' exists in the web application's 'public' directory.
+   If it does then Phusion Passenger will serve this file through Apache immediately.
+2. If that doesn't exist, then Phusion Passenger will check whether the file
+   'foo/bar.html' exists. If it does then Phusion Passenger will serve this file
+   through Apache immediately.
+3. If 'foo/bar.html' doesn't exist either, then Phusion Passenger will forward the
+   request to the underlying web application.
+
+Note that Phusion Passenger's page caching support doesn't work if your web
+application uses a non-standard page cache directory, i.e. if it doesn't cache to
+the 'public' directory. In that case you'll need to use mod_rewrite to serve such
+page cache files.
+
+=== How Phusion Passenger detects whether a virtual host is a web application ===
+After you've read the deployment instructions you might wonder how Phusion Passenger
+knows that the DocumentRoot points to a web application that Phusion Passenger is
+able to serve, and how it knows what kind of web application it is (e.g. Rails or Rack).
+
+Phusion Passenger checks whether the virtual host is a Rails application by checking
+whether the following file exists:
+
+------------------------------------------------
+dirname(DocumentRoot) + &quot;/config/environment.rb&quot;
+------------------------------------------------
+
+If you're not a programmer and don't understand the above pseudo-code snippet, it means
+that Phusion Passenger will:
+
+1. Extract the parent directory filename from the value of the DocumentRoot directory.
+2. Append the text &quot;/config/environment.rb&quot; to the result, and check whether the resulting
+   filename exists.
+
+So suppose that your document root is '/webapps/foo/public'. Phusion Passenger will check
+whether the file '/webapps/foo/config/environment.rb' exists.
+
+Note that Phusion Passenger does *not* resolve any symlinks in the document root path since
+version 2.2.0 -- in contrast to versions earlier than 2.2.0, which does resolve symlinks.
+So for example, suppose that your DocumentRoot points to '/home/www/example.com', which in
+turn is a symlink to '/webapps/example.com/public'. In versions earlier than 2.2.0, Phusion
+Passenger will check whether '/webapps/example.com/config/environment.rb' exists because it
+resolves all symlinks. Phusion Passenger 2.2.0 and later however will check for
+'/home/www/config/environment.rb'. This file of course doesn't exist, and as a result Phusion
+Passenger will not activate itself for this virtual host, and you'll most likely see an Apache
+mod_dirindex directory listing.
+
+If you're running into this problem, you can explicitly tell Phusion Passenger what the
+correct application root is through the &lt;&lt;PassengerAppRoot,PassengerAppRoot&gt;&gt; configuration directive.
+
+Autodetection of Rack applications happens through the same mechanism, exception that
+Phusion Passenger will look for 'config.ru' instead of 'config/environment.rb'.
+
+
 include::users_guide_snippets/appendix_a_about.txt[]
 
 include::users_guide_snippets/appendix_b_terminology.txt[]</diff>
      <filename>doc/Users guide Apache.txt</filename>
    </modified>
    <modified>
      <diff>@@ -272,7 +272,7 @@ There are two ways to restart the application:
 1. By restarting Nginx.
 2. By creating or modifying the file 'tmp/restart.txt' in the Rails
    application's &lt;&lt;application_root,root folder&gt;&gt;. Phusion Passenger will
-   automatically restart the application.
+   automatically restart the application during the next request.
 
 For example, to restart our example MyCook application, we type this in the
 command line:
@@ -280,6 +280,11 @@ command line:
 touch /webapps/mycook/tmp/restart.txt
 -------------------------------------------
 
+Please note that, unlike earlier versions of Phusion Passenger, 'restart.txt'
+is not automatically deleted. Phusion Passenger checks whether the timestamp
+of this file has changed in order to determine whether the application should
+be restarted.
+
 === Migrations ===
 
 Phusion Passenger is not related to Ruby on Rails migrations in any way. To
@@ -346,7 +351,7 @@ $ some_awesome_editor config.ru
 ...type in some source code...
 $ cat config.ru
 app = proc do |env|
-    return [200, { &quot;Content-Type&quot; =&gt; &quot;text/html&quot; }, &quot;hello &lt;b&gt;world&lt;/b&gt;&quot;]
+    [200, { &quot;Content-Type&quot; =&gt; &quot;text/html&quot; }, [&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;]]
 end
 run app
 -------------------------------------------</diff>
      <filename>doc/Users guide Nginx.txt</filename>
    </modified>
    <modified>
      <diff>@@ -41,7 +41,7 @@ require 'rubygems'
 require 'merb-core'
 
 Merb::Config.setup(
-  :merb_root   =&gt; File.expand_path(File.dirname(__FILE__)),
+  :merb_root   =&gt; ::File.expand_path(::File.dirname(__FILE__)),
   :environment =&gt; ENV['RACK_ENV']
 )
 Merb.environment = Merb::Config[:environment]
@@ -65,13 +65,7 @@ run Ramaze::Adapter::Base
 ------------------------------------------------------
 require 'rubygems'
 require 'sinatra'
-
-root_dir = File.dirname(__FILE__)
-
-set :environment, ENV['RACK_ENV'].to_sym
-set :root,        root_dir
-set :app_file,    File.join(root_dir, 'app.rb')
-disable :run
+require 'app.rb'
 
 run Sinatra::Application
 ------------------------------------------------------</diff>
      <filename>doc/users_guide_snippets/rackup_specifications.txt</filename>
    </modified>
    <modified>
      <diff>@@ -24,7 +24,7 @@
  */
 #include &quot;Bucket.h&quot;
 
-using namespace Passenger;
+namespace Passenger {
 
 static void bucket_destroy(void *data);
 static apr_status_t bucket_read(apr_bucket *a, const char **str, apr_size_t *len, apr_read_type_e block);
@@ -42,7 +42,24 @@ static const apr_bucket_type_t apr_bucket_type_passenger_pipe = {
 
 struct BucketData {
 	Application::SessionPtr session;
-	apr_file_t *pipe;
+	PassengerBucketStatePtr state;
+	int stream;
+	
+	~BucketData() {
+		/* The session here is an ApplicationPoolServer::RemoteSession.
+		 * The only reason why its destructor might fail is when sending
+		 * the 'close' command failed. We don't care about that, and we
+		 * don't want C++ exceptions to propagate onto a C stack (this
+		 * bucket is probably used by Apache's bucket brigade code, which
+		 * is written in C), so we ignore all errors in the session's
+		 * destructor.
+		 */
+		try {
+			session.reset();
+		} catch (const SystemException &amp;e) {
+			// Do nothing.
+		}
+	}
 };
 
 static void
@@ -55,15 +72,13 @@ bucket_destroy(void *data) {
 
 static apr_status_t
 bucket_read(apr_bucket *bucket, const char **str, apr_size_t *len, apr_read_type_e block) {
-	apr_file_t *pipe;
 	char *buf;
-	apr_status_t ret;
-
-	BucketData *data = (BucketData *) bucket-&gt;data;
-	pipe = data-&gt;pipe;
-
+	ssize_t ret;
+	BucketData *data;
+	
+	data = (BucketData *) bucket-&gt;data;
 	*str = NULL;
-	*len = APR_BUCKET_BUFF_SIZE;
+	*len = 0;
 	
 	if (block == APR_NONBLOCK_READ) {
 		/*
@@ -85,55 +100,74 @@ bucket_read(apr_bucket *bucket, const char **str, apr_size_t *len, apr_read_type
 		return APR_EAGAIN;
 	}
 	
-	buf = (char *) apr_bucket_alloc(*len, bucket-&gt;list); // TODO: check for failure?
-
-	do {
-		ret = apr_file_read(pipe, buf, len);
-	} while (APR_STATUS_IS_EAGAIN(ret));
-
-	if (ret != APR_SUCCESS &amp;&amp; ret != APR_EOF) {
-		// ... we might want to set an error flag here ...
-		delete data;
-		bucket-&gt;data = NULL;
-		apr_bucket_free(buf);
-		return ret;
+	buf = (char *) apr_bucket_alloc(APR_BUCKET_BUFF_SIZE, bucket-&gt;list);
+	if (buf == NULL) {
+		return APR_ENOMEM;
 	}
-	/*
-	 * If there's more to read we have to keep the rest of the pipe
-	 * for later.
-	 */
-	if (*len &gt; 0) {
+	
+	do {
+		ret = read(data-&gt;stream, buf, APR_BUCKET_BUFF_SIZE);
+	} while (ret == -1 &amp;&amp; errno == EINTR);
+	
+	if (ret &gt; 0) {
 		apr_bucket_heap *h;
 		
+		data-&gt;state-&gt;bytesRead += ret;
+		
 		*str = buf;
+		*len = ret;
 		bucket-&gt;data = NULL;
 		
-		/* Change the current bucket to refer to what we read */
+		/* Change the current bucket (which is a Passenger Bucket) into a heap bucket
+		 * that contains the data that we just read. This newly created heap bucket
+		 * will be the first in the bucket list.
+		 */
 		bucket = apr_bucket_heap_make(bucket, buf, *len, apr_bucket_free);
 		h = (apr_bucket_heap *) bucket-&gt;data;
 		h-&gt;alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
+		
+		/* And after this newly created bucket we insert a new Passenger Bucket
+		 * which can read the next chunk from the stream.
+		 */
 		APR_BUCKET_INSERT_AFTER(bucket, passenger_bucket_create(
-			data-&gt;session, pipe, bucket-&gt;list));
+			data-&gt;session, data-&gt;state, bucket-&gt;list));
+		
+		/* The newly created Passenger Bucket has a reference to the session
+		 * object, so we can delete data here.
+		 */
 		delete data;
-	} else {
+		
+		return APR_SUCCESS;
+		
+	} else if (ret == 0) {
+		data-&gt;state-&gt;completed = true;
 		delete data;
 		bucket-&gt;data = NULL;
 		
 		apr_bucket_free(buf);
+		
 		bucket = apr_bucket_immortal_make(bucket, &quot;&quot;, 0);
 		*str = (const char *) bucket-&gt;data;
-		// if (rv != APR_EOF) {
-		//     ... we might want to set an error flag here ...
-		// }
+		*len = 0;
+		return APR_SUCCESS;
+		
+	} else /* ret == -1 */ {
+		int e = errno;
+		data-&gt;state-&gt;completed = true;
+		data-&gt;state-&gt;errorCode = e;
+		delete data;
+		bucket-&gt;data = NULL;
+		apr_bucket_free(buf);
+		return APR_FROM_OS_ERROR(e);
 	}
-	return APR_SUCCESS;
 }
 
 static apr_bucket *
-passenger_bucket_make(apr_bucket *bucket, Application::SessionPtr session, apr_file_t *pipe) {
+passenger_bucket_make(apr_bucket *bucket, Application::SessionPtr session, PassengerBucketStatePtr state) {
 	BucketData *data = new BucketData();
 	data-&gt;session  = session;
-	data-&gt;pipe     = pipe;
+	data-&gt;stream   = session-&gt;getStream();
+	data-&gt;state    = state;
 	
 	bucket-&gt;type   = &amp;apr_bucket_type_passenger_pipe;
 	bucket-&gt;length = (apr_size_t)(-1);
@@ -143,13 +177,14 @@ passenger_bucket_make(apr_bucket *bucket, Application::SessionPtr session, apr_f
 }
 
 apr_bucket *
-passenger_bucket_create(Application::SessionPtr session, apr_file_t *pipe, apr_bucket_alloc_t *list) {
+passenger_bucket_create(Application::SessionPtr session, PassengerBucketStatePtr state, apr_bucket_alloc_t *list) {
 	apr_bucket *bucket;
 	
 	bucket = (apr_bucket *) apr_bucket_alloc(sizeof(*bucket), list);
 	APR_BUCKET_INIT(bucket);
 	bucket-&gt;free = apr_bucket_free;
 	bucket-&gt;list = list;
-	return passenger_bucket_make(bucket, session, pipe);
+	return passenger_bucket_make(bucket, session, state);
 }
 
+} // namespace Passenger</diff>
      <filename>ext/apache2/Bucket.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -25,26 +25,63 @@
 #ifndef _PASSENGER_BUCKET_H_
 #define _PASSENGER_BUCKET_H_
 
-/**
- * apr_bucket_pipe closes a pipe's file descriptor when it has reached
- * end-of-stream, but not when an error has occurred. This behavior is
- * undesirable because it can easily cause file descriptor leaks.
- *
- * passenger_bucket is like apr_bucket_pipe, but it also holds a reference to
- * a Session. When a read error has occured or when end-of-stream has been
- * reached, the Session will be dereferenced, so that the underlying file
- * descriptor is closed.
- *
- * passenger_bucket also ignores the APR_NONBLOCK_READ flag because that's
- * known to cause strange I/O problems.
- */
-
+#include &lt;boost/shared_ptr.hpp&gt;
 #include &lt;apr_buckets.h&gt;
 #include &quot;Application.h&quot;
 
+namespace Passenger {
+
+using namespace boost;
+
+struct PassengerBucketState {
+	/** The number of bytes that this PassengerBucket has read so far. */
+	unsigned long bytesRead;
+	
+	/** Whether this PassengerBucket is completed, i.e. no more data
+	 * can be read from the underlying file descriptor. When true,
+	 * this can either mean that EOF has been reached, or that an I/O
+	 * error occured. Use errorCode to check whether an error occurred.
+	 */
+	bool completed;
+	
+	/** When completed is true, errorCode contains the errno value of
+	 * the last read() call.
+	 *
+	 * A value of 0 means that no error occured.
+	 */
+	int errorCode;
+	
+	PassengerBucketState() {
+		bytesRead = 0;
+		completed = false;
+		errorCode = 0;
+	}
+};
+
+typedef shared_ptr&lt;PassengerBucketState&gt; PassengerBucketStatePtr;
+
+/**
+ * We used to use an apr_bucket_pipe for forwarding the backend process's
+ * response to the HTTP client. However, apr_bucket_pipe has a number of
+ * issues:
+ * - It closes the pipe's file descriptor when it has reached
+ *   end-of-stream, but not when an error has occurred. This behavior is
+ *   undesirable because it can easily cause file descriptor leaks.
+ * - It does weird non-blocking-I/O related things which can cause it
+ *   to read less data than can actually be read.
+ *
+ * PassengerBucket is like apr_bucket_pipe, but:
+ * - It also holds a reference to a Session. When a read error has occured
+ *   or when end-of-stream has been reached, the Session will be
+ *   dereferenced, so that the underlying file descriptor is closed.
+ * - It ignores the APR_NONBLOCK_READ flag because that's known to cause
+ *   strange I/O problems.
+ * - It can store its current state in a PassengerBucketState data structure.
+ */
 apr_bucket *passenger_bucket_create(Passenger::Application::SessionPtr session,
-                                    apr_file_t *pipe,
+                                    PassengerBucketStatePtr state,
                                     apr_bucket_alloc_t *list);
 
-#endif /* _PASSENGER_BUCKET_H_ */
+} // namespace Passenger
 
+#endif /* _PASSENGER_BUCKET_H_ */</diff>
      <filename>ext/apache2/Bucket.h</filename>
    </modified>
    <modified>
      <diff>@@ -87,6 +87,7 @@ passenger_config_create_dir(apr_pool_t *p, char *dirspec) {
 	config-&gt;statThrottleRate = 0;
 	config-&gt;statThrottleRateSpecified = false;
 	config-&gt;restartDir = NULL;
+	config-&gt;uploadBufferDir = NULL;
 	/*************************************/
 	return config;
 }
@@ -127,6 +128,7 @@ passenger_config_merge_dir(apr_pool_t *p, void *basev, void *addv) {
 	config-&gt;statThrottleRate = (add-&gt;statThrottleRateSpecified) ? add-&gt;statThrottleRate : base-&gt;statThrottleRate;
 	config-&gt;statThrottleRateSpecified = base-&gt;statThrottleRateSpecified || add-&gt;statThrottleRateSpecified;
 	config-&gt;restartDir = (add-&gt;restartDir == NULL) ? base-&gt;restartDir : add-&gt;restartDir;
+	config-&gt;uploadBufferDir = (add-&gt;uploadBufferDir == NULL) ? base-&gt;uploadBufferDir : add-&gt;uploadBufferDir;
 	/*************************************/
 	return config;
 }
@@ -401,6 +403,13 @@ cmd_passenger_app_root(cmd_parms *cmd, void *pcfg, const char *arg) {
 	return NULL;
 }
 
+static const char *
+cmd_passenger_upload_buffer_dir(cmd_parms *cmd, void *pcfg, const char *arg) {
+	DirConfig *config = (DirConfig *) pcfg;
+	config-&gt;uploadBufferDir = arg;
+	return NULL;
+}
+
 
 /*************************************************
  * Rails-specific settings
@@ -622,6 +631,12 @@ const command_rec passenger_commands[] = {
 		NULL,
 		OR_OPTIONS | ACCESS_CONF | RSRC_CONF,
 		&quot;The application's root directory.&quot;),
+	AP_INIT_TAKE1(&quot;PassengerUploadBufferDir&quot;,
+		(Take1Func) cmd_passenger_upload_buffer_dir,
+		NULL,
+		OR_OPTIONS,
+		&quot;The directory in which upload buffer files should be placed.&quot;),
+	/*****************************/
 
 	// Rails-specific settings.
 	AP_INIT_TAKE1(&quot;RailsBaseURI&quot;,</diff>
      <filename>ext/apache2/Configuration.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -25,8 +25,10 @@
 #ifndef _PASSENGER_CONFIGURATION_H_
 #define _PASSENGER_CONFIGURATION_H_
 
-#include &quot;Utils.h&quot;
-#include &quot;MessageChannel.h&quot;
+#ifdef __cplusplus
+	#include &quot;Utils.h&quot;
+	#include &quot;MessageChannel.h&quot;
+#endif
 
 /* The APR headers must come after the Passenger headers. See Hooks.cpp
  * to learn why.
@@ -153,6 +155,13 @@
 			 */
 			const char *restartDir;
 			
+			/**
+			 * The directory in which Passenger should place upload buffer
+			 * files. NULL means that the default directory should be used.
+			 */
+			const char *uploadBufferDir;
+			
+			/*************************************/
 			/*************************************/
 			
 			bool isEnabled() const {
@@ -244,6 +253,14 @@
 				}
 			}
 			
+			string getUploadBufferDir() const {
+				if (uploadBufferDir != NULL) {
+					return uploadBufferDir;
+				} else {
+					return getPassengerTempDir() + &quot;/webserver_private&quot;;
+				}
+			}
+			
 			/*************************************/
 		};
 		</diff>
      <filename>ext/apache2/Configuration.h</filename>
    </modified>
    <modified>
      <diff>@@ -31,7 +31,7 @@
 
 #include &lt;oxt/backtrace.hpp&gt;
 
-#include &quot;CachedFileStat.h&quot;
+#include &quot;CachedFileStat.hpp&quot;
 #include &quot;Configuration.h&quot;
 #include &quot;Utils.h&quot;
 
@@ -66,7 +66,7 @@ public:
 private:
 	DirConfig *config;
 	request_rec *r;
-	CachedMultiFileStat *mstat;
+	CachedFileStat *cstat;
 	unsigned int throttleRate;
 	bool baseURIKnown;
 	const char *baseURI;
@@ -91,16 +91,16 @@ public:
 	/**
 	 * Create a new DirectoryMapper object.
 	 *
-	 * @param mstat A CachedMultiFileStat object used for statting files.
-	 * @param throttleRate A throttling rate for mstat.
+	 * @param cstat A CachedFileStat object used for statting files.
+	 * @param throttleRate A throttling rate for cstat.
 	 * @warning Do not use this object after the destruction of &lt;tt&gt;r&lt;/tt&gt;,
-	 *          &lt;tt&gt;config&lt;/tt&gt; or &lt;tt&gt;mstat&lt;/tt&gt;.
+	 *          &lt;tt&gt;config&lt;/tt&gt; or &lt;tt&gt;cstat&lt;/tt&gt;.
 	 */
 	DirectoryMapper(request_rec *r, DirConfig *config,
-	                CachedMultiFileStat *mstat, unsigned int throttleRate) {
+	                CachedFileStat *cstat, unsigned int throttleRate) {
 		this-&gt;r = r;
 		this-&gt;config = config;
-		this-&gt;mstat = mstat;
+		this-&gt;cstat = cstat;
 		this-&gt;throttleRate = throttleRate;
 		appType = NONE;
 		baseURIKnown = false;
@@ -171,7 +171,7 @@ public:
 		
 		UPDATE_TRACE_POINT();
 		if (shouldAutoDetectRails()
-		 &amp;&amp; verifyRailsDir(config-&gt;getAppRoot(ap_document_root(r)), mstat, throttleRate)) {
+		 &amp;&amp; verifyRailsDir(config-&gt;getAppRoot(ap_document_root(r)), cstat, throttleRate)) {
 			baseURIKnown = true;
 			baseURI = &quot;/&quot;;
 			appType = RAILS;
@@ -180,7 +180,7 @@ public:
 		
 		UPDATE_TRACE_POINT();
 		if (shouldAutoDetectRack()
-		 &amp;&amp; verifyRackDir(config-&gt;getAppRoot(ap_document_root(r)), mstat, throttleRate)) {
+		 &amp;&amp; verifyRackDir(config-&gt;getAppRoot(ap_document_root(r)), cstat, throttleRate)) {
 			baseURIKnown = true;
 			baseURI = &quot;/&quot;;
 			appType = RACK;
@@ -189,7 +189,7 @@ public:
 		
 		UPDATE_TRACE_POINT();
 		if (shouldAutoDetectWSGI()
-		 &amp;&amp; verifyWSGIDir(config-&gt;getAppRoot(ap_document_root(r)), mstat, throttleRate)) {
+		 &amp;&amp; verifyWSGIDir(config-&gt;getAppRoot(ap_document_root(r)), cstat, throttleRate)) {
 			baseURIKnown = true;
 			baseURI = &quot;/&quot;;
 			appType = WSGI;</diff>
      <filename>ext/apache2/DirectoryMapper.h</filename>
    </modified>
    <modified>
      <diff>@@ -39,6 +39,7 @@
 #include &quot;ApplicationPoolServer.h&quot;
 #include &quot;MessageChannel.h&quot;
 #include &quot;DirectoryMapper.h&quot;
+#include &quot;Timer.h&quot;
 #include &quot;Version.h&quot;
 
 /* The Apache/APR headers *must* come after the Boost headers, otherwise
@@ -138,12 +139,44 @@ private:
 		}
 	};
 	
+	/**
+	 * A StringListCreator which returns a list of environment variable
+	 * names and values, as found in r-&gt;subprocess_env.
+	 */
+	class EnvironmentVariablesStringListCreator: public StringListCreator {
+	private:
+		request_rec *r;
+	public:
+		EnvironmentVariablesStringListCreator(request_rec *r) {
+			this-&gt;r = r;
+		}
+		
+		virtual const StringListPtr getItems() const {
+			const apr_array_header_t *env_arr;
+			apr_table_entry_t *env_entries;
+			StringListPtr result = ptr(new StringList());
+			
+			// Some standard CGI headers.
+			result-&gt;push_back(&quot;SERVER_SOFTWARE&quot;);
+			result-&gt;push_back(ap_get_server_version());
+			
+			// Subprocess environment variables.
+			env_arr = apr_table_elts(r-&gt;subprocess_env);
+			env_entries = (apr_table_entry_t *) env_arr-&gt;elts;
+			for (int i = 0; i &lt; env_arr-&gt;nelts; ++i) {
+				result-&gt;push_back(env_entries[i].key);
+				result-&gt;push_back(env_entries[i].val);
+			}
+			return result;
+		}
+	};
+	
 	enum Threeway { YES, NO, UNKNOWN };
 
 	ApplicationPoolServerPtr applicationPoolServer;
 	thread_specific_ptr&lt;ApplicationPoolPtr&gt; threadSpecificApplicationPool;
 	Threeway m_hasModRewrite, m_hasModDir, m_hasModAutoIndex;
-	CachedMultiFileStat *mstat;
+	CachedFileStat cstat;
 	
 	inline DirConfig *getDirConfig(request_rec *r) {
 		return (DirConfig *) ap_get_module_config(r-&gt;per_dir_config, &amp;passenger_module);
@@ -249,7 +282,7 @@ private:
 	 * (C) r-&gt;filename already exists.
 	 * (D) There is a page cache file for the URI.
 	 *
-	 * - If A is not true, or if B is not true, or if C is true, then won't do anything.
+	 * - If A is not true, or if B is not true, or if C is true, then don't do anything.
 	 *   Passenger will be disabled during the rest of this request.
 	 * - If D is true, then we first transform r-&gt;filename to the page cache file's
 	 *   filename, and then we let Apache serve it statically.
@@ -261,7 +294,7 @@ private:
 	 */
 	bool prepareRequest(request_rec *r, DirConfig *config, const char *filename, bool coreModuleWillBeRun = false) {
 		TRACE_POINT();
-		DirectoryMapper mapper(r, config, mstat, config-&gt;getStatThrottleRate());
+		DirectoryMapper mapper(r, config, &amp;cstat, config-&gt;getStatThrottleRate());
 		try {
 			if (mapper.getBaseURI() == NULL) {
 				// (B) is not true.
@@ -378,6 +411,9 @@ private:
 			void *pointer;
 		} u;
 		
+		/* Did an error occur in any of the previous hook methods during
+		 * this request? If so, show the error and stop here.
+		 */
 		u.errorReport = 0;
 		apr_pool_userdata_get(&amp;u.pointer, &quot;Phusion Passenger: error report&quot;, r-&gt;pool);
 		if (u.errorReport != 0) {
@@ -411,8 +447,6 @@ private:
 		try {
 			this_thread::disable_interruption di;
 			this_thread::disable_syscall_interruption dsi;
-			apr_bucket_brigade *bb;
-			apr_bucket *b;
 			Application::SessionPtr session;
 			bool expectingUploadData;
 			shared_ptr&lt;BufferedUpload&gt; uploadData;
@@ -421,10 +455,11 @@ private:
 			expectingUploadData = ap_should_client_block(r);
 			contentLength = lookupHeader(r, &quot;Content-Length&quot;);
 			
-			// If the HTTP upload data is larger than a threshold, or if the HTTP
-			// client sent HTTP upload data using the &quot;chunked&quot; transfer encoding
-			// (which implies Content-Length == NULL), then buffer the upload
-			// data into a tempfile.
+			/* If the HTTP upload data is larger than a threshold, or if the HTTP
+			 * client sent HTTP upload data using the &quot;chunked&quot; transfer encoding
+			 * (which implies Content-Length == NULL), then buffer the upload
+			 * data into a tempfile.
+			 */
 			if (expectingUploadData &amp;&amp; (
 			          contentLength == NULL ||
 			          atol(contentLength) &gt; UPLOAD_ACCELERATION_THRESHOLD
@@ -434,10 +469,11 @@ private:
 			}
 			
 			if (expectingUploadData &amp;&amp; contentLength == NULL) {
-				// In case of &quot;chunked&quot; transfer encoding, we'll set the
-				// Content-Length header to the length of the received upload
-				// data. Rails requires this header for its HTTP upload data
-				// multipart parsing process.
+				/* In case of &quot;chunked&quot; transfer encoding, we'll set the
+				 * Content-Length header to the length of the received upload
+				 * data. Rails requires this header for its HTTP upload data
+				 * multipart parsing process.
+				 */
 				apr_table_set(r-&gt;headers_in, &quot;Content-Length&quot;,
 					toString(ftell(uploadData-&gt;handle)).c_str());
 			}
@@ -450,10 +486,8 @@ private:
 			try {
 				ServerConfig *sconfig = getServerConfig(r-&gt;server);
 				string publicDirectory(mapper.getPublicDirectory());
-				string appRoot(config-&gt;getAppRoot(publicDirectory.c_str()));
-				
-				session = getApplicationPool()-&gt;get(PoolOptions(
-					appRoot,
+				PoolOptions options(
+					config-&gt;getAppRoot(publicDirectory.c_str()),
 					true,
 					sconfig-&gt;getDefaultUser(),
 					mapper.getEnvironment(),
@@ -465,8 +499,12 @@ private:
 					config-&gt;getMemoryLimit(),
 					config-&gt;usingGlobalQueue(),
 					config-&gt;getStatThrottleRate(),
-					config-&gt;getRestartDir()
-				));
+					config-&gt;getRestartDir(),
+					mapper.getBaseURI()
+				);
+				options.environmentVariables = ptr(new EnvironmentVariablesStringListCreator(r));
+				
+				session = getApplicationPool()-&gt;get(options);
 				P_TRACE(3, &quot;Forwarding &quot; &lt;&lt; r-&gt;uri &lt;&lt; &quot; to PID &quot; &lt;&lt; session-&gt;getPid());
 			} catch (const SpawnException &amp;e) {
 				r-&gt;status = 500;
@@ -500,34 +538,62 @@ private:
 					sendRequestBody(r, session);
 				}
 			}
-			session-&gt;shutdownWriter();
+			try {
+				session-&gt;shutdownWriter();
+			} catch (const SystemException &amp;e) {
+				// Ignore ENOTCONN. This error occurs for some people
+				// for unknown reasons, but it's harmless.
+				if (e.code() != ENOTCONN) {
+					throw;
+				}
+			}
 			
 			
 			/********** Step 4: forwarding the response from the backend
 			                    process back to the HTTP client **********/
 			
 			UPDATE_TRACE_POINT();
-			apr_file_t *readerPipe = NULL;
-			int reader = session-&gt;getStream();
-			pid_t backendPid = session-&gt;getPid();
-			apr_os_pipe_put(&amp;readerPipe, &amp;reader, r-&gt;pool);
-			apr_file_pipe_timeout_set(readerPipe, r-&gt;server-&gt;timeout);
-
+			apr_bucket_brigade *bb;
+			apr_bucket *b;
+			PassengerBucketStatePtr bucketState;
+			pid_t backendPid;
+			
+			/* Setup the bucket brigade. */
+			bucketState = ptr(new PassengerBucketState());
 			bb = apr_brigade_create(r-&gt;connection-&gt;pool, r-&gt;connection-&gt;bucket_alloc);
-			b = passenger_bucket_create(session, readerPipe, r-&gt;connection-&gt;bucket_alloc);
+			b = passenger_bucket_create(session, bucketState, r-&gt;connection-&gt;bucket_alloc);
+			
+			/* The bucket (b) still has a reference to the session, so the reset()
+			 * call here is guaranteed not to throw any exceptions.
+			 */
+			backendPid = session-&gt;getPid();
 			session.reset();
+			
 			APR_BRIGADE_INSERT_TAIL(bb, b);
 
 			b = apr_bucket_eos_create(r-&gt;connection-&gt;bucket_alloc);
 			APR_BRIGADE_INSERT_TAIL(bb, b);
 
-			// I know the size because I read util_script.c's source. :-(
+			/* Now read the HTTP response header, parse it and fill relevant
+			 * information in our request_rec structure.
+			 */
+			
+			/* I know the required size for backendData because I read
+			 * util_script.c's source. :-(
+			 */
 			char backendData[MAX_STRING_LEN];
+			Timer timer;
 			int result = ap_scan_script_header_err_brigade(r, bb, backendData);
+			
 			if (result == OK) {
 				// The API documentation for ap_scan_script_err_brigade() says it
 				// returns HTTP_OK on success, but it actually returns OK.
 				
+				/* We were able to parse the HTTP response header sent by the
+				 * backend process! Proceed with passing the bucket brigade,
+				 * for forwarding the response body to the HTTP client.
+				 */
+				
 				/* Manually set the Status header because
 				 * ap_scan_script_header_err_brigade() filters it
 				 * out. Some broken HTTP clients depend on the
@@ -540,17 +606,73 @@ private:
 				}
 				apr_table_setn(r-&gt;headers_out, &quot;Status&quot;, r-&gt;status_line);
 				
+				UPDATE_TRACE_POINT();
 				ap_pass_brigade(r-&gt;output_filters, bb);
+				
+				if (r-&gt;connection-&gt;aborted) {
+					P_WARN(&quot;The HTTP client closed the connection before &quot;
+						&quot;the response could be completely sent. As a &quot;
+						&quot;result, you will probably see a 'Broken Pipe' &quot;
+						&quot;error in this log file. Please ignore it, &quot;
+						&quot;this is normal.&quot;);
+				} else if (!bucketState-&gt;completed) {
+					P_WARN(&quot;Apache stopped forwarding the backend's response, &quot;
+						&quot;even though the HTTP client did not close the &quot;
+						&quot;connection. Is this an Apache bug?&quot;);
+				}
+				
 				return OK;
 			} else if (backendData[0] == '\0') {
-				P_ERROR(&quot;Backend process &quot; &lt;&lt; backendPid &lt;&lt;
-					&quot; did not return a valid HTTP response. It returned no data.&quot;);
+				if ((long long) timer.elapsed() &gt;= r-&gt;server-&gt;timeout / 1000) {
+					// Looks like an I/O timeout.
+					P_ERROR(&quot;No data received from &quot; &lt;&lt;
+						&quot;the backend application (process &quot; &lt;&lt;
+						backendPid &lt;&lt; &quot;) within &quot; &lt;&lt;
+						(r-&gt;server-&gt;timeout / 1000) &lt;&lt; &quot; msec. Either &quot; &lt;&lt;
+						&quot;the backend application is frozen, or &quot; &lt;&lt;
+						&quot;your TimeOut value of &quot; &lt;&lt;
+						(r-&gt;server-&gt;timeout / 1000000) &lt;&lt;
+						&quot; seconds is too low. Please check &quot; &lt;&lt;
+						&quot;whether your application is frozen, or &quot; &lt;&lt;
+						&quot;increase the value of the TimeOut &quot; &lt;&lt;
+						&quot;configuration directive.&quot;);
+				} else {
+					P_ERROR(&quot;The backend application (process &quot; &lt;&lt;
+						backendPid &lt;&lt; &quot;) did not send a valid &quot; &lt;&lt;
+						&quot;HTTP response; instead, it sent nothing &quot; &lt;&lt;
+						&quot;at all. It is possible that it has crashed; &quot; &lt;&lt;
+						&quot;please check whether there are crashing &quot; &lt;&lt;
+						&quot;bugs in this application.&quot;);
+				}
 				apr_table_setn(r-&gt;err_headers_out, &quot;Status&quot;, &quot;500 Internal Server Error&quot;);
 				return HTTP_INTERNAL_SERVER_ERROR;
 			} else {
-				P_ERROR(&quot;Backend process &quot; &lt;&lt; backendPid &lt;&lt;
-					&quot; did not return a valid HTTP response. It returned: [&quot; &lt;&lt;
-					backendData &lt;&lt; &quot;]&quot;);
+				if ((long long) timer.elapsed() &gt;= r-&gt;server-&gt;timeout / 1000) {
+					// Looks like an I/O timeout.
+					P_ERROR(&quot;The backend application (process &quot; &lt;&lt;
+						backendPid &lt;&lt; &quot;) hasn't sent a valid &quot; &lt;&lt;
+						&quot;HTTP response within &quot; &lt;&lt;
+						(r-&gt;server-&gt;timeout / 1000) &lt;&lt; &quot; msec. Either &quot; &lt;&lt;
+						&quot;the backend application froze while &quot; &lt;&lt;
+						&quot;sending a response, or &quot; &lt;&lt;
+						&quot;your TimeOut value of &quot; &lt;&lt;
+						(r-&gt;server-&gt;timeout / 1000000) &lt;&lt;
+						&quot; seconds is too low. Please check &quot; &lt;&lt;
+						&quot;whether the application is frozen, or &quot; &lt;&lt;
+						&quot;increase the value of the TimeOut &quot; &lt;&lt;
+						&quot;configuration directive. The application &quot; &lt;&lt;
+						&quot;has sent this data so far: [&quot; &lt;&lt;
+						backendData &lt;&lt; &quot;]&quot;);
+				} else {
+					P_ERROR(&quot;The backend application (process &quot; &lt;&lt;
+						backendPid &lt;&lt; &quot;) didn't send a valid &quot; &lt;&lt;
+						&quot;HTTP response. It might have crashed &quot; &lt;&lt;
+						&quot;during the middle of sending an HTTP &quot; &lt;&lt;
+						&quot;response, so please check whether there &quot; &lt;&lt;
+						&quot;are crashing problems in your application. &quot; &lt;&lt;
+						&quot;This is the data that it sent: [&quot; &lt;&lt;
+						backendData &lt;&lt; &quot;]&quot;);
+				}
 				apr_table_setn(r-&gt;err_headers_out, &quot;Status&quot;, &quot;500 Internal Server Error&quot;);
 				return HTTP_INTERNAL_SERVER_ERROR;
 			}
@@ -668,16 +790,6 @@ private:
 			}
 		}
 		
-		// Add other environment variables.
-		const apr_array_header_t *env_arr;
-		apr_table_entry_t *env;
-		
-		env_arr = apr_table_elts(r-&gt;subprocess_env);
-		env = (apr_table_entry_t*) env_arr-&gt;elts;
-		for (i = 0; i &lt; env_arr-&gt;nelts; ++i) {
-			addHeader(headers, env[i].key, env[i].val);
-		}
-		
 		// Now send the headers.
 		string buffer;
 		
@@ -721,6 +833,169 @@ private:
 		return APR_SUCCESS;
 	}
 	
+	void throwUploadBufferingException(request_rec *r, int code) {
+		DirConfig *config = getDirConfig(r);
+		string message(&quot;An error occured while &quot;
+			&quot;buffering HTTP upload data to &quot;
+			&quot;a temporary file in &quot;);
+		message.append(config-&gt;getUploadBufferDir());
+		
+		switch (code) {
+		case ENOSPC:
+			message.append(&quot;. Disk directory doesn't have enough disk space, &quot;
+				&quot;so please make sure that it has &quot;
+				&quot;enough disk space for buffering file uploads, &quot;
+				&quot;or set the 'PassengerUploadBufferDir' directive &quot;
+				&quot;to a directory that has enough disk space.&quot;);
+			throw RuntimeException(message);
+			break;
+		case EDQUOT:
+			message.append(&quot;. The current Apache worker process (which is &quot;
+				&quot;running as &quot;);
+			message.append(getProcessUsername());
+			message.append(&quot;) cannot write to this directory because of &quot;
+				&quot;disk quota limits. Please make sure that the volume &quot;
+				&quot;that this directory resides on has enough disk space &quot;
+				&quot;quota for the Apache worker process, or set the &quot;
+				&quot;'PassengerUploadBufferDir' directive to a different &quot;
+				&quot;directory that has enough disk space quota.&quot;);
+			throw RuntimeException(message);
+			break;
+		case ENOENT:
+			message.append(&quot;. This directory doesn't exist, so please make &quot;
+				&quot;sure that this directory exists, or set the &quot;
+				&quot;'PassengerUploadBufferDir' directive to a &quot;
+				&quot;directory that exists and can be written to.&quot;);
+			throw RuntimeException(message);
+			break;
+		case EACCES:
+			message.append(&quot;. The current Apache worker process (which is &quot;
+				&quot;running as &quot;);
+			message.append(getProcessUsername());
+			message.append(&quot;) doesn't have permissions to write to this &quot;
+				&quot;directory. Please change the permissions for this &quot;
+				&quot;directory (as well as all parent directories) so that &quot;
+				&quot;it is writable by the Apache worker process, or set &quot;
+				&quot;the 'PassengerUploadBufferDir' directive to a directory &quot;
+				&quot;that Apache can write to.&quot;);
+			throw RuntimeException(message);
+			break;
+		default:
+			throw SystemException(message, code);
+			break;
+		}
+	}
+	
+	/**
+	 * Reads the next chunk of the request body and put it into a buffer.
+	 *
+	 * This is like ap_get_client_block(), but can actually report errors
+	 * in a sane way. ap_get_client_block() tells you that something went
+	 * wrong, but not *what* went wrong.
+	 *
+	 * @param r The current request.
+	 * @param buffer A buffer to put the read data into.
+	 * @param bufsiz The size of the buffer.
+	 * @return The number of bytes read, or 0 on EOF.
+	 * @throws RuntimeException Something non-I/O related went wrong, e.g.
+	 *                          failure to allocate memory and stuff.
+	 * @throws IOException An I/O error occurred while trying to read the
+	 *                     request body data.
+	 */
+	unsigned long readRequestBodyFromApache(request_rec *r, char *buffer, apr_size_t bufsiz) {
+		apr_status_t rv;
+		apr_bucket_brigade *bb;
+		
+		if (r-&gt;remaining &lt; 0 || (!r-&gt;read_chunked &amp;&amp; r-&gt;remaining == 0)) {
+			return 0;
+		}
+
+		bb = apr_brigade_create(r-&gt;pool, r-&gt;connection-&gt;bucket_alloc);
+		if (bb == NULL) {
+			r-&gt;connection-&gt;keepalive = AP_CONN_CLOSE;
+			throw RuntimeException(&quot;An error occurred while receiving HTTP upload data: &quot;
+				&quot;unable to create a bucket brigade. Maybe the system doesn't have &quot;
+				&quot;enough free memory.&quot;);
+		}
+		
+		rv = ap_get_brigade(r-&gt;input_filters, bb, AP_MODE_READBYTES,
+		                    APR_BLOCK_READ, bufsiz);
+			
+		/* We lose the failure code here.  This is why ap_get_client_block should
+		 * not be used.
+		 */
+		if (rv != APR_SUCCESS) {
+			/* if we actually fail here, we want to just return and
+			 * stop trying to read data from the client.
+			 */
+			r-&gt;connection-&gt;keepalive = AP_CONN_CLOSE;
+			apr_brigade_destroy(bb);
+			
+			char buf[150], *errorString, message[1024];
+			errorString = apr_strerror(rv, buf, sizeof(buf));
+			if (errorString != NULL) {
+				snprintf(message, sizeof(message),
+					&quot;An error occurred while receiving HTTP upload data: %s (%d)&quot;,
+					errorString, rv);
+			} else {
+				snprintf(message, sizeof(message),
+					&quot;An error occurred while receiving HTTP upload data: unknown error %d&quot;,
+					rv);
+			}
+			message[sizeof(message) - 1] = '\0';
+			throw RuntimeException(message);
+		}
+		
+		/* If this fails, it means that a filter is written incorrectly and that
+		 * it needs to learn how to properly handle APR_BLOCK_READ requests by
+		 * returning data when requested.
+		 */
+		if (APR_BRIGADE_EMPTY(bb)) {
+			throw RuntimeException(&quot;An error occurred while receiving HTTP upload data: &quot;
+				&quot;the next filter in the input filter chain has &quot;
+				&quot;a bug. Please contact the author who wrote this filter about &quot;
+				&quot;this. This problem is not caused by Phusion Passenger.&quot;);
+		}
+
+		/* Check to see if EOS in the brigade.
+		 *
+		 * If so, we have to leave a nugget for the *next* readRequestBodyFromApache()
+		 * call to return 0.
+		 */
+		if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) {
+			if (r-&gt;read_chunked) {
+				r-&gt;remaining = -1;
+			} else {
+				r-&gt;remaining = 0;
+			}
+		}
+
+		rv = apr_brigade_flatten(bb, buffer, &amp;bufsiz);
+		if (rv != APR_SUCCESS) {
+			apr_brigade_destroy(bb);
+			
+			char buf[150], *errorString, message[1024];
+			errorString = apr_strerror(rv, buf, sizeof(buf));
+			if (errorString != NULL) {
+				snprintf(message, sizeof(message),
+					&quot;An error occurred while receiving HTTP upload data: %s (%d)&quot;,
+					errorString, rv);
+			} else {
+				snprintf(message, sizeof(message),
+					&quot;An error occurred while receiving HTTP upload data: unknown error %d&quot;,
+					rv);
+			}
+			message[sizeof(message) - 1] = '\0';
+			throw IOException(message);
+		}
+		
+		/* XXX yank me? */
+		r-&gt;read_length += bufsiz;
+		
+		apr_brigade_destroy(bb);
+		return bufsiz;
+	}
+	
 	/**
 	 * Receive the HTTP upload data and buffer it into a BufferedUpload temp file.
 	 *
@@ -729,44 +1004,35 @@ private:
 	 *                      to check whether the HTTP client has sent complete upload
 	 *                      data. NULL indicates that there is no Content-Length header,
 	 *                      i.e. that the HTTP client used chunked transfer encoding.
+	 * @throws RuntimeException
+	 * @throws SystemException
+	 * @throws IOException
 	 */
 	shared_ptr&lt;BufferedUpload&gt; receiveRequestBody(request_rec *r, const char *contentLength) {
 		TRACE_POINT();
-		shared_ptr&lt;BufferedUpload&gt; tempFile(new BufferedUpload());
+		DirConfig *config = getDirConfig(r);
+		shared_ptr&lt;BufferedUpload&gt; tempFile;
+		try {
+			tempFile.reset(new BufferedUpload(config-&gt;getUploadBufferDir()));
+		} catch (const SystemException &amp;e) {
+			throwUploadBufferingException(r, e.code());
+		}
+		
 		char buf[1024 * 32];
 		apr_off_t len;
 		size_t total_written = 0;
 		
-		while ((len = ap_get_client_block(r, buf, sizeof(buf))) &gt; 0) {
+		while ((len = readRequestBodyFromApache(r, buf, sizeof(buf))) &gt; 0) {
 			size_t written = 0;
 			do {
 				size_t ret = fwrite(buf, 1, len - written, tempFile-&gt;handle);
 				if (ret &lt;= 0 || fflush(tempFile-&gt;handle) == EOF) {
-					int e = errno;
-					string message(&quot;An error occured while &quot;
-						&quot;buffering HTTP upload data to &quot;
-						&quot;a temporary file in &quot;);
-					message.append(BufferedUpload::getDir());
-					if (e == ENOSPC) {
-						message.append(&quot;. Please make sure &quot;
-							&quot;that this directory has &quot;
-							&quot;enough disk space for &quot;
-							&quot;buffering file uploads, &quot;
-							&quot;or set the 'PassengerTempDir' &quot;
-							&quot;directive to a directory &quot;
-							&quot;that has enough disk space.&quot;);
-						throw RuntimeException(message);
-					} else {
-						throw SystemException(message, e);
-					}
+					throwUploadBufferingException(r, errno);
 				}
 				written += ret;
 			} while (written &lt; (size_t) len);
 			total_written += written;
 		}
-		if (len == -1) {
-			throw IOException(&quot;An error occurred while receiving HTTP upload data.&quot;);
-		}
 		
 		if (contentLength != NULL &amp;&amp; ftell(tempFile-&gt;handle) != atol(contentLength)) {
 			throw IOException(&quot;The HTTP client sent incomplete upload data.&quot;);
@@ -791,23 +1057,20 @@ private:
 		char buf[1024 * 32];
 		apr_off_t len;
 
-		while ((len = ap_get_client_block(r, buf, sizeof(buf))) &gt; 0) {
+		while ((len = readRequestBodyFromApache(r, buf, sizeof(buf))) &gt; 0) {
 			session-&gt;sendBodyBlock(buf, len);
 		}
-		if (len == -1) {
-			throw IOException(&quot;An error occurred while receiving HTTP upload data.&quot;);
-		}
 	}
 
 public:
-	Hooks(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) {
+	Hooks(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s)
+	    : cstat(1024) {
 		passenger_config_merge_all_servers(pconf, s);
 		ServerConfig *config = getServerConfig(s);
 		Passenger::setLogLevel(config-&gt;logLevel);
 		m_hasModRewrite = UNKNOWN;
 		m_hasModDir = UNKNOWN;
 		m_hasModAutoIndex = UNKNOWN;
-		mstat = cached_multi_file_stat_new(1024);
 		
 		P_DEBUG(&quot;Initializing Phusion Passenger...&quot;);
 		ap_add_version_component(pconf, &quot;Phusion_Passenger/&quot; PASSENGER_VERSION);
@@ -823,11 +1086,9 @@ public:
 		 * of the process in which the Hooks constructor was called for
 		 * the second time.
 		 */
-		unsetenv(&quot;TMPDIR&quot;);
 		createPassengerTempDir(config-&gt;getTempDir(), config-&gt;userSwitching,
 			config-&gt;getDefaultUser(), unixd_config.user_id,
 			unixd_config.group_id);
-		setenv(&quot;TMPDIR&quot;, (getPassengerTempDir() + &quot;/var&quot;).c_str(), 1);
 		
 		ruby = (config-&gt;ruby != NULL) ? config-&gt;ruby : DEFAULT_RUBY_COMMAND;
 		if (config-&gt;userSwitching) {
@@ -873,7 +1134,6 @@ public:
 	}
 	
 	~Hooks() {
-		cached_multi_file_stat_free(mstat);
 		removeDirTree(getPassengerTempDir().c_str());
 	}
 	</diff>
      <filename>ext/apache2/Hooks.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -135,7 +135,10 @@ struct apply
 
 ///// iteration, depth == 1
 
-#elif BOOST_PP_ITERATION_DEPTH() == 1
+// For gcc 4.4 compatability, we must include the
+// BOOST_PP_ITERATION_DEPTH test inside an #else clause.
+#else // BOOST_PP_IS_ITERATING
+#if BOOST_PP_ITERATION_DEPTH() == 1
 
 #   define i_ BOOST_PP_FRAME_ITERATION(1)
 
@@ -222,4 +225,5 @@ struct apply_chooser&lt;i_&gt;
 
 #   undef i_
 
+#endif // BOOST_PP_ITERATION_DEPTH()
 #endif // BOOST_PP_IS_ITERATING</diff>
      <filename>ext/boost/mpl/apply.hpp</filename>
    </modified>
    <modified>
      <diff>@@ -78,7 +78,10 @@ namespace boost { namespace mpl {
 
 ///// iteration, depth == 1
 
-#elif BOOST_PP_ITERATION_DEPTH() == 1
+// For gcc 4.4 compatability, we must include the
+// BOOST_PP_ITERATION_DEPTH test inside an #else clause.
+#else
+#if BOOST_PP_ITERATION_DEPTH() == 1
 
 #   define i_ BOOST_PP_FRAME_ITERATION(1)
 
@@ -196,5 +199,5 @@ struct BOOST_PP_CAT(apply_wrap_impl,i_)&lt;
 };
 
 #   undef j_
-
+#endif // BOOST_PP_ITERATION_DEPTH()
 #endif // BOOST_PP_IS_ITERATING</diff>
      <filename>ext/boost/mpl/apply_wrap.hpp</filename>
    </modified>
    <modified>
      <diff>@@ -227,7 +227,10 @@ BOOST_MPL_AUX_NA_SPEC2(2, 3, lambda)
 
 ///// iteration, depth == 1
 
-#elif BOOST_PP_ITERATION_DEPTH() == 1
+// For gcc 4.4 compatability, we must include the
+// BOOST_PP_ITERATION_DEPTH test inside an #else clause.
+#else // BOOST_PP_IS_ITERATING
+#if BOOST_PP_ITERATION_DEPTH() == 1
 #define i_ BOOST_PP_FRAME_ITERATION(1)
 
 #if i_ &gt; 0
@@ -347,4 +350,5 @@ struct lambda&lt;
 };
 
 #undef i_
+#endif // BOOST_PP_ITERATION_DEPTH()
 #endif // BOOST_PP_IS_ITERATING</diff>
      <filename>ext/boost/mpl/aux_/full_lambda.hpp</filename>
    </modified>
    <modified>
      <diff>@@ -361,7 +361,10 @@ BOOST_MPL_AUX_TEMPLATE_ARITY_SPEC(
 
 ///// iteration, depth == 1
 
-#elif BOOST_PP_ITERATION_DEPTH() == 1
+// For gcc 4.4 compatability, we must include the
+// BOOST_PP_ITERATION_DEPTH test inside an #else clause.
+#else // BOOST_PP_IS_ITERATING
+#if BOOST_PP_ITERATION_DEPTH() == 1
 
 #   define i_ BOOST_PP_FRAME_ITERATION(1)
 
@@ -544,4 +547,5 @@ struct bind_chooser&lt;i_&gt;
 #   endif
 #   undef j_
 
+#endif // BOOST_PP_ITERATION_DEPTH()
 #endif // BOOST_PP_IS_ITERATING</diff>
      <filename>ext/boost/mpl/bind.hpp</filename>
    </modified>
    <modified>
      <diff>@@ -283,7 +283,7 @@ private:
 			if (fd != -1) {
 				int ret = syscalls::shutdown(fd, SHUT_RD);
 				if (ret == -1) {
-					throw SystemException(&quot;Cannot shutdown the writer stream&quot;,
+					throw SystemException(&quot;Cannot shutdown the reader stream&quot;,
 						errno);
 				}
 			}
@@ -304,11 +304,16 @@ private:
 			TRACE_POINT();
 			if (fd != -1) {
 				int ret = syscalls::close(fd);
+				fd = -1;
 				if (ret == -1) {
-					throw SystemException(&quot;Cannot close the session stream&quot;,
-						errno);
+					if (errno == EIO) {
+						throw SystemException(&quot;A write operation on the session stream failed&quot;,
+							errno);
+					} else {
+						throw SystemException(&quot;Cannot close the session stream&quot;,
+							errno);
+					}
 				}
-				fd = -1;
 			}
 		}
 		
@@ -329,33 +334,7 @@ private:
 	
 	SessionPtr connectToUnixServer(const function&lt;void()&gt; &amp;closeCallback) const {
 		TRACE_POINT();
-		int fd, ret;
-		
-		do {
-			fd = socket(PF_UNIX, SOCK_STREAM, 0);
-		} while (fd == -1 &amp;&amp; errno == EINTR);
-		if (fd == -1) {
-			throw SystemException(&quot;Cannot create a new unconnected Unix socket&quot;, errno);
-		}
-		
-		struct sockaddr_un addr;
-		addr.sun_family = AF_UNIX;
-		strncpy(addr.sun_path, listenSocketName.c_str(), sizeof(addr.sun_path));
-		addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
-		do {
-			ret = ::connect(fd, (const sockaddr *) &amp;addr, sizeof(addr));
-		} while (ret == -1 &amp;&amp; errno == EINTR);
-		if (ret == -1) {
-			int e = errno;
-			string message(&quot;Cannot connect to Unix socket '&quot;);
-			message.append(listenSocketName);
-			message.append(&quot;'&quot;);
-			do {
-				ret = close(fd);
-			} while (ret == -1 &amp;&amp; errno == EINTR);
-			throw SystemException(message, e);
-		}
-		
+		int fd = Passenger::connectToUnixServer(listenSocketName.c_str());
 		return ptr(new StandardSession(pid, closeCallback, fd));
 	}
 	
@@ -510,6 +489,7 @@ public:
 	 * @post this-&gt;getSessions() == old-&gt;getSessions() + 1
 	 * @throws SystemException Something went wrong during the connection process.
 	 * @throws IOException Something went wrong during the connection process.
+	 * @throws boost::thread_interrupted
 	 */
 	SessionPtr connect(const function&lt;void()&gt; &amp;closeCallback) const {
 		TRACE_POINT();</diff>
      <filename>ext/common/Application.h</filename>
    </modified>
    <modified>
      <diff>@@ -126,7 +126,8 @@ public:
 	 * @throw BusyException The application pool is too busy right now, and cannot
 	 *       satisfy the request. One should either abort, or try again later.
 	 * @throw IOException Something else went wrong.
-	 * @throw thread_interrupted
+	 * @throw boost::thread_interrupted
+	 * @throws Anything thrown by options.environmentVariables-&gt;getItems().
 	 * @note Applications are uniquely identified with the application root
 	 *       string. So although &lt;tt&gt;appRoot&lt;/tt&gt; does not have to be absolute, it
 	 *       should be. If one calls &lt;tt&gt;get(&quot;/home/foo&quot;)&lt;/tt&gt; and</diff>
      <filename>ext/common/ApplicationPool.h</filename>
    </modified>
    <modified>
      <diff>@@ -216,7 +216,7 @@ private:
 			if (fd != -1) {
 				int ret = syscalls::shutdown(fd, SHUT_RD);
 				if (ret == -1) {
-					throw SystemException(&quot;Cannot shutdown the writer stream&quot;,
+					throw SystemException(&quot;Cannot shutdown the reader stream&quot;,
 						errno);
 				}
 			}
@@ -235,11 +235,16 @@ private:
 		virtual void closeStream() {
 			if (fd != -1) {
 				int ret = syscalls::close(fd);
+				fd = -1;
 				if (ret == -1) {
-					throw SystemException(&quot;Cannot close the session stream&quot;,
-						errno);
+					if (errno == EIO) {
+						throw SystemException(&quot;A write operation on the session stream failed&quot;,
+							errno);
+					} else {
+						throw SystemException(&quot;Cannot close the session stream&quot;,
+							errno);
+					}
 				}
-				fd = -1;
 			}
 		}
 		
@@ -380,12 +385,18 @@ private:
 			vector&lt;string&gt; args;
 			int stream;
 			bool result;
+			bool serverMightNeedEnvironmentVariables = true;
 			
+			/* Send a 'get' request to the ApplicationPool server.
+			 * For efficiency reasons, we do not send the data for
+			 * options.environmentVariables over the wire yet until
+			 * it's necessary.
+			 */
 			try {
 				vector&lt;string&gt; args;
 				
 				args.push_back(&quot;get&quot;);
-				options.toVector(args);
+				options.toVector(args, false);
 				channel.write(args);
 			} catch (const SystemException &amp;e) {
 				UPDATE_TRACE_POINT();
@@ -395,22 +406,52 @@ private:
 				message.append(e.brief());
 				throw SystemException(message, e.code());
 			}
-			try {
-				UPDATE_TRACE_POINT();
-				result = channel.read(args);
-			} catch (const SystemException &amp;e) {
-				UPDATE_TRACE_POINT();
-				data-&gt;disconnect();
-				throw SystemException(&quot;Could not read a message from &quot;
-					&quot;the ApplicationPool server&quot;, e.code());
-			}
-			if (!result) {
-				UPDATE_TRACE_POINT();
-				data-&gt;disconnect();
-				throw IOException(&quot;The ApplicationPool server unexpectedly &quot;
-					&quot;closed the connection while we're reading a response &quot;
-					&quot;for the 'get' command.&quot;);
+			
+			/* The first few replies from the server might be for requesting
+			 * environment variables in the pool options object, so keep handling
+			 * these requests until we receive a different reply.
+			 */
+			while (serverMightNeedEnvironmentVariables) {
+				try {
+					result = channel.read(args);
+				} catch (const SystemException &amp;e) {
+					UPDATE_TRACE_POINT();
+					data-&gt;disconnect();
+					throw SystemException(&quot;Could not read a response from &quot;
+						&quot;the ApplicationPool server for the 'get' command&quot;, e.code());
+				}
+				if (!result) {
+					UPDATE_TRACE_POINT();
+					data-&gt;disconnect();
+					throw IOException(&quot;The ApplicationPool server unexpectedly &quot;
+						&quot;closed the connection while we're reading a response &quot;
+						&quot;for the 'get' command.&quot;);
+				}
+				
+				if (args[0] == &quot;getEnvironmentVariables&quot;) {
+					try {
+						if (options.environmentVariables) {
+							UPDATE_TRACE_POINT();
+							channel.writeScalar(options.serializeEnvironmentVariables());
+						} else {
+							UPDATE_TRACE_POINT();
+							channel.writeScalar(&quot;&quot;);
+						}
+					} catch (const SystemException &amp;e) {
+						data-&gt;disconnect();
+						throw SystemException(&quot;Could not send a response &quot;
+							&quot;for the 'getEnvironmentVariables' request &quot;
+							&quot;to the ApplicationPool server&quot;,
+							e.code());
+					}
+				} else {
+					serverMightNeedEnvironmentVariables = false;
+				}
 			}
+			
+			/* We've now received a reply other than &quot;getEnvironmentVariables&quot;.
+			 * Handle this...
+			 */
 			if (args[0] == &quot;ok&quot;) {
 				UPDATE_TRACE_POINT();
 				pid_t pid = (pid_t) atol(args[1]);</diff>
      <filename>ext/common/ApplicationPoolServer.h</filename>
    </modified>
    <modified>
      <diff>@@ -288,6 +288,131 @@ private:
 	/** Last used session ID. */
 	int lastSessionID;
 	
+	class ClientCommunicationError: public oxt::tracable_exception {
+	private:
+		string briefMessage;
+		string systemMessage;
+		string fullMessage;
+		int m_code;
+	public:
+		/**
+		 * Create a new ClientCommunicationError.
+		 *
+		 * @param briefMessage A brief message describing the error.
+		 * @param errorCode An optional error code, i.e. the value of errno right after the error occured, if applicable.
+		 * @note A system description of the error will be appended to the given message.
+		 *    For example, if &lt;tt&gt;errorCode&lt;/tt&gt; is &lt;tt&gt;EBADF&lt;/tt&gt;, and &lt;tt&gt;briefMessage&lt;/tt&gt;
+		 *    is &lt;em&gt;&quot;Something happened&quot;&lt;/em&gt;, then what() will return &lt;em&gt;&quot;Something happened: Bad
+		 *    file descriptor (10)&quot;&lt;/em&gt; (if 10 is the number for EBADF).
+		 * @post code() == errorCode
+		 * @post brief() == briefMessage
+		 */
+		ClientCommunicationError(const string &amp;briefMessage, int errorCode = -1) {
+			if (errorCode != -1) {
+				stringstream str;
+				
+				str &lt;&lt; strerror(errorCode) &lt;&lt; &quot; (&quot; &lt;&lt; errorCode &lt;&lt; &quot;)&quot;;
+				systemMessage = str.str();
+			}
+			setBriefMessage(briefMessage);
+			m_code = errorCode;
+		}
+
+		virtual ~ClientCommunicationError() throw() {}
+
+		virtual const char *what() const throw() {
+			return fullMessage.c_str();
+		}
+
+		void setBriefMessage(const string &amp;message) {
+			briefMessage = message;
+			if (systemMessage.empty()) {
+				fullMessage = briefMessage;
+			} else {
+				fullMessage = briefMessage + &quot;: &quot; + systemMessage;
+			}
+		}
+
+		/**
+		 * The value of &lt;tt&gt;errno&lt;/tt&gt; at the time the error occured.
+		 */
+		int code() const throw() {
+			return m_code;
+		}
+
+		/**
+		 * Returns a brief version of the exception message. This message does
+		 * not include the system error description, and is equivalent to the
+		 * value of the &lt;tt&gt;message&lt;/tt&gt; parameter as passed to the constructor.
+		 */
+		string brief() const throw() {
+			return briefMessage;
+		}
+
+		/**
+		 * Returns the system's error message. This message contains both the
+		 * content of &lt;tt&gt;strerror(errno)&lt;/tt&gt; and the errno number itself.
+		 *
+		 * @post if code() == -1: result.empty()
+		 */
+		string sys() const throw() {
+			return systemMessage;
+		}
+	};
+	
+	/**
+	 * A StringListCreator which fetches its items from the client.
+	 * Used as an optimization for ApplicationPoolServer::Client.get():
+	 * environment variables are only serialized by the client process
+	 * if a new backend process is being spawned.
+	 */
+	class EnvironmentVariablesFetcher: public StringListCreator {
+	private:
+		MessageChannel &amp;channel;
+		PoolOptions &amp;options;
+	public:
+		EnvironmentVariablesFetcher(MessageChannel &amp;theChannel, PoolOptions &amp;theOptions)
+			: channel(theChannel),
+			  options(theOptions)
+		{ }
+		
+		/**
+		 * @throws ClientCommunicationError
+		 */
+		virtual const StringListPtr getItems() const {
+			string data;
+			
+			/* If an I/O error occurred while communicating with the client,
+			 * then throw a ClientCommunicationException, which will bubble
+			 * all the way up to the thread main loop, where the connection
+			 * with the client will be broken.
+			 */
+			try {
+				channel.write(&quot;getEnvironmentVariables&quot;, NULL);
+			} catch (const SystemException &amp;e) {
+				throw ClientCommunicationError(
+					&quot;Unable to send a 'getEnvironmentVariables' request to the client&quot;,
+					e.code());
+			}
+			try {
+				if (!channel.readScalar(data)) {
+					throw ClientCommunicationError(&quot;Unable to read a reply from the client for the 'getEnvironmentVariables' request.&quot;);
+				}
+			} catch (const SystemException &amp;e) {
+				throw ClientCommunicationError(
+					&quot;Unable to read a reply from the client for the 'getEnvironmentVariables' request&quot;,
+					e.code());
+			}
+			
+			if (!data.empty()) {
+				SimpleStringListCreator list(data);
+				return list.getItems();
+			} else {
+				return ptr(new StringList());
+			}
+		}
+	};
+	
 	void processGet(const vector&lt;string&gt; &amp;args) {
 		TRACE_POINT();
 		Application::SessionPtr session;
@@ -295,6 +420,7 @@ private:
 		
 		try {
 			PoolOptions options(args, 1);
+			options.environmentVariables = ptr(new EnvironmentVariablesFetcher(channel, options));
 			session = server.pool-&gt;get(options);
 			sessions[lastSessionID] = session;
 			lastSessionID++;
@@ -331,12 +457,14 @@ private:
 				UPDATE_TRACE_POINT();
 				channel.write(&quot;ok&quot;, toString(session-&gt;getPid()).c_str(),
 					toString(lastSessionID - 1).c_str(), NULL);
+				UPDATE_TRACE_POINT();
 				channel.writeFileDescriptor(session-&gt;getStream());
-				session-&gt;closeStream();
-			} catch (const exception &amp;) {
 				UPDATE_TRACE_POINT();
-				P_TRACE(3, &quot;Client &quot; &lt;&lt; this &lt;&lt; &quot;: something went wrong &quot;
-					&quot;while sending 'ok' back to the client.&quot;);
+				session-&gt;closeStream();
+			} catch (const exception &amp;e) {
+				P_TRACE(3, &quot;Client &quot; &lt;&lt; this &lt;&lt; &quot;: could not send &quot;
+					&quot;'ok' back to the ApplicationPool client: &quot; &lt;&lt;
+					e.what());
 				sessions.erase(lastSessionID - 1);
 				throw;
 			}</diff>
      <filename>ext/common/ApplicationPoolServerExecutable.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -33,6 +33,8 @@
 
 #include &lt;string&gt;
 #include &lt;sys/types.h&gt;
+#include &lt;sys/socket.h&gt;
+#include &lt;sys/un.h&gt;
 #include &lt;sys/stat.h&gt;
 #include &lt;cstdio&gt;
 #include &lt;unistd.h&gt;
@@ -40,6 +42,7 @@
 
 #include &quot;StandardApplicationPool.h&quot;
 #include &quot;MessageChannel.h&quot;
+#include &quot;Exceptions.h&quot;
 #include &quot;Logging.h&quot;
 #include &quot;Utils.h&quot;
 
@@ -51,72 +54,176 @@ using namespace std;
 
 /**
  * An ApplicationPoolStatusReporter allows commandline admin tools to inspect
- * the status of a StandardApplicationPool. It does so by creating a FIFO
- * in the Passenger temp folder.
+ * the status of a StandardApplicationPool. It does so by creating a Unix socket
+ * in the Passenger temp folder, which tools can connect to to query for
+ * information.
  *
- * An ApplicationPoolStatusReporter creates a background thread, which
- * continuously sends new information through the FIFO. This thread will
- * be automatically cleaned up upon destroying the ApplicationPoolStatusReporter
- * object.
+ * An ApplicationPoolStatusReporter creates a background thread for handling
+ * connections on the socket. This thread will be automatically cleaned up upon
+ * destroying the ApplicationPoolStatusReporter object.
  */
 class ApplicationPoolStatusReporter {
 private:
+	/**
+	 * Wrapper class around a file descriptor integer, for RAII behavior.
+	 *
+	 * A FileDescriptor object behaves just like an int, so that you can pass it to
+	 * system calls such as read(). It performs reference counting. When the last
+	 * copy of a FileDescriptor has been destroyed, the underlying file descriptor
+	 * will be automatically closed.
+	 */
+	class FileDescriptor {
+	private:
+		struct SharedData {
+			int fd;
+
+			/**
+			 * Constructor to assign this file descriptor's handle.
+			 */
+			SharedData(int fd) {
+				this-&gt;fd = fd;
+			}
+
+			/**
+			 * Attempts to close this file descriptor. When created on the stack,
+			 * this destructor will automatically be invoked as a result of C++
+			 * semantics when exiting the scope this object was created in. This
+			 * ensures that stack created objects with destructors like these will
+			 * de-allocate their resources upon leaving their corresponding scope.
+			 * This pattern is also known Resource Acquisition Is Initialization (RAII).
+			 *
+			 * @throws SystemException File descriptor could not be closed.
+			 */
+			~SharedData() {
+				this_thread::disable_syscall_interruption dsi;
+				if (syscalls::close(fd) == -1) {
+					throw SystemException(&quot;Cannot close file descriptor&quot;, errno);
+				}
+			}
+		};
+
+		/* Shared pointer for reference counting on this file descriptor */
+		shared_ptr&lt;SharedData&gt; data;
+
+	public:
+		FileDescriptor() {
+			// Do nothing.
+		}
+
+		/**
+		 * Creates a new FileDescriptor instance with the given fd as a handle.
+		 */
+		FileDescriptor(int fd) {
+			data = ptr(new SharedData(fd));
+		}
+
+		/**
+		 * Overloads the integer cast operator so that it will return the file
+		 * descriptor handle as an integer.
+		 *
+		 * @return This file descriptor's handle as an integer.
+		 */
+		operator int () const {
+			return data-&gt;fd;
+		}
+	};
+	
 	/** The application pool to monitor. */
 	StandardApplicationPoolPtr pool;
 	
-	/** The FIFO's filename. */
+	/** The socket's filename. */
 	char filename[PATH_MAX];
 	
-	/** The background thread. */
-	oxt::thread *thr;
+	/** The socket's file descriptor. */
+	int serverFd;
+	
+	/** The main thread. */
+	oxt::thread *mainThread;
 	
-	void threadMain() {
+	/** The mutex which protects the 'threads' member. */
+	boost::mutex threadsLock;
+	
+	/** A map which maps a client file descriptor to its handling thread. */
+	map&lt; int, shared_ptr&lt;oxt::thread&gt; &gt; threads;
+	
+	void writeScalarAndIgnoreErrors(MessageChannel &amp;channel, const string &amp;data) {
+		try {
+			channel.writeScalar(data);
+		} catch (const SystemException &amp;e) {
+			// Don't care about write errors.
+		}
+	}
+	
+	void mainThreadFunction() {
 		TRACE_POINT();
 		try {
 			while (!this_thread::interruption_requested()) {
-				struct stat buf;
-				int ret;
-				
 				UPDATE_TRACE_POINT();
-				do {
-					ret = stat(filename, &amp;buf);
-				} while (ret == -1 &amp;&amp; errno == EINTR);
-				if (ret == -1 || !S_ISFIFO(buf.st_mode)) {
-					// Something bad happened with the status
-					// report FIFO, so we bail out.
-					break;
-				}
+				sockaddr_un addr;
+				socklen_t addr_len = sizeof(addr);
 				
-				UPDATE_TRACE_POINT();
-				FILE *f = syscalls::fopen(filename, &quot;w&quot;);
-				if (f == NULL) {
+				FileDescriptor fd(syscalls::accept(serverFd, (struct sockaddr *) &amp;addr, &amp;addr_len));
+				if (fd == -1) {
 					int e = errno;
-					P_ERROR(&quot;Cannot open status report FIFO &quot; &lt;&lt;
-						filename &lt;&lt; &quot;: &quot; &lt;&lt;
+					P_ERROR(&quot;Cannot accept new client on status reporter socket: &quot; &lt;&lt;
 						strerror(e) &lt;&lt; &quot; (&quot; &lt;&lt; e &lt;&lt; &quot;)&quot;);
 					break;
 				}
 				
-				UPDATE_TRACE_POINT();
-				MessageChannel channel(fileno(f));
-				string report;
-				report.append(&quot;----------- Backtraces -----------\n&quot;);
-				report.append(oxt::thread::all_backtraces());
-				report.append(&quot;\n\n&quot;);
-				report.append(pool-&gt;toString());
+				boost::lock_guard&lt;boost::mutex&gt; l(threadsLock);
+				this_thread::disable_syscall_interruption dsi;
+				this_thread::disable_interruption di;
+				shared_ptr&lt;oxt::thread&gt; thread(new oxt::thread(
+					bind(&amp;ApplicationPoolStatusReporter::clientThreadFunction, this, fd),
+					&quot;Status reporter client thread &quot; + toString(fd),
+					1024 * 128
+				));
+				threads[fd] = thread;
+			}
+		} catch (const boost::thread_interrupted &amp;) {
+			P_TRACE(2, &quot;Status reporter main thread interrupted.&quot;);
+		} catch (const exception &amp;e) {
+			P_ERROR(&quot;Error in status reporter main thread: &quot; &lt;&lt; e.what());
+		}
+	}
+	
+	void clientThreadFunction(FileDescriptor fd) {
+		TRACE_POINT();
+		MessageChannel channel(fd);
+		
+		try {
+			while (!this_thread::interruption_requested()) {
+				vector&lt;string&gt; args;
 				
 				UPDATE_TRACE_POINT();
-				try {
-					channel.writeScalar(report);
-					channel.writeScalar(pool-&gt;toXml());
-				} catch (...) {
-					// Ignore write errors.
+				if (!channel.read(args) || args.size() &lt; 1) {
+					break;
+				}
+				
+				if (args[0] == &quot;backtraces&quot;) {
+					UPDATE_TRACE_POINT();
+					writeScalarAndIgnoreErrors(channel, oxt::thread::all_backtraces());
+				} else if (args[0] == &quot;status&quot;) {
+					UPDATE_TRACE_POINT();
+					writeScalarAndIgnoreErrors(channel, pool-&gt;toString());
+				} else if (args[0] == &quot;status_xml&quot;) {
+					UPDATE_TRACE_POINT();
+					writeScalarAndIgnoreErrors(channel, pool-&gt;toXml());
+				} else {
+					P_ERROR(&quot;Error in status reporter client thread: unknown query '&quot; &lt;&lt;
+						args[0] &lt;&lt; &quot;'.&quot;);
 				}
-				syscalls::fclose(f);
 			}
 		} catch (const boost::thread_interrupted &amp;) {
-			P_TRACE(2, &quot;Status report thread interrupted.&quot;);
+			P_TRACE(2, &quot;Status reporter client thread &quot; &lt;&lt; fd &lt;&lt; &quot; interrupted.&quot;);
+		} catch (const exception &amp;e) {
+			P_ERROR(&quot;Error in status reporter client thread: &quot; &lt;&lt; e.what());
 		}
+		
+		boost::lock_guard&lt;boost::mutex&gt; l(threadsLock);
+		this_thread::disable_syscall_interruption dsi;
+		this_thread::disable_interruption di;
+		threads.erase(fd);
 	}
 
 public:
@@ -134,9 +241,11 @@ public:
 	 *            -1 if the current user should be set as owner.
 	 * @param gid The GID of the user who should own the FIFO file, or
 	 *            -1 if the current group should be set as group.
-	 * @throws SystemException An error occurred while creating the FIFO.
+	 * @throws RuntimeException An error occurred.
+	 * @throws SystemException An error occurred while creating the server socket.
 	 * @throws boost::thread_resource_error Something went wrong during
 	 *     creation of the thread.
+	 * @throws boost::thread_interrupted A system call has been interrupted.
 	 */
 	ApplicationPoolStatusReporter(StandardApplicationPoolPtr &amp;pool,
 	                              bool userSwitching,
@@ -149,27 +258,17 @@ public:
 		createPassengerTempDir(getSystemTempDir(), userSwitching,
 			&quot;nobody&quot;, geteuid(), getegid());
 		
-		snprintf(filename, sizeof(filename) - 1, &quot;%s/info/status.fifo&quot;,
+		snprintf(filename, sizeof(filename) - 1, &quot;%s/info/status.socket&quot;,
 			getPassengerTempDir().c_str());
 		filename[PATH_MAX - 1] = '\0';
+		serverFd = createUnixServer(filename, 10);
 		
-		do {
-			ret = mkfifo(filename, permissions);
-		} while (ret == -1 &amp;&amp; errno == EINTR);
-		if (ret == -1 &amp;&amp; errno != EEXIST) {
-			int e = errno;
-			string message(&quot;Cannot create FIFO '&quot;);
-			message.append(filename);
-			message.append(&quot;'&quot;);
-			throw SystemException(message, e);
-		}
-		
-		// It seems that the permissions passed to mkfifo()
-		// aren't respected, so here we chmod the file.
+		/* Set the socket file's permissions... */
 		do {
 			ret = chmod(filename, permissions);
 		} while (ret == -1 &amp;&amp; errno == EINTR);
 		
+		/* ...and ownership. */
 		if (uid != (uid_t) -1 &amp;&amp; gid != (gid_t) -1) {
 			do {
 				ret = chown(filename, uid, gid);
@@ -179,31 +278,56 @@ public:
 				char message[1024];
 				
 				snprintf(message, sizeof(message) - 1,
-					&quot;Cannot set the FIFO file '%s' its owner to %lld and group to %lld&quot;,
+					&quot;Cannot set the owner for socket file '%s' to %lld and its group to %lld&quot;,
 					filename, (long long) uid, (long long) gid);
 				message[sizeof(message) - 1] = '\0';
+				do {
+					ret = close(serverFd);
+				} while (ret == -1 &amp;&amp; errno == EINTR);
 				throw SystemException(message, e);
 			}
 		}
 		
-		thr = new oxt::thread(
-			bind(&amp;ApplicationPoolStatusReporter::threadMain, this),
-			&quot;Status report thread&quot;,
-			1024 * 128
-		);
+		try {
+			mainThread = new oxt::thread(
+				bind(&amp;ApplicationPoolStatusReporter::mainThreadFunction, this),
+				&quot;Status reporter main thread&quot;,
+				1024 * 128
+			);
+		} catch (...) {
+			do {
+				ret = close(serverFd);
+			} while (ret == -1 &amp;&amp; errno == EINTR);
+			throw;
+		}
 	}
 	
 	~ApplicationPoolStatusReporter() {
 		this_thread::disable_syscall_interruption dsi;
 		this_thread::disable_interruption di;
-		
-		thr-&gt;interrupt_and_join();
-		delete thr;
-		
 		int ret;
+		
 		do {
 			ret = unlink(filename);
 		} while (ret == -1 &amp;&amp; errno == EINTR);
+		
+		mainThread-&gt;interrupt_and_join();
+		delete mainThread;
+		
+		do {
+			ret = close(serverFd);
+		} while (ret == -1 &amp;&amp; errno == EINTR);
+		
+		/* We make a copy of the data structure here to avoid deadlocks. */
+		map&lt; int, shared_ptr&lt;oxt::thread&gt; &gt; threadsCopy;
+		{
+			boost::lock_guard&lt;boost::mutex&gt; l(threadsLock);
+			threadsCopy = threads;
+		}
+		map&lt; int, shared_ptr&lt;oxt::thread&gt; &gt;::iterator it;
+		for (it = threadsCopy.begin(); it != threadsCopy.end(); it++) {
+			it-&gt;second-&gt;interrupt_and_join();
+		}
 	}
 };
 </diff>
      <filename>ext/common/ApplicationPoolStatusReporter.h</filename>
    </modified>
    <modified>
      <diff>@@ -23,97 +23,40 @@
  *  THE SOFTWARE.
  */
 #include &quot;CachedFileStat.h&quot;
+#include &quot;CachedFileStat.hpp&quot;
 
-#include &lt;map&gt;
-#include &lt;list&gt;
+extern &quot;C&quot; {
 
-#include &lt;boost/shared_ptr.hpp&gt;
-#include &lt;boost/thread.hpp&gt;
-
-using namespace std;
-using namespace boost;
-using namespace Passenger;
-
-// CachedMultiFileStat is written in C++, with a C wrapper API around it.
-// I'm not going to reinvent my own linked list and hash table in C when I
-// can just use the STL.
-struct CachedMultiFileStat {
-	struct Item {
-		string filename;
-		CachedFileStat cstat;
-		
-		Item(const string &amp;filename)
-			: cstat(filename)
-		{
-			this-&gt;filename = filename;
-		}
-	};
-	
-	typedef shared_ptr&lt;Item&gt; ItemPtr;
-	typedef list&lt;ItemPtr&gt; ItemList;
-	typedef map&lt;string, ItemList::iterator&gt; ItemMap;
+struct CachedFileStat {
+	Passenger::CachedFileStat cfs;
 	
-	unsigned int maxSize;
-	ItemList items;
-	ItemMap cache;
-	boost::mutex lock;
-	
-	CachedMultiFileStat(unsigned int maxSize) {
-		this-&gt;maxSize = maxSize;
-	}
-	
-	int stat(const string &amp;filename, struct stat *buf, unsigned int throttleRate = 0) {
-		boost::unique_lock&lt;boost::mutex&gt; l(lock);
-		ItemMap::iterator it(cache.find(filename));
-		ItemPtr item;
-		int ret;
-		
-		if (it == cache.end()) {
-			// Filename not in cache.
-			// If cache is full, remove the least recently used
-			// cache entry.
-			if (cache.size() == maxSize) {
-				ItemList::iterator listEnd(items.end());
-				listEnd--;
-				string filename((*listEnd)-&gt;filename);
-				items.pop_back();
-				cache.erase(filename);
-			}
-			
-			// Add to cache as most recently used.
-			item = ItemPtr(new Item(filename));
-			items.push_front(item);
-			cache[filename] = items.begin();
-		} else {
-			// Cache hit.
-			item = *it-&gt;second;
-			
-			// Mark this cache item as most recently used.
-			items.erase(it-&gt;second);
-			items.push_front(item);
-			cache[filename] = items.begin();
-		}
-		ret = item-&gt;cstat.refresh(throttleRate);
-		*buf = item-&gt;cstat.info;
-		return ret;
-	}
+	CachedFileStat(unsigned int maxSize): cfs(maxSize) { }
 };
 
-CachedMultiFileStat *
-cached_multi_file_stat_new(unsigned int max_size) {
-	return new CachedMultiFileStat(max_size);
+CachedFileStat *
+cached_file_stat_new(unsigned int max_size) {
+	return new CachedFileStat(max_size);
 }
 
 void
-cached_multi_file_stat_free(CachedMultiFileStat *mstat) {
-	delete mstat;
+cached_file_stat_free(CachedFileStat *cstat) {
+	delete cstat;
 }
 
 int
-cached_multi_file_stat_perform(CachedMultiFileStat *mstat,
-                               const char *filename,
-                               struct stat *buf,
-                               unsigned int throttle_rate)
-{
-	return mstat-&gt;stat(filename, buf, throttle_rate);
+cached_file_stat_perform(CachedFileStat *cstat,
+                         const char *filename,
+                         struct stat *buf,
+                         unsigned int throttle_rate) {
+	try {
+		return cstat-&gt;cfs.stat(filename, buf, throttle_rate);
+	} catch (const Passenger::TimeRetrievalException &amp;e) {
+		errno = e.code();
+		return -1;
+	} catch (const boost::thread_interrupted &amp;) {
+		errno = EINTR;
+		return -1;
+	}
 }
+
+} // extern &quot;C&quot;</diff>
      <filename>ext/common/CachedFileStat.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -25,141 +25,27 @@
 #ifndef _PASSENGER_CACHED_FILE_STAT_H_
 #define _PASSENGER_CACHED_FILE_STAT_H_
 
-#include &lt;sys/types.h&gt;
 #include &lt;sys/stat.h&gt;
-#include &lt;unistd.h&gt;
-#include &lt;time.h&gt;
 
 #ifdef __cplusplus
+extern &quot;C&quot; {
+#endif
 
-#include &lt;errno.h&gt;
-#include &lt;string&gt;
-#include &lt;oxt/system_calls.hpp&gt;
-
-#include &quot;SystemTime.h&quot;
-
-namespace Passenger {
-
-using namespace std;
-using namespace oxt;
-
-/**
- * CachedFileStat allows one to stat() a file at a throttled rate, in order
- * to minimize stress on the filesystem. It does this by caching the old stat
- * data for a specified amount of time.
- */
-class CachedFileStat {
-private:
-	/** The last return value of stat(). */
-	int last_result;
-
-	/** The errno set by the last stat() call. */
-	int last_errno;
-
-	/** The filename of the file to stat. */
-	string filename;
-
-	/** The last time a stat() was performed. */
-	time_t last_time;
-	
-	/**
-	 * Checks whether &lt;em&gt;interval&lt;/em&gt; seconds have elapsed since &lt;em&gt;begin&lt;/em&gt;
-	 * The current time is returned via the &lt;tt&gt;currentTime&lt;/tt&gt; argument,
-	 * so that the caller doesn't have to call time() again if it needs the current
-	 * time.
-	 *
-	 * @pre begin &lt;= time(NULL)
-	 * @return Whether &lt;tt&gt;interval&lt;/tt&gt; seconds have elapsed since &lt;tt&gt;begin&lt;/tt&gt;.
-	 * @throws SystemException Something went wrong while retrieving the time.
-	 * @throws boost::thread_interrupted
-	 */
-	bool expired(time_t begin, unsigned int interval, time_t &amp;currentTime) {
-		currentTime = SystemTime::get();
-		return (unsigned int) (currentTime - begin) &gt;= interval;
-	}
-
-public:
-	/** The cached stat info. */
-	struct stat info;
-	
-	/**
-	 * Creates a new CachedFileStat object. The file will not be
-	 * stat()ted until you call refresh().
-	 *
-	 * @param filename The file to stat.
-	 */
-	CachedFileStat(const string &amp;filename) {
-		memset(&amp;info, 0, sizeof(struct stat));
-		last_result = -1;
-		last_errno = 0;
-		this-&gt;filename = filename;
-		last_time = 0;
-	}
-	
-	/**
-	 * Re-stat() the file, if necessary. If &lt;tt&gt;throttleRate&lt;/tt&gt; seconds have
-	 * passed since the last time stat() was called, then the file will be
-	 * re-stat()ted.
-	 *
-	 * The stat information, which may either be the result of a new stat() call
-	 * or just the old cached information, is be available in the &lt;tt&gt;info&lt;/tt&gt;
-	 * member.
-	 *
-	 * @return 0 if the stat() call succeeded or if no stat() was performed,
-	 *         -1 if something went wrong while statting the file. In the latter
-	 *         case, &lt;tt&gt;errno&lt;/tt&gt; will be populated with an appropriate error code.
-	 * @throws SystemException Something went wrong while retrieving the
-	 *         system time. stat() errors will &lt;em&gt;not&lt;/em&gt; result in SystemException
-	 *         being thrown.
-	 * @throws boost::thread_interrupted
-	 */
-	int refresh(unsigned int throttleRate) {
-		time_t currentTime;
-		
-		if (expired(last_time, throttleRate, currentTime)) {
-			last_result = syscalls::stat(filename.c_str(), &amp;info);
-			last_errno = errno;
-			last_time = currentTime;
-			return last_result;
-		} else {
-			errno = last_errno;
-			return last_result;
-		}
-	}
-};
 
-} // namespace Passenger
+/** C bindings for Passenger::CachedFileStat. */
 
-#endif /* __cplusplus */
+typedef struct CachedFileStat CachedFileStat;
 
+CachedFileStat *cached_file_stat_new(unsigned int max_size);
+void cached_file_stat_free(CachedFileStat *cstat);
+int  cached_file_stat_perform(CachedFileStat *cstat,
+                              const char *filename,
+                              struct stat *buf,
+                              unsigned int throttle_rate);
 
-#ifdef __cplusplus
-	extern &quot;C&quot; {
-#endif
-
-/**
- * CachedMultiFileStat allows one to stat() files at a throttled rate, in order
- * to minimize stress on the filesystem. It does this by caching the old stat
- * data for a specified amount of time.
- *
- * Unlike CachedFileStat, which can only stat() one specific file per
- * CachedFileStat object, CachedMultiFileStat can stat() any file. The
- * number of cached stat() information is limited by the given cache size.
- *
- * This class is fully thread-safe.
- */
-typedef struct CachedMultiFileStat CachedMultiFileStat;
-
-CachedMultiFileStat *cached_multi_file_stat_new(unsigned int max_size);
-void cached_multi_file_stat_free(CachedMultiFileStat *mstat);
-int  cached_multi_file_stat_perform(CachedMultiFileStat *mstat,
-                                    const char *filename,
-                                    struct stat *buf,
-                                    unsigned int throttle_rate);
 
 #ifdef __cplusplus
-	}
+}
 #endif
 
 #endif /* _PASSENGER_CACHED_FILE_STAT_H_ */
-</diff>
      <filename>ext/common/CachedFileStat.h</filename>
    </modified>
    <modified>
      <diff>@@ -137,6 +137,19 @@ public:
 };
 
 /**
+ * Unable to retrieve the system time using &lt;tt&gt;time()&lt;/tt&gt;.
+ *
+ * @ingroup Exceptions
+ */
+class TimeRetrievalException: public SystemException {
+public:
+	TimeRetrievalException(const string &amp;message, int errorCode)
+		: SystemException(message, errorCode)
+		{}
+	virtual ~TimeRetrievalException() throw() {}
+};
+
+/**
  * Represents an error that occured during an I/O operation.
  *
  * @ingroup Exceptions</diff>
      <filename>ext/common/Exceptions.h</filename>
    </modified>
    <modified>
      <diff>@@ -64,8 +64,9 @@ void setDebugFile(const char *logFile = NULL);
 			strftime(datetime_buf, sizeof(datetime_buf), &quot;%F %H:%M:%S&quot;, the_tm); \
 			gettimeofday(&amp;tv, NULL); \
 			sstream &lt;&lt; \
-				&quot;[ pid=&quot; &lt;&lt; getpid() &lt;&lt; &quot; file=&quot; &lt;&lt; __FILE__ &lt;&lt; &quot;:&quot; &lt;&lt; __LINE__ &lt;&lt; \
-				&quot; time=&quot; &lt;&lt; datetime_buf &lt;&lt; &quot;.&quot; &lt;&lt; (tv.tv_usec / 1000) &lt;&lt; &quot; ]:&quot; &lt;&lt; \
+				&quot;[ pid=&quot; &lt;&lt; ((unsigned long) getpid()) &lt;&lt;  \
+				&quot; file=&quot; &lt;&lt; __FILE__ &lt;&lt; &quot;:&quot; &lt;&lt; (unsigned long) __LINE__ &lt;&lt; \
+				&quot; time=&quot; &lt;&lt; datetime_buf &lt;&lt; &quot;.&quot; &lt;&lt; (unsigned long) (tv.tv_usec / 1000) &lt;&lt; &quot; ]:&quot; &lt;&lt; \
 				&quot;\n  &quot; &lt;&lt; expr &lt;&lt; std::endl;	\
 			*stream &lt;&lt; sstream.str();		\
 			stream-&gt;flush();			\</diff>
      <filename>ext/common/Logging.h</filename>
    </modified>
    <modified>
      <diff>@@ -570,11 +570,11 @@ public:
 		ret = syscalls::setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO,
 			&amp;tv, sizeof(tv));
 		#ifndef __SOLARIS__
-		// SO_RCVTIMEO is unimplemented and retuns an error on Solaris
-		// 9 and 10 SPARC.  Seems to work okay without it.
-		if (ret == -1) {
-			throw SystemException(&quot;Cannot set read timeout for socket&quot;, errno);
-		}
+			// SO_RCVTIMEO is unimplemented and returns an error on Solaris
+			// 9 and 10 SPARC.  Seems to work okay without it.
+			if (ret == -1) {
+				throw SystemException(&quot;Cannot set read timeout for socket&quot;, errno);
+			}
 		#endif
 	}
 	
@@ -601,11 +601,11 @@ public:
 		ret = syscalls::setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO,
 			&amp;tv, sizeof(tv));
 		#ifndef __SOLARIS__
-		// SO_SNDTIMEO is unimplemented and returns an error on Solaris
-		// 9 and 10 SPARC.  Seems to work okay without it.
-		if (ret == -1) {
-			throw SystemException(&quot;Cannot set read timeout for socket&quot;, errno);
-		}
+			// SO_SNDTIMEO is unimplemented and returns an error on Solaris
+			// 9 and 10 SPARC.  Seems to work okay without it.
+			if (ret == -1) {
+				throw SystemException(&quot;Cannot set read timeout for socket&quot;, errno);
+			}
 		#endif
 	}
 };</diff>
      <filename>ext/common/MessageChannel.h</filename>
    </modified>
    <modified>
      <diff>@@ -26,7 +26,8 @@
 #define _PASSENGER_SPAWN_OPTIONS_H_
 
 #include &lt;string&gt;
-#include &quot;Utils.h&quot;
+#include &lt;vector&gt;
+#include &quot;StringListCreator.h&quot;
 
 namespace Passenger {
 
@@ -148,6 +149,29 @@ struct PoolOptions {
 	string restartDir;
 	
 	/**
+	 * If a new backend process is started, then the getItems() method
+	 * on this object will be called, which is to return environment
+	 * variables that should be passed to the newly spawned backend process.
+	 * Odd indices in the resulting array contain keys, even indices contain
+	 * the value for the key in the previous index.
+	 *
+	 * May be set to NULL.
+	 *
+	 * @invariant environmentVariables.size() is an even number.
+	 */
+	StringListCreatorPtr environmentVariables;
+	
+	/**
+	 * The base URI on which the application runs. If the application is
+	 * running on the root URI, then this value must be &quot;/&quot;.
+	 *
+	 * @invariant baseURI != &quot;&quot;
+	 */
+	string baseURI;
+	
+	/*********************************/
+	
+	/**
 	 * Creates a new PoolOptions object with the default values filled in.
 	 * One must still set appRoot manually, after having used this constructor.
 	 */
@@ -163,6 +187,7 @@ struct PoolOptions {
 		memoryLimit    = 0;
 		useGlobalQueue = false;
 		statThrottleRate        = 0;
+		baseURI        = &quot;/&quot;;
 	}
 	
 	/**
@@ -180,7 +205,8 @@ struct PoolOptions {
 		unsigned long memoryLimit    = 0,
 		bool useGlobalQueue          = false,
 		unsigned long statThrottleRate = 0,
-		const string &amp;restartDir  = &quot;&quot;
+		const string &amp;restartDir  = &quot;&quot;,
+		const string &amp;baseURI     = &quot;/&quot;
 	) {
 		this-&gt;appRoot        = appRoot;
 		this-&gt;lowerPrivilege = lowerPrivilege;
@@ -195,6 +221,7 @@ struct PoolOptions {
 		this-&gt;useGlobalQueue = useGlobalQueue;
 		this-&gt;statThrottleRate        = statThrottleRate;
 		this-&gt;restartDir     = restartDir;
+		this-&gt;baseURI        = baseURI;
 	}
 	
 	/**
@@ -230,16 +257,24 @@ struct PoolOptions {
 		useGlobalQueue = vec[startIndex + 21] == &quot;true&quot;;
 		statThrottleRate = atol(vec[startIndex + 23]);
 		restartDir     = vec[startIndex + 25];
+		baseURI        = vec[startIndex + 27];
+		if (vec.size() &gt; startIndex + 29) {
+			environmentVariables = ptr(new SimpleStringListCreator(vec[startIndex + 29]));
+		}
 	}
 	
 	/**
 	 * Append the information in this PoolOptions object to the given
 	 * string vector. The resulting array could, for example, be used
 	 * as a message to be sent to the spawn server.
+	 *
+	 * @param vec The vector to store the information in.
+	 * @param storeEnvVars Whether to store environment variable information into vec as well.
+	 * @throws Anything thrown by environmentVariables-&gt;getItems().
 	 */
-	void toVector(vector&lt;string&gt; &amp;vec) const {
-		if (vec.capacity() &lt; vec.size() + 10) {
-			vec.reserve(vec.size() + 10);
+	void toVector(vector&lt;string&gt; &amp;vec, bool storeEnvVars = true) const {
+		if (vec.capacity() &lt; vec.size() + 30) {
+			vec.reserve(vec.size() + 30);
 		}
 		appendKeyValue (vec, &quot;app_root&quot;,        appRoot);
 		appendKeyValue (vec, &quot;lower_privilege&quot;, lowerPrivilege ? &quot;true&quot; : &quot;false&quot;);
@@ -254,6 +289,38 @@ struct PoolOptions {
 		appendKeyValue (vec, &quot;use_global_queue&quot;, useGlobalQueue ? &quot;true&quot; : &quot;false&quot;);
 		appendKeyValue3(vec, &quot;stat_throttle_rate&quot;, statThrottleRate);
 		appendKeyValue (vec, &quot;restart_dir&quot;,     restartDir);
+		appendKeyValue (vec, &quot;base_uri&quot;,        baseURI);
+		if (storeEnvVars) {
+			vec.push_back(&quot;environment_variables&quot;);
+			vec.push_back(serializeEnvironmentVariables());
+		}
+	}
+	
+	/**
+	 * Serializes the items in environmentVariables into a string, which
+	 * can be used to create a SimpleStringListCreator object.
+	 *
+	 * @throws Anything thrown by environmentVariables-&gt;getItems().
+	 */
+	string serializeEnvironmentVariables() const {
+		vector&lt;string&gt;::const_iterator it, end;
+		string result;
+		
+		if (environmentVariables) {
+			result.reserve(1024);
+			
+			StringListPtr items = environmentVariables-&gt;getItems();
+			end = items-&gt;end();
+			
+			for (it = items-&gt;begin(); it != end; it++) {
+				result.append(*it);
+				result.append(1, '\0');
+				it++;
+				result.append(*it);
+				result.append(1, '\0');
+			}
+		}
+		return Base64::encode(result);
 	}
 
 private:</diff>
      <filename>ext/common/PoolOptions.h</filename>
    </modified>
    <modified>
      <diff>@@ -265,15 +265,16 @@ private:
 	 * @param PoolOptions The spawn options to use.
 	 * @return An Application smart pointer, representing the spawned application.
 	 * @throws SpawnException Something went wrong.
+	 * @throws Anything thrown by options.environmentVariables-&gt;getItems().
 	 */
-	ApplicationPtr sendSpawnCommand(const PoolOptions &amp;PoolOptions) {
+	ApplicationPtr sendSpawnCommand(const PoolOptions &amp;options) {
 		TRACE_POINT();
 		vector&lt;string&gt; args;
 		int ownerPipe;
 		
 		try {
 			args.push_back(&quot;spawn_application&quot;);
-			PoolOptions.toVector(args);
+			options.toVector(args);
 			channel.write(args);
 		} catch (const SystemException &amp;e) {
 			throw SpawnException(string(&quot;Could not write 'spawn_application' &quot;
@@ -346,15 +347,16 @@ private:
 				ret = chown(args[1].c_str(), getuid(), getgid());
 			} while (ret == -1 &amp;&amp; errno == EINTR);
 		}
-		return ApplicationPtr(new Application(PoolOptions.appRoot,
+		return ApplicationPtr(new Application(options.appRoot,
 			pid, args[1], args[2], ownerPipe));
 	}
 	
 	/**
 	 * @throws boost::thread_interrupted
+	 * @throws Anything thrown by options.environmentVariables-&gt;getItems().
 	 */
 	ApplicationPtr
-	handleSpawnException(const SpawnException &amp;e, const PoolOptions &amp;PoolOptions) {
+	handleSpawnException(const SpawnException &amp;e, const PoolOptions &amp;options) {
 		TRACE_POINT();
 		bool restarted;
 		try {
@@ -371,7 +373,7 @@ private:
 			restarted = false;
 		}
 		if (restarted) {
-			return sendSpawnCommand(PoolOptions);
+			return sendSpawnCommand(options);
 		} else {
 			throw SpawnException(&quot;The spawn server died unexpectedly, and restarting it failed.&quot;);
 		}
@@ -500,17 +502,18 @@ public:
 	 *         spawned application.
 	 * @throws SpawnException Something went wrong.
 	 * @throws boost::thread_interrupted
+	 * @throws Anything thrown by options.environmentVariables-&gt;getItems().
 	 */
-	ApplicationPtr spawn(const PoolOptions &amp;PoolOptions) {
+	ApplicationPtr spawn(const PoolOptions &amp;options) {
 		TRACE_POINT();
 		boost::mutex::scoped_lock l(lock);
 		try {
-			return sendSpawnCommand(PoolOptions);
+			return sendSpawnCommand(options);
 		} catch (const SpawnException &amp;e) {
 			if (e.hasErrorPage()) {
 				throw;
 			} else {
-				return handleSpawnException(e, PoolOptions);
+				return handleSpawnException(e, options);
 			}
 		}
 	}</diff>
      <filename>ext/common/SpawnManager.h</filename>
    </modified>
    <modified>
      <diff>@@ -52,8 +52,8 @@
 
 #include &quot;ApplicationPool.h&quot;
 #include &quot;Logging.h&quot;
-#include &quot;FileChecker.h&quot;
-#include &quot;CachedFileStat.h&quot;
+#include &quot;FileChangeChecker.h&quot;
+#include &quot;CachedFileStat.hpp&quot;
 #ifdef PASSENGER_USE_DUMMY_SPAWN_MANAGER
 	#include &quot;DummySpawnManager.h&quot;
 #else
@@ -121,25 +121,6 @@ private:
 		AppContainerList instances;
 		unsigned int size;
 		unsigned long maxRequests;
-		FileChecker restartFileChecker;
-		CachedFileStat alwaysRestartFileStatter;
-		
-		Domain(const PoolOptions &amp;options)
-			: restartFileChecker(determineRestartDir(options) + &quot;/restart.txt&quot;),
-			  alwaysRestartFileStatter(determineRestartDir(options) + &quot;/always_restart.txt&quot;)
-		{
-		}
-	
-	private:
-		static string determineRestartDir(const PoolOptions &amp;options) {
-			if (options.restartDir.empty()) {
-				return options.appRoot + &quot;/tmp&quot;;
-			} else if (options.restartDir[0] == '/') {
-				return options.restartDir;
-			} else {
-				return options.appRoot + &quot;/&quot; + options.restartDir;
-			}
-		}
 	};
 	
 	struct AppContainer {
@@ -267,6 +248,8 @@ private:
 	unsigned int maxIdleTime;
 	unsigned int waitingOnGlobalQueue;
 	condition cleanerThreadSleeper;
+	CachedFileStat cstat;
+	FileChangeChecker fileChangeChecker;
 	
 	// Shortcuts for instance variables in SharedData. Saves typing in get().
 	boost::mutex &amp;lock;
@@ -364,12 +347,24 @@ private:
 	/**
 	 * Checks whether the given application domain needs to be restarted.
 	 *
-	 * @throws SystemException Something went wrong while retrieving the system time.
+	 * @throws TimeRetrievalException Something went wrong while retrieving the system time.
 	 * @throws boost::thread_interrupted
 	 */
-	bool needsRestart(const string &amp;appRoot, Domain *domain, const PoolOptions &amp;options) {
-		return domain-&gt;alwaysRestartFileStatter.refresh(options.statThrottleRate) == 0
-		    || domain-&gt;restartFileChecker.changed(options.statThrottleRate);
+	bool needsRestart(const string &amp;appRoot, const PoolOptions &amp;options) {
+		string restartDir;
+		if (options.restartDir.empty()) {
+			restartDir = appRoot + &quot;/tmp&quot;;
+		} else if (options.restartDir[0] == '/') {
+			restartDir = options.restartDir;
+		} else {
+			restartDir = appRoot + &quot;/&quot; + options.restartDir;
+		}
+		
+		string alwaysRestartFile = restartDir + &quot;/always_restart.txt&quot;;
+		string restartFile = restartDir + &quot;/restart.txt&quot;;
+		struct stat buf;
+		return cstat.stat(alwaysRestartFile, &amp;buf, options.statThrottleRate) == 0 ||
+		       fileChangeChecker.changed(restartFile, options.statThrottleRate);
 	}
 	
 	void cleanerThreadMainLoop() {
@@ -430,6 +425,7 @@ private:
 	 * @throws boost::thread_interrupted
 	 * @throws SpawnException
 	 * @throws SystemException
+	 * @throws TimeRetrievalException Something went wrong while retrieving the system time.
 	 */
 	pair&lt;AppContainerPtr, Domain *&gt;
 	spawnOrUseExisting(boost::mutex::scoped_lock &amp;l, const PoolOptions &amp;options) {
@@ -446,21 +442,24 @@ private:
 		try {
 			DomainMap::iterator it(domains.find(appRoot));
 			
-			if (it != domains.end() &amp;&amp; needsRestart(appRoot, it-&gt;second.get(), options)) {
-				AppContainerList::iterator it2;
-				instances = &amp;it-&gt;second-&gt;instances;
-				for (it2 = instances-&gt;begin(); it2 != instances-&gt;end(); it2++) {
-					container = *it2;
-					if (container-&gt;sessions == 0) {
-						inactiveApps.erase(container-&gt;ia_iterator);
-					} else {
-						active--;
+			if (needsRestart(appRoot, options)) {
+				if (it != domains.end()) {
+					AppContainerList::iterator it2;
+					instances = &amp;it-&gt;second-&gt;instances;
+					for (it2 = instances-&gt;begin(); it2 != instances-&gt;end(); it2++) {
+						container = *it2;
+						if (container-&gt;sessions == 0) {
+							inactiveApps.erase(container-&gt;ia_iterator);
+						} else {
+							active--;
+						}
+						it2--;
+						instances-&gt;erase(container-&gt;iterator);
+						count--;
 					}
-					it2--;
-					instances-&gt;erase(container-&gt;iterator);
-					count--;
+					domains.erase(appRoot);
 				}
-				domains.erase(appRoot);
+				P_DEBUG(&quot;Restarting &quot; &lt;&lt; appRoot);
 				spawnManager.reload(appRoot);
 				it = domains.end();
 				activeOrMaxChanged.notify_all();
@@ -548,7 +547,7 @@ private:
 				container-&gt;sessions = 0;
 				it = domains.find(appRoot);
 				if (it == domains.end()) {
-					domain = new Domain(options);
+					domain = new Domain();
 					domain-&gt;size = 1;
 					domain-&gt;maxRequests = options.maxRequests;
 					domains[appRoot] = ptr(domain);</diff>
      <filename>ext/common/StandardApplicationPool.h</filename>
    </modified>
    <modified>
      <diff>@@ -26,6 +26,7 @@
 #define _PASSENGER_STATIC_STRING_H_
 
 #include &lt;string&gt;
+#include &lt;cstring&gt;
 
 namespace Passenger {
 </diff>
      <filename>ext/common/StaticString.h</filename>
    </modified>
    <modified>
      <diff>@@ -49,7 +49,7 @@ public:
 	 * Returns the time since the Epoch, measured in seconds. Or, if a time
 	 * was forced, then the forced time is returned instead.
 	 *
-	 * @throws SystemException Something went wrong while retrieving the time.
+	 * @throws TimeRetrievalException Something went wrong while retrieving the time.
 	 * @throws boost::thread_interrupted
 	 */
 	static time_t get() {
@@ -58,7 +58,8 @@ public:
 		} else {
 			time_t ret = syscalls::time(NULL);
 			if (ret == -1) {
-				throw SystemException(&quot;Unable to retrieve the system time&quot;,
+				throw TimeRetrievalException(
+					&quot;Unable to retrieve the system time&quot;,
 					errno);
 			}
 			return ret;</diff>
      <filename>ext/common/SystemTime.h</filename>
    </modified>
    <modified>
      <diff>@@ -23,10 +23,16 @@
  *  THE SOFTWARE.
  */
 
+#include &lt;oxt/system_calls.hpp&gt;
+
+#include &lt;sys/socket.h&gt;
+#include &lt;sys/types.h&gt;
+#include &lt;sys/un.h&gt;
 #include &lt;cassert&gt;
 #include &lt;libgen.h&gt;
 #include &lt;pwd.h&gt;
-#include &quot;CachedFileStat.h&quot;
+#include &quot;CachedFileStat.hpp&quot;
+#include &quot;Exceptions.h&quot;
 #include &quot;Utils.h&quot;
 
 #define SPAWN_SERVER_SCRIPT_NAME &quot;passenger-spawn-server&quot;
@@ -58,17 +64,17 @@ split(const string &amp;str, char sep, vector&lt;string&gt; &amp;output) {
 }
 
 bool
-fileExists(const char *filename, CachedMultiFileStat *mstat, unsigned int throttleRate) {
-	return getFileType(filename, mstat, throttleRate) == FT_REGULAR;
+fileExists(const char *filename, CachedFileStat *cstat, unsigned int throttleRate) {
+	return getFileType(filename, cstat, throttleRate) == FT_REGULAR;
 }
 
 FileType
-getFileType(const char *filename, CachedMultiFileStat *mstat, unsigned int throttleRate) {
+getFileType(const char *filename, CachedFileStat *cstat, unsigned int throttleRate) {
 	struct stat buf;
 	int ret;
 	
-	if (mstat != NULL) {
-		ret = cached_multi_file_stat_perform(mstat, filename, &amp;buf, throttleRate);
+	if (cstat != NULL) {
+		ret = cstat-&gt;stat(filename, &amp;buf, throttleRate);
 	} else {
 		ret = stat(filename, &amp;buf);
 	}
@@ -267,6 +273,29 @@ escapeForXml(const string &amp;input) {
 	return result;
 }
 
+string
+getProcessUsername() {
+	struct passwd pwd, *result;
+	char strings[1024];
+	int ret;
+	
+	result = (struct passwd *) NULL;
+	do {
+		ret = getpwuid_r(getuid(), &amp;pwd, strings, sizeof(strings), &amp;result);
+	} while (ret == -1 &amp;&amp; errno == EINTR);
+	if (ret == -1) {
+		result = (struct passwd *) NULL;
+	}
+	
+	if (result == (struct passwd *) NULL) {
+		snprintf(strings, sizeof(strings), &quot;UID %lld&quot;, (long long) getuid());
+		strings[sizeof(strings) - 1] = '\0';
+		return strings;
+	} else {
+		return result-&gt;pw_name;
+	}
+}
+
 void
 determineLowestUserAndGroup(const string &amp;user, uid_t &amp;uid, gid_t &amp;gid) {
 	struct passwd *ent;
@@ -407,36 +436,6 @@ createPassengerTempDir(const string &amp;parentDir, bool userSwitching,
 		 */
 		makeDirTree(tmpDir + &quot;/backends&quot;, &quot;u=wxs,g=,o=&quot;);
 	}
-	
-	if (geteuid() == 0) {
-		if (userSwitching) {
-			/* If user switching is possible and is on, then each backend
-			 * process may be running as a different user. So make the var
-			 * directory world-writable.
-			 *
-			 * The directory is not readable as a security precaution.
-			 */
-			makeDirTree(tmpDir + &quot;/var&quot;, &quot;u=wxs,g=wx,o=wx&quot;);
-		} else {
-			/* If user switching is off then all backend processes
-			 * will be running as lowestUser, so make lowestUser the
-			 * owner of the var directory. Only lowestUser may access
-			 * the directory.
-			 *
-			 * The directory is not readble as a security precaution.
-			 */
-			makeDirTree(tmpDir + &quot;/var&quot;, &quot;u=wxs,g=,o=&quot;, lowestUid, lowestGid);
-		}
-	} else {
-		/* If user switching is not possible then all backend processes will
-		 * be running as the same user as the web server. So we'll make the
-		 * var subdirectory only accessible by this user. Nobody else
-		 * (except root) may access this subdirectory.
-		 *
-		 * The directory is not readble as a security precaution.
-		 */
-		makeDirTree(tmpDir + &quot;/var&quot;, &quot;u=wxs,g=,o=&quot;);
-	}
 }
 
 void
@@ -513,24 +512,144 @@ removeDirTree(const string &amp;path) {
 }
 
 bool
-verifyRailsDir(const string &amp;dir, CachedMultiFileStat *mstat, unsigned int throttleRate) {
+verifyRailsDir(const string &amp;dir, CachedFileStat *cstat, unsigned int throttleRate) {
 	string temp(dir);
 	temp.append(&quot;/config/environment.rb&quot;);
-	return fileExists(temp.c_str(), mstat, throttleRate);
+	return fileExists(temp.c_str(), cstat, throttleRate);
 }
 
 bool
-verifyRackDir(const string &amp;dir, CachedMultiFileStat *mstat, unsigned int throttleRate) {
+verifyRackDir(const string &amp;dir, CachedFileStat *cstat, unsigned int throttleRate) {
 	string temp(dir);
 	temp.append(&quot;/config.ru&quot;);
-	return fileExists(temp.c_str(), mstat, throttleRate);
+	return fileExists(temp.c_str(), cstat, throttleRate);
 }
 
 bool
-verifyWSGIDir(const string &amp;dir, CachedMultiFileStat *mstat, unsigned int throttleRate) {
+verifyWSGIDir(const string &amp;dir, CachedFileStat *cstat, unsigned int throttleRate) {
 	string temp(dir);
 	temp.append(&quot;/passenger_wsgi.py&quot;);
-	return fileExists(temp.c_str(), mstat, throttleRate);
+	return fileExists(temp.c_str(), cstat, throttleRate);
+}
+
+int
+createUnixServer(const char *filename, unsigned int backlogSize, bool autoDelete) {
+	struct sockaddr_un addr;
+	int fd, ret;
+	
+	if (strlen(filename) &gt; sizeof(addr.sun_path) - 1) {
+		string message = &quot;Cannot create Unix socket '&quot;;
+		message.append(filename);
+		message.append(&quot;': filename is too long.&quot;);
+		throw RuntimeException(message);
+	}
+	
+	fd = syscalls::socket(PF_UNIX, SOCK_STREAM, 0);
+	if (fd == -1) {
+		throw SystemException(&quot;Cannot create a Unix socket file descriptor&quot;, errno);
+	}
+	
+	addr.sun_family = AF_UNIX;
+	strncpy(addr.sun_path, filename, sizeof(addr.sun_path));
+	addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
+	
+	if (autoDelete) {
+		do {
+			ret = unlink(filename);
+		} while (ret == -1 &amp;&amp; errno == EINTR);
+	}
+	
+	try {
+		ret = syscalls::bind(fd, (const struct sockaddr *) &amp;addr, sizeof(addr));
+	} catch (...) {
+		do {
+			ret = close(fd);
+		} while (ret == -1 &amp;&amp; errno == EINTR);
+		throw;
+	}
+	if (ret == -1) {
+		int e = errno;
+		string message = &quot;Cannot bind Unix socket '&quot;;
+		message.append(filename);
+		message.append(&quot;'&quot;);
+		do {
+			ret = close(fd);
+		} while (ret == -1 &amp;&amp; errno == EINTR);
+		throw SystemException(message, e);
+	}
+	
+	if (backlogSize == 0) {
+		#ifdef SOMAXCONN
+			backlogSize = SOMAXCONN;
+		#else
+			backlogSize = 128;
+		#endif
+	}
+	try {
+		ret = syscalls::listen(fd, backlogSize);
+	} catch (...) {
+		do {
+			ret = close(fd);
+		} while (ret == -1 &amp;&amp; errno == EINTR);
+		throw;
+	}
+	if (ret == -1) {
+		int e = errno;
+		string message = &quot;Cannot listen on Unix socket '&quot;;
+		message.append(filename);
+		message.append(&quot;'&quot;);
+		do {
+			ret = close(fd);
+		} while (ret == -1 &amp;&amp; errno == EINTR);
+		throw SystemException(message, e);
+	}
+	
+	return fd;
+}
+
+int
+connectToUnixServer(const char *filename) {
+	int fd, ret;
+	struct sockaddr_un addr;
+	
+	if (strlen(filename) &gt; sizeof(addr.sun_path) - 1) {
+		string message = &quot;Cannot connect to Unix socket '&quot;;
+		message.append(filename);
+		message.append(&quot;': filename is too long.&quot;);
+		throw RuntimeException(message);
+	}
+	
+	do {
+		fd = syscalls::socket(PF_UNIX, SOCK_STREAM, 0);
+	} while (fd == -1 &amp;&amp; errno == EINTR);
+	if (fd == -1) {
+		throw SystemException(&quot;Cannot create a Unix socket file descriptor&quot;, errno);
+	}
+	
+	addr.sun_family = AF_UNIX;
+	strncpy(addr.sun_path, filename, sizeof(addr.sun_path));
+	addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
+	
+	try {
+		ret = syscalls::connect(fd, (const sockaddr *) &amp;addr, sizeof(addr));
+	} catch (...) {
+		do {
+			ret = close(fd);
+		} while (ret == -1 &amp;&amp; errno == EINTR);
+		throw;
+	}
+	if (ret == -1) {
+		int e = errno;
+		string message(&quot;Cannot connect to Unix socket '&quot;);
+		message.append(filename);
+		message.append(&quot;'&quot;);
+		do {
+			ret = close(fd);
+		} while (ret == -1 &amp;&amp; errno == EINTR);
+		throw SystemException(message, e);
+	}
+	
+	return fd;
 }
 
 } // namespace Passenger</diff>
      <filename>ext/common/Utils.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -40,13 +40,13 @@
 #include &lt;unistd.h&gt;
 #include &quot;Exceptions.h&quot;
 
-typedef struct CachedMultiFileStat CachedMultiFileStat;
-
 namespace Passenger {
 
 using namespace std;
 using namespace boost;
 
+typedef struct CachedFileStat CachedFileStat;
+
 /** Enumeration which indicates what kind of file a file is. */
 typedef enum {
 	/** The file doesn't exist. */
@@ -158,31 +158,34 @@ void split(const string &amp;str, char sep, vector&lt;string&gt; &amp;output);
  * Check whether the specified file exists.
  *
  * @param filename The filename to check.
- * @param mstat A CachedMultiFileStat object, if you want to use cached statting.
- * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL.
+ * @param cstat A CachedFileStat object, if you want to use cached statting.
+ * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL.
  * @return Whether the file exists.
  * @throws FileSystemException Unable to check because of a filesystem error.
+ * @throws TimeRetrievalException
+ * @throws boost::thread_interrupted
  * @ingroup Support
  */
-bool fileExists(const char *filename, CachedMultiFileStat *mstat = 0,
+bool fileExists(const char *filename, CachedFileStat *cstat = 0,
                 unsigned int throttleRate = 0);
 
 /**
  * Check whether 'filename' exists and what kind of file it is.
  *
  * @param filename The filename to check.
- * @param mstat A CachedMultiFileStat object, if you want to use cached statting.
- * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL.
+ * @param mstat A CachedFileStat object, if you want to use cached statting.
+ * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL.
  * @return The file type.
  * @throws FileSystemException Unable to check because of a filesystem error.
+ * @throws TimeRetrievalException
+ * @throws boost::thread_interrupted
  * @ingroup Support
  */
-FileType getFileType(const char *filename, CachedMultiFileStat *mstat = 0,
+FileType getFileType(const char *filename, CachedFileStat *cstat = 0,
                      unsigned int throttleRate = 0);
 
 /**
  * Find the location of the Passenger spawn server script.
- * If passengerRoot is given, t T
  *
  * @param passengerRoot The Passenger root folder. If NULL is given, then
  *      the spawn server is found by scanning $PATH. For security reasons,
@@ -247,6 +250,13 @@ string extractDirName(const string &amp;path);
 string escapeForXml(const string &amp;input);
 
 /**
+ * Returns the username of the user that the current process is running as.
+ * If the user has no associated username, then the &quot;UID xxxx&quot; is returned,
+ * where xxxx is the current UID.
+ */
+string getProcessUsername();
+
+/**
  * Given a username that's supposed to be the &quot;lowest user&quot; in the user switching mechanism,
  * checks whether this username exists. If so, this users's UID and GID will be stored into
  * the arguments of the same names. If not, &lt;em&gt;uid&lt;/em&gt; and &lt;em&gt;gid&lt;/em&gt; will be set to
@@ -308,8 +318,6 @@ void setPassengerTempDir(const string &amp;dir);
  *          about a running Phusion Passenger instance.
  * - backends - for storing Unix sockets created by backend processes.
  * - master - for storing files such as the Passenger HelperServer socket.
- * - var - for storing all other kinds of temp files that the backend processes
- *         create.
  *
  * If a (sub)directory already exists, then it will not result in an error.
  *
@@ -359,7 +367,7 @@ void createPassengerTempDir(const string &amp;parentDir, bool userSwitching,
  * @throws SystemException Something went wrong.
  * @throws FileSystemException Something went wrong.
  */
-void makeDirTree(const string &amp;path, const char *mode = &quot;u=rwx,g=,o=&quot;, uid_t owner = -1, gid_t group = -1);
+void makeDirTree(const string &amp;path, const char *mode = &quot;u=rwx,g=,o=&quot;, uid_t owner = (uid_t) -1, gid_t group = (gid_t) -1);
 
 /**
  * Remove an entire directory tree recursively.
@@ -372,39 +380,70 @@ void removeDirTree(const string &amp;path);
  * Check whether the specified directory is a valid Ruby on Rails
  * application root directory.
  *
- * @param mstat A CachedMultiFileStat object, if you want to use cached statting.
- * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL.
+ * @param cstat A CachedFileStat object, if you want to use cached statting.
+ * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL.
  * @throws FileSystemException Unable to check because of a system error.
+ * @throws TimeRetrievalException
+ * @throws boost::thread_interrupted
  * @ingroup Support
  */
-bool verifyRailsDir(const string &amp;dir, CachedMultiFileStat *mstat = 0,
+bool verifyRailsDir(const string &amp;dir, CachedFileStat *cstat = 0,
                     unsigned int throttleRate = 0);
 
 /**
  * Check whether the specified directory is a valid Rack application
  * root directory.
  *
- * @param mstat A CachedMultiFileStat object, if you want to use cached statting.
- * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL.
+ * @param cstat A CachedFileStat object, if you want to use cached statting.
+ * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL.
  * @throws FileSystemException Unable to check because of a filesystem error.
+ * @throws TimeRetrievalException
+ * @throws boost::thread_interrupted
  * @ingroup Support
  */
-bool verifyRackDir(const string &amp;dir, CachedMultiFileStat *mstat = 0,
+bool verifyRackDir(const string &amp;dir, CachedFileStat *cstat = 0,
                    unsigned int throttleRate = 0);
 
 /**
  * Check whether the specified directory is a valid WSGI application
  * root directory.
  *
- * @param mstat A CachedMultiFileStat object, if you want to use cached statting.
- * @param throttleRate A throttle rate for mstat. Only applicable if mstat is not NULL.
+ * @param cstat A CachedFileStat object, if you want to use cached statting.
+ * @param throttleRate A throttle rate for cstat. Only applicable if cstat is not NULL.
  * @throws FileSystemException Unable to check because of a filesystem error.
+ * @throws TimeRetrievalException
+ * @throws boost::thread_interrupted
  * @ingroup Support
  */
-bool verifyWSGIDir(const string &amp;dir, CachedMultiFileStat *mstat = 0,
+bool verifyWSGIDir(const string &amp;dir, CachedFileStat *cstat = 0,
                    unsigned int throttleRate = 0);
 
 /**
+ * Create a new Unix server socket which is bounded to &lt;tt&gt;filename&lt;/tt&gt;.
+ *
+ * @param filename The filename to bind the socket to.
+ * @param backlogSize The size of the socket's backlog. Specify 0 to use the
+ *                    platform's maximum allowed backlog size.
+ * @param autoDelete Whether &lt;tt&gt;filename&lt;/tt&gt; should be deleted, if it already exists.
+ * @return The file descriptor of the newly created Unix server socket.
+ * @throws RuntimeException Something went wrong.
+ * @throws SystemException Something went wrong while creating the Unix server socket.
+ * @throws boost::thread_interrupted A system call has been interrupted.
+ */
+int createUnixServer(const char *filename, unsigned int backlogSize = 0, bool autoDelete = true);
+
+/**
+ * Connect to a Unix server socket at &lt;tt&gt;filename&lt;/tt&gt;.
+ *
+ * @param filename The filename of the socket to connect to.
+ * @return The file descriptor of the connected client socket.
+ * @throws RuntimeException Something went wrong.
+ * @throws SystemException Something went wrong while connecting to the Unix server.
+ * @throws boost::thread_interrupted A system call has been interrupted.
+ */
+int connectToUnixServer(const char *filename);
+
+/**
  * Represents a buffered upload file.
  *
  * @ingroup Support
@@ -419,11 +458,11 @@ public:
 	 *
 	 * @throws SystemException Something went wrong.
 	 */
-	BufferedUpload(const char *identifier = &quot;temp&quot;) {
+	BufferedUpload(const string &amp;dir, const char *identifier = &quot;temp&quot;) {
 		char templ[PATH_MAX];
 		int fd;
 		
-		snprintf(templ, sizeof(templ), &quot;%s/%s.XXXXXX&quot;, getDir().c_str(), identifier);
+		snprintf(templ, sizeof(templ), &quot;%s/%s.XXXXXX&quot;, dir.c_str(), identifier);
 		templ[sizeof(templ) - 1] = '\0';
 		fd = mkstemp(templ);
 		if (fd == -1) {
@@ -450,14 +489,6 @@ public:
 	~BufferedUpload() {
 		fclose(handle);
 	}
-	
-	/**
-	 * Returns the directory in which upload buffer files are stored.
-	 * This is a subdirectory of the directory returned by getPassengerTempDir(). 
-	 */
-	static string getDir() {
-		return getPassengerTempDir() + &quot;/webserver_private&quot;;
-	}
 };
 
 } // namespace Passenger</diff>
      <filename>ext/common/Utils.h</filename>
    </modified>
    <modified>
      <diff>@@ -26,6 +26,6 @@
 #define _PASSENGER_VERSION_H_
 
 /* Don't forget to update lib/phusion_passenger/constants.rb too. */
-#define PASSENGER_VERSION &quot;2.2.2&quot;
+#define PASSENGER_VERSION &quot;2.2.4&quot;
 
 #endif /* _PASSENGER_VERSION_H */</diff>
      <filename>ext/common/Version.h</filename>
    </modified>
    <modified>
      <diff>@@ -83,10 +83,6 @@ passenger_init_main_conf(ngx_conf_t *cf, void *conf_pointer)
     conf = &amp;passenger_main_conf;
     *conf = *((passenger_main_conf_t *) conf_pointer);
     
-    if (conf-&gt;root_dir.len == 0) {
-        return &quot;-- The 'passenger_root' directive must be set&quot;;
-    }
-    
     if (conf-&gt;ruby.len == 0) {
         conf-&gt;ruby.data = (u_char *) &quot;ruby&quot;;
         conf-&gt;ruby.len  = sizeof(&quot;ruby&quot;) - 1;</diff>
      <filename>ext/nginx/Configuration.c</filename>
    </modified>
    <modified>
      <diff>@@ -63,10 +63,10 @@ get_file_type(const u_char *filename, unsigned int throttle_rate) {
     struct stat buf;
     int ret;
     
-    ret = cached_multi_file_stat_perform(passenger_stat_cache,
-                                         (const char *) filename,
-                                         &amp;buf,
-                                         throttle_rate);
+    ret = cached_file_stat_perform(passenger_stat_cache,
+                                   (const char *) filename,
+                                   &amp;buf,
+                                   throttle_rate);
     if (ret == 0) {
         if (S_ISREG(buf.st_mode)) {
             return FT_FILE;
@@ -105,7 +105,7 @@ detect_application_type(const ngx_str_t *public_dir) {
     
     ngx_memzero(filename, sizeof(filename));
     ngx_snprintf(filename, sizeof(filename), &quot;%s/%s&quot;,
-                 public_dir-&gt;data, &quot;../config/passenger_wsgi.py&quot;);
+                 public_dir-&gt;data, &quot;../passenger_wsgi.py&quot;);
     if (file_exists(filename, 1)) {
         return AP_WSGI;
     }
@@ -1077,7 +1077,9 @@ passenger_content_handler(ngx_http_request_t *r)
     ngx_str_t              page_cache_file;
     passenger_context_t   *context;
 
-    if (r-&gt;subrequest_in_memory) {
+    if (passenger_main_conf.root_dir.len == 0) {
+        return NGX_DECLINED;
+    } else if (r-&gt;subrequest_in_memory) {
         ngx_log_error(NGX_LOG_ALERT, r-&gt;connection-&gt;log, 0,
                       &quot;ngx_http_passenger_module does not support &quot;
                       &quot;subrequest in memory&quot;);</diff>
      <filename>ext/nginx/ContentHandler.c</filename>
    </modified>
    <modified>
      <diff>@@ -122,6 +122,8 @@ public:
 	}
 };
 
+struct ClientDisconnectedException { };
+
 /**
  * A representation of a Client from the Server's point of view. This class
  * contains the methods used to communicate from a server to a connected
@@ -311,7 +313,12 @@ private:
 	 * 
 	 * @param session The Ruby on Rails session to read the response from.
 	 * @param clientFd The client file descriptor to write the response to.
-	 * @throws SystemException Response could not be read from backend (Rails) process.
+	 * @throws SystemException Something went wrong while reading the response
+	 *                         from the backend process or while writing to the
+	 *                         response back to the web server.
+	 * @throws ClientDisconnectedException The HTTP client closed the connection
+	 *                                     before we were able to send back the
+	 *                                     full response.
 	 */
 	void forwardResponse(Application::SessionPtr &amp;session, FileDescriptor &amp;clientFd) {
 		TRACE_POINT();
@@ -339,13 +346,21 @@ private:
 				 * of the response data.
 				 */
 				UPDATE_TRACE_POINT();
-				string statusLine(&quot;HTTP/1.1 &quot;);
-				statusLine.append(ex.getStatusLine());
-				UPDATE_TRACE_POINT();
-				output.writeRaw(statusLine.c_str(), statusLine.size());
-				UPDATE_TRACE_POINT();
-				output.writeRaw(ex.getBuffer().c_str(), ex.getBuffer().size());
-				break;
+				try {
+					string statusLine(&quot;HTTP/1.1 &quot;);
+					statusLine.append(ex.getStatusLine());
+					UPDATE_TRACE_POINT();
+					output.writeRaw(statusLine.c_str(), statusLine.size());
+					UPDATE_TRACE_POINT();
+					output.writeRaw(ex.getBuffer().c_str(), ex.getBuffer().size());
+					break;
+				} catch (const SystemException &amp;e) {
+					if (e.code() == EPIPE) {
+						throw ClientDisconnectedException();
+					} else {
+						throw;
+					}
+				}
 			}
 		}
 		
@@ -359,7 +374,15 @@ private:
 				throw SystemException(&quot;Cannot read response from backend process&quot;, errno);
 			} else {
 				UPDATE_TRACE_POINT();
-				output.writeRaw(buf, size);
+				try {
+					output.writeRaw(buf, size);
+				} catch (const SystemException &amp;e) {
+					if (e.code() == EPIPE) {
+						throw ClientDisconnectedException();
+					} else {
+						throw;
+					}
+				}
 			}
 		}
 	}
@@ -399,10 +422,8 @@ private:
 	 * Handles an SCGI request from a client whose identity is derived by the given &lt;tt&gt;clientFd&lt;/tt&gt;.
 	 *
 	 * @param clientFd The file descriptor identifying the client to handle the request from.
-	 * @return True if an error occurred while trying to handle the request of the client. False if
-	 *   the request was succesfully processed.
 	 */
-	bool handleRequest(FileDescriptor &amp;clientFd) {
+	void handleRequest(FileDescriptor &amp;clientFd) {
 		TRACE_POINT();
 		ScgiRequestParser parser;
 		string partialRequestBody;
@@ -410,18 +431,19 @@ private:
 		
 		if (!readAndCheckPassword(clientFd)) {
 			P_ERROR(&quot;Client did not send a correct password.&quot;);
-			return true;
+			return;
 		}
 		if (!readAndParseRequestHeaders(clientFd, parser, partialRequestBody)) {
-			return true;
+			return;
 		}
 		
 		try {
 			PoolOptions options;
-			if (parser.getHeader(&quot;RAILS_RELATIVE_URL_ROOT&quot;).empty()) {
+			if (parser.getHeader(&quot;SCRIPT_NAME&quot;).empty()) {
 				options.appRoot = extractDirName(parser.getHeader(&quot;DOCUMENT_ROOT&quot;));
 			} else {
 				options.appRoot = extractDirName(resolveSymlink(parser.getHeader(&quot;DOCUMENT_ROOT&quot;)));
+				options.baseURI = parser.getHeader(&quot;SCRIPT_NAME&quot;);
 			}
 			options.useGlobalQueue = parser.getHeader(&quot;PASSENGER_USE_GLOBAL_QUEUE&quot;) == &quot;true&quot;;
 			options.environment    = parser.getHeader(&quot;PASSENGER_ENVIRONMENT&quot;);
@@ -450,23 +472,23 @@ private:
 				forwardResponse(session, clientFd);
 			} catch (const SpawnException &amp;e) {
 				handleSpawnException(clientFd, e);
+			} catch (const ClientDisconnectedException &amp;) {
+				P_WARN(&quot;Couldn't forward the HTTP response back to the HTTP client: &quot;
+					&quot;It seems the user clicked on the 'Stop' button in his &quot;
+					&quot;browser.&quot;);
 			}
-			return false;
 		} catch (const boost::thread_interrupted &amp;) {
 			throw;
 		} catch (const tracable_exception &amp;e) {
 			P_ERROR(&quot;Uncaught exception in PassengerServer client thread:\n&quot;
 				&lt;&lt; &quot;   exception: &quot; &lt;&lt; e.what() &lt;&lt; &quot;\n&quot;
 				&lt;&lt; &quot;   backtrace:\n&quot; &lt;&lt; e.backtrace());
-			return true;
 		} catch (const exception &amp;e) {
 			P_ERROR(&quot;Uncaught exception in PassengerServer client thread:\n&quot;
 				&lt;&lt; &quot;   exception: &quot; &lt;&lt; e.what() &lt;&lt; &quot;\n&quot;
 				&lt;&lt; &quot;   backtrace: not available&quot;);
-			return true;
 		} catch (...) {
 			P_ERROR(&quot;Uncaught unknown exception in PassengerServer client thread.&quot;);
-			return true;
 		}
 	}
 	
@@ -555,8 +577,6 @@ typedef shared_ptr&lt;Client&gt; ClientPtr;
  */
 class Server {
 private:
-	static const unsigned int BACKLOG_SIZE = 50;
-
 	string password;
 	int adminPipe;
 	int serverSocket;
@@ -573,45 +593,15 @@ private:
 	 * attempt to bind on. Once it is bound, it will start listening for incoming client
 	 * activity.
 	 *
-	 * @throws SystemException Could not create unconnected Unix socket or bind on Unix socket.
+	 * @throws SystemException Something went wrong while trying to create and bind to the Unix socket.
+	 * @throws RuntimeException Something went wrong.
 	 */
 	void startListening() {
 		this_thread::disable_syscall_interruption dsi;
 		string socketName = getPassengerTempDir() + &quot;/master/helper_server.sock&quot;;
-		struct sockaddr_un addr;
-		int ret;
-		
-		serverSocket = syscalls::socket(PF_UNIX, SOCK_STREAM, 0);
-		if (serverSocket == -1) {
-			throw SystemException(&quot;Cannot create an unconnected Unix socket&quot;, errno);
-		}
-		
-		addr.sun_family = AF_UNIX;
-		strncpy(addr.sun_path, socketName.c_str(), sizeof(addr.sun_path));
-		addr.sun_path[sizeof(addr.sun_path) - 1] = '\0';
-		
-		ret = syscalls::bind(serverSocket, (const struct sockaddr *) &amp;addr, sizeof(addr));
-		if (ret == -1) {
-			int e = errno;
-			syscalls::close(serverSocket);
-			
-			string message(&quot;Cannot bind on Unix socket '&quot;);
-			message.append(socketName);
-			message.append(&quot;'&quot;);
-			throw SystemException(message, e);
-		}
-		
-		ret = syscalls::listen(serverSocket, BACKLOG_SIZE);
-		if (ret == -1) {
-			int e = errno;
-			syscalls::close(serverSocket);
-			
-			string message(&quot;Cannot bind on Unix socket '&quot;);
-			message.append(socketName);
-			message.append(&quot;'&quot;);
-			throw SystemException(message, e);
-		}
+		serverSocket = createUnixServer(socketName.c_str());
 		
+		int ret;
 		do {
 			ret = chmod(socketName.c_str(), S_ISVTX |
 				S_IRUSR | S_IWUSR | S_IXUSR |</diff>
      <filename>ext/nginx/HelperServer.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -159,6 +159,10 @@ private:
 			case 307:
 				statusLine = &quot;307 Temporary Redirect\x0D\x0A&quot;;
 				break;
+			case 308:
+				// Google Gears: http://code.google.com/p/gears/wiki/ResumableHttpRequestsProposal
+				statusLine = &quot;308 Resume Incomplete\x0D\x0A&quot;;
+				break;
 			case 400:
 				statusLine = &quot;400 Bad Request\x0D\x0A&quot;;
 				break;</diff>
      <filename>ext/nginx/HttpStatusExtractor.h</filename>
    </modified>
    <modified>
      <diff>@@ -26,6 +26,25 @@
 
 #include &quot;StaticContentHandler.h&quot;
 
+static void
+set_request_extension(ngx_http_request_t *r, ngx_str_t *filename) {
+    u_char *tmp;
+    
+    /* Scan filename from the right until we've found a slash or a dot. */
+    tmp = filename-&gt;data + filename-&gt;len - 1;
+    while (tmp &gt;= filename-&gt;data &amp;&amp; *tmp != '/' &amp;&amp; *tmp != '.') {
+        tmp--;
+    }
+    if (tmp &gt;= filename-&gt;data &amp;&amp; *tmp == '.') {
+        /* We found a dot, and until now we haven't seen any slashes.
+         * So we know that this is the filename's extension.
+        */
+        tmp++;
+        r-&gt;exten.data = tmp;
+        r-&gt;exten.len  = filename-&gt;len - (tmp - filename-&gt;data);
+    }
+}
+
 ngx_int_t
 passenger_static_content_handler(ngx_http_request_t *r, ngx_str_t *filename)
 {
@@ -58,10 +77,10 @@ passenger_static_content_handler(ngx_http_request_t *r, ngx_str_t *filename)
 
     clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
 
-    #if NGX_VERSION_NUM &lt; 7000
+    ngx_memzero(&amp;of, sizeof(ngx_open_file_info_t));
+    #if NGINX_VERSION_NUM &lt; 7000
         of.test_dir = 0;
     #else
-        ngx_memzero(&amp;of, sizeof(ngx_open_file_info_t));
         of.directio = clcf-&gt;directio;
     #endif
     of.valid = clcf-&gt;open_file_cache_valid;
@@ -106,7 +125,7 @@ passenger_static_content_handler(ngx_http_request_t *r, ngx_str_t *filename)
         return rc;
     }
 
-    #if NGX_VERSION_NUM &gt;= 7000
+    #if NGINX_VERSION_NUM &gt;= 7000
         r-&gt;root_tested = !r-&gt;error_page;
     #endif
 	
@@ -131,7 +150,7 @@ passenger_static_content_handler(ngx_http_request_t *r, ngx_str_t *filename)
                 len += r-&gt;args.len + 1;
             }
 
-            #if NGX_VERSION_NUM &lt; 7000
+            #if NGINX_VERSION_NUM &lt; 7000
                 location = ngx_palloc(r-&gt;pool, len);
             #else
             	location = ngx_pnalloc(r-&gt;pool, len);
@@ -188,6 +207,7 @@ passenger_static_content_handler(ngx_http_request_t *r, ngx_str_t *filename)
     r-&gt;headers_out.content_length_n = of.size;
     r-&gt;headers_out.last_modified_time = of.mtime;
 
+    set_request_extension(r, filename);
     if (ngx_http_set_content_type(r) != NGX_OK) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
@@ -226,7 +246,7 @@ passenger_static_content_handler(ngx_http_request_t *r, ngx_str_t *filename)
     b-&gt;file-&gt;fd = of.fd;
     b-&gt;file-&gt;name = *filename;
     b-&gt;file-&gt;log = log;
-    #if NGX_VERSION_NUM &gt;= 7000
+    #if NGINX_VERSION_NUM &gt;= 7000
         b-&gt;file-&gt;directio = of.is_directio;
     #endif
 </diff>
      <filename>ext/nginx/StaticContentHandler.c</filename>
    </modified>
    <modified>
      <diff>@@ -26,6 +26,9 @@ CORE_LIBS=&quot;$CORE_LIBS  \
     ${ngx_addon_dir}/libpassenger_common.a \
     ${ngx_addon_dir}/libboost_oxt.a \
     -lstdc++ -lpthread&quot;
+if test x`uname` = xOpenBSD; then
+    CORE_LIBS=&quot;$CORE_LIBS -lm&quot;
+fi
 
 nginx_version=`grep 'NGINX_VERSION ' src/core/nginx.h | awk '{ print $3 }' | sed 's/&quot;//g'`
 </diff>
      <filename>ext/nginx/config</filename>
    </modified>
    <modified>
      <diff>@@ -54,11 +54,18 @@ static ngx_str_t  ngx_http_scgi_script_name = ngx_string(&quot;scgi_script_name&quot;);
 static pid_t      helper_server_pid = 0;
 static int        helper_server_admin_pipe;
 static u_char     helper_server_password_data[HELPER_SERVER_PASSWORD_SIZE];
+/** perl_module destroys the original environment variables for some reason,
+ * so when we get a SIGHUP (for restarting Nginx) $TMPDIR might not have the
+ * same value as it had during Nginx startup. We need the original $TMPDIR
+ * value for calculating the Passenger temp dir location, so here we cache
+ * the original value instead of getenv()'ing it every time.
+ */
+const char       *system_temp_dir = NULL;
 const char        passenger_temp_dir[NGX_MAX_PATH];
 ngx_str_t         passenger_schema_string;
 ngx_str_t         passenger_helper_server_password;
 const char        passenger_helper_server_socket[NGX_MAX_PATH];
-CachedMultiFileStat *passenger_stat_cache;
+CachedFileStat   *passenger_stat_cache;
 
 static void shutdown_helper_server(ngx_cycle_t *cycle);
 
@@ -122,10 +129,17 @@ start_helper_server(ngx_cycle_t *cycle)
     long                   i;
     ssize_t                ret;
     char                   buf;
-    FILE                  *f;
+    ngx_str_t             *log_filename;
+    FILE                  *f, *log_file;
+    
+    shutdown_helper_server(cycle);
     
-    if (helper_server_pid != 0) {
-        shutdown_helper_server(cycle);
+    if (main_conf-&gt;root_dir.len == 0) {
+        ngx_log_error(NGX_LOG_ALERT, cycle-&gt;log, 0,
+                      &quot;Phusion Passenger is disabled because the &quot;
+                      &quot;'passenger_root' option is not set. Please set &quot;
+                      &quot;this option if you want to enable Phusion Passenger.&quot;);
+        return NGX_OK;
     }
     
     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle-&gt;conf_ctx, ngx_core_module);
@@ -219,10 +233,35 @@ start_helper_server(ngx_cycle_t *cycle)
         close(admin_pipe[1]);
         close(feedback_pipe[0]);
         
-        /* Nginx redirects stderr to the error log file. Make sure that
-         * stdout is redirected to the error log file as well.
+        /* At this point, stdout and stderr may still point to the console.
+         * Make sure that they're both redirected to the log file.
          */
-        dup2(2, 1);
+        log_file = NULL;
+        #if NGINX_VERSION_NUM &lt; 7000
+            log_filename = &amp;cycle-&gt;new_log-&gt;file-&gt;name;
+        #else
+            log_filename = &amp;cycle-&gt;new_log.file-&gt;name;
+        #endif
+        if (log_filename-&gt;len &gt; 0) {
+            log_file = fopen((const char *) log_filename-&gt;data, &quot;a&quot;);
+            if (log_file == NULL) {
+                ngx_log_error(NGX_LOG_ALERT, cycle-&gt;log, ngx_errno,
+                              &quot;could not open the error log file for writing&quot;);
+            }
+        }
+        if (log_file == NULL) {
+            /* If the log file cannot be opened then we redirect stdout
+             * and stderr to /dev/null, because if the user disconnects
+             * from the console on which Nginx is started, then on Linux
+             * any writes to stdout or stderr will result in an EIO error.
+             */
+            log_file = fopen(&quot;/dev/null&quot;, &quot;w&quot;);
+        }
+        if (log_file != NULL) {
+            dup2(fileno(log_file), 1);
+            dup2(fileno(log_file), 2);
+            fclose(log_file);
+        }
         
         /* Close all file descriptors except stdin, stdout, stderr and
          * the reader part of the pipe we just created. 
@@ -241,6 +280,8 @@ start_helper_server(ngx_cycle_t *cycle)
             close(i);
         }
         
+        setenv(&quot;SERVER_SOFTWARE&quot;, NGINX_VER, 1);
+        
         execlp((const char *) helper_server_filename,
                &quot;PassengerNginxHelperServer&quot;,
                main_conf-&gt;root_dir.data,
@@ -256,7 +297,7 @@ start_helper_server(ngx_cycle_t *cycle)
                worker_uid_string,
                worker_gid_string,
                passenger_temp_dir,
-               NULL);
+               (char *) 0);
         e = errno;
         fprintf(stderr, &quot;*** Could not start the Passenger helper server (%s): &quot;
                 &quot;exec() failed: %s (%d)\n&quot;,
@@ -357,6 +398,10 @@ save_master_process_pid(ngx_cycle_t *cycle) {
     u_char *last;
     FILE *f;
     
+    if (passenger_main_conf.root_dir.len == 0) {
+        return NGX_OK;
+    }
+    
     last = ngx_snprintf(filename, sizeof(filename) - 1,
         &quot;%s/control_process.pid&quot;, passenger_temp_dir);
     *last = (u_char) '\0';
@@ -380,6 +425,10 @@ shutdown_helper_server(ngx_cycle_t *cycle)
     int    helper_server_exited, ret;
     u_char command[NGX_MAX_PATH + 10];
     
+    if (helper_server_pid == 0) {
+        return;
+    }
+    
     /* We write one byte to the admin pipe, doesn't matter what the byte is.
      * The helper server will detect this as an exit command.
      */
@@ -442,7 +491,6 @@ shutdown_helper_server(ngx_cycle_t *cycle)
                           passenger_temp_dir);
         }
     }
-    
     helper_server_pid = 0;
 }
 
@@ -509,15 +557,15 @@ static ngx_int_t
 pre_config_init(ngx_conf_t *cf)
 {
     ngx_int_t   ret;
-    const char *system_temp_dir;
     u_char      command[NGX_MAX_PATH + 30];
+    u_char     *last;
     
     ngx_memzero(&amp;passenger_main_conf, sizeof(passenger_main_conf_t));
     
     passenger_schema_string.data = (u_char *) &quot;passenger://&quot;;
     passenger_schema_string.len  = sizeof(&quot;passenger://&quot;) - 1;
     
-    passenger_stat_cache = cached_multi_file_stat_new(1024);
+    passenger_stat_cache = cached_file_stat_new(1024);
     
     ret = add_variables(cf);
     if (ret != NGX_OK) {
@@ -526,9 +574,13 @@ pre_config_init(ngx_conf_t *cf)
     
     /* Setup Passenger temp folder. */
     
-    system_temp_dir = getenv(&quot;TMPDIR&quot;);
-    if (!system_temp_dir || !*system_temp_dir) {
-        system_temp_dir = &quot;/tmp&quot;;
+    if (system_temp_dir == NULL) {
+        const char *tmp = getenv(&quot;TMPDIR&quot;);
+        if (tmp == NULL || *tmp == '\0') {
+            system_temp_dir = &quot;/tmp&quot;;
+        } else {
+            system_temp_dir = strdup(tmp);
+        }
     }
     
     ngx_memzero(&amp;passenger_temp_dir, sizeof(passenger_temp_dir));
@@ -554,13 +606,16 @@ pre_config_init(ngx_conf_t *cf)
     
     /* Build helper server socket filename string. */
     
-    if (ngx_snprintf((u_char *) passenger_helper_server_socket, NGX_MAX_PATH,
-                     &quot;unix:%s/master/helper_server.sock&quot;,
-                     passenger_temp_dir) == NULL) {
+    last = ngx_snprintf((u_char *) passenger_helper_server_socket, NGX_MAX_PATH,
+                        &quot;unix:%s/master/helper_server.sock&quot;,
+                        passenger_temp_dir);
+    if (last == NULL) {
         ngx_log_error(NGX_LOG_ALERT, cf-&gt;log, ngx_errno,
                       &quot;could not create Passenger helper server &quot;
                       &quot;socket filename string&quot;);
         return NGX_ERROR;
+    } else {
+        *last = (u_char) '\0';
     }
     
     return NGX_OK;</diff>
      <filename>ext/nginx/ngx_http_passenger_module.c</filename>
    </modified>
    <modified>
      <diff>@@ -64,9 +64,9 @@ extern ngx_str_t    passenger_helper_server_password;
 extern const char   passenger_helper_server_socket[NGX_MAX_PATH];
 
 /**
- * A CachedMultiFileStat object used for caching stat() calls.
+ * A CachedFileStat object used for caching stat() calls.
  */
-extern CachedMultiFileStat *passenger_stat_cache;
+extern CachedFileStat *passenger_stat_cache;
 
 #endif /* _PASSENGER_NGINX_MODULE_H_ */
 </diff>
      <filename>ext/nginx/ngx_http_passenger_module.h</filename>
    </modified>
    <modified>
      <diff>@@ -97,8 +97,6 @@ class AbstractRequestHandler
 	IGNORE              = 'IGNORE'              # :nodoc:
 	DEFAULT             = 'DEFAULT'             # :nodoc:
 	NULL                = &quot;\0&quot;                  # :nodoc:
-	CONTENT_LENGTH      = 'CONTENT_LENGTH'      # :nodoc:
-	HTTP_CONTENT_LENGTH = 'HTTP_CONTENT_LENGTH' # :nodoc:
 	X_POWERED_BY        = 'X-Powered-By'        # :nodoc:
 	REQUEST_METHOD      = 'REQUEST_METHOD'      # :nodoc:
 	PING                = 'ping'                # :nodoc:
@@ -144,11 +142,13 @@ class AbstractRequestHandler
 		@socket.close_on_exec!
 		@owner_pipe = owner_pipe
 		@previous_signal_handlers = {}
+		@main_loop_generation  = 0
 		@main_loop_thread_lock = Mutex.new
 		@main_loop_thread_cond = ConditionVariable.new
 		@memory_limit = options[&quot;memory_limit&quot;] || 0
 		@iterations = 0
 		@processed_requests = 0
+		@main_loop_running = false
 	end
 	
 	# Clean up temporary stuff created by the request handler.
@@ -160,7 +160,9 @@ class AbstractRequestHandler
 	# may be called at any time, and it will stop the main loop thread.
 	def cleanup
 		if @main_loop_thread
-			@main_loop_thread.raise(Interrupt.new(&quot;Cleaning up&quot;))
+			@main_loop_thread_lock.synchronize do
+				@graceful_termination_pipe[1].close rescue nil
+			end
 			@main_loop_thread.join
 		end
 		@socket.close rescue nil
@@ -182,6 +184,7 @@ class AbstractRequestHandler
 			@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
 			end
@@ -226,10 +229,11 @@ class AbstractRequestHandler
 				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
+				@graceful_termination_pipe[0].close rescue nil
+				@graceful_termination_pipe[1].close rescue nil
+				@main_loop_generation += 1
 				@main_loop_running = false
 				@main_loop_thread_cond.broadcast
 			end
@@ -238,11 +242,12 @@ class AbstractRequestHandler
 	
 	# Start the main loop in a new thread. This thread will be stopped by #cleanup.
 	def start_main_loop_thread
+		current_generation = @main_loop_generation
 		@main_loop_thread = Thread.new do
 			main_loop
 		end
 		@main_loop_thread_lock.synchronize do
-			while !@main_loop_running
+			while @main_loop_generation == current_generation
 				@main_loop_thread_cond.wait(@main_loop_thread_lock)
 			end
 		end
@@ -399,6 +404,10 @@ private
 				undef rewind if respond_to?(:rewind)
 			end
 			
+			# Set encoding for Ruby 1.9 compatibility.
+			client.set_encoding(Encoding::BINARY) if client.respond_to?(:set_encoding)
+			client.binmode
+			
 			return client
 		else
 			# The other end of the owner pipe has been closed, or the
@@ -422,7 +431,6 @@ private
 			return
 		end
 		headers = Hash[*headers_data.split(NULL)]
-		headers[CONTENT_LENGTH] = headers[HTTP_CONTENT_LENGTH]
 		return [headers, socket]
 	rescue SecurityError =&gt; e
 		STDERR.puts(&quot;*** Passenger RequestHandler: HTTP header size exceeded maximum.&quot;)</diff>
      <filename>lib/phusion_passenger/abstract_request_handler.rb</filename>
    </modified>
    <modified>
      <diff>@@ -124,7 +124,7 @@ class AbstractServer
 		if started?
 			raise ServerAlreadyStarted, &quot;Server is already started&quot;
 		end
-	
+		
 		@parent_socket, @child_socket = UNIXSocket.pair
 		before_fork
 		@pid = fork
@@ -138,7 +138,12 @@ class AbstractServer
 				# on a white list of file descriptors. That proved to be way too fragile:
 				# too many file descriptors are being left open even though they shouldn't
 				# be. So now we close file descriptors based on a black list.
-				file_descriptors_to_leave_open = [0, 1, 2, @child_socket.fileno]
+				#
+				# Note that STDIN, STDOUT and STDERR may be temporarily set to
+				# different file descriptors than 0, 1 and 2, e.g. in unit tests.
+				# We don't want to close these either.
+				file_descriptors_to_leave_open = [0, 1, 2, @child_socket.fileno,
+					fileno_of(STDIN), fileno_of(STDOUT), fileno_of(STDERR)].compact.uniq
 				NativeSupport.close_all_file_descriptors(file_descriptors_to_leave_open)
 				# In addition to closing the file descriptors, one must also close
 				# the associated IO objects. This is to prevent IO.close from
@@ -151,6 +156,9 @@ class AbstractServer
 				# clear all open file handles.
 				Gem.clear_paths
 				
+				# Reseed pseudo-random number generator for security reasons.
+				srand
+				
 				start_synchronously(@child_socket)
 			rescue Interrupt
 				# Do nothing.
@@ -292,6 +300,12 @@ protected
 	def quit_main
 		@done = true
 	end
+	
+	def fileno_of(io)
+		return io.fileno
+	rescue
+		return nil
+	end
 
 private
 	# Reset all signal handlers to default. This is called in the child process,</diff>
      <filename>lib/phusion_passenger/abstract_server.rb</filename>
    </modified>
    <modified>
      <diff>@@ -23,6 +23,7 @@
 
 require 'rexml/document'
 require 'fileutils'
+require 'socket'
 require 'phusion_passenger/admin_tools'
 require 'phusion_passenger/message_channel'
 
@@ -79,37 +80,30 @@ class ControlProcess
 	end
 	
 	def status
-		reload
-		return @status
-	end
-	
-	def xml
-		reload
-		return @xml
+		connect do |channel|
+			channel.write(&quot;status&quot;)
+			return channel.read_scalar
+		end
 	end
 	
-	def domains
-		reload
-		return @domains
+	def backtraces
+		connect do |channel|
+			channel.write(&quot;backtraces&quot;)
+			return channel.read_scalar
+		end
 	end
 	
-	def instances
-		return domains.map do |domain|
-			domain[:instances]
-		end.flatten
+	def xml
+		connect do |channel|
+			channel.write(&quot;status_xml&quot;)
+			return channel.read_scalar
+		end
 	end
 	
-private
-	def reload
-		return if @status
-		File.open(&quot;#{path}/info/status.fifo&quot;, 'r') do |f|
-			channel = MessageChannel.new(f)
-			@status = channel.read_scalar
-			@xml = channel.read_scalar
-		end
-		doc = REXML::Document.new(@xml)
+	def domains
+		doc = REXML::Document.new(xml)
 		
-		@domains = []
+		domains = []
 		doc.elements.each(&quot;info/domains/domain&quot;) do |domain|
 			instances = []
 			d = {
@@ -130,7 +124,24 @@ private
 				end
 				instances &lt;&lt; i
 			end
-			@domains &lt;&lt; d
+			domains &lt;&lt; d
+		end
+		return domains
+	end
+	
+	def instances
+		return domains.map do |domain|
+			domain[:instances]
+		end.flatten
+	end
+	
+private
+	def connect
+		channel = MessageChannel.new(UNIXSocket.new(&quot;#{path}/info/status.socket&quot;))
+		begin
+			yield channel
+		ensure
+			channel.close
 		end
 	end
 end</diff>
      <filename>lib/phusion_passenger/admin_tools/control_process.rb</filename>
    </modified>
    <modified>
      <diff>@@ -24,7 +24,7 @@
 module PhusionPassenger
 	# Phusion Passenger version number.
 	# Don't forget to edit ext/common/Version.h too.
-	VERSION_STRING = '2.2.2'
+	VERSION_STRING = '2.2.4'
 	
 	DEFAULT_FRAMEWORK_SPAWNER_MAX_IDLE_TIME = 30 * 60
 	DEFAULT_APP_SPAWNER_MAX_IDLE_TIME       = 10 * 60</diff>
      <filename>lib/phusion_passenger/constants.rb</filename>
    </modified>
    <modified>
      <diff>@@ -296,6 +296,16 @@ module Dependencies # :nodoc: all
 				result.found(PlatformInfo.apu_config)
 			end
 		end
+		if RUBY_PLATFORM =~ /linux/
+			case PlatformInfo.linux_distro
+			when :ubuntu, :debian
+				dep.install_command = &quot;apt-get install libaprutil1-dev&quot;
+			end
+		elsif RUBY_PLATFORM =~ /darwin/
+			dep.install_instructions = &quot;Please install Apache from MacPorts, which will &quot; &lt;&lt;
+				&quot;provide APU automatically. &lt;b&gt;Or&lt;/b&gt;, if you're installing against MacOS X's &quot; &lt;&lt;
+				&quot;default provided Apache, then please install the OS X Developer SDK.&quot;
+		end
 		dep.website = &quot;http://httpd.apache.org/&quot;
 		dep.website_comments = &quot;APR Utility is an integrated part of Apache.&quot;
 	end</diff>
      <filename>lib/phusion_passenger/dependencies.rb</filename>
    </modified>
    <modified>
      <diff>@@ -339,7 +339,7 @@ public
 		# _GLIBCPP__PTHREADS is for fixing Boost compilation on OpenBSD.
 		flags = [&quot;-D_REENTRANT -I/usr/local/include&quot;]
 		if RUBY_PLATFORM =~ /solaris/
-			flags &lt;&lt; '-D_XOPEN_SOURCE=500 -D_XPG4_2 -D__EXTENSIONS__ -D__SOLARIS__'
+			flags &lt;&lt; '-D_XOPEN_SOURCE=500 -D_XPG4_2 -D__EXTENSIONS__ -D__SOLARIS__ -D_FILE_OFFSET_BITS=64'
 			flags &lt;&lt; '-DBOOST_HAS_STDINT_H' unless RUBY_PLATFORM =~ /solaris2.9/
 			flags &lt;&lt; '-D__SOLARIS9__ -DBOOST__STDC_CONSTANT_MACROS_DEFINED' if RUBY_PLATFORM =~ /solaris2.9/
 			flags &lt;&lt; '-mcpu=ultrasparc' if RUBY_PLATFORM =~ /sparc/</diff>
      <filename>lib/phusion_passenger/platform_info.rb</filename>
    </modified>
    <modified>
      <diff>@@ -49,6 +49,8 @@ class ApplicationSpawner
 	# Application object will be returned, which represents the spawned
 	# application.
 	#
+	# Accepts the same options as Railz::ApplicationSpawner#initialize.
+	#
 	# Raises:
 	# - AppInitError: The Rack application raised an exception or called
 	#   exit() during startup.
@@ -70,7 +72,7 @@ class ApplicationSpawner
 		Process.waitpid(pid) rescue nil
 		
 		channel = MessageChannel.new(a)
-		unmarshal_and_raise_errors(channel, &quot;rack&quot;)
+		unmarshal_and_raise_errors(channel, !!options[&quot;print_exceptions&quot;], &quot;rack&quot;)
 		
 		# No exception was raised, so spawning succeeded.
 		pid, socket_name, socket_type = channel.read
@@ -83,13 +85,19 @@ class ApplicationSpawner
 	end
 
 private
-	
 	def run(channel, app_root, options)
 		$0 = &quot;Rack: #{app_root}&quot;
 		app = nil
 		success = report_app_init_status(channel) do
 			ENV['RACK_ENV'] = options[&quot;environment&quot;]
+			if options[&quot;base_uri&quot;] &amp;&amp; options[&quot;base_uri&quot;] != &quot;/&quot;
+				ENV['RACK_BASE_URI'] = options[&quot;base_uri&quot;]
+				ENV['RAILS_RELATIVE_URL_ROOT'] = options[&quot;base_uri&quot;]
+			end
 			Dir.chdir(app_root)
+			if options[&quot;environment_variables&quot;]
+				set_passed_environment_variables(options[&quot;environment_variables&quot;])
+			end
 			if options[&quot;lower_privilege&quot;]
 				lower_privilege('config.ru', options[&quot;lowest_user&quot;])
 			end
@@ -116,6 +124,17 @@ private
 			end
 		end
 	end
+	
+	def set_passed_environment_variables(encoded_environment_variables)
+		env_vars_string = encoded_environment_variables.unpack(&quot;m&quot;).first
+		# Prevent empty string as last item from b0rking the Hash[...] statement.
+		# See comment in Hooks.cpp (sendHeaders) for details.
+		env_vars_string &lt;&lt; &quot;_\0_&quot;
+		env_vars = Hash[*env_vars_string.split(&quot;\0&quot;)]
+		env_vars.each_pair do |key, value|
+			ENV[key] = value
+		end
+	end
 
 	def load_rack_app
 		rackup_code = ::File.read(&quot;config.ru&quot;)</diff>
      <filename>lib/phusion_passenger/rack/application_spawner.rb</filename>
    </modified>
    <modified>
      <diff>@@ -45,6 +45,9 @@ class RequestHandler &lt; AbstractRequestHandler
 	PATH_INFO          = &quot;PATH_INFO&quot;           # :nodoc:
 	REQUEST_URI        = &quot;REQUEST_URI&quot;         # :nodoc:
 	QUESTION_MARK      = &quot;?&quot;                   # :nodoc:
+	QUERY_STRING       = &quot;QUERY_STRING&quot;        # :nodoc:
+	CONTENT_LENGTH      = &quot;CONTENT_LENGTH&quot;       # :nodoc:
+	HTTP_CONTENT_LENGTH = &quot;HTTP_CONTENT_LENGTH&quot;  # :nodoc:
 	HTTPS          = &quot;HTTPS&quot;  # :nodoc:
 	HTTPS_DOWNCASE = &quot;https&quot;  # :nodoc:
 	HTTP           = &quot;http&quot;   # :nodoc:
@@ -70,8 +73,15 @@ protected
 			env[RACK_MULTITHREAD]  = false
 			env[RACK_MULTIPROCESS] = true
 			env[RACK_RUN_ONCE]     = false
+			env[QUERY_STRING]    ||= &quot;&quot;
 			env[PATH_INFO]       ||= env[REQUEST_URI].split(QUESTION_MARK, 2).first
 			env[PATH_INFO].sub!(/^#{Regexp.escape(env[SCRIPT_NAME])}/, &quot;&quot;)
+			if env[HTTP_CONTENT_LENGTH] &amp;&amp; env[CONTENT_LENGTH]
+				env.delete(HTTP_CONTENT_LENGTH)
+			elsif env[HTTP_CONTENT_LENGTH] &amp;&amp; !env[CONTENT_LENGTH]
+				env[CONTENT_LENGTH] = env[HTTP_CONTENT_LENGTH]
+				env.delete(HTTP_CONTENT_LENGTH)
+			end
 			if env[HTTPS] == YES || env[HTTPS] == ON || env[HTTPS] == ONE
 				env[RACK_URL_SCHEME] = HTTPS_DOWNCASE
 			else</diff>
      <filename>lib/phusion_passenger/rack/request_handler.rb</filename>
    </modified>
    <modified>
      <diff>@@ -84,6 +84,20 @@ class ApplicationSpawner &lt; AbstractServer
 	# - +environment+:
 	#   Allows one to specify the RAILS_ENV environment to use.
 	#
+	# - +environment_variables+:
+	#   Environment variables which should be passed to the spawned application.
+	#   This is NULL-seperated string of key-value pairs, encoded in base64.
+	#   The last byte in the unencoded data must be a NULL.
+	#
+	# - +base_uri+:
+	#   The base URI on which this application is deployed. It equals &quot;/&quot;
+	#   string if the application is deployed on the root URI. It must not
+	#   equal the empty string.
+	#
+	# - +print_exceptions+:
+	#   Whether exceptions that have occurred during application initialization
+	#   should be printed to STDERR. The default is true.
+	#
 	# All other options will be passed on to RequestHandler.
 	def initialize(app_root, options = {})
 		super()
@@ -93,6 +107,9 @@ class ApplicationSpawner &lt; AbstractServer
 		@lower_privilege = @options[&quot;lower_privilege&quot;]
 		@lowest_user     = @options[&quot;lowest_user&quot;]
 		@environment     = @options[&quot;environment&quot;]
+		@encoded_environment_variables = @options[&quot;environment_variables&quot;]
+		@base_uri = @options[&quot;base_uri&quot;] if @options[&quot;base_uri&quot;] &amp;&amp; @options[&quot;base_uri&quot;] != &quot;/&quot;
+		@print_exceptions = @options[&quot;print_exceptions&quot;]
 		self.max_idle_time = DEFAULT_APP_SPAWNER_MAX_IDLE_TIME
 		assert_valid_app_root(@app_root)
 		define_message_handler(:spawn_application, :handle_spawn_application)
@@ -144,7 +161,11 @@ class ApplicationSpawner &lt; AbstractServer
 				channel = MessageChannel.new(b)
 				success = report_app_init_status(channel) do
 					ENV['RAILS_ENV'] = @environment
+					ENV['RAILS_RELATIVE_URL_ROOT'] = @base_uri
 					Dir.chdir(@app_root)
+					if @encoded_environment_variables
+						set_passed_environment_variables
+					end
 					if @lower_privilege
 						lower_privilege('config/environment.rb', @lowest_user)
 					end
@@ -170,7 +191,7 @@ class ApplicationSpawner &lt; AbstractServer
 		Process.waitpid(pid) rescue nil
 		
 		channel = MessageChannel.new(a)
-		unmarshal_and_raise_errors(channel)
+		unmarshal_and_raise_errors(channel, @print_exceptions)
 		
 		# No exception was raised, so spawning succeeded.
 		pid, socket_name, socket_type = channel.read
@@ -191,7 +212,7 @@ class ApplicationSpawner &lt; AbstractServer
 	def start
 		super
 		begin
-			unmarshal_and_raise_errors(server)
+			unmarshal_and_raise_errors(server, @print_exceptions)
 		rescue IOError, SystemCallError, SocketError =&gt; e
 			stop
 			raise Error, &quot;The application spawner server exited unexpectedly: #{e}&quot;
@@ -216,11 +237,15 @@ protected
 		report_app_init_status(client) do
 			$0 = &quot;Passenger ApplicationSpawner: #{@app_root}&quot;
 			ENV['RAILS_ENV'] = @environment
+			ENV['RAILS_RELATIVE_URL_ROOT'] = @base_uri
 			if defined?(RAILS_ENV)
 				Object.send(:remove_const, :RAILS_ENV)
 				Object.const_set(:RAILS_ENV, ENV['RAILS_ENV'])
 			end
 			Dir.chdir(@app_root)
+			if @encoded_environment_variables
+				set_passed_environment_variables
+			end
 			if @lower_privilege
 				lower_privilege('config/environment.rb', @lowest_user)
 			end
@@ -229,6 +254,17 @@ protected
 	end
 	
 private
+	def set_passed_environment_variables
+		env_vars_string = @encoded_environment_variables.unpack(&quot;m&quot;).first
+		# Prevent empty string as last item from b0rking the Hash[...] statement.
+		# See comment in Hooks.cpp (sendHeaders) for details.
+		env_vars_string &lt;&lt; &quot;_\0_&quot;
+		env_vars = Hash[*env_vars_string.split(&quot;\0&quot;)]
+		env_vars.each_pair do |key, value|
+			ENV[key] = value
+		end
+	end
+	
 	def preload_application
 		Object.const_set(:RAILS_ROOT, @canonicalized_app_root)
 		if defined?(::Rails::Initializer)</diff>
      <filename>lib/phusion_passenger/railz/application_spawner.rb</filename>
    </modified>
    <modified>
      <diff>@@ -58,6 +58,9 @@ class FrameworkSpawner &lt; AbstractServer
 	#   this version is actually installed.
 	# - &lt;tt&gt;:vendor&lt;/tt&gt;: The directory to the vendor Rails framework to use. This is
 	#   usually something like &quot;/webapps/foo/vendor/rails&quot;.
+	# - &lt;tt&gt;:print_framework_loading_exceptions&lt;/tt&gt;:
+	#   Whether exceptions that have occurred while loading the Ruby on Rails framework
+	#   should be printed to STDERR. The default is true.
 	#
 	# It is not allowed to specify both +version+ and +vendor+.
 	#
@@ -72,6 +75,11 @@ class FrameworkSpawner &lt; AbstractServer
 		end
 		@version = options[:version]
 		@vendor  = options[:vendor]
+		if options.has_key?(:print_framework_loading_exceptions)
+			@print_framework_loading_exceptions = options[:print_framework_loading_exceptions]
+		else
+			@print_framework_loading_exceptions = true
+		end
 		if !@version &amp;&amp; !@vendor
 			raise ArgumentError, &quot;Either the 'version' or the 'vendor' option must specified&quot;
 		elsif @version &amp;&amp; @vendor
@@ -87,7 +95,7 @@ class FrameworkSpawner &lt; AbstractServer
 	# Overrided from AbstractServer#start.
 	#
 	# May raise these additional exceptions:
-	# - FrameworkInitError: The specified Ruby on Rails framework could not be loaded.
+	# - FrameworkInitError: An error occurred while loading the specified Ruby on Rails framework.
 	# - FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly.
 	def start
 		super
@@ -109,6 +117,9 @@ class FrameworkSpawner &lt; AbstractServer
 						&quot;#{child_exception.class} (#{child_exception.message})&quot;
 				end
 				options = { :vendor =&gt; @vendor, :version =&gt; @version }
+				if @print_framework_loading_exceptions
+					print_exception(self.class.to_s, child_exception)
+				end
 				raise FrameworkInitError.new(message, child_exception, options)
 			end
 		rescue IOError, SystemCallError, SocketError
@@ -122,23 +133,7 @@ class FrameworkSpawner &lt; AbstractServer
 	# When successful, an Application object will be returned, which represents
 	# the spawned RoR application.
 	#
-	# The following options are allowed:
-	# - +lower_privilege+ and +lowest_user+:
-	#   If +lower_privilege+ is true, then ApplicationSpawner will attempt to
-	#   switch to the user who owns the application's &lt;tt&gt;config/environment.rb&lt;/tt&gt;,
-	#   and to the default group of that user.
-	#
-	#   If that user doesn't exist on the system, or if that user is root,
-	#   then ApplicationSpawner will attempt to switch to the username given by
-	#   +lowest_user+ (and to the default group of that user).
-	#   If +lowest_user+ doesn't exist either, or if switching user failed
-	#   (because the current process does not have the privilege to do so),
-	#   then ApplicationSpawner will continue without reporting an error.
-	#
-	# - +environment+:
-	#   Allows one to specify the RAILS_ENV environment to use.
-	#
-	# All other options will be passed on to ApplicationSpawner and RequestHandler.
+	# All options accepted by ApplicationSpawner.new and RequestHandler.new are accepted.
 	#
 	# FrameworkSpawner will internally cache the code of applications, in order to
 	# speed up future spawning attempts. This implies that, if you've changed
@@ -148,7 +143,7 @@ class FrameworkSpawner &lt; AbstractServer
 	#
 	# Raises:
 	# - AbstractServer::ServerNotStarted: The FrameworkSpawner server hasn't already been started.
-	# - InvalidAppRoot: +app_root+ doesn't appear to be a valid Ruby on Rails application root.
+	# - InvalidPath: +app_root+ doesn't appear to be a valid Ruby on Rails application root.
 	# - AppInitError: The application raised an exception or called exit() during startup.
 	# - ApplicationSpawner::Error: The ApplicationSpawner server exited unexpectedly.
 	# - FrameworkSpawner::Error: The FrameworkSpawner server exited unexpectedly.
@@ -156,8 +151,12 @@ class FrameworkSpawner &lt; AbstractServer
 		assert_valid_app_root(app_root)
 		options = sanitize_spawn_options(options)
 		options[&quot;app_root&quot;] = app_root
+		# No need for the ApplicationSpawner to print exceptions. All
+		# exceptions raised by the ApplicationSpawner are sent back here,
+		# so we just need to decide here whether we want to print it.
+		print_exceptions = options[&quot;print_exceptions&quot;]
+		options[&quot;print_exceptions&quot;] = false
 		
-		exception_to_propagate = nil
 		begin
 			server.write(&quot;spawn_application&quot;, *options.to_a.flatten)
 			result = server.read
@@ -166,8 +165,10 @@ class FrameworkSpawner &lt; AbstractServer
 			end
 			if result[0] == 'exception'
 				e = unmarshal_exception(server.read_scalar)
-				if e.respond_to?(:child_exception) &amp;&amp; e.child_exception
-					#print_exception(self.class.to_s, e.child_exception)
+				if print_exceptions &amp;&amp; e.respond_to?(:child_exception) &amp;&amp; e.child_exception
+					print_exception(self.class.to_s, e.child_exception)
+				elsif print_exceptions
+					print_exception(self.class.to_s, e)
 				end
 				raise e
 			else
@@ -276,10 +277,7 @@ private
 	end
 
 	def handle_spawn_application(*options)
-		options = Hash[*options]
-		options[&quot;lower_privilege&quot;]     = options[&quot;lower_privilege&quot;] == &quot;true&quot;
-		options[&quot;app_spawner_timeout&quot;] = options[&quot;app_spawner_timeout&quot;].to_i
-		options[&quot;memory_limit&quot;]        = options[&quot;memory_limit&quot;].to_i
+		options = sanitize_spawn_options(Hash[*options])
 		
 		app = nil
 		app_root = options[&quot;app_root&quot;]
@@ -293,10 +291,10 @@ private
 					spawner.start
 					spawner
 				end
-			rescue ArgumentError, AppInitError, ApplicationSpawner::Error =&gt; e
+			rescue InvalidPath, AppInitError, ApplicationSpawner::Error =&gt; e
 				client.write('exception')
 				client.write_scalar(marshal_exception(e))
-				if e.child_exception.is_a?(LoadError)
+				if e.respond_to?(:child_exception) &amp;&amp; e.child_exception.is_a?(LoadError)
 					# A source file failed to load, maybe because of a
 					# missing gem. If that's the case then the sysadmin
 					# will install probably the gem. So we clear RubyGems's</diff>
      <filename>lib/phusion_passenger/railz/framework_spawner.rb</filename>
    </modified>
    <modified>
      <diff>@@ -28,7 +28,10 @@ module Railz
 
 # A request handler for Ruby on Rails applications.
 class RequestHandler &lt; AbstractRequestHandler
-	NINJA_PATCHING_LOCK = Mutex.new
+	CONTENT_LENGTH      = 'CONTENT_LENGTH'      # :nodoc:
+	HTTP_CONTENT_LENGTH = 'HTTP_CONTENT_LENGTH' # :nodoc:
+	
+	NINJA_PATCHING_LOCK = Mutex.new             # :nodoc:
 	@@ninja_patched_action_controller = false
 	
 	def initialize(owner_pipe, options = {})
@@ -41,6 +44,7 @@ class RequestHandler &lt; AbstractRequestHandler
 protected
 	# Overrided method.
 	def process_request(headers, input, output)
+		headers[CONTENT_LENGTH] = headers[HTTP_CONTENT_LENGTH]
 		cgi = CGIFixed.new(headers, input, output)
 		::Dispatcher.dispatch(cgi,
 			::ActionController::CgiRequest::DEFAULT_SESSION_OPTIONS,</diff>
      <filename>lib/phusion_passenger/railz/request_handler.rb</filename>
    </modified>
    <modified>
      <diff>@@ -97,7 +97,7 @@ class SpawnManager &lt; AbstractServer
 	#
 	# Other options are:
 	#
-	# ['lower_privilege', 'lowest_user' and 'environment']
+	# ['lower_privilege', 'lowest_user', 'environment', 'environment_variables', 'base_uri' and 'print_exceptions']
 	#   See Railz::ApplicationSpawner.new for an explanation of these options.
 	# 
 	# ['app_type']
@@ -234,7 +234,11 @@ private
 			else
 				key = &quot;version:#{framework_version}&quot;
 				create_spawner = proc do
-					Railz::FrameworkSpawner.new(:version =&gt; framework_version)
+					framework_options = { :version =&gt; framework_version }
+					if options.has_key?(:print_framework_loading_exceptions)
+						framework_options[:print_framework_loading_exceptions] = options[:print_framework_loading_exceptions]
+					end
+					Railz::FrameworkSpawner.new(framework_options)
 				end
 				spawner_timeout = options[&quot;framework_spawner_timeout&quot;]
 			end</diff>
      <filename>lib/phusion_passenger/spawn_manager.rb</filename>
    </modified>
    <modified>
      <diff>@@ -154,10 +154,10 @@ protected
 	#
 	# +current_location+ is a string which describes where the code is
 	# currently at. Usually the current class name will be enough.
-	def print_exception(current_location, exception)
+	def print_exception(current_location, exception, destination = STDERR)
 		if !exception.is_a?(SystemExit)
-			STDERR.puts(exception.backtrace_string(current_location))
-			STDERR.flush
+			destination.puts(exception.backtrace_string(current_location))
+			destination.flush if destination.respond_to?(:flush)
 		end
 	end
 	
@@ -178,9 +178,11 @@ protected
 				if double_fork
 					pid2 = fork
 					if pid2.nil?
+						srand
 						yield
 					end
 				else
+					srand
 					yield
 				end
 			rescue Exception =&gt; e
@@ -201,9 +203,21 @@ protected
 	# Run the given block. A message will be sent through +channel+ (a
 	# MessageChannel object), telling the remote side whether the block
 	# raised an exception, called exit(), or succeeded.
-	# Returns whether the block succeeded.
-	# Exceptions are not propagated, except for SystemExit.
-	def report_app_init_status(channel)
+	#
+	# Anything written to $stderr and STDERR during execution of the block
+	# will be buffered. If &lt;tt&gt;write_stderr_contents_to&lt;/tt&gt; is non-nil,
+	# then the buffered stderr data will be written to this object. In this
+	# case, &lt;tt&gt;write_stderr_contents_to&lt;/tt&gt; must be an IO-like object.
+	# If &lt;tt&gt;write_stderr_contents_to&lt;/tt&gt; is nil, then the stder data will
+	# be discarded.
+	# 
+	# Returns whether the block succeeded, i.e. whether it didn't raise an
+	# exception.
+	#
+	# Exceptions are not propagated, except SystemExit and a few
+	# non-StandardExeption classes such as SignalException. Of the
+	# exceptions that are propagated, only SystemExit will be reported.
+	def report_app_init_status(channel, write_stderr_contents_to = STDERR)
 		begin
 			old_global_stderr = $stderr
 			old_stderr = STDERR
@@ -218,10 +232,13 @@ protected
 				Object.send(:remove_const, 'STDERR') rescue nil
 				Object.const_set('STDERR', old_stderr)
 				$stderr = old_global_stderr
-				if tempfile
-					tempfile.rewind
-					stderr_output = tempfile.read
-					tempfile.close rescue nil
+				tempfile.rewind
+				stderr_output = tempfile.read
+				tempfile.close rescue nil
+				
+				if write_stderr_contents_to
+					write_stderr_contents_to.write(stderr_output)
+					write_stderr_contents_to.flush
 				end
 			end
 			channel.write('success')
@@ -247,10 +264,24 @@ protected
 	# received information, then an appropriate exception will be
 	# raised.
 	#
+	# If &lt;tt&gt;print_exception&lt;/tt&gt; evaluates to true, then the
+	# exception message and the backtrace will also be printed.
+	# Where it is printed to depends on the type of
+	# &lt;tt&gt;print_exception&lt;/tt&gt;:
+	# - If it responds to #puts, then the exception information will
+	#   be printed using this method.
+	# - If it responds to #to_str, then the exception information
+	#   will be appended to the file whose filename equals the return
+	#   value of the #to_str call.
+	# - Otherwise, it will be printed to STDERR.
+	#
 	# Raises:
-	# - AppInitError
-	# - IOError, SystemCallError, SocketError
-	def unmarshal_and_raise_errors(channel, app_type = &quot;rails&quot;)
+	# - AppInitError: this class wraps the exception information
+	#   received through the channel.
+	# - IOError, SystemCallError, SocketError: these errors are
+	#   raised if an error occurred while receiving the information
+	#   through the channel.
+	def unmarshal_and_raise_errors(channel, print_exception = nil, app_type = &quot;rails&quot;)
 		args = channel.read
 		if args.nil?
 			raise EOFError, &quot;Unexpected end-of-file detected.&quot;
@@ -259,8 +290,7 @@ protected
 		if status == 'exception'
 			child_exception = unmarshal_exception(channel.read_scalar)
 			stderr = channel.read_scalar
-			#print_exception(self.class.to_s, child_exception)
-			raise AppInitError.new(
+			exception = AppInitError.new(
 				&quot;Application '#{@app_root}' raised an exception: &quot; &lt;&lt;
 				&quot;#{child_exception.class} (#{child_exception.message})&quot;,
 				child_exception,
@@ -269,9 +299,25 @@ protected
 		elsif status == 'exit'
 			child_exception = unmarshal_exception(channel.read_scalar)
 			stderr = channel.read_scalar
-			raise AppInitError.new(&quot;Application '#{@app_root}' exited during startup&quot;,
+			exception = AppInitError.new(&quot;Application '#{@app_root}' exited during startup&quot;,
 				child_exception, app_type, stderr.empty? ? nil : stderr)
+		else
+			exception = nil
+		end
+		
+		if print_exception &amp;&amp; exception
+			if print_exception.respond_to?(:puts)
+				print_exception(self.class.to_s, child_exception, print_exception)
+			elsif print_exception.respond_to?(:to_str)
+				filename = print_exception.to_str
+				File.open(filename, 'a') do |f|
+					print_exception(self.class.to_s, child_exception, f)
+				end
+			else
+				print_exception(self.class.to_s, child_exception)
+			end
 		end
+		raise exception if exception
 	end
 	
 	# Lower the current process's privilege to the owner of the given file.
@@ -320,6 +366,10 @@ protected
 		end
 	end
 	
+	def to_boolean(value)
+		return !(value.nil? || value == false || value == &quot;false&quot;)
+	end
+	
 	def sanitize_spawn_options(options)
 		defaults = {
 			&quot;lower_privilege&quot; =&gt; true,
@@ -328,12 +378,15 @@ protected
 			&quot;app_type&quot;        =&gt; &quot;rails&quot;,
 			&quot;spawn_method&quot;    =&gt; &quot;smart-lv2&quot;,
 			&quot;framework_spawner_timeout&quot; =&gt; -1,
-			&quot;app_spawner_timeout&quot;       =&gt; -1
+			&quot;app_spawner_timeout&quot;       =&gt; -1,
+			&quot;print_exceptions&quot; =&gt; true
 		}
 		options = defaults.merge(options)
-		options[&quot;lower_privilege&quot;]           = options[&quot;lower_privilege&quot;].to_s == &quot;true&quot;
+		options[&quot;lower_privilege&quot;]           = to_boolean(options[&quot;lower_privilege&quot;])
 		options[&quot;framework_spawner_timeout&quot;] = options[&quot;framework_spawner_timeout&quot;].to_i
 		options[&quot;app_spawner_timeout&quot;]       = options[&quot;app_spawner_timeout&quot;].to_i
+		# Force this to be a boolean for easy use with Utils#unmarshal_and_raise_errors.
+		options[&quot;print_exceptions&quot;]          = to_boolean(options[&quot;print_exceptions&quot;])
 		return options
 	end
 	
@@ -367,6 +420,8 @@ protected
 	def self.passenger_tmpdir=(dir)
 		@@passenger_tmpdir = dir
 	end
+	
+	####################################
 end
 
 end # module PhusionPassenger
@@ -479,16 +534,13 @@ end
 module Signal
 	# Like Signal.list, but only returns signals that we can actually trap.
 	def self.list_trappable
-		ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : &quot;mri&quot;
+		ruby_engine = defined?(RUBY_ENGINE) ? RUBY_ENGINE : &quot;ruby&quot;
 		case ruby_engine
-		when &quot;mri&quot;
-			if RUBY_VERSION &gt;= '1.9.0'
-				return Signal.list
-			else
-				result = Signal.list
-				result.delete(&quot;ALRM&quot;)
-				return result
-			end
+		when &quot;ruby&quot;
+			result = Signal.list
+			result.delete(&quot;ALRM&quot;)
+			result.delete(&quot;VTALRM&quot;)
+			return result
 		when &quot;jruby&quot;
 			result = Signal.list
 			result.delete(&quot;QUIT&quot;)</diff>
      <filename>lib/phusion_passenger/utils.rb</filename>
    </modified>
    <modified>
      <diff>@@ -23,11 +23,11 @@
 
 # Rake functions for compiling/linking C++ stuff.
 
-def compile_c(source, flags = CXXFLAGS)
-	sh &quot;#{CXX} #{flags} -c #{source}&quot;
+def compile_c(source, flags = &quot;#{PlatformInfo.portability_cflags} #{EXTRA_CXXFLAGS}&quot;)
+	sh &quot;#{CC} #{flags} -c #{source}&quot;
 end
 
-def compile_cxx(source, flags = CXXFLAGS)
+def compile_cxx(source, flags = &quot;#{PlatformInfo.portability_cflags} #{EXTRA_CXXFLAGS}&quot;)
 	sh &quot;#{CXX} #{flags} -c #{source}&quot;
 end
 
@@ -43,11 +43,11 @@ def create_static_library(target, sources)
 	sh &quot;ranlib #{target}&quot;
 end
 
-def create_executable(target, sources, linkflags = LDFLAGS)
+def create_executable(target, sources, linkflags = &quot;#{PlatformInfo.portability_cflags} #{EXTRA_CXXFLAGS} #{PlatformInfo.portability_ldflags} #{EXTRA_LDFLAGS}&quot;)
 	sh &quot;#{CXX} #{sources} -o #{target} #{linkflags}&quot;
 end
 
-def create_shared_library(target, sources, flags = LDFLAGS)
+def create_shared_library(target, sources, flags = &quot;#{PlatformInfo.portability_cflags} #{EXTRA_CXXFLAGS} #{PlatformInfo.portability_ldflags} #{EXTRA_LDFLAGS}&quot;)
 	if RUBY_PLATFORM =~ /darwin/
 		shlib_flag = &quot;-flat_namespace -bundle -undefined dynamic_lookup&quot;
 	else</diff>
      <filename>misc/rake/cplusplus.rb</filename>
    </modified>
    <modified>
      <diff>@@ -68,5 +68,47 @@ namespace tut {
 			}
 		}
 	}
+	
+	/* A StringListCreator which not only returns a dummy value, but also
+	 * increments a counter each time getItems() is called. */
+	class DummyStringListCreator: public StringListCreator {
+	public:
+		mutable int counter;
+		
+		DummyStringListCreator() {
+			counter = 0;
+		}
+		
+		virtual const StringListPtr getItems() const {
+			StringListPtr result = ptr(new StringList());
+			counter++;
+			result-&gt;push_back(&quot;hello&quot;);
+			result-&gt;push_back(&quot;world&quot;);
+			return result;
+		}
+	};
+	
+	TEST_METHOD(5) {
+		// When calling get() with a PoolOptions object,
+		// options.environmentVariables-&gt;getItems() isn't called unless
+		// the pool had to spawn something.
+		ApplicationPoolServerPtr server = ptr(new ApplicationPoolServer(
+			&quot;./ApplicationPoolServerExecutable&quot;,
+			&quot;../bin/passenger-spawn-server&quot;));
+		ApplicationPoolPtr pool = server-&gt;connect();
+		
+		shared_ptr&lt;DummyStringListCreator&gt; strList = ptr(new DummyStringListCreator());
+		PoolOptions options(&quot;stub/rack&quot;);
+		options.appType = &quot;rack&quot;;
+		options.environmentVariables = strList;
+		
+		Application::SessionPtr session1 = pool-&gt;get(options);
+		session1.reset();
+		ensure_equals(strList-&gt;counter, 1);
+		
+		session1 = pool-&gt;get(options);
+		session1.reset();
+		ensure_equals(strList-&gt;counter, 1);
+	}
 }
 </diff>
      <filename>test/ApplicationPoolServerTest.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -12,7 +12,8 @@
  * This file is used as a template to test the different ApplicationPool implementations.
  * It is #included in StandardApplicationPoolTest.cpp and ApplicationServer_ApplicationPoolTest.cpp
  */
-#ifdef USE_TEMPLATE	
+#ifdef USE_TEMPLATE
+	/** Create some stub request headers. */
 	static string createRequestHeaders(const char *uri = &quot;/foo/new&quot;) {
 		string headers;
 		#define ADD_HEADER(name, value) \
@@ -25,29 +26,11 @@
 		ADD_HEADER(&quot;REQUEST_URI&quot;, uri);
 		ADD_HEADER(&quot;REQUEST_METHOD&quot;, &quot;GET&quot;);
 		ADD_HEADER(&quot;REMOTE_ADDR&quot;, &quot;localhost&quot;);
+		ADD_HEADER(&quot;SCRIPT_NAME&quot;, &quot;&quot;);
 		ADD_HEADER(&quot;PATH_INFO&quot;, uri);
 		return headers;
 	}
 	
-	static string readAll(int fd) {
-		string result;
-		char buf[1024 * 32];
-		ssize_t ret;
-		while (true) {
-			do {
-				ret = read(fd, buf, sizeof(buf));
-			} while (ret == -1 &amp;&amp; errno == EINTR);
-			if (ret == 0) {
-				break;
-			} else if (ret == -1) {
-				throw SystemException(&quot;Cannot read from socket&quot;, errno);
-			} else {
-				result.append(buf, ret);
-			}
-		}
-		return result;
-	}
-	
 	static Application::SessionPtr spawnRackApp(ApplicationPoolPtr pool, const char *appRoot) {
 		PoolOptions options;
 		options.appRoot = appRoot;
@@ -61,17 +44,17 @@
 		options.appType = &quot;wsgi&quot;;
 		return pool-&gt;get(options);
 	}
-
+	
 	TEST_METHOD(1) {
 		// Calling ApplicationPool.get() once should return a valid Session.
-		Application::SessionPtr session(pool-&gt;get(&quot;stub/railsapp&quot;));
+		Application::SessionPtr session(spawnRackApp(pool, &quot;stub/rack&quot;));
 		session-&gt;sendHeaders(createRequestHeaders());
 		session-&gt;shutdownWriter();
 
 		int reader = session-&gt;getStream();
 		string result(readAll(reader));
 		session-&gt;closeStream();
-		ensure(result.find(&quot;hello world&quot;) != string::npos);
+		ensure(result.find(&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;) != string::npos);
 	}
 	
 	TEST_METHOD(2) {
@@ -99,26 +82,29 @@
 		// If we call get() with an application root, then we call get() again before closing
 		// the session, then the pool should have spawned 2 apps in total.
 		Application::SessionPtr session(spawnRackApp(pool, &quot;stub/rack&quot;));
-		Application::SessionPtr session2(spawnRackApp(pool, &quot;stub/rack&quot;));
+		Application::SessionPtr session2(spawnRackApp(pool2, &quot;stub/rack&quot;));
 		ensure_equals(pool-&gt;getCount(), 2u);
 	}
 	
 	TEST_METHOD(5) {
 		// If we call get() twice with different application roots,
 		// then the pool should spawn two different apps.
-		Application::SessionPtr session(pool-&gt;get(&quot;stub/railsapp&quot;));
-		Application::SessionPtr session2(pool2-&gt;get(&quot;stub/railsapp2&quot;));
+		TempDirCopy c1(&quot;stub/rack&quot;, &quot;rackapp1.tmp&quot;);
+		TempDirCopy c2(&quot;stub/rack&quot;, &quot;rackapp2.tmp&quot;);
+		replaceStringInFile(&quot;rackapp2.tmp/config.ru&quot;, &quot;world&quot;, &quot;world 2&quot;);
+		Application::SessionPtr session(spawnRackApp(pool, &quot;rackapp1.tmp&quot;));
+		Application::SessionPtr session2(spawnRackApp(pool2, &quot;rackapp2.tmp&quot;));
 		ensure_equals(&quot;Before the sessions were closed, both apps were busy&quot;, pool-&gt;getActive(), 2u);
 		ensure_equals(&quot;Before the sessions were closed, both apps were in the pool&quot;, pool-&gt;getCount(), 2u);
 		
 		session-&gt;sendHeaders(createRequestHeaders());
 		string result(readAll(session-&gt;getStream()));
-		ensure(&quot;Session 1 belongs to the correct app&quot;, result.find(&quot;hello world&quot;) != string::npos);
+		ensure(&quot;Session 1 belongs to the correct app&quot;, result.find(&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;) != string::npos);
 		session.reset();
 		
 		session2-&gt;sendHeaders(createRequestHeaders());
 		result = readAll(session2-&gt;getStream());
-		ensure(&quot;Session 2 belongs to the correct app&quot;, result.find(&quot;this is railsapp2&quot;) != string::npos);
+		ensure(&quot;Session 2 belongs to the correct app&quot;, result.find(&quot;hello &lt;b&gt;world 2&lt;/b&gt;&quot;) != string::npos);
 		session2.reset();
 	}
 	
@@ -126,12 +112,14 @@
 		// If we call get() twice with different application roots,
 		// and we close both sessions, then both 2 apps should still
 		// be in the pool.
-		Application::SessionPtr session(pool-&gt;get(&quot;stub/railsapp&quot;));
-		Application::SessionPtr session2(pool-&gt;get(&quot;stub/railsapp2&quot;));
+		TempDirCopy c1(&quot;stub/rack&quot;, &quot;rackapp1.tmp&quot;);
+		TempDirCopy c2(&quot;stub/rack&quot;, &quot;rackapp2.tmp&quot;);
+		Application::SessionPtr session(spawnRackApp(pool, &quot;rackapp1.tmp&quot;));
+		Application::SessionPtr session2(spawnRackApp(pool, &quot;rackapp2.tmp&quot;));
 		session.reset();
 		session2.reset();
-		ensure_equals(pool-&gt;getActive(), 0u);
-		ensure_equals(pool-&gt;getCount(), 2u);
+		ensure_equals(&quot;There are 0 active apps&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;There are 2 apps in total&quot;, pool-&gt;getCount(), 2u);
 	}
 	
 	TEST_METHOD(7) {
@@ -150,26 +138,26 @@
 		// But the get() thereafter should not:
 		// ApplicationPool should have spawned a new instance
 		// after detecting that the original one died.
-		Application::SessionPtr session(pool-&gt;get(&quot;stub/railsapp&quot;));
+		Application::SessionPtr session(spawnRackApp(pool, &quot;stub/rack&quot;));
 		kill(session-&gt;getPid(), SIGTERM);
 		session.reset();
 		try {
-			session = pool-&gt;get(&quot;stub/railsapp&quot;);
+			session = spawnRackApp(pool, &quot;stub/rack&quot;);
 			fail(&quot;ApplicationPool::get() is supposed to &quot;
 				&quot;throw an exception because we killed &quot;
 				&quot;the app instance.&quot;);
 		} catch (const exception &amp;e) {
-			session = pool-&gt;get(&quot;stub/railsapp&quot;);
+			session = spawnRackApp(pool, &quot;stub/rack&quot;);
 			// Should not throw.
 		}
 	}
 	
-	struct TestThread1 {
+	struct PoolWaitTestThread {
 		ApplicationPoolPtr pool;
 		Application::SessionPtr &amp;m_session;
 		bool &amp;m_done;
 		
-		TestThread1(const ApplicationPoolPtr &amp;pool,
+		PoolWaitTestThread(const ApplicationPoolPtr &amp;pool,
 			Application::SessionPtr &amp;session,
 			bool &amp;done)
 		: m_session(session), m_done(done) {
@@ -194,12 +182,13 @@
 		Application::SessionPtr session3;
 		bool done;
 		
-		thread *thr = new thread(TestThread1(pool2, session3, done));
+		shared_ptr&lt;thread&gt; thr = ptr(new thread(PoolWaitTestThread(pool2, session3, done)));
 		usleep(500000);
-		ensure(&quot;ApplicationPool is waiting&quot;, !done);
+		ensure(&quot;ApplicationPool is still waiting&quot;, !done);
 		ensure_equals(pool-&gt;getActive(), 2u);
 		ensure_equals(pool-&gt;getCount(), 2u);
 		
+		// Now release one slot from the pool.
 		session1.reset();
 		
 		// Wait at most 10 seconds.
@@ -213,7 +202,7 @@
 		ensure_equals(pool-&gt;getCount(), 2u);
 		
 		thr-&gt;join();
-		delete thr;
+		thr.reset();
 	}
 	
 	TEST_METHOD(10) {
@@ -221,25 +210,27 @@
 		// * the pool is already full, but there are inactive apps
 		//   (active &lt; count &amp;&amp; count == max)
 		// and
-		// * the application root is *not* already in the pool
+		// * the application root for this get() is *not* already in the pool
 		// then the an inactive app should be killed in order to
 		// satisfy this get() command.
+		TempDirCopy c1(&quot;stub/rack&quot;, &quot;rackapp1.tmp&quot;);
+		TempDirCopy c2(&quot;stub/rack&quot;, &quot;rackapp2.tmp&quot;);
 		pool-&gt;setMax(2);
-		Application::SessionPtr session1(pool-&gt;get(&quot;stub/railsapp&quot;));
-		Application::SessionPtr session2(pool-&gt;get(&quot;stub/railsapp&quot;));
+		Application::SessionPtr session1(spawnRackApp(pool, &quot;rackapp1.tmp&quot;));
+		Application::SessionPtr session2(spawnRackApp(pool, &quot;rackapp1.tmp&quot;));
 		session1.reset();
 		session2.reset();
 		
 		ensure_equals(pool-&gt;getActive(), 0u);
 		ensure_equals(pool-&gt;getCount(), 2u);
-		session1 = pool2-&gt;get(&quot;stub/railsapp2&quot;);
+		session1 = spawnRackApp(pool, &quot;rackapp2.tmp&quot;);
 		ensure_equals(pool-&gt;getActive(), 1u);
 		ensure_equals(pool-&gt;getCount(), 2u);
 	}
 	
 	TEST_METHOD(11) {
-		// Test whether Session is still usable after the Application has been destroyed.
-		Application::SessionPtr session(pool-&gt;get(&quot;stub/railsapp&quot;));
+		// A Session should still be usable after the pool has been destroyed.
+		Application::SessionPtr session(spawnRackApp(pool, &quot;stub/rack&quot;));
 		pool-&gt;clear();
 		pool.reset();
 		pool2.reset();
@@ -250,32 +241,27 @@
 		int reader = session-&gt;getStream();
 		string result(readAll(reader));
 		session-&gt;closeStream();
-		ensure(result.find(&quot;hello world&quot;) != string::npos);
+		ensure(result.find(&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;) != string::npos);
 	}
 	
 	TEST_METHOD(12) {
 		// If tmp/restart.txt didn't exist but has now been created,
 		// then the applications under app_root should be restarted.
 		struct stat buf;
-		Application::SessionPtr session1 = pool-&gt;get(&quot;stub/railsapp&quot;);
-		Application::SessionPtr session2 = pool2-&gt;get(&quot;stub/railsapp&quot;);
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session2 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
 		session1.reset();
 		session2.reset();
 		
-		system(&quot;touch stub/railsapp/tmp/restart.txt&quot;);
-		pool-&gt;get(&quot;stub/railsapp&quot;);
+		touchFile(&quot;rackapp.tmp/tmp/restart.txt&quot;);
+		spawnRackApp(pool, &quot;rackapp.tmp&quot;);
 		
 		ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
 		ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
 			pool-&gt;getCount(), 1u);
-		try {
-			ensure(&quot;Restart file still exists&quot;,
-				stat(&quot;stub/railsapp/tmp/restart.txt&quot;, &amp;buf) == 0);
-			unlink(&quot;stub/railsapp/tmp/restart.txt&quot;);
-		} catch (...) {
-			unlink(&quot;stub/railsapp/tmp/restart.txt&quot;);
-			throw;
-		}
+		ensure(&quot;Restart file still exists&quot;,
+			stat(&quot;rackapp.tmp/tmp/restart.txt&quot;, &amp;buf) == 0);
 	}
 	
 	TEST_METHOD(13) {
@@ -284,56 +270,213 @@
 		// should still be restarted. However, a subsequent get()
 		// should not result in a restart.
 		pid_t old_pid;
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		TempDir d(&quot;rackapp.tmp/tmp/restart.txt&quot;);
+		Application::SessionPtr session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		old_pid = session-&gt;getPid();
+		session.reset();
 		
-		system(&quot;mkdir -p stub/railsapp/tmp/restart.txt&quot;);
-		try {
-			Application::SessionPtr session = pool-&gt;get(&quot;stub/railsapp&quot;);
-			old_pid = session-&gt;getPid();
-			session.reset();
-			
-			struct utimbuf buf;
-			buf.actime = time(NULL) - 10;
-			buf.modtime = time(NULL) - 10;
-			utime(&quot;stub/railsapp/tmp/restart.txt&quot;, &amp;buf);
-			
-			session = pool-&gt;get(&quot;stub/railsapp&quot;);
-			ensure(&quot;The app was restarted&quot;, session-&gt;getPid() != old_pid);
-			old_pid = session-&gt;getPid();
-			session.reset();
-			
-			session = pool-&gt;get(&quot;stub/railsapp&quot;);
-			ensure_equals(&quot;The app was not restarted&quot;,
-				old_pid, session-&gt;getPid());
-		} catch (...) {
-			system(&quot;rmdir stub/railsapp/tmp/restart.txt&quot;);
-			throw;
-		}
+		touchFile(&quot;rackapp.tmp/tmp/restart.txt&quot;, 10);
+		
+		session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		ensure(&quot;The app was restarted&quot;, session-&gt;getPid() != old_pid);
+		old_pid = session-&gt;getPid();
+		session.reset();
+		
+		session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		ensure_equals(&quot;The app was not restarted&quot;,
+			old_pid, session-&gt;getPid());
 	}
 	
 	TEST_METHOD(15) {
-		// Test whether restarting really results in code reload.
-		DeleteFileEventually f1(&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		DeleteFileEventually f2(&quot;stub/railsapp/tmp/restart.txt&quot;);
-		
-		system(&quot;cp -f stub/railsapp/app/controllers/bar_controller_1.txt &quot;
-			&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		Application::SessionPtr session = pool-&gt;get(&quot;stub/railsapp&quot;);
-		session-&gt;sendHeaders(createRequestHeaders(&quot;/bar&quot;));
+		// Test whether restarting with restart.txt really results in code reload.
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session-&gt;sendHeaders(createRequestHeaders());
 		string result = readAll(session-&gt;getStream());
-		ensure(result.find(&quot;bar 1!&quot;) != string::npos);
+		ensure(result.find(&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;) != string::npos);
 		session.reset();
 		
-		system(&quot;cp -f stub/railsapp/app/controllers/bar_controller_2.txt &quot;
-			&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		system(&quot;touch stub/railsapp/tmp/restart.txt&quot;);
+		touchFile(&quot;rackapp.tmp/tmp/restart.txt&quot;);
+		replaceStringInFile(&quot;rackapp.tmp/config.ru&quot;, &quot;world&quot;, &quot;world 2&quot;);
 		
-		session = pool-&gt;get(&quot;stub/railsapp&quot;);
-		session-&gt;sendHeaders(createRequestHeaders(&quot;/bar&quot;));
+		session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session-&gt;sendHeaders(createRequestHeaders());
 		result = readAll(session-&gt;getStream());
-		ensure(&quot;App code has been reloaded&quot;, result.find(&quot;bar 2!&quot;) != string::npos);
+		ensure(&quot;App code has been reloaded&quot;, result.find(&quot;hello &lt;b&gt;world 2&lt;/b&gt;&quot;) != string::npos);
 	}
 	
 	TEST_METHOD(16) {
+		// If tmp/always_restart.txt is present and is a file,
+		// then the application under app_root should be always restarted.
+		struct stat buf;
+		pid_t old_pid;
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session2 = spawnRackApp(pool2, &quot;rackapp.tmp&quot;);
+		session1.reset();
+		session2.reset();
+		
+		touchFile(&quot;rackapp.tmp/tmp/always_restart.txt&quot;);
+		
+		// This get() results in a restart.
+		session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		old_pid = session1-&gt;getPid();
+		session1.reset();
+		ensure_equals(&quot;First restart: no apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;First restart: the first 2 apps were killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;always_restart file has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
+		
+		// This get() results in a restart as well.
+		session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		ensure(old_pid != session1-&gt;getPid());
+		session1.reset();
+		ensure_equals(&quot;Second restart: no apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;Second restart: the last app was killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;always_restart file has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
+	}
+	
+	TEST_METHOD(17) {
+		// If tmp/always_restart.txt is present and is a directory,
+		// then the application under app_root should be always restarted.
+		struct stat buf;
+		pid_t old_pid;
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session2 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session1.reset();
+		session2.reset();
+		
+		TempDir d(&quot;rackapp.tmp/tmp/always_restart.txt&quot;);
+		
+		// This get() results in a restart.
+		session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		old_pid = session1-&gt;getPid();
+		session1.reset();
+		ensure_equals(&quot;First restart: no apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;First restart: the first 2 apps were killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;always_restart directory has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
+		
+		// This get() results in a restart as well.
+		session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		ensure(old_pid != session1-&gt;getPid());
+		session1.reset();
+		ensure_equals(&quot;Second restart: no apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;Second restart: the last app was killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;always_restart directory has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
+	}
+	
+	TEST_METHOD(18) {
+		// Test whether restarting with tmp/always_restart.txt really results in code reload.
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session-&gt;sendHeaders(createRequestHeaders());
+		string result = readAll(session-&gt;getStream());
+		ensure(result.find(&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;) != string::npos);
+		session.reset();
+
+		touchFile(&quot;rackapp.tmp/tmp/always_restart.txt&quot;);
+		replaceStringInFile(&quot;rackapp.tmp/config.ru&quot;, &quot;world&quot;, &quot;world 2&quot;);
+		
+		session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session-&gt;sendHeaders(createRequestHeaders());
+		result = readAll(session-&gt;getStream());
+		ensure(&quot;App code has been reloaded (1)&quot;, result.find(&quot;hello &lt;b&gt;world 2&lt;/b&gt;&quot;) != string::npos);
+		session.reset();
+		
+		replaceStringInFile(&quot;rackapp.tmp/config.ru&quot;, &quot;world 2&quot;, &quot;world 3&quot;);
+		session = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		session-&gt;sendHeaders(createRequestHeaders());
+		result = readAll(session-&gt;getStream());
+		ensure(&quot;App code has been reloaded (2)&quot;, result.find(&quot;hello &lt;b&gt;world 3&lt;/b&gt;&quot;) != string::npos);
+		session.reset();
+	}
+	
+	TEST_METHOD(19) {
+		// If tmp/restart.txt and tmp/always_restart.txt are present, 
+		// the application under app_root should still be restarted and
+		// both files must be kept.
+		pid_t old_pid, pid;
+		struct stat buf;
+		TempDirCopy c(&quot;stub/rack&quot;, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session1 = spawnRackApp(pool, &quot;rackapp.tmp&quot;);
+		Application::SessionPtr session2 = spawnRackApp(pool2, &quot;rackapp.tmp&quot;);
+		session1.reset();
+		session2.reset();
+		
+		touchFile(&quot;rackapp.tmp/tmp/restart.txt&quot;);
+		touchFile(&quot;rackapp.tmp/tmp/always_restart.txt&quot;);
+		
+		old_pid = spawnRackApp(pool, &quot;rackapp.tmp&quot;)-&gt;getPid();
+		ensure(&quot;always_restart.txt file has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
+		ensure(&quot;restart.txt file has not been deleted&quot;,
+			stat(&quot;rackapp.tmp/tmp/restart.txt&quot;, &amp;buf) == 0);
+		
+		pid = spawnRackApp(pool, &quot;rackapp.tmp&quot;)-&gt;getPid();
+		ensure(&quot;The app was restarted&quot;, pid != old_pid);
+	}
+	
+	TEST_METHOD(20) {
+		// It should look for restart.txt in the directory given by
+		// the restartDir option, if available.
+		struct stat buf;
+		char path[1024];
+		PoolOptions options(&quot;stub/rack&quot;);
+		options.appType = &quot;rack&quot;;
+		options.restartDir = string(getcwd(path, sizeof(path))) + &quot;/stub/rack&quot;;
+		
+		Application::SessionPtr session1 = pool-&gt;get(options);
+		Application::SessionPtr session2 = pool2-&gt;get(options);
+		session1.reset();
+		session2.reset();
+		
+		DeleteFileEventually f(&quot;stub/rack/restart.txt&quot;);
+		touchFile(&quot;stub/rack/restart.txt&quot;);
+		
+		pool-&gt;get(options);
+		
+		ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;Restart file still exists&quot;,
+			stat(&quot;stub/rack/restart.txt&quot;, &amp;buf) == 0);
+	}
+	
+	TEST_METHOD(21) {
+		// restartDir may also be a directory relative to the
+		// application root.
+		struct stat buf;
+		PoolOptions options(&quot;stub/rack&quot;);
+		options.appType = &quot;rack&quot;;
+		options.restartDir = &quot;public&quot;;
+		
+		Application::SessionPtr session1 = pool-&gt;get(options);
+		Application::SessionPtr session2 = pool2-&gt;get(options);
+		session1.reset();
+		session2.reset();
+		
+		DeleteFileEventually f(&quot;stub/rack/public/restart.txt&quot;);
+		touchFile(&quot;stub/rack/public/restart.txt&quot;);
+		
+		pool-&gt;get(options);
+		
+		ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
+		ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
+			pool-&gt;getCount(), 1u);
+		ensure(&quot;Restart file still exists&quot;,
+			stat(&quot;stub/rack/public/restart.txt&quot;, &amp;buf) == 0);
+	}
+	
+	TEST_METHOD(22) {
 		// The cleaner thread should clean idle applications without crashing.
 		pool-&gt;setMaxIdleTime(1);
 		spawnRackApp(pool, &quot;stub/rack&quot;);
@@ -345,7 +488,7 @@
 		ensure_equals(&quot;App should have been cleaned up&quot;, pool-&gt;getCount(), 0u);
 	}
 	
-	TEST_METHOD(17) {
+	TEST_METHOD(23) {
 		// MaxPerApp is respected.
 		pool-&gt;setMax(3);
 		pool-&gt;setMaxPerApp(1);
@@ -359,18 +502,20 @@
 		
 		// We connect to stub/wsgi. Assert that the pool spawns a new
 		// instance for this app.
+		TempDirCopy c(&quot;stub/wsgi&quot;, &quot;wsgiapp.tmp&quot;);
 		ApplicationPoolPtr pool3(newPoolConnection());
-		Application::SessionPtr session3 = spawnWsgiApp(pool3, &quot;stub/wsgi&quot;);
+		Application::SessionPtr session3 = spawnWsgiApp(pool3, &quot;wsgiapp.tmp&quot;);
 		ensure_equals(pool-&gt;getCount(), 2u);
 	}
 	
-	TEST_METHOD(18) {
+	TEST_METHOD(24) {
 		// Application instance is shutdown after 'maxRequests' requests.
-		PoolOptions options(&quot;stub/railsapp&quot;);
+		PoolOptions options(&quot;stub/rack&quot;);
 		int reader;
 		pid_t originalPid;
 		Application::SessionPtr session;
 		
+		options.appType = &quot;rack&quot;;
 		options.maxRequests = 4;
 		pool-&gt;setMax(1);
 		session = pool-&gt;get(options);
@@ -411,7 +556,7 @@
 		}
 	};
 	
-	TEST_METHOD(19) {
+	TEST_METHOD(25) {
 		// If global queueing mode is enabled, then get() waits until
 		// there's at least one idle backend process for this application
 		// domain.
@@ -439,171 +584,14 @@
 		thr.join();
 	}
 	
-	TEST_METHOD(20) {
-		// If tmp/always_restart.txt is present, then the application under app_root
-		// should be always restarted.
-		try {
-			struct stat buf;
-			Application::SessionPtr session1 = pool-&gt;get(&quot;stub/railsapp&quot;);
-			Application::SessionPtr session2 = pool2-&gt;get(&quot;stub/railsapp&quot;);
-			session1.reset();
-			session2.reset();
-		
-			system(&quot;touch stub/railsapp/tmp/always_restart.txt&quot;);
-			pool-&gt;get(&quot;stub/railsapp&quot;);
-		
-			ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
-			ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
-				pool-&gt;getCount(), 1u);
-			ensure(&quot;always_restart file has not been deleted&quot;,
-				stat(&quot;stub/railsapp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
-			unlink(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-		} catch (...) {
-			unlink(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-			throw;
-		}
-	}
-	
-	TEST_METHOD(21) {
-		// If tmp/always_restart.txt is present and is a directory, 
-		// then the application under app_root
-		// should be always restarted.
-		try {
-			struct stat buf;
-			Application::SessionPtr session1 = pool-&gt;get(&quot;stub/railsapp&quot;);
-			Application::SessionPtr session2 = pool2-&gt;get(&quot;stub/railsapp&quot;);
-			session1.reset();
-			session2.reset();
-		
-			system(&quot;mkdir stub/railsapp/tmp/always_restart.txt&quot;);
-			pool-&gt;get(&quot;stub/railsapp&quot;);
-		
-			ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
-			ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
-				pool-&gt;getCount(), 1u);
-			ensure(&quot;always_restart file has not been deleted&quot;,
-				stat(&quot;stub/railsapp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
-			system(&quot;rmdir stub/railsapp/tmp/always_restart.txt&quot;);
-		} catch (...) {
-			system(&quot;rmdir stub/railsapp/tmp/always_restart.txt&quot;);
-			throw;
-		}
-	}
-	
-	TEST_METHOD(22) {
-		// Test whether tmp/always_restart.txt really results in code reload.
-		DeleteFileEventually f1(&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		DeleteFileEventually f2(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-		
-		system(&quot;cp -f stub/railsapp/app/controllers/bar_controller_1.txt &quot;
-			&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		Application::SessionPtr session = pool-&gt;get(&quot;stub/railsapp&quot;);
-		session-&gt;sendHeaders(createRequestHeaders(&quot;/bar&quot;));
-		string result = readAll(session-&gt;getStream());
-		ensure(result.find(&quot;bar 1!&quot;) != string::npos);
-		session.reset();
-
-		system(&quot;cp -f stub/railsapp/app/controllers/bar_controller_2.txt &quot;
-			&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		system(&quot;touch stub/railsapp/tmp/always_restart.txt&quot;);
-		session = pool-&gt;get(&quot;stub/railsapp&quot;);
-		session-&gt;sendHeaders(createRequestHeaders(&quot;/bar&quot;));
-		result = readAll(session-&gt;getStream());
-		ensure(&quot;App code has been reloaded (1)&quot;, result.find(&quot;bar 2!&quot;) != string::npos);
-		session.reset();
-
-		system(&quot;cp -f stub/railsapp/app/controllers/bar_controller_1.txt &quot;
-			&quot;stub/railsapp/app/controllers/bar_controller.rb&quot;);
-		session = pool-&gt;get(&quot;stub/railsapp&quot;);
-		session-&gt;sendHeaders(createRequestHeaders(&quot;/bar&quot;));
-		result = readAll(session-&gt;getStream());
-		ensure(&quot;App code has been reloaded (2)&quot;, result.find(&quot;bar 1!&quot;) != string::npos);
-		session.reset();
-	}
-	
-	TEST_METHOD(23) {
-		// If tmp/restart.txt and tmp/always_restart.txt are present, 
-		// the application under app_root should still be restarted and
-		// both files must be kept
-		try {
-			pid_t old_pid, pid;
-			struct stat buf;
-			Application::SessionPtr session1 = pool-&gt;get(&quot;stub/railsapp&quot;);
-			Application::SessionPtr session2 = pool2-&gt;get(&quot;stub/railsapp&quot;);
-			session1.reset();
-			session2.reset();
-		
-			system(&quot;touch stub/railsapp/tmp/restart.txt&quot;);
-			system(&quot;touch stub/railsapp/tmp/always_restart.txt&quot;);
-		
-			old_pid = pool-&gt;get(&quot;stub/railsapp&quot;)-&gt;getPid();
-			ensure(&quot;always_restart file has not been deleted&quot;,
-				stat(&quot;stub/railsapp/tmp/always_restart.txt&quot;, &amp;buf) == 0);
-
-			ensure(&quot;Restart file has not been deleted&quot;,
-				stat(&quot;stub/railsapp/tmp/restart.txt&quot;, &amp;buf) == 0);
-		
-			pid = pool-&gt;get(&quot;stub/railsapp&quot;)-&gt;getPid();
-			ensure(&quot;The app was restarted&quot;, pid != old_pid);
-
-			unlink(&quot;stub/railsapp/tmp/restart.txt&quot;);
-			unlink(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-		} catch (...) {
-			unlink(&quot;stub/railsapp/tmp/restart.txt&quot;);
-			unlink(&quot;stub/railsapp/tmp/always_restart.txt&quot;);
-			throw;
-		}
-	}
-	
-	TEST_METHOD(24) {
-		// It should look for restart.txt in the directory given by
-		// the restartDir option, if available.
-		struct stat buf;
-		char path[1024];
-		PoolOptions options(&quot;stub/rack&quot;);
-		options.appType = &quot;rack&quot;;
-		options.restartDir = string(getcwd(path, sizeof(path))) + &quot;/stub/rack&quot;;
-		
-		Application::SessionPtr session1 = pool-&gt;get(options);
-		Application::SessionPtr session2 = pool2-&gt;get(options);
-		session1.reset();
-		session2.reset();
-		
-		DeleteFileEventually f(&quot;stub/rack/restart.txt&quot;);
-		system(&quot;touch stub/rack/restart.txt&quot;);
-		
-		pool-&gt;get(options);
-		
-		ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
-		ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
-			pool-&gt;getCount(), 1u);
-		ensure(&quot;Restart file still exists&quot;,
-			stat(&quot;stub/rack/restart.txt&quot;, &amp;buf) == 0);
-	}
-	
-	TEST_METHOD(25) {
-		// restartDir may also be a directory relative to the
-		// application root.
-		struct stat buf;
-		PoolOptions options(&quot;stub/rack&quot;);
-		options.appType = &quot;rack&quot;;
-		options.restartDir = &quot;public&quot;;
-		
-		Application::SessionPtr session1 = pool-&gt;get(options);
-		Application::SessionPtr session2 = pool2-&gt;get(options);
-		session1.reset();
-		session2.reset();
-		
-		DeleteFileEventually f(&quot;stub/rack/public/restart.txt&quot;);
-		system(&quot;touch stub/rack/public/restart.txt&quot;);
-		
-		pool-&gt;get(options);
-		
-		ensure_equals(&quot;No apps are active&quot;, pool-&gt;getActive(), 0u);
-		ensure_equals(&quot;Both apps are killed, and a new one was spawned&quot;,
-			pool-&gt;getCount(), 1u);
-		ensure(&quot;Restart file still exists&quot;,
-			stat(&quot;stub/rack/public/restart.txt&quot;, &amp;buf) == 0);
+	TEST_METHOD(26) {
+		// When a previous application domain spinned down, and we touched
+		// restart.txt and try to spin up a new process for this domain,
+		// then any ApplicationSpawner/FrameworkSpawner processes should be
+		// killed first.
+		
+		// TODO: to test this we first need to be able to move
+		// ApplicationPoolServer in-process so that we can use mock objects
 	}
 	
 	/*************************************/</diff>
      <filename>test/ApplicationPoolTest.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 #include &quot;tut.h&quot;
-#include &quot;CachedFileStat.h&quot;
+#include &quot;CachedFileStat.hpp&quot;
 #include &quot;SystemTime.h&quot;
 #include &lt;sys/types.h&gt;
 #include &lt;utime.h&gt;
@@ -9,21 +9,9 @@ using namespace Passenger;
 
 namespace tut {
 	struct CachedFileStatTest {
-		CachedFileStat *stat;
-		CachedMultiFileStat *mstat;
-		
-		CachedFileStatTest() {
-			stat = (CachedFileStat *) NULL;
-			mstat = (CachedMultiFileStat *) NULL;
-		}
+		struct stat buf;
 		
 		~CachedFileStatTest() {
-			if (stat != NULL) {
-				delete stat;
-			}
-			if (mstat != NULL) {
-				cached_multi_file_stat_free(mstat);
-			}
 			SystemTime::release();
 			unlink(&quot;test.txt&quot;);
 			unlink(&quot;test2.txt&quot;);
@@ -32,6 +20,8 @@ namespace tut {
 		}
 	};
 	
+	DEFINE_TEST_GROUP(CachedFileStatTest);
+	
 	static void touch(const char *filename, time_t timestamp = 0) {
 		FILE *f = fopen(filename, &quot;w&quot;);
 		fprintf(f, &quot;hi&quot;);
@@ -44,129 +34,106 @@ namespace tut {
 		}
 	}
 	
-	DEFINE_TEST_GROUP(CachedFileStatTest);
-	
-	/************ Tests for CachedFileStat ************/
+	/************ Tests involving a single file ************/
 	
 	TEST_METHOD(1) {
-		// cached_file_stat_new() does not stat the file immediately.
+		// Statting a new file works.
 		touch(&quot;test.txt&quot;);
-		stat = new CachedFileStat(&quot;test.txt&quot;);
-		ensure_equals((long) stat-&gt;info.st_size, (long) 0);
-		ensure_equals(stat-&gt;info.st_mtime, (time_t) 0);
+		CachedFileStat stat(1);
+		ensure_equals(stat.stat(&quot;test.txt&quot;, &amp;buf, 1), 0);
+		ensure_equals((long) buf.st_size, (long) 2);
 	}
 	
 	TEST_METHOD(2) {
-		// cached_file_stat_refresh() on a newly created
-		// CachedFileStat works.
-		touch(&quot;test.txt&quot;);
-		stat = new CachedFileStat(&quot;test.txt&quot;);
-		ensure_equals(stat-&gt;refresh(1), 0);
-		ensure_equals((long) stat-&gt;info.st_size, (long) 2);
-	}
-	
-	TEST_METHOD(3) {
-		// cached_file_stat_refresh() does not re-stat the file
-		// until the cache has expired.
+		// It does not re-stat an existing file until the cache has expired.
+		CachedFileStat stat(1);
+		
 		SystemTime::force(5);
-		stat = new CachedFileStat(&quot;test.txt&quot;);
 		touch(&quot;test.txt&quot;, 1);
-		ensure_equals(&quot;1st refresh succceeded&quot;,
-			stat-&gt;refresh(1),
+		ensure_equals(&quot;1st stat succceeded&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			0);
 		
 		touch(&quot;test.txt&quot;, 1000);
-		ensure_equals(&quot;2nd refresh succceeded&quot;,
-			stat-&gt;refresh(1),
+		ensure_equals(&quot;2nd stat succceeded&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(&quot;Cached value was used&quot;,
-			stat-&gt;info.st_mtime,
+			buf.st_mtime,
 			(time_t) 1);
 		
 		SystemTime::force(6);
-		ensure_equals(&quot;3rd refresh succceeded&quot;,
-			stat-&gt;refresh(1),
+		ensure_equals(&quot;3rd stat succceeded&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(&quot;Cache has been invalidated&quot;,
-			stat-&gt;info.st_mtime,
+			buf.st_mtime,
 			(time_t) 1000);
 	}
 	
-	TEST_METHOD(5) {
-		// cached_file_stat_refresh() on a nonexistant file returns
-		// an error.
-		stat = new CachedFileStat(&quot;test.txt&quot;);
-		ensure_equals(stat-&gt;refresh(1), -1);
+	TEST_METHOD(3) {
+		// Statting a nonexistant file returns an error.
+		CachedFileStat stat(1);
+		ensure_equals(stat.stat(&quot;test.txt&quot;, &amp;buf, 1), -1);
 		ensure_equals(&quot;It sets errno appropriately&quot;, errno, ENOENT);
 	}
 	
-	TEST_METHOD(6) {
-		// cached_file_stat_refresh() on a nonexistant file does not
-		// re-stat the file until the cache has expired.
+	TEST_METHOD(4) {
+		// It does not re-stat a previously nonexistant file until
+		// the cache has expired.
 		SystemTime::force(5);
-		stat = new CachedFileStat(&quot;test.txt&quot;);
-		ensure_equals(&quot;1st refresh failed&quot;,
-			stat-&gt;refresh(1),
+		CachedFileStat stat(1);
+		ensure_equals(&quot;1st stat failed&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			-1);
 		ensure_equals(&quot;It sets errno appropriately&quot;, errno, ENOENT);
 		
 		errno = EEXIST;
-		ensure_equals(&quot;2nd refresh failed&quot;,
-			stat-&gt;refresh(1),
+		touch(&quot;test.txt&quot;, 1000);
+		ensure_equals(&quot;2nd stat failed&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			-1);
 		ensure_equals(&quot;It sets errno appropriately&quot;, errno, ENOENT);
 		ensure_equals(&quot;Cached value was used&quot;,
-			stat-&gt;info.st_mtime,
+			buf.st_mtime,
 			(time_t) 0);
 		
-		touch(&quot;test.txt&quot;, 1000);
 		SystemTime::force(6);
-		ensure_equals(&quot;3rd refresh succeeded&quot;,
-			stat-&gt;refresh(1),
+		ensure_equals(&quot;3rd stat succeeded&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(&quot;Cache has been invalidated&quot;,
-			stat-&gt;info.st_mtime,
+			buf.st_mtime,
 			(time_t) 1000);
 		
 		unlink(&quot;test.txt&quot;);
-		ensure_equals(&quot;4th refresh succeeded even though file was unlinked&quot;,
-			stat-&gt;refresh(1),
+		ensure_equals(&quot;4th stat succeeded even though file was unlinked&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(&quot;Cached value was used&quot;,
-			stat-&gt;info.st_mtime,
+			buf.st_mtime,
 			(time_t) 1000);
 	}
 	
-	
-	/************ Tests for CachedMultiFileStat ************/
-	
-	TEST_METHOD(10) {
-		// Statting an existing file works.
-		struct stat buf;
-		
+	TEST_METHOD(5) {
+		// If the throttling rate is 0 then the cache will be bypassed.
+		SystemTime::force(5);
+		CachedFileStat stat(2);
+		ensure_equals(&quot;1st stat returns -1&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 0),
+			-1);
 		touch(&quot;test.txt&quot;);
-		mstat = cached_multi_file_stat_new(1);
-		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test.txt&quot;, &amp;buf, 0),
+		ensure_equals(&quot;2nd stat did not go through the cache&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 0),
 			0);
-		ensure_equals((long) buf.st_size, (long) 2);
 	}
 	
-	TEST_METHOD(11) {
-		// Statting a nonexistant file works.
-		struct stat buf;
-		
-		mstat = cached_multi_file_stat_new(1);
-		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test.txt&quot;, &amp;buf, 0),
-			-1);
-	}
 	
-	TEST_METHOD(12) {
-		// Throttling works.
-		struct stat buf;
-		
-		mstat = cached_multi_file_stat_new(2);
+	/************ Tests involving multiple files ************/
+	
+	TEST_METHOD(10) {
+		// Throttling in combination with multiple files works.
+		CachedFileStat stat(2);
 		SystemTime::force(5);
 		
 		// Touch and stat test.txt. The next stat should return
@@ -174,13 +141,13 @@ namespace tut {
 		
 		touch(&quot;test.txt&quot;, 10);
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(buf.st_mtime, (time_t) 10);
 		
 		touch(&quot;test.txt&quot;, 20);
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(buf.st_mtime, (time_t) 10);
 		
@@ -189,13 +156,13 @@ namespace tut {
 		
 		touch(&quot;test2.txt&quot;, 30);
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test2.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test2.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(buf.st_mtime, (time_t) 30);
 		
 		touch(&quot;test2.txt&quot;, 40);
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test2.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test2.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(buf.st_mtime, (time_t) 30);
 		
@@ -204,39 +171,37 @@ namespace tut {
 		
 		SystemTime::force(6);
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(buf.st_mtime, (time_t) 20);
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test2.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test2.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(buf.st_mtime, (time_t) 40);
 	}
 	
-	TEST_METHOD(13) {
+	TEST_METHOD(11) {
 		// Cache limiting works.
-		struct stat buf;
-		
-		mstat = cached_multi_file_stat_new(3);
+		CachedFileStat stat(3);
 		SystemTime::force(5);
 		
 		// Create and stat test.txt, test2.txt and test3.txt.
 		
 		touch(&quot;test.txt&quot;, 1000);
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(buf.st_mtime, (time_t) 1000);
 		
 		touch(&quot;test2.txt&quot;, 1001);
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test2.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test2.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(buf.st_mtime, (time_t) 1001);
 		
 		touch(&quot;test3.txt&quot;, 1003);
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test3.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test3.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(buf.st_mtime, (time_t) 1003);
 		
@@ -245,18 +210,193 @@ namespace tut {
 		// upon statting it again its new timestamp should be returned.
 		
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test2.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test2.txt&quot;, &amp;buf, 1),
 			0);
 		
 		touch(&quot;test4.txt&quot;, 1004);
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test4.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test4.txt&quot;, &amp;buf, 1),
 			0);
 		
 		touch(&quot;test.txt&quot;, 3000);
 		ensure_equals(
-			cached_multi_file_stat_perform(mstat, &quot;test.txt&quot;, &amp;buf, 1),
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
 			0);
 		ensure_equals(buf.st_mtime, (time_t) 3000);
 	}
+	
+	TEST_METHOD(12) {
+		// Increasing the cache size dynamically works.
+		SystemTime::force(5);
+		CachedFileStat stat(2);
+		touch(&quot;test.txt&quot;, 1);
+		touch(&quot;test2.txt&quot;, 2);
+		touch(&quot;test3.txt&quot;, 3);
+		
+		ensure_equals(&quot;1st stat succeeded&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
+			0);
+		ensure_equals(&quot;2nd stat succeeded&quot;,
+			stat.stat(&quot;test2.txt&quot;, &amp;buf, 1),
+			0);
+		ensure_equals(&quot;3rd stat succeeded&quot;,
+			stat.stat(&quot;test3.txt&quot;, &amp;buf, 1),
+			0);
+		
+		// test.txt should now be removed from the cache.
+		
+		touch(&quot;test.txt&quot;, 10);
+		ensure_equals(&quot;4th stat succeeded&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
+			0);
+		ensure_equals(buf.st_mtime, (time_t) 10);
+		
+		// test2.txt should now be removed from the cache.
+		// If we stat test2.txt now, test3.txt would normally
+		// be removed from the cache. But if we increase the
+		// cache size here then that won't happen:
+		stat.setMaxSize(3);
+		touch(&quot;test2.txt&quot;, 11);
+		touch(&quot;test3.txt&quot;, 12);
+		
+		ensure_equals(&quot;5th stat succeeded&quot;,
+			stat.stat(&quot;test2.txt&quot;, &amp;buf, 1),
+			0);
+		ensure_equals(buf.st_mtime, (time_t) 11);
+		
+		ensure_equals(&quot;6th stat succeeded&quot;,
+			stat.stat(&quot;test3.txt&quot;, &amp;buf, 1),
+			0);
+		ensure_equals(&quot;test3.txt is still cached&quot;,
+			buf.st_mtime,
+			(time_t) 3);
+		
+		ensure_equals(&quot;7th stat succeeded&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
+			0);
+		ensure_equals(&quot;test.txt is still cached&quot;,
+			buf.st_mtime,
+			(time_t) 10);
+	}
+	
+	TEST_METHOD(13) {
+		// If we decrease the cache size dynamically, then
+		// the oldest entries will be removed.
+		SystemTime::force(5);
+		CachedFileStat stat(3);
+		touch(&quot;test.txt&quot;, 1);
+		touch(&quot;test2.txt&quot;, 2);
+		touch(&quot;test3.txt&quot;, 3);
+		
+		ensure_equals(&quot;1st stat succeeded&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
+			0);
+		ensure_equals(&quot;2nd stat succeeded&quot;,
+			stat.stat(&quot;test2.txt&quot;, &amp;buf, 1),
+			0);
+		ensure_equals(&quot;3rd stat succeeded&quot;,
+			stat.stat(&quot;test3.txt&quot;, &amp;buf, 1),
+			0);
+		
+		// The following should remove test.txt and test2.txt from the cache.
+		stat.setMaxSize(1);
+		
+		touch(&quot;test.txt&quot;, 10);
+		touch(&quot;test2.txt&quot;, 11);
+		touch(&quot;test3.txt&quot;, 12);
+		
+		ensure_equals(&quot;6th stat succeeded&quot;,
+			stat.stat(&quot;test3.txt&quot;, &amp;buf, 1),
+			0);
+		ensure_equals(&quot;test3.txt is still in the cache&quot;,
+			buf.st_mtime,
+			(time_t) 3);
+		
+		ensure_equals(&quot;4th stat succeeded&quot;,
+			stat.stat(&quot;test.txt&quot;, &amp;buf, 1),
+			0);
+		ensure_equals(&quot;test.txt is removed from the cache&quot;,
+			buf.st_mtime,
+			(time_t) 10);
+		
+		ensure_equals(&quot;5th stat succeeded&quot;,
+			stat.stat(&quot;test2.txt&quot;, &amp;buf, 1),
+			0);
+		ensure_equals(&quot;test2.txt is removed from the cache&quot;,
+			buf.st_mtime,
+			(time_t) 11);
+	}
+	
+	TEST_METHOD(14) {
+		// An initial cache size of 0 means that the cache size is unlimited.
+		SystemTime::force(1);
+		CachedFileStat stat(0);
+		
+		touch(&quot;test.txt&quot;, 1);
+		touch(&quot;test2.txt&quot;, 2);
+		touch(&quot;test3.txt&quot;, 3);
+		stat.stat(&quot;test.txt&quot;, &amp;buf, 1);
+		stat.stat(&quot;test2.txt&quot;, &amp;buf, 1);
+		stat.stat(&quot;test3.txt&quot;, &amp;buf, 1);
+		
+		touch(&quot;test.txt&quot;, 11);
+		touch(&quot;test2.txt&quot;, 12);
+		touch(&quot;test3.txt&quot;, 13);
+		stat.stat(&quot;test.txt&quot;, &amp;buf, 1);
+		ensure_equals(buf.st_mtime, (time_t) 1);
+		stat.stat(&quot;test2.txt&quot;, &amp;buf, 1);
+		ensure_equals(buf.st_mtime, (time_t) 2);
+		stat.stat(&quot;test3.txt&quot;, &amp;buf, 1);
+		ensure_equals(buf.st_mtime, (time_t) 3);
+	}
+	
+	TEST_METHOD(15) {
+		// Setting the cache size dynamically to 0 makes the cache size unlimited.
+		SystemTime::force(1);
+		CachedFileStat stat(2);
+		
+		touch(&quot;test.txt&quot;, 1);
+		touch(&quot;test2.txt&quot;, 2);
+		touch(&quot;test3.txt&quot;, 3);
+		stat.stat(&quot;test.txt&quot;, &amp;buf, 1);
+		stat.stat(&quot;test2.txt&quot;, &amp;buf, 1);
+		stat.stat(&quot;test3.txt&quot;, &amp;buf, 1);
+		
+		// test.txt is now no longer in the cache.
+		
+		stat.setMaxSize(0);
+		touch(&quot;test.txt&quot;, 11);
+		touch(&quot;test2.txt&quot;, 12);
+		touch(&quot;test3.txt&quot;, 13);
+		stat.stat(&quot;test.txt&quot;, &amp;buf, 1);
+		stat.stat(&quot;test2.txt&quot;, &amp;buf, 1);
+		stat.stat(&quot;test3.txt&quot;, &amp;buf, 1);
+		
+		// test.txt should now have been re-statted while test2.txt
+		// and test3.txt are still cached.
+		
+		stat.stat(&quot;test.txt&quot;, &amp;buf, 1);
+		ensure_equals(&quot;test.txt is re-statted&quot;, buf.st_mtime, (time_t) 11);
+		stat.stat(&quot;test2.txt&quot;, &amp;buf, 1);
+		ensure_equals(&quot;test2.txt is still cached&quot;, buf.st_mtime, (time_t) 2);
+		stat.stat(&quot;test3.txt&quot;, &amp;buf, 1);
+		ensure_equals(&quot;test3.txt is still cached&quot;, buf.st_mtime, (time_t) 3);
+	}
+	
+	TEST_METHOD(16) {
+		// Changing the cache size dynamically from 0 to non-0 works;
+		// it removes the oldest entries, if necessary.
+		CachedFileStat stat(0);
+		stat.stat(&quot;test.txt&quot;, &amp;buf, 1);
+		stat.stat(&quot;test2.txt&quot;, &amp;buf, 1);
+		stat.stat(&quot;test3.txt&quot;, &amp;buf, 1);
+		stat.stat(&quot;test4.txt&quot;, &amp;buf, 1);
+		stat.stat(&quot;test5.txt&quot;, &amp;buf, 1);
+		stat.setMaxSize(2);
+		ensure(!stat.knows(&quot;test.txt&quot;));
+		ensure(!stat.knows(&quot;test2.txt&quot;));
+		ensure(!stat.knows(&quot;test3.txt&quot;));
+		ensure(stat.knows(&quot;test4.txt&quot;));
+		ensure(stat.knows(&quot;test5.txt&quot;));
+	}
 }</diff>
      <filename>test/CachedFileStatTest.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -34,4 +34,84 @@ namespace tut {
 		ensure_equals(options.appSpawnerTimeout, copy.appSpawnerTimeout);
 		ensure_equals(options.maxRequests, copy.maxRequests);
 	}
+	
+	// Test empty environmentVariables serialization and deserialization.
+	TEST_METHOD(2) {
+		PoolOptions options;
+		vector&lt;string&gt; args;
+		options.toVector(args);
+		
+		PoolOptions options2(args);
+		ensure_equals(options2.environmentVariables-&gt;getItems()-&gt;size(), 0u);
+	}
+	
+	// Test single item environmentVariables serialization and deserialization.
+	TEST_METHOD(3) {
+		PoolOptions options;
+		SimpleStringListCreatorPtr list = ptr(new SimpleStringListCreator());
+		vector&lt;string&gt; args;
+		list-&gt;items-&gt;push_back(&quot;hello&quot;);
+		list-&gt;items-&gt;push_back(&quot;world !!&quot;);
+		options.environmentVariables = list;
+		options.toVector(args);
+		
+		PoolOptions options2(args);
+		const StringListPtr list2 = options2.environmentVariables-&gt;getItems();
+		ensure_equals(list2-&gt;size(), 2u);
+		ensure_equals(list2-&gt;at(0), &quot;hello&quot;);
+		ensure_equals(list2-&gt;at(1), &quot;world !!&quot;);
+	}
+	
+	// Test multiple items environmentVariables serialization and deserialization.
+	TEST_METHOD(4) {
+		PoolOptions options;
+		SimpleStringListCreatorPtr list = ptr(new SimpleStringListCreator());
+		vector&lt;string&gt; args;
+		list-&gt;items-&gt;push_back(&quot;hello&quot;);
+		list-&gt;items-&gt;push_back(&quot;world !!&quot;);
+		list-&gt;items-&gt;push_back(&quot;PATH&quot;);
+		list-&gt;items-&gt;push_back(&quot;/usr/local/bin&quot;);
+		options.environmentVariables = list;
+		options.toVector(args);
+		
+		PoolOptions options2(args);
+		const StringListPtr list2 = options2.environmentVariables-&gt;getItems();
+		ensure_equals(list2-&gt;size(), 4u);
+		ensure_equals(list2-&gt;at(0), &quot;hello&quot;);
+		ensure_equals(list2-&gt;at(1), &quot;world !!&quot;);
+		ensure_equals(list2-&gt;at(2), &quot;PATH&quot;);
+		ensure_equals(list2-&gt;at(3), &quot;/usr/local/bin&quot;);
+	}
+	
+	// Calling toVector() with storeEnvVars = false on a PoolOption object that
+	// has no environment variables works, and the resulting data can be unserialized.
+	TEST_METHOD(5) {
+		PoolOptions options;
+		vector&lt;string&gt; args;
+		options.appRoot = &quot;hello&quot;;
+		options.toVector(args, false);
+		
+		PoolOptions options2(args);
+		ensure_equals(options2.appRoot, &quot;hello&quot;);
+		ensure_equals(options2.environmentVariables, StringListCreatorPtr());
+	}
+	
+	// Calling toVector() with storeEnvVars = false on a PoolOption object that
+	// has no environment variables works, and the resulting data can be unserialized.
+	TEST_METHOD(6) {
+		PoolOptions options;
+		SimpleStringListCreatorPtr list = ptr(new SimpleStringListCreator());
+		vector&lt;string&gt; args;
+		list-&gt;items-&gt;push_back(&quot;hello&quot;);
+		list-&gt;items-&gt;push_back(&quot;world&quot;);
+		list-&gt;items-&gt;push_back(&quot;foo&quot;);
+		list-&gt;items-&gt;push_back(&quot;bar&quot;);
+		options.appRoot = &quot;hello&quot;;
+		options.environmentVariables = list;
+		options.toVector(args, false);
+		
+		PoolOptions options2(args);
+		ensure_equals(options2.appRoot, &quot;hello&quot;);
+		ensure_equals(options2.environmentVariables, StringListCreatorPtr());
+	}
 }</diff>
      <filename>test/PoolOptionsTest.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -192,22 +192,10 @@ namespace tut {
 	
 	/***** Test BufferedUpload *****/
 	
-	struct TemporarilySetInstanceTempDir {
-		TemporarilySetInstanceTempDir() {
-			setPassengerTempDir(&quot;utils_test.tmp&quot;);
-			mkdir(&quot;utils_test.tmp&quot;, S_IRWXU);
-			mkdir(BufferedUpload::getDir().c_str(), S_IRWXU);
-		}
-		
-		~TemporarilySetInstanceTempDir() {
-			removeDirTree(&quot;utils_test.tmp&quot;);
-		}
-	};
-	
 	TEST_METHOD(20) {
 		// The resulting file handle is readable and writable.
-		TemporarilySetInstanceTempDir d;
-		BufferedUpload t;
+		TempDir td(&quot;utils_test.tmp&quot;);
+		BufferedUpload t(&quot;utils_test.tmp&quot;);
 		char line[30];
 		
 		fprintf(t.handle, &quot;hello world!&quot;);
@@ -220,9 +208,9 @@ namespace tut {
 	
 	TEST_METHOD(21) {
 		// It immediately unlinks the temp file.
-		TemporarilySetInstanceTempDir d;
-		BufferedUpload t;
-		ensure_equals(listDir(BufferedUpload::getDir().c_str()).size(), 0u);
+		TempDir td(&quot;utils_test.tmp&quot;);
+		BufferedUpload t(&quot;utils_test.tmp&quot;);
+		ensure_equals(listDir(&quot;utils_test.tmp&quot;).size(), 0u);
 	}
 	
 	/***** Test escapeForXml() *****/</diff>
      <filename>test/UtilsTest.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -93,6 +93,18 @@ describe &quot;mod_passenger running in Apache 2&quot; do
 				end
 			end
 		end
+		
+		it &quot;supports environment variable passing through mod_env&quot; do
+			begin
+				File.open(&quot;#{@stub.app_root}/public/.htaccess&quot;, 'w') do |f|
+					f.puts 'SetEnv FOO &quot;Foo Bar!&quot;'
+				end
+				File.touch(&quot;#{@stub.app_root}/tmp/restart.txt&quot;)
+				get('/welcome/environment').should =~ /FOO = Foo Bar\!/
+			ensure
+				File.unlink(&quot;#{@stub.app_root}/public/.htaccess&quot;) rescue nil
+			end
+		end
 	end
 	
 	describe &quot;: MyCook(tm) beta running in a sub-URI&quot; do
@@ -199,7 +211,8 @@ describe &quot;mod_passenger running in Apache 2&quot; do
 					})
 				end
 				
-				File.open(restart_file, 'w').close
+				now = Time.now
+				File.touch(restart_file, now - 5)
 				get('/bar').should == &quot;hello world&quot;
 				
 				File.open(controller, 'w') do |f|
@@ -212,9 +225,7 @@ describe &quot;mod_passenger running in Apache 2&quot; do
 					})
 				end
 				
-				now = Time.now
-				File.open(restart_file, 'w').close
-				File.utime(now - 10, now - 10, restart_file)
+				File.touch(restart_file, now - 10)
 				get('/bar').should == &quot;oh hai&quot;
 			ensure
 				File.unlink(controller) rescue nil</diff>
      <filename>test/integration_tests/apache2_tests.rb</filename>
    </modified>
    <modified>
      <diff>@@ -124,8 +124,7 @@ shared_examples_for &quot;MyCook(tm) beta&quot; do
 					end
 				}
 			end
-			File.open(restart_file, 'w').close
-			File.utime(now - 10, now - 10, restart_file)
+			File.touch(restart_file, now - 10)
 			get('/test').should == &quot;foo&quot;
 			
 			File.open(controller, 'w') do |f|
@@ -139,7 +138,7 @@ shared_examples_for &quot;MyCook(tm) beta&quot; do
 				}
 			end
 
-			File.utime(now - 5, now - 5, restart_file)
+			File.touch(restart_file, now - 5)
 			get('/test').should == 'bar'
 		ensure
 			File.unlink(controller) rescue nil
@@ -165,7 +164,7 @@ shared_examples_for &quot;MyCook(tm) beta&quot; do
 			})
 		end
 		begin
-			system &quot;touch '#{@stub.app_root}/tmp/restart.txt'&quot;
+			File.touch(&quot;#{@stub.app_root}/tmp/restart.txt&quot;)
 			get('/welcome/passenger_name').should == 'Gourry Gabriev'
 		ensure
 			File.unlink(&quot;#{@stub.app_root}/app/models/passenger.rb&quot;) rescue nil</diff>
      <filename>test/integration_tests/mycook_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -9,22 +9,10 @@ using namespace oxt;
 using namespace std;
 
 namespace tut {
-	struct SyscallInterruptionTest {
-		SyscallInterruptionTest() {
-			setup_syscall_interruption_support();
-		}
-		
-		~SyscallInterruptionTest() {
-			struct sigaction action;
-			
-			action.sa_handler = SIG_DFL;
-			action.sa_flags   = 0;
-			sigemptyset(&amp;action.sa_mask);
-			sigaction(INTERRUPTION_SIGNAL, &amp;action, NULL);
-		}
+	struct syscall_interruption_test {
 	};
 	
-	DEFINE_TEST_GROUP(SyscallInterruptionTest);
+	DEFINE_TEST_GROUP(syscall_interruption_test);
 	
 	struct SleepFunction {
 		void operator()() {</diff>
      <filename>test/oxt/syscall_interruption_test.cpp</filename>
    </modified>
    <modified>
      <diff>@@ -63,7 +63,7 @@ describe AbstractServerCollection do
 		end
 	end
 	
-	specify &quot;#delete stop the server if it's started&quot; do
+	specify &quot;#delete stops the server if it's started&quot; do
 		@collection.synchronize do
 			server = AbstractServer.new
 			@collection.lookup_or_add('foo') do</diff>
      <filename>test/ruby/abstract_server_collection_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,10 @@
-shared_examples_for &quot;AbstractServer&quot; do
+require 'support/config'
+
+require 'phusion_passenger/abstract_server'
+
+include PhusionPassenger
+
+shared_examples_for &quot;an AbstractServer&quot; do
 	it &quot;doesn't crash if it's started and stopped multiple times&quot; do
 		3.times do
 			# Give the server some time to install the
@@ -15,3 +21,31 @@ shared_examples_for &quot;AbstractServer&quot; do
 		lambda { @server.start }.should raise_error(AbstractServer::ServerAlreadyStarted)
 	end
 end
+
+describe AbstractServer do
+	before :each do
+		@server = AbstractServer.new
+	end
+	
+	after :each do
+		@server.stop if @server.started?
+	end
+	
+	it &quot;reseeds the pseudo-random number generator after forking off a process&quot; do
+		@server.send(:define_message_handler, :random_number, :handle_random_number)
+		@server.stub!(:handle_random_number).and_return do
+			@server.send(:client).write(rand.to_s)
+		end
+		
+		@server.start
+		@server.send(:server).write(&quot;random_number&quot;)
+		first_num = @server.send(:server).read
+		
+		@server.stop
+		@server.start
+		@server.send(:server).write(&quot;random_number&quot;)
+		second_num = @server.send(:server).read
+		
+		first_num.should_not == second_num
+	end
+end</diff>
      <filename>test/ruby/abstract_server_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -19,7 +19,7 @@ describe PhusionPassenger::Rack::ApplicationSpawner do
 	
 	it &quot;propagates exceptions in application startup&quot; do
 		File.prepend(&quot;#{@stub.app_root}/config.ru&quot;, &quot;raise StandardError, 'foo'\n&quot;)
-		spawn = lambda { spawn(@stub.app_root) }
+		spawn = lambda { spawn(@stub.app_root, &quot;print_exceptions&quot; =&gt; false) }
 		spawn.should raise_error(StandardError)
 	end
 	
@@ -34,8 +34,25 @@ describe PhusionPassenger::Rack::ApplicationSpawner do
 		config_ru_owner.should == touch_txt_owner
 	end if Process.euid == 0
 	
+	it &quot;sets the environment variables passed in the environment_variables option&quot; do
+		File.append(&quot;#{@stub.app_root}/config.ru&quot;, %q{
+			File.open(&quot;env.txt&quot;, &quot;w&quot;) do |f|
+				ENV.each_pair do |key, value|
+					f.puts(&quot;#{key} = #{value}&quot;)
+				end
+			end
+		})
+		env_vars_string = &quot;PATH\0/usr/bin:/opt/sw/bin\0FOO\0foo bar!\0&quot;
+		options = { &quot;environment_variables&quot; =&gt; [env_vars_string].pack(&quot;m&quot;) }
+		spawn(@stub.app_root, options).close
+		
+		contents = File.read(&quot;#{@stub.app_root}/env.txt&quot;)
+		contents.should =~ %r(PATH = /usr/bin:/opt/sw/bin\n)
+		contents.should =~ %r(FOO = foo bar\!\n)
+	end
+	
 	it &quot;calls the starting_worker_process event after config.ru has been loaded&quot; do
-	  File.append(&quot;#{@stub.app_root}/config.ru&quot;, %q{
+		File.append(&quot;#{@stub.app_root}/config.ru&quot;, %q{
 			PhusionPassenger.on_event(:starting_worker_process) do
 				File.append(&quot;rackresult.txt&quot;, &quot;worker_process_started\n&quot;)
 			end
@@ -55,7 +72,7 @@ describe PhusionPassenger::Rack::ApplicationSpawner do
 	end
 	
 	it &quot;calls the stopping_worker_process event&quot; do
-	  File.append(&quot;#{@stub.app_root}/config.ru&quot;, %q{
+		File.append(&quot;#{@stub.app_root}/config.ru&quot;, %q{
 			PhusionPassenger.on_event(:stopping_worker_process) do
 				File.append(&quot;rackresult.txt&quot;, &quot;worker_process_stopped\n&quot;)
 			end
@@ -74,9 +91,9 @@ describe PhusionPassenger::Rack::ApplicationSpawner do
 			&quot;worker_process_stopped\n&quot;
 	end	
 	
-	def spawn(app_root)
-		PhusionPassenger::Rack::ApplicationSpawner.spawn_application(app_root,
-			&quot;lowest_user&quot; =&gt; CONFIG['lowest_user'])
+	def spawn(app_root, extra_options = {})
+		options = { &quot;lowest_user&quot; =&gt; CONFIG['lowest_user'] }.merge(extra_options)
+		PhusionPassenger::Rack::ApplicationSpawner.spawn_application(app_root, options)
 	end
 end
 </diff>
      <filename>test/ruby/rack/application_spawner_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -69,9 +69,9 @@ describe ApplicationSpawner do
 			end
 		end
 		
-		def spawn_stub_application(stub)
-			@spawner = ApplicationSpawner.new(stub.app_root,
-				&quot;lowest_user&quot; =&gt; CONFIG['lowest_user'])
+		def spawn_stub_application(stub, extra_options = {})
+			options = { &quot;lowest_user&quot; =&gt; CONFIG['lowest_user'] }.merge(extra_options)
+			@spawner = ApplicationSpawner.new(stub.app_root, options)
 			begin
 				@spawner.start
 				return @spawner.spawn_application
@@ -107,9 +107,9 @@ describe ApplicationSpawner do
 			end
 		end
 		
-		def spawn_stub_application(stub)
-			@spawner = ApplicationSpawner.new(stub.app_root,
-				&quot;lowest_user&quot; =&gt; CONFIG['lowest_user'])
+		def spawn_stub_application(stub, extra_options = {})
+			options = { &quot;lowest_user&quot; =&gt; CONFIG['lowest_user'] }.merge(extra_options)
+			@spawner = ApplicationSpawner.new(stub.app_root, options)
 			return @spawner.spawn_application!
 		end
 	end</diff>
      <filename>test/ruby/rails/application_spawner_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -76,7 +76,7 @@ describe FrameworkSpawner do
 		it_should_behave_like &quot;handling errors in framework initialization&quot;
 	end
 	
-	def spawn_stub_application(stub)
+	def spawn_stub_application(stub, extra_options = {})
 		if use_vendor_rails?
 			stub.use_vendor_rails('minimal')
 		end
@@ -86,18 +86,19 @@ describe FrameworkSpawner do
 		else
 			options = { :version =&gt; version }
 		end
+		options[&quot;lowest_user&quot;] = CONFIG['lowest_user']
+		options = options.merge(extra_options)
 		spawner = FrameworkSpawner.new(options)
 		spawner.start
 		begin
-			return spawner.spawn_application(stub.app_root,
-				&quot;lowest_user&quot; =&gt; CONFIG['lowest_user'])
+			return spawner.spawn_application(stub.app_root, options)
 		ensure
 			spawner.stop
 		end
 	end
 	
-	def load_nonexistant_framework
-		spawner = FrameworkSpawner.new(:version =&gt; &quot;1.9.827&quot;)
+	def load_nonexistant_framework(options = {})
+		spawner = FrameworkSpawner.new(options.merge(:version =&gt; &quot;1.9.827&quot;))
 		begin
 			spawner.start
 		ensure</diff>
      <filename>test/ruby/rails/framework_spawner_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -71,4 +71,23 @@ shared_examples_for &quot;a minimal spawner&quot; do
 			lambda { spawn_stub_application(stub).close }.should_not raise_error
 		end
 	end
+	
+	it &quot;sets the environment variables passed in the environment_variables option&quot; do
+		use_rails_stub('foobar') do |stub|
+			File.append(stub.environment_rb, %q{
+				File.open(&quot;env.txt&quot;, &quot;w&quot;) do |f|
+					ENV.each_pair do |key, value|
+						f.puts(&quot;#{key} = #{value}&quot;)
+					end
+				end
+			})
+			env_vars_string = &quot;PATH\0/usr/bin:/opt/sw/bin\0FOO\0foo bar!\0&quot;
+			options = { &quot;environment_variables&quot; =&gt; [env_vars_string].pack(&quot;m&quot;) }
+			spawn_stub_application(stub, options).close
+			
+			contents = File.read(&quot;#{stub.app_root}/env.txt&quot;)
+			contents.should =~ %r(PATH = /usr/bin:/opt/sw/bin\n)
+			contents.should =~ %r(FOO = foo bar\!\n)
+		end
+	end
 end</diff>
      <filename>test/ruby/rails/minimal_spawner_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,5 @@
+require 'stringio'
+
 shared_examples_for &quot;handling errors in application initialization&quot; do
 	before :each do
 		@stub = setup_rails_stub('foobar')
@@ -11,8 +13,8 @@ shared_examples_for &quot;handling errors in application initialization&quot; do
 	it &quot;raises an AppInitError if the spawned app raises a standard exception during startup&quot; do
 		File.prepend(@stub.environment_rb, &quot;raise 'This is a dummy exception.'\n&quot;)
 		begin
-			spawn_stub_application(@stub).close
-			violated &quot;Spawning the application should have raised an InitializationError.&quot;
+			spawn_stub_application(@stub, &quot;print_exceptions&quot; =&gt; false).close
+			violated &quot;Spawning the application should have raised an AppInitError.&quot;
 		rescue AppInitError =&gt; e
 			e.child_exception.message.should == &quot;This is a dummy exception.&quot;
 		end
@@ -26,8 +28,8 @@ shared_examples_for &quot;handling errors in application initialization&quot; do
 			raise MyError, &quot;This is a custom exception.&quot;
 		})
 		begin
-			spawn_stub_application(@stub).close
-			violated &quot;Spawning the application should have raised an InitializationError.&quot;
+			spawn_stub_application(@stub, &quot;print_exceptions&quot; =&gt; false).close
+			violated &quot;Spawning the application should have raised an AppInitError.&quot;
 		rescue AppInitError =&gt; e
 			e.child_exception.message.should == &quot;This is a custom exception. (MyError)&quot;
 		end
@@ -36,17 +38,70 @@ shared_examples_for &quot;handling errors in application initialization&quot; do
 	it &quot;raises an AppInitError if the spawned app calls exit() during startup&quot; do
 		File.prepend(@stub.environment_rb, &quot;exit\n&quot;)
 		begin
-			spawn_stub_application(@stub).close
-			violated &quot;Spawning the application should have raised an InitializationError.&quot;
+			spawn_stub_application(@stub, &quot;print_exceptions&quot; =&gt; false).close
+			violated &quot;Spawning the application should have raised an AppInitError.&quot;
 		rescue AppInitError =&gt; e
 			e.child_exception.class.should == SystemExit
 		end
 	end
+	
+	it &quot;prints the exception to STDERR if the spawned app raised an error&quot; do
+		old_stderr = STDERR
+		file = File.new('output.tmp', 'w+')
+		begin
+			Object.send(:remove_const, &quot;STDERR&quot;) rescue nil
+			Object.const_set(&quot;STDERR&quot;, file)
+			
+			File.prepend(@stub.environment_rb, &quot;raise 'This is a dummy exception.'\n&quot;)
+			block = lambda do
+				spawn_stub_application(@stub).close
+			end
+			block.should raise_error(AppInitError)
+			
+			file.rewind
+			data = file.read
+			data.should =~ /spawn_stub_application/
+			data.should =~ /spawner_error_handling_spec\.rb/
+		ensure
+			Object.send(:remove_const, &quot;STDERR&quot;) rescue nil
+			Object.const_set(&quot;STDERR&quot;, old_stderr)
+			file.close rescue nil
+			File.unlink('output.tmp') rescue nil
+		end
+	end
 end
 
 shared_examples_for &quot;handling errors in framework initialization&quot; do
 	include Utils
+	
 	it &quot;raises FrameworkInitError if the framework could not be loaded&quot; do
-		lambda { load_nonexistant_framework.close }.should raise_error(FrameworkInitError)
+		block = lambda do
+			load_nonexistant_framework(:print_framework_loading_exceptions =&gt; false).close
+		end
+		block.should raise_error(FrameworkInitError)
+	end
+	
+	it &quot;prints the exception to STDERR if the framework could not be loaded&quot; do
+		old_stderr = STDERR
+		file = File.new('output.tmp', 'w+')
+		begin
+			Object.send(:remove_const, &quot;STDERR&quot;) rescue nil
+			Object.const_set(&quot;STDERR&quot;, file)
+			
+			block = lambda do
+				load_nonexistant_framework.close
+			end
+			block.should raise_error(FrameworkInitError)
+			
+			file.rewind
+			data = file.read
+			data.should =~ /load_nonexistant_framework/
+			data.should =~ /spawner_error_handling_spec\.rb/
+		ensure
+			Object.send(:remove_const, &quot;STDERR&quot;) rescue nil
+			Object.const_set(&quot;STDERR&quot;, old_stderr)
+			file.close rescue nil
+			File.unlink('output.tmp') rescue nil
+		end
 	end
 end</diff>
      <filename>test/ruby/rails/spawner_error_handling_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,6 @@
 require 'support/config'
 require 'support/test_helper'
+require 'phusion_passenger/application'
 require 'phusion_passenger/spawn_manager'
 
 require 'ruby/abstract_server_spec'
@@ -32,7 +33,7 @@ describe SpawnManager do
 			@spawn_method = &quot;smart&quot;
 		end
 		
-		it_should_behave_like &quot;AbstractServer&quot;
+		it_should_behave_like &quot;an AbstractServer&quot;
 	end
 	
 	describe &quot;conservative spawning&quot; do
@@ -40,7 +41,7 @@ describe SpawnManager do
 			@spawn_method = &quot;conservative&quot;
 		end
 		
-		it_should_behave_like &quot;AbstractServer&quot;
+		it_should_behave_like &quot;an AbstractServer&quot;
 	end
 	
 	def spawn_arbitrary_application
@@ -169,19 +170,21 @@ describe SpawnManager do
 	it_should_behave_like &quot;handling errors in application initialization&quot;
 	it_should_behave_like &quot;handling errors in framework initialization&quot;
 	
-	def spawn_stub_application(stub)
+	def spawn_stub_application(stub, extra_options = {})
 		spawner = SpawnManager.new
 		begin
-			return spawner.spawn_application(
+			options = {
 				&quot;app_root&quot; =&gt; stub.app_root,
 				&quot;spawn_method&quot; =&gt; @spawn_method,
-				&quot;lowest_user&quot; =&gt; CONFIG['lowest_user'])
+				&quot;lowest_user&quot; =&gt; CONFIG['lowest_user']
+			}.merge(extra_options)
+			return spawner.spawn_application(options)
 		ensure
 			spawner.cleanup
 		end
 	end
 	
-	def load_nonexistant_framework
+	def load_nonexistant_framework(extra_options = {})
 		# Prevent detect_framework_version from raising VersionNotFound
 		Application.instance_eval do
 			alias orig_detect_framework_version detect_framework_version
@@ -192,7 +195,7 @@ describe SpawnManager do
 		begin
 			File.write(@stub.environment_rb, &quot;RAILS_GEM_VERSION = '1.9.827'&quot;)
 			@stub.dont_use_vendor_rails
-			return spawn_stub_application(@stub)
+			return spawn_stub_application(@stub, extra_options)
 		ensure
 			Application.instance_eval do
 				alias detect_framework_version orig_detect_framework_version</diff>
      <filename>test/ruby/spawn_manager_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -3,7 +3,7 @@ require 'support/config'
 require 'ruby/abstract_server_spec'
 
 shared_examples_for &quot;a spawn server&quot; do
-	it_should_behave_like &quot;AbstractServer&quot;
+	it_should_behave_like &quot;an AbstractServer&quot;
 	
 	it &quot;raises an AbstractServer::ServerError if the server was killed&quot; do
 		Process.kill('SIGABRT', @spawner.server_pid)</diff>
      <filename>test/ruby/spawn_server_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,6 +2,8 @@ require 'support/config'
 
 require 'tmpdir'
 require 'fileutils'
+require 'stringio'
+require 'phusion_passenger/message_channel'
 require 'phusion_passenger/utils'
 
 include PhusionPassenger
@@ -11,36 +13,204 @@ describe Utils do
 	
 	specify &quot;#close_all_io_objects_for_fds closes all IO objects that are associated with the given file descriptors&quot; do
 		filename = &quot;#{Dir.tmpdir}/passenger_test.#{Process.pid}.txt&quot;
-		puts &quot;#{$$}: 1&quot;
 		begin
-			pid = fork do
-				begin
-					puts &quot;#{$$}: 2&quot;
-					a, b = IO.pipe
-					puts &quot;#{$$}: 3&quot;
-					close_all_io_objects_for_fds([0, 1, 2])
-					puts &quot;#{$$}: 4&quot;
-					File.open(filename, &quot;w&quot;) do |f|
-						f.write(&quot;#{a.closed?}, #{b.closed?}&quot;)
-					end
-					puts &quot;#{$$}: 5&quot;
-				rescue Exception =&gt; e
-					print_exception(&quot;utils_spec&quot;, e)
-				ensure
-					puts &quot;#{$$}: 6&quot;
-					exit!
+			pid = safe_fork('utils_spec') do
+				a, b = IO.pipe
+				close_all_io_objects_for_fds([0, 1, 2])
+				File.open(filename, &quot;w&quot;) do |f|
+					f.write(&quot;#{a.closed?}, #{b.closed?}&quot;)
 				end
 			end
-			puts &quot;#{$$}: 6&quot;
 			Process.waitpid(pid) rescue nil
-			puts &quot;#{$$}: 7&quot;
 			File.read(filename).should == &quot;true, true&quot;
 		ensure
-			puts &quot;#{$$}: 8&quot;
 			File.unlink(filename) rescue nil
 		end
 	end
 	
+	describe &quot;#report_app_init_status&quot; do
+		it &quot;reports normal errors, which #unmarshal_and_raise_errors raises&quot; do
+			a, b = IO.pipe
+			begin
+				pid = safe_fork('utils_spec') do
+					a.close
+					report_app_init_status(MessageChannel.new(b)) do
+						raise RuntimeError, &quot;hello world&quot;
+					end
+				end
+				b.close
+				lambda { unmarshal_and_raise_errors(MessageChannel.new(a)) }.should raise_error(/hello world/)
+			ensure
+				a.close rescue nil
+				b.close rescue nil
+			end
+		end
+		
+		it &quot;reports SystemExit errors, which #unmarshal_and_raise_errors raises&quot; do
+			a, b = IO.pipe
+			begin
+				pid = safe_fork('utils_spec') do
+					a.close
+					report_app_init_status(MessageChannel.new(b)) do
+						exit
+					end
+				end
+				b.close
+				lambda { unmarshal_and_raise_errors(MessageChannel.new(a)) }.should raise_error(/exited during startup/)
+			ensure
+				a.close rescue nil
+				b.close rescue nil
+			end
+		end
+		
+		it &quot;returns whether the block succeeded&quot; do
+			channel = MessageChannel.new(StringIO.new)
+			success = report_app_init_status(channel) do
+				false
+			end
+			success.should be_true
+			
+			success = report_app_init_status(channel) do
+				raise StandardError, &quot;hi&quot;
+			end
+			success.should be_false
+		end
+		
+		it &quot;reports all data written to stderr&quot; do
+			a, b = IO.pipe
+			begin
+				pid = safe_fork('utils_spec') do
+					a.close
+					report_app_init_status(MessageChannel.new(b)) do
+						STDERR.puts &quot;Something went wrong!&quot;
+						exit
+					end
+				end
+				b.close
+				
+				begin
+					unmarshal_and_raise_errors(MessageChannel.new(a))
+					violated &quot;No exception raised&quot;
+				rescue AppInitError =&gt; e
+					e.stderr.should =~ /Something went wrong!/
+				end
+			ensure
+				a.close rescue nil
+				b.close rescue nil
+			end
+		end
+		
+		it &quot;writes all buffered stderr data to the 'write_stderr_contents_to' argument if the block failed&quot; do
+			stderr_buffer = StringIO.new
+			report_app_init_status(MessageChannel.new(StringIO.new), stderr_buffer) do
+				STDERR.puts &quot;Something went wrong!&quot;
+				raise StandardError, &quot;:-(&quot;
+			end
+			
+			stderr_buffer.string.should =~ /Something went wrong!/
+		end
+		
+		it &quot;writes all buffered stderr data to the 'write_stderr_contents_to' argument if the block succeeded&quot; do
+			stderr_buffer = StringIO.new
+			report_app_init_status(MessageChannel.new(StringIO.new), stderr_buffer) do
+				STDERR.puts &quot;Something went wrong!&quot;
+			end
+			
+			stderr_buffer.string.should =~ /Something went wrong!/
+		end
+	end
+	
+	specify &quot;#safe_fork with double_fork == false reseeds the pseudo-random number generator&quot; do
+		a, b = IO.pipe
+		begin
+			pid = safe_fork do
+				b.puts(rand)
+			end
+			Process.waitpid(pid) rescue nil
+			pid = safe_fork do
+				b.puts(rand)
+			end
+			Process.waitpid(pid) rescue nil
+			
+			first_num = a.readline
+			second_num = a.readline
+			first_num.should_not == second_num
+		ensure
+			a.close rescue nil
+			b.close rescue nil
+		end
+	end
+	
+	specify &quot;#safe_fork with double_fork == true reseeds the pseudo-random number generator&quot; do
+		a, b = IO.pipe
+		begin
+			# Seed the pseudo-random number generator here
+			# so that it doesn't happen in the child processes.
+			srand
+			
+			safe_fork(self.class, true) do
+				b.puts(rand)
+			end
+			safe_fork(self.class, true) do
+				b.puts(rand)
+			end
+			
+			first_num = a.readline
+			second_num = a.readline
+			first_num.should_not == second_num
+		ensure
+			a.close rescue nil
+			b.close rescue nil
+		end
+	end
+	
+	describe &quot;#unmarshal_and_raise_errors&quot; do
+		before :each do
+			@a, @b = IO.pipe
+			@report_channel = MessageChannel.new(@a)
+			report_app_init_status(MessageChannel.new(@b)) do
+				raise StandardError, &quot;Something went wrong!&quot;
+			end
+		end
+		
+		after :each do
+			@a.close rescue nil
+			@b.close rescue nil
+		end
+		
+		it &quot;prints the exception information to the 'print_exception' argument using #puts, if 'print_exception' responds to that&quot; do
+			buffer = StringIO.new
+			lambda { unmarshal_and_raise_errors(@report_channel, buffer) }.should raise_error(AppInitError)
+			buffer.string.should =~ /Something went wrong!/
+			buffer.string.should =~ /utils\.rb/
+			buffer.string.should =~ /utils_spec\.rb/
+		end
+		
+		it &quot;appends the exception information to the file pointed to by 'print_exception', if 'print_exception' responds to #to_str&quot; do
+			begin
+				lambda { unmarshal_and_raise_errors(@report_channel, &quot;exception.txt&quot;) }.should raise_error(AppInitError)
+				data = File.read('exception.txt')
+				data.should =~ /Something went wrong!/
+				data.should =~ /utils\.rb/
+				data.should =~ /utils_spec\.rb/
+			ensure
+				File.unlink('exception.txt') rescue nil
+			end
+		end
+	end
+	
+	specify &quot;#to_boolean works&quot; do
+		to_boolean(nil).should be_false
+		to_boolean(false).should be_false
+		to_boolean(true).should be_true
+		to_boolean(1).should be_true
+		to_boolean(0).should be_true
+		to_boolean(&quot;&quot;).should be_true
+		to_boolean(&quot;true&quot;).should be_true
+		to_boolean(&quot;false&quot;).should be_false
+		to_boolean(&quot;bla bla&quot;).should be_true
+	end
+	
 	describe &quot;#passenger_tmpdir&quot; do
 		before :each do
 			@old_passenger_tmpdir = Utils.passenger_tmpdir
@@ -71,8 +241,11 @@ describe Utils do
 			begin
 				File.directory?('utils_spec.tmp').should be_true
 			ensure
+				FileUtils.chmod_R(0777, 'utils_spec.tmp')
 				FileUtils.rm_rf('utils_spec.tmp')
 			end
 		end
 	end
+	
+	######################
 end</diff>
      <filename>test/ruby/utils_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -5,6 +5,8 @@ require 'phusion_passenger/utils'
 require 'fileutils'
 require 'tempfile'
 
+include PhusionPassenger
+
 describe PhusionPassenger::WSGI::ApplicationSpawner do
 	include TestHelper
 	include PhusionPassenger::Utils
@@ -39,7 +41,7 @@ describe PhusionPassenger::WSGI::ApplicationSpawner do
 	
 	specify &quot;the backend process deletes its socket upon termination&quot; do
 		spawn(@stub.app_root).close
-		sleep 0.2 # Give it some time to terminate.
+		sleep 0.25 # Give it some time to terminate.
 		File.chmod(0700, &quot;#{passenger_tmpdir}/backends&quot;)
 		Dir[&quot;#{passenger_tmpdir}/backends/wsgi_backend.*&quot;].should be_empty
 	end</diff>
      <filename>test/ruby/wsgi/application_spawner_spec.rb</filename>
    </modified>
    <modified>
      <diff>@@ -23,6 +23,9 @@ Listen 127.0.0.1:&lt;%= @port %&gt;
 &lt;% if !has_builtin_module?('mod_rewrite.c') &amp;&amp; has_module?('mod_rewrite.so') %&gt;
 	LoadModule rewrite_module &quot;&lt;%= modules_dir %&gt;/mod_rewrite.so&quot;
 &lt;% end %&gt;
+&lt;% if !has_builtin_module?('mod_env.c') %&gt;
+	LoadModule env_module &quot;&lt;%= modules_dir %&gt;/mod_env.so&quot;
+&lt;% end %&gt;
 LoadModule passenger_module &quot;&lt;%= @mod_passenger %&gt;&quot;
 
 PassengerRoot &quot;&lt;%= @passenger_root %&gt;&quot;</diff>
      <filename>test/stub/apache2/httpd.conf.erb</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
 app = lambda do |env|
-    return [200, { &quot;Content-Type&quot; =&gt; &quot;text/html&quot; }, &quot;hello &lt;b&gt;world&lt;/b&gt;&quot;]
+    [200, { &quot;Content-Type&quot; =&gt; &quot;text/html&quot; }, [&quot;hello &lt;b&gt;world&lt;/b&gt;&quot;]]
 end
 run app</diff>
      <filename>test/stub/rack/config.ru</filename>
    </modified>
    <modified>
      <diff>@@ -40,6 +40,14 @@ class WelcomeController &lt; ApplicationController
 	def show_id
 		render :text =&gt; params[:id]
 	end
+	
+	def environment
+		text = &quot;&quot;
+		ENV.each_pair do |key, value|
+			text &lt;&lt; &quot;#{key} = #{value}\n&quot;
+		end
+		render :text =&gt; text
+	end
 
 	def request_uri
 		render :text =&gt; request.request_uri</diff>
      <filename>test/stub/rails_apps/mycook/app/controllers/welcome_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -6,12 +6,49 @@
 #include &lt;iostream&gt;
 #include &lt;string&gt;
 #include &lt;exception&gt;
+#include &lt;cstdio&gt;
 #include &lt;cerrno&gt;
 #include &lt;cstring&gt;
+#include &lt;utime.h&gt;
+
+#include &quot;Exceptions.h&quot;
+#include &quot;Utils.h&quot;
 
 namespace Test {
 
 using namespace std;
+using namespace Passenger;
+
+/**
+ * Read all data from the given file descriptor until EOF.
+ *
+ * @throws SystemException
+ */
+string readAll(int fd);
+
+/**
+ * Look for 'toFind' inside 'str', replace it with 'replaceWith' and return the result.
+ * Only the first occurence of 'toFind' is replaced.
+ */
+string replaceString(const string &amp;str, const string &amp;toFind, const string &amp;replaceWith);
+
+/**
+ * Look for 'toFind' inside the given file, replace it with 'replaceWith' and write
+ * the result back to the file. Only the first occurence of 'toFind' is replaced.
+ *
+ * @throws FileSystemException
+ */
+void replaceStringInFile(const char *filename, const string &amp;toFind, const string &amp;replaceWith);
+
+/**
+ * Touch the given file: create the file if it doesn't exist, update its
+ * timestamp if it does. If the &lt;tt&gt;timestamp&lt;/tt&gt; argument is -1, then
+ * the current system time will be used, otherwise the given timestamp
+ * will be used.
+ *
+ * @throws FileSystemException
+ */
+void touchFile(const char *filename, time_t timestamp = (time_t) - 1);
 
 /**
  * Class which creates a temporary directory of the given name, and deletes
@@ -25,17 +62,38 @@ public:
 		this-&gt;name = name;
 		if (mkdir(name.c_str(), S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) != 0) {
 			int e = errno;
-			cerr &lt;&lt; &quot;Cannot create directory '&quot; &lt;&lt; name &lt;&lt; &quot;': &quot; &lt;&lt;
-				strerror(e) &lt;&lt;&quot; (&quot; &lt;&lt; e &lt;&lt; &quot;)&quot; &lt;&lt; endl;
-			throw exception();
+			string message = &quot;Cannot create directory '&quot;;
+			message.append(name);
+			message.append(&quot;'&quot;);
+			throw FileSystemException(message, e, name);
 		}
 	}
 	
 	~TempDir() {
-		string command(&quot;rm -rf \&quot;&quot;);
-		command.append(name);
-		command.append(&quot;\&quot;&quot;);
-		system(command.c_str());
+		removeDirTree(name);
+	}
+};
+
+/**
+ * Creates a temporary copy of the given directory. This copy is deleted
+ * upon object destruction.
+ */
+class TempDirCopy {
+private:
+	string dir;
+public:
+	TempDirCopy(const string &amp;source, const string &amp;dest) {
+		dir = dest;
+		removeDirTree(dest);
+		
+		char command[1024];
+		snprintf(command, sizeof(command), &quot;cp -pR \&quot;%s\&quot; \&quot;%s\&quot;&quot;,
+			source.c_str(), dest.c_str());
+		system(command);
+	}
+	
+	~TempDirCopy() {
+		removeDirTree(dir);
 	}
 };
 
@@ -50,7 +108,7 @@ public:
 		this-&gt;filename = filename;
 	}
 	
-	DeleteFileEventually() {
+	~DeleteFileEventually() {
 		unlink(filename.c_str());
 	}
 };</diff>
      <filename>test/support/Support.h</filename>
    </modified>
    <modified>
      <diff>@@ -18,9 +18,21 @@ require 'phusion_passenger/utils'
 # Calculate location of the temp dir and cache it.
 PhusionPassenger::Utils.passenger_tmpdir
 
+# Seed the pseudo-random number generator here
+# so that it doesn't happen in the child processes.
+srand
+
 Spec::Runner.configure do |config|
+	config.append_before do
+		# Create the temp directory.
+		PhusionPassenger::Utils.passenger_tmpdir
+	end
+	
 	config.append_after do
-		FileUtils.chmod_R(0777, PhusionPassenger::Utils.passenger_tmpdir);
-		FileUtils.rm_rf(PhusionPassenger::Utils.passenger_tmpdir)
+		tmpdir = PhusionPassenger::Utils.passenger_tmpdir(false)
+		if File.exist?(tmpdir)
+			FileUtils.chmod_R(0777, tmpdir)
+			FileUtils.rm_rf(tmpdir)
+		end
 	end
 end
\ No newline at end of file</diff>
      <filename>test/support/config.rb</filename>
    </modified>
    <modified>
      <diff>@@ -169,5 +169,10 @@ File.class_eval do
 			f.write(content)
 		end
 	end
+	
+	def self.touch(filename, timestamp = nil)
+		File.open(filename, 'w').close
+		File.utime(timestamp, timestamp, filename) if timestamp
+	end
 end
 </diff>
      <filename>test/support/test_helper.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,13 +1,14 @@
-# AUTHOR: blink &lt;blinketje@gmail.com&gt;; blink#ruby-lang@irc.freenode.net
+# AUTHOR: Scytrin dai Kinthra &lt;scytrin@gmail.com&gt;; blink#ruby-lang@irc.freenode.net
 
 gem 'ruby-openid', '~&gt; 2' if defined? Gem
 require 'rack/request'
 require 'rack/utils'
 require 'rack/auth/abstract/handler'
+
 require 'uri'
-require 'openid' #gem
-require 'openid/extension' #gem
-require 'openid/store/memory' #gem
+require 'openid'
+require 'openid/extension'
+require 'openid/store/memory'
 
 module Rack
   class Request
@@ -45,108 +46,111 @@ module Rack
     #
     # NOTE: Due to the amount of data that this library stores in the
     # session, Rack::Session::Cookie may fault.
+    #
+    # == Examples
+    #
+    #   simple_oid = OpenID.new('http://mysite.com/')
+    #
+    #   return_oid = OpenID.new('http://mysite.com/', {
+    #     :return_to =&gt; 'http://mysite.com/openid'
+    #   })
+    #
+    #   complex_oid = OpenID.new('http://mysite.com/',
+    #     :immediate =&gt; true,
+    #     :extensions =&gt; {
+    #       ::OpenID::SReg =&gt; [['email'],['nickname']]
+    #     }
+    #   )
+    #
+    # = Advanced
+    #
+    # Most of the functionality of this library is encapsulated such that
+    # expansion and overriding functions isn't difficult nor tricky.
+    # Alternately, to avoid opening up singleton objects or subclassing, a
+    # wrapper rack middleware can be composed to act upon Auth::OpenID's
+    # responses. See #check and #finish for locations of pertinent data.
+    #
+    # == Responses
+    #
+    # To change the responses that Auth::OpenID returns, override the methods
+    # #redirect, #bad_request, #unauthorized, #access_denied, and
+    # #foreign_server_failure.
+    #
+    # Additionally #confirm_post_params is used when the URI would exceed
+    # length limits on a GET request when doing the initial verification
+    # request.
+    #
+    # == Processing
+    #
+    # To change methods of processing completed transactions, override the
+    # methods #success, #setup_needed, #cancel, and #failure. Please ensure
+    # the returned object is a rack compatible response.
+    #
+    # The first argument is an OpenID::Response, the second is a
+    # Rack::Request of the current request, the last is the hash used in
+    # ruby-openid handling, which can be found manually at
+    # env['rack.session'][:openid].
+    #
+    # This is useful if you wanted to expand the processing done, such as
+    # setting up user accounts.
+    #
+    #   oid_app = Rack::Auth::OpenID.new realm, :return_to =&gt; return_to
+    #   def oid_app.success oid, request, session
+    #     user = Models::User[oid.identity_url]
+    #     user ||= Models::User.create_from_openid oid
+    #     request['rack.session'][:user] = user.id
+    #     redirect MyApp.site_home
+    #   end
+    #
+    #   site_map['/openid'] = oid_app
+    #   map = Rack::URLMap.new site_map
+    #   ...
 
     class OpenID
-
+      # Raised if an incompatible session is being used.
       class NoSession &lt; RuntimeError; end
+      # Raised if an extension not matching specifications is provided.
       class BadExtension &lt; RuntimeError; end
-      # Required for ruby-openid
-      ValidStatus = [:success, :setup_needed, :cancel, :failure]
+      # Possible statuses returned from consumer responses. See definitions
+      # in the ruby-openid library.
+      ValidStatus = [
+        ::OpenID::Consumer::SUCCESS,
+        ::OpenID::Consumer::FAILURE,
+        ::OpenID::Consumer::CANCEL,
+        ::OpenID::Consumer::SETUP_NEEDED
+      ]
 
-      # = Arguments
-      #
       # The first argument is the realm, identifying the site they are trusting
       # with their identity. This is required, also treated as the trust_root
       # in OpenID 1.x exchanges.
       #
-      # The optional second argument is a hash of options.
-      #
-      # == Options
+      # The lits of acceptable options include :return_to, :session_key,
+      # :openid_param, :store, :immediate, :extensions.
       #
       # &lt;tt&gt;:return_to&lt;/tt&gt; defines the url to return to after the client
       # authenticates with the openid service provider. This url should point
-      # to where Rack::Auth::OpenID is mounted. If &lt;tt&gt;:return_to&lt;/tt&gt; is not
-      # provided, return_to will be the current url which allows flexibility
-      # with caveats.
+      # to where Rack::Auth::OpenID is mounted. If unprovided, the url of
+      # the current request is used.
       #
       # &lt;tt&gt;:session_key&lt;/tt&gt; defines the key to the session hash in the env.
-      # It defaults to 'rack.session'.
+      # The default is 'rack.session'.
       #
       # &lt;tt&gt;:openid_param&lt;/tt&gt; defines at what key in the request parameters to
       # find the identifier to resolve. As per the 2.0 spec, the default is
       # 'openid_identifier'.
       #
       # &lt;tt&gt;:store&lt;/tt&gt; defined what OpenID Store to use for persistant
-      # information. By default a Store::Memory will be used.
+      # information. By default a Store::Memory is used.
       #
       # &lt;tt&gt;:immediate&lt;/tt&gt; as true will make initial requests to be of an
       # immediate type. This is false by default. See OpenID specification
       # documentation.
       #
       # &lt;tt&gt;:extensions&lt;/tt&gt; should be a hash of openid extension
-      # implementations. The key should be the extension main module, the value
-      # should be an array of arguments for extension::Request.new.
+      # implementations. The key should be the extension module, the value
+      # should be an array of arguments for extension::Request.new().
       # The hash is iterated over and passed to #add_extension for processing.
       # Please see #add_extension for further documentation.
-      #
-      # == Examples
-      #
-      #   simple_oid = OpenID.new('http://mysite.com/')
-      #
-      #   return_oid = OpenID.new('http://mysite.com/', {
-      #     :return_to =&gt; 'http://mysite.com/openid'
-      #   })
-      #
-      #   complex_oid = OpenID.new('http://mysite.com/',
-      #     :immediate =&gt; true,
-      #     :extensions =&gt; {
-      #       ::OpenID::SReg =&gt; [['email'],['nickname']]
-      #     }
-      #   )
-      #
-      # = Advanced
-      #
-      # Most of the functionality of this library is encapsulated such that
-      # expansion and overriding functions isn't difficult nor tricky.
-      # Alternately, to avoid opening up singleton objects or subclassing, a
-      # wrapper rack middleware can be composed to act upon Auth::OpenID's
-      # responses. See #check and #finish for locations of pertinent data.
-      #
-      # == Responses
-      #
-      # To change the responses that Auth::OpenID returns, override the methods
-      # #redirect, #bad_request, #unauthorized, #access_denied, and
-      # #foreign_server_failure.
-      #
-      # Additionally #confirm_post_params is used when the URI would exceed
-      # length limits on a GET request when doing the initial verification
-      # request.
-      #
-      # == Processing
-      #
-      # To change methods of processing completed transactions, override the
-      # methods #success, #setup_needed, #cancel, and #failure. Please ensure
-      # the returned object is a rack compatible response.
-      #
-      # The first argument is an OpenID::Response, the second is a
-      # Rack::Request of the current request, the last is the hash used in
-      # ruby-openid handling, which can be found manually at
-      # env['rack.session'][:openid].
-      #
-      # This is useful if you wanted to expand the processing done, such as
-      # setting up user accounts.
-      #
-      #   oid_app = Rack::Auth::OpenID.new realm, :return_to =&gt; return_to
-      #   def oid_app.success oid, request, session
-      #     user = Models::User[oid.identity_url]
-      #     user ||= Models::User.create_from_openid oid
-      #     request['rack.session'][:user] = user.id
-      #     redirect MyApp.site_home
-      #   end
-      #
-      #   site_map['/openid'] = oid_app
-      #   map = Rack::URLMap.new site_map
-      #   ...
 
       def initialize(realm, options={})
         realm = URI(realm)
@@ -162,7 +166,7 @@ module Rack
           ruri = URI(ruri)
           raise ArgumentError, &quot;Invalid return_to: #{ruri}&quot; \
             unless ruri.absolute? \
-            and ruri.scheme  =~ /^https?$/ \
+            and ruri.scheme =~ /^https?$/ \
             and ruri.fragment.nil?
           raise ArgumentError, &quot;return_to #{ruri} not within realm #{realm}&quot; \
             unless self.within_realm?(ruri)
@@ -174,10 +178,10 @@ module Rack
         @store        = options[:store]         || ::OpenID::Store::Memory.new
         @immediate    = !!options[:immediate]
 
-        @extensions = {}
-        if extensions = options.delete(:extensions)
+        @extensions   = {}
+        if extensions = options[:extensions]
           extensions.each do |ext, args|
-            add_extension ext, *args
+            add_extension(ext, *args)
           end
         end
 
@@ -199,33 +203,29 @@ module Rack
       # If the parameter specified by &lt;tt&gt;options[:openid_param]&lt;/tt&gt; is
       # present, processing is passed to #check and the result is returned.
       #
-      # If neither of these conditions are met, #unauthorized is called.
+      # If neither of these conditions are met, #bad_request is called.
 
       def call(env)
         env['rack.auth.openid'] = self
         env_session = env[@session_key]
         unless env_session and env_session.is_a?(Hash)
-          raise NoSession, 'No compatible session'
+          raise NoSession, 'No compatible session.'
         end
         # let us work in our own namespace...
         session = (env_session[:openid] ||= {})
         unless session and session.is_a?(Hash)
-          raise NoSession, 'Incompatible openid session'
+          raise NoSession, 'Incompatible openid session.'
         end
 
         request = Rack::Request.new(env)
         consumer = ::OpenID::Consumer.new(session, @store)
 
         if mode = request.GET['openid.mode']
-          if session.key?(:openid_param)
-            finish(consumer, session, request)
-          else
-            bad_request
-          end
+          finish(consumer, session, request)
         elsif request.GET[@openid_param]
           check(consumer, session, request)
         else
-          unauthorized
+          bad_request
         end
       end
 
@@ -263,14 +263,13 @@ module Rack
         immediate = session.key?(:setup_needed) ? false : immediate
 
         if oid.send_redirect?(realm, return_to_uri, immediate)
-          uri = oid.redirect_url(realm, return_to_uri, immediate)
-          redirect(uri)
+          redirect(oid.redirect_url(realm, return_to_uri, immediate))
         else
           confirm_post_params(oid, realm, return_to_uri, immediate)
         end
       rescue ::OpenID::DiscoveryFailure =&gt; e
         # thrown from inside OpenID::Consumer#begin by yadis stuff
-        req.env['rack.errors'].puts([e.message, *e.backtrace]*&quot;\n&quot;)
+        req.env['rack.errors'].puts( [e.message, *e.backtrace]*&quot;\n&quot; )
         return foreign_server_failure
       end
 
@@ -290,21 +289,24 @@ module Rack
         req.env['rack.errors'].puts(oid.message)
         p oid if $DEBUG
 
-        raise unless ValidStatus.include?(oid.status)
-        __send__(oid.status, oid, req, session)
+        if ValidStatus.include?(oid.status)
+          __send__(oid.status, oid, req, session)
+        else
+          invalid_status(oid, req, session)
+        end
       end
 
       # The first argument should be the main extension module.
       # The extension module should contain the constants:
-      #   * class Request, should have OpenID::Extension as an ancestor
-      #   * class Response, should have OpenID::Extension as an ancestor
-      #   * string NS_URI, which defining the namespace of the extension
+      # * class Request, should have OpenID::Extension as an ancestor
+      # * class Response, should have OpenID::Extension as an ancestor
+      # * string NS_URI, which defining the namespace of the extension
       #
       # All trailing arguments will be passed to extension::Request.new in
       # #check.
       # The openid response will be passed to
-      # extension::Response#from_success_response, #get_extension_args will be
-      # called on the result to attain the gathered data.
+      # extension::Response#from_success_response, oid#get_extension_args will
+      # be called on the result to attain the gathered data.
       #
       # This method returns the key at which the response data will be found in
       # the session, which is the namespace uri by default.
@@ -344,28 +346,27 @@ module Rack
         return false unless uri.host.match(realm_match)
         return true
       end
+
       alias_method :include?, :within_realm?
 
       protected
 
-      ### These methods define some of the boilerplate responses.
-
       # Returns an html form page for posting to an Identity Provider if the
       # GET request would exceed the upper URI length limit.
 
       def confirm_post_params(oid, realm, return_to, immediate)
-        Rack::Response.new.finish do |r|
-          r.write '&lt;html&gt;&lt;head&gt;&lt;title&gt;Confirm...&lt;/title&gt;&lt;/head&gt;&lt;body&gt;'
-          r.write oid.form_markup(realm, return_to, immediate)
-          r.write '&lt;/body&gt;&lt;/html&gt;'
-        end
+        response = Rack::Response.new '&lt;html&gt;'+
+          '&lt;head&gt;&lt;title&gt;Confirm...&lt;/title&gt;&lt;/head&gt;'+
+          '&lt;body&gt;'+oid.form_markup(realm, return_to, immediate)+'&lt;/body&gt;'+
+          '&lt;/html&gt;'
+        response.finish
       end
 
       # Returns a 303 redirect with the destination of that provided by the
       # argument.
 
       def redirect(uri)
-        [ 303, {'Content-Length'=&gt;'0', 'Content-Type'=&gt;'text/plain',
+        [ 303, {'Content-Type'=&gt;'text/plain', 'Content-Length'=&gt;'0',
           'Location' =&gt; uri},
           [] ]
       end
@@ -401,10 +402,6 @@ module Rack
 
       private
 
-      ### These methods are called after a transaction is completed, depending
-      # on its outcome. These should all return a rack compatible response.
-      # You'd want to override these to provide additional functionality.
-
       # Called to complete processing on a successful transaction.
       # Within the openid session, :openid_identity and :openid_identifier are
       # set to the user friendly and the standard representation of the
@@ -430,7 +427,7 @@ module Rack
       def setup_needed(oid, request, session)
         identifier = session[:openid_param]
         session[:setup_needed] = true
-        redirect req.script_name + '?' + openid_param + '=' + identifier
+        redirect(req.script_name + '?' + openid_param + '=' + identifier)
       end
 
       # Called if the user indicates they wish to cancel identification.
@@ -448,6 +445,16 @@ module Rack
       def failure(oid, request, session)
         unauthorized
       end
+
+      # To be called if there is no method for handling the OpenID response
+      # status.
+
+      def invalid_status(oid, request, session)
+        msg = 'Invalid status returned by the OpenID authorization reponse.'
+        [ 500,
+          {'Content-Type'=&gt;'text/plain','Content-Length'=&gt;msg.length.to_s},
+          [msg] ]
+      end
     end
 
     # A class developed out of the request to use OpenID as an authentication
@@ -472,8 +479,8 @@ module Rack
       end
 
       def call(env)
-        to = auth.call(env) ? @app : @oid
-        to.call env
+        to = @authenticator.call(env) ? @app : @oid
+        to.call(env)
       end
     end
   end</diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/auth/openid.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,31 +4,36 @@ module Rack
   # status codes).
 
   class Cascade
+    NotFound = [404, {}, []]
+
     attr_reader :apps
 
     def initialize(apps, catch=404)
-      @apps = apps
-      @catch = [*catch]
+      @apps = []; @has_app = {}
+      apps.each { |app| add app }
+
+      @catch = {}
+      [*catch].each { |status| @catch[status] = true }
     end
 
     def call(env)
-      status = headers = body = nil
-      raise ArgumentError, &quot;empty cascade&quot;  if @apps.empty?
-      @apps.each { |app|
-        begin
-          status, headers, body = app.call(env)
-          break  unless @catch.include?(status.to_i)
-        end
-      }
-      [status, headers, body]
+      result = NotFound
+
+      @apps.each do |app|
+        result = app.call(env)
+        break unless @catch.include?(result[0].to_i)
+      end
+
+      result
     end
 
     def add app
+      @has_app[app] = true
       @apps &lt;&lt; app
     end
 
     def include? app
-      @apps.include? app
+      @has_app.include? app
     end
 
     alias_method :&lt;&lt;, :add</diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/cascade.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,60 +2,51 @@ module Rack
   # Rack::CommonLogger forwards every request to an +app+ given, and
   # logs a line in the Apache common log format to the +logger+, or
   # rack.errors by default.
-
   class CommonLogger
+    # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
+    # lilith.local - - [07/Aug/2006 23:58:02] &quot;GET / HTTP/1.1&quot; 500 -
+    #             %{%s - %s [%s] &quot;%s %s%s %s&quot; %d %s\n} %
+    FORMAT = %{%s - %s [%s] &quot;%s %s%s %s&quot; %d %s %0.4f\n}
+
     def initialize(app, logger=nil)
       @app = app
       @logger = logger
     end
 
     def call(env)
-      dup._call(env)
-    end
-
-    def _call(env)
-      @env = env
-      @logger ||= self
-      @time = Time.now
-      @status, @header, @body = @app.call(env)
-      [@status, @header, self]
+      began_at = Time.now
+      status, header, body = @app.call(env)
+      log(env, status, header, began_at)
+      [status, header, body]
     end
 
-    def close
-      @body.close if @body.respond_to? :close
+    private
+
+    def log(env, status, header, began_at)
+      now = Time.now
+      length = extract_content_length(header)
+
+      logger = @logger || env['rack.errors']
+      logger.write FORMAT % [
+        env['HTTP_X_FORWARDED_FOR'] || env[&quot;REMOTE_ADDR&quot;] || &quot;-&quot;,
+        env[&quot;REMOTE_USER&quot;] || &quot;-&quot;,
+        now.strftime(&quot;%d/%b/%Y %H:%M:%S&quot;),
+        env[&quot;REQUEST_METHOD&quot;],
+        env[&quot;PATH_INFO&quot;],
+        env[&quot;QUERY_STRING&quot;].empty? ? &quot;&quot; : &quot;?&quot;+env[&quot;QUERY_STRING&quot;],
+        env[&quot;HTTP_VERSION&quot;],
+        status.to_s[0..3],
+        length,
+        now - began_at ]
     end
 
-    # By default, log to rack.errors.
-    def &lt;&lt;(str)
-      @env[&quot;rack.errors&quot;].write(str)
-      @env[&quot;rack.errors&quot;].flush
-    end
-
-    def each
-      length = 0
-      @body.each { |part|
-        length += part.size
-        yield part
-      }
-
-      @now = Time.now
-
-      # Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
-      # lilith.local - - [07/Aug/2006 23:58:02] &quot;GET / HTTP/1.1&quot; 500 -
-      #             %{%s - %s [%s] &quot;%s %s%s %s&quot; %d %s\n} %
-      @logger &lt;&lt; %{%s - %s [%s] &quot;%s %s%s %s&quot; %d %s %0.4f\n} %
-        [
-         @env['HTTP_X_FORWARDED_FOR'] || @env[&quot;REMOTE_ADDR&quot;] || &quot;-&quot;,
-         @env[&quot;REMOTE_USER&quot;] || &quot;-&quot;,
-         @now.strftime(&quot;%d/%b/%Y %H:%M:%S&quot;),
-         @env[&quot;REQUEST_METHOD&quot;],
-         @env[&quot;PATH_INFO&quot;],
-         @env[&quot;QUERY_STRING&quot;].empty? ? &quot;&quot; : &quot;?&quot;+@env[&quot;QUERY_STRING&quot;],
-         @env[&quot;HTTP_VERSION&quot;],
-         @status.to_s[0..3],
-         (length.zero? ? &quot;-&quot; : length.to_s),
-         @now - @time
-        ]
+    def extract_content_length(headers)
+      headers.each do |key, value|
+        if key.downcase == 'content-length'
+          return value.to_s == '0' ? '-' : value
+        end
+      end
+      '-'
     end
   end
 end</diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/commonlogger.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,7 +15,7 @@ module Rack
 
         env[&quot;SCRIPT_NAME&quot;] = &quot;&quot;  if env[&quot;SCRIPT_NAME&quot;] == &quot;/&quot;
 
-        env.update({&quot;rack.version&quot; =&gt; [0,1],
+        env.update({&quot;rack.version&quot; =&gt; [1,0],
                      &quot;rack.input&quot; =&gt; $stdin,
                      &quot;rack.errors&quot; =&gt; $stderr,
 </diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/handler/cgi.rb</filename>
    </modified>
    <modified>
      <diff>@@ -34,7 +34,7 @@ module Rack
         
         rack_input = RewindableInput.new(request.in)
 
-        env.update({&quot;rack.version&quot; =&gt; [0,1],
+        env.update({&quot;rack.version&quot; =&gt; [1,0],
                      &quot;rack.input&quot; =&gt; rack_input,
                      &quot;rack.errors&quot; =&gt; request.err,
 </diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/handler/fastcgi.rb</filename>
    </modified>
    <modified>
      <diff>@@ -15,7 +15,7 @@ module Rack
         env = ENV.to_hash
         env.delete &quot;HTTP_CONTENT_LENGTH&quot;
         env[&quot;SCRIPT_NAME&quot;] = &quot;&quot; if env[&quot;SCRIPT_NAME&quot;] == &quot;/&quot;
-        env.update({&quot;rack.version&quot; =&gt; [0,1],
+        env.update({&quot;rack.version&quot; =&gt; [1,0],
                      &quot;rack.input&quot; =&gt; StringIO.new($stdin.read.to_s),
                      &quot;rack.errors&quot; =&gt; $stderr,
                      &quot;rack.multithread&quot; =&gt; false,</diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/handler/lsws.rb</filename>
    </modified>
    <modified>
      <diff>@@ -45,7 +45,7 @@ module Rack
 
         env[&quot;SCRIPT_NAME&quot;] = &quot;&quot;  if env[&quot;SCRIPT_NAME&quot;] == &quot;/&quot;
 
-        env.update({&quot;rack.version&quot; =&gt; [0,1],
+        env.update({&quot;rack.version&quot; =&gt; [1,0],
                      &quot;rack.input&quot; =&gt; request.body || StringIO.new(&quot;&quot;),
                      &quot;rack.errors&quot; =&gt; $stderr,
 </diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/handler/mongrel.rb</filename>
    </modified>
    <modified>
      <diff>@@ -32,7 +32,7 @@ module Rack
         env[&quot;PATH_INFO&quot;] = env[&quot;REQUEST_PATH&quot;]
         env[&quot;QUERY_STRING&quot;] ||= &quot;&quot;
         env[&quot;SCRIPT_NAME&quot;] = &quot;&quot;
-        env.update({&quot;rack.version&quot; =&gt; [0,1],
+        env.update({&quot;rack.version&quot; =&gt; [1,0],
                      &quot;rack.input&quot; =&gt; StringIO.new(input_body),
                      &quot;rack.errors&quot; =&gt; $stderr,
 </diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/handler/scgi.rb</filename>
    </modified>
    <modified>
      <diff>@@ -22,7 +22,7 @@ module Rack
         env = req.meta_vars
         env.delete_if { |k, v| v.nil? }
 
-        env.update({&quot;rack.version&quot; =&gt; [0,1],
+        env.update({&quot;rack.version&quot; =&gt; [1,0],
                      &quot;rack.input&quot; =&gt; StringIO.new(req.body.to_s),
                      &quot;rack.errors&quot; =&gt; $stderr,
 </diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/handler/webrick.rb</filename>
    </modified>
    <modified>
      <diff>@@ -101,22 +101,9 @@ module Rack
         elsif !opts.has_key?(:input)
           opts[&quot;CONTENT_TYPE&quot;] = &quot;application/x-www-form-urlencoded&quot;
           if params.is_a?(Hash)
-            multipart = false
-            query = lambda { |value|
-              case value
-              when Array
-                value.each(&amp;query)
-              when Hash
-                value.values.each(&amp;query)
-              when Utils::Multipart::UploadedFile
-                multipart = true
-              end
-            }
-            opts[:params].values.each(&amp;query)
-
-            if multipart
-              opts[:input] = Utils::Multipart.build_multipart(params)
-              opts[&quot;CONTENT_LENGTH&quot;] ||= opts[:input].length.to_s
+            if data = Utils::Multipart.build_multipart(params)
+              opts[:input] = data
+              opts[&quot;CONTENT_LENGTH&quot;] ||= data.length.to_s
               opts[&quot;CONTENT_TYPE&quot;] = &quot;multipart/form-data; boundary=#{Utils::Multipart::MULTIPART_BOUNDARY}&quot;
             else
               opts[:input] = Utils.build_nested_query(params)
@@ -162,7 +149,7 @@ module Rack
       @body = &quot;&quot;
       body.each { |part| @body &lt;&lt; part }
 
-      @errors = errors.string
+      @errors = errors.string if errors.respond_to?(:string)
     end
 
     # Status</diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/mock.rb</filename>
    </modified>
    <modified>
      <diff>@@ -17,14 +17,6 @@ module Rack
     # The environment of the request.
     attr_reader :env
 
-    def self.new(env, *args)
-      if self == Rack::Request
-        env[&quot;rack.request&quot;] ||= super
-      else
-        super
-      end
-    end
-
     def initialize(env)
       @env = env
     end
@@ -133,7 +125,9 @@ module Rack
     # This method support both application/x-www-form-urlencoded and
     # multipart/form-data.
     def POST
-      if @env[&quot;rack.request.form_input&quot;].eql? @env[&quot;rack.input&quot;]
+      if @env[&quot;rack.input&quot;].nil?
+        raise &quot;Missing rack.input&quot;
+      elsif @env[&quot;rack.request.form_input&quot;].eql? @env[&quot;rack.input&quot;]
         @env[&quot;rack.request.form_hash&quot;]
       elsif form_data? || parseable_data?
         @env[&quot;rack.request.form_input&quot;] = @env[&quot;rack.input&quot;]</diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/request.rb</filename>
    </modified>
    <modified>
      <diff>@@ -72,6 +72,8 @@ module Rack
       # access it because we have the file handle open.
       @rewindable_io = Tempfile.new('RackRewindableInput')
       @rewindable_io.chmod(0000)
+      @rewindable_io.set_encoding(Encoding::BINARY) if @rewindable_io.respond_to?(:set_encoding)
+      @rewindable_io.binmode
       if filesystem_has_posix_semantics?
         @rewindable_io.unlink
         @unlinked = true</diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/rewindable_input.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,5 @@
+# -*- encoding: binary -*-
+
 require 'set'
 require 'tempfile'
 
@@ -24,16 +26,18 @@ module Rack
     end
     module_function :unescape
 
+    DEFAULT_SEP = /[&amp;;] */n
+    
     # Stolen from Mongrel, with some small modifications:
     # Parses a query string by breaking it up at the '&amp;'
     # and ';' characters.  You can also use this to parse
     # cookies by changing the characters used in the second
     # parameter (which defaults to '&amp;;').
-    def parse_query(qs, d = '&amp;;')
+    def parse_query(qs, d = nil)
       params = {}
 
-      (qs || '').split(/[#{d}] */n).each do |p|
-        k, v = unescape(p).split('=', 2)
+      (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
+        k, v = p.split('=', 2).map { |x| unescape(x) }
 
         if cur = params[k]
           if cur.class == Array
@@ -50,10 +54,10 @@ module Rack
     end
     module_function :parse_query
 
-    def parse_nested_query(qs, d = '&amp;;')
+    def parse_nested_query(qs, d = nil)
       params = {}
 
-      (qs || '').split(/[#{d}] */n).each do |p|
+      (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p|
         k, v = unescape(p).split('=', 2)
         normalize_params(params, k, v)
       end
@@ -63,7 +67,7 @@ module Rack
     module_function :parse_nested_query
 
     def normalize_params(params, name, v = nil)
-      name =~ %r([\[\]]*([^\[\]]+)\]*)
+      name =~ %r(\A[\[\]]*([^\[\]]+)\]*)
       k = $1 || ''
       after = $' || ''
 
@@ -99,7 +103,7 @@ module Rack
         if v.class == Array
           build_query(v.map { |x| [k, x] })
         else
-          escape(k) + &quot;=&quot; + escape(v)
+          &quot;#{escape(k)}=#{escape(v)}&quot;
         end
       }.join(&quot;&amp;&quot;)
     end
@@ -207,6 +211,7 @@ module Rack
     # header when set.
     class HeaderHash &lt; Hash
       def initialize(hash={})
+        super()
         @names = {}
         hash.each { |k, v| self[k] = v }
       end
@@ -223,21 +228,24 @@ module Rack
       end
 
       def [](k)
-        super @names[k.downcase]
+        super(@names[k] ||= @names[k.downcase])
       end
 
       def []=(k, v)
         delete k
-        @names[k.downcase] = k
+        @names[k] = @names[k.downcase] = k
         super k, v
       end
 
       def delete(k)
-        super @names.delete(k.downcase)
+        canonical = k.downcase
+        result = super @names.delete(canonical)
+        @names.delete_if { |name,| name.downcase == canonical }
+        result
       end
 
       def include?(k)
-        @names.has_key? k.downcase
+        @names.include?(k) || @names.include?(k.downcase)
       end
 
       alias_method :has_key?, :include?
@@ -351,7 +359,7 @@ module Rack
           input = env['rack.input']
           input.rewind
 
-          boundary_size = boundary.size + EOL.size
+          boundary_size = Utils.bytesize(boundary) + EOL.size
           bufsize = 16384
 
           content_length -= boundary_size
@@ -441,6 +449,26 @@ module Rack
       end
 
       def self.build_multipart(params, first = true)
+        if first
+          unless params.is_a?(Hash)
+            raise ArgumentError, &quot;value must be a Hash&quot;
+          end
+
+          multipart = false
+          query = lambda { |value|
+            case value
+            when Array
+              value.each(&amp;query)
+            when Hash
+              value.values.each(&amp;query)
+            when UploadedFile
+              multipart = true
+            end
+          }
+          params.values.each(&amp;query)
+          return nil unless multipart
+        end
+
         flattened_params = Hash.new
 
         params.each do |key, value|</diff>
      <filename>vendor/rack-1.0.0-git/lib/rack/utils.rb</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>doc/Architectural overview.html</filename>
    </removed>
    <removed>
      <filename>doc/Security of user switching support.html</filename>
    </removed>
    <removed>
      <filename>doc/Users guide Apache.html</filename>
    </removed>
    <removed>
      <filename>doc/Users guide Nginx.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/Bucket_8h-source.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/Configuration_8h-source.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/DirectoryMapper_8h-source.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/Hooks_8h-source.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/annotated.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/classHooks-members.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/classHooks.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/classPassenger_1_1DirectoryMapper-members.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/classPassenger_1_1DirectoryMapper.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/classes.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/definitions_8h-source.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/doxygen.css</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/doxygen.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/files.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2blank.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2doc.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2folderclosed.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2folderopen.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2lastnode.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2link.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2mlastnode.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2mnode.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2node.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2plastnode.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2pnode.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/ftv2vertline.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/functions.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/functions_func.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/graph_legend.dot</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/graph_legend.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/graph_legend.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/group__Configuration.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/group__Configuration.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/group__Core.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/group__Core.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/group__Hooks.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/group__Hooks.png</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/group__Support.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/index.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/main.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/modules.html</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/tab_b.gif</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/tab_l.gif</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/tab_r.gif</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/tabs.css</filename>
    </removed>
    <removed>
      <filename>doc/cxxapi/tree.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/ConditionVariable.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/Exception.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/GC.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/IO.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AbstractInstaller.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AbstractRequestHandler.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AbstractServer.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerAlreadyStarted.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerError.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AbstractServer/ServerNotStarted.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AbstractServer/UnknownMessage.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AbstractServerCollection.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AdminTools.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AdminTools/ControlProcess.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AdminTools/ControlProcess/Instance.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/AppInitError.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Application.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/ConsoleTextTemplate.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/FrameworkInitError.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/HTMLTemplate.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/InitializationError.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/InvalidPath.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/MessageChannel.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/NativeSupport.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Rack.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Rack/ApplicationSpawner.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Rack/RequestHandler.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Railz.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Railz/ApplicationSpawner.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Railz/ApplicationSpawner/Error.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Railz/CGIFixed.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Railz/FrameworkSpawner.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Railz/FrameworkSpawner/Error.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Railz/RequestHandler.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/SpawnManager.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/UnknownError.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/Utils.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/VersionNotFound.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/WSGI.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PhusionPassenger/WSGI/ApplicationSpawner.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/PlatformInfo.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/RakeExtensions.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/classes/Signal.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/created.rid</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/DEVELOPERS_TXT.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/README.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/ext/phusion_passenger/native_support_c.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/abstract_installer_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/abstract_request_handler_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/abstract_server_collection_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/abstract_server_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/admin_tools/control_process_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/admin_tools_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/application_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/console_text_template_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/constants_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/dependencies_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/events_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/exceptions_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/html_template_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/message_channel_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/packaging_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/platform_info_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/rack/application_spawner_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/rack/request_handler_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/railz/application_spawner_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/railz/cgi_fixed_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/railz/framework_spawner_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/railz/request_handler_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/simple_benchmarking_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/spawn_manager_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/utils_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/lib/phusion_passenger/wsgi/application_spawner_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/files/misc/rake/extensions_rb.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/fr_class_index.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/fr_file_index.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/fr_method_index.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/index.html</filename>
    </removed>
    <removed>
      <filename>doc/rdoc/rdoc-style.css</filename>
    </removed>
    <removed>
      <filename>ext/apache2/ApplicationPoolServerExecutable</filename>
    </removed>
    <removed>
      <filename>ext/apache2/Bucket.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/Configuration.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/Hooks.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libboost_oxt.a</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libboost_oxt/boost/exceptions.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libboost_oxt/boost/once.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libboost_oxt/boost/thread.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libboost_oxt/oxt/backtrace.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libboost_oxt/oxt/system_calls.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libboost_oxt/oxt/thread.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libboost_oxt/oxt/tracable_exception.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libpassenger_common.a</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libpassenger_common/CachedFileStat.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libpassenger_common/Logging.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libpassenger_common/SystemTime.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/libpassenger_common/Utils.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/mod_passenger.o</filename>
    </removed>
    <removed>
      <filename>ext/apache2/mod_passenger.so</filename>
    </removed>
    <removed>
      <filename>ext/common/FileChecker.h</filename>
    </removed>
    <removed>
      <filename>ext/phusion_passenger/Makefile</filename>
    </removed>
    <removed>
      <filename>ext/phusion_passenger/native_support.o</filename>
    </removed>
    <removed>
      <filename>ext/phusion_passenger/native_support.so</filename>
    </removed>
    <removed>
      <filename>test/FileCheckerTest.cpp</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/README</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/config/application.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/config/environment.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/actionmailer/lib/action_mailer.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/actionpack/lib/action_controller.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/actionpack/lib/action_pack.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/actionpack/lib/action_view.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/activerecord/lib/active_record.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/activeresource/lib/active_resource.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/activesupport/lib/active_support.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/activesupport/lib/active_support/whiny_nil.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/railties/lib/dispatcher.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/railties/lib/initializer.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/minimal-railsapp/vendor/rails/railties/lib/ruby_version_check.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/app/controllers/application.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/app/controllers/bar_controller_1.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/app/controllers/bar_controller_2.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/app/controllers/foo_controller.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/app/helpers/application_helper.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/boot.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/database.yml</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/environment.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/environments/development.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/environments/production.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/initializers/inflections.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/initializers/mime_types.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/config/routes.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/log/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/public/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/tmp/cache/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/tmp/pids/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/tmp/sessions/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp/tmp/sockets/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/app/controllers/application.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/app/controllers/foo_controller.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/app/helpers/application_helper.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/boot.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/database.yml</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/environment.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/environments/development.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/environments/production.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/initializers/inflections.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/initializers/mime_types.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/config/routes.rb</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/log/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/public/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/tmp/cache/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/tmp/pids/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/tmp/sessions/useless.txt</filename>
    </removed>
    <removed>
      <filename>test/stub/railsapp2/tmp/sockets/useless.txt</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>1135954b4f6ab744fcbc0b734d062644217ee265</id>
    </parent>
  </parents>
  <author>
    <name>John Leach</name>
    <email>john@johnleach.co.uk</email>
  </author>
  <url>http://github.com/johnl/deb-passenger/commit/80fd64fb1cd0e0a043170df3047b7ae731cfcd6f</url>
  <id>80fd64fb1cd0e0a043170df3047b7ae731cfcd6f</id>
  <committed-date>2009-06-29T03:05:27-07:00</committed-date>
  <authored-date>2009-06-29T03:05:27-07:00</authored-date>
  <message>Imported Upstream version 2.2.4</message>
  <tree>fc24d458ba99812b9df798e1fc2819f52e505911</tree>
  <committer>
    <name>John Leach</name>
    <email>john@johnleach.co.uk</email>
  </committer>
</commit>
