00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021 #include <serverconfig/serverconfig.h>
00022 #include <requesthandler.h>
00023 #include <servlet/IllegalArgumentException.h>
00024 #include <sptk4/CThread.h>
00025 #include <sptk4/CBuffer.h>
00026 #include <sptk4/CBase64.h>
00027 #include <sptk4/CGuard.h>
00028 #include <dlfcn.h>
00029 #include <fcntl.h>
00030
00031 using namespace container;
00032 namespace container {
00033 namespace serverconfig {
00034
00035
00036 class RHServletRemover
00037 {
00038 public:
00039 void operator()(const std::string& path)
00040 {
00041 RequestHandler::removeServlet(path);
00042 }
00043 };
00044
00045
00046 template<class T>
00047 class SMFunctor
00048 {
00049 private:
00050 T* m_inst;
00051 typedef void (T::*F)(const std::string&);
00052 F m_f;
00053 public:
00054 SMFunctor(T* inst, F f): m_inst(inst), m_f(f){}
00055 void operator()(const std::string& path)
00056 {
00057 (m_inst->*m_f)(path);
00058 }
00059 };
00060
00061 AppContext::AppContext(const ConfigNode& node, Context* parent)
00062 : Context(parent->getServerConfig(), parent)
00063 , servlet::ServletContext()
00064 , m_sessionTimeout(600)
00065 , m_uploadDir("/tmp")
00066 , m_cache(false)
00067 #ifdef HAVE_LIBMAGIC
00068 , m_mime_cookie(0)
00069 #endif
00070 {
00071 m_paramregistry.getParamList(getUnsetParams());
00072 util::param_t attrs = node.getAttrs();
00073 util::param_t::const_iterator cacheIt = attrs.find("allow_cacheing");
00074 if(cacheIt != attrs.end())
00075 {
00076 std::string cacheVal(cacheIt->second);
00077 std::transform(cacheVal.begin(), cacheVal.end(), cacheVal.begin(), ::tolower);
00078 if(cacheVal == "true")
00079 m_cache=true;
00080 }
00081 }
00082
00083 bool AppContext::onSetParam(const ConfigNode& node)
00084 {
00085 return m_paramregistry.setParam(node, this);
00086 }
00087
00088 AppContext::~AppContext()
00089 {
00090 if(m_cleaner) {
00091 m_cleaner->stop();
00092 delete m_cleaner;
00093 }
00094 killAllSessions();
00095 std::string empty;
00096 destroyServlets();
00097 RHServletRemover rh;
00098 SMFunctor<AppContext> unl(this, &AppContext::unloadServlet);
00099
00100
00101
00102 m_maptop.forEachServletPath(rh, empty);
00103 m_maptop.forEachServletPath(unl, empty);
00104 #ifdef HAVE_LIBMAGIC
00105 if(m_mime_cookie)
00106 ::magic_close(m_mime_cookie);
00107 #endif
00108 }
00109
00110 void AppContext::registerContexts(ContextRegistry& reg)
00111 {
00112 reg.registerContext("servlet",serverconfig::ServletContext::contextCreator, 0);
00113 reg.registerContext("csp",CSPContext::contextCreator, 0);
00114 }
00115
00116 void AppContext::registerParams(ParamRegistry<AppContext>& reg)
00117 {
00118 reg.registerParam("sessiontimeout",&AppContext::setSessionTimeout,PARAM_INHERITABLE|PARAM_SINGLE_OF_TYPE);
00119 reg.registerParam("upload_dir",&AppContext::setUploadDir,PARAM_INHERITABLE|PARAM_SINGLE_OF_TYPE);
00120 reg.registerParam("parameter",&AppContext::addInitParam,0);
00121 reg.registerParam("max_request_size", &Context::setIgnore,PARAM_INHERITABLE|PARAM_SINGLE_OF_TYPE);
00122 reg.registerParam("max_file_size", &Context::setIgnore,PARAM_INHERITABLE|PARAM_SINGLE_OF_TYPE);
00123 reg.registerParam("uri_base", &AppContext::setUriBase,PARAM_INHERITABLE|PARAM_REQUIRED|PARAM_SINGLE_OF_TYPE);
00124 reg.registerParam("phys_base", &AppContext::setPhysBase,PARAM_INHERITABLE|PARAM_SINGLE_OF_TYPE);
00125 reg.registerParam("default_mime_type", &AppContext::setDefaultMimeType,PARAM_SINGLE_OF_TYPE);
00126 reg.registerParam("default_character_encoding", &AppContext::setDefaultCharacterEncoding,PARAM_SINGLE_OF_TYPE);
00127 }
00128
00129 bool AppContext::setSessionTimeout(const ConfigNode& val)
00130 {
00131 util::param_t attrs = val.getAttrs();
00132 util::param_t::const_iterator value = attrs.find("value");
00133 if(attrs.end() == value) {
00134 std::cerr<<"Value missing for session timeout paramter"<<std::endl;
00135 return false;
00136 }
00137 m_sessionTimeout=::atoi(value->second.c_str());
00138 return true;
00139 }
00140
00141 bool AppContext::setUploadDir(const ConfigNode& val)
00142 {
00143 return setString(val, m_uploadDir);
00144 }
00145
00146 bool AppContext::setPhysBase(const ConfigNode& val)
00147 {
00148 return setString(val, m_physbase);
00149 }
00150
00151 bool AppContext::setUriBase(const ConfigNode& val)
00152 {
00153 return setString(val, m_uribase);
00154 }
00155
00156 bool AppContext::setDefaultMimeType(const ConfigNode& val)
00157 {
00158 return setString(val, m_mime);
00159 }
00160
00161 bool AppContext::setDefaultCharacterEncoding(const ConfigNode& val)
00162 {
00163 return setString(val, m_enc);
00164 }
00165
00166 bool AppContext::addInitParam(const ConfigNode& val)
00167 {
00168 util::param_t::const_iterator name=val.getAttrs().find("name");
00169 util::param_t::const_iterator value=val.getAttrs().find("value");
00170 if(value == val.getAttrs().end() || name == val.getAttrs().end()) {
00171 std::cerr<<"Application paramter is missing name or value"<<std::endl;
00172 return false;
00173 }
00174 m_init_params.push_back(std::pair<std::string,std::string>(name->second,value->second));
00175 return true;
00176 }
00177
00178 bool AppContext::onPreComplete()
00179 {
00180
00181 if(m_uribase.empty())
00182 m_uribase="/";
00183 else if(*m_uribase.rbegin()!='/')
00184 m_uribase+='/';
00185 m_sessionCookieName=makeSName(getName());
00186 if(m_mime.empty())
00187 m_mime="text/html";
00188 if(m_enc.empty())
00189 m_enc="utf8";
00190 for(util::pairlist_t::iterator it=m_init_params.begin(); it!=m_init_params.end(); it++) {
00191 addInitParam(it->first,it->second);
00192 }
00193 getGlobalContext()->registerApp(getName(), this);
00194 return true;
00195 }
00196
00197 bool AppContext::onPostComplete()
00198 {
00199 m_fileSaveTemplate=m_uploadDir+'/'+"cppserv.tmpXXXXXX";
00200 initServlets();
00201 m_cleaner=new SessionCleaner(this,m_sessionTimeout/2+1);
00202 m_cleaner->run();
00203 return true;
00204 }
00205
00206 ParamRegistry<AppContext> AppContext::m_paramregistry(AppContext::registerParams);
00207 ContextRegistry AppContext::m_contextregistry(AppContext::registerContexts);
00208
00209 ServletConfigImpl* AppContext::addServlet(const std::string& path, const std::string& name, const std::string& dso, bool hidden, size_t maxRequestSize, size_t maxFileSize)
00210 {
00211 std::string fullpath("/");
00212 if(!getServletContextName().empty()) {
00213 fullpath.append(getServletContextName());
00214 fullpath+='/';
00215 }
00216 fullpath.append(path);
00217 ServletDesc* desc = getDesc(fullpath);
00218 if(desc) {
00219 std::cerr<<"Path "<<fullpath<<" is already used"<<std::endl;
00220 return 0;
00221 }
00222 void* dsoHandle=dlopen(dso.c_str(),RTLD_LAZY);
00223 if(!dsoHandle) {
00224 std::cerr<<"Error loading library \""<<dso<<"\": "<<dlerror()<<std::endl;
00225 return 0;
00226 }
00227 servletcreatefunc_t createFunc=(servletcreatefunc_t)dlsym(dsoHandle,(name + "_createServlet").c_str());
00228 if(!createFunc) {
00229 std::cerr<<"Could not locate servlet "<<name<<" in the library "<<dso<<": "<<dlerror()<<std::endl;
00230 return 0;
00231 }
00232 ServletConfigImpl* config=new ServletConfigImpl(this, name);
00233 desc=new ServletDesc(createFunc(), config, dsoHandle, fullpath, maxRequestSize, maxFileSize, m_mime, m_enc, m_cache);
00234 m_maptop[fullpath]=desc;
00235 if(!hidden) {
00236 if(!RequestHandler::addServlet(fullpath,getServletContainer(fullpath))) {
00237 unloadServlet(fullpath);
00238 delServlet(fullpath);
00239 return 0;
00240 }
00241 }
00242 return config;
00243 }
00244
00245 AppContext::ServletDesc* AppContext::getDesc(const std::string& path)
00246 {
00247 return m_maptop.getServletDesc(path);
00248 }
00249
00250 void AppContext::delServlet(const std::string& path)
00251 {
00252 m_maptop.removeDesc(path);
00253 }
00254
00255 ServletContainer* AppContext::getServletContainer(const std::string& path)
00256 {
00257 ServletDesc* desc = getDesc(path);
00258 if(!desc)
00259 return 0;
00260 return &(desc->m_cont);
00261 }
00262
00263 void AppContext::splitServPath(const std::string& path, std::string& dir, std::string& name)
00264 {
00265 std::string::size_type slash = path.rfind('/');
00266 if(slash == std::string::npos) {
00267 dir = "/";
00268 name = path;
00269 } else {
00270 dir.assign(path.substr(0, slash));
00271 name.assign(path.substr(slash + 1));
00272 }
00273 }
00274
00275 void AppContext::initServlet(const std::string& path)
00276 {
00277 ServletContainer* cont=getServletContainer(path);
00278 cont->init();
00279 }
00280
00281 void AppContext::destroyServlet(const std::string& path)
00282 {
00283 ServletContainer* cont=getServletContainer(path);
00284 cont->destroy();
00285 }
00286
00293 void AppContext::unloadServlet(const std::string& path)
00294 {
00295 ServletContainer* cont=getServletContainer(path);
00296 servlet::ServletConfig* conf=cont->getConfig();
00297 ServletDesc* d=getDesc(path);
00298 void* dsoHandle=d->m_h;
00299 delete d;
00300 delete conf;
00301 ::dlclose(dsoHandle);
00302 }
00303
00304 void AppContext::initServlets()
00305 {
00306 std::string empty;
00307 SMFunctor<AppContext> init(this, &AppContext::initServlet);
00308 m_maptop.forEachServletPath(init, empty);
00309 }
00310
00311 void AppContext::destroyServlets()
00312 {
00313 std::string empty;
00314 SMFunctor<AppContext> destroy(this, &AppContext::destroyServlet);
00315 m_maptop.forEachServletPath(destroy, empty);
00316 }
00317
00318
00327 container::HttpSessionImpl* AppContext::getSession(const std::string& sid, bool create)
00328 {
00329 sptk::CGuard guard(m_sessionLock);
00330 if(sid.empty()) {
00331 container::HttpSessionImpl *s = 0;
00332 if(create)
00333 s = newSession();
00334 return s;
00335 }
00336 sessionlist_t::iterator s=m_sessions.find(sid);
00337 if(s==m_sessions.end()||!s->second->validP()){
00338 HttpSessionImpl* s = 0;
00339 if(create)
00340 s = newSession();
00341 return s;
00342 }
00343 s->second->notNew();
00344 return s->second;
00345 }
00346
00347
00352 container::HttpSessionImpl* AppContext::newSession()
00353 {
00354 std::string sid=container::util::getRandomString(10);
00355 HttpSessionImpl* s=new HttpSessionImpl(*this,sid,m_sessionTimeout);
00356 m_sessions[sid]=s;
00357 return s;
00358 }
00359
00360
00366 void AppContext::killSession(const std::string& sid)
00367 {
00368 sessionlist_t::iterator s=m_sessions.find(sid);
00369 if(s==m_sessions.end()){
00370 return;
00371 }
00372 delete s->second;
00373 m_sessions.erase(sid);
00374 }
00375
00376
00380 void AppContext::cleanSessions()
00381 {
00382 sptk::CGuard guard(m_sessionLock);
00383 sessionlist_t::iterator cur=m_sessions.begin();
00384 while(cur!=m_sessions.end()){
00385 if(!cur->second->validP()){
00386 std::string sid = cur->first;
00387 cur++;
00388 killSession(sid);
00389 } else {
00390 cur++;
00391 }
00392 }
00393 }
00398 AppContext::SessionCleaner::SessionCleaner(AppContext* ctx,int freq)
00399 : sptk::CThread("SessionCleaner",true)
00400 , m_running(true)
00401 , m_freq(freq)
00402 , m_ctx(ctx)
00403 {
00404 if(::pipe(m_trigger)==-1) {
00405 char errbuf[1024];
00406 const char* err = ::strerror_r(errno, errbuf, sizeof(errbuf));
00407 std::cerr<<"Error setting up session cleaner trigger: "<<err<<std::endl;
00408 ::exit(1);
00409 }
00410 if(::fcntl(m_trigger[0],F_SETFL,O_NONBLOCK)<0) {
00411 char errbuf[1024];
00412 const char* err = ::strerror_r(errno, errbuf, sizeof(errbuf));
00413 std::cerr<<"Error setting trigger reader to non-blocking mode: "
00414 <<err<<std::endl;
00415 ::exit(1);
00416 }
00417 }
00421 AppContext::SessionCleaner::~SessionCleaner()
00422 {
00423 ::close(m_trigger[0]);
00424 ::close(m_trigger[1]);
00425 }
00426 void AppContext::SessionCleaner::threadFunction()
00427 {
00428 while(m_running) {
00429 m_ctx->cleanSessions();
00430 struct timeval timeout={m_freq,0};
00431 fd_set rfd;
00432 FD_ZERO(&rfd);
00433 FD_SET(m_trigger[0],&rfd);
00434 int r=::select(m_trigger[0]+1,&rfd,0,0,&timeout);
00435 if(r){
00436 char buf[4];
00437 while(::read(m_trigger[0],buf,sizeof(buf))>0){;}
00438 }
00439 }
00440 }
00447 void AppContext::SessionCleaner::stop()
00448 {
00449 m_running=false;
00450 this->wakeUp();
00451 this->terminate();
00452 }
00453
00459 bool AppContext::isSessionValid(const std::string& sid)
00460 {
00461 sessionlist_t::iterator s=m_sessions.find(sid);
00462 return s!=m_sessions.end()&&s->second->validP();
00463 }
00464
00465
00469 void AppContext::addInitParam(const std::string& name, const std::string& value)
00470 {
00471 m_params[name]=value;
00472 }
00473
00478 std::string AppContext::getServletContextName() const
00479 {
00480 return getName();
00481 }
00482
00483 void AppContext::log(const std::string& msg) const
00484 {
00485 std::cerr<<msg<<std::endl;
00486 }
00487
00488 void AppContext::log(const std::string& message, const std::exception& e) const
00489 {
00490 log(message);
00491 std::cerr<<e.what()<<std::endl;
00492 }
00493
00494
00499 void AppContext::SessionCleaner::wakeUp()
00500 {
00501 char c='1';
00502 ::write(m_trigger[1],&c,1);
00503 }
00504
00505
00512 void AppContext::killAllSessions()
00513 {
00514 sptk::CGuard guard(m_sessionLock);
00515 sessionlist_t::iterator cur=m_sessions.begin();
00516 while(cur!=m_sessions.end()){
00517 delete cur->second;
00518 cur++;
00519 }
00520 m_sessions.clear();
00521 }
00522
00523 std::string AppContext::makeSName(const std::string& name)
00524 {
00525 sptk::CBuffer in(name.c_str());
00526 sptk::CBuffer out(name.length()+5);
00527 std::string ret="CPPSERV";
00528 ::memset(out.data(), 0, out.size());
00529 sptk::CBase64::encode(out,in);
00530 ret+=out.data();
00531 std::transform(ret.begin(),ret.end(),ret.begin(),container::util::TrEq());
00532 return ret;
00533 }
00534
00535
00536 servlet::ServletContext* AppContext::getContext(const std::string& uripath)
00537 {
00538 std::string uribase = getUriBase();
00539 if(uripath.find(uribase) != 0)
00540 return 0;
00541 std::string contextName = uripath.substr(uribase.length());
00542 return getServerConfig().getApp(contextName);
00543 }
00544 int AppContext::getMajorVersion()
00545 {
00546 return VERSION_MAJOR;
00547 }
00548 int AppContext::getMinorVersion()
00549 {
00550 return VERSION_MINOR;
00551 }
00552 std::string AppContext::getMimeType(const std::string& file)
00553 {
00554 std::string ret;
00555 #ifdef HAVE_LIBMAGIC
00556 if(!m_mime_cookie)
00557 {
00558 m_mime_cookie=::magic_open(MAGIC_SYMLINK|MAGIC_MIME_TYPE|MAGIC_ERROR);
00559 if(!m_mime_cookie)
00560 {
00561 std::cerr<<"Error initializing MIME magic library"<<std::endl;
00562 return ret;
00563 }
00564 if(::magic_load(m_mime_cookie, 0)!=0)
00565 {
00566 std::cerr<<"Error loading MIME magic database: "<<::magic_error(m_mime_cookie)<<std::endl;
00567 return ret;
00568 }
00569 }
00570 const char* cstr_ret = ::magic_file(m_mime_cookie, file.c_str());
00571 if(!cstr_ret)
00572 {
00573 std::cerr<<"Error determining MIME type of file \""<<file<<"\": "<<::magic_error(m_mime_cookie)<<std::endl;
00574 return ret;
00575 }
00576 ret = cstr_ret;
00577 #endif
00578 return ret;
00579 }
00580 std::auto_ptr< std::set<std::string> > AppContext::getResourcePaths(const std::string& path)
00581 {
00582 std::auto_ptr< std::set<std::string> > ret(new std::set<std::string>);
00583
00584
00585 servletmap_t* sp = m_maptop.getPath(path, false);
00586 if(!sp)
00587 return ret;
00588 std::insert_iterator<std::set<std::string> > ii(*ret, ret->begin());
00589 std::transform(sp->getSubpaths().begin(), sp->getSubpaths().end(), ii, __gnu_cxx::select1st<std::pair<std::string, ServletMap<ServletDesc>*> >());
00590 std::transform(sp->getServlets().begin(), sp->getServlets().end(), ii, __gnu_cxx::select1st<std::pair<std::string, ServletDesc*> >());
00591 return ret;
00592 }
00593 std::string AppContext::getResource(const std::string& )
00594 {
00595
00596 return "";
00597 }
00598 std::istream& AppContext::getResourceAsStream(const std::string& )
00599 {
00600 throw servlet::ServletException("Method unimplemented");
00601 }
00602 servlet::RequestDispatcher* AppContext::getRequestDispatcher(const std::string& path)
00603 {
00604 if(path[0]!='/')
00605 throw servlet::IllegalArgumentException();
00606 std::string name(path.substr(1));
00607 return getServletContainer(name);
00608 }
00609 servlet::RequestDispatcher* AppContext::getNamedDispatcher(const std::string& name)
00610 {
00611 return getServletContainer(name);
00612 }
00613 std::string AppContext::getRealPath(const std::string& path)
00614 {
00615
00616
00617 std::string ret = m_physbase;
00618 if(path.empty())
00619 return "";
00620 if(path[0]!='/')
00621 ret += '/';
00622 ret+=path;
00623 return ret;
00624 }
00625 std::string AppContext::getServerInfo()
00626 {
00627 return "CPPSERV/" CPPSERV_VERSION;
00628 }
00629 std::string AppContext::getInitParameter(const std::string& name)
00630 {
00631 param_t::const_iterator iRet = m_params.find(name);
00632 if(iRet == m_params.end())
00633 return "";
00634 else
00635 return iRet->second;
00636 }
00637 std::auto_ptr< std::vector<std::string> > AppContext::getInitParameterNames()
00638 {
00639 return util::getMapKeyNames(m_params);
00640 }
00641 boost::shared_ptr<void> AppContext::getAttribute(const std::string& name)
00642 {
00643 attr_t::const_iterator iRet = m_attrs.find(name);
00644 if(iRet == m_attrs.end())
00645 return boost::shared_ptr<void>();
00646 else
00647 return iRet->second;
00648 }
00649 std::auto_ptr< std::vector<std::string> > AppContext::getAttributeNames()
00650 {
00651 return util::getMapKeyNames(m_attrs);
00652 }
00653 void AppContext::setAttribute(const std::string& name, boost::shared_ptr<void> object)
00654 {
00655 m_attrs[name] = object;
00656 }
00657 void AppContext::removeAttribute(const std::string& name)
00658 {
00659 attr_t::iterator it=m_attrs.find(name);
00660 if(it!=m_attrs.end())
00661 m_attrs.erase(it);
00662 }
00663 bool AppContext::hasAttribute(const std::string& name) const
00664 {
00665 attr_t::const_iterator it=m_attrs.find(name);
00666 return it!=m_attrs.end();
00667 }
00668
00669 }
00670 }