<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>ext/common/StringListCreator.h</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -189,6 +189,7 @@ def define_common_library_task(output_dir, extra_compiler_flags = nil,
 			'ext/common/MessageChannel.h',
 			'ext/common/SpawnManager.h',
 			'ext/common/PoolOptions.h',
+			'ext/common/StringListCreator.h',
 			'ext/common/FileChecker.h',
 			'ext/common/SystemTime.h',
 			'ext/common/CachedFileStat.h',
@@ -243,6 +244,7 @@ end
 			ext/common/Application.h
 			ext/common/MessageChannel.h
 			ext/common/PoolOptions.h
+			ext/common/StringListCreator.h
 			ext/common/Version.h
 			ext/common/Utils.h)
 	}
@@ -390,12 +392,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
@@ -404,6 +408,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(
@@ -413,11 +418,13 @@ end
 			ext/common/StandardApplicationPool.h
 			ext/common/SpawnManager.h
 			ext/common/PoolOptions.h
+			ext/common/StringListCreator.h
 			ext/common/FileChecker.h
 			ext/common/Application.h),
 		'test/PoolOptionsTest.o' =&gt; %w(
 			test/PoolOptionsTest.cpp
-			ext/common/PoolOptions.h),
+			ext/common/PoolOptions.h
+			ext/common/StringListCreator.h),
 		'test/StaticStringTest.o' =&gt; %w(
 			test/StaticStringTest.cpp
 			ext/common/StaticString.h),</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -138,6 +138,33 @@ 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());
+			
+			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;
@@ -455,10 +482,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(),
@@ -471,7 +496,10 @@ private:
 					config-&gt;usingGlobalQueue(),
 					config-&gt;getStatThrottleRate(),
 					config-&gt;getRestartDir()
-				));
+				);
+				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;</diff>
      <filename>ext/apache2/Hooks.cpp</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>@@ -380,12 +380,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 +401,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++;</diff>
      <filename>ext/common/ApplicationPoolServerExecutable.cpp</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,19 @@ 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;
+	
+	/**
 	 * Creates a new PoolOptions object with the default values filled in.
 	 * One must still set appRoot manually, after having used this constructor.
 	 */
@@ -230,16 +244,23 @@ struct PoolOptions {
 		useGlobalQueue = vec[startIndex + 21] == &quot;true&quot;;
 		statThrottleRate = atol(vec[startIndex + 23]);
 		restartDir     = vec[startIndex + 25];
+		if (vec.size() &gt; startIndex + 27) {
+			environmentVariables = ptr(new SimpleStringListCreator(vec[startIndex + 27]));
+		}
 	}
 	
 	/**
 	 * 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() + 28) {
+			vec.reserve(vec.size() + 28);
 		}
 		appendKeyValue (vec, &quot;app_root&quot;,        appRoot);
 		appendKeyValue (vec, &quot;lower_privilege&quot;, lowerPrivilege ? &quot;true&quot; : &quot;false&quot;);
@@ -254,6 +275,37 @@ 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);
+		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>@@ -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.
@@ -83,13 +85,15 @@ 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;]
 			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 +120,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>@@ -84,6 +84,11 @@ 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.
+	#
 	# All other options will be passed on to RequestHandler.
 	def initialize(app_root, options = {})
 		super()
@@ -93,6 +98,7 @@ 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;]
 		self.max_idle_time = DEFAULT_APP_SPAWNER_MAX_IDLE_TIME
 		assert_valid_app_root(@app_root)
 		define_message_handler(:spawn_application, :handle_spawn_application)
@@ -145,6 +151,9 @@ class ApplicationSpawner &lt; AbstractServer
 				success = report_app_init_status(channel) do
 					ENV['RAILS_ENV'] = @environment
 					Dir.chdir(@app_root)
+					if @encoded_environment_variables
+						set_passed_environment_variables
+					end
 					if @lower_privilege
 						lower_privilege('config/environment.rb', @lowest_user)
 					end
@@ -221,6 +230,9 @@ protected
 				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 +241,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>@@ -97,7 +97,7 @@ class SpawnManager &lt; AbstractServer
 	#
 	# Other options are:
 	#
-	# ['lower_privilege', 'lowest_user' and 'environment']
+	# ['lower_privilege', 'lowest_user', 'environment' and 'environment_variables']
 	#   See Railz::ApplicationSpawner.new for an explanation of these options.
 	# 
 	# ['app_type']</diff>
      <filename>lib/phusion_passenger/spawn_manager.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>@@ -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>@@ -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>@@ -34,6 +34,23 @@ 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{
 			PhusionPassenger.on_event(:starting_worker_process) do
@@ -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,11 +86,12 @@ 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</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>@@ -169,13 +169,15 @@ 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</diff>
      <filename>test/ruby/spawn_manager_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>@@ -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>@@ -62,6 +62,36 @@ public:
 };
 
 /**
+ * Creates a temporary copy of the given directory. This copy is deleted
+ * upon object destruction.
+ */
+class TempDirCopy {
+private:
+	string dir;
+	
+	void rm_rf(const string &amp;dir) {
+		string command(&quot;rm -rf \&quot;&quot;);
+		command.append(dir);
+		command.append(&quot;\&quot;&quot;);
+		system(command.c_str());
+	}
+public:
+	TempDirCopy(const string &amp;source, const string &amp;dest) {
+		dir = dest;
+		rm_rf(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() {
+		rm_rf(dir);
+	}
+};
+
+/**
  * Class which deletes the given file upon destruction.
  */
 class DeleteFileEventually {</diff>
      <filename>test/support/Support.h</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>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>0d1371fb6e4f0cf8f521508bfeec4d39aa77d8c1</id>
    </parent>
  </parents>
  <author>
    <name>Hongli Lai (Phusion)</name>
    <email>hongli@phusion.nl</email>
  </author>
  <url>http://github.com/FooBarWidget/passenger/commit/f0859191f8f31f047bbcd9564839c1e8b1462fbd</url>
  <id>f0859191f8f31f047bbcd9564839c1e8b1462fbd</id>
  <committed-date>2009-05-12T07:45:15-07:00</committed-date>
  <authored-date>2009-04-25T11:16:48-07:00</authored-date>
  <message>Make it possible to pass mod_env environment variables to the backend process.</message>
  <tree>65eb04018b3e375a791cf9fa930da526d2481f85</tree>
  <committer>
    <name>Hongli Lai (Phusion)</name>
    <email>hongli@phusion.nl</email>
  </committer>
</commit>
