CTE_ENC.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;
}