00001
00002
00003 #include "Iguana/WebFramework/interface/IgServerPool.h"
00004 #include "Iguana/WebFramework/interface/IgServerSocket.h"
00005 #include "Iguana/Framework/interface/IgArgsElement.h"
00006 #include "Iguana/Framework/interface/IgState.h"
00007 #include <classlib/iobase/SubProcess.h>
00008 #include <classlib/iobase/TempFile.h>
00009 #include <classlib/utils/DebugAids.h>
00010 #include <classlib/utils/StringOps.h>
00011 #include <classlib/utils/StringList.h>
00012 #include <classlib/iobase/Filename.h>
00013 #include <classlib/utils/Argz.h>
00014 #include <stdlib.h>
00015 #include <iostream>
00016 #include <signal.h>
00017 #include <sys/wait.h>
00018
00019
00020
00021
00022
00023
00024
00025 IgServerPool* IgServerPool::m_instance = 0;
00026
00027
00028
00029 IG_DEFINE_STATE_ELEMENT (IgServerPool, "Server pool");
00030
00031
00032
00033 void childServerFinished (int signum)
00034 {
00035 if (signum != SIGCHLD) return;
00036 int status;
00037 IgServerPool::getInstance(0)->childTerminated(wait (&status));
00038 }
00039
00040
00041 IgServerPool* IgServerPool::getInstance (IgState *state)
00042 {
00043 if (!m_instance)
00044 m_instance = new IgServerPool(state);
00045 return m_instance;
00046 }
00047
00048 IgServerPool::IgServerPool (IgState *state)
00049 : m_state (state),
00050 m_argz (new lat::Argz ("iguana")),
00051 m_maxProcesses (65000),
00052 m_processPoolSize(2),
00053 m_hostname ("")
00054 {
00055 m_state->put (s_key, this);
00056 signal (SIGCHLD, childServerFinished);
00057
00058 IgArgsElement *args = IgArgsElement::get (state);
00059 std::string processPoolSize = QString::number(m_processPoolSize);
00060 if (args->find ("--ig-process-pool-size", processPoolSize))
00061 m_processPoolSize = atoi (processPoolSize.c_str ());
00062
00063 std::string maxProcesses = QString::number(m_maxProcesses);
00064 if (args->find ("--ig-max-processes", maxProcesses))
00065 m_maxProcesses = atoi (maxProcesses.c_str ());
00066
00067 ASSERT (m_processPoolSize <= m_maxProcesses);
00068
00069 m_hostname = IgServerSocket::cmdLineHostname(m_state);
00070 ASSERT (!m_hostname.empty());
00071
00072 m_argz->add ("-D");
00073 m_argz->add ("SOCKET");
00074
00075 if (IgServerSocket::cmdLineProxy(state).empty())
00076 {
00077 m_argz->add ("--ig-main-hosturl");
00078 m_argz->add (m_hostname + ":" +
00079 QString::number(IgServerSocket::cmdLinePort(state)));
00080 }
00081
00082
00083
00084 std::map <std::string, bool> ignoreArgs;
00085 ignoreArgs ["--ig-port"]=true;
00086 ignoreArgs ["--ig-server"]=false;
00087 ignoreArgs ["--ig-main-hosturl"]=true;
00088 ignoreArgs ["--ig-process-pool-size"]=true;
00089 ignoreArgs ["--ig-max-processes"]=true;
00090 ignoreArgs ["-D"]=true;
00091 ignoreArgs ["--driver"]=true;
00092
00093 for (int i = 1 ; i < args->args (); i++)
00094 {
00095 std::string arg (args->arg (i));
00096 if (arg.empty ()) continue;
00097 if (ignoreArgs.find (arg) != ignoreArgs.end())
00098 {
00099 if (ignoreArgs[arg])
00100 i++;
00101 }
00102 else
00103 m_argz->add (args->arg (i));
00104 }
00105
00106 for (int i = 0; i < m_processPoolSize; i++)
00107 addServer ();
00108 }
00109
00110 IgServerPool::~IgServerPool (void)
00111 {
00112 m_state->detach (s_key);
00113 }
00114
00115
00116 std::string
00117 IgServerPool::createProcess (void)
00118 {
00119 lat::Argz argz (*m_argz);
00120 int childPort = IgServerSocket::getFreePort ();
00121 std::string portValue = QString::number (childPort).latin1 ();
00122 argz.add ("--ig-port");
00123 argz.add (portValue);
00124 lat::SubProcess *process = new lat::SubProcess ();
00125
00126 std::cerr << argz.quote () << std::endl;
00127
00128 lat::Argz shellCommand ("/bin/sh");
00129 shellCommand.add ("-c");
00130 lat::Filename directoryName (lat::TempFile::tempdir().asDirectory());
00131 directoryName.append ("IGUANAWEB/");
00132 directoryName.makedir(directoryName, 0700, true, true);
00133 lat::TempFile::dir (directoryName);
00134
00135 shellCommand.add (std::string ("\"cd ") + directoryName.name () + "; "+
00136 argz.quote () + "\"");
00137 std::cerr << shellCommand.quote () << std::endl;
00138
00139 pid_t pid = process->run (shellCommand.argz ());
00140
00141 std::string result = m_hostname + ":" + portValue;
00142 m_pidServerMap.insert (std::pair<pid_t, std::string>(pid,result));
00143
00144 return result;
00145 }
00146
00147 std::string
00148 IgServerPool::redirect (const std::string& url, std::string& cookie)
00149 {
00150 std::string result;
00151 std::string redir;
00152
00153 if (cookie.empty())
00154 IgServerSocket::generateCookie (cookie);
00155
00156 if (m_cookieServerMap.find (cookie) != m_cookieServerMap.end ())
00157 {
00158 redir = m_cookieServerMap[cookie];
00159 }
00160 else
00161 {
00162 if (m_availableServers.size ())
00163 {
00164
00165 redir = *(m_availableServers.begin ());
00166 m_availableServers.erase (m_availableServers.begin ());
00167 m_cookieServerMap.insert (std::pair<std::string, std::string>
00168 (cookie,redir));
00169 }
00170 else
00171 {
00172
00173 result = "HTTP/1.1 200 Ok\r\n"
00174 "Content-Type: text/html; charset=\"utf-8\"\r\n\r\n"
00175 "<html><body>"
00176 "<style type=\"text/css\">"
00177 "a {color:#FFFFFF}"
00178 "</style>"
00179 "<h3>Server is busy.</h3>"
00180 "Max number of clients are already connected. Please try again later."
00181 "</body>"
00182 "</html>\r\n\r\n";
00183
00184 std::cerr << result << std::endl;
00185
00186 return result;
00187 }
00188 }
00189
00190 return IgServerSocket::redirect (url, redir, cookie);
00191 }
00192
00193 void
00194 IgServerPool::addServer (void)
00195 {
00196 if (((int)m_pidServerMap.size() < m_maxProcesses) &&
00197 ((int)m_availableServers.size () < m_processPoolSize))
00198 {
00199 m_availableServers.insert (createProcess ());
00200 }
00201 }
00202
00203
00204 void
00205 IgServerPool::childTerminated (int pid)
00206 {
00207 PIDToServerMap::iterator pidItr = m_pidServerMap.find(pid);
00208 if (pidItr != m_pidServerMap.end())
00209 {
00210 std::string server = m_pidServerMap[pid];
00211 std::cerr <<"Server terminated:"<<server<<std::endl;
00212 m_pidServerMap.erase(pidItr);
00213 if (m_availableServers.find(server)!=m_availableServers.end())
00214 m_availableServers.erase (server);
00215 CookieToServerMap::iterator itr = m_cookieServerMap.begin();
00216 for(itr = m_cookieServerMap.begin();
00217 itr != m_cookieServerMap.end();
00218 itr++)
00219 {
00220 if (itr->second == server)
00221 m_cookieServerMap.erase (itr);
00222 }
00223 addServer ();
00224 }
00225 }
00226
00227 int
00228 IgServerPool::availableServers (void)const
00229 {
00230 return m_availableServers.size ();
00231 }
00232
00233 int
00234 IgServerPool::processPoolSize (void) const
00235 { return m_processPoolSize; }