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