📄 isapi.cpp
字号:
// This is a part of the Microsoft Foundation Classes C++ library.
// Copyright (C) 1992-1998 Microsoft Corporation
// All rights reserved.
//
// This source code is only intended as a supplement to the
// Microsoft Foundation Classes Reference and related
// electronic documentation provided with the library.
// See these sources for detailed information regarding the
// Microsoft Foundation Classes product.
// This module is unique -- no need for PCH
#include <limits.h>
#ifdef _AFXDLL
#include <afx.h>
#include <afxwin.h>
#include <afxdb.h>
#include <afxpriv.h>
#endif
#include <afxisapi.h>
#include <afxres.h>
#include <stdio.h>
#include <malloc.h>
#include "dispimpl.h"
///////////////////////////////////////////////////////////////////////
// Globals
// pointers to single global server and filter objects
static CHttpServer* pServer = NULL;
static CHttpFilter* pFilter = NULL;
// stock server extension strings
static const TCHAR szGet[] = _T("GET");
static const TCHAR szPost[] = _T("POST");
static const TCHAR szDecimalFormat[] = _T("%d");
static const TCHAR szFloatFormat[] = _T("%f");
// stock HTML tags
static const TCHAR szContentType[] = _T("Content-Type: text/html\r\n");
static const TCHAR szEndBody[] = _T("</body></html>");
static const TCHAR szStartTitle[] = _T("<html><head><title>");
static const TCHAR szEndTitle[] = _T("</title></head><body>");
static const TCHAR szDefaultTitle[] = _T("Default MFC Web Server Extension");
// error texts
// these aren't localized as they are part of the HTTP spec
typedef struct _httpstatinfo {
DWORD dwCode;
LPCTSTR pstrString;
} HTTPStatusInfo;
static HTTPStatusInfo statusStrings[] = {
{ HTTP_STATUS_OK, _T("OK") },
{ HTTP_STATUS_CREATED, _T("Created") },
{ HTTP_STATUS_ACCEPTED, _T("Accepted") },
{ HTTP_STATUS_NO_CONTENT, _T("No Content") },
{ HTTP_STATUS_TEMP_REDIRECT, _T("Moved Temporarily") },
{ HTTP_STATUS_REDIRECT, _T("Moved Permanently") },
{ HTTP_STATUS_NOT_MODIFIED, _T("Not Modified") },
{ HTTP_STATUS_BAD_REQUEST, _T("Bad Request") },
{ HTTP_STATUS_AUTH_REQUIRED, _T("Unauthorized") },
{ HTTP_STATUS_FORBIDDEN, _T("Forbidden") },
{ HTTP_STATUS_NOT_FOUND, _T("Not Found") },
{ HTTP_STATUS_REQUEST_TOO_LARGE,_T("Request entity was too large") },
{ HTTP_STATUS_SERVER_ERROR, _T("Internal Server Error") },
{ HTTP_STATUS_NOT_IMPLEMENTED, _T("Not Implemented") },
{ HTTP_STATUS_BAD_GATEWAY, _T("Bad Gateway") },
{ HTTP_STATUS_SERVICE_NA, _T("Service Unavailable") },
{ 0, NULL }
};
/////////////////////////////////////////////////////////////////////////////
// Root of all parse maps
const AFXIS_DATADEF AFX_PARSEMAP CHttpServer::parseMap =
{
&CHttpServer::GetNumMapEntries,
#ifdef _AFXDLL
&CHttpServer::_GetBaseParseMap,
#else
NULL,
#endif
&CHttpServer::_parseEntries[0],
};
AFX_PARSEMAP_ENTRY CHttpServer::_parseEntries[] =
{
{ NULL, NULL, NULL } // nothing here
};
#ifdef _AFXDLL
const AFX_PARSEMAP* CHttpServer::_GetBaseParseMap()
{
return NULL;
}
#endif
UINT PASCAL CHttpServer::GetNumMapEntries()
{
return 0;
}
const AFX_PARSEMAP* CHttpServer::GetParseMap() const
{
return NULL;
}
AFX_PARSEMAP::~AFX_PARSEMAP()
{
// walk through parse map to find any dying parsed parameter entries
UINT iEntry;
UINT cEntries = (*pfnGetNumMapEntries)();
AFX_PARSEMAP_ENTRY* pCurrent = (AFX_PARSEMAP_ENTRY*) lpEntries;
for (iEntry = 0; iEntry < cEntries; iEntry++, pCurrent++)
{
if (pCurrent->pfn == NULL)
{
delete (AFX_PARSEMAP_ENTRY_PARAMS*) pCurrent->pszFnName;
pCurrent->pszFnName = NULL;
free(pCurrent->pszParamInfo);
pCurrent->pszParamInfo = NULL;
}
}
}
AFX_PARSEMAP_ENTRY_PARAMS::~AFX_PARSEMAP_ENTRY_PARAMS()
{
delete [] ppszInfo;
delete [] ppszDefaults;
delete [] ppszValues;
}
///////////////////////////////////////////////////////////////////////
// Entry points for HTTP Server Extensions
extern "C" DWORD WINAPI HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
{
#ifdef _AFXDLL
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
DWORD dwRet;
ISAPIASSERT(pServer != NULL);
if (pServer == NULL)
{
dwRet = HSE_STATUS_ERROR;
pECB->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
}
else
dwRet = pServer->HttpExtensionProc(pECB);
return dwRet;
}
extern "C" BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO *pVer)
{
#ifdef _AFXDLL
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
BOOL bRet;
ISAPIASSERT(pServer != NULL);
if (pServer == NULL)
bRet = FALSE;
else
bRet = pServer->GetExtensionVersion(pVer);
return bRet;
}
extern "C" BOOL WINAPI TerminateExtension(DWORD dwFlags)
{
#ifdef _AFXDLL
AFX_MANAGE_STATE(AfxGetStaticModuleState());
#endif
if (pServer == NULL)
return TRUE;
return pServer->TerminateExtension(dwFlags);
}
///////////////////////////////////////////////////////////////////////
// CHttpAsyncContext implementation
class CHttpAsyncContext
{
public:
CHttpAsyncContext(HANDLE hFile, LPVOID pvHeader, DWORD dwHeaderLen,
LPVOID pvTail, DWORD dwTailLen);
virtual ~CHttpAsyncContext();
protected:
LPVOID m_pvHeader;
DWORD m_dwHeaderLen;
LPVOID m_pvTail;
DWORD m_dwTailLen;
HANDLE m_hFile;
};
CHttpAsyncContext::CHttpAsyncContext(HANDLE hFile, LPVOID pvHeader,
DWORD dwHeaderLen, LPVOID pvTail, DWORD dwTailLen)
: m_hFile(hFile)
{
if (pvHeader == NULL || dwHeaderLen == 0)
{
m_pvHeader = NULL;
dwHeaderLen = 0;
}
else
{
m_pvHeader = new BYTE[dwHeaderLen+1];
memcpy(m_pvHeader, pvHeader, dwHeaderLen);
reinterpret_cast<BYTE*>(m_pvHeader)[dwHeaderLen] = '\0';
m_dwHeaderLen = dwHeaderLen;
}
if (pvTail == NULL || dwTailLen == 0)
{
m_pvTail = NULL;
dwTailLen = 0;
}
else
{
m_pvTail = new BYTE[dwTailLen+1];
memcpy(m_pvTail, pvTail, dwTailLen);
reinterpret_cast<BYTE*>(m_pvTail)[dwTailLen] = '\0';
m_dwTailLen = dwTailLen;
}
}
CHttpAsyncContext::~CHttpAsyncContext()
{
if (m_hFile != NULL && m_hFile != INVALID_HANDLE_VALUE)
CloseHandle(m_hFile);
delete [] m_pvTail;
delete [] m_pvHeader;
}
///////////////////////////////////////////////////////////////////////
// CHttpServerContext implementation
void CHttpServerContext::Reset()
{
#ifdef _DEBUG
m_dwOldEndOfHeaders = 0;
#endif
m_pStream->Reset();
}
DWORD CHttpServerContext::SetChunkSize(DWORD dwNewSize)
{
DWORD dwReturn = m_dwChunkSize;
m_dwChunkSize = dwNewSize;
return dwReturn;
}
DWORD CHttpServerContext::GetChunkSize() const
{
return m_dwChunkSize;
}
VOID WINAPI AfxIOCallback(EXTENSION_CONTROL_BLOCK* pECB,
PVOID pvContext, DWORD /* cbIO */, DWORD /* dwError */)
{
if (!pECB->ServerSupportFunction(pECB->ConnID, HSE_REQ_DONE_WITH_SESSION, NULL, NULL, NULL))
ISAPITRACE("HttpExtensionProc io callback error\n");
CHttpAsyncContext* pInfo = (CHttpAsyncContext*) pvContext;
delete pInfo;
}
BOOL CHttpServerContext::TransmitFile(HANDLE hFile,
DWORD dwFlags /* = HSE_IO_DISCONNECT_AFTER_SEND */,
LPVOID pvHeader /* = NULL */, DWORD dwHeaderLen /* = 0 */,
LPVOID pvTail /* = NULL */, DWORD dwTailLen /* = 0 */)
{
if (hFile == INVALID_HANDLE_VALUE)
return FALSE;
ISAPIASSERT(dwHeaderLen == 0 || pvHeader != NULL);
ISAPIASSERT(dwTailLen == 0 || pvTail != NULL);
if (dwTailLen == 0 && pvTail != NULL)
dwTailLen = lstrlen((LPCTSTR) pvTail);
if (dwHeaderLen == 0 && pvHeader != NULL)
dwHeaderLen = lstrlen((LPCTSTR) pvHeader);
CHttpAsyncContext* pInfo =
new CHttpAsyncContext(hFile, pvHeader, dwTailLen, pvTail, dwHeaderLen);
HSE_TF_INFO info;
info.pszStatusCode = NULL;
info.hFile = hFile;
info.dwFlags = dwFlags;
info.BytesToWrite = 0;
info.Offset = 0;
info.pfnHseIO = AfxIOCallback;
info.pContext = pInfo;
info.pHead = pvHeader;
info.HeadLength = dwHeaderLen;
info.pTail = pvTail;
info.TailLength = dwTailLen;
BOOL bResult = ServerSupportFunction(HSE_REQ_TRANSMIT_FILE, &info, 0, 0);
if (bResult)
{
m_dwStatusCode = HSE_STATUS_PENDING;
m_bSendHeaders = FALSE;
}
else
delete pInfo;
return bResult;
}
///////////////////////////////////////////////////////////////////////
// CHttpServer implementation
BOOL CHttpServer::TerminateExtension(DWORD /* dwFlags */)
{
// okay to unload at any time, by default
return TRUE;
}
LPTSTR CHttpServer::GetQuery(CHttpServerContext* pCtxt, LPTSTR lpszQuery)
{
// If the request is a POST, get all of the data. First get what's
// already available, then read any additional data via the
// ReadClient() call.
memcpy(lpszQuery, (LPCTSTR) pCtxt->m_pECB->lpbData, pCtxt->m_pECB->cbAvailable);
lpszQuery[pCtxt->m_pECB->cbAvailable] = '\0';
if (pCtxt->m_pECB->cbAvailable < pCtxt->m_pECB->cbTotalBytes)
{
LPTSTR pstrTarget = lpszQuery + pCtxt->m_pECB->cbAvailable;
DWORD cbRemaining = pCtxt->m_pECB->cbTotalBytes - pCtxt->m_pECB->cbAvailable;
DWORD cbRead;
while (cbRemaining > 0)
{
cbRead = cbRemaining;
if (!pCtxt->ReadClient(pstrTarget, &cbRead))
{
ISAPITRACE("Error: only %d of %d bytes read!\n",
pCtxt->m_pECB->cbTotalBytes - cbRemaining,
pCtxt->m_pECB->cbTotalBytes);
return NULL;
}
if (cbRead == 0)
break;
pstrTarget += cbRead;
cbRemaining -= cbRead;
}
*pstrTarget = '\0';
}
pCtxt->m_dwBytesReceived = pCtxt->m_pECB->cbTotalBytes;
return lpszQuery;
}
BOOL CHttpServer::OnWriteBody(CHttpServerContext* pCtxt, LPBYTE pbContent,
DWORD dwSize, DWORD dwReserved /* = 0 */)
{
BOOL bRetVal;
if (pCtxt->m_dwChunkSize == 0)
bRetVal = pCtxt->WriteClient(pbContent, &dwSize, dwReserved);
else
{
LPBYTE pbStart = pbContent;
DWORD dwBytesLeft = dwSize;
bRetVal = TRUE;
while (bRetVal && (dwBytesLeft > 0))
{
DWORD dwThisChunk = min(dwBytesLeft, pCtxt->m_dwChunkSize);
bRetVal = pCtxt->WriteClient(pbStart, &dwThisChunk, dwReserved);
pbStart += dwThisChunk;
dwBytesLeft -= dwThisChunk;
}
}
return bRetVal;
}
DWORD CHttpServer::HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB)
{
DWORD dwRet = HSE_STATUS_SUCCESS;
LPTSTR pszPostBuffer = NULL;
LPTSTR pszQuery;
LPTSTR pszCommand = NULL;
int nMethodRet;
LPTSTR pstrLastChar;
DWORD cbStream = 0;
BYTE* pbStream = NULL;
CHttpServerContext ctxtCall(pECB);
pECB->dwHttpStatusCode = 0;
ISAPIASSERT(NULL != pServer);
if (pServer == NULL)
{
dwRet = HSE_STATUS_ERROR;
goto CleanUp;
}
// get the query
if (lstrcmpi(pECB->lpszMethod, szGet) == 0)
{
pszQuery = pECB->lpszQueryString;
ctxtCall.m_dwBytesReceived = lstrlen(pszQuery);
}
else if (lstrcmpi(pECB->lpszMethod, szPost) == 0)
{
if(pECB->cbTotalBytes == 0xffffffff)
{
dwRet = HSE_STATUS_ERROR ;
pECB->dwHttpStatusCode = HTTP_STATUS_REQUEST_TOO_LARGE ;
goto CleanUp;
}
if(pECB->cbTotalBytes < pECB->cbAvailable)
{
dwRet = HSE_STATUS_ERROR ;
pECB->dwHttpStatusCode = HTTP_STATUS_BAD_REQUEST;
goto CleanUp;
}
pszCommand = pECB->lpszQueryString;
pszPostBuffer = new TCHAR[pECB->cbTotalBytes + 1];
if( pszPostBuffer == NULL )
{
dwRet = HSE_STATUS_ERROR;
goto CleanUp;
}
pszQuery = GetQuery(&ctxtCall, pszPostBuffer);
if (pszQuery == NULL)
{
dwRet = HSE_STATUS_ERROR;
goto CleanUp;
}
}
else
{
ISAPITRACE1("Error: Unrecognized method: %s\n", pECB->lpszMethod);
dwRet = HSE_STATUS_ERROR;
goto CleanUp;
}
// trim junk that some browsers put at the very end
pstrLastChar = pszQuery + ctxtCall.m_dwBytesReceived -1;
while ((*pstrLastChar == ' ' || *pstrLastChar == '\n' ||
*pstrLastChar == '\r') && pstrLastChar > pszQuery)
{
*pstrLastChar-- = '\0';
}
// do something about it
if (!pServer->InitInstance(&ctxtCall))
dwRet = HSE_STATUS_ERROR;
else
{
pECB->dwHttpStatusCode = HTTP_STATUS_OK;
try {
nMethodRet = pServer->CallFunction(&ctxtCall, pszQuery, pszCommand);
}
catch (...)
{
ISAPITRACE1("Error: command %s caused an unhandled exception!\n",
pszQuery);
nMethodRet = callNoStackSpace;
}
// was an error caused by trying to dispatch?
if (nMethodRet != callOK && pECB->dwHttpStatusCode == HTTP_STATUS_OK)
{
dwRet = HSE_STATUS_ERROR;
switch (nMethodRet)
{
case callNoStream:
pECB->dwHttpStatusCode = HTTP_STATUS_NO_CONTENT;
break;
case callParamRequired:
case callBadParamCount:
case callBadParam:
pECB->dwHttpStatusCode = HTTP_STATUS_BAD_REQUEST;
break;
case callBadCommand:
pECB->dwHttpStatusCode = HTTP_STATUS_NOT_IMPLEMENTED;
break;
case callNoStackSpace:
default:
pECB->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
break;
}
}
// if there was no error or the user said they handled
// the error, prepare to spit out the generated HTML
if (nMethodRet == callOK ||
OnParseError(&ctxtCall, nMethodRet))
{
cbStream = ctxtCall.m_pStream->GetStreamSize();
pbStream = ctxtCall.m_pStream->Detach();
}
}
CleanUp:
// if there was an error, return an appropriate status
TCHAR szResponse[64];
BuildStatusCode(szResponse, pECB->dwHttpStatusCode);
DWORD dwSize = cbStream - ctxtCall.m_dwEndOfHeaders;
LPBYTE pbContent = NULL;
BYTE cSaved = 0;
if (pbStream != NULL && ctxtCall.m_bSendHeaders)
{
cSaved = pbStream[ctxtCall.m_dwEndOfHeaders];
pbStream[ctxtCall.m_dwEndOfHeaders] = '\0';
pbContent = &pbStream[ctxtCall.m_dwEndOfHeaders];
}
if (ctxtCall.m_bSendHeaders &&
!ctxtCall.ServerSupportFunction(HSE_REQ_SEND_RESPONSE_HEADER,
szResponse, 0, (LPDWORD) pbStream) &&
::GetLastError() != 10054) // WSAECONNRESET
{
pECB->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
dwRet = HSE_STATUS_ERROR;
#ifdef _DEBUG
DWORD dwCause = ::GetLastError();
ISAPITRACE1("Error: Unable to write headers: 0x%8.8X!\n", dwCause);
#endif
}
else
{
if (pbContent != NULL)
{
BOOL bWorked = TRUE;
// write a newline to separate content from headers
if (ctxtCall.m_bSendHeaders)
{
*pbContent = cSaved;
DWORD dwNewLineSize = 2;
bWorked = ctxtCall.WriteClient(_T("\r\n"), &dwNewLineSize, 0);
}
if (!bWorked || !OnWriteBody(&ctxtCall, pbContent, dwSize))
{
dwRet = HSE_STATUS_ERROR;
pECB->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR;
#ifdef _DEBUG
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -