⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cgi.cpp

📁 mini http server,可以集成嵌入到程序中,实现简单的web功能
💻 CPP
📖 第 1 页 / 共 4 页
字号:
			PIString sTmp( pDQS, i );
			tContext.sIsIndexValue = " ";
			tContext.sIsIndexValue.Concatenate( sTmp );
			};
		Pi3String_delete( pEncodedQS );
		Pi3String_delete( pDecodedQS );

		FILEDESC tStdout_Parent = INVALID_DESC;
		FILEDESC tStdout_CGI = INVALID_DESC;
		FILEDESC tStdin_Parent = INVALID_DESC;
		FILEDESC tStdin_CGI = INVALID_DESC;
		FILEDESC tStderr_Parent = INVALID_DESC;
		FILEDESC tStderr_CGI = INVALID_DESC;
		/*
		** Define a macro to make sure these handles are all closed
		** if we return in the middle of these functions
		*/
#define CLOSE_ALL_HANDLES							\
		{										\
		CloseIfValidDesc( tStdin_Parent );		\
		CloseIfValidDesc( tStdin_CGI ); 		\
		CloseIfValidDesc( tStdout_Parent ); 	\
		CloseIfValidDesc( tStdout_CGI );		\
		CloseIfValidDesc( tStderr_Parent ); 	\
		CloseIfValidDesc( tStderr_CGI ); 		\
		}
		
		tContext.sExePath = PIFInfo_getPath( pFile );
		const char *pDirectory = PIFInfo_getPathRoot( pFile );

		/* ---
		Find the appropriate command line pattern 
		--- */
		Pi3Expression *pCommandPattern = pDefaultCommandLine;
		const char *pExtn = PIFInfo_getExtension( pFile );
		for( DblListIterator k( lCommandLineByExt ); !k.BadIndex(); k++ )
			{
			FileExtToCommandLineMap *pMap =
					(FileExtToCommandLineMap *)k.Current();
			if ( pMap->sFileExtension==pExtn ) 
				{
				pCommandPattern = pMap->pExpr;
				break;
				};
			};
	
		/* ---
		Generate command line pattern
		--- */
		enum { CMD_BUF_SIZE=255 };/* --- this will fit most command lines --- */
		char szCmdBuf[CMD_BUF_SIZE+1];
		char *pCommandLine = szCmdBuf;
		int iLen = Pi3Expression_write( pCommandPattern, &tPIHTTP, &tContext,
			pCommandLine, CMD_BUF_SIZE );
		szCmdBuf[CMD_BUF_SIZE] = '\0';
		if ( iLen>CMD_BUF_SIZE )
			{
			/* --- automatic buffer not big enough, allocate a new one --- */
			pCommandLine = (char *)PIHTTP_allocMem( &tPIHTTP, iLen+1 );
			Pi3Expression_write( pCommandPattern, &tPIHTTP, &tContext, 
				pCommandLine, iLen );
			Pi3Expression_write( pCommandPattern, &tPIHTTP, &tContext,
				pCommandLine, iLen );
			};
		pCommandLine[iLen] = '\0';

#if VERBOSE_DEBUG
		cerr << "Executing commandline: '" << pCommandLine << "'" << endl;
