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

📄 cgi.cpp

📁 mini http server,可以集成嵌入到程序中,实现简单的web功能
💻 CPP
📖 第 1 页 / 共 4 页
字号:
				{ goto nt_error_return; };
			CloseFileDesc( tStdin_Parent );
			tStdin_Parent = INVALID_DESC;
			};	/* end if ( iDoStdin ) */
		
		/* --- setup pipes (stderr) --- */
		if ( !::CreatePipe( &tStderr_Parent, &tStderr_CGI, &saAttr, 0 ) )
			{ goto nt_error_return; };
		if ( !::DuplicateHandle(
				tProc,
				tStderr_Parent,
				tProc,
				&tStderr_ParentDup,
				0,
				FALSE,
				DUPLICATE_SAME_ACCESS
				) )
			{ goto nt_error_return; };
		CloseFileDesc( tStderr_Parent );
		tStderr_Parent = INVALID_HANDLE_VALUE;
		
		tStartUp.dwFlags |= STARTF_USESTDHANDLES;
		tStartUp.hStdInput = tStdin_CGI;
		tStartUp.hStdOutput = tStdout_CGI;
		tStartUp.hStdError = tStderr_CGI;

		/*
		** Execution flags
		*/
		if ( iFlags & FLG_16BIT )
			{ dwFlags |= CREATE_SEPARATE_WOW_VDM; };

		PROCESS_INFORMATION tProcessInfo;
        ppEnv = CreateCGIEnvironment( tPIHTTP );

		/*
		** complete command line for associations
		*/
		AppName = (char *)PIUtil_malloc(_MAX_PATH); 
		// Set AppName if the script name in pPath is associated
		if ((int)FindExecutable(pPath, NULL, AppName) < 33)
			{
			PIUtil_free(AppName);
			AppName = NULL;
			};

		/* --- spawn the CGI program --- */
		bProcessCreated = CreateProcess(
				AppName,			/* address of module name */
				(char *)pPath,		/* address of command line */
				NULL,				/* address of process security attributes */
				NULL,				/* address of thread security attributes */
				TRUE,               /* new process inherits handles */
				dwFlags,			/* creation flags */
				ppEnv,				/* address of new environment block */
				pDirectory,			/* address of current directory name */
				&tStartUp,			/* address of STARTUPINFO */
				&tProcessInfo		/* address of PROCESS_INFORMATION */
				);

		PIUtil_free(AppName);

		if ( !bProcessCreated )
			{ goto nt_error_return; };

		/* --- log debug information about the process --- */
		HTTPCore_logDebug( DBG_MED, "Process created!, \n\thProcess %p\
\n\thThread %p\n\tdwProcessId %lu\t\n\tdwThreadId %lu",
			tProcessInfo.dwThreadId,
			tProcessInfo.dwProcessId,
			tProcessInfo.hThread,
			tProcessInfo.hProcess );

#	if VERBOSE_DEBUG
		os << "Process created!" << endl;
		os << "	hProcess " << hex << tProcessInfo.hProcess << endl;
		os << "	hThread " << hex << tProcessInfo.hThread << endl;
		os << "	dwProcessId " << hex << tProcessInfo.dwProcessId << endl;
		os << "	dwThreadId " << hex << tProcessInfo.dwThreadId << endl;
#	endif

		/* --- Handle to main thread in new process --- */
		::CloseHandle( tProcessInfo.hThread );

		*pProcess = tProcessInfo.hProcess;
		*pStdout_Parent = tStdout_ParentDup;
		*pStdout_CGI = tStdout_CGI;
		if ( iDoStdin )
			{
			*pStdin_Parent = tStdin_ParentDup;
			*pStdin_CGI = tStdin_CGI;
			};
		*pStderr_Parent = tStderr_ParentDup;
		*pStderr_CGI = tStderr_CGI;

		/* --- OK --- */
		return 0;

nt_error_return:
		/* --- restore handles --- */
		int iTmp = GetLastError();
		goto generic_error_return;

#elif POSIX
		const char **ppCommandArgs=0;
		register int iPid = 0;

		/* --- setup pipes (stdout) --- */
		int aOutFDS[2];
		if ( ::pipe( aOutFDS )==-1 )
			{ goto posix_error_return; };
		tStdout_Parent = aOutFDS[0];
		tStdout_CGI = aOutFDS[1];

		/* --- setup pipes (stderr) --- */
		int aErrFDS[2];
		if ( ::pipe( aErrFDS )==-1 )
			{ goto posix_error_return; };
		tStderr_Parent = aErrFDS[0];
		tStderr_CGI = aErrFDS[1];

		/* --- setup pipes (stdin) --- */
		if ( iDoStdin )
			{
			int aInFDS[2];
			if ( ::pipe( aInFDS )==-1 )
				{ goto posix_error_return; };
			tStdin_CGI = aInFDS[0];
			tStdin_Parent = aInFDS[1];
			};

		ppEnv = CreateCGIEnvironment( tPIHTTP );

		/* ---
		Create command line
		--- */
		{
		/* --- scan once and NULL seperate path components --- */
		int i;
		int j = strlen( pPath );
		int k=0;
		for( i=0; i<j; i++ )
			{
			if ( (isspace(pPath[i])) )
				{
				pPath[i]='\0';
				k++;
				};
			}; 
	
		/* ---
		k contains number of spaces in command line.

		cmd1[sp]cmd2[sp]cmd3[sp]cmd4[0],

			increment k to include command terminated by final null.
			increment k again to allow NULL command at end of array.
		--- */
		k += 2;

		/* --- allocate and assign argument array --- */
		ppCommandArgs = (const char **)PIHTTP_allocMem( &tPIHTTP,
			sizeof( const char * ) * k ); 
		int x = 0;
		const char *pLastArg = 0;
		for( i=0; i<=j; i++ )
			{
			if ( pLastArg==0 )
				{ pLastArg=&(pPath[i]); };
			if ( pPath[i]=='\0' )
				{
				ppCommandArgs[x++] = pLastArg;
				pLastArg = 0;
				};
			};
		ppCommandArgs[x++] = 0;
		assert( x==k );
		};

		/* ---
		Try not to write any data to the processes data space to avoid
		copy-on-write penalty for CGI child process. 
		iPid is declared register so its assignment on fork in the child
		process will not write to the child process data space.
		--- */
