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

📄 wincgi.cpp

📁 mini http server,可以集成嵌入到程序中,实现简单的web功能
💻 CPP
📖 第 1 页 / 共 5 页
字号:
	--- */
	bool WaitForChild( const char *pszCmdLine, PIHTTP &tPIHTTP,
		PROCDESC tProcess )
		{
		int iRet = PIPlatform_waitForProcess( (int)tProcess, 0, iKillAfter );
		switch( iRet )
			{
			case PIAPI_COMPLETED:	return true;
			case PIAPI_TIMEOUT:
				HTTPCore_logError( &tPIHTTP,  "WinCGI: CGI \
process did \
not terminate after %d seconds.\nCGI program command line is '%s'.\n\
The process will be terminated.", iKillAfter, pszCmdLine );
#	if WIN32
				::TerminateProcess( tProcess, (UINT)-1 );
#	elif POSIX
				::kill( (pid_t)tProcess, SIGTERM );
#	endif
				return false;

			default:
				HTTPCore_logError( &tPIHTTP,  "WinCGI: Error waiting for \
CGI process to terminate.\nCGI program command line is '%s'",
					pszCmdLine );
				return false;
			};
		}

	/* ---
	Write output from one buffer into another. Transfer data until no more
	data can be read.
	Returns 0 on success.
	--- */
	int WriteBufferToBuffer( PIIOBuffer &tInBuffer, PIIOBuffer &tOutBuffer )
		{
		int iLen;
		const char *pBuffer = PIIOBuffer_read( &tInBuffer, &iLen );
		while( pBuffer )
			{
			if ( PIIOBuffer_write( &tOutBuffer, pBuffer, iLen, PIIOBUF_NONE )
				<=0 )
				{
				assert( 0 );
				return -1;
				};
			pBuffer = PIIOBuffer_read( &tInBuffer, &iLen );
			};
		return 0;
		};

	/* ---
	Write output from one buffer into another.
	iReadLen gives number of bytes to transfer, must be > 0
	Returns 0 on success.
	--- */
	int WriteBufferToBuffer( PIIOBuffer &tInBuffer, PIIOBuffer &tOutBuffer,
		int iReadLen, PIString &sResult )
		{
		assert( iReadLen>0 );
		if ( iReadLen<=0 )
			{ return 0; };
		enum { BUF_SIZE=4096 };
		char szBuffer[BUF_SIZE];
		int iAttemptToRead = (iReadLen >= BUF_SIZE) ? BUF_SIZE : iReadLen;
		int iInKey = 0;
		int iValLen = 0;
		int iStdinLen = iReadLen;

		int iLen;
		iLen = PIIOBuffer_readToBuffer( &tInBuffer, szBuffer, iAttemptToRead );

		while( iLen>0 )
			{
			if ( PIIOBuffer_write( &tOutBuffer, szBuffer, iLen,
				PIIOBUF_NONE )<=0 )
				{
				assert( 0 );
				return -1;
				};

		/* --- Scan the buffer for [Form Huge] literals --- */
		if (iStdinLen > 8192)
			{
			int iScanned = 0;
			enum { KEYSIZE=255 };
			char szKeyBuf[KEYSIZE+1];
			int i;
			for (i=0; i<iLen; i++)
				{
				if (szBuffer[i] == '=')
					{
					strncpy(szKeyBuf, &szBuffer[iScanned], i-iScanned+1);
					szKeyBuf[i-iScanned+1]='\0';
					sResult.Concatenate(szKeyBuf);
					sprintf( szKeyBuf, "%lu", iStdinLen-iReadLen+i+1 );
					sResult.Concatenate( szKeyBuf );
					sResult.Concatenate(' ');
					iScanned = i;
					iInKey = 0;
					}
				if (szBuffer[i] == '&')
					{
					sprintf( szKeyBuf, "%lu", iValLen+i-iScanned-1 );
					sResult.Concatenate( szKeyBuf );
#if WIN32
					sResult.Concatenate("\r\n");
#else
					sResult.Concatenate("\n");
#endif
					iScanned = i+1;
					iInKey = 1;
					iValLen = 0;
					}
				}
				if (iInKey)
					{
					strncpy(szKeyBuf, &szBuffer[iScanned], i-iScanned+1);
					szKeyBuf[i-iScanned+1]='\0';
					sResult.Concatenate(szKeyBuf);
					}
				else
					{
					if (iReadLen - iLen)
						{
						iValLen += i-iScanned;
						}
					else
						{
						sprintf( szKeyBuf, "%lu", iValLen+i-iScanned-1 );
						sResult.Concatenate( szKeyBuf );
#if WIN32
					sResult.Concatenate("\r\n");
#else
					sResult.Concatenate("\n");
#endif
						}
					};
			}

			iReadLen -= iLen;
			if ( !iReadLen )
				{
                return 0;
                };

    		iAttemptToRead = (iReadLen >= BUF_SIZE) ? BUF_SIZE : iReadLen;
			iLen = PIIOBuffer_readToBuffer( &tInBuffer, szBuffer,
				iAttemptToRead );
			};

		/* ---
		Done
		--- */
		return iLen;
		};

	/* ---
	Send input data to the CGI program using a copy of the pipe
	IO object. Return non-zero on success, 0 on error.
	--- */
	int SendCGIInput( PIIOBuffer &tIn, PIIOBuffer &tOut, int iLen, PIString &sResult )
		{
		int iRet = 1;	/* initially success */

		/* ---
		Write input to child stdin and close CGI side of pipe
		--- */
		if ( WriteBufferToBuffer( tIn, tOut, iLen, sResult ) )
			{
			iRet = 0;
			goto SendCGIInput_error;
			};

		if ( iSendCRLF )
			{
			/* --- Write CRLF to the CGI program --- */
			if ( PIIOBuffer_writeLn( &tOut, "", 0, PIIOBUF_NONE )<=0 )
				{
				iRet = 0;
				goto SendCGIInput_error;
				};
			};

		/* --- flush --- */
		if ( !PIIOBuffer_flush( &tOut ) )
			{
			iRet = 0;
			goto SendCGIInput_error;
			};

		/* --- done --- */
SendCGIInput_error:
		if ( !iRet )
			{
			HTTPCore_logDebug( DBG_MED, "WinCGI: SendCGIInput() failed." );
			};

		return iRet;
		};

	/* ---
	Execute the CGI program and examine the output.
	--- */
	bool DoExecCGI(
		PIFInfo *pFile,
		PIHTTP &tPIHTTP,
		PROCDESC &tProcess )
		{
		/* ---
		Normally the callee to DoExecCGI would generate an internal redirect
		to handle an error code. The flag bIgnoreStatus can be set to 
		true to tell the callee that a redirect should not happen, because
		the protocol headers have already been sent to the client. This
		happens for full response
		--- */
		bool bIgnoreStatus = false;

		PIDB *pQ = tPIHTTP.pRequestDB;
		PIDB *pR = tPIHTTP.pResponseDB;
		PIDB *pC = tPIHTTP.pConnectionDB;
		PIDB *pH = tPIHTTP.pHostDB;
		PIIOBuffer &tB = *( tPIHTTP.pBuffer );

		/* --- sanity check --- */
		assert( pFile && pQ && pR );
		if ( !( pFile && pQ && pR ) )
			{
			tPIHTTP.iStatus = ST_INTERNALERROR;
			return bIgnoreStatus;
			};

		/* --- 
		Make CGI context data 
		--- */
		CGIContextData tContext;

		/* ---
		Determine whether or not there is any input to be sent to the
		CGI program
		--- */
		const char *pContentLength = (const char *)PIDB_lookup( pQ,
			PIDBTYPE_RFC822, KEY_HTTP_CONTENTLENGTH, 0 );
		int iStdinLen = pContentLength ? atoi( pContentLength ) : 0;
		int iDoStdin = ( iStdinLen > 0 );

		/* ---
		Set filenames 
		--- */
		enum { BUFSIZE=1023 };
		char szFileBuf[BUFSIZE+1];
		if ( pDataFile ) {
			int iLen = Pi3Expression_write( pDataFile, &tPIHTTP, &tContext,
				szFileBuf, BUFSIZE );
			if ( iLen>0 )
				{
				szFileBuf[iLen] = '\0';
				Pi3String *pTmp = Pi3String_new( tContext.sDataFile );
				HTTPCore_relativeToAbsolutePath( pH, szFileBuf, pTmp );
				tContext.sDataFile = Pi3String_getPtr( pTmp );
				Pi3String_delete( pTmp );
				};
			};
		if ( pStdinFile ) {
			int iLen = Pi3Expression_write( pStdinFile, &tPIHTTP, &tContext,
				szFileBuf, BUFSIZE );
			if ( iLen>0 )
				{
				szFileBuf[iLen] = '\0';
				Pi3String *pTmp = Pi3String_new( tContext.sInputFile );
				HTTPCore_relativeToAbsolutePath( pH, szFileBuf, pTmp );
				tContext.sInputFile = Pi3String_getPtr( pTmp );
				Pi3String_delete( pTmp );
				};
			};
		if ( pStdoutFile ) {
			int iLen = Pi3Expression_write( pStdoutFile, &tPIHTTP, &tContext,
				szFileBuf, BUFSIZE );
			if ( iLen>0 )
				{
				szFileBuf[iLen] = '\0';
				Pi3String *pTmp = Pi3String_new( tContext.sOutputFile );
				HTTPCore_relativeToAbsolutePath( pH, szFileBuf, pTmp );
				tContext.sOutputFile = Pi3String_getPtr( pTmp );
				Pi3String_delete( pTmp );

				/* --- mark output file as a temporary file --- */
				PIDB_add( pR, PIDBTYPE_STRING, KEY_INT_TEMPORARYFILE,
					(void *)(const char *)tContext.sOutputFile, 0 );
				};
			};

		/* ---
		See if this is an 'ISINDEX' query, and if it is, set the
		ISINDEX value appropriately

		An ISINDEX query will not have a '=' encoded in the query string.
		--- */
		const char *pQS = (const char *)PIDB_lookup( pQ, PIDBTYPE_STRING,
			KEY_HTTP_QUERYSTRING, 0 );
		Pi3String *pEncodedQS = Pi3String_new( pQS );
		Pi3String *pDecodedQS = Pi3String_new( 0 );
		HTTPUtil_urlDecode( pEncodedQS, pDecodedQS );
		const char *pDQS = Pi3String_getPtr( pDecodedQS );
		int i;
		for( i=0; pDQS[i] && pDQS[i]!='='; i++ );
		if ( !pDQS[i] )
			{
			/* --- '=' not found, therefore this is an ISINDEX query --- */
		
			/* ---
			Only include up to the first punctuation character, to
			keep out 'special' parameters being passed to exec(). Thi
			is a security message
			--- */
			for( i=0; pDQS[i] && !ispunct(pDQS[i]); i++ );
			PIString sTmp( pDQS, i );
			tContext.sIsIndexValue = " ";
			tContext.sIsIndexValue.Concatenate( sTmp );
			};
		Pi3String_delete( pEncodedQS );
		Pi3String_delete( pDecodedQS );

		/* ---
		Write extra headers to context
		--- */
		if ( iExtraHeaders )
			{
			switch (AddClientRequestHeaders( &tPIHTTP, pQ, szFileBuf, BUFSIZE+1 )) {
				case 0:
					tContext.sExtraHeaders = szFileBuf;
				    break;
				case -1:
					HTTPCore_logError( &tPIHTTP,  
						"WinCGI: Internal error getting data of extra request headers." );
				    tPIHTTP.iStatus = ST_INTERNALERROR;
				    return bIgnoreStatus;
				case 1:
					HTTPCore_logError( &tPIHTTP,  
						"WinCGI: Buffer exhausted when getting data of extra request headers" );
				    tPIHTTP.iStatus = ST_INTERNALERROR;
				    return bIgnoreStatus;
				}
			};

		/* ---
		Write accept header to context
		--- */
		if ( iExtraHeaders )
			{
			switch (AddClientAcceptHeader( &tPIHTTP, pQ, szFileBuf, BUFSIZE+1 )) {
				case 0:
					tContext.sAcceptHeader = szFileBuf;
				    break;
				case -1:
					HTTPCore_logError( &tPIHTTP,  
						"WinCGI: Internal error getting data of accept header." );
				    tPIHTTP.iStatus = ST_INTERNALERROR;
				    return bIgnoreStatus;
				case 1:
					HTTPCore_logError( &tPIHTTP, 
						"WinCGI: Buffer exhausted when getting data of accept header." );
				    tPIHTTP.iStatus = ST_INTERNALERROR;
				    return bIgnoreStatus;
				}
			};

		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;
				};
			};
	
		/* ---
		Write input to input file
		--- */
		PIPLATFORM_FD tFd = PIFile_open( tContext.sInputFile, "w" );

		if ( tFd==PIPLATFORM_FD_INVALID )
			{
			HTTPCore_logError(  &tPIHTTP, 
				"WinCGI: Failed to open input file, path is '%s'.",
				((const char *)tContext.sInputFile) );
			tPIHTTP.iStatus = ST_INTERNALERROR;
			return bIgnoreStatus;
			};

		/* --- mark input file as a temporary file --- */
		PIDB_add( pR, PIDBTYPE_STRING, KEY_INT_TEMPORARYFILE,
			(void *)(const char *)tContext.sInputFile, 0 );

		if ( iDoStdin && iStdinLen>0 )
			{
			const char *pArgs[3];
			pArgs[0] = (const char *)-1;
			pArgs[1] = (const char *)tFd;
			pArgs[2] = (const char *)0;     /* handle is a pipe - no? */
			PIObject *pChildIO = PIObject_copy( pPipeIO, 3, pArgs );
			PIIOBuffer *pCGIBuffer = 0;
			int iResult = 0;
			if ( pChildIO )
				{
				pCGIBuffer = PIIOBuffer_new( pChildIO );
				iResult = SendCGIInput( tB, *pCGIBuffer, iStdinLen, tContext.sFormHuge );

				if ( !iResult )
					{
					HTTPCore_logError(  &tPIHTTP, 
						"WinCGI: Failed to write input file, path is '%s'.",
						((const char *)tContext.sInputFile) );
					tPIHTTP.iStatus = ST_INTERNALERROR;
					PIIOBuffer_delete( pCGIBuffer );
					PIObject_delete( pChildIO, 0, 0 );
					PIFile_close( tFd );
					return bIgnoreStatus;
					};

				/*
				** Tricky! The flushed buffer points to the begin of the old data
				** (We expect a flushed buffer from SendCGIInput)
				**
				** We currently support only up to 8192 Bytes (PIIOBuffer size)
				** for [Form Literal] or [Form External], then [Form Huge] has been
				** used by SendCGIInput() already!
				*/
				char *pBuf;
				if ( iStdinLen <= PIIOBuffer_getOutputBuffer( pCGIBuffer, &pBuf ))
					{
					/* ---
					Write accept header to context
					--- */
					if (AddClientFormData( &tPIHTTP, pQ, pBuf, iStdinLen,
						tContext.sFormLiteral, tContext.sFormExternal))
						{
						HTTPCore_logError( &tPIHTTP,  
							"WinCGI: Internal error getting data of request form literals." );
						tPIHTTP.iStatus = ST_INTERNALERROR;

⌨️ 快捷键说明

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