📄 httpcgi.cpp
字号:
/*
Module : HttpCGI.cpp
Purpose: Implementation for the CHttpCGI class
Created: PJN / 26-02-2003
History: None
Copyright (c) 2003 - 2005 by PJ Naughter. (Web: www.naughter.com, Email: pjna@naughter.com)
All rights reserved.
Copyright / Usage Details:
You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise)
when your product is released in binary form. You are allowed to modify the source code in any way you want
except you cannot modify the copyright details at the top of each module. If you want to distribute source
code with your application, then you are only allowed to distribute versions released by the author. This is
to maintain a single distribution point for the source code.
*/
//////////////// Includes ////////////////////////////////////////////
#include "stdafx.h"
#include "HttpCGI.h"
#include "Win32Handle.h"
#include "..\resource.h"
#include "HttpClient.h"
//////////////// Macros //////////////////////////////////////////////
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
//////////////// Implementation //////////////////////////////////////
CHttpCGI::CHttpCGI()
{
}
CHttpCGI::~CHttpCGI()
{
}
void CHttpCGI::TransmitCGIResponse(CHttpClient* pClient)
{
//Validate our parameters
ASSERT(pClient);
ASSERT(pClient->m_pServer);
//Check to make sure the file exists become we try to run it
CFileStatus fs;
if (!CFile::GetStatus(pClient->m_Request.m_sLocalFile, fs))
{
//Report the error
CString sError;
sError.Format(_T("CHttpCGI::TransmitCGIResponse, Requested file %s does not exist"), pClient->m_Request.m_sLocalFile);
pClient->m_pServer->OnError(sError);
//Handle the error
pClient->ReturnErrorMessage(500);
return;
}
//First need to create an anonymous pipe for the child process's redirected
//STDOUT
SECURITY_ATTRIBUTES sa;
sa.nLength= sizeof(SECURITY_ATTRIBUTES);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = TRUE;
CW32Handle ChildStdoutReadTemp;
CW32Handle ChildStdoutWrite;
if (!CreatePipe(&ChildStdoutReadTemp, &ChildStdoutWrite, &sa, 0))
{
//Report the error
CString sError;
sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to create anonymous pipes for STDOUT for CGI child process, Error:%d"), GetLastError());
pClient->m_pServer->OnError(sError);
//Handle the error
pClient->ReturnErrorMessage(500);
return;
}
//Next need to create an anonymous pipe for the child process's redirected
//STDIN
CW32Handle ChildStdinRead;
CW32Handle ChildStdinWriteTemp;
if (pClient->m_Request.m_dwRawEntitySize)
{
if (!CreatePipe(&ChildStdinRead, &ChildStdinWriteTemp, &sa, 0))
{
//Report the error
CString sError;
sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to create anonymous pipes for STDIN for CGI child process, Error:%d"), GetLastError());
pClient->m_pServer->OnError(sError);
//Handle the error
pClient->ReturnErrorMessage(500);
return;
}
}
//Create a duplicate of the child's STDOUT write handle for the STDERR write handle.
//This is necessary in case the child application closes one of its std output handles
CW32Handle ChildStderrWrite;
if (!DuplicateHandle(GetCurrentProcess(), ChildStdoutWrite, GetCurrentProcess(), &ChildStderrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))
{
//Report the error
CString sError;
sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to duplicate STDOUT handle for STDERR handle CGI child process, Error:%d"), GetLastError());
pClient->m_pServer->OnError(sError);
//Handle the error
pClient->ReturnErrorMessage(500);
return;
}
//Create new STDOUT read handle (which is non-inheritable). if we did not do this then the child inherits the
//properties and, as a result non-closeable handles to the pipes are created
CW32Handle ChildStdoutRead;
if (!DuplicateHandle(GetCurrentProcess(), ChildStdoutReadTemp, GetCurrentProcess(), &ChildStdoutRead, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
//Report the error
CString sError;
sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to duplicate STDOUT read handle, Error:%d"), GetLastError());
pClient->m_pServer->OnError(sError);
//Handle the error
pClient->ReturnErrorMessage(500);
return;
}
//Close the temp STDOUT read handle now that we have duplicated it successfully
ChildStdoutReadTemp.Close();
//Create new write handle (if we need to which is non-inheritable). if we did not do this then the child inherits the
//properties and, as a result non-closeable handles to the pipes are created
CW32Handle ChildStdinWrite;
if (pClient->m_Request.m_dwRawEntitySize)
{
if (!DuplicateHandle(GetCurrentProcess(), ChildStdinWriteTemp, GetCurrentProcess(), &ChildStdinWrite, 0, FALSE, DUPLICATE_SAME_ACCESS))
{
//Report the error
CString sError;
sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to duplicate STDIN write handle, Error:%d"), GetLastError());
pClient->m_pServer->OnError(sError);
//Handle the error
pClient->ReturnErrorMessage(500);
return;
}
//Close the temp STDIN write handle now that we have duplicated it successfully
ChildStdinWriteTemp.Close();
}
//Now setup the structures for a call to CreateProcess
PROCESS_INFORMATION pi;
STARTUPINFO si;
ZeroMemory(&si,sizeof(STARTUPINFO));
si.cb = sizeof(STARTUPINFO);
si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
si.hStdOutput = ChildStdoutWrite;
si.hStdInput = ChildStdinRead;
si.hStdError = ChildStderrWrite;
si.wShowWindow = SW_HIDE;
//Get the environment variables we will be sending down to the CGI process
CString sEnvironment = FormCGIEnvironment(pClient);
//Convert the "sEnvironment" CString into a MULTI_SZ suitable for calling CreateProcess with
DWORD dwEnvironmentSize = (sEnvironment.GetLength() + 2);
TCHAR* pszEnvironment = new TCHAR[dwEnvironmentSize];
_tcscpy(pszEnvironment, sEnvironment);
pszEnvironment[dwEnvironmentSize-1] = _T('\0'); //Double NULL terminate the data
//Replace all '\n' with '\0'
for (DWORD i=0; i<dwEnvironmentSize; i++)
{
if (pszEnvironment[i] == _T('\n'))
pszEnvironment[i] = _T('\0');
}
//Setup the creation flags
DWORD dwCreationFlags = CREATE_NEW_CONSOLE;
#ifdef _UNICODE
dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
#endif
//Get the working directory of the script
TCHAR sDrive[_MAX_DRIVE];
TCHAR sDir[_MAX_DIR];
_tsplitpath(pClient->m_Request.m_sLocalFile, sDrive, sDir, NULL, NULL);
TCHAR sCWD[_MAX_PATH];
_tmakepath(sCWD, sDrive, sDir, NULL, NULL);
//Form the correct command line for the call to CreateProcess
CString sCommandLine = GetCGICommandLine(pClient);
//Launch the process that we want to redirect
BOOL bSuccess = CreateProcess(NULL, sCommandLine.GetBuffer(sCommandLine.GetLength()), NULL, NULL, TRUE, dwCreationFlags, pszEnvironment, sCWD, &si, &pi);
DWORD dwError = GetLastError();
sCommandLine.ReleaseBuffer();
//Tidy up the temp heap memory we have used
delete [] pszEnvironment;
//Close the STDOUT write handle as we have no use for it now
ChildStdoutWrite.Close();
//Close the STDIN read handle as we have no use for it now
if (pClient->m_Request.m_dwRawEntitySize)
ChildStdinRead.Close();
//Close the STDERR write handle as we have no use for it now
ChildStderrWrite.Close();
//Handle the error if we could not run the child process
if (!bSuccess)
{
//Report the error
CString sError;
sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to run the CGI child process, Error:%d"), GetLastError());
pClient->m_pServer->OnError(sError);
//Return an appropriate error page
if (dwError == ERROR_FILE_NOT_FOUND)
pClient->ReturnErrorMessage(404);
else
pClient->ReturnErrorMessage(500);
return;
}
//Write to stdin of the client
if (pClient->m_Request.m_dwRawEntitySize)
{
WriteToChildStdin(pClient, ChildStdinWrite);
//Close the pipe handle now that we are finished with it
ChildStdinWrite.Close();
}
//Read from stdout of the client and send the data back down the socket
DWORD dwDataSent = ReadFromClientStdout(pClient, ChildStdoutRead, pClient->m_bResponseKeepAlive);
if (dwDataSent == 0)
pClient->ReturnErrorMessage(500);
else
pClient->PostLog(200, dwDataSent);
//Close the anonymous pipe handles
ChildStdoutRead.Close();
//Wait until the child process exits
WaitForSingleObject(pi.hProcess, INFINITE);
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
CString CHttpCGI::FormCGIEnvironment(CHttpClient* pClient)
{
//Validate our parameters
ASSERT(pClient);
ASSERT(pClient->m_pServer);
CHttpServerSettings* pSettings = pClient->m_pServer->GetSettings();
ASSERT(pSettings);
//Form the environment string to send to the child process
CString sEnvironment;
CString sLine;
//Add all the environment variables W3MFC has before we add the custom ones
for (TCHAR** szVariable = _tenviron; *szVariable; szVariable++ )
{
sEnvironment += *szVariable;
sEnvironment += _T("\n");
}
if (pClient->m_Request.m_AuthorizationType == CHttpRequest::HTTP_AUTHORIZATION_PLAINTEXT)
{
sEnvironment += _T("AUTH_TYPE=Basic\n");
sLine.Format(_T("AUTH_REALM=%s\n"), pClient->m_Request.m_sURL);
sEnvironment += sLine;
sLine.Format(_T("REMOTE_USER=%s\n"), pClient->m_Request.m_sUsername);
sEnvironment += sLine;
sLine.Format(_T("AUTH_USER=%s\n"), pClient->m_Request.m_sUsername);
sEnvironment += sLine;
sLine.Format(_T("REMOTE_PASSWORD=%s\n"), pClient->m_Request.m_sPassword);
sEnvironment += sLine;
sLine.Format(_T("AUTH_PASSWORD=%s\n"), pClient->m_Request.m_sPassword);
sEnvironment += sLine;
}
else if (pClient->m_Request.m_AuthorizationType == CHttpRequest::HTTP_AUTHORIZATION_NTLM)
{
sEnvironment += _T("AUTH_TYPE=NTLM\n");
sLine.Format(_T("REMOTE_USER=%s\n"), pClient->m_Request.m_sUsername);
sEnvironment += sLine;
sLine.Format(_T("AUTH_USER=%s\n"), pClient->m_Request.m_sUsername);
sEnvironment += sLine;
}
sEnvironment += _T("GATEWAY_INTERFACE=CGI/1.1\n");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -