/*++ Copyright (c) 1997 Microsoft Corporation Module Name: UReadCli.c Abstract: This module tests the ReadClient for both Sync and Async calls Author: Stanle Tam (stanleyt) 4-June 1997 Revision History: --*/ #include #include #include #include #define MAX_BUF_SIZE 49152 // 48 K - max number of bytes for each read #define PARC_IO_CTX LPDWORD // Context for Async ReadClient // // Number of bytes read so far - pContext of the IO completion routine // PARC_IO_CTX pByteReadSoFar; // // Buffer to store bytes each time that read from client // BYTE g_ReadBuffer[MAX_BUF_SIZE] = {0}; DWORD SendHeaderToClient(IN LPEXTENSION_CONTROL_BLOCK pecb, IN LPCSTR pszErrorMsg, IN BOOL fKeepAlive); DWORD DoReadClient(IN LPEXTENSION_CONTROL_BLOCK pecb); DWORD DoSyncRC(IN LPEXTENSION_CONTROL_BLOCK pecb); DWORD DoAsyncRC(IN LPEXTENSION_CONTROL_BLOCK pecb); VOID WINAPI AsyncReadClientIoCompletion(IN LPEXTENSION_CONTROL_BLOCK pecb, IN PVOID pContext, IN DWORD cbIO, IN DWORD dwError); BOOL ValidBytes (IN LPEXTENSION_CONTROL_BLOCK pecb, IN BYTE * pbSrc, IN DWORD dwByteRead, OUT LPDWORD dwOffSet); DWORD IsKeepAlive(IN LPEXTENSION_CONTROL_BLOCK pecb); DllLibMain( IN HINSTANCE hinstDll, IN DWORD fdwReason, IN LPVOID lpvContext OPTIONAL) /*++ Routine Description: This function DllLibMain() is the main initialization function for this DLL. It initializes local variables and prepares it to be invoked subsequently. Arguments: hinstDll Instance Handle of the DLL fdwReason Reason why NT called this DLL lpvReserved Reserved parameter for future use. fReturn Value: fReturns TRUE is successful; otherwise FALSE is fReturned. --*/ { BOOL fReturn = TRUE; switch (fdwReason ) { case DLL_PROCESS_ATTACH: { // // Initialize various data and modules. // break; } /* case DLL_PROCESS_ATTACH */ case DLL_PROCESS_DETACH: { // // Only cleanup when we are called because of a FreeLibrary(). // i.e., when lpvContext == NULL // If we are called because of a process termination, // dont free anything. System will free resources and memory for us. // if ( lpvContext != NULL) { } break; } /* case DLL_PROCESS_DETACH */ default: break; } /* switch */ return (fReturn); } /* DllLibMain() */ BOOL WINAPI GetExtensionVersion(HSE_VERSION_INFO * Version) { Version->dwExtensionVersion = MAKELONG(HSE_VERSION_MINOR, HSE_VERSION_MAJOR); strcpy(Version->lpszExtensionDesc, "Universal Read Client Test ISAPI DLL"); return TRUE; } DWORD WINAPI HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK pecb) { DWORD hseStatus; if ( 0 == strcmp(pecb->lpszQueryString, "") || (0 == strspn(pecb->lpszQueryString, "SYNC" ) && 0 == strspn(pecb->lpszQueryString, "ASYNC")) ) { hseStatus = SendHeaderToClient( pecb, "Expected to specify what kind of ReadClient to test.\r\n" \ "SYNC - Sync ReadClient\r\n" \ "ASYNC - Async ReadClient", FALSE); } else { hseStatus = DoReadClient( pecb); } return (hseStatus); } BOOL WINAPI TerminateExtension(DWORD dwFlags) { return TRUE; } DWORD SendHeaderToClient(IN LPEXTENSION_CONTROL_BLOCK pecb, IN LPCSTR pszErrorMsg, IN BOOL fKeepAlive) { BOOL fReturn; CHAR szText[MAX_PATH] = ""; CHAR szHeader[MAX_PATH] = ""; DWORD cbText; DWORD hseStatus = HSE_STATUS_SUCCESS; // // The HTTP header block is terminated by a blank '\r\n' pair, // followed by the document body // cbText = wsprintf( szText, "Unified Read Client\n" "

%s

\n", pszErrorMsg ); if ( fKeepAlive) { wsprintf( szHeader, "Content-type: text/html\r\n" "Connection: keep-alive\r\n" "Content-Length: %d\r\n" "\r\n", cbText); fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, "200 OK", NULL, (LPDWORD) szHeader) && pecb->WriteClient( pecb->ConnID, szText, &cbText, 0 ); } else { wsprintf( szHeader, "Content-Type: text/html\r\n" "\r\n" "Unified Read Client\n" "

%s

