ext/common/ApplicationPoolServerExecutable.cpp in passenger-2.2.2 vs ext/common/ApplicationPoolServerExecutable.cpp in passenger-2.2.3
- old
+ new
@@ -286,17 +286,143 @@
map<int, Application::SessionPtr> sessions;
/** 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 <tt>errorCode</tt> is <tt>EBADF</tt>, and <tt>briefMessage</tt>
+ * is <em>"Something happened"</em>, then what() will return <em>"Something happened: Bad
+ * file descriptor (10)"</em> (if 10 is the number for EBADF).
+ * @post code() == errorCode
+ * @post brief() == briefMessage
+ */
+ ClientCommunicationError(const string &briefMessage, int errorCode = -1) {
+ if (errorCode != -1) {
+ stringstream str;
+
+ str << strerror(errorCode) << " (" << errorCode << ")";
+ 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 &message) {
+ briefMessage = message;
+ if (systemMessage.empty()) {
+ fullMessage = briefMessage;
+ } else {
+ fullMessage = briefMessage + ": " + systemMessage;
+ }
+ }
+
+ /**
+ * The value of <tt>errno</tt> 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 <tt>message</tt> 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 <tt>strerror(errno)</tt> 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 &channel;
+ PoolOptions &options;
+ public:
+ EnvironmentVariablesFetcher(MessageChannel &theChannel, PoolOptions &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("getEnvironmentVariables", NULL);
+ } catch (const SystemException &e) {
+ throw ClientCommunicationError(
+ "Unable to send a 'getEnvironmentVariables' request to the client",
+ e.code());
+ }
+ try {
+ if (!channel.readScalar(data)) {
+ throw ClientCommunicationError("Unable to read a reply from the client for the 'getEnvironmentVariables' request.");
+ }
+ } catch (const SystemException &e) {
+ throw ClientCommunicationError(
+ "Unable to read a reply from the client for the 'getEnvironmentVariables' request",
+ e.code());
+ }
+
+ if (!data.empty()) {
+ SimpleStringListCreator list(data);
+ return list.getItems();
+ } else {
+ return ptr(new StringList());
+ }
+ }
+ };
+
void processGet(const vector<string> &args) {
TRACE_POINT();
Application::SessionPtr session;
bool failed = false;
try {
PoolOptions options(args, 1);
+ options.environmentVariables = ptr(new EnvironmentVariablesFetcher(channel, options));
session = server.pool->get(options);
sessions[lastSessionID] = session;
lastSessionID++;
} catch (const SpawnException &e) {
UPDATE_TRACE_POINT();
@@ -329,15 +455,17 @@
this_thread::disable_syscall_interruption dsi;
try {
UPDATE_TRACE_POINT();
channel.write("ok", toString(session->getPid()).c_str(),
toString(lastSessionID - 1).c_str(), NULL);
+ UPDATE_TRACE_POINT();
channel.writeFileDescriptor(session->getStream());
- session->closeStream();
- } catch (const exception &) {
UPDATE_TRACE_POINT();
- P_TRACE(3, "Client " << this << ": something went wrong "
- "while sending 'ok' back to the client.");
+ session->closeStream();
+ } catch (const exception &e) {
+ P_TRACE(3, "Client " << this << ": could not send "
+ "'ok' back to the ApplicationPool client: " <<
+ e.what());
sessions.erase(lastSessionID - 1);
throw;
}
}
}