/*++ 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() , 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; }