You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1484 lines
33 KiB
1484 lines
33 KiB
/*++
|
|
|
|
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_
|