#ifndef W3RESPONSE_HXX_INCLUDED #define W3RESPONSE_HXX_INCLUDED // // SEND_RAW_BUFFER // // Stupid little helper class which wraps a buffer which is acached // class SEND_RAW_BUFFER { public: SEND_RAW_BUFFER() : _buffSendRaw( _abBuffer, sizeof( _abBuffer ) ), _cbLen( 0 ) { } virtual ~SEND_RAW_BUFFER() { } VOID * operator new( size_t size ) { DBG_ASSERT( size == sizeof( SEND_RAW_BUFFER ) ); DBG_ASSERT( sm_pachSendRawBuffers != NULL ); return sm_pachSendRawBuffers->Alloc(); } VOID operator delete( VOID * pSendRawBuffer ) { DBG_ASSERT( pSendRawBuffer != NULL ); DBG_ASSERT( sm_pachSendRawBuffers != NULL ); DBG_REQUIRE( sm_pachSendRawBuffers->Free( pSendRawBuffer ) ); } VOID * QueryPtr( VOID ) { return _buffSendRaw.QueryPtr(); } DWORD QueryCB( VOID ) { return _cbLen; } DWORD QuerySize( VOID ) { return _buffSendRaw.QuerySize(); } VOID SetLen( DWORD cbLen ) { _cbLen = cbLen; } HRESULT Resize( DWORD cbLen ) { if ( !_buffSendRaw.Resize( cbLen ) ) { return HRESULT_FROM_WIN32( GetLastError() ); } else { return NO_ERROR; } } static HRESULT Initialize( VOID ); static VOID Terminate( VOID ); LIST_ENTRY _listEntry; private: BUFFER _buffSendRaw; BYTE _abBuffer[ 8192 ]; DWORD _cbLen; static ALLOC_CACHE_HANDLER* sm_pachSendRawBuffers; }; // // HTTP status codes // #define REASON(x) (x),sizeof(x)-sizeof(CHAR) struct HTTP_STATUS { USHORT statusCode; PCHAR pszReason; DWORD cbReason; }; extern HTTP_STATUS HttpStatusOk; extern HTTP_STATUS HttpStatusPartialContent; extern HTTP_STATUS HttpStatusMultiStatus; extern HTTP_STATUS HttpStatusMovedPermanently; extern HTTP_STATUS HttpStatusRedirect; extern HTTP_STATUS HttpStatusMovedTemporarily; extern HTTP_STATUS HttpStatusNotModified; extern HTTP_STATUS HttpStatusBadRequest; extern HTTP_STATUS HttpStatusUnauthorized; extern HTTP_STATUS HttpStatusForbidden; extern HTTP_STATUS HttpStatusNotFound; extern HTTP_STATUS HttpStatusMethodNotAllowed; extern HTTP_STATUS HttpStatusNotAcceptable; extern HTTP_STATUS HttpStatusProxyAuthRequired; extern HTTP_STATUS HttpStatusPreconditionFailed; extern HTTP_STATUS HttpStatusUrlTooLong; extern HTTP_STATUS HttpStatusRangeNotSatisfiable; extern HTTP_STATUS HttpStatusLockedError; extern HTTP_STATUS HttpStatusServerError; extern HTTP_STATUS HttpStatusNotImplemented; extern HTTP_STATUS HttpStatusBadGateway; extern HTTP_STATUS HttpStatusServiceUnavailable; extern HTTP_STATUS HttpStatusGatewayTimeout; // // HTTP Sub Errors // struct HTTP_SUB_ERROR { USHORT mdSubError; DWORD dwStringId; }; extern HTTP_SUB_ERROR HttpNoSubError; extern HTTP_SUB_ERROR Http401BadLogon; extern HTTP_SUB_ERROR Http401Config; extern HTTP_SUB_ERROR Http401Resource; extern HTTP_SUB_ERROR Http401Filter; extern HTTP_SUB_ERROR Http401Application; extern HTTP_SUB_ERROR Http403ExecAccessDenied; extern HTTP_SUB_ERROR Http403ReadAccessDenied; extern HTTP_SUB_ERROR Http403WriteAccessDenied; extern HTTP_SUB_ERROR Http403SSLRequired; extern HTTP_SUB_ERROR Http403SSL128Required; extern HTTP_SUB_ERROR Http403IPAddressReject; extern HTTP_SUB_ERROR Http403CertRequired; extern HTTP_SUB_ERROR Http403SiteAccessDenied; extern HTTP_SUB_ERROR Http403TooManyUsers; extern HTTP_SUB_ERROR Http403InvalidateConfig; extern HTTP_SUB_ERROR Http403PasswordChange; extern HTTP_SUB_ERROR Http403MapperDenyAccess; extern HTTP_SUB_ERROR Http403CertRevoked; extern HTTP_SUB_ERROR Http403DirBrowsingDenied; extern HTTP_SUB_ERROR Http403CertInvalid; extern HTTP_SUB_ERROR Http403CertTimeInvalid; extern HTTP_SUB_ERROR Http404SiteNotFound; extern HTTP_SUB_ERROR Http502Timeout; extern HTTP_SUB_ERROR Http502PrematureExit; #define W3_RESPONSE_INLINE_HEADERS 10 #define W3_RESPONSE_INLINE_CHUNKS 5 #define W3_RESPONSE_SIGNATURE ((DWORD) 'PR3W') #define W3_RESPONSE_SIGNATURE_FREE ((DWORD) 'pr3w') #define W3_RESPONSE_ASYNC 0x01 #define W3_RESPONSE_MORE_DATA 0x02 #define W3_RESPONSE_DISCONNECT 0x04 #define W3_RESPONSE_UL_CACHEABLE 0x08 #define W3_RESPONSE_SUPPRESS_ENTITY 0x10 #define W3_RESPONSE_SUPPRESS_HEADERS 0x20 enum W3_RESPONSE_MODE { RESPONSE_MODE_PARSED, RESPONSE_MODE_RAW }; class W3_RESPONSE { private: DWORD _dwSignature; HTTP_RESPONSE _ulHttpResponse; HTTP_SUB_ERROR _subError; // // Buffer to store any strings for header values/names which are // referenced in the _ulHttpResponse // CHUNK_BUFFER _HeaderBuffer; HTTP_UNKNOWN_HEADER _rgUnknownHeaders[ W3_RESPONSE_INLINE_HEADERS ]; BUFFER _bufUnknownHeaders; // // Buffer to store chunk data structures referenced in calls // to UlSendHttpResponse or UlSendEntityBody // HTTP_DATA_CHUNK _rgChunks[ W3_RESPONSE_INLINE_CHUNKS ]; BUFFER _bufChunks; DWORD _cChunks; ULONGLONG _cbContentLength; // // Has this response been touched at all? // BOOL _fResponseTouched; // // Has a response been sent // BOOL _fResponseSent; // // Some buffers used when in raw mode // STRA _strRawCoreHeaders; // // Are we in raw mode or parsed mode? // W3_RESPONSE_MODE _responseMode; // // Does an ISAPI expect to complete headers with WriteClient() // BOOL _fIncompleteHeaders; // // Which chunk is the first of actual entity // DWORD _cFirstEntityChunk; // // List of send raw buffers that need to be freed on reset // LIST_ENTRY _SendRawBufferHead; HRESULT BuildRawCoreHeaders( W3_CONTEXT * pW3Context ); HRESULT SwitchToRawMode( W3_CONTEXT * pW3Context, CHAR * pszAdditionalHeaders, DWORD cchAdditionalHeaders ); HRESULT SwitchToParsedMode( VOID ); HRESULT ParseHeadersFromStream( CHAR * pszStream ); HRESULT InsertDataChunk( HTTP_DATA_CHUNK * pNewChunk, LONG cPosition ); HTTP_DATA_CHUNK * QueryChunks( VOID ) { return (HTTP_DATA_CHUNK*) _bufChunks.QueryPtr(); } VOID Reset( VOID ); public: W3_RESPONSE() : _bufUnknownHeaders( (BYTE*) _rgUnknownHeaders, sizeof( _rgUnknownHeaders ) ), _bufChunks( (BYTE*) _rgChunks, sizeof( _rgChunks ) ) { InitializeListHead( &_SendRawBufferHead ); Reset(); _dwSignature = W3_RESPONSE_SIGNATURE; } ~W3_RESPONSE() { SEND_RAW_BUFFER * pBuffer; while ( !IsListEmpty( &_SendRawBufferHead ) ) { pBuffer = CONTAINING_RECORD( _SendRawBufferHead.Flink, SEND_RAW_BUFFER, _listEntry ); RemoveEntryList( &( pBuffer->_listEntry ) ); delete pBuffer; } _dwSignature = W3_RESPONSE_SIGNATURE_FREE; } static HRESULT Initialize( VOID ); static VOID Terminate( VOID ); static HRESULT ReadFileIntoBuffer( HANDLE hFile, SEND_RAW_BUFFER * pSendBuffer, ULONGLONG cbCurrentFileOffset ); BOOL CheckSignature( VOID ) const { return _dwSignature == W3_RESPONSE_SIGNATURE; } // // Core header adding/deleting functions // HRESULT DeleteHeader( CHAR * pszHeaderName ); HRESULT SetHeader( DWORD headerIndex, CHAR * pszHeaderValue, DWORD cchHeaderValue, BOOL fAppend = FALSE ); HRESULT SetHeaderByReference( DWORD headerIndex, CHAR * pszHeaderValue, DWORD cchHeaderValue, BOOL fForceParsedMode = FALSE ); HRESULT SetHeader( CHAR * pszHeaderName, DWORD cchHeaderName, CHAR * pszHeaderValue, DWORD cchHeaderValue, BOOL fAppend = FALSE, BOOL fForceParsedMode = FALSE, BOOL fAlwaysAddUnknown = FALSE ); // // Some friendly SetHeader helpers // CHAR * GetHeader( DWORD headerIndex ) { HRESULT hr; if ( _responseMode == RESPONSE_MODE_RAW ) { hr = SwitchToParsedMode(); if ( FAILED( hr ) ) { return NULL; } } DBG_ASSERT( _responseMode == RESPONSE_MODE_PARSED ); DBG_ASSERT(headerIndex < HttpHeaderResponseMaximum); return _ulHttpResponse.Headers.pKnownHeaders[headerIndex].pRawValue; } HRESULT GetHeader( CHAR * pszHeaderName, STRA * pstrHeaderValue ); VOID ClearHeaders( VOID ); // // Chunk management // HRESULT AddFileHandleChunk( HANDLE hFile, ULONGLONG cbOffset, ULONGLONG cbLength ); HRESULT AddMemoryChunkByReference( PVOID pvBuffer, DWORD cbBuffer ); HRESULT GetChunks( OUT BUFFER *chunkBuffer, OUT DWORD *pdwNumChunks ); HRESULT Clear( BOOL fClearEntityOnly = FALSE ); HRESULT GenerateAutomaticHeaders( W3_CONTEXT * pW3Context, DWORD dwFlags ); // // ULATQ send APIs // HRESULT SendResponse( W3_CONTEXT * pW3Context, DWORD dwResponseFlags, DWORD * pcbSent, HTTP_LOG_FIELDS_DATA * pUlLogData ); HRESULT SendEntity( W3_CONTEXT * pW3Context, DWORD dwResponseFlags, DWORD * pcbSent, HTTP_LOG_FIELDS_DATA * pUlLogData ); HRESULT ProcessRawChunks( W3_CONTEXT * pW3Context, BOOL * pfFinished ); // // Setup and send an ISAPI response // HRESULT FilterWriteClient( W3_CONTEXT * pW3Context, PVOID pvData, DWORD cbData ); HRESULT BuildResponseFromIsapi( W3_CONTEXT * pW3Context, LPSTR pszStatusLine, LPSTR pszHeaderStream, DWORD cchHeaderStream ); HRESULT BuildStatusFromIsapi( LPSTR pszStatusLine ); HRESULT AppendResponseHeaders( STRA & strHeaders ); // // Get the raw response stream for use by raw data filter // HRESULT GetRawResponseStream( STRA * pstrResponseStream ); // // Status code and reason // VOID SetStatus( HTTP_STATUS & httpStatus, HTTP_SUB_ERROR & httpSubError = HttpNoSubError ) { _ulHttpResponse.StatusCode = httpStatus.statusCode; _ulHttpResponse.pReason = httpStatus.pszReason; _ulHttpResponse.ReasonLength = httpStatus.cbReason; _subError = httpSubError; _fResponseTouched = TRUE; } VOID GetStatus( HTTP_STATUS *phttpStatus ) { phttpStatus->statusCode = _ulHttpResponse.StatusCode; phttpStatus->pszReason = _ulHttpResponse.pReason; phttpStatus->cbReason = _ulHttpResponse.ReasonLength; } HRESULT SetStatus( USHORT StatusCode, STRA & strReason, HTTP_SUB_ERROR & subError = HttpNoSubError ); HRESULT GetStatusLine( STRA * pstrStatusLine ); USHORT QueryStatusCode( VOID ) const { return _ulHttpResponse.StatusCode; } HRESULT QuerySubError( HTTP_SUB_ERROR * pSubError ) { if ( pSubError == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } pSubError->mdSubError = _subError.mdSubError; pSubError->dwStringId = _subError.dwStringId; return NO_ERROR; } VOID SetSubError( HTTP_SUB_ERROR * pSubError ) { if ( pSubError == NULL ) { DBG_ASSERT( FALSE ); return; } _subError.mdSubError = pSubError->mdSubError; _subError.dwStringId = pSubError->dwStringId; } // // Touched state. Used to determine whether the system needs to send // an error (500) response due to not state handler creating a response // BOOL QueryResponseTouched( VOID ) const { return _fResponseTouched; } BOOL QueryResponseSent( VOID ) const { return _fResponseSent; } ULONGLONG QueryContentLength( VOID ) const { return _cbContentLength; } // // Is there any entity in this response (used by custom error logic) // BOOL QueryEntityExists( VOID ) { return _cChunks > 0 && _cChunks > _cFirstEntityChunk; } }; #endif