//============================================================================= // YHTTPD // Webserver Class : Until now: exact one instance //============================================================================= // c++ #include #include #include // system #include #include #include #include #include #include #include // tuxbox #include // yhttpd #include "yhttpd.h" #include "ytypes_globals.h" #include "ywebserver.h" #include "ylogging.h" #include "helper.h" #include "ysocket.h" #include "yconnection.h" #include "yrequest.h" //============================================================================= // Initialization of static variables //============================================================================= bool CWebserver::is_threading = true; pthread_mutex_t CWebserver::mutex = PTHREAD_MUTEX_INITIALIZER;; //============================================================================= // Constructor & Destructor & Initialization //============================================================================= CWebserver::CWebserver() { terminate = false; for(int i=0;i= HTTPD_MAX_CONNECTIONS-1) sleep(1); // Socket Error? if(fd == -1 && errno != EINTR) { perror("select"); return false; } // Socket Timeout? if(fd == 0) { // Testoutput for long living threads if(++test_counter >= MAX_TIMEOUTS_TO_TEST) { for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++) if(SocketList[j] != NULL) // here is a socket log_level_printf(2,"FD-TEST sock:%d handle:%d open:%d\n",SocketList[j]->get_socket(), SocketList[j]->handling,SocketList[j]->isOpened); test_counter=0; } // some connection closing previous missed? if(++timeout_counter >= MAX_TIMEOUTS_TO_CLOSE) { CloseConnectionSocketsByTimeout(); timeout_counter=0; } continue; // main loop again } //---------------------------------------------------------------------------------------- // Check all observed descriptors & check new or re-use Connections //---------------------------------------------------------------------------------------- for(int i = listener; i <= fdmax; i++) { int slot = -1; if(FD_ISSET(i, &read_fds)) // Socket observed? { // we got one!! if (i == listener) // handle new connections slot = AcceptNewConnectionSocket(); else // Connection on an existing open Socket = reuse (keep-alive) { slot = SL_GetExistingSocket(i); if(slot>=0) log_level_printf(2,"FD: reuse con fd:%d\n",SocketList[slot]->get_socket()); } // prepare Connection handling if(slot>=0) if(SocketList[slot] != NULL && !SocketList[slot]->handling && SocketList[slot]->isValid) { log_level_printf(2,"FD: START CON HANDLING con fd:%d\n",SocketList[slot]->get_socket()); FD_CLR(SocketList[slot]->get_socket(), &master); // remove from master set SocketList[slot]->handling = true; // prepares for thread-handling if(!handle_connection(SocketList[slot]))// handle this activity { // Can not handle more threads char httpstr[]=HTTP_PROTOCOL " 503 Service Unavailable\r\n\r\n"; SocketList[slot]->Send(httpstr, strlen(httpstr)); SL_CloseSocketBySlot(slot); } } } }// for CloseConnectionSocketsByTimeout(); // Check connections to close }//while #else while(!terminate) { CySocket *newConnectionSock; if(!(newConnectionSock = listenSocket.accept() )) //Now: Blocking wait { dperror("Socket accept error. Continue.\n"); continue; } log_level_printf(3,"Socket connect from %s\n", (listenSocket.get_client_ip()).c_str() ); #ifdef Y_CONFIG_USE_OPEN_SSL if(Cyhttpd::ConfigList["SSL"]=="true") newConnectionSock->initAsSSL(); // make it a SSL-socket #endif handle_connection(newConnectionSock); } #endif return true; } //============================================================================= // SocketList Handler //============================================================================= //----------------------------------------------------------------------------- // Accept new Connection //----------------------------------------------------------------------------- int CWebserver::AcceptNewConnectionSocket() { int slot = -1; CySocket *connectionSock = NULL; int newfd; if(!(connectionSock = listenSocket.accept() )) // Blocking wait { dperror("Socket accept error. Continue.\n"); delete connectionSock; return -1; } #ifdef Y_CONFIG_USE_OPEN_SSL if(Cyhttpd::ConfigList["SSL"]=="true") connectionSock->initAsSSL(); // make it a SSL-socket #endif log_level_printf(2,"FD: new con fd:%d on port:%d\n",connectionSock->get_socket(), connectionSock->get_accept_port()); // Add Socket to List slot = SL_GetFreeSlot(); if(slot < 0) { connectionSock->close(); aprintf("No free Slot in SocketList found. Open:%d\n",open_connections); } else { SocketList[slot] = connectionSock; // put it to list fcntl(connectionSock->get_socket() , F_SETFD , O_NONBLOCK); // set non-blocking open_connections++; // count open connectins newfd = connectionSock->get_socket(); if (newfd > fdmax) // keep track of the maximum fd fdmax = newfd; } return slot; } //----------------------------------------------------------------------------- // Get Index for Socket from SocketList //----------------------------------------------------------------------------- int CWebserver::SL_GetExistingSocket(SOCKET sock) { int slot = -1; for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++) if(SocketList[j] != NULL // here is a socket && SocketList[j]->get_socket() == sock) // we know that socket { slot = j; break; } return slot; } //----------------------------------------------------------------------------- // Get Index for free Slot in SocketList //----------------------------------------------------------------------------- int CWebserver::SL_GetFreeSlot() { int slot = -1; for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++) if(SocketList[j] == NULL) // here is a free slot { slot = j; break; } return slot; } //----------------------------------------------------------------------------- // Look for Sockets to close //----------------------------------------------------------------------------- void CWebserver::CloseConnectionSocketsByTimeout() { CySocket *connectionSock = NULL; for(int j=0;j < HTTPD_MAX_CONNECTIONS;j++) if(SocketList[j] != NULL // here is a socket && !SocketList[j]->handling) // it is not handled { connectionSock = SocketList[j]; SOCKET thisSocket = connectionSock->get_socket(); bool shouldClose = true; if(!connectionSock->isValid) // If not valid -> close ; // close else if(connectionSock->tv_start_waiting.tv_sec != 0 || SocketList[j]->tv_start_waiting.tv_usec != 0) { // calculate keep-alive timeout struct timeval tv_now; struct timezone tz_now; gettimeofday(&tv_now, &tz_now); long long tdiff = ((tv_now.tv_sec - connectionSock->tv_start_waiting.tv_sec) * 1000000 + (tv_now.tv_usec - connectionSock->tv_start_waiting.tv_usec)); if(tdiff < HTTPD_KEEPALIVE_TIMEOUT || tdiff <0) shouldClose = false; } if(shouldClose) { log_level_printf(2,"FD: close con Timeout fd:%d\n",thisSocket); SL_CloseSocketBySlot(j); } } } //----------------------------------------------------------------------------- // Add Socket fd to FD_SET again (for select-handling) // Add start-time for waiting for connection re-use / keep-alive //----------------------------------------------------------------------------- void CWebserver::addSocketToMasterSet(SOCKET fd) { int slot = SL_GetExistingSocket(fd); // get slot/index for fd if(slot<0) return; log_level_printf(2,"FD: add to master fd:%d\n",fd); struct timeval tv_now; struct timezone tz_now; gettimeofday(&tv_now, &tz_now); SocketList[slot]->tv_start_waiting = tv_now; // add keep-alive wait time FD_SET(fd, &master); // add fd to select-master-set } //----------------------------------------------------------------------------- // Close (FD_SET handled) Socket // Clear it from SocketList //----------------------------------------------------------------------------- void CWebserver::SL_CloseSocketBySlot(int slot) { open_connections--; // count open connections if(SocketList[slot] == NULL) return; SocketList[slot]->handling = false; // no handling anymore FD_CLR(SocketList[slot]->get_socket(), &master);// remove from master set SocketList[slot]->close(); // close the socket delete SocketList[slot]; // destroy ySocket SocketList[slot] = NULL; // free in list } //============================================================================= // Thread Handling //============================================================================= //----------------------------------------------------------------------------- // Check if IP is allowed for keep-alive //----------------------------------------------------------------------------- bool CWebserver::CheckKeepAliveAllowedByIP(std::string client_ip) { pthread_mutex_lock( &mutex ); bool do_keep_alive = true; CStringVector::const_iterator it = conf_no_keep_alive_ips.begin(); while(it != conf_no_keep_alive_ips.end()) { if(trim(*it) == client_ip) do_keep_alive = false; it++; } pthread_mutex_unlock( &mutex ); return do_keep_alive; } //----------------------------------------------------------------------------- // Set Entry(number)to NULL in Threadlist //----------------------------------------------------------------------------- void CWebserver::clear_Thread_List_Number(int number) { pthread_mutex_lock( &mutex ); if(number ySock = newSock; newConn->ySock->handling = true; newConn->WebserverBackref = this; //newConn->is_treaded = is_threading; newConn->is_treaded = false; int index = -1; if(0) /*if(is_threading) FIXME not work */ { pthread_mutex_lock( &mutex ); // look for free Thread slot for(int i=0;ithread_number = index; //remember Index of Thread slot (for clean up) // Create an orphan Thread. It is not joinable anymore pthread_mutex_unlock( &mutex ); // start connection Thread if(pthread_create(&Connection_Thread_List[index], &attr, WebThread, (void *)newConn) != 0) dperror("Could not create Connection-Thread\n"); } else // non threaded WebThread((void *)newConn); return ((index != -1) || !is_threading); } //------------------------------------------------------------------------- // Webserver-Thread for each connection //------------------------------------------------------------------------- void *WebThread(void *args) { CWebserverConnection *con; CWebserver *ws; TWebserverConnectionArgs *newConn = (TWebserverConnectionArgs *) args; ws = newConn->WebserverBackref; bool is_threaded = newConn->is_treaded; if(is_threaded) log_level_printf(1,"++ Thread 0x06%X gestartet\n", (int) pthread_self()); if (!newConn) { dperror("WebThread called without arguments!\n"); if(newConn->is_treaded) pthread_exit(NULL); } // (1) create & init Connection con = new CWebserverConnection(ws); con->Request.UrlData["clientaddr"] = newConn->ySock->get_client_ip(); // TODO:here? con->sock = newConn->ySock; // give socket reference newConn->ySock->handling = true; // dont handle this socket now be webserver main loop // (2) handle the connection con->HandleConnection(); // (3) end connection handling #ifdef Y_CONFIG_FEATURE_KEEP_ALIVE if(!con->keep_alive) log_level_printf(2,"FD SHOULD CLOSE sock:%d!!!\n",con->sock->get_socket()); else ws->addSocketToMasterSet(con->sock->get_socket()); // add to master set #else delete newConn->ySock; #endif if(!con->keep_alive) con->sock->isValid = false; con->sock->handling = false; // socket can be handled by webserver main loop (select) again // (4) end thread delete con; int thread_number = newConn->thread_number; delete newConn; if(is_threaded) { log_level_printf(1,"-- Thread 0x06%X beendet\n",(int)pthread_self()); ws->clear_Thread_List_Number(thread_number); pthread_exit(NULL); } return NULL; }