2025-04-27 07:49:33 -04:00

700 lines
19 KiB
C

/*++
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 <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <iisext.h>
#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,
"<head><title>Unified Read Client</title></head>\n"
"<body><h1>%s</h1>\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"
"<head><title>Unified Read Client</title></head>\n"
"<body><h1>%s</h1>\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);
}