0
+ * Phusion Passenger - http://www.modrails.com/
0
+ * Copyright (C) 2008 Phusion
0
+ * This program is free software; you can redistribute it and/or modify
0
+ * it under the terms of the GNU General Public License as published by
0
+ * the Free Software Foundation; version 2 of the License.
0
+ * This program is distributed in the hope that it will be useful,
0
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
0
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
0
+ * GNU General Public License for more details.
0
+ * You should have received a copy of the GNU General Public License along
0
+ * with this program; if not, write to the Free Software Foundation, Inc.,
0
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
0
+#ifndef _PASSENGER_APPLICATION_POOL_SERVER_EXECUTABLE_H_
0
+#define _PASSENGER_APPLICATION_POOL_SERVER_EXECUTABLE_H_
0
+#include <boost/bind.hpp>
0
+#include <boost/thread/thread.hpp>
0
+#include "MessageChannel.h"
0
+#include "StandardApplicationPool.h"
0
+#include "Application.h"
0
+#include "Exceptions.h"
0
+using namespace Passenger;
0
+typedef shared_ptr<Client> ClientPtr;
0
+#define SERVER_SOCKET_FD 3
0
+/*****************************************
0
+ *****************************************/
0
+ StandardApplicationPool pool;
0
+ set<ClientPtr> clients;
0
+ Server(int serverSocket,
0
+ const string &spawnServerCommand,
0
+ const string &logFile,
0
+ const string &environment,
0
+ const string &rubyCommand,
0
+ : pool(spawnServerCommand, logFile, environment, rubyCommand, user) {
0
+ this->serverSocket = serverSocket;
0
+ ret = close(serverSocket);
0
+ } while (ret == -1 && errno == EINTR);
0
+ // Wait for all clients to disconnect.
0
+ mutex::scoped_lock l(lock);
0
+ int start(); // Will be defined later, because Client depends on Server's interface.
0
+/*****************************************
0
+ *****************************************/
0
+ * Represents a single ApplicationPool client, connected to this server.
0
+ * The life time of a Client object is guaranteed to be less than
0
+ * that of its associated Server object.
0
+ /** The Server that this Client object belongs to. */
0
+ /** The connection to the client. */
0
+ MessageChannel channel;
0
+ /** The thread which handles the client connection. */
0
+ * Maps session ID to sessions created by ApplicationPool::get(). Session IDs
0
+ * are sent back to the ApplicationPool client. This allows the ApplicationPool
0
+ * client to tell us which of the multiple sessions it wants to close, later on.
0
+ map<int, Application::SessionPtr> sessions;
0
+ /** Last used session ID. */
0
+ void processGet(const vector<string> &args) {
0
+ Application::SessionPtr session;
0
+ session = server.pool.get(args[1], args[2] == "true", args[3]);
0
+ sessions[lastSessionID] = session;
0
+ } catch (const SpawnException &e) {
0
+ if (e.hasErrorPage()) {
0
+ P_TRACE(3, "Client " << this << ": SpawnException "
0
+ "occured (with error page)");
0
+ channel.write("SpawnException", e.what(), "true", NULL);
0
+ channel.writeScalar(e.getErrorPage());
0
+ P_TRACE(3, "Client " << this << ": SpawnException "
0
+ "occured (no error page)");
0
+ channel.write("SpawnException", e.what(), "false", NULL);
0
+ } catch (const IOException &e) {
0
+ channel.write("IOException", e.what(), NULL);
0
+ channel.write("ok", toString(session->getPid()).c_str(),
0
+ toString(lastSessionID - 1).c_str(), NULL);
0
+ channel.writeFileDescriptor(session->getReader());
0
+ channel.writeFileDescriptor(session->getWriter());
0
+ session->closeReader();
0
+ session->closeWriter();
0
+ } catch (const exception &) {
0
+ P_TRACE(3, "Client " << this << ": something went wrong "
0
+ "while sending 'ok' back to the client.");
0
+ sessions.erase(lastSessionID - 1);
0
+ void processClose(const vector<string> &args) {
0
+ sessions.erase(atoi(args[1]));
0
+ void processClear(const vector<string> &args) {
0
+ void processSetMaxIdleTime(const vector<string> &args) {
0
+ server.pool.setMaxIdleTime(atoi(args[1]));
0
+ void processSetMax(const vector<string> &args) {
0
+ server.pool.setMax(atoi(args[1]));
0
+ void processGetActive(const vector<string> &args) {
0
+ channel.write(toString(server.pool.getActive()).c_str(), NULL);
0
+ void processGetCount(const vector<string> &args) {
0
+ channel.write(toString(server.pool.getCount()).c_str(), NULL);
0
+ void processGetSpawnServerPid(const vector<string> &args) {
0
+ channel.write(toString(server.pool.getSpawnServerPid()).c_str(), NULL);
0
+ void processUnknownMessage(const vector<string> &args) {
0
+ P_WARN("An ApplicationPool client sent an invalid command: "
0
+ << name << " (" << args.size() << " elements)");
0
+ * The entry point of the thread that handles the client connection.
0
+ void threadMain(const set<ClientPtr>::iterator &it) {
0
+ if (!channel.read(args)) {
0
+ // Client closed connection.
0
+ P_TRACE(3, "Client " << this << ": received message: " <<
0
+ if (args[0] == "get" && args.size() == 4) {
0
+ } else if (args[0] == "close" && args.size() == 2) {
0
+ } else if (args[0] == "clear" && args.size() == 1) {
0
+ } else if (args[0] == "setMaxIdleTime" && args.size() == 2) {
0
+ processSetMaxIdleTime(args);
0
+ } else if (args[0] == "setMax" && args.size() == 2) {
0
+ } else if (args[0] == "getActive" && args.size() == 1) {
0
+ processGetActive(args);
0
+ } else if (args[0] == "getCount" && args.size() == 1) {
0
+ processGetCount(args);
0
+ } else if (args[0] == "getSpawnServerPid" && args.size() == 1) {
0
+ processGetSpawnServerPid(args);
0
+ processUnknownMessage(args);
0
+ } catch (const exception &e) {
0
+ P_WARN("Uncaught exception in ApplicationPoolServer client thread: " <<
0
+ mutex::scoped_lock l(server.lock);
0
+ server.clients.erase(it);
0
+ * Create a new Client object.
0
+ * @param connection The connection to the ApplicationPool client.
0
+ * <tt>connection</tt> will be closed upon destruction
0
+ Client(Server &the_server, int connection)
0
+ * Start the thread for handling the connection with this client.
0
+ * @param The iterator of this Client object inside the server's
0
+ * <tt>clients</tt> set. This is used to remove itself from
0
+ * the <tt>clients</tt> set once the client has closed the
0
+ void start(const set<ClientPtr>::iterator &it) {
0
+ thr = new thread(bind(&Client::threadMain, this, it));
0
+ // The received data only serves to wake up the server socket,
0
+ // and is not important.
0
+ ret = read(serverSocket, &x, 1);
0
+ } while (ret == -1 && errno == EINTR);
0
+ // All web server processes disconnected from this server.
0
+ // So we can safely quit.
0
+ // We have an incoming connect request from an
0
+ // ApplicationPool client.
0
+ ret = socketpair(AF_UNIX, SOCK_STREAM, 0, fds);
0
+ } while (ret == -1 && errno == EINTR);
0
+ P_ERROR("Cannot create an anonymous Unix socket: " <<
0
+ strerror(e) << " (" << e << ") --- aborting!");
0
+ // Shut up compiler warning.
0
+ MessageChannel(serverSocket).writeFileDescriptor(fds[1]);
0
+ } while (ret == -1 && errno == EINTR);
0
+ } catch (SystemException &e) {
0
+ P_ERROR("Cannot send a file descriptor: " << e.sys() <<
0
+ } catch (const exception &e) {
0
+ P_ERROR("Cannot send a file descriptor: " << e.what() <<
0
+ ClientPtr client(new Client(*this, fds[0]));
0
+ pair<set<ClientPtr>::iterator, bool> p;
0
+ mutex::scoped_lock l(lock);
0
+ p = clients.insert(client);
0
+ client->start(p.first);
0
+main(int argc, char *argv[]) {
0
+ Server server(SERVER_SOCKET_FD, argv[1], argv[2], argv[2], argv[3], argv[5]);
0
+ return server.start();
0
+#endif /* _PASSENGER_APPLICATION_POOL_SERVER_EXECUTABLE_H_ */
Comments
No one has commented yet.