CPPSERV


Home Projects Jobs Clientele Contact
CPPSERV Documentation Download TODO Mailing lists Bug tracker News RSS Feed Browse source

requestlistener.cpp

Go to the documentation of this file.
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 #include "requestlistener.h"
00021 #include "requesthandler.h"
00022 
00023 #include <sys/socket.h>
00024 #include <sys/types.h>
00025 #include <sys/file.h>
00026 #include <sys/stat.h>
00027 #include <netdb.h>
00028 #include <netinet/in.h>
00029 #include <sys/un.h>
00030 #include <errno.h>
00031 #include <fcntl.h>
00032 #include <unistd.h>
00033 
00034 #include <functional>
00035 #include <algorithm>
00036 #include <socket++/sockinet.h>
00037 #include <sptk4/CGuard.h>
00038 
00039 namespace container {
00040 
00041 RequestListener::RequestListener(sptk::CThreadPool& pool)
00042     : m_maxfd(-1)
00043     , m_pool(pool)
00044 {
00045     FD_ZERO(&m_accept_fds);
00046 }
00047 
00048 
00049 RequestListener::~RequestListener()
00050 {
00051 }
00052 
00053 class AcceptConnection: public std::unary_function<RequestListener::Acceptor*, void>
00054 {
00055     sptk::CThreadPool& m_pool;
00056     fd_set *m_set;
00057 public:
00058     explicit AcceptConnection(sptk::CThreadPool& pool, fd_set* fds)
00059         : m_pool(pool), m_set(fds){}
00060     void operator()(std::pair<const int, RequestListener::Acceptor*>& pair)
00061     {
00062         if(FD_ISSET(pair.first, m_set)) {
00063             Connection *con=pair.second->accept();
00064             if(con) {
00065                 RequestHandler *rh=new RequestHandler(con);
00066                 m_pool.queue(rh);
00067             }
00068         }
00069     }
00070 };
00071 
00072 }
00073 
00074 
00075 
00081 void container::RequestListener::acceptRequests()
00082 {
00083     while(true) {
00084         m_listenerLock.lock();
00085         fd_set rfds=m_accept_fds;
00086         int maxfd=m_maxfd;
00087         m_listenerLock.unlock();
00088         int ret=::select(maxfd+1,&rfds,0,0,0);
00089         if(ret==-1)
00090             throw servlet::IOError();
00091         {
00092             sptk::CGuard guard(m_listenerLock);
00093             try {
00094                 for_each(m_listeners.begin(), m_listeners.end(), AcceptConnection (m_pool,&rfds));
00095             } catch(...) {
00096                 std::cerr<<"Exception while accepting connections"<<std::endl;
00097                 throw;
00098             }
00099         }
00100     }
00101 }
00102 
00103 container::RequestListener::Acceptor::~Acceptor()
00104 {
00105 }
00106 
00107 void container::RequestListener::Acceptor::init(container::RequestListener::Acceptor::FileDesc& sock)
00108     throw(servlet::IOError)
00109 {
00110     m_sock=sock;
00111     sock=-1;
00112     unsigned long flags = ::fcntl(m_sock,F_GETFL);
00113     if((long)flags==-1)
00114         throw servlet::IOError("getting socket flags");
00115     if(fcntl(m_sock, F_SETFL, flags|O_ASYNC)==-1)
00116         throw servlet::IOError("setting O_ASYNC socket flag");
00117 }
00118 
00119 container::RequestListener::TCPAcceptor::TCPAcceptor(std::string host, unsigned short port)
00120     throw(servlet::IOError, container::ResolverError)
00121 {
00122     protoent* p=::getprotobyname("tcp");
00123     if(!p)
00124         throw servlet::IOError("cannot get protocol number for TCP protocol");
00125     if(host.empty())
00126         host = "0.0.0.0";
00127     sockinetaddr bindto(host.c_str(),port);
00128     FileDesc sock=::socket(PF_INET,SOCK_STREAM,p->p_proto);
00129     if(sock==-1)
00130         throw servlet::IOError("creating socket");
00131     int ret=::bind(sock,bindto.addr(),bindto.size());
00132     if(ret==-1)
00133         throw servlet::IOError("binding socket");
00134     ret=::listen(sock,MAX_BACKLOG);
00135     if(ret==-1)
00136         throw servlet::IOError("listening on socket");
00137     init(sock);
00138 }
00139 
00140 container::RequestListener::UnixAcceptor::UnixAcceptor(const std::string& path)
00141     throw(servlet::IOError)
00142     : m_path(path)
00143     , m_socklock(::open((path+".lck").c_str(),O_CREAT|O_NONBLOCK,0600))
00144 {
00145     if(m_socklock==-1)
00146         throw servlet::IOError("unable to open socket lock file "+path+".lck");
00147     if(::flock(m_socklock, LOCK_EX|LOCK_NB)==-1)
00148         throw servlet::IOError("Unable to lock socket "+path+". Is another instance of CPPSERV running?");
00149     ::unlink(path.c_str());
00150     FileDesc sock=::socket(PF_UNIX,SOCK_STREAM,0);
00151     if(sock==-1)
00152         throw servlet::IOError("unable to create listener socket");
00153     struct sockaddr_un bindto={AF_UNIX,{0,}};
00154     ::strncpy(bindto.sun_path, path.c_str(), sizeof(bindto.sun_path));
00155     int ret=::bind(sock,(sockaddr*)&bindto,sizeof(bindto));
00156     if(ret==-1)
00157         throw servlet::IOError("unable to bind to listener socket \""+path+"\"");
00158     ret=::listen(sock,MAX_BACKLOG);
00159     if(ret==-1)
00160         throw servlet::IOError("unable to listen to listener socket \""+path+"\"");
00161     // No threading is going on yet, so it's ok to use strerror here
00162     if(::chmod(path.c_str(),0666) == -1)
00163         std::cerr << "Error setting mode on socket "
00164             << path <<": " << strerror(errno) << std::endl;
00165     init(sock);
00166 }
00167 
00168 container::RequestListener::UnixAcceptor::~UnixAcceptor()
00169 {
00170     if(::unlink(m_path.c_str())==-1) {
00171         char errbuf[1024];
00172         const char* err = strerror_r(errno, errbuf, sizeof(errbuf));
00173         std::cerr<<"Error removing "<<m_path<<": "<<err<<std::endl;
00174     }
00175 }
00176 
00177 container::RequestListener::Acceptor::FileDesc::~FileDesc()
00178 {
00179     if(m_fd!=-1)
00180         close(m_fd);
00181 }
00182 
00183 container::Connection* container::RequestListener::Acceptor::accept()
00184 {
00185     struct sockaddr_in remote;
00186     socklen_t remlen=sizeof(remote);
00187     int con=::accept(m_sock,(sockaddr*)&remote,&remlen);
00188     if(con==-1){
00189         char errbuf[1024];
00190         const char* err = strerror_r(errno, errbuf, sizeof(errbuf));
00191         std::cerr<<"Error accepting request: "<<err<<'\n';
00192         //Error may not be fatal, so let caller handle it
00193         return 0;
00194     }
00195     try {
00196         if(::fcntl(con, F_SETFL, 0)==-1)
00197             throw servlet::IOError("clearing O_ASYNC socket flag");
00198         Connection *c=new Connection(con);
00199         return c;
00200     } catch (sockerr& e) {
00201         std::cerr<<"Error at "<<__FILE__<<": "<<__LINE__<<": "<<e.errstr()<<std::endl;
00202         return 0;
00203     }
00204 }
00205 
00206 
00210 void container::RequestListener::addAcceptor(container::RequestListener::Acceptor* acc)
00211 {
00212     sptk::CGuard guard(m_listenerLock);
00213     m_listeners[acc->m_sock]=acc;
00214     FD_SET(acc->m_sock, &m_accept_fds);
00215     if(acc->m_sock > m_maxfd)
00216         m_maxfd=acc->m_sock;
00217 }

SourceForge.net Logo