00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
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
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
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 }