예제 #1
0
파일: ehs.cpp 프로젝트: Audifire/mtasa-blue
EHS::StartServerResult 
EHS::StartServer ( EHSServerParameters & iroEHSServerParameters )
{
	StartServerResult nResult = STARTSERVER_INVALID;

	m_oEHSServerParameters = iroEHSServerParameters;

	if ( poEHSServer != NULL ) {

		EHS_TRACE ( "Warning: Tried to start server that was already running\n" );


		nResult = STARTSERVER_ALREADYRUNNING;

	} else {

		// associate a EHSServer object to this EHS object
		poEHSServer = new EHSServer ( this  );

		if ( poEHSServer->m_nServerRunningStatus == EHSServer::SERVERRUNNING_NOTRUNNING ) {
	

			EHS_TRACE ( "Error: Failed to start server\n" );



			return STARTSERVER_FAILED;

		}
	}

	return STARTSERVER_SUCCESS;

}
예제 #2
0
파일: ehs.cpp 프로젝트: Audifire/mtasa-blue
HttpResponse * 
EHS::RouteRequest ( HttpRequest * ipoHttpRequest ///< request info for service
	)
{

	// get the next path from the URI
	std::string sNextPathPart = GetNextPathPart ( ipoHttpRequest->sUri );


	EHS_TRACE ( "Info: Trying to route: '%s'\n", sNextPathPart.c_str ( ) );


	// if there is no more path, call HandleRequest on this EHS object with
	//   whatever's left - or if we're not routing
	if ( sNextPathPart.empty ( ) ||
		 m_oEHSServerParameters.find ( "norouterequest" ) !=
		 m_oEHSServerParameters.end ( ) ) {

		// create an HttpRespose object for the client
		HttpResponse * poHttpResponse = 
			new HttpResponse ( ipoHttpRequest->m_nRequestId,
							   ipoHttpRequest->m_poSourceEHSConnection );
		
		// get the actual response and return code
		poHttpResponse->m_nResponseCode = 
			HandleRequest ( ipoHttpRequest, poHttpResponse );

		return poHttpResponse;

	}

	// if the path exists, check it against the map of EHSs
	if ( oEHSMap [ sNextPathPart ] ) {

		// if it exists, call RouteRequest with that EHS and the
		//   new shortened path

		return oEHSMap [ sNextPathPart ]->RouteRequest ( ipoHttpRequest );

	}
	// if it doesn't exist, send an error back up saying resource doesn't exist
	else {

		
		EHS_TRACE ( "Info: Routing failed.  Most likely caused by an invalid URL, not internal error\n" );


		// send back a 404 response
		HttpResponse * poHttpResponse = new HttpResponse ( ipoHttpRequest->m_nRequestId,
														   ipoHttpRequest->m_poSourceEHSConnection );

		poHttpResponse->m_nResponseCode = HTTPRESPONSECODE_404_NOTFOUND;
		poHttpResponse->SetBody ( "404 - Not Found", strlen ( "404 - Not Found" ) );

		return poHttpResponse;
		
	}

}
예제 #3
0
파일: ehs.cpp 프로젝트: Audifire/mtasa-blue
void EHSConnection::AddResponse ( HttpResponse * ipoHttpResponse )
{

	MUTEX_LOCK ( m_oConnectionMutex );

	// push the object on to the list
	m_oHttpResponseMap [ ipoHttpResponse->m_nResponseId ] = ipoHttpResponse;

	// go through the list until we can't find the next response to send
	int nFoundNextResponse = 0;
	do {

		nFoundNextResponse = 0;

		if ( m_oHttpResponseMap.find ( m_nResponses + 1 ) != m_oHttpResponseMap.end ( ) ) {

			nFoundNextResponse = 1;
			
			HttpResponseMap::iterator i = m_oHttpResponseMap.find ( m_nResponses + 1 );
			
			SendHttpResponse ( i->second );

			delete i->second;

			m_oHttpResponseMap.erase ( i );

			m_nResponses++;
			
			// set last activity to the current time for idle purposes
			UpdateLastActivity ( );
			
			// if we're done with this connection, get rid of it
			if ( CheckDone ( ) ) {
				EHS_TRACE( "add response found something to delete\n" );

				// careful with mutexes around here.. Don't want to hold both
				MUTEX_UNLOCK ( m_oConnectionMutex );
				MUTEX_LOCK ( m_poEHSServer->m_oMutex );
				m_poEHSServer->RemoveEHSConnection ( this );
				MUTEX_UNLOCK ( m_poEHSServer->m_oMutex );
                return;

			}


			EHS_TRACE ( "Sending response %d to %x\n", m_nResponses, this );


		}

	} while ( nFoundNextResponse == 1 );

	MUTEX_UNLOCK ( m_oConnectionMutex );

}
예제 #4
0
파일: ehs.cpp 프로젝트: Audifire/mtasa-blue
int EHSServer::RemoveEHSConnection ( EHSConnection * ipoEHSConnection )
{

	// don't lock as it's only called from within locked sections
	assert ( ipoEHSConnection != NULL );
	int nDeletedOneAlready = 0;


	EHS_TRACE ( "%d elements to look for somethign to delete\n",
				m_oEHSConnectionList.size ( ) );


	// go through the list and find all occurances of ipoEHSConnection
	for ( EHSConnectionList::iterator i = m_oEHSConnectionList.begin ( );
		  i != m_oEHSConnectionList.end ( );
		  /* no third part */ ) {

		if ( *i == ipoEHSConnection ) {
			if ( nDeletedOneAlready ) {
				EHS_TRACE ( "FATAL ERROR: Deleting a second element in RemoveEHSConnection - EXITING\n" );
				exit ( 2 );
			}
			nDeletedOneAlready = 1;


			// close the FD
			EHSConnection * poEHSConnection = *i;

			NetworkAbstraction * poNetworkAbstraction = poEHSConnection->GetNetworkAbstraction ( );
			
			poNetworkAbstraction->Close ( );
#if STOP_MEMORY_LEAKS   // Needs testing
            delete poEHSConnection;
#endif

			// start back over at the beginning of the list
			m_oEHSConnectionList.erase ( i );
			i = m_oEHSConnectionList.begin ( );

            // Mark as unused
            poEHSConnection->m_UnusedSyncId = m_ThreadsSyncPoint.GetSyncId ();
            m_oEHSConnectionUnusedList.push_back ( poEHSConnection );
		} else {
			i++;
		}

	}

	return nDeletedOneAlready;

}
예제 #5
0
파일: ehs.cpp 프로젝트: AdiBoy/mtasa-blue
int EHSServer::CreateFdSet ( )
{
    MUTEX_LOCK ( m_oMutex );
	// don't lock mutex, as it is only called from within a locked section

	FD_ZERO( &m_oReadFds );

	// add the accepting FD	
	FD_SET( m_poNetworkAbstraction->GetFd ( ), &m_oReadFds );

	int nHighestFd = m_poNetworkAbstraction->GetFd ( );

	for ( EHSConnectionList::iterator oCurrentConnection = m_oEHSConnectionList.begin ( );
		  !( oCurrentConnection == m_oEHSConnectionList.end ( ) );
		  oCurrentConnection++ ) {
		
		/// skip this one if it's already been used
		if ( (*oCurrentConnection)->StillReading ( ) ) {

			int nCurrentFd = 
				( * oCurrentConnection )->GetNetworkAbstraction ( )->GetFd ( );
			
			EHS_TRACE ( "Adding %d to FD SET\n", nCurrentFd );

			FD_SET ( nCurrentFd, &m_oReadFds );

			// store the highest FD in the set to return it
			if ( nCurrentFd > nHighestFd ) {

				nHighestFd = nCurrentFd;

			}

		} else {

			EHS_TRACE ( "FD %d isn't reading anymore\n", 
						( * oCurrentConnection )->GetNetworkAbstraction ( )->GetFd ( ) );

		}
	}

    MUTEX_UNLOCK ( m_oMutex );

	return nHighestFd;

}
예제 #6
0
파일: ehs.cpp 프로젝트: Audifire/mtasa-blue
int EHSServer::ClearIdleConnections ( )
{

	// don't lock mutex, as it is only called rom within locked sections

	int nIdleConnections = 0;

	for ( EHSConnectionList::iterator i = m_oEHSConnectionList.begin ( );
		  i != m_oEHSConnectionList.end ( );
		  i++ ) {

        if ( MUTEX_TRY_LOCK ( (*i)->m_oConnectionMutex ) == false )
            continue;

		// if it's been more than N seconds since a response has been
		//   sent and there are no pending requests
		if ( (*i)->StillReading ( ) &&
			 ( time ( NULL ) - (*i)->LastActivity ( ) > m_nIdleTimeout || (*i)->m_iStopASAP ) &&
			 !(*i)->RequestsPending ( )
			  ) {

			EHS_TRACE ( "Done reading because of idle timeout\n" );
    
			(*i)->DoneReading ( false );

			nIdleConnections++;

		}
		
		MUTEX_UNLOCK ( (*i)->m_oConnectionMutex );

	}


	if ( nIdleConnections > 0 ) {
		EHS_TRACE ( "Cleared %d connections\n", nIdleConnections );
	}


	RemoveFinishedConnections ( );

	return nIdleConnections;

}
예제 #7
0
파일: ehs.cpp 프로젝트: Audifire/mtasa-blue
void EHSServer::HandleData ( int inTimeoutMilliseconds, ///< milliseconds for timeout on select
							 pthread_t inThreadId ///< numeric ID for this thread to help debug
	)
{

	//fprintf ( stderr, "##### [%d] Trying to lock server mutex\n", inThreadId );

	MUTEX_LOCK ( m_oMutex );

	//fprintf ( stderr, "##### [%d] Got lock on server mutex\n", inThreadId );

	// determine if there are any jobs waiting if this thread should --
	//   if we're running one-thread-per-request and this is the accept thread
	//   we don't look for requests
	HttpRequest * poHttpRequest = NULL;
	if ( m_nServerRunningStatus != SERVERRUNNING_ONETHREADPERREQUEST ||
		 !pthread_equal(inThreadId,m_nAcceptThreadId) ) {

		poHttpRequest = GetNextRequest ( );

	}

	// if we got a request to handle
	if ( poHttpRequest != NULL ) {

		//fprintf ( stderr, "##### [%d] Got a request to handle\n", inThreadId );

		// handle the request and post it back to the connection object
		MUTEX_UNLOCK ( m_oMutex );

		// route the request
		HttpResponse * poHttpResponse = 
			m_poTopLevelEHS->RouteRequest ( poHttpRequest );

		// add the response to the appropriate connection's response list
		poHttpResponse->m_poEHSConnection->AddResponse ( poHttpResponse );

		delete poHttpRequest;

	} 
	// otherwise, no requests are pending
	else {

		// if something is already accepting, sleep
		if ( m_nAccepting ) {
			
			// wait until something happens
			// it's ok to not recheck our condition here, as we'll come back in the same way and recheck then
			//fprintf ( stderr, "##### [%d] Sleeping because no requests and someone else is accepting\n", inThreadId );

			pthread_cond_wait ( & m_oDoneAccepting,
								& m_oMutex );			

			MUTEX_UNLOCK ( m_oMutex );

		} 
		// if no one is accepting, we accept
		else {

			m_nAcceptedNewConnection = 0;
	
            
			
			//fprintf ( stderr, "Accepting\n" );

			// we're now accepting
			m_nAccepting = 1;

			MUTEX_UNLOCK ( m_oMutex );

			// create the FD set for poll
			CreateFdSet();
			int nSocketCount = poll( m_oReadFds.GetFdArray(), m_oReadFds.GetFdCount(), inTimeoutMilliseconds );

			// handle select error
#ifdef _WIN32
			if ( nSocketCount == SOCKET_ERROR )
#else // NOT _WIN32
			if ( nSocketCount == -1 )
#endif // _WIN32
			{

				EHS_TRACE ( "[%d] Critical Error: select() failed.  Aborting\n", inThreadId );

				// Idea! Remove stupid idea
				//exit ( 0 );
			}
			
			
			
			// if no sockets have data to read, clear accepting flag and return
			if ( nSocketCount > 0 ) {

				// Check the accept socket for a new connection
				CheckAcceptSocket ( );

				// check client sockets for data
				CheckClientSockets ( );

			}

			MUTEX_LOCK ( m_oMutex );
			ClearIdleConnections ( );
			m_nAccepting = 0;
			MUTEX_UNLOCK ( m_oMutex );

            // Occasional pulse for updating of things
            m_poTopLevelEHS->HttpPulse ();

		} // END ACCEPTING
		
	} // END NO REQUESTS PENDING


    MUTEX_LOCK ( m_oMutex );

    // Delete unused connections after all threads have been synced
    EHSConnectionList :: iterator iter = m_oEHSConnectionUnusedList.begin ();
    while ( iter != m_oEHSConnectionUnusedList.end () )
    {
        EHSConnection* pConnection = *iter;
        // Delete it after all threads are past syncing point
        if ( pConnection->m_UnusedSyncId < m_ThreadsSyncPoint.GetSyncId () - 1 )
        {
            iter = m_oEHSConnectionUnusedList.erase ( iter );
            delete pConnection;
        }
        else
            iter++;
    }

    MUTEX_UNLOCK ( m_oMutex );

}
예제 #8
0
파일: ehs.cpp 프로젝트: Audifire/mtasa-blue
HttpRequest * EHSServer::GetNextRequest ( )
{

	// don't lock because it's only called from within locked sections

	HttpRequest * poNextRequest = NULL;

	// pick a random connection if the list isn't empty
	if ( ! m_oEHSConnectionList.empty ( ) ) {
		
		// pick a random connection, so no one takes too much time
		int nWhich = (int) ( ( (double) m_oEHSConnectionList.size ( ) ) * rand ( ) / ( RAND_MAX + 1.0 ) );
		
		// go to that element
		EHSConnectionList::iterator i = m_oEHSConnectionList.begin ( );
		int nCounter = 0;
		
		for ( nCounter = 0; nCounter < nWhich; nCounter++ ) {
			i++;
		}
		
		// now get the next available request treating the list as circular
		
		EHSConnectionList::iterator iStartPoint = i;
		int nFirstTime = 1;
		while ( poNextRequest == NULL &&
				!( iStartPoint == i && nFirstTime == 0 ) ) {
			
			// check this one to see if it has anything
			poNextRequest = (*i)->GetNextRequest ( );

			
			i++;
			
			if ( i == m_oEHSConnectionList.end ( ) ) {
				i = m_oEHSConnectionList.begin ( );
			}
			
			nFirstTime = 0;
			
			// decrement the number of pending requests
			if ( poNextRequest != NULL ) {

				m_nRequestsPending--;

			}

		}

	}


	if ( poNextRequest == NULL ) {
//		EHS_TRACE ( "No request found\n" );
	} else {
		EHS_TRACE ( "Found request\n" );
	}


	return poNextRequest;

}
예제 #9
0
파일: ehs.cpp 프로젝트: Audifire/mtasa-blue
EHSServer::EHSServer ( EHS * ipoTopLevelEHS ///< pointer to top-level EHS for request routing
	) :
	m_nServerRunningStatus ( SERVERRUNNING_NOTRUNNING ),
	m_poTopLevelEHS ( ipoTopLevelEHS ),
	m_nRequestsPending ( 0 ),
	m_nIdleTimeout ( 15 )
{
	// you HAVE to specify a top-level EHS object
	//  compare against NULL for 64-bit systems
	assert ( m_poTopLevelEHS != NULL );

	
	MUTEX_SETUP ( m_oMutex );
	pthread_cond_init ( & m_oDoneAccepting, NULL );

	m_nAccepting = 0;
    m_iActiveThreadCount = 0;

	// grab out the parameters for less typing later on
	EHSServerParameters & roEHSServerParameters = 
		ipoTopLevelEHS->m_oEHSServerParameters;

	// whether to run with https support
	int nHttps = roEHSServerParameters [ "https" ];

#ifdef EHS_MEMORY
	fprintf ( stderr, "[EHS_MEMORY] Allocated: EHSServer\n" );
#endif		


	if ( nHttps ) {
		EHS_TRACE ( "EHSServer running in HTTPS mode\n");
	} else {
		EHS_TRACE ( "EHSServer running in plain-text mode (no HTTPS)\n");
	}

	

	// are we using secure sockets?
	if ( !nHttps ) {
		m_poNetworkAbstraction = new Socket ( );
	} else {

#ifdef COMPILE_WITH_SSL		


		EHS_TRACE ( "Trying to create secure socket with certificate='%s' and passphrase='%s'\n",
					(const char*)roEHSServerParameters [ "certificate" ],
					(const char*)roEHSServerParameters [ "passphrase" ] );

		
		SecureSocket * poSecureSocket = 
			new SecureSocket ( roEHSServerParameters [ "certificate" ],
							   roEHSServerParameters [ "passphrase" ] );
		
		// HIGHLY EXPERIMENTAL
		// scary shit
		EHS_TRACE ( "Thinking about loading callback - '%s'\n", 
					roEHSServerParameters [ "passphrasecallback" ].
					GetCharString ( ) );
		

		if ( roEHSServerParameters [ "passphrasecallback" ] != "" &&
			 dlsym ( RTLD_DEFAULT, 
					 roEHSServerParameters [ "passphrasecallback" ] ) == 
			 NULL ) {

			EHS_TRACE ( "Couldn't load symbol for '%s' -- make sure you extern \"C\" the function\n",
						roEHSServerParameters [ "passphrasecallback" ].
						GetCharString ( ) );
		}



		EHS_TRACE ( "done thinking about loading callback\n" );



		
		poSecureSocket->SetPassphraseCallback (  (int ( * ) ( char *, int, int, void * ))
												 dlsym ( RTLD_DEFAULT,
														 roEHSServerParameters [ "passphrasecallback" ] ) );
		// END EXPERIMENTAL
		
		m_poNetworkAbstraction = poSecureSocket;
#else // COMPILE_WITH_SSL
		fprintf ( stderr, "EHS not compiled with SSL support.  Cannot create HTTPS server.  Aborting\n" );
		exit ( 1 );
#endif // COMPILE_WITH_SSL



	}
	

	// initialize the socket
	assert ( m_poNetworkAbstraction != NULL );
	int nResult = m_poNetworkAbstraction->Init ( roEHSServerParameters [ "bindip" ], roEHSServerParameters [ "port" ] ); // initialize socket stuff

	

	
	if ( nResult != NetworkAbstraction::INITSOCKET_SUCCESS ) {

		EHS_TRACE ( "Error: Failed to initialize sockets\n" );

		return;
	}


	if ( roEHSServerParameters [ "mode" ] == "threadpool" ) {

		// need to set this here because the thread will check this to make
		//   sure it's supposed to keep running
		m_nServerRunningStatus = SERVERRUNNING_THREADPOOL;

		// create a pthread 
		int nResult = -1;

		EHS_TRACE ( "Starting %d threads\n", roEHSServerParameters [ "threadcount" ].GetInt ( ) );


		int nThreadsToStart = roEHSServerParameters [ "threadcount" ].GetInt ( );
		if ( nThreadsToStart == 0 ) {
			nThreadsToStart = 1;
		}

		for ( int i = 0; i < nThreadsToStart; i++ ) {

			EHS_TRACE ( "creating thread with %x, NULL, %x, %x\n",
					  &m_nAcceptThreadId,
					  EHSServer::PthreadHandleData_ThreadedStub,
					  (void *) this );

			// create new thread and detach so we don't have to join on it
			nResult = 
				pthread_create ( &m_nAcceptThreadId,
								 NULL,
								 EHSServer::PthreadHandleData_ThreadedStub,
								 (void *) this );
			pthread_detach ( m_nAcceptThreadId );

		}

		if ( nResult != 0 ) {
			m_nServerRunningStatus = SERVERRUNNING_NOTRUNNING;
		}


	} else if ( roEHSServerParameters [ "mode" ] == "onethreadperrequest" ) {

		m_nServerRunningStatus = SERVERRUNNING_ONETHREADPERREQUEST;

		// spawn off one thread just to deal with basic stuff
		nResult = pthread_create ( &m_nAcceptThreadId,
								   NULL,
								   EHSServer::PthreadHandleData_ThreadedStub,
								   (void *) this );
		pthread_detach ( m_nAcceptThreadId );
	
		// check to make sure the thread was created properly
		if ( nResult != 0 ) {
			m_nServerRunningStatus = SERVERRUNNING_NOTRUNNING;
		}

	} else if ( roEHSServerParameters [ "mode" ] == "singlethreaded" ) {

		// we're single threaded
		m_nServerRunningStatus = SERVERRUNNING_SINGLETHREADED;

	} else {

		EHS_TRACE ( "INVALID 'mode' SPECIFIED.\ntMust be 'singlethreaded', 'threadpool', or 'onethreadperrequest'\n" );

		assert ( 0 );
	}


	if ( m_nServerRunningStatus == SERVERRUNNING_THREADPOOL ) {
		EHS_TRACE ( "Info: EHS Server running in dedicated thread mode with '%s' threads\n",
				  roEHSServerParameters [ "threadcount" ] == "" ? 
				  roEHSServerParameters [ "threadcount" ].GetCharString ( ) : "1" );
	} else if ( m_nServerRunningStatus == SERVERRUNNING_ONETHREADPERREQUEST ) {
		EHS_TRACE ( "Info: EHS Server running with one thread per request\n" );
	} else if ( m_nServerRunningStatus == SERVERRUNNING_SINGLETHREADED ) {
		EHS_TRACE ( "Info: EHS Server running in non-dedicated thread mode\n" );
	} else {
		EHS_TRACE ( "Error: EHS Server not running.  Server initialization failed\n" );
	}

	return;


}
예제 #10
0
파일: ehs.cpp 프로젝트: Audifire/mtasa-blue
void EHSServer::CheckClientSockets ( )
{
    MUTEX_LOCK ( m_oMutex );

	// go through all the sockets from which we're still reading
	for ( EHSConnectionList::iterator i = m_oEHSConnectionList.begin ( );
		  i != m_oEHSConnectionList.end ( );
		  i++ ) {

		if ( m_oReadFds.IsSet( (*i)->GetNetworkAbstraction()->GetFd(), POLLIN ) ) {

            if ( MUTEX_TRY_LOCK ( (*i)->m_oConnectionMutex ) == false )
                continue;

			EHS_TRACE ( "$$$$$ Got data on client connection\n" );


			// prepare a buffer for the read
			static const int BYTES_TO_READ_AT_A_TIME = 10240;
			char psReadBuffer [ BYTES_TO_READ_AT_A_TIME + 1 ];
			memset ( psReadBuffer, 0, BYTES_TO_READ_AT_A_TIME + 1 );

			// do the actual read
			int nBytesReceived =
				(*i)->GetNetworkAbstraction ( )->Read ( psReadBuffer,
														BYTES_TO_READ_AT_A_TIME );

            if ( nBytesReceived == SOCKET_ERROR )
            {
                int err = GetLastSocketError ();
                if ( err == E_WOULDBLOCK )
                {
                    MUTEX_UNLOCK ( (*i)->m_oConnectionMutex );
                    continue;
                }
            }
			// if we received a disconnect
			if ( nBytesReceived <= 0 ) {

				// we're done reading and we received a disconnect
				(*i)->DoneReading ( true );

			}
			// otherwise we got data
			else {

				// take the data we got and append to the connection's buffer
				EHSConnection::AddBufferResult nAddBufferResult = 
					(*i)->AddBuffer ( psReadBuffer, nBytesReceived );

				// if add buffer failed, don't read from this connection anymore
				if ( nAddBufferResult == EHSConnection::ADDBUFFER_INVALIDREQUEST ||
					 nAddBufferResult == EHSConnection::ADDBUFFER_TOOBIG ) {

					// done reading but did not receieve disconnect
					EHS_TRACE ( "Done reading because we got a bad request\n" );
					(*i)->DoneReading ( false );

				} // end error with AddBuffer

			} // end nBytesReceived

            MUTEX_UNLOCK ( (*i)->m_oConnectionMutex );
		} // FD_ISSET
		
	} // for loop through connections
    MUTEX_UNLOCK ( m_oMutex );

}