336 lines
6.4 KiB
C
336 lines
6.4 KiB
C
/*++
|
||
|
||
Copyright (c) 1997 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
cte_enc.c
|
||
|
||
Abstract:
|
||
|
||
This module contains routines implementing Chunked Transfer
|
||
Encoding (CTE) for ISAPI Extension DLLs. See Section 3.6
|
||
"Transfer Codings" of RFC 2068 for details.
|
||
|
||
Functions:
|
||
CteBeginWrite
|
||
CteWrite
|
||
CteEndWrite
|
||
|
||
--*/
|
||
|
||
|
||
#include "ctetest.h"
|
||
|
||
|
||
//
|
||
// Encoder context structure
|
||
//
|
||
|
||
typedef struct CTE_ENCODER_STRUCT {
|
||
EXTENSION_CONTROL_BLOCK * pECB; // a copy of current ECB pointer
|
||
DWORD dwChunkSize; // user-specified chunk size
|
||
DWORD cbData; // number of bytes in the buffer
|
||
BYTE * pData; // pointer to chunk data bytes
|
||
} CTE_ENCODER;
|
||
|
||
//
|
||
// Chunk header consists of HEX string for the chunk size in bytes
|
||
// (DWORD needs up to 8 bytes in HEX), followed by CRLF,
|
||
// therefore the maximum chunk header size is 10 bytes.
|
||
//
|
||
|
||
#define CTE_MAX_CHUNK_HEADER_SIZE 10
|
||
|
||
//
|
||
// Chunk data is always followed by CRLF
|
||
//
|
||
|
||
#define CTE_MAX_ENCODING_OVERHEAD (CTE_MAX_CHUNK_HEADER_SIZE + 2)
|
||
|
||
//
|
||
// Total encoder size includes:
|
||
// the size of the encoder context structure itself,
|
||
// the chunk data size,
|
||
// the maximum encoding overhead (header and terminating CRLF)
|
||
//
|
||
|
||
#define CTE_ENCODER_SIZE(dwChunkSize) \
|
||
(sizeof(CTE_ENCODER) + dwChunkSize + CTE_MAX_ENCODING_OVERHEAD)
|
||
|
||
|
||
|
||
HCTE_ENCODER
|
||
CteBeginWrite(
|
||
IN EXTENSION_CONTROL_BLOCK * pECB,
|
||
IN DWORD dwChunkSize
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
Allocate and initialize chunked transfer encoder context
|
||
|
||
Arguments:
|
||
|
||
pECB - pointer to extension control as passed to HttpExtensionProc()
|
||
dwChunkSize - the maximum size of the chunk to transmit
|
||
|
||
Returns:
|
||
encoder context handle, or
|
||
NULL if memory allocation failed or chunk size was zero
|
||
|
||
--*/
|
||
{
|
||
HCTE_ENCODER h;
|
||
|
||
|
||
//
|
||
// reject zero-length chunk size
|
||
//
|
||
|
||
if( dwChunkSize == 0 ) {
|
||
SetLastError( ERROR_INVALID_PARAMETER );
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// allocate context structure
|
||
//
|
||
|
||
h = LocalAlloc( LMEM_FIXED, CTE_ENCODER_SIZE(dwChunkSize) );
|
||
|
||
if( h != NULL ) {
|
||
|
||
//
|
||
// initialize context structure
|
||
//
|
||
|
||
h->pECB = pECB;
|
||
h->dwChunkSize = dwChunkSize;
|
||
h->cbData = 0;
|
||
|
||
//
|
||
// chunk data bytes follow the context structure itself
|
||
// and chunk header
|
||
//
|
||
|
||
h->pData = (BYTE *) h + sizeof( *h ) + CTE_MAX_CHUNK_HEADER_SIZE;
|
||
|
||
//
|
||
// this is the CRLF which follows chunk size
|
||
// (and immediately precedes data)
|
||
//
|
||
|
||
h->pData[-2] = '\r';
|
||
h->pData[-1] = '\n';
|
||
|
||
} else {
|
||
|
||
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
||
}
|
||
|
||
return h;
|
||
}
|
||
|
||
|
||
static BOOL
|
||
CteSendChunk(
|
||
IN HCTE_ENCODER h
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
Send one chunk of data using ClientWrite()
|
||
<hex encoded chunk size>, CRLF, data bytes, if any, CRLF
|
||
|
||
Arguments:
|
||
|
||
h - CTE Encoder handle
|
||
|
||
Returns:
|
||
|
||
TRUE if WriteClient succeeded
|
||
FALSE if WriteClient failed
|
||
|
||
--*/
|
||
{
|
||
char szChunkLength[9];
|
||
DWORD cbChunkLength;
|
||
BYTE *buf;
|
||
DWORD cbToSend;
|
||
BOOL success;
|
||
|
||
//
|
||
// produce hex string of the number of bytes
|
||
// and compute the length of this string
|
||
//
|
||
_itoa( h->cbData, szChunkLength, 16 );
|
||
cbChunkLength = strlen( szChunkLength );
|
||
|
||
//
|
||
// step back to make place for hex number and CRLF,
|
||
// copy hex string to its location
|
||
//
|
||
|
||
buf = h->pData - 2 - cbChunkLength;
|
||
memmove( buf, szChunkLength, cbChunkLength );
|
||
|
||
//
|
||
// compute the number of bytes to send
|
||
// (this includes chunk data size, hex string and CRLF)
|
||
//
|
||
|
||
cbToSend = h->cbData + cbChunkLength + 2;
|
||
|
||
//
|
||
// append trailing CRLF right after the data bytes
|
||
//
|
||
|
||
buf[cbToSend++] = '\r';
|
||
buf[cbToSend++] = '\n';
|
||
|
||
//
|
||
// issue synchronous WriteClient and return result to the caller
|
||
//
|
||
|
||
success = h->pECB->WriteClient(
|
||
h->pECB->ConnID,
|
||
buf,
|
||
&cbToSend,
|
||
HSE_IO_SYNC
|
||
);
|
||
|
||
//
|
||
// reset buffer pointer
|
||
//
|
||
|
||
h->cbData = 0;
|
||
|
||
return success;
|
||
}
|
||
|
||
|
||
BOOL
|
||
CteWrite(
|
||
IN HCTE_ENCODER h,
|
||
IN PVOID pData,
|
||
IN DWORD cbData
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
Write specified number of data bytes to the chunk buffer.
|
||
When the chunk buffer becomes full, call CteSendChunk()
|
||
to send it out.
|
||
|
||
Arguments:
|
||
|
||
h - CTE Encoder handle
|
||
pData - pointer to data bytes
|
||
cbData - number of data bytes to send
|
||
|
||
Returns:
|
||
|
||
TRUE if bytes were successfully written
|
||
FALSE if WriteClient() failed
|
||
|
||
--*/
|
||
{
|
||
DWORD cbToConsume;
|
||
PBYTE pBytesToSend = (PBYTE) pData;
|
||
|
||
for( ;; ) {
|
||
|
||
//
|
||
// compute the number of bytes to consume,
|
||
// break out of the loop, if nothing is left
|
||
//
|
||
|
||
cbToConsume = min( cbData, h->dwChunkSize - h->cbData );
|
||
|
||
if( cbToConsume == 0 ) {
|
||
break;
|
||
}
|
||
|
||
//
|
||
// move bytes to the buffer, advance pointers and counters
|
||
//
|
||
|
||
memmove( h->pData + h->cbData, pBytesToSend, cbToConsume );
|
||
|
||
h->cbData += cbToConsume;
|
||
pBytesToSend += cbToConsume;
|
||
cbData -= cbToConsume;
|
||
|
||
//
|
||
// if the chunk buffer is full, send it
|
||
//
|
||
|
||
if( h->cbData == h->dwChunkSize ) {
|
||
if( !CteSendChunk( h ) ) {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
BOOL
|
||
CteEndWrite(
|
||
IN HCTE_ENCODER h
|
||
)
|
||
/*++
|
||
|
||
Purpose:
|
||
|
||
Complete the transfer and release the encoder context
|
||
|
||
Arguments:
|
||
|
||
h - CTE Encoder handle
|
||
|
||
Returns:
|
||
|
||
TRUE if transfer was successfully completed
|
||
FALSE if WriteClient() failed
|
||
|
||
--*/
|
||
{
|
||
BOOL success;
|
||
|
||
//
|
||
// if there are some bytes in the chunk, send them
|
||
//
|
||
|
||
if( h->cbData ) {
|
||
if( !CteSendChunk( h ) ) {
|
||
return FALSE;
|
||
}
|
||
}
|
||
|
||
//
|
||
// send empty chunk (which means EOF)
|
||
//
|
||
|
||
success = CteSendChunk( h );
|
||
|
||
//
|
||
// release chunk transfer context
|
||
//
|
||
|
||
LocalFree( h );
|
||
|
||
return success;
|
||
}
|
||
|
||
|
||
|
||
|
||
|