/*++ Copyright (c) 1998-2002 Microsoft Corporation Module Name: sendresponse.h Abstract: This module contains declarations for manipulating HTTP responses. Author: Keith Moore (keithmo) 07-Aug-1998 Revision History: Paul McDaniel (paulmcd) 15-Mar-1999 Modified SendResponse --*/ #ifndef _SENDRESPONSE_H_ #define _SENDRESPONSE_H_ // // Forwarders. // typedef struct _UL_INTERNAL_DATA_CHUNK *PUL_INTERNAL_DATA_CHUNK; typedef struct _UL_INTERNAL_REQUEST *PUL_INTERNAL_REQUEST; typedef struct _UL_INTERNAL_RESPONSE *PUL_INTERNAL_RESPONSE; typedef struct _UL_HTTP_CONNECTION *PUL_HTTP_CONNECTION; typedef struct _UL_LOG_DATA_BUFFER *PUL_LOG_DATA_BUFFER; typedef struct _UL_URI_CACHE_ENTRY *PUL_URI_CACHE_ENTRY; typedef enum _UL_SEND_CACHE_RESULT { UlSendCacheResultNotSet, // Not yet set UlSendCacheServedFromCache, // Served from cache successfully UlSendCacheMiss, // Need to bounce up to user mode UlSendCacheConnectionRefused, // Connection is refused (con limits) UlSendCachePreconditionFailed, // Need to terminate connection UlSendCacheFailed, // Other failure (memory etc) need to terminate UlSendCacheMaximum } UL_SEND_CACHE_RESULT, *PUL_SEND_CACHE_RESULT; #define TRANSLATE_SEND_CACHE_RESULT(r) \ ((r) == UlSendCacheResultNotSet ? "ResultNotSet" : \ (r) == UlSendCacheServedFromCache ? "ServedFromCache" : \ (r) == UlSendCacheMiss ? "CacheMiss" : \ (r) == UlSendCacheConnectionRefused ? "ConnectionRefused" : \ (r) == UlSendCachePreconditionFailed ? "PreconditionFailed" : \ (r) == UlSendCacheFailed ? "SendCacheFailed" : "UNKNOWN") typedef enum _UL_RESUME_PARSING_TYPE { UlResumeParsingNone, // No need to resume parsing UlResumeParsingOnLastSend, // Resume parsing on last send UlResumeParsingOnSendCompletion, // Resume parsing on send completion UlResumeParsingMaximum } UL_RESUME_PARSING_TYPE, *PUL_RESUME_PARSING_TYPE; NTSTATUS UlSendHttpResponse( IN PUL_INTERNAL_REQUEST pRequest, IN PUL_INTERNAL_RESPONSE pResponse, IN PUL_COMPLETION_ROUTINE pCompletionRoutine, IN PVOID pCompletionContext ); NTSTATUS UlSendCachedResponse( IN PUL_HTTP_CONNECTION pHttpConn, OUT PUL_SEND_CACHE_RESULT pSendCacheResult, OUT PBOOLEAN pResumeParsing ); NTSTATUS UlCacheAndSendResponse( IN PUL_INTERNAL_REQUEST pRequest, IN PUL_INTERNAL_RESPONSE pResponse, IN PUL_APP_POOL_PROCESS pProcess, IN HTTP_CACHE_POLICY Policy, IN PUL_COMPLETION_ROUTINE pCompletionRoutine, IN PVOID pCompletionContext, OUT PBOOLEAN pServedFromCache ); typedef enum _UL_CAPTURE_FLAGS { UlCaptureNothing = 0x00, UlCaptureCopyData = 0x01, UlCaptureKernelMode = 0x02, UlCaptureCopyDataInKernelMode = UlCaptureCopyData | UlCaptureKernelMode, } UL_CAPTURE_FLAGS; typedef struct _UL_INTERNAL_DATA_CHUNK { // // Chunk type. // HTTP_DATA_CHUNK_TYPE ChunkType; // // The data chunk structures, one per supported data chunk type. // union { // // From memory data chunk. // struct { PMDL pMdl; PVOID pCopiedBuffer; PVOID pUserBuffer; ULONG BufferLength; } FromMemory; // // From file handle data chunk. // struct { HTTP_BYTE_RANGE ByteRange; HANDLE FileHandle; UL_FILE_CACHE_ENTRY FileCacheEntry; } FromFileHandle; // // From fragment cache data chunk. // struct { PUL_URI_CACHE_ENTRY pCacheEntry; } FromFragmentCache; }; } UL_INTERNAL_DATA_CHUNK, *PUL_INTERNAL_DATA_CHUNK; #define IS_FROM_MEMORY( pchunk ) \ ( (pchunk)->ChunkType == HttpDataChunkFromMemory ) #define IS_FROM_FILE_HANDLE( pchunk ) \ ( (pchunk)->ChunkType == HttpDataChunkFromFileHandle ) #define IS_FROM_FRAGMENT_CACHE( pchunk ) \ ( (pchunk)->ChunkType == HttpDataChunkFromFragmentCache ) #define UL_IS_VALID_INTERNAL_RESPONSE(x) \ HAS_VALID_SIGNATURE(x, UL_INTERNAL_RESPONSE_POOL_TAG) // // WARNING! All fields of this structure must be explicitly initialized. // typedef struct _UL_INTERNAL_RESPONSE { // // NonPagedPool // // // This MUST be the first field in the structure. This is the linkage // used by the lookaside package for storing entries in the lookaside // list. // SLIST_ENTRY LookasideEntry; // // UL_INTERNAL_RESPONSE_POOL_TAG // ULONG Signature; // // Reference count. // LONG ReferenceCount; // // The original request. // PUL_INTERNAL_REQUEST pRequest; // // Does the response need to perform a sync I/O read? // BOOLEAN SyncRead; // // Was a Content-Length specified? // BOOLEAN ContentLengthSpecified; // // Should we generate a Date: header? // BOOLEAN GenDateHeader; // // Was Transfer-Encoding "Chunked" specified? // BOOLEAN ChunkedSpecified; // // Is this from a lookaside list? Used to determine how to free. // BOOLEAN FromLookaside; // // Is this from kernel mode (UlSendErrorResponse)? // BOOLEAN FromKernelMode; // // Has this response gone through the EnqueueSendHttpResponse logic? // BOOLEAN SendEnqueued; // // Should we try to copy some data so we can complete the IRP early? // BOOLEAN CopySend; // // The maximum IRP stack size of all file systems associated // with this response. // CCHAR MaxFileSystemStackSize; // // If parsing needs to be resumed on send completion. // UL_RESUME_PARSING_TYPE ResumeParsingType; // // HTTP_SEND_RESPONSE flags. // ULONG Flags; // // Status code & verb. // USHORT StatusCode; HTTP_VERB Verb; // // Should we generate a ConnectionHeader? // UL_CONN_HDR ConnHeader; // // The headers. // ULONG HeaderLength; ULONG VariableHeaderLength; PUCHAR pHeaders; PUCHAR pVariableHeader; // // System time of Date header // LARGE_INTEGER CreationTime; // // ETag from HTTP_RESPONSE // ULONG ETagLength; PUCHAR pETag; // // Content-Type and Content-Encoding from HTTP_RESPONSE // UL_CONTENT_TYPE ContentType; ULONG ContentEncodingLength; PUCHAR pContentEncoding; // // Optional pointer to the space containing all embedded // file names and copied data. This may be NULL for in-memory-only // responses that are strictly locked down. // ULONG AuxBufferLength; PVOID pAuxiliaryBuffer; // // Logging data passed down by the user // PUL_LOG_DATA_BUFFER pLogData; // // Length of the entire response // ULONGLONG ResponseLength; // // Total length of the FromMemory chunks of the response // ULONGLONG FromMemoryLength; // // "Quota" taken in either ConnectionSendLimit or GlobalSendLimit // ULONGLONG ConnectionSendBytes; ULONGLONG GlobalSendBytes; // // Total number of bytes transferred for the entire // response. These are necessary to properly complete the IRP. // ULONGLONG BytesTransferred; // // A push lock taken when a send (call to TDI) is in progress. // UL_PUSH_LOCK PushLock; // // IoStatus and IRP used to complete the send response IRP. // IO_STATUS_BLOCK IoStatus; PIRP pIrp; // // Completion routine & context. // PUL_COMPLETION_ROUTINE pCompletionRoutine; PVOID pCompletionContext; // // Current file read offset and bytes remaining. // ULARGE_INTEGER FileOffset; ULARGE_INTEGER FileBytesRemaining; // // The total number of chunks in pDataChunks[]. // ULONG ChunkCount; // // The current chunk in pDataChunks[]. // ULONG CurrentChunk; // // The data chunks describing the data for this response. // UL_INTERNAL_DATA_CHUNK pDataChunks[0]; } UL_INTERNAL_RESPONSE, *PUL_INTERNAL_RESPONSE; #define IS_SEND_COMPLETE( resp ) \ ( ( (resp)->CurrentChunk ) == (resp)->ChunkCount ) #define IS_DISCONNECT_TIME( resp ) \ ( (((resp)->Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT) != 0) && \ (((resp)->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) ) // // Types of trackers // typedef enum _UL_TRACKER_TYPE { UlTrackerTypeSend, UlTrackerTypeBuildUriEntry, UlTrackerTypeMaximum } UL_TRACKER_TYPE, *PUL_TRACKER_TYPE; // // A MDL_RUN is a set of MDLs that came from the same source (either // a series of memory buffers, or data from a single file read) that // can be released all at once with the same mechanism. // #define UL_MAX_MDL_RUNS 5 typedef struct _UL_MDL_RUN { PMDL pMdlTail; UL_FILE_BUFFER FileBuffer; } UL_MDL_RUN, *PUL_MDL_RUN; // // The UL_CHUNK_TRACKER is for iterating through the chunks in // a UL_INTERNAL_RESPONSE. It is used for sending responses // and generating cache entries. // // WARNING! All fields of this structure must be explicitly initialized. // typedef struct _UL_CHUNK_TRACKER { // // This MUST be the first field in the structure. This is the linkage // used by the lookaside package for storing entries in the lookaside // list. // SLIST_ENTRY LookasideEntry; // // A signature. // ULONG Signature; // // Refcount on the tracker. We only use this refcount for the non-cache // case to sync various aynsc paths happening because of two outstanding // IRPs; Read and Send IRPs. // LONG RefCount; // // Flag to understand whether we have completed the send request on // this tracker or not. To synch the multiple completion paths. // LONG Terminated; // // Is this from a lookaside list? Used to determine how to free. // BOOLEAN FromLookaside; // // First piece of the response (MDL_RUN) of SendHttpResponse/EntityBody. // BOOLEAN FirstResponse; // // type of tracker // UL_TRACKER_TYPE Type; // // this connection keeps our reference count on the UL_CONNECTION // PUL_HTTP_CONNECTION pHttpConnection; // // The actual response. // PUL_INTERNAL_RESPONSE pResponse; // // The precreated file read and send IRP. // PIRP pIrp; // // The precreated IRP context for send. // UL_IRP_CONTEXT IrpContext; // // A work item, used for queuing to a worker thread. // UL_WORK_ITEM WorkItem; // // WARNING: RtlZeroMemory is only called for feilds below this line. // All fields above should be explicitly initialized. // IO_STATUS_BLOCK IoStatus; // // Used to queue the tracker on the pending response list. // LIST_ENTRY ListEntry; union { struct _SEND_TRACK_INFO { // // The head of the MDL chain buffered for this send. // PMDL pMdlHead; // // Pointer to the Next field of the last MDL on the chain. // This makes it very easy to append to the chain. // PMDL *pMdlLink; // // The number of bytes currently buffered in the MDL chain. // ULONG BytesBuffered; // // The number of active MDL runs. // ULONG MdlRunCount; // // This is the MDL in the MDL chain starting from pMdlHead that // we are going to split. // PMDL pMdlToSplit; // // This is the MDL whose Next field points to pMdlToSplit or it is // NULL when pMdlToSplit == pMdlHead. // PMDL pMdlPrevious; // // This is the partial MDL we have built for the split send and // represents the first part of the data in pMdlToSplit; or // it can be pMdlToSplit itself where the MDL chain up to // pMdlToSplit has exactly 1/2 of BytesBuffered. // PMDL pMdlSplitFirst; // // This is the partial MDL we have built for the split send and // represents the second part of the data in pMdlToSplit; or // it can be pMdlToSplit->Next where the MDL chain up to // pMdlToSplit has exactly 1/2 of BytesBuffered. // PMDL pMdlSplitSecond; // // How many sends (TDI calls) we have issued to flush the tracker. // LONG SendCount; // // The MDL runs. // UL_MDL_RUN MdlRuns[UL_MAX_MDL_RUNS]; } SendInfo; struct _BUILD_TRACK_INFO { // // The cache entry // PUL_URI_CACHE_ENTRY pUriEntry; // // File buffer information for reading. // UL_FILE_BUFFER FileBuffer; // // Offset inside pUriEntry->pMdl to copy the next buffer. // ULONG Offset; } BuildInfo; }; } UL_CHUNK_TRACKER, *PUL_CHUNK_TRACKER; #define IS_VALID_CHUNK_TRACKER( tracker ) \ (HAS_VALID_SIGNATURE(tracker, UL_CHUNK_TRACKER_POOL_TAG) \ && ((tracker)->Type < UlTrackerTypeMaximum) ) // // This structure is for tracking an autonomous send with one full response // // WARNING! All fields of this structure must be explicitly initialized. // typedef struct _UL_FULL_TRACKER { // // This MUST be the first field in the structure. This is the linkage // used by the lookaside package for storing entries in the lookaside // list. // SLIST_ENTRY LookasideEntry; // // A signature. // ULONG Signature; // // The HTTP Verb // HTTP_VERB RequestVerb; // // The HTTP status code (e.g. 200). // USHORT ResponseStatusCode; // // Is this from a lookaside list? Used to determine how to free. // BOOLEAN FromLookaside; // // Is this from the internal request? Won't try to free if set. // BOOLEAN FromRequest; // // Set if send is buffered for this response. // BOOLEAN SendBuffered; // // If parsing needs to be resumed on send completion. // UL_RESUME_PARSING_TYPE ResumeParsingType; // // A work item, used for queuing to a worker thread. // UL_WORK_ITEM WorkItem; // // The cache entry. // PUL_URI_CACHE_ENTRY pUriEntry; // // Preallocated buffer for the fixed headers, variable headers and entity // body to be copied in the cache-miss case, or for the variable headers // only in the cache-hit case. // ULONG AuxilaryBufferLength; PUCHAR pAuxiliaryBuffer; // // MDL for the variable headers in the cache-hit case or for both the // fixed headers and variable headers plus the copied entity body in // the cache-miss case. // union { PMDL pMdlVariableHeaders; PMDL pMdlAuxiliary; }; // // MDL for the fixed headers in the cache-hit case or for the user // buffer in the cache-miss case. // union { PMDL pMdlFixedHeaders; PMDL pMdlUserBuffer; }; // // MDL for the content in the cache-hit case. // PMDL pMdlContent; // // The original request that is saved for logging purpose. // PUL_INTERNAL_REQUEST pRequest; // // This connection keeps our reference count on the UL_CONNECTION. // PUL_HTTP_CONNECTION pHttpConnection; // // The log data captured if any. // PUL_LOG_DATA_BUFFER pLogData; // // Completion routine & context. // PUL_COMPLETION_ROUTINE pCompletionRoutine; PVOID pCompletionContext; // // Flags. // ULONG Flags; // // The precreated send IRP. // PIRP pSendIrp; // // The precreated IRP context for send. // UL_IRP_CONTEXT IrpContext; // // The orignal user send IRP if exists. // PIRP pUserIrp; // // "Quota" taken in either ConnectionSendLimit or GlobalSendLimit. // ULONGLONG ConnectionSendBytes; ULONGLONG GlobalSendBytes; // // I/O status from the completion routine. // IO_STATUS_BLOCK IoStatus; } UL_FULL_TRACKER, *PUL_FULL_TRACKER; #define IS_VALID_FULL_TRACKER( tracker ) \ HAS_VALID_SIGNATURE(tracker, UL_FULL_TRACKER_POOL_TAG) // // An inline function to initialize the full tracker. // __inline VOID UlInitializeFullTrackerPool( IN PUL_FULL_TRACKER pTracker, IN CCHAR SendIrpStackSize ) { USHORT SendIrpSize; UlInitializeWorkItem(&pTracker->WorkItem); // // Set up the IRP. // SendIrpSize = IoSizeOfIrp(SendIrpStackSize); pTracker->pSendIrp = (PIRP)((PCHAR)pTracker + ALIGN_UP(sizeof(UL_FULL_TRACKER), PVOID)); IoInitializeIrp( pTracker->pSendIrp, SendIrpSize, SendIrpStackSize ); pTracker->pLogData = NULL; // // Set the Mdl's for the FixedHeaders/Variable pair and // the UserBuffer/AuxiliaryBuffer pair. // pTracker->pMdlFixedHeaders = (PMDL)((PCHAR)pTracker->pSendIrp + SendIrpSize); pTracker->pMdlVariableHeaders = (PMDL)((PCHAR)pTracker->pMdlFixedHeaders + g_UlFixedHeadersMdlLength); pTracker->pMdlContent = (PMDL)((PCHAR)pTracker->pMdlVariableHeaders + g_UlVariableHeadersMdlLength); // // Set up the auxiliary buffer pointer for the variable header plus // the fixed header and the entity body in the cache-miss case. // pTracker->pAuxiliaryBuffer = (PUCHAR)((PCHAR)pTracker->pMdlContent + g_UlContentMdlLength); // // Initialize the auxiliary MDL. // MmInitializeMdl( pTracker->pMdlAuxiliary, pTracker->pAuxiliaryBuffer, pTracker->AuxilaryBufferLength ); MmBuildMdlForNonPagedPool( pTracker->pMdlAuxiliary ); } NTSTATUS UlCaptureHttpResponse( IN PUL_APP_POOL_PROCESS pProcess OPTIONAL, IN PHTTP_RESPONSE pUserResponse OPTIONAL, IN PUL_INTERNAL_REQUEST pRequest, IN USHORT ChunkCount, IN PHTTP_DATA_CHUNK pDataChunks, IN UL_CAPTURE_FLAGS Flags, IN ULONG SendFlags, IN BOOLEAN CaptureCache, IN PHTTP_LOG_FIELDS_DATA pLogData OPTIONAL, OUT PUSHORT pStatusCode, OUT PUL_INTERNAL_RESPONSE *ppKernelResponse ); NTSTATUS UlCaptureUserLogData( IN PHTTP_LOG_FIELDS_DATA pCapturedUserLogData, IN PUL_INTERNAL_REQUEST pRequest, OUT PUL_LOG_DATA_BUFFER *ppKernelLogData ); NTSTATUS UlPrepareHttpResponse( IN HTTP_VERSION Version, IN PHTTP_RESPONSE pUserResponse, IN PUL_INTERNAL_RESPONSE pResponse, IN KPROCESSOR_MODE AccessMode ); VOID UlCleanupHttpResponse( IN PUL_INTERNAL_RESPONSE pResponse ); VOID UlReferenceHttpResponse( IN PUL_INTERNAL_RESPONSE pResponse REFERENCE_DEBUG_FORMAL_PARAMS ); VOID UlDereferenceHttpResponse( IN PUL_INTERNAL_RESPONSE pResponse REFERENCE_DEBUG_FORMAL_PARAMS ); #define UL_REFERENCE_INTERNAL_RESPONSE( presp ) \ UlReferenceHttpResponse( \ (presp) \ REFERENCE_DEBUG_ACTUAL_PARAMS \ ) #define UL_DEREFERENCE_INTERNAL_RESPONSE( presp ) \ UlDereferenceHttpResponse( \ (presp) \ REFERENCE_DEBUG_ACTUAL_PARAMS \ ) PMDL UlAllocateLockedMdl( IN PVOID VirtualAddress, IN ULONG Length, IN LOCK_OPERATION Operation ); VOID UlFreeLockedMdl( PMDL pMdl ); NTSTATUS UlInitializeAndLockMdl( IN PMDL pMdl, IN PVOID VirtualAddress, IN ULONG Length, IN LOCK_OPERATION Operation ); VOID UlCompleteSendResponse( IN PUL_CHUNK_TRACKER pTracker, IN NTSTATUS Status ); VOID UlSetRequestSendsPending( IN PUL_INTERNAL_REQUEST pRequest, IN OUT PUL_LOG_DATA_BUFFER * ppLogData, IN OUT PUL_RESUME_PARSING_TYPE pResumeParsingType ); VOID UlUnsetRequestSendsPending( IN PUL_INTERNAL_REQUEST pRequest, OUT PUL_LOG_DATA_BUFFER * ppLogData, OUT PBOOLEAN pResumeParsing ); // // Check pRequest->SentResponse and pRequest->SentLast flags for // UlSendHttpResponseIoctl // __inline NTSTATUS UlCheckSendHttpResponseFlags( IN PUL_INTERNAL_REQUEST pRequest, IN ULONG Flags ) { NTSTATUS Status = STATUS_SUCCESS; // // Make sure only one response header goes back. We can test this // without acquiring the request resource, since the flag is only set // (never reset). // if (1 == InterlockedCompareExchange( (PLONG)&pRequest->SentResponse, 1, 0 )) { // // Already sent a response. Bad. // Status = STATUS_INVALID_PARAMETER; UlTraceError(SEND_RESPONSE, ( "http!UlCheckSendHttpResponseFlags(pRequest = %p (%I64x)) %s\n" " Tried to send a second response!\n", pRequest, pRequest->RequestId, HttpStatusToString(Status) )); goto end; } // // Also ensure that all previous calls to SendHttpResponse // and SendEntityBody had the MORE_DATA flag set. // if (0 == (Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA)) { // // Remember if the more data flag is not set. // if (1 == InterlockedCompareExchange( (PLONG)&pRequest->SentLast, 1, 0 )) { Status = STATUS_INVALID_PARAMETER; UlTraceError(SEND_RESPONSE, ( "http!UlCheckSendHttpResponseFlags(pRequest = %p (%I64x)) %s\n" " Last send after previous last send!\n", pRequest, pRequest->RequestId, HttpStatusToString(Status) )); goto end; } } else if (pRequest->SentLast == 1) { Status = STATUS_INVALID_PARAMETER; UlTraceError(SEND_RESPONSE, ( "http!UlCheckSendHttpResponseFlags(pRequest = %p (%I64x)) %s\n" " Tried to send again after last send!\n", pRequest, pRequest->RequestId, HttpStatusToString(Status) )); goto end; } end: return Status; } // UlCheckSendHttpResponseFlags // // Check pRequest->SentResponse and pRequest->SentLast flags for // UlSendEntityBodyIoctl // __inline NTSTATUS UlCheckSendEntityBodyFlags( IN PUL_INTERNAL_REQUEST pRequest, IN ULONG Flags ) { NTSTATUS Status = STATUS_SUCCESS; // // Ensure a response has already been sent. We can test this without // acquiring the request resource, since the flag is only set (never // reset). // if (pRequest->SentResponse == 0) { // // The application is sending entity without first having // send a response header. This is generally an error, however // we allow the application to override this by passing // the HTTP_SEND_RESPONSE_FLAG_RAW_HEADER flag. // if (Flags & HTTP_SEND_RESPONSE_FLAG_RAW_HEADER) { UlTrace(SEND_RESPONSE, ( "http!UlCheckSendEntityBodyFlags(pRequest = %p (%I64x))\n" " Intentionally sending raw header!\n", pRequest, pRequest->RequestId )); if (1 == InterlockedCompareExchange( (PLONG)&pRequest->SentResponse, 1, 0 )) { Status = STATUS_INVALID_PARAMETER; UlTraceError(SEND_RESPONSE, ( "http!UlCheckSendEntityBodyFlags(pRequest = %p (%I64x))\n" " Already sent a response, %s!\n", pRequest, pRequest->RequestId, HttpStatusToString(Status) )); goto end; } } else { Status = STATUS_INVALID_PARAMETER; UlTraceError(SEND_RESPONSE, ( "http!UlCheckSendEntityBodyFlags(pRequest = %p (%I64x)) %s\n" " No response yet!\n", pRequest, pRequest->RequestId, HttpStatusToString(Status) )); goto end; } } // // Also ensure that all previous calls to SendHttpResponse // and SendEntityBody had the MORE_DATA flag set. // if ((Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) { // // Remember that this was the last send. We shouldn't // get any more data after this. // if (1 == InterlockedCompareExchange( (PLONG)&pRequest->SentLast, 1, 0 )) { Status = STATUS_INVALID_PARAMETER; UlTraceError(SEND_RESPONSE, ( "http!UlCheckSendEntityBodyFlags(pRequest = %p (%I64x)) %s\n" " Last send after previous last send!\n", pRequest, pRequest->RequestId, HttpStatusToString(Status) )); goto end; } } else if (pRequest->SentLast == 1) { Status = STATUS_INVALID_PARAMETER; UlTraceError(SEND_RESPONSE, ( "http!UlCheckSendEntityBodyFlags(pRequest = %p (%I64x)) %s\n" " Tried to send again after last send!\n", pRequest, pRequest->RequestId, HttpStatusToString(Status) )); goto end; } end: return Status; } // UlCheckSendEntityBodyFlags // // Check/Uncheck ConnectionSendLimit and GlobalSendLimit // extern ULONGLONG g_UlTotalSendBytes; extern UL_EXCLUSIVE_LOCK g_UlTotalSendBytesExLock; __inline NTSTATUS UlCheckSendLimit( IN PUL_HTTP_CONNECTION pHttpConnection, IN ULONGLONG SendBytes, IN PULONGLONG pConnectionSendBytes, IN PULONGLONG pGlobalSendBytes ) { NTSTATUS Status = STATUS_DEVICE_BUSY; PAGED_CODE(); ASSERT( pConnectionSendBytes && pGlobalSendBytes ); ASSERT( *pConnectionSendBytes == 0 && *pGlobalSendBytes == 0 ); // // Try ConnectionSendLimit first. // UlAcquireExclusiveLock( &pHttpConnection->ExLock ); if (pHttpConnection->TotalSendBytes <= g_UlConnectionSendLimit) { pHttpConnection->TotalSendBytes += SendBytes; *pConnectionSendBytes = SendBytes; Status = STATUS_SUCCESS; } UlReleaseExclusiveLock( &pHttpConnection->ExLock ); if (STATUS_SUCCESS == Status) { return Status; } // // If we fail the ConnectionSendLimit test, try GlobalSendLimit. // UlAcquireExclusiveLock( &g_UlTotalSendBytesExLock ); if (g_UlTotalSendBytes <= g_UlGlobalSendLimit) { g_UlTotalSendBytes += SendBytes; *pGlobalSendBytes = SendBytes; Status = STATUS_SUCCESS; } UlReleaseExclusiveLock( &g_UlTotalSendBytesExLock ); return Status; } // UlCheckSendLimit __inline VOID UlUncheckSendLimit( IN PUL_HTTP_CONNECTION pHttpConnection, IN ULONGLONG ConnectionSendBytes, IN ULONGLONG GlobalSendBytes ) { if (ConnectionSendBytes) { ASSERT( GlobalSendBytes == 0 ); UlAcquireExclusiveLock( &pHttpConnection->ExLock ); pHttpConnection->TotalSendBytes -= ConnectionSendBytes; UlReleaseExclusiveLock( &pHttpConnection->ExLock ); } if (GlobalSendBytes) { ASSERT( ConnectionSendBytes == 0 ); UlAcquireExclusiveLock( &g_UlTotalSendBytesExLock ); g_UlTotalSendBytes -= GlobalSendBytes; UlReleaseExclusiveLock( &g_UlTotalSendBytesExLock ); } } // UlUncheckSendLimit // // Checkout a cache entry using the fragment name // __inline NTSTATUS UlCheckoutFragmentCacheEntry( IN PCWSTR pFragmentName, IN ULONG FragmentNameLength, IN PUL_APP_POOL_PROCESS pProcess, OUT PUL_URI_CACHE_ENTRY *pFragmentCacheEntry ) { PUL_URI_CACHE_ENTRY pCacheEntry = NULL; URI_SEARCH_KEY SearchKey; NTSTATUS Status = STATUS_SUCCESS; *pFragmentCacheEntry = NULL; if (!g_UriCacheConfig.EnableCache) { Status = STATUS_NOT_SUPPORTED; goto end; } SearchKey.Type = UriKeyTypeNormal; SearchKey.Key.Hash = HashStringNoCaseW(pFragmentName, 0); SearchKey.Key.Hash = HashRandomizeBits(SearchKey.Key.Hash); SearchKey.Key.pUri = (PWSTR) pFragmentName; SearchKey.Key.Length = FragmentNameLength; SearchKey.Key.pPath = NULL; pCacheEntry = UlCheckoutUriCacheEntry(&SearchKey); if (NULL == pCacheEntry) { Status = STATUS_OBJECT_PATH_NOT_FOUND; goto end; } // // Return error if the cache entry has no content. // if (0 == pCacheEntry->ContentLength) { Status = STATUS_FILE_INVALID; goto end; } // // Make sure the process belongs to the same AppPool that created // the fragment cache entry or this is a full response cache that // is meant to be public. // if (IS_FRAGMENT_CACHE_ENTRY(pCacheEntry) && pCacheEntry->pAppPool != pProcess->pAppPool) { Status = STATUS_INVALID_ID_AUTHORITY; goto end; } end: if (NT_SUCCESS(Status)) { *pFragmentCacheEntry = pCacheEntry; } else { if (pCacheEntry) { UlCheckinUriCacheEntry(pCacheEntry); } } return Status; } // UlCheckoutFragmentCacheEntry // // Check to if we need to log. // __inline VOID UlLogHttpResponse( IN PUL_INTERNAL_REQUEST pRequest, IN PUL_LOG_DATA_BUFFER pLogData ) { // // If this is the last response for this request and there was a // log data passed down by the user then now its time to log. // ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) ); ASSERT( IS_VALID_LOG_DATA_BUFFER( pLogData ) ); // // Update the send status if send was not success. // LOG_UPDATE_WIN32STATUS( pLogData, pRequest->LogStatus ); // // Pick the right logging type. // if (pLogData->Flags.Binary) { UlRawLogHttpHit( pLogData ); } else { UlLogHttpHit( pLogData ); } // // Done with pLogData. // UlDestroyLogDataBuffer( pLogData ); } // UlLogHttpResponse // // Validate and sanitize the specified file byte range. // __inline NTSTATUS UlSanitizeFileByteRange ( IN PHTTP_BYTE_RANGE InByteRange, OUT PHTTP_BYTE_RANGE OutByteRange, IN ULONGLONG FileLength ) { ULONGLONG Offset; ULONGLONG Length; Offset = InByteRange->StartingOffset.QuadPart; Length = InByteRange->Length.QuadPart; if (HTTP_BYTE_RANGE_TO_EOF == Offset) { return STATUS_NOT_SUPPORTED; } if (HTTP_BYTE_RANGE_TO_EOF == Length) { if (Offset > FileLength) { return STATUS_FILE_INVALID; } Length = FileLength - Offset; } if (Length > FileLength || Offset > (FileLength - Length)) { return STATUS_FILE_INVALID; } OutByteRange->StartingOffset.QuadPart = Offset; OutByteRange->Length.QuadPart = Length; return STATUS_SUCCESS; } // UlSanitizeFileByteRange #endif // _SENDRESPONSE_H_