#if 0
/*
** NOTE
*/
#define P ( PIThread_getSystemHandle( PIThread_getCurrent() ) )
cerr << P << " 1.." << tStdin_Parent << endl;
cerr << P << " 2.." << tStdin_CGI << endl;
cerr << P << " 3.." << tStdout_Parent << endl;
cerr << P << " 4.." << tStdout_CGI << endl;
cerr << P << " 5.." << tStderr_Parent << endl;
cerr << P << " 6.." << tStderr_CGI << endl;

#endif
		iPid = ::fork();
		switch( iPid )
			{
			case -1:
				break;

			case 0:
				/* ---
				This is the child
				--- */
				CloseFileDesc( tStdout_Parent );
				CloseFileDesc( tStderr_Parent );
				CloseFileDesc( tStdin_Parent );
				if ( iDoStdin )
					{
					if ( ::dup2( tStdin_CGI, 0 )==-1 )
						{
						perror( "CGI child, dup2( stdin ) failed" );
	 					goto cgi_error;
						};
					};
				CloseFileDesc( tStdin_CGI ); 

				if ( ::dup2( tStdout_CGI, 1 )==-1 )
                    {
					perror( "CGI child, dup2( stdout ) failed" );
					goto cgi_error;
                    };
				CloseFileDesc( tStdout_CGI ); 

				if ( ::dup2( tStderr_CGI, 2 )==-1 )
                    {
					perror( "CGI child, dup2( stderr ) failed" );
					goto cgi_error;
                    };
				CloseFileDesc( tStderr_CGI ); 

				/* --- change current directory --- */
				if ( pDirectory )
					{ ::chdir( pDirectory ); };
				if ( ::execve( pPath, (char *const *)ppCommandArgs,
					(char *const *)ppEnv )==-1 )
					{
					perror( "CGI child, execve() failed" );
					fprintf( stderr, "Path is '%s'\n", pPath );
					fflush( stderr );
					goto cgi_error;
					};
cgi_error:
				::_exit( 1 );

				/* none of the following should happen */
				::_exit( -1 );
				assert( 0 );
				break;

			default:
				/* --- this is the parent --- */
				*pProcess = iPid;
				*pStdout_CGI = tStdout_CGI;
				*pStdout_Parent = tStdout_Parent;
				*pStderr_CGI = tStderr_CGI;
				*pStderr_Parent = tStderr_Parent;
				if ( iDoStdin )
					{
					*pStdin_CGI = tStdin_CGI;
					*pStdin_Parent = tStdin_Parent;
					};

				/* --- log debug information about the process --- */
				HTTPCore_logDebug( DBG_MED, "Process created!, \n\tPid %d\
\n\tStdout_Parent %d\n\tStdout_CGI %d\n\tStderr_Parent %d\n\tStderr_CGI %d",
					iPid, tStdout_Parent, tStdout_CGI, tStderr_Parent,
					tStderr_CGI );

				/* --- OK --- */
				return 0;
			};

		/* --- error --- */
posix_error_return:
		int iTmp = errno;
		goto generic_error_return;

#endif


generic_error_return:
		/* --- stdout handles --- */
		CloseIfValidDesc( tStdout_CGI );
		CloseIfValidDesc( tStdout_Parent );
		CloseIfValidDesc( tStdout_ParentDup );

		/* --- stdin handles --- */
		CloseIfValidDesc( tStdin_CGI );
		CloseIfValidDesc( tStdin_Parent );
		CloseIfValidDesc( tStdin_ParentDup );

		/* --- stdin handles --- */
		CloseIfValidDesc( tStderr_CGI );
		CloseIfValidDesc( tStderr_Parent );
		CloseIfValidDesc( tStderr_ParentDup );

		HTTPCore_logError( &tPIHTTP, "CGI: Error executing CGI program, \
system specific error code is '%d'.", iTmp );

		/* --- return error --- */
		return iTmp ? iTmp : -1;
		};

	/* ---
	Wait for a child process to terminate
	--- */
	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,  "CGI: 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,  "CGI: 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 )
		{
		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 iLen;
		iLen = PIIOBuffer_readToBuffer( &tInBuffer, szBuffer, iAttemptToRead );
		while( iLen>0 )
			{
			if ( PIIOBuffer_write( &tOutBuffer, szBuffer, iLen,
				PIIOBUF_NOBUFFER )<=0 )
				{
				assert( 0 );
				return -1;
				};

			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 )
		{
		int iRet = 1;	/* initially success */

		/* ---
		Write input to child stdin and close CGI side of pipe
		--- */
		if ( WriteBufferToBuffer( tIn, tOut, iLen ) )
			{
			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, "CGI: 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;
		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 );

		/* ---
		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++ );

⌨️ 快捷键说明

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