[CPPSERV] [Documentation] [Download] [Contact] [Bug tracker] [News] [RSS Feed] [gitweb]
00001 /*************************************************************************** 00002 * Copyright (C) 2004-2006 by Ilya A. Volynets-Evenbakh * 00003 * ilya@total-knowledge.com * 00004 * * 00005 * This program is free software; you can redistribute it and/or modify * 00006 * it under the terms of the GNU General Public License as published by * 00007 * the Free Software Foundation; either version 2 of the License, or * 00008 * (at your option) any later version. * 00009 * * 00010 * This program is distributed in the hope that it will be useful, * 00011 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00013 * GNU General Public License for more details. * 00014 * * 00015 * You should have received a copy of the GNU General Public License * 00016 * along with this program; if not, write to the * 00017 * Free Software Foundation, Inc., * 00018 * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * 00019 ***************************************************************************/ 00020 00021 #include <App.h> 00022 #include <requesthandler.h> 00023 #include <dlfcn.h> 00024 #include <fcntl.h> 00025 #include <sptk3/CBase64.h> 00026 #include <servlet/IllegalArgumentException.h> 00027 #include <serverconfig.h> 00028 00029 namespace container 00030 { 00038 ServletConfigImpl* App::addServlet(const std::string& path, const std::string& name, const std::string& dso, bool hidden) 00039 { 00040 void* dsoHandle=dlopen(dso.c_str(),RTLD_LAZY); 00041 if(!dsoHandle) { 00042 std::cerr<<dlerror()<<std::endl; 00043 return 0; 00044 } 00045 servletcreatefunc_t createFunc=(servletcreatefunc_t)dlsym(dsoHandle,(name + "_createServlet").c_str()); 00046 if(!createFunc) { 00047 std::cerr<<"Could not locate servlet "<<name<<" in the library "<<dso<<": "<<dlerror()<<std::endl; 00048 return 0; 00049 } 00050 ServletConfigImpl* config=new ServletConfigImpl(this, name); 00051 ServletDesc* desc=new ServletDesc(createFunc(), config, dsoHandle, path); 00052 addServlet(path, desc); 00053 if(!hidden) 00054 RequestHandler::addServlet(path,getServletContainer(path)); 00055 return config; 00056 } 00057 00058 bool App::addServlet(const std::string& path,ServletDesc* d) 00059 { 00060 maptop[path] = d; 00061 return true; 00062 } 00063 00064 App::ServletDesc* App::getDesc(const std::string& path) 00065 { 00066 return maptop.getServletDesc(path); 00067 } 00068 00069 void App::delServlet(const std::string& path) 00070 { 00071 maptop.removeDesc(path); 00072 } 00073 00074 ServletContainer* App::getServletContainer(const std::string& path) 00075 { 00076 ServletDesc* desc = getDesc(path); 00077 if(!desc) 00078 return 0; 00079 return &(desc->cont); 00080 } 00081 00082 void* App::getDso(const std::string& path) 00083 { 00084 ServletDesc* desc = getDesc(path); 00085 if(!desc) 00086 return 0; 00087 return desc->h; 00088 } 00089 00090 void App::splitServPath(const std::string& path, std::string& dir, std::string& name) 00091 { 00092 std::string::size_type slash = path.rfind('/'); 00093 if(slash == std::string::npos) { 00094 dir = "/"; 00095 name = path; 00096 } else { 00097 dir.assign(path.substr(0, slash)); 00098 name.assign(path.substr(slash + 1)); 00099 } 00100 } 00101 00102 void App::initServlet(const std::string& path) 00103 { 00104 ServletContainer* cont=getServletContainer(path); 00105 cont->init(); 00106 } 00107 00108 void App::destroyServlet(const std::string& path) 00109 { 00110 ServletContainer* cont=getServletContainer(path); 00111 cont->destroy(); 00112 } 00113 00120 void App::unloadServlet(const std::string& path) 00121 { 00122 ServletContainer* cont=getServletContainer(path); 00123 servlet::ServletConfig* conf=cont->getConfig(); 00124 ServletDesc* d=getDesc(path); 00125 void* dsoHandle=d->h; 00126 delServlet(path); 00127 delete d; // this will delete ServletContainer, which will in turn delete servlet object 00128 delete conf; 00129 dlclose(dsoHandle); 00130 } 00131 00132 class RHServletRemover 00133 { 00134 public: 00135 void operator()(const std::string& path) 00136 { 00137 RequestHandler::removeServlet(path); 00138 } 00139 }; 00140 00141 template<class T> 00142 class SMFunctor 00143 { 00144 private: 00145 T* inst; 00146 typedef void (T::*F)(const std::string&); 00147 F f; 00148 public: 00149 SMFunctor(T* inst, F f): inst(inst), f(f){} 00150 void operator()(const std::string& path) 00151 { 00152 (inst->*f)(path); 00153 } 00154 }; 00155 00156 App::~App() 00157 { 00158 cleaner->stop(); 00159 delete cleaner; 00160 killAllSessions(); 00161 std::string empty; 00162 destroyServlets(); // Calls destroy() on all servlets, before unloading them 00163 RHServletRemover rh; 00164 SMFunctor<App> unl(this, &App::unloadServlet); 00165 // This will block till all requests being processed by this servlet 00166 // are done. Alternatively we may want to kill all processes that are 00167 // in this servlet. Maybe there should be some timeout 00168 maptop.forEachServletPath(rh, empty); 00169 maptop.forEachServletPath(unl, empty); 00170 } 00171 00172 void App::initServlets() 00173 { 00174 std::string empty; 00175 SMFunctor<App> init(this, &App::initServlet); 00176 maptop.forEachServletPath(init, empty); 00177 } 00178 00179 void App::destroyServlets() 00180 { 00181 std::string empty; 00182 SMFunctor<App> destroy(this, &App::destroyServlet); 00183 maptop.forEachServletPath(destroy, empty); 00184 } 00185 00186 00187 App::App(ServerConfig& cfg, time_t sessionTimeout, const std::string& name, const std::string& fileSaveDir, const std::string& uribase) 00188 : servlet::ServletContext() 00189 , cfg(cfg) 00190 , sessionCookieName(makeSName(name)) 00191 , sessionTimeout(sessionTimeout) 00192 , fileSaveTemplate(fileSaveDir+'/'+"cppserv.tmpXXXXXX") 00193 , name(name) 00194 , m_uribase(uribase) 00195 { 00196 cleaner=new SessionCleaner(this,sessionTimeout/2+1); // +1, so that it doesn't become 0... 00197 cleaner->run(); 00198 } 00199 00208 container::HttpSessionImpl* App::getSession(const std::string& sid, bool create) 00209 { 00210 sessionLock.lock(); 00211 if(sid.empty()) { 00212 container::HttpSessionImpl *s = 0; 00213 if(create) s = newSession(); 00214 sessionLock.unlock(); 00215 return s; 00216 } 00217 sessionlist_t::iterator s=sessions.find(sid); 00218 //FIXME: should we perhaps return an invalid session in this case? 00219 if(s==sessions.end()||!s->second->validP()){ 00220 HttpSessionImpl* s = 0; 00221 if(create) s = newSession(); 00222 //s->invalidate(); 00223 sessionLock.unlock(); 00224 return s; 00225 } 00226 s->second->notNew(); 00227 sessionLock.unlock(); 00228 return s->second; 00229 } 00230 00231 00236 container::HttpSessionImpl* App::newSession() 00237 { 00238 std::string sid=container::util::getRandomString(10); 00239 HttpSessionImpl* s=new HttpSessionImpl(*this,sid,sessionTimeout); 00240 sessions[sid]=s; 00241 return s; 00242 } 00243 00244 00250 void App::killSession(const std::string& sid) 00251 { 00252 sessionlist_t::iterator s=sessions.find(sid); 00253 if(s==sessions.end()){ 00254 return; 00255 } 00256 delete s->second; 00257 sessions.erase(sid); 00258 } 00259 00260 00264 void App::cleanSessions() 00265 { 00266 sessionLock.lock(); 00267 sessionlist_t::iterator cur=sessions.begin(); 00268 while(cur!=sessions.end()){ 00269 if(!cur->second->validP()){ 00270 killSession(cur->first); 00271 // killSession modifies the map, so we have 00272 // to restart our search from scratch. 00273 // Another option would be to leave this alone, 00274 // In hopes that we'll get to every expired session 00275 // sooner or later - Harder on RAM, easier on CPU 00276 cur=sessions.begin(); 00277 } else { 00278 cur++; 00279 } 00280 } 00281 sessionLock.unlock(); 00282 } 00287 App::SessionCleaner::SessionCleaner(App* ctx,int freq) 00288 : sptk::CThread("",true) 00289 , running(true) 00290 , freq(freq) 00291 , ctx(ctx) 00292 { 00293 if(pipe(trigger)==-1) { 00294 std::cerr<<"Error setting up session cleaner trigger: "<<strerror(errno)<<std::endl; 00295 ::exit(1); 00296 } 00297 if(fcntl(trigger[0],F_SETFL,O_NONBLOCK)<0) { 00298 std::cerr<<"Error setting trigger reader to non-blocking mode: " 00299 <<strerror(errno)<<std::endl; 00300 exit(1); 00301 } 00302 } 00306 App::SessionCleaner::~SessionCleaner() 00307 { 00308 close(trigger[0]); 00309 close(trigger[1]); 00310 } 00311 void App::SessionCleaner::threadFunction() 00312 { 00313 while(running) { 00314 ctx->cleanSessions(); 00315 struct timeval timeout={freq,0}; 00316 fd_set rfd; 00317 FD_ZERO(&rfd); 00318 FD_SET(trigger[0],&rfd); 00319 int r=select(trigger[0]+1,&rfd,0,0,&timeout); 00320 if(r){ 00321 char buf[4]; 00322 while(::read(trigger[0],buf,sizeof(buf))>0); 00323 } 00324 } 00325 } 00332 void App::SessionCleaner::stop() 00333 { 00334 running=false; 00335 this->wakeUp(); 00336 this->terminate(); 00337 } 00338 00344 bool App::isSessionValid(const std::string& sid) 00345 { 00346 sessionlist_t::iterator s=sessions.find(sid); 00347 return s!=sessions.end()&&s->second->validP(); 00348 } 00349 00350 00354 void App::addInitParam(const std::string& name, const std::string& value) 00355 { 00356 m_params[name]=value; 00357 } 00358 00363 std::string App::getServletContextName() const 00364 { 00365 return name; 00366 } 00367 00368 void App::log(const std::string& msg) const 00369 { 00370 std::cerr<<msg<<std::endl; 00371 } 00372 00373 void App::log(const std::string& message, const std::exception& e) const 00374 { 00375 log(message); 00376 std::cerr<<e.what()<<std::endl; 00377 } 00378 00379 00384 void App::SessionCleaner::wakeUp() 00385 { 00386 char c='1'; 00387 ::write(trigger[1],&c,1); 00388 } 00389 00390 00397 void App::killAllSessions() 00398 { 00399 sessionLock.lock(); 00400 sessionlist_t::iterator cur=sessions.begin(); 00401 while(cur!=sessions.end()){ 00402 delete cur->second; 00403 cur++; 00404 } 00405 sessions.clear(); 00406 sessionLock.unlock(); 00407 } 00408 00409 std::string App::makeSName(const std::string& name) 00410 { 00411 sptk::CBuffer in(name.c_str()); 00412 sptk::CBuffer out(name.length()+5); 00413 sptk::CBase64::encode(out,in); 00414 std::string ret="CPPSERV"; 00415 ret+=out.data(); 00416 std::transform(ret.begin(),ret.end(),ret.begin(),container::util::TrEq()); 00417 return ret; 00418 } 00419 00420 00421 servlet::ServletContext* App::getContext(const std::string& uripath) 00422 { 00423 std::string uribase = getUriBase(); 00424 if(uripath.find(uribase) != 0) 00425 return 0; 00426 std::string contextName = uripath.substr(uribase.length()); 00427 return cfg.getApp(contextName); 00428 } 00429 int App::getMajorVersion() 00430 { 00431 return VERSION_MAJOR; 00432 } 00433 int App::getMinorVersion() 00434 { 00435 return VERSION_MINOR; 00436 } 00437 std::string App::getMimeType(const std::string& /*file*/) 00438 { 00439 //FIXME: should we do something here? Talk to web server? 00440 //FIXME: Call file? 00441 return ""; 00442 } 00443 std::auto_ptr< std::set<std::string> > App::getResourcePaths(const std::string& path) 00444 { 00445 std::auto_ptr< std::set<std::string> > ret(new std::set<std::string>); 00446 // For now we can only return list of available Servlets 00447 // Which means our implementation is not really complete 00448 servletmap_t* sp = maptop.getPath(path, false); 00449 if(!sp) 00450 return ret; 00451 std::insert_iterator<std::set<std::string> > ii(*ret, ret->begin()); 00452 std::transform(sp->getSubpaths().begin(), sp->getSubpaths().end(), ii, __gnu_cxx::select1st<std::pair<std::string, ServletMap<ServletDesc>*> >()); 00453 std::transform(sp->getServlets().begin(), sp->getServlets().end(), ii, __gnu_cxx::select1st<std::pair<std::string, ServletDesc*> >()); 00454 return ret; 00455 } 00456 std::string App::getResource(const std::string& /*path*/) 00457 { 00458 //throw servlet::ServletException("Method unimplemented"); 00459 return ""; 00460 } 00461 std::istream& App::getResourceAsStream(const std::string& /*path*/) 00462 { 00463 throw servlet::ServletException("Method unimplemented"); 00464 } 00465 servlet::RequestDispatcher* App::getRequestDispatcher(const std::string& path) 00466 { 00467 if(path[0]!='/') 00468 throw servlet::IllegalArgumentException(); 00469 std::string name(path.substr(1)); 00470 return getServletContainer(name); 00471 } 00472 servlet::RequestDispatcher* App::getNamedDispatcher(const std::string& name) 00473 { 00474 return getServletContainer(name); 00475 } 00476 std::string App::getRealPath(const std::string& /*path*/) 00477 { 00478 return ""; 00479 } 00480 std::string App::getServerInfo() 00481 { 00482 return "CPPSERV/" CPPSERV_VERSION; 00483 } 00484 std::string App::getInitParameter(const std::string& name) 00485 { 00486 param_t::const_iterator iRet = m_params.find(name); 00487 if(iRet == m_params.end()) 00488 return ""; 00489 else 00490 return iRet->second; 00491 } 00492 std::auto_ptr< std::vector<std::string> > App::getInitParameterNames() 00493 { 00494 return util::getMapKeyNames(m_params); 00495 } 00496 boost::shared_ptr<void> App::getAttribute(const std::string& name) 00497 { 00498 attr_t::const_iterator iRet = m_attrs.find(name); 00499 if(iRet == m_attrs.end()) 00500 return boost::shared_ptr<void>(); 00501 else 00502 return iRet->second; 00503 } 00504 std::auto_ptr< std::vector<std::string> > App::getAttributeNames() 00505 { 00506 return util::getMapKeyNames(m_attrs); 00507 } 00508 void App::setAttribute(const std::string& name, boost::shared_ptr<void> object) 00509 { 00510 m_attrs[name] = object; 00511 } 00512 void App::removeAttribute(const std::string& name) 00513 { 00514 attr_t::iterator it=m_attrs.find(name); 00515 if(it!=m_attrs.end()) 00516 m_attrs.erase(it); 00517 } 00518 bool App::hasAttribute(const std::string& name) const 00519 { 00520 attr_t::const_iterator it=m_attrs.find(name); 00521 return it!=m_attrs.end(); 00522 } 00523 00524 }