#ifndef W3RESPONSE_HXX_INCLUDED #define W3RESPONSE_HXX_INCLUDED // // HTTP status codes // #define REASON(x) (x),sizeof(x)-sizeof(CHAR) struct HTTP_STATUS { USHORT statusCode; PCHAR pszReason; USHORT 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 HttpStatusEntityTooLarge; extern HTTP_STATUS HttpStatusUrlTooLong; extern HTTP_STATUS HttpStatusRangeNotSatisfiable; extern HTTP_STATUS HttpStatusExpectationFailed; 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 Http403AppPoolDenied; extern HTTP_SUB_ERROR Http403InsufficientPrivilegeForCgi; extern HTTP_SUB_ERROR Http403PassportLoginFailure; extern HTTP_SUB_ERROR Http404SiteNotFound; extern HTTP_SUB_ERROR Http404DeniedByPolicy; extern HTTP_SUB_ERROR Http404DeniedByMimeMap; extern HTTP_SUB_ERROR Http500UNCAccess; extern HTTP_SUB_ERROR Http500BadMetadata; 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_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; USHORT _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; CHAR _achRawCoreHeaders[ 200 ]; // // 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 // USHORT _cFirstEntityChunk; // // Manage where we are as it relates to chunks to be filtered // USHORT _cCurrentChunk; ULONGLONG _cbCurrentOffset; BUFFER _bufRawChunk; DWORD _dwSendFlags; BOOL _fHandleCompletion; DWORD _cbTotalCompletion; static DWORD sm_dwSendRawDataBufferSize; HRESULT BuildRawCoreHeaders( VOID ); HRESULT SwitchToRawMode( CHAR * pszAdditionalHeaders, DWORD cchAdditionalHeaders ); 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 ) ), _strRawCoreHeaders( _achRawCoreHeaders, sizeof( _achRawCoreHeaders ) ) { Reset(); _dwSignature = W3_RESPONSE_SIGNATURE; } ~W3_RESPONSE() { _dwSignature = W3_RESPONSE_SIGNATURE_FREE; } static HRESULT Initialize( VOID ); static VOID Terminate( VOID ); BOOL CheckSignature( VOID ) const { return _dwSignature == W3_RESPONSE_SIGNATURE; } // // Core header adding/deleting functions // HRESULT SwitchToParsedMode( VOID ); HRESULT DeleteHeader( CHAR * pszHeaderName ); HRESULT SetHeader( DWORD headerIndex, CHAR * pszHeaderValue, USHORT cchHeaderValue, BOOL fAppend = FALSE ); HRESULT SetHeaderByReference( DWORD headerIndex, CHAR * pszHeaderValue, USHORT cchHeaderValue, BOOL fForceParsedMode = FALSE ); HRESULT SetHeader( CHAR * pszHeaderName, USHORT cchHeaderName, CHAR * pszHeaderValue, USHORT cchHeaderValue, BOOL fAppend = FALSE, BOOL fForceParsedMode = FALSE, BOOL fAlwaysAddUnknown = FALSE, BOOL fAppendAsDupHeader = FALSE ); // // Some friendly SetHeader helpers // const 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.KnownHeaders[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 AddFragmentChunk( WCHAR * pszFragmentName, USHORT dwNameLength ); HRESULT Clear( BOOL fClearEntityOnly = FALSE ); HRESULT GenerateAutomaticHeaders( W3_CONTEXT * pW3Context, DWORD * pdwFlags ); // // ULATQ send APIs // HRESULT SendResponse( W3_CONTEXT * pW3Context, DWORD dwResponseFlags, HTTP_CACHE_POLICY *pCachePolicy, DWORD * pcbSent ); HRESULT SendEntity( W3_CONTEXT * pW3Context, DWORD dwResponseFlags, DWORD * pcbSent ); HRESULT ResumeResponseTransfer( W3_CONTEXT * pW3Context, BOOL fCompletion, DWORD cbCompletion, DWORD dwCompletionStatus, BOOL fAsync, DWORD * pcbSent ); HRESULT OnIoCompletion( W3_CONTEXT * pW3Context, DWORD cbCompletion, DWORD dwCompletionStatus ) { DWORD cbSent; if ( _fHandleCompletion == FALSE ) { return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } else { ResumeResponseTransfer( pW3Context, TRUE, cbCompletion, dwCompletionStatus, TRUE, &cbSent ); return NO_ERROR; } } // // 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 SetStatusCode( USHORT StatusCode ) { _ulHttpResponse.StatusCode = StatusCode; } VOID GetStatus( HTTP_STATUS *phttpStatus ) { phttpStatus->statusCode = _ulHttpResponse.StatusCode; phttpStatus->pszReason = (PSTR) _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; } VOID FindStringId( VOID ); // // 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