/*++ Copyright (c) 2001 Microsoft Corporation Module Name: ssi_vector_send.cxx Abstract: wrapper for VectorSend related buffer manipulation Segments of final response of stm file processing are buffered in the SSI_VECTOR_BUFFER to optimize the "send response" path Author: Jaroslad Apr-2001 --*/ #include "precomp.hxx" HRESULT SSI_VECTOR_BUFFER::AddVectorHeaders( IN CHAR * pszHeaders, IN BOOL fIncludesContentLength, IN CHAR * pszStatus ) /*++ Routine Description: add headers and status line to be sent in response Note: caller is responsible to ensure that pszHeaders and pszStatus will not be freed If lifetime cannot be guaranteed or use AllocateSpace() to allocate memory Arguments: pszHeaders - pointer to headers (including \r\n) fIncludesContentLength - flag that headers include "Content-Length" header pszStatus - if "" then it will be filled in automatically to "200 OK" Return Value: HRESULT --*/ { // // function is expected to be called not more than once per request // if ( !_fHeadersSent ) { _RespVector.dwFlags |= HSE_IO_SEND_HEADERS; DBG_ASSERT( _RespVector.pszHeaders == NULL ); _RespVector.pszHeaders = pszHeaders; _fVectorHeadersIncludeContentLength = fIncludesContentLength; // // pszStatus is required not to be NULL with HSE_IO_SEND_HEADERS // so set it to empty string // _RespVector.pszStatus = pszStatus; } else { DBG_ASSERT( FALSE ); return( E_FAIL ); } return S_OK; } HRESULT SSI_VECTOR_BUFFER::AddToVector( IN PCHAR pbData, IN DWORD cbData ) /*++ Routine Description: add another chunk to response Vector Note: caller is responsible to ensure that buffer will not be freed until VectorSend completed If lifetime cannot be guaranteed use CopyToVector() instead (or use AllocateSpace() to allocate memory for pbData ) Arguments: pbData - pointer to chunk cbData - size of chunk Return Value: HRESULT --*/ { DWORD nElementCount = _RespVector.nElementCount; if( cbData != 0 ) { if (!_buffVectorElementArray.Resize( (nElementCount + 1)*sizeof(HSE_VECTOR_ELEMENT) , 256 )) { return HRESULT_FROM_WIN32(GetLastError()); } _RespVector.lpElementArray = (HSE_VECTOR_ELEMENT *)_buffVectorElementArray.QueryPtr(); _RespVector.lpElementArray[ nElementCount ].ElementType = HSE_VECTOR_ELEMENT_TYPE_MEMORY_BUFFER; _RespVector.lpElementArray[ nElementCount ].pvContext = pbData; _RespVector.lpElementArray[ nElementCount ].cbSize = cbData; _cbTotalBytesInVector += cbData; _RespVector.nElementCount++; } return S_OK; } HRESULT SSI_VECTOR_BUFFER::AddFileChunkToVector( IN DWORD cbOffset, IN DWORD cbData, IN HANDLE hFile ) /*++ Routine Description: add another chunk to response Vector Note: caller is responsible to ensure that buffer will nt be freed until VectorSend completed If lifetime cannot be guaranteed use CopyToVector() instead (or use AllocateSpace() to allocate memory for pbData ) Arguments: pbOffset - offset within the file cbData - size of chunk hFile - file where file chunk is located Return Value: HRESULT --*/ { DWORD nElementCount = _RespVector.nElementCount; if( cbData != 0 ) { if (!_buffVectorElementArray.Resize( (nElementCount + 1)*sizeof(HSE_VECTOR_ELEMENT) , 256 )) { return HRESULT_FROM_WIN32(GetLastError()); } _RespVector.lpElementArray = (HSE_VECTOR_ELEMENT *)_buffVectorElementArray.QueryPtr(); _RespVector.lpElementArray[ nElementCount ].ElementType = HSE_VECTOR_ELEMENT_TYPE_FILE_HANDLE; _RespVector.lpElementArray[ nElementCount ].pvContext = hFile; _RespVector.lpElementArray[ nElementCount ].cbOffset = cbOffset; _RespVector.lpElementArray[ nElementCount ].cbSize = cbData; _cbTotalBytesInVector += cbData; _RespVector.nElementCount++; } return S_OK; } HRESULT SSI_VECTOR_BUFFER::CopyToVector( IN PCHAR pszData, IN DWORD cchData ) /*++ Routine Description: Copy data to private buffer and then add it to response Vector Arguments: pbData - pointer to chunk cbData - size of chunk Return Value: HRESULT --*/ { PCHAR pszVectorBufferSpace = NULL; HRESULT hr = E_FAIL; // // Allocate space in private buffer // if ( FAILED( hr = AllocateSpace( cchData, &pszVectorBufferSpace ) ) ) { return hr; } else { memcpy( pszVectorBufferSpace, pszData, cchData ); hr = AddToVector( pszVectorBufferSpace, cchData ); } return hr; } HRESULT SSI_VECTOR_BUFFER::CopyToVector( IN STRA& straSource ) /*++ Routine Description: Copy data to private buffer and then add it to response Vector Arguments: straSource -string to be copied Return Value: HRESULT --*/ { return CopyToVector( straSource.QueryStr(), straSource.QueryCB() ); } HRESULT SSI_VECTOR_BUFFER::CopyToVector( IN STRU& struSource ) /*++ Routine Description: Copy data to private buffer and then add it to response Vector Arguments: struSource - string to be copied Return Value: HRESULT --*/ { HRESULT hr = E_FAIL; STRA straSource; if ( FAILED( hr = straSource.CopyW( struSource.QueryStr() ) ) ) { return hr; } else { return CopyToVector( straSource ); } } HRESULT SSI_VECTOR_BUFFER::VectorSend( OUT BOOL * pfAsyncPending, IN BOOL fFinalSend ) /*++ Routine Description: HSE_REQ_VECTOR_SEND wrapper. Arguments: pfAsyncPending - Set to TRUE if async is pending fFinalSend - TRUE if this is the last send for the response it can help to determine if Keep-alive can be used Return Value: HRESULT --*/ { HRESULT hr = E_FAIL; DBG_ASSERT( pfAsyncPending != NULL ); *pfAsyncPending = FALSE; // // check if there is any data to be sent // if ( ( _RespVector.pszHeaders == NULL && _RespVector.nElementCount == 0 ) || ( _fHeadersSent && _fHeadRequest ) ) { // // there is nothing to be sent // Also handle HEAD requests. Never send body with them // handling HEAD request by processing whole SSI and only not // sending data is not ideal, but this way it is guaranteed // that HEAD and GET will get same headers // *pfAsyncPending = FALSE; return S_OK; } if ( _fVectorHeadersIncludeContentLength ) { // // remove the disconnect flag since we have content length // _RespVector.dwFlags &= ( ~HSE_IO_DISCONNECT_AFTER_SEND ); } else if ( !_fHeadersSent && fFinalSend ) { // // Optimization: // This is final send and headers were not yet sent // That means we can do keep-alive and specify Content-length // STACK_STRA( straFinalHeaders, SSI_DEFAULT_RESPONSE_HEADERS_SIZE + 1 ); CHAR achNum[ SSI_MAX_NUMBER_STRING + 1 ]; _ultoa( _cbTotalBytesInVector, achNum, 10 ); if ( FAILED( hr = _straFinalHeaders.Copy( "Content-Length: " ) ) ) { return hr; } if ( FAILED( hr = _straFinalHeaders.Append( achNum ) ) ) { return hr; } if ( FAILED( hr = _straFinalHeaders.Append( "\r\n" ) ) ) { return hr; } if ( FAILED( hr = _straFinalHeaders.Append( _RespVector.pszHeaders ) ) ) { return hr; } _RespVector.pszHeaders = _straFinalHeaders.QueryStr(); // remove the disconnect flag _RespVector.dwFlags &= ( ~HSE_IO_DISCONNECT_AFTER_SEND ); } // // Send Response Vector // if( _RespVector.dwFlags & HSE_IO_SEND_HEADERS ) { _fHeadersSent = TRUE; } if ( _fHeadRequest ) { // Handle HEAD requests. Never send body with them // Handling HEAD request by processing the whole SSI and only not // sending data is not ideal, but this way it is guaranteed // that HEAD and GET will get the same headers // _RespVector.nElementCount = 0; } if (! _pECB->ServerSupportFunction( _pECB->ConnID, HSE_REQ_VECTOR_SEND, &_RespVector, NULL, NULL) ) { // // No use in trying to reset _fHeadersSent in case of failure // return HRESULT_FROM_WIN32( GetLastError() ); } *pfAsyncPending = TRUE; return S_OK; }