467 lines
12 KiB
C
467 lines
12 KiB
C
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
rasync.c
|
|
|
|
Abstract:
|
|
|
|
This module tests the web server's server extension interface
|
|
|
|
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);
|
|
|
|
DWORD
|
|
DoReadClient(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);
|
|
|
|
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, "Async Read Client Test ISAPI DLL");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD WINAPI
|
|
HttpExtensionProc(LPEXTENSION_CONTROL_BLOCK pecb)
|
|
{
|
|
DWORD hseStatus;
|
|
DWORD cbExpectedByteRead;
|
|
|
|
cbExpectedByteRead = (DWORD)atol(pecb->lpszQueryString);
|
|
|
|
if (0 == strcmp(pecb->lpszQueryString, "")) {
|
|
|
|
hseStatus = SendHeaderToClient(
|
|
pecb,
|
|
"Expected total number of bytes from client is not specified.");
|
|
|
|
} else if ( (DWORD)atol(pecb->lpszQueryString) != pecb->cbTotalBytes ) {
|
|
|
|
hseStatus = SendHeaderToClient(
|
|
pecb,
|
|
"Expected total number of bytes from client \
|
|
does not match with pecb->cbTotalBytes.");
|
|
} else {
|
|
|
|
hseStatus = DoReadClient( pecb );
|
|
}
|
|
|
|
return (hseStatus);
|
|
}
|
|
|
|
|
|
BOOL WINAPI
|
|
TerminateExtension(DWORD dwFlags)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
DWORD
|
|
SendHeaderToClient(IN LPEXTENSION_CONTROL_BLOCK pecb, IN LPCSTR pszErrorMsg)
|
|
{
|
|
CHAR szBuff[MAX_PATH] = "";
|
|
|
|
//
|
|
// The HTTP header block is terminated by a blank '\r\n' pair,
|
|
// followed by the document body
|
|
//
|
|
|
|
wsprintf(szBuff,
|
|
"Content-Type: text/html\r\n"
|
|
"\r\n"
|
|
"<head><title>Simple Async Read Client</title></head>\n"
|
|
"<body><h1>%s</h1>\n",
|
|
pszErrorMsg );
|
|
|
|
if ( !pecb->ServerSupportFunction( pecb->ConnID,
|
|
HSE_REQ_SEND_RESPONSE_HEADER,
|
|
"200 OK",
|
|
NULL,
|
|
(LPDWORD) szBuff)
|
|
) {
|
|
|
|
return (HSE_STATUS_ERROR);
|
|
}
|
|
|
|
return (HSE_STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
DWORD
|
|
DoReadClient(IN LPEXTENSION_CONTROL_BLOCK pecb)
|
|
{
|
|
char szHeader[256] = "";
|
|
BOOL fReturn = TRUE;
|
|
DWORD dwFlags;
|
|
DWORD dwLocation;
|
|
DWORD cbTotalToRead = MAX_BUF_SIZE;
|
|
DWORD hseStatus = HSE_STATUS_PENDING;
|
|
|
|
//
|
|
// 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);
|
|
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 );
|
|
|
|
hseStatus = SendHeaderToClient(pecb, szHeader);
|
|
|
|
fReturn =
|
|
pecb->ServerSupportFunction(
|
|
pecb->ConnID,
|
|
HSE_REQ_DONE_WITH_SESSION,
|
|
&hseStatus,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (!fReturn || hseStatus == HSE_STATUS_ERROR) {
|
|
hseStatus = HSE_STATUS_ERROR;
|
|
} else {
|
|
hseStatus = HSE_STATUS_SUCCESS;
|
|
}
|
|
|
|
return (hseStatus);
|
|
}
|
|
|
|
//
|
|
// 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 - Context for Async ReadClient
|
|
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;
|
|
|
|
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 );
|
|
hseStatus = SendHeaderToClient(pecb, szHeader);
|
|
fReturn =
|
|
pecb->ServerSupportFunction(
|
|
pecb->ConnID,
|
|
HSE_REQ_DONE_WITH_SESSION,
|
|
&hseStatus,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
|
|
//
|
|
// 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 );
|
|
|
|
hseStatus = SendHeaderToClient(pecb, szHeader);
|
|
|
|
fReturn =
|
|
pecb->ServerSupportFunction(
|
|
pecb->ConnID,
|
|
HSE_REQ_DONE_WITH_SESSION,
|
|
&hseStatus,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (!fReturn) {
|
|
hseStatus = HSE_STATUS_ERROR;
|
|
}
|
|
//
|
|
// Read remaining bytes
|
|
//
|
|
} else {
|
|
|
|
dwFlags = HSE_IO_ASYNC;
|
|
fReturn =
|
|
pecb->ServerSupportFunction(
|
|
pecb->ConnID,
|
|
HSE_REQ_ASYNC_READ_CLIENT,
|
|
g_ReadBuffer,
|
|
&cbTotalToRead,
|
|
&dwFlags);
|
|
}
|
|
//
|
|
// Error on read
|
|
//
|
|
} else {
|
|
|
|
hseStatus = dwError;
|
|
}
|
|
|
|
if (hseStatus != HSE_STATUS_SUCCESS) {
|
|
|
|
fReturn =
|
|
pecb->ServerSupportFunction(
|
|
pecb->ConnID,
|
|
HSE_REQ_DONE_WITH_SESSION,
|
|
&hseStatus,
|
|
NULL,
|
|
NULL);
|
|
}
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
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
|
|
only if this returns FALSE
|
|
|
|
fReturn Value:
|
|
|
|
TRUE - All bytes are valid
|
|
FALSE - Discovered bad byte and return its location
|
|
|
|
|
|
--*/
|
|
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);
|
|
}
|