#endif
		int iRCode = DoExecCGIChild( 
				pCommandLine, 
				pDirectory,
				&tProcess, 
				iDoStdin,
				&tStdout_Parent,
				&tStdout_CGI,
				&tStdin_Parent,
				&tStdin_CGI,
				&tStderr_Parent,
				&tStderr_CGI,
				tPIHTTP
#if VERBOSE_DEBUG
				, cerr
#endif
				);

		/* ---
		Error in CGI program
		--- */
		if ( iRCode )
			{
			HTTPCore_logError( &tPIHTTP,  
				"CGI: DoExecCGIChild() failed. Error code is %d. \
Start of command line is '%s'.", iRCode, szCmdBuf );
			tPIHTTP.iStatus = ST_INTERNALERROR;
			CLOSE_ALL_HANDLES;
			return bIgnoreStatus;
			};

		/*
		** pError serves as error flag
		*/
		int iRet = 0;
		const char *pError = 0;
		const char *pArgs[3];
		PIObject *pChildIO = 0;
		PIIOBuffer *pCGIBuffer = 0;

		/* ---
		Make a clone of the prototype pipe IO object for communication
		with this CGI process
		--- */
		pArgs[0] = (const char *)tStdout_Parent;
		pArgs[1] = (const char *)tStdin_Parent;
		pArgs[2] = (const char *)1;         /* handle is a pipe? - yes */
		pChildIO = PIObject_copy( pPipeIO, 3, pArgs );
		if ( !pChildIO )
			{
			HTTPCore_logError( &tPIHTTP,  
				"CGI: Failed to open communication channel to CGI \
program." );
			tPIHTTP.iStatus = ST_INTERNALERROR;
			CLOSE_ALL_HANDLES;
			return bIgnoreStatus;
			};

		/* ---
		Make CGI buffer object
		--- */
		pCGIBuffer = PIIOBuffer_new( pChildIO );
		assert( pCGIBuffer );

		/* ---
		Write input to child stdin and close CGI side of pipe
		--- */
		CloseIfValidDesc( tStdin_CGI );
		if ( iDoStdin && iStdinLen>0 )
			{
			HTTPCore_logDebug( DBG_MED, "Sending input to CGI, length=%d",
				iStdinLen );
			if ( !SendCGIInput( tB, *pCGIBuffer, iStdinLen ) )
				{
				pError = "Error sending input to CGI child program";
				};
			};

		CloseIfValidDesc( tStdin_Parent );

		/* ---
		Close CGI side of pipe
		--- */
		CloseFileDesc( tStdout_CGI );

		/* ---
		Read and discard stderr, write this CGI error to server error file
		--- */
		enum { BUF_SIZE=2047 };
		char szBuf[BUF_SIZE+1];

		/* ---	
		Read CGI request headers
		--- */
		if ( !pError && !iRet )
			{
			if ( HTTPCore_readHeaders( pCGIBuffer, pR, RH_RESPONSE ) )
				{ pError = "Error reading response headers from CGI program"; };
			};

		if ( pError )
			{
			if ( *pError )
				{
				HTTPCore_logError( &tPIHTTP, "CGI: %s.\nStart of command line \
is '%s'.", pError, szCmdBuf );
				}
			else
				{
				HTTPCore_logError( &tPIHTTP, "CGI: Start of command line \
is '%s'.", szCmdBuf );
				};
			iRet = -1;
			};
#if VERBOSE_DEBUG
		cerr << dec << iRet << endl;
#endif
		
		/* ---
		Check the type of the response
			- Partial (usual case)
			- Full
			- or redirect
		--- */
		const char *pCLF = 0;
		const char *pLocation = 0;
		if ( !iRet && ( pCLF = (const char *)
			PIDB_lookup( pR, PIDBTYPE_STRING, KEY_HTTP_CLF, 0 ) ) ) 
			{

			/* ++++ __________________________ +++ *
				Full Response (Non-Parsed Headers)
			 * ++++ __________________________ +++ */

			/* --- grap the status code sent by the CGI --- */
			int i=0;
			for(; pCLF[i] && !(isspace(pCLF[i])); i++); /* advance to space */
			for(; pCLF[i] && (isspace(pCLF[i])); i++); /* advance past space */
			tPIHTTP.iStatus = atoi( &(pCLF[i]) );

			/* ---
			disable keep open, hope the CGI sent the appropriate 
			'Connection' headers for this HTTP version
			--- */
			PIDB_replace( tPIHTTP.pConnectionDB, PIDBTYPE_OPAQUE, 
				KEY_INT_KEEPOPEN, (void *)0, 0 );

			/* --- send status header from CGI --- */
			PIIOBuffer_writeLn( &tB, pCLF, -1, PIIOBUF_NONE );

			/* --- loop over RFC822 headers to send them --- */
			PIDBIterator *pIter = PIDB_getIterator( pR, PIDBTYPE_RFC822, 0, 0 );
			for( ;
				PIDBIterator_atValidElement( pIter );
				PIDBIterator_next( pIter ) )
				{
				const char *pHeader;
				const char *pContent = (const char *)
					PIDBIterator_current( pIter, &pHeader );
				PIIOBuffer_write( &tB, pHeader, -1, PIIOBUF_NONE );
				PIIOBuffer_write( &tB, ": ", 2, PIIOBUF_NONE );
				PIIOBuffer_writeLn( &tB, pContent, -1, PIIOBUF_NONE );
				};
			PIDBIterator_delete( pIter );
			
			/* --- empty CRLF --- */
			PIIOBuffer_writeLn( &tB, "", 0, PIIOBUF_NONE );

			/* ---
			for a full reponse no REDIRECT action should be
			taken based on the status code, as the CGI program is handling
			the status itself
			--- */
			bIgnoreStatus = true;

			/* --- continue to allow the content-body to be sent --- */
			}
		else if ( !iRet && (pLocation=(const char *)
			PIDB_lookup( pR, PIDBTYPE_RFC822, KEY_HTTP_LOCATION, 0 ) ) )
			{

			/* ++++ __________________________ +++ *
					  Location Response
			 * ++++ __________________________ +++ */

			/* --- determine if this is a URL path or a URI --- */
			if ( *pLocation=='/' )
				{
				/* ---
				URL path, expand to full URI and replace in Location
				--- */
				PIOStrStream ostr;
				if ( PIDB_lookup( pC, PIDBTYPE_STRING, KEY_HTTPS_KEYSIZE, 0 ) )
					{
					/*
					** Running under SSL
					*/
					ostr << "https://";
					}
				else
					{
					/*
					** Not running under SSL
					*/
					ostr << "http://";
					};
				const char *pHostName = HTTPUtil_getHostName( &tPIHTTP );
				if ( pHostName )
					{ ostr << pHostName; };
				const char *pPort = HTTPUtil_getHostPort( &tPIHTTP );
				if ( pPort )
					{ ostr << ':' << pPort; };

				ostr << pLocation;
				ostr << ends;

				PIDB_replace( pR, PIDBTYPE_RFC822, KEY_HTTP_LOCATION,
						(void *)ostr.str(), 0 );
				};

			/* --- change status to permanent redirect --- */
			tPIHTTP.iStatus = ST_PERMANENTREDIRECT;

			/* ---
			Has the CGI program sent any content ?
			--- */
			if ( !PIIOBuffer_pollBeforeRead( pCGIBuffer ) )
				{
				/* --- 
				No content 
				--- */
				if ( pCGIBuffer )
					{ PIIOBuffer_delete( pCGIBuffer ); };
				PIObject_delete( pChildIO, 0, 0 );
				CLOSE_ALL_HANDLES;
				return bIgnoreStatus;
				};

			/* ---
			for a location reponse with no content no REDIRECT action should be
			taken based on the status code, as the CGI program is handling
			the content itself
			--- */
			bIgnoreStatus = true;

			/* ---
			start to send the response and allow the 
			output from the CGI to be sent to the client as entity 
			body for the request.
			--- */
			PIDB_replace( tPIHTTP.pConnectionDB, PIDBTYPE_OPAQUE, 
				KEY_INT_KEEPOPEN, (void *)0, 0 );
			if (	HTTPCore_sendGeneralHeaders( &tPIHTTP ) ||
					HTTPCore_sendEntityHeaders( &tPIHTTP, pR ) ) 
				{ iRet = -1; };
			}
		else if ( !iRet )
			{

			/* ++++ __________________________ +++ *
			  Partial Response (aka Parsed Headers)
			 * ++++ __________________________ +++ */

			/* ---
				Make sure that at least 'Content-Type' exists.
			--- */
			if ( !PIDB_lookup( pR, PIDBTYPE_RFC822, KEY_HTTP_CONTENTTYPE, 0 ))
				{
				HTTPCore_logError( &tPIHTTP, "CGI: CGI program \
did not provide 'Content-Type' header. Start of command line is '%s'.", szCmdBuf );
				iRet = -1;

				/* ---
				if 'Status' header exists,
				then set the status appropriately
				--- */
				const char *pStatus = (const char *)PIDB_lookup( pR,
					PIDBTYPE_RFC822, KEY_HTTP_STATUS, 0 );
				if ( pStatus )
					{
					tPIHTTP.iStatus = atoi( pStatus );
					};	
				}
			else
				{
				/* --- start the response --- */
				tPIHTTP.iStatus = ST_OK;

				PIDB_replace( tPIHTTP.pConnectionDB, PIDBTYPE_OPAQUE, 
					KEY_INT_KEEPOPEN, (void *)0, 0 );
				if (	HTTPCore_sendGeneralHeaders( &tPIHTTP ) ||
						HTTPCore_sendEntityHeaders( &tPIHTTP, pR ) ) 
					{ iRet = -1; };
				};
			};

		if ( !iRet )
			{ iRet = WriteBufferToBuffer( *pCGIBuffer, tB ); };
		if ( pCGIBuffer )
			{ PIIOBuffer_delete( pCGIBuffer ); };