\n", pszErrorMsg ); fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_SEND_RESPONSE_HEADER, "200 OK", NULL, (LPDWORD) szHeader); } if ( !fReturn) { fReturn = HSE_STATUS_ERROR; } return (fReturn); } DWORD DoReadClient(IN LPEXTENSION_CONTROL_BLOCK pecb) { char szHeader[256] = ""; BOOL fReturn = TRUE; BOOL fKeepAlive = FALSE; DWORD dwLocation; DWORD hseStatus = HSE_STATUS_SUCCESS; // // Firstly, check if there is any corrupted byte in the // first chunk (the first chunk could be the last chunk, ie // when cbAvailable==cbToTalBytes). Expecting the client to send // bytes in the format of "0123456789012345678....". // dwLocation = 0; if ( ! ValidBytes ( pecb, pecb->lpbData, // check these bytes pecb->cbAvailable, // number of bytes read &dwLocation)) { // offset, 0 = starts from the first byte wsprintf( szHeader, "Bad data at location %d.", dwLocation); hseStatus = SendHeaderToClient(pecb, szHeader, FALSE); fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_DONE_WITH_SESSION, &hseStatus, NULL, NULL); return (HSE_STATUS_ERROR); } // // Check if cbTotalBytes == cbAvailable // if so lpbData contains all the data sent by // the client, and complete the session. Very likely.. // if (pecb->cbTotalBytes == pecb->cbAvailable) { wsprintf ( szHeader, "ECB Total Bytes: %d. Actual Read Bytes: %d", pecb->cbTotalBytes, pecb->cbAvailable); if ( HSE_STATUS_SUCCESS_AND_KEEP_CONN == (hseStatus = IsKeepAlive(pecb)) ) { SendHeaderToClient(pecb, szHeader, TRUE); } else { SendHeaderToClient(pecb, szHeader, FALSE); fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_DONE_WITH_SESSION, &hseStatus, NULL, NULL); if ( !fReturn) hseStatus = HSE_STATUS_ERROR; } return (hseStatus); } // // we already ensured the validity of the query string // inside HttpExtensionProc, hence simple check here // if ( strspn(pecb->lpszQueryString, "SYNC" ) ) return ( DoSyncRC(pecb)); else return ( DoAsyncRC(pecb)); } DWORD DoSyncRC(IN LPEXTENSION_CONTROL_BLOCK pecb) { CHAR szHeader[256] = ""; BOOL fReturn = TRUE; DWORD cbReadSoFar; DWORD dwOffSet; DWORD cbCurrentRead = MAX_BUF_SIZE; DWORD hseStatus = HSE_STATUS_SUCCESS; cbReadSoFar = pecb->cbAvailable; while ( cbReadSoFar < pecb->cbTotalBytes) { if ((pecb->cbTotalBytes - cbReadSoFar) < sizeof(g_ReadBuffer)) cbCurrentRead = (pecb->cbTotalBytes - cbReadSoFar); else cbCurrentRead = sizeof(g_ReadBuffer); fReturn = pecb->ReadClient( pecb->ConnID, g_ReadBuffer, &cbCurrentRead); if (!fReturn) { wsprintf( szHeader, "Problem on ReadClient()"); hseStatus = SendHeaderToClient(pecb, szHeader, FALSE); fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_DONE_WITH_SESSION, &hseStatus, NULL, NULL); hseStatus = HSE_STATUS_ERROR; break; } dwOffSet = cbReadSoFar; // where it left off last time if ( ! ValidBytes ( pecb, g_ReadBuffer, // check these bytes cbCurrentRead, // number of bytes read &dwOffSet )) { wsprintf( szHeader, "Bad data at location %d.", dwOffSet ); hseStatus = SendHeaderToClient(pecb, szHeader, FALSE); fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_DONE_WITH_SESSION, &hseStatus, NULL, NULL); hseStatus = HSE_STATUS_ERROR; break; } cbReadSoFar += cbCurrentRead; // update current total bytes read } // // if they are equal, ie all bytes are read // if (cbReadSoFar >= pecb->cbTotalBytes) { wsprintf ( szHeader, "ECB Total Bytes: %d. Actual Read Bytes: %d", pecb->cbTotalBytes, cbReadSoFar ); if ( HSE_STATUS_SUCCESS_AND_KEEP_CONN == (hseStatus = IsKeepAlive(pecb)) ) { SendHeaderToClient(pecb, szHeader, TRUE); } else { SendHeaderToClient(pecb, szHeader, FALSE); fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_DONE_WITH_SESSION, &hseStatus, NULL, NULL); if ( !fReturn) hseStatus = HSE_STATUS_ERROR; } } return (hseStatus); // default = HSE_STATUS_SUCCESS } DWORD DoAsyncRC(IN LPEXTENSION_CONTROL_BLOCK pecb) { char szHeader[256] = ""; BOOL fReturn = TRUE; DWORD dwFlags; DWORD cbTotalToRead = MAX_BUF_SIZE; DWORD hseStatus = HSE_STATUS_PENDING; // // Initialize the context for ReadClient // pByteReadSoFar = &(pecb->cbAvailable); fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_IO_COMPLETION, AsyncReadClientIoCompletion, 0, pByteReadSoFar); if (!fReturn) { hseStatus = HSE_STATUS_ERROR; } dwFlags = HSE_IO_ASYNC; fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_ASYNC_READ_CLIENT, g_ReadBuffer, &cbTotalToRead, &dwFlags); if (!fReturn) { hseStatus = HSE_STATUS_ERROR; } return (hseStatus); } VOID WINAPI AsyncReadClientIoCompletion( IN LPEXTENSION_CONTROL_BLOCK pecb, IN PVOID pContext, IN DWORD cbIO, IN DWORD dwError) /*++ Routine Description: This is the io completion routine for ReadClient Arguments: pecb - extension control block pContext - this is a PASYNC_RC_O_STRUCTURE cbIO - bytes read dwError - error on read fReturn Value: None --*/ { BOOL fReturn; DWORD dwFlags; DWORD dwOffSet; DWORD cbTotalToRead = MAX_BUF_SIZE; DWORD hseStatus = HSE_STATUS_SUCCESS; LPDWORD pcbTotalReadSoFar = (LPDWORD) pContext; __try { if (ERROR_SUCCESS == dwError) { CHAR szHeader[100] = ""; dwOffSet = *pcbTotalReadSoFar; // where it left off last time *pcbTotalReadSoFar += cbIO; // update current total bytes read if ( ! ValidBytes ( pecb, g_ReadBuffer, // check these bytes cbIO, // number of bytes read &dwOffSet )) { wsprintf( szHeader, "Bad data at location %d.", dwOffSet ); SendHeaderToClient(pecb, szHeader, FALSE); hseStatus = HSE_STATUS_ERROR; __leave; } // // if they are equal, ie all bytes are read // if (*pcbTotalReadSoFar >= pecb->cbTotalBytes ) { wsprintf (szHeader, "ECB Total Bytes: %d. Actual Read Bytes: %d", pecb->cbTotalBytes, *pcbTotalReadSoFar ); if ( HSE_STATUS_SUCCESS_AND_KEEP_CONN == (hseStatus = IsKeepAlive(pecb)) ) { SendHeaderToClient(pecb, szHeader, TRUE); } else { SendHeaderToClient(pecb, szHeader, FALSE); fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_DONE_WITH_SESSION, &hseStatus, NULL, NULL); if (!fReturn) { hseStatus = HSE_STATUS_ERROR; __leave; } } // // Read remaining bytes // } else { dwFlags = HSE_IO_ASYNC; fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_ASYNC_READ_CLIENT, g_ReadBuffer, &cbTotalToRead, &dwFlags); if (!fReturn) { hseStatus = HSE_STATUS_ERROR; __leave; } } // // Error on read // } else { hseStatus = dwError; __leave; } } // __try __finally { if (hseStatus != HSE_STATUS_SUCCESS) { fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_DONE_WITH_SESSION, &hseStatus, NULL, NULL); } } // __finally } /*++ Routine Description: This function checks if client issues Keep-Alive connection Arguments: pecb ECB pfKeepAlive KeepAlive flag fReturn Value: HSE Return Code - either HSE_STATUS_SUCCESS or HSE_STATUS_ERROR pfKeepAlive - shows whether the client issues Keep-Alive --*/ DWORD IsKeepAlive( IN LPEXTENSION_CONTROL_BLOCK pecb) { CHAR szBuff[256] = {0}; DWORD cbBuff = sizeof(szBuff); DWORD hseStatus = HSE_STATUS_SUCCESS; BOOL fReturn; fReturn = pecb->GetServerVariable( pecb->ConnID, "HTTP_CONNECTION", szBuff, &cbBuff ); if ( ! fReturn) { wsprintf( szBuff, "Client does not specify keep-alive connection. No keep-alive."); SendHeaderToClient(pecb, szBuff, FALSE); fReturn = pecb->ServerSupportFunction( pecb->ConnID, HSE_REQ_DONE_WITH_SESSION, &hseStatus, NULL, NULL); if ( !fReturn) hseStatus = HSE_STATUS_ERROR; } // // in order to achieve keep-alive, client has // to ensure keep-alive as well // if ( !_strnicmp( szBuff, "Keep-Alive", 10 )) hseStatus = HSE_STATUS_SUCCESS_AND_KEEP_CONN ; return ( hseStatus); } /*++ Routine Description: This function checks if any byte in the buffer is corrupted. Arguments: pecb ECB pbSrc The source buffer dwByteRead Number of bytes read dwOffSet [IN] Start from where it left out in last read [OUT] Return the location of the first invalid byte fReturn Value: TRUE - Discovered bad byte and return its location FALSE - All bytes are valid --*/ BOOL ValidBytes (IN LPEXTENSION_CONTROL_BLOCK pecb, IN BYTE * pbSrc, IN DWORD dwBytesRead, OUT LPDWORD pbOffSet) { DWORD i; BOOL fValidByte = TRUE; for (i = 0; i < dwBytesRead; i++) { if (pbSrc[i] != ((*pbOffSet + i) % 10) + '0') { if ( ((i + *pbOffSet) == pecb->cbTotalBytes) && (pbSrc[i] == 0x0d) && (pbSrc[i+1] == 0x0a)) { break; // ALL good bytes } else { fValidByte = FALSE; *pbOffSet = i; break; // First bad byte } } } return (fValidByte); }