int CHttpServer::CallMemberFunc(CHttpServerContext* pCtxt, const AFX_PARSEMAP_ENTRY* pEntry, AFX_PARSEMAP_ENTRY* pParams, LPTSTR pszParams) { ISAPIASSERT(NULL != pEntry); AFX_PISAPICMD pFunc = pEntry->pfn; ISAPIASSERT(NULL != pFunc); int nRet = callOK; // get default function and parameters BYTE bNoParams = 0; const BYTE* pbParams = (const BYTE*)pEntry->pszArgs; if (NULL == pbParams) pbParams = &bNoParams; UINT nParams = lstrlenA((LPCSTR)pbParams); AFX_PARSEMAP_ENTRY_PARAMS *pDefaultParams = NULL; if (pParams != NULL) { ::EnterCriticalSection(m_pCritSec); if (pParams->pszFnName == NULL) nRet = ParseDefaultParams(pParams, nParams, pDefaultParams, pbParams); else pDefaultParams = (AFX_PARSEMAP_ENTRY_PARAMS*) pParams->pszFnName; ::LeaveCriticalSection(m_pCritSec); } if (nRet == callOK) { // get default function and return value information AFX_PISAPICMD pfn = pEntry->pfn; // determine size of arguments and allocate stack space // include space for our context pointer UINT nSizeArgs = GetStackSize(pbParams) + sizeof(_STACK_PTR); ISAPIASSERT(nSizeArgs != 0); if (nSizeArgs < _STACK_MIN) nSizeArgs = _STACK_MIN; BYTE* pStack = (BYTE*)_alloca(nSizeArgs + _SCRATCH_SIZE); if (pStack == NULL) { ISAPITRACE0("Error: stack overflow in CHttpServer::CallMemberFunc()!\n"); return callNoStackSpace; } if (pDefaultParams != NULL) #ifndef _SHADOW_DOUBLES nRet = PushDefaultStackArgs(pStack, pCtxt, pbParams, pszParams, pDefaultParams); #else nRet = PushDefaultStackArgs(pStack, pCtxt, pbParams, pszParams, pDefaultParams, nSizeArgs); #endif else
UINT PASCAL CHttpServer::GetStackSize(const BYTE* pbParams) { // size of arguments on stack when pushed by value static const UINT rgnByValue[] = { sizeof(_STACK_INT), // ITS_I2 sizeof(_STACK_LONG), // ITS_I4 sizeof(_STACK_FLOAT), // ITS_R4 sizeof(_STACK_DOUBLE), // ITS_R8 sizeof(LPCTSTR), // ITS_PSTR 0, // ITS_EMPTY }; // sizeof 'this' pointer UINT nCount = sizeof(CHttpServer*); #ifdef _ALIGN_STACK nCount = (nCount + (_ALIGN_STACK-1)) & ~(_ALIGN_STACK-1); #endif // count arguments ISAPIASSERT(pbParams != NULL); while (*pbParams != 0 && *pbParams != IT_EMPTY) { // align if necessary // get and add appropriate byte count #ifdef _ALIGN_DOUBLES // align doubles on 8 byte for some platforms if (*pbParams == IT_R8) nCount = (nCount + _ALIGN_DOUBLES-1) & ~(_ALIGN_DOUBLES-1); #endif // *pbParams must fit in the rgnByValue array ISAPIASSERT(*pbParams >= 1 && *pbParams <= sizeof(rgnByValue)/sizeof(UINT)); nCount += rgnByValue[*pbParams-1]; #ifdef _ALIGN_STACK nCount = (nCount + (_ALIGN_STACK-1)) & ~(_ALIGN_STACK-1); #endif ++pbParams; } #if defined(_ALIGN_DOUBLES) && defined(_SHADOW_DOUBLES) // align doubles on 8 byte for some platforms nCount = (nCount + _ALIGN_DOUBLES-1) & ~(_ALIGN_DOUBLES-1); #endif return nCount; }
CHttpServer::CHttpServer(TCHAR cDelimiter /* = '&' */) : m_cTokenDelimiter(cDelimiter) { ISAPIASSERT(pServer == NULL); // only one server instance pServer = this; // allocate our critical section directly to avoid bogus traces m_pCritSec = (LPCRITICAL_SECTION) LocalAlloc(LPTR, sizeof(CRITICAL_SECTION)); ::InitializeCriticalSection(m_pCritSec); }
void CHttpServer::BuildStatusCode(LPTSTR pszResponse, DWORD dwCode) { ISAPIASSERT(pszResponse != NULL); HTTPStatusInfo* pInfo = statusStrings; while (pInfo->pstrString != NULL) { if (dwCode == pInfo->dwCode) break; pInfo++; } if (pInfo->pstrString != NULL) wsprintf(pszResponse, _T("%d %s"), dwCode, pInfo->pstrString); else { ISAPIASSERT(dwCode != HTTP_STATUS_OK); ISAPITRACE1("Warning: Nonstandard status code %d\n", dwCode); BuildStatusCode(pszResponse, HTTP_STATUS_OK); } }
void CHttpServer::AddHeader(CHttpServerContext* pCtxt, LPCTSTR pszString) const { #ifdef _DEBUG // Can't call AddHeader() after writing directly to the stream. ISAPIASSERT(pCtxt->m_dwOldEndOfHeaders == pCtxt->m_pStream->GetStreamSize()); #endif *pCtxt << pszString; pCtxt->m_dwEndOfHeaders = pCtxt->m_pStream->GetStreamSize(); #ifdef _DEBUG pCtxt->m_dwOldEndOfHeaders = pCtxt->m_dwEndOfHeaders; #endif }
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" 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; }
const AFX_PARSEMAP_ENTRY* CHttpServer::LookUp(LPCTSTR pszMethod, const AFX_PARSEMAP*& pMap, AFX_PARSEMAP_ENTRY*& pParams, AFX_PISAPICMD pCmdDefault /* = NULL */) { UINT iEntry; LPCTSTR pszFnName; const AFX_PARSEMAP* pParseMap = GetParseMap(); const AFX_PARSEMAP* pBaseMap; const AFX_PARSEMAP_ENTRY* pRet = NULL; while (pParseMap != NULL && pRet == NULL) { UINT cEntries = (*pParseMap->pfnGetNumMapEntries)(); const AFX_PARSEMAP_ENTRY* pCurrent = pParseMap->lpEntries; for (iEntry = 0; iEntry < cEntries && pRet == NULL; iEntry++, pCurrent++) { #ifdef _DEBUG // make sure not 2 parameter maps in a row if (pCurrent->pfn == NULL && (iEntry+1 < cEntries)) ISAPIASSERT(pCurrent[1].pfn != NULL); #endif // skip parameter maps if (pCurrent->pfn == NULL) continue; pszFnName = pCurrent->pszFnName; // if the caller wants the default command, find that-- // if the caller wants something specific, perform a compare // otherwise, see if we recursed to look up the default command if (pCmdDefault == NULL) { if (pszMethod == NULL && pCurrent->pszArgs == NULL) pRet = pCurrent; else if (pszMethod != NULL && pCurrent->pszArgs != NULL && _tcsicmp(pszFnName, pszMethod) == 0) pRet = pCurrent; } else if (pCurrent->pfn == pCmdDefault && pCurrent->pszArgs != NULL) pRet = pCurrent; if (pRet != NULL) { // if we need the default, recurse to find it by name if (pszMethod == NULL && pCmdDefault == NULL) return LookUp(NULL, pMap, pParams, pCurrent->pfn); // found it! see if there are parameters if (iEntry+1 >= cEntries || pCurrent[1].pfn != NULL) { pParams = NULL; pMap = NULL; } else { pParams = (AFX_PARSEMAP_ENTRY*) &(pCurrent[1]); pMap = pParseMap; } } } #ifdef _AFXDLL pBaseMap = (*pParseMap->pfnGetBaseMap)(); #else pBaseMap = pParseMap->pBaseMap; #endif // catch simple mistake of BEGIN_PARSE_MAP(CMyClass, CMyClass) ISAPIASSERT(pBaseMap != pParseMap); pParseMap = pBaseMap; } // no matching entry ? if (pRet == NULL) ISAPITRACE1("Warning: no handler for command '%s'\n", pszMethod); return pRet; }
int CHttpServer::CallFunction(CHttpServerContext* pCtxt, LPTSTR pszQuery, LPTSTR pszCommand) { int nRet; AFX_PARSEMAP_ENTRY* pParams; const AFX_PARSEMAP* pMap; const AFX_PARSEMAP_ENTRY* pFn; ISAPIASSERT(pCtxt->m_pStream == NULL); pCtxt->m_pStream = ConstructStream(); if (pCtxt->m_pStream == NULL) nRet = callNoStream; else { ISAPIASSERT(pszQuery != NULL); if (pszQuery == NULL) nRet = callBadCommand; else { LPTSTR pszMethod; LPTSTR pszParams; // did the user specify a command via "MfcISAPICommand"? LPTSTR pszHiddenCommand = _tcschr(pszQuery, '='); if (pszHiddenCommand != NULL) { *pszHiddenCommand = '\0'; // is it there? if (_tcsicmp(pszQuery, _T("MfcISAPICommand")) == 0) { // did they have a method, too? pszMethod = pszHiddenCommand+1; if (*pszMethod == '\0') pszParams = pszMethod; else { pszParams = _tcschr(pszMethod, m_cTokenDelimiter); if (pszParams != NULL && *pszParams != '\0') *pszParams++ = '\0'; } // if we find it, we can call it pFn = LookUp(pszMethod, pMap, pParams); if (pFn != NULL) goto MakeTheCall; } // we didn't find the command, or we didn't have // "MfcISAPICommand", so we'll try and process things // normally... *pszHiddenCommand = '='; } if (pszCommand == NULL) { // got something via a GET method // is the first thing a parameter or a command? LPTSTR pszEquals; LPTSTR pszQMark; pszParams = _tcschr(pszQuery, m_cTokenDelimiter); pszQMark = _tcschr(pszQuery, '?'); // Parameters start at the first delimiter if (pszParams == NULL || (pszQMark != NULL && (pszQMark < pszParams))) { pszParams = pszQMark; // if the command ends in question mark // and nothing else, ignore it if (pszQMark != NULL && pszQMark[1] == '\0') { *pszQMark = '\0'; pszParams = NULL; } } // Does an equals sign show up before the first delimiter? pszEquals = _tcschr(pszQuery, '='); if (pszEquals == NULL || pszEquals > pszParams) { // It might be a command. If it isn't blank, // try and find it in the parameter map--if // we can't, then assume it is a parameter. pszMethod = pszQuery; if (*pszMethod != '\0') { TCHAR cTemp; if (pszParams != NULL) { cTemp = *pszParams; *pszParams++ = '\0'; } pFn = LookUp(pszMethod, pMap, pParams); if (pFn != NULL) goto MakeTheCall; if (pszParams != NULL) *--pszParams = cTemp; } } // we can be sure it's a parameter if (pszQMark == NULL || pszQMark >= pszParams) { // default command, params as supplied pszMethod = NULL; pszParams = pszQuery; } else { pszMethod = pszQuery; *pszQMark++ = '\0'; pszParams = pszQMark; } } else { // with a POST, the verb arrives seperately pszMethod = pszCommand; pszParams = pszQuery; } // is it a default verb? if (pszMethod != NULL && _tcslen(pszMethod) == 0) pszMethod = NULL; // is it a useless parameter? if (pszParams != NULL && _tcslen(pszParams) == 0) pszParams = NULL; pFn = LookUp(pszMethod, pMap, pParams); MakeTheCall: if (pFn == NULL) nRet = callBadCommand; else { pCtxt->m_pStream->InitStream(); nRet = CallMemberFunc(pCtxt, pFn, pParams, pszParams); } } } return nRet; }
DWORD CHttpServer::HttpExtensionProc(EXTENSION_CONTROL_BLOCK *pECB) { DWORD dwRet = HSE_STATUS_SUCCESS; BOOL bDefault = FALSE; 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 (_tcsicmp(pECB->lpszMethod, szGet) == 0) { pszQuery = pECB->lpszQueryString; } else if (_tcsicmp(pECB->lpszMethod, szPost) == 0) { pszCommand = pECB->lpszQueryString; pszPostBuffer = new TCHAR[pECB->cbAvailable + 1]; pszQuery = GetQuery(&ctxtCall, pszPostBuffer, pECB->cbAvailable); } 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 + _tcslen(pszQuery) -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) == TRUE) { 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; BYTE* pbContent = NULL; BYTE cSaved; if (pbStream != NULL) { cSaved = pbStream[ctxtCall.m_dwEndOfHeaders]; pbStream[ctxtCall.m_dwEndOfHeaders] = '\0'; pbContent = &pbStream[ctxtCall.m_dwEndOfHeaders]; } if (!ctxtCall.ServerSupportFunction( HSE_REQ_SEND_RESPONSE_HEADER, szResponse, 0, (LPDWORD) pbStream)) { pECB->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR; dwRet = HSE_STATUS_ERROR; #ifdef _DEBUG DWORD dwCause = ::GetLastError(); ISAPITRACE1("Error: Unable to write headers: %8.8X!\n", dwCause); #endif } else { if (pbContent != NULL) { // write a newline to separate content from headers *pbContent = cSaved; DWORD dwNewLineSize = 2; if (!ctxtCall.WriteClient(_T("\r\n"), &dwNewLineSize, 0) || !ctxtCall.WriteClient(pbContent, &dwSize, 0)) { dwRet = HSE_STATUS_ERROR; pECB->dwHttpStatusCode = HTTP_STATUS_SERVER_ERROR; ISAPITRACE("Error: Unable to write content body!\n"); } } else ISAPITRACE("Error: No body content!\n"); } if (pbStream != NULL) ctxtCall.m_pStream->Free(pbStream); if (dwRet == HSE_STATUS_SUCCESS) pECB->dwHttpStatusCode = HTTP_STATUS_OK; if (pszPostBuffer != NULL) delete [] pszPostBuffer; return dwRet; }
CHttpServerContext& CHttpServerContext::operator<<(const CByteArray& array) { ISAPIASSERT(m_pStream != NULL); if (m_pStream != NULL) *m_pStream << array; return *this; }
CHttpServerContext& CHttpServerContext::operator<<(const CLongBinary& blob) { ISAPIASSERT(m_pStream != NULL); if (m_pStream != NULL) *m_pStream << blob; return *this; }