#if VERBOSE_DEBUG
		cerr << dec << iRet << endl;
#endif
		CloseIfValidDesc( tStdout_Parent );

		/* ---
		Swallow up stderr stream from CGI child
		--- */
		CloseFileDesc( tStderr_CGI );
		for(;;)
			{
			int iRead;

			int iRet = PIPlatform_pollPipeFD(
					(PIPLATFORM_FD)tStderr_Parent, PIPLATFORM_POLL_READ, 0 );

			if ( iRet != PIPLATFORM_POLL_READ )
				{
				if (iRet == PIAPI_ERROR)
					{
					iRet = PIPlatform_getLastError();
					if ((iRet != PIAPI_NOTSUPPORTED) && (iRet != PIAPI_EINVAL))
						{ break; };
					}
				else
					{ break; };
				};

//			if ( iRet!=PIPLATFORM_POLL_READ )
//				{ break; };

#if WIN32
			if ( !::ReadFile( tStderr_Parent, szBuf, BUF_SIZE, 
				(unsigned long *)&iRead, NULL ) || !iRead )
				{ break; };
#elif POSIX
			iRead = ::read( tStderr_Parent, szBuf, BUF_SIZE );
			if ( iRead==-1 && errno==EINTR )
				{ continue; };
			if ( iRead==-1 || !iRead )
				{ break; };
#endif
			szBuf[iRead] = '\0';

			/*
			** Write stderr output into the error logfile
			*/
			HTTPCore_logError( &tPIHTTP, "CGI: Stderr output from \
CGI program follows \n\
______________________________________________________________________________\
__\n%s\n\
______________________________________________________________________________\
__\nCGI program command line is '%s'.", szBuf, szCmdBuf );
			/* --- mark an error --- */

			iRet = -1;
			};
		CloseIfValidDesc( tStderr_Parent );

		PIObject_delete( pChildIO, 0, 0 );

		if ( iRet )
			{
			tPIHTTP.iStatus = ST_INTERNALERROR;
			return bIgnoreStatus;
			};

		/* --- 
		Done!
		--- */
		return bIgnoreStatus;
		};

	/* ---
	Handle request
	--- */
	int Handle( int /* iPhase */, PIHTTP &tPIHTTP, PIIOBuffer &/* tBuffer */ )
		{
		PIDB *pR = tPIHTTP.pResponseDB;

		/* ---
		Get file
		--- */
		const char *pPath = (const char *)PIDB_lookup( pR, PIDBTYPE_STRING,
			pFKPath, PIDBFLAG_FASTKEY );
		PIFInfo *pFile = HTTPCore_getCachedFile( pPath );

		if ( !pFile )
			{
			HTTPCore_releaseCachedFile( pFile );
			HTTPCore_logError( &tPIHTTP, "Could not get file object for \
path '%s'", pPath ? pPath : "" );
			return PIAPI_ERROR;
			};

		/* --- DoExecCGI() --- */
		PROCDESC tProcess = INVALID_DESC; 
		bool bIgnoreStatus = DoExecCGI( pFile, tPIHTTP, tProcess );
		if ( tProcess!=INVALID_DESC )
			{
			/* ---
			Collect the process
			--- */
			if( !WaitForChild( pPath, tPIHTTP, tProcess ) )
				{
				/* --- error message already given --- */
				tPIHTTP.iStatus = ST_INTERNALERROR;
				};
			CloseProcessDesc( tProcess );
			tProcess = INVALID_DESC; 
			};

		HTTPCore_releaseCachedFile( pFile );

		if( bIgnoreStatus )
			{
			return PIAPI_COMPLETED;
			};

		if ( tPIHTTP.iStatus && tPIHTTP.iStatus!=ST_OK )
			{
			return HTTPUtil_doHTTPError( &tPIHTTP, tPIHTTP.iStatus );
			};

		return PIAPI_COMPLETED;
		};
};

/*____________________________________________________________________________*\
 *
 Function:
 Synopsis:
 Description:
\*____________________________________________________________________________*/
PUBLIC_PIAPI int CGI_constructor( PIObject *pObj,
	int iArgc, const char *ppArgv[] )
{
	CGI *pHand = PI_NEW( CGI( pObj, iArgc, ppArgv) );

	HandlerBaseHTTP *pHandBase = (HandlerBaseHTTP *)pHand;
	return HandlerBaseHTTP_constructor(pObj, pHandBase );
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -