HttpCGI.cpp
上传用户:dengkfang
上传日期:2008-12-30
资源大小:5233k
文件大小:21k
源码类别:

CA认证

开发平台:

Visual C++

  1. /*
  2. Module : HttpCGI.cpp
  3. Purpose: Implementation for the CHttpCGI class
  4. Created: PJN / 26-02-2003
  5. History: None
  6. Copyright (c) 2003 - 2005 by PJ Naughter.  (Web: www.naughter.com, Email: pjna@naughter.com)
  7. All rights reserved.
  8. Copyright / Usage Details:
  9. You are allowed to include the source code in any product (commercial, shareware, freeware or otherwise) 
  10. when your product is released in binary form. You are allowed to modify the source code in any way you want 
  11. except you cannot modify the copyright details at the top of each module. If you want to distribute source 
  12. code with your application, then you are only allowed to distribute versions released by the author. This is 
  13. to maintain a single distribution point for the source code. 
  14. */
  15. //////////////// Includes ////////////////////////////////////////////
  16. #include "stdafx.h"
  17. #include "HttpCGI.h"
  18. #include "Win32Handle.h"
  19. #include "..resource.h"
  20. #include "HttpClient.h"
  21. //////////////// Macros //////////////////////////////////////////////
  22. #ifdef _DEBUG
  23. #define new DEBUG_NEW
  24. #undef THIS_FILE
  25. static char THIS_FILE[] = __FILE__;
  26. #endif
  27. //////////////// Implementation //////////////////////////////////////
  28. CHttpCGI::CHttpCGI()
  29. {
  30. }
  31. CHttpCGI::~CHttpCGI()
  32. {
  33. }
  34. void CHttpCGI::TransmitCGIResponse(CHttpClient* pClient)
  35. {
  36.   //Validate our parameters
  37.   ASSERT(pClient);
  38.   ASSERT(pClient->m_pServer);
  39.   //Check to make sure the file exists become we try to run it
  40.   CFileStatus fs;
  41.   if (!CFile::GetStatus(pClient->m_Request.m_sLocalFile, fs))
  42.   {
  43.     //Report the error
  44.     CString sError;
  45.     sError.Format(_T("CHttpCGI::TransmitCGIResponse, Requested file %s does not exist"), pClient->m_Request.m_sLocalFile);
  46.     pClient->m_pServer->OnError(sError);
  47.     //Handle the error
  48.     pClient->ReturnErrorMessage(500);
  49.     return;
  50.   }
  51.   //First need to create an anonymous pipe for the child process's redirected 
  52.   //STDOUT
  53.   SECURITY_ATTRIBUTES sa;
  54.   sa.nLength= sizeof(SECURITY_ATTRIBUTES);
  55.   sa.lpSecurityDescriptor = NULL;
  56.   sa.bInheritHandle = TRUE;
  57.   CW32Handle ChildStdoutReadTemp;
  58.   CW32Handle ChildStdoutWrite;
  59.   if (!CreatePipe(&ChildStdoutReadTemp, &ChildStdoutWrite, &sa, 0)) 
  60.   {
  61.     //Report the error
  62.     CString sError;
  63.     sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to create anonymous pipes for STDOUT for CGI child process, Error:%d"), GetLastError());
  64.     pClient->m_pServer->OnError(sError);
  65.     //Handle the error
  66.     pClient->ReturnErrorMessage(500);
  67.     return;
  68.   }
  69.   //Next need to create an anonymous pipe for the child process's redirected 
  70.   //STDIN
  71.   CW32Handle ChildStdinRead;
  72.   CW32Handle ChildStdinWriteTemp;
  73.   if (pClient->m_Request.m_dwRawEntitySize)
  74.   {
  75.     if (!CreatePipe(&ChildStdinRead, &ChildStdinWriteTemp, &sa, 0)) 
  76.     {
  77.       //Report the error
  78.       CString sError;
  79.       sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to create anonymous pipes for STDIN for CGI child process, Error:%d"), GetLastError());
  80.       pClient->m_pServer->OnError(sError);
  81.       //Handle the error
  82.       pClient->ReturnErrorMessage(500);
  83.       return;
  84.     }
  85.   }
  86.   //Create a duplicate of the child's STDOUT write handle for the STDERR write handle.
  87.   //This is necessary in case the child application closes one of its std output handles
  88.   CW32Handle ChildStderrWrite;
  89.   if (!DuplicateHandle(GetCurrentProcess(), ChildStdoutWrite, GetCurrentProcess(), &ChildStderrWrite, 0, TRUE, DUPLICATE_SAME_ACCESS))
  90.   {
  91.     //Report the error
  92.     CString sError;
  93.     sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to duplicate STDOUT handle for STDERR handle CGI child process, Error:%d"), GetLastError());
  94.     pClient->m_pServer->OnError(sError);
  95.     //Handle the error
  96.     pClient->ReturnErrorMessage(500);
  97.     return;
  98.   }
  99.   //Create new STDOUT read handle (which is non-inheritable). if we did not do this then the child inherits the 
  100.   //properties and, as a result non-closeable handles to the pipes are created
  101.   CW32Handle ChildStdoutRead;
  102.   if (!DuplicateHandle(GetCurrentProcess(), ChildStdoutReadTemp, GetCurrentProcess(), &ChildStdoutRead, 0, FALSE, DUPLICATE_SAME_ACCESS))
  103.   {
  104.     //Report the error
  105.     CString sError;
  106.     sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to duplicate STDOUT read handle, Error:%d"), GetLastError());
  107.     pClient->m_pServer->OnError(sError);
  108.     //Handle the error
  109.     pClient->ReturnErrorMessage(500);
  110.     return;
  111.   }
  112.   //Close the temp STDOUT read handle now that we have duplicated it successfully
  113.   ChildStdoutReadTemp.Close();
  114.   //Create new write handle (if we need to which is non-inheritable). if we did not do this then the child inherits the 
  115.   //properties and, as a result non-closeable handles to the pipes are created
  116.   CW32Handle ChildStdinWrite;
  117.   if (pClient->m_Request.m_dwRawEntitySize)
  118.   {
  119.     if (!DuplicateHandle(GetCurrentProcess(), ChildStdinWriteTemp, GetCurrentProcess(), &ChildStdinWrite, 0, FALSE, DUPLICATE_SAME_ACCESS))
  120.     {
  121.       //Report the error
  122.       CString sError;
  123.       sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to duplicate STDIN write handle, Error:%d"), GetLastError());
  124.       pClient->m_pServer->OnError(sError);
  125.       //Handle the error
  126.       pClient->ReturnErrorMessage(500);
  127.       return;
  128.     }
  129.     //Close the temp STDIN write handle now that we have duplicated it successfully
  130.     ChildStdinWriteTemp.Close();
  131.   }
  132.   //Now setup the structures for a call to CreateProcess
  133.   PROCESS_INFORMATION pi;
  134.   STARTUPINFO si;
  135.   ZeroMemory(&si,sizeof(STARTUPINFO));
  136.   si.cb = sizeof(STARTUPINFO);
  137.   si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
  138.   si.hStdOutput = ChildStdoutWrite;
  139.   si.hStdInput  = ChildStdinRead;
  140.   si.hStdError  = ChildStderrWrite;
  141.   si.wShowWindow = SW_HIDE;
  142.   //Get the environment variables we will be sending down to the CGI process
  143.   CString sEnvironment = FormCGIEnvironment(pClient);
  144.   //Convert the "sEnvironment" CString into a MULTI_SZ suitable for calling CreateProcess with
  145.   DWORD dwEnvironmentSize = (sEnvironment.GetLength() + 2);
  146.   TCHAR* pszEnvironment = new TCHAR[dwEnvironmentSize];
  147.   _tcscpy(pszEnvironment, sEnvironment);
  148.   pszEnvironment[dwEnvironmentSize-1] = _T(''); //Double NULL terminate the data
  149.   //Replace all 'n' with ''
  150.   for (DWORD i=0; i<dwEnvironmentSize; i++)
  151.   {
  152.     if (pszEnvironment[i] == _T('n'))
  153.       pszEnvironment[i] = _T('');
  154.   }
  155.  
  156.   //Setup the creation flags
  157.   DWORD dwCreationFlags = CREATE_NEW_CONSOLE;
  158.   #ifdef _UNICODE
  159.   dwCreationFlags |= CREATE_UNICODE_ENVIRONMENT;
  160.   #endif
  161.   //Get the working directory of the script
  162.   TCHAR sDrive[_MAX_DRIVE];
  163.   TCHAR sDir[_MAX_DIR];
  164.   _tsplitpath(pClient->m_Request.m_sLocalFile, sDrive, sDir, NULL, NULL);
  165.   TCHAR sCWD[_MAX_PATH];
  166.   _tmakepath(sCWD, sDrive, sDir, NULL, NULL);
  167.   //Form the correct command line for the call to CreateProcess
  168.   CString sCommandLine = GetCGICommandLine(pClient);
  169.   //Launch the process that we want to redirect
  170.   BOOL bSuccess = CreateProcess(NULL, sCommandLine.GetBuffer(sCommandLine.GetLength()), NULL, NULL, TRUE, dwCreationFlags, pszEnvironment, sCWD, &si, &pi);
  171.   DWORD dwError = GetLastError();
  172.   sCommandLine.ReleaseBuffer();
  173.   //Tidy up the temp heap memory we have used
  174.   delete [] pszEnvironment;
  175.   //Close the STDOUT write handle as we have no use for it now
  176.   ChildStdoutWrite.Close();
  177.   //Close the STDIN read handle as we have no use for it now
  178.   if (pClient->m_Request.m_dwRawEntitySize)
  179.     ChildStdinRead.Close();
  180.   //Close the STDERR write handle as we have no use for it now
  181.   ChildStderrWrite.Close();
  182.   //Handle the error if we could not run the child process
  183.   if (!bSuccess)
  184.   {
  185.     //Report the error
  186.     CString sError;
  187.     sError.Format(_T("CHttpCGI::TransmitCGIResponse, Failed to run the CGI child process, Error:%d"), GetLastError());
  188.     pClient->m_pServer->OnError(sError);
  189.     //Return an appropriate error page
  190.     if (dwError == ERROR_FILE_NOT_FOUND)
  191.       pClient->ReturnErrorMessage(404);
  192.     else
  193.       pClient->ReturnErrorMessage(500);
  194.     return;
  195.   }
  196.   //Write to stdin of the client
  197.   if (pClient->m_Request.m_dwRawEntitySize)
  198.   {
  199.     WriteToChildStdin(pClient, ChildStdinWrite);
  200.     //Close the pipe handle now that we are finished with it
  201.     ChildStdinWrite.Close();
  202.   }
  203.   //Read from stdout of the client and send the data back down the socket
  204.   DWORD dwDataSent = ReadFromClientStdout(pClient, ChildStdoutRead, pClient->m_bResponseKeepAlive);
  205.   if (dwDataSent == 0)
  206.     pClient->ReturnErrorMessage(500);
  207.   else
  208.     pClient->PostLog(200, dwDataSent);
  209.   //Close the anonymous pipe handles
  210.   ChildStdoutRead.Close();
  211.   //Wait until the child process exits
  212.   WaitForSingleObject(pi.hProcess, INFINITE);
  213.   // Close process and thread handles.     
  214.   CloseHandle(pi.hProcess);
  215.   CloseHandle(pi.hThread);
  216. }
  217. CString CHttpCGI::FormCGIEnvironment(CHttpClient* pClient)
  218. {
  219.   //Validate our parameters
  220.   ASSERT(pClient);
  221.   ASSERT(pClient->m_pServer);
  222.   CHttpServerSettings* pSettings = pClient->m_pServer->GetSettings();
  223.   ASSERT(pSettings);
  224.   //Form the environment string to send to the child process
  225.   CString sEnvironment;
  226.   CString sLine;
  227.   //Add all the environment variables W3MFC has before we add the custom ones
  228.   for (TCHAR** szVariable = _tenviron; *szVariable; szVariable++ )
  229.   {
  230.     sEnvironment += *szVariable;
  231.     sEnvironment += _T("n");
  232.   }    
  233.   if (pClient->m_Request.m_AuthorizationType == CHttpRequest::HTTP_AUTHORIZATION_PLAINTEXT)
  234.   {
  235.     sEnvironment += _T("AUTH_TYPE=Basicn");
  236.     sLine.Format(_T("AUTH_REALM=%sn"), pClient->m_Request.m_sURL);
  237.     sEnvironment += sLine;
  238.     sLine.Format(_T("REMOTE_USER=%sn"), pClient->m_Request.m_sUsername);
  239.     sEnvironment += sLine;
  240.     sLine.Format(_T("AUTH_USER=%sn"), pClient->m_Request.m_sUsername);
  241.     sEnvironment += sLine;
  242.     sLine.Format(_T("REMOTE_PASSWORD=%sn"), pClient->m_Request.m_sPassword);
  243.     sEnvironment += sLine;
  244.     sLine.Format(_T("AUTH_PASSWORD=%sn"), pClient->m_Request.m_sPassword);
  245.     sEnvironment += sLine;
  246.   }
  247.   else if (pClient->m_Request.m_AuthorizationType == CHttpRequest::HTTP_AUTHORIZATION_NTLM)
  248.   {
  249.     sEnvironment += _T("AUTH_TYPE=NTLMn");
  250.     sLine.Format(_T("REMOTE_USER=%sn"), pClient->m_Request.m_sUsername);
  251.     sEnvironment += sLine;
  252.     sLine.Format(_T("AUTH_USER=%sn"), pClient->m_Request.m_sUsername);
  253.     sEnvironment += sLine;
  254.   }
  255.   sEnvironment += _T("GATEWAY_INTERFACE=CGI/1.1n");
  256.   if (pSettings->m_sBindAddress.GetLength())
  257.     sLine.Format(_T("SERVER_NAME=%sn"), pSettings->m_sBindAddress);
  258.   else
  259.   {
  260.     char pszHostName[256];
  261.     if (gethostname(pszHostName, 256) == 0)
  262.     {
  263.       CString sName(pszHostName);
  264.       sLine.Format(_T("SERVER_NAME=%sn"), sName);
  265.     }
  266.     else
  267.       sLine = (_T("SERVER_NAME=n"));
  268.   }
  269.   sEnvironment += sLine;
  270.   if (pClient->m_Request.m_nContentLength)
  271.   {
  272.     sLine.Format(_T("CONTENT_LENGTH=%dn"), pClient->m_Request.m_nContentLength);
  273.     sEnvironment += sLine;
  274.   }
  275.   if (pClient->m_Request.m_sContentType.GetLength())
  276.   {
  277.     sLine.Format(_T("CONTENT_TYPE=%sn"), pClient->m_Request.m_sContentType);
  278.     sEnvironment += sLine;
  279.   }
  280.   sLine.Format(_T("SERVER_PORT=%dn"), pSettings->m_nPort);
  281.   sEnvironment += sLine;
  282. #ifdef W3MFC_SSL_SUPPORT
  283.   if (pSettings->m_SSLProtocol == CHttpServerSettings::SSL_NONE)
  284.     sEnvironment += _T("SERVER_PROTOCOL=HTTP/1.0n");
  285.   else
  286.     sEnvironment += _T("SERVER_PROTOCOL=HTTPS/1.0n");
  287. #else
  288.   sEnvironment += _T("SERVER_PROTOCOL=HTTP/1.0n");
  289. #endif
  290.   sLine.Format(_T("SERVER_SOFTWARE=%sn"), pSettings->m_sServerName);
  291.   sEnvironment += sLine;
  292.   CString sScriptName = pClient->m_Request.m_sLocalFile;
  293.   sScriptName.Replace(_T('\'), _T('/')); //Ensure we use unix directory separators
  294.   if (sScriptName.GetLength() && sScriptName.GetAt(0) != _T('/'))
  295.     sScriptName = _T("/") + sScriptName;
  296.   sLine.Format(_T("SCRIPT_NAME=%sn"), sScriptName);
  297.   sEnvironment += sLine;
  298.   //Form the absolute path for PATH_TRANSLATED
  299.   LPTSTR pszFilePart;
  300.   TCHAR pszAbsolutePath[_MAX_PATH];
  301.   GetFullPathName(pClient->m_Request.m_sLocalFile, _MAX_PATH, pszAbsolutePath, &pszFilePart);
  302.   sLine.Format(_T("PATH_TRANSLATED=%sn"), pszAbsolutePath);
  303.   sEnvironment += sLine;
  304.   sLine.Format(_T("REQUEST_LINE=%sn"), pClient->m_Request.m_sRequest);
  305.   sEnvironment += sLine;
  306.   sLine.Format(_T("QUERY_STRING=%sn"), pClient->m_Request.m_sRawExtra);
  307.   sEnvironment += sLine;
  308.   
  309.   if (!pClient->m_Request.m_sRemoteHost.IsEmpty())
  310.   {
  311.     sLine.Format(_T("REMOTE_HOST=%sn"), pClient->m_Request.m_sRemoteHost);
  312.     sEnvironment += sLine;
  313.   }
  314.   else
  315.   {
  316.     sLine.Format(_T("REMOTE_HOST=%d.%d.%d.%dn"), pClient->m_Request.m_ClientAddress.sin_addr.S_un.S_un_b.s_b1,
  317.                  pClient->m_Request.m_ClientAddress.sin_addr.S_un.S_un_b.s_b2, pClient->m_Request.m_ClientAddress.sin_addr.S_un.S_un_b.s_b3,
  318.                  pClient->m_Request.m_ClientAddress.sin_addr.S_un.S_un_b.s_b4);
  319.     sEnvironment += sLine;
  320.   }
  321.   sLine.Format(_T("REMOTE_ADDR=%d.%d.%d.%dn"), pClient->m_Request.m_ClientAddress.sin_addr.S_un.S_un_b.s_b1,
  322.                pClient->m_Request.m_ClientAddress.sin_addr.S_un.S_un_b.s_b2, pClient->m_Request.m_ClientAddress.sin_addr.S_un.S_un_b.s_b3,
  323.                pClient->m_Request.m_ClientAddress.sin_addr.S_un.S_un_b.s_b4);
  324.   sEnvironment += sLine;
  325.   sLine.Format(_T("PATH_INFO=%sn"), pClient->m_Request.m_sPathInfo);
  326.   sEnvironment += sLine;
  327.   sLine.Format(_T("REQUEST_METHOD=%sn"), pClient->m_Request.m_sVerb);
  328.   sEnvironment += sLine;
  329.   int nKeySize = 0;
  330.   if (pClient->GetKeySizeServerVariable(nKeySize))
  331.   {
  332.     sLine.Format(_T("HTTPS_KEYSIZE=%dnCERT_KEYSIZE=%dn"), nKeySize, nKeySize);
  333.     sEnvironment += sLine;
  334.   }
  335.   if (pClient->GetServerKeySizeServerVariable(nKeySize))
  336.   {
  337.     sLine.Format(_T("HTTPS_SECRETKEYSIZE=%dnCERT_SECRETKEYSIZE=%dn"), nKeySize, nKeySize);
  338.     sEnvironment += sLine;
  339.   }
  340.   sEnvironment += sLine;
  341. #ifdef W3MFC_SSL_SUPPORT
  342.   if (pSettings->m_SSLProtocol == CHttpServerSettings::SSL_NONE)
  343.     sLine = _T("HTTPS=offn");
  344.   else
  345.     sLine = _T("HTTPS=onn");
  346. #else
  347.   sLine = _T("HTTPS=offn");
  348. #endif
  349.   sEnvironment += sLine;
  350. #ifdef W3MFC_SSL_SUPPORT
  351.   if (pSettings->m_SSLProtocol != CHttpServerSettings::SSL_NONE)
  352.   {
  353.     sLine.Format(_T("SERVER_PORT_SECURE=%dn"), pSettings->m_nPort);
  354.     sEnvironment += sLine;
  355.   }
  356. #endif
  357.   long nSerialNumber = 0;
  358.   if (pClient->GetCertSerialNumberServerVariable(nSerialNumber))
  359.   {
  360.     sLine.Format(_T("CERT_SERIALNUMBER=%dn"), nSerialNumber);
  361.     sEnvironment += sLine;
  362.   }
  363.   sLine.Format(_T("REQUEST_URI=%sn"), pClient->m_Request.m_sURL);
  364.   sEnvironment += sLine;
  365.   sLine.Format(_T("URL=%sn"), pClient->m_Request.m_sURL);
  366.   sEnvironment += sLine;
  367.   if (pClient->m_Request.m_bLoggedOn)
  368.   {
  369.     sLine.Format(_T("LOGON_USER=%sn"), pClient->m_Request.m_sUsername);
  370.     sEnvironment += sLine;
  371.   }
  372.   else if (pSettings->m_sUsername.GetLength())
  373.   {
  374.     sLine.Format(_T("LOGON_USER=%sn"), pSettings->m_sUsername);
  375.     sEnvironment += sLine;
  376.   }
  377.   char* szIssuer = NULL;
  378.   if (pClient->GetCertIssuerServerVariable(szIssuer))
  379.   { 
  380.     CString sIssuer(szIssuer);
  381.     sLine.Format(_T("CERT_SERVER_ISSUER=%snHTTPS_SERVER_ISSUER=%sn"), sIssuer, sIssuer);
  382.     sEnvironment += sLine;
  383.   }
  384.   char* szSubject = NULL;
  385.   if (pClient->GetCertSubjectServerVariable(szSubject))
  386.   { 
  387.     CString sSubject(szSubject);
  388.     sLine.Format(_T("CERT_SERVER_SUBJECT=%snHTTPS_SERVER_SUBJECT=%sn"), sSubject, sSubject);
  389.     sEnvironment += sLine;
  390.   }
  391.   sLine.Format(_T("HTTP_VERSION=%d.%dn"), HIWORD(pClient->m_Request.m_dwHttpVersion), LOWORD(pClient->m_Request.m_dwHttpVersion));
  392.   sEnvironment += sLine;
  393.   POSITION posMap = pClient->m_Request.m_HeaderMap.GetStartPosition();
  394.   while (posMap)
  395.   {
  396.     CString sKey;
  397.     CString sValue;
  398.     pClient->m_Request.m_HeaderMap.GetNextAssoc(posMap, sKey, sValue);
  399.     sLine.Format(_T("HTTP_%s=%sn"), sKey, sValue);
  400.     sEnvironment += sLine;
  401.   }
  402.   return sEnvironment;
  403. }
  404. CString CHttpCGI::GetCGICommandLine(CHttpClient* pClient)
  405. {
  406.   //What will be the return value
  407.   CString sCommandLine(pClient->m_Request.m_sLocalFile);
  408.   //First get the extension of the file
  409.   TCHAR sExt[_MAX_EXT];
  410.   _tsplitpath(pClient->m_Request.m_sLocalFile, NULL, NULL, NULL, sExt);
  411.   //Now get the command line for this extension from the registry
  412.   TCHAR pszValue[1024/sizeof(TCHAR)];
  413.   DWORD nData = 1024;
  414.   HKEY hExtKey = NULL;
  415.   if (RegOpenKeyEx(HKEY_CLASSES_ROOT, sExt, 0, KEY_READ, &hExtKey) == ERROR_SUCCESS)
  416.   {
  417.   if (RegQueryValueEx(hExtKey, NULL, NULL, NULL, (LPBYTE)pszValue, &nData) == ERROR_SUCCESS)
  418.   {
  419.       //Close the registry key now that we are finished with it
  420.       RegCloseKey(hExtKey);
  421.   //pszValue is now the Extension File Subkey to use.
  422.   CString sValue;
  423.   sValue.Format(_T("%s\Shell\Open\Command"), pszValue);
  424.   nData = 1024; //reset the size parameter
  425.       HKEY hExtensionFileKey = NULL;
  426.       if (RegOpenKeyEx(HKEY_CLASSES_ROOT, sValue, 0, KEY_READ, &hExtensionFileKey) == ERROR_SUCCESS)
  427.   {
  428.     if (RegQueryValueEx(hExtensionFileKey, NULL, NULL, NULL, (LPBYTE)pszValue, &nData) == ERROR_SUCCESS)
  429.   {
  430.           //Expand the string with any environment variables it may contain
  431.           nData = 1024;
  432. LPTSTR szBuf = sCommandLine.GetBuffer(nData);
  433. DWORD  dwRet = ExpandEnvironmentStrings(pszValue, szBuf, nData); 
  434. sCommandLine.ReleaseBuffer();
  435.           if (dwRet != 0)
  436.           {
  437.       sCommandLine.Replace(_T("%L"), pClient->m_Request.m_sLocalFile);
  438.       sCommandLine.Replace(_T("%1"), pClient->m_Request.m_sLocalFile);
  439.       sCommandLine.Replace(_T("%*"), _T("")); 
  440.           }
  441.   }
  442.         //Close the registry key now that we are finished with it
  443.   RegCloseKey(hExtensionFileKey);
  444.   }
  445.   }
  446.   }
  447.   return sCommandLine;
  448. }
  449. void CHttpCGI::WriteToChildStdin(CHttpClient* pClient, HANDLE hChildStdin)
  450. {
  451.   //Write the entity body to STDIN of the CGI child process
  452.   DWORD dwWritten;
  453.   ASSERT(pClient->m_Request.m_pRawEntity);
  454.   WriteFile(hChildStdin, pClient->m_Request.m_pRawEntity, pClient->m_Request.m_dwRawEntitySize, &dwWritten, NULL);
  455. }
  456. DWORD CHttpCGI::ReadFromClientStdout(CHttpClient* pClient, HANDLE hChildStdout, BOOL& bFoundKeepAlive) 
  457.   //Validate our parameters
  458.   ASSERT(pClient);
  459.   ASSERT(pClient->m_pServer);
  460.   CHttpServerSettings* pSettings = pClient->m_pServer->GetSettings();
  461.   ASSERT(pSettings);
  462.   //Initialize the out parameters
  463.   bFoundKeepAlive = FALSE;
  464.   DWORD dwDataSent = 0;
  465.   //Allocate the receive / re transmit buffer
  466.   char* pszBuf = (char*) alloca(pSettings->m_dwCGIResponseBufferSize); 
  467.   //Read output from the child process, and write to parent's STDOUT. 
  468.   BOOL bMore = TRUE;
  469.   BOOL bFirstBuffer = TRUE;
  470.   while (bMore) 
  471.   { 
  472.     DWORD dwRead;
  473.     if (!ReadFile(hChildStdout, pszBuf, pSettings->m_dwCGIResponseBufferSize-1, &dwRead, NULL) || dwRead == 0) 
  474.       bMore = FALSE; 
  475.     else
  476.     {
  477.       //Used as a return value to indicate how much was sent to the client
  478.       dwDataSent += dwRead;
  479.       BOOL bFoundHTTPReturnCodeLine = FALSE;
  480.       if (bFirstBuffer)
  481.       {
  482.         //NULL terminate the data (so that we can use strstr)
  483.         pszBuf[dwRead] = '';
  484.         //See if the "Connection: Keep Alive" is being transmitted
  485.         bFoundKeepAlive = (strstr(pszBuf, "Connection: Keep-Alive") != NULL);
  486.         if (bFoundKeepAlive)
  487.         {
  488.           //Note it is important that "Content-Length" be also sent when using keep alives
  489.           //as otherwise client browsers do not know when to stop reading to get the end
  490.           //of the first response
  491.           bFoundKeepAlive = (strstr(pszBuf, "Content-Length: ") != NULL);
  492.         }
  493.         
  494.         //Do we have a HTTP return code line
  495.         bFoundHTTPReturnCodeLine = (strstr(pszBuf,"HTTP/") != NULL);
  496.         //We only do this parse on the first buffer
  497.         bFirstBuffer = FALSE;
  498.       }
  499.       //Send the data back down the socket
  500.       try
  501.       {
  502.     char* pszTTPReturnCodeLine = "HTTP/1.0 200 OKn";
  503. #ifdef W3MFC_SSL_SUPPORT
  504.         if (!bFoundHTTPReturnCodeLine)
  505.     pClient->m_Socket.SendWithRetry(pszTTPReturnCodeLine, strlen(pszTTPReturnCodeLine), pSettings->m_dwWritableTimeout, pClient->m_SSL);
  506.         pClient->m_Socket.SendWithRetry(pszBuf, dwRead, pSettings->m_dwWritableTimeout, pClient->m_SSL);
  507. #else
  508.         if (!bFoundHTTPReturnCodeLine)
  509.     pClient->m_Socket.SendWithRetry(pszTTPReturnCodeLine, strlen(pszTTPReturnCodeLine), pSettings->m_dwWritableTimeout);
  510.         pClient->m_Socket.SendWithRetry(pszBuf, dwRead, pSettings->m_dwWritableTimeout);
  511. #endif
  512.       }
  513.       catch(CWSocketException* pEx)
  514.       {
  515.         //Report the error
  516.         CString sError;
  517.         sError.Format(_T("CHttpCGI::ReadFromClientStdout, Failed to send to socket, Error:%d"), pEx->m_nError);
  518.         pClient->m_pServer->OnError(sError);
  519.         pEx->Delete();  
  520.         bMore = FALSE;
  521.       }
  522.     }
  523.   } 
  524.   return dwDataSent;