Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

4991 lines
120 KiB

/*++
Copyright (c) 1998-2001 Microsoft Corporation
Module Name:
apool.cxx
Abstract:
Note that most of the routines in this module assume they are called
at PASSIVE_LEVEL.
Author:
Paul McDaniel (paulmcd) 28-Jan-1999
Revision History:
--*/
#include "precomp.h" // project wide headers
#include "apoolp.h" // private header
#ifdef ALLOC_PRAGMA
#pragma alloc_text( INIT, UlInitializeAP )
#pragma alloc_text( PAGE, UlTerminateAP )
#pragma alloc_text( PAGE, UlAttachProcessToAppPool )
#pragma alloc_text( PAGE, UlDeliverRequestToProcess )
#if REFERENCE_DEBUG
#pragma alloc_text( PAGE, UlDereferenceAppPool )
#pragma alloc_text( PAGE, UlReferenceAppPool )
#endif
#pragma alloc_text( PAGE, UlDeleteAppPool )
#pragma alloc_text( PAGE, UlGetPoolFromHandle )
#pragma alloc_text( PAGE, UlQueryAppPoolInformation )
#pragma alloc_text( PAGE, UlSetAppPoolInformation )
#pragma alloc_text( PAGE, UlpSetAppPoolState )
#pragma alloc_text( PAGE, UlWaitForDemandStart )
#pragma alloc_text( PAGE, UlAppPoolObjectFromProcess )
#pragma alloc_text( PAGE, UlLinkConfigGroupToAppPool )
#pragma alloc_text( PAGE, UlpCancelDemandStartWorker )
#pragma alloc_text( PAGE, UlpCancelHttpReceiveWorker )
#pragma alloc_text( PAGE, UlpCopyRequestToIrp )
#pragma alloc_text( PAGE, UlpCopyRequestToBuffer )
#pragma alloc_text( PAGE, UlpPopNewIrp )
#pragma alloc_text( PAGE, UlpIsProcessInAppPool )
#pragma alloc_text( PAGE, UlpRedeliverRequestWorker )
#pragma alloc_text( PAGE, UlpIsRequestQueueEmpty )
#pragma alloc_text( PAGE, UlpSetAppPoolQueueLength )
#pragma alloc_text( PAGE, UlpGetAppPoolQueueLength )
#endif // ALLOC_PRAGMA
#if 0
NOT PAGEABLE -- UlDetachProcessFromAppPool
NOT PAGEABLE -- UlReceiveHttpRequest
NOT PAGEABLE -- UlUnlinkRequestFromProcess
NOT PAGEABLE -- UlWaitForDisconnect
NOT PAGEABLE -- UlpPopIrpFromProcess
NOT PAGEABLE -- UlpQueuePendingRequest
NOT PAGEABLE -- UlpQueueUnboundRequest
NOT PAGEABLE -- UlpDequeueNewRequest
NOT PAGEABLE -- UlpUnbindQueuedRequests
NOT PAGEABLE -- UlCompleteAllWaitForDisconnect
NOT PAGEABLE -- UlpCancelDemandStart
NOT PAGEABLE -- UlpCancelHttpReceive
NOT PAGEABLE -- UlpCancelWaitForDisconnect
#endif
//
// Globals
//
LIST_ENTRY g_AppPoolListHead = {NULL,NULL};
BOOLEAN g_InitAPCalled = FALSE;
/***************************************************************************++
Routine Description:
creates a new process object and attaches it to an apool .
called by handle create and returns the process object to attach to the
handle.
Arguments:
pName - the name of the apool to attach to.
NameLength - the byte count of pName.
Create - whether or not a new apool should be created if pName does not
exist.
pAccessState -
DesiredAccess -
RequestorMode -
ppProcess - returns the newly created PROCESS object.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlAttachProcessToAppPool(
IN PWCHAR pName OPTIONAL,
IN ULONG NameLength,
IN BOOLEAN Create,
IN PACCESS_STATE pAccessState,
IN ACCESS_MASK DesiredAccess,
IN KPROCESSOR_MODE RequestorMode,
OUT PUL_APP_POOL_PROCESS * ppProcess
)
{
NTSTATUS Status;
PUL_APP_POOL_OBJECT pObject = NULL;
PUL_APP_POOL_PROCESS pProcess = NULL;
LIST_ENTRY * pEntry;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(ppProcess != NULL);
Status = STATUS_SUCCESS;
*ppProcess = NULL;
//
// try and find an existing app pool of this name
//
//
// CODEWORK: try and grab the lock shared first then upgrade to
// exclusive if we need to create.
//
// also potentially pre-allocate the memory.
//
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->AppPoolResource, TRUE);
if (pName != NULL)
{
pEntry = g_AppPoolListHead.Flink;
while (pEntry != &g_AppPoolListHead)
{
pObject = CONTAINING_RECORD(
pEntry,
UL_APP_POOL_OBJECT,
ListEntry
);
if (pObject->NameLength == NameLength &&
_wcsnicmp(pObject->pName, pName, NameLength/sizeof(WCHAR)) == 0)
{
//
// match!
//
break;
}
pEntry = pEntry->Flink;
}
//
// found 1?
//
if (pEntry == &g_AppPoolListHead)
{
pObject = NULL;
}
}
//
// Found 1?
//
if (pObject == NULL)
{
//
// nope, allowed to create?
//
if (!Create)
{
UlReleaseResource(&g_pUlNonpagedData->AppPoolResource);
Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto end;
}
//
// create it
//
//
// allocate the object memory
//
pObject = UL_ALLOCATE_STRUCT_WITH_SPACE(
NonPagedPoolCacheAligned,
UL_APP_POOL_OBJECT,
NameLength + sizeof(WCHAR),
UL_APP_POOL_OBJECT_POOL_TAG
);
if (pObject == NULL)
{
UlReleaseResource(&g_pUlNonpagedData->AppPoolResource);
Status = STATUS_NO_MEMORY;
goto end;
}
RtlZeroMemory(
pObject,
NameLength + sizeof(WCHAR) +
sizeof(UL_APP_POOL_OBJECT)
);
pObject->Signature = UL_APP_POOL_OBJECT_POOL_TAG;
pObject->RefCount = 1;
pObject->NameLength = NameLength;
pObject->Enabled = HttpEnabledStateActive;
InitializeListHead(&pObject->ProcessListHead);
UlInitializeNotifyHead(
&pObject->TransientHead,
&g_pUlNonpagedData->ConfigGroupResource
);
Status = UlpInitRequestQueue(
&pObject->NewRequestQueue,
DEFAULT_APP_POOL_QUEUE_MAX
);
ASSERT(NT_SUCCESS(Status)); // default size can't be invalid.
UlInitializeSpinLock(&pObject->QueueSpinLock, "QueueSpinLock");
//
// allocate the resource for sync
//
pObject->pResource = UlResourceNew(UL_APP_POOL_OBJECT_POOL_TAG);
if (pObject->pResource == NULL)
{
UlReleaseResource(&g_pUlNonpagedData->AppPoolResource);
Status = STATUS_NO_MEMORY;
goto end;
}
if (pName != NULL)
{
RtlCopyMemory(
pObject->pName,
pName,
NameLength + sizeof(WCHAR)
);
}
//
// Set the security descriptor.
//
Status = UlAssignSecurity(
&pObject->pSecurityDescriptor,
pAccessState
);
if (!NT_SUCCESS(Status))
{
goto end;
}
//
// Insert it into the global list
//
InsertHeadList(&g_AppPoolListHead, &pObject->ListEntry);
UlReleaseResource(&g_pUlNonpagedData->AppPoolResource);
UlTrace(
REFCOUNT,
("ul!UlAttachProcessToAppPool ap=%p refcount=%d\n",
pObject,
pObject->RefCount)
);
}
else // if (pObject == NULL)
{
//
// reference it
//
REFERENCE_APP_POOL( pObject );
//
// let the lock go
//
UlReleaseResource(&g_pUlNonpagedData->AppPoolResource);
//
// we found one. were we trying to create?
//
if (Create)
{
Status = STATUS_OBJECT_NAME_COLLISION;
goto end;
}
//
// Perform an access check against the app pool.
//
Status = UlAccessCheck(
pObject->pSecurityDescriptor,
pAccessState,
DesiredAccess,
RequestorMode,
pName
);
if (!NT_SUCCESS(Status))
{
goto end;
}
}
//
// Create a process entry for it
//
pProcess = UlCreateAppPoolProcess(pObject);
if (pProcess == NULL)
{
Status = STATUS_NO_MEMORY;
goto end;
}
//
// put it in the app pool list
//
UlAcquireResourceExclusive(&pObject->pResource->Resource, TRUE);
if (DesiredAccess & WRITE_OWNER)
{
pProcess->Controller = 1;
}
else
{
pObject->NumberActiveProcesses++;
}
InsertHeadList(&pObject->ProcessListHead, &pProcess->ListEntry);
UlReleaseResource(&pObject->pResource->Resource);
//
// Return it
//
*ppProcess = pProcess;
end:
if (NT_SUCCESS(Status) == FALSE)
{
if (pObject != NULL)
{
DEREFERENCE_APP_POOL(pObject);
pObject = NULL;
}
if (pProcess != NULL)
{
UL_FREE_POOL_WITH_SIG(pProcess, UL_APP_POOL_PROCESS_POOL_TAG);
}
}
return Status;
}
/***************************************************************************++
Routine Description:
this is called by UlCleanup when the handle count goes to 0. it removes
the PROCESS object from the apool, cancelling all i/o .
Arguments:
pProcess - the process to detach.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlDetachProcessFromAppPool(
IN PUL_APP_POOL_PROCESS pProcess
)
{
LIST_ENTRY PendingRequestHead;
PUL_APP_POOL_OBJECT pAppPool;
NTSTATUS CancelStatus = STATUS_CANCELLED;
PUL_INTERNAL_REQUEST pRequest;
KLOCK_QUEUE_HANDLE LockHandle;
UlTrace(ROUTING, (
"ul!UlDetachProcessFromAppPool(%p, %S)\n",
pProcess,
pProcess->pAppPool->pName
));
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
pAppPool = pProcess->pAppPool;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
UlAcquireResourceExclusive(&pAppPool->pResource->Resource, TRUE);
//
// Mark the process as InCleanup so new I/O won't be attached
//
ASSERT( !pProcess->InCleanup );
pProcess->InCleanup = 1;
//
// Unlink from the App Pool list.
//
RemoveEntryList(&pProcess->ListEntry);
pProcess->ListEntry.Flink = pProcess->ListEntry.Blink = NULL;
//
// Kill our transient URL bindings
//
UlNotifyAllEntries(
UlNotifyOrphanedConfigGroup,
&pProcess->pAppPool->TransientHead,
NULL
);
//
// cancel any pending io.
//
if (pAppPool->pDemandStartIrp != NULL &&
pAppPool->pDemandStartProcess == PsGetCurrentProcess())
{
if (IoSetCancelRoutine(pAppPool->pDemandStartIrp, NULL) == NULL)
{
//
// IoCancelIrp pop'd it first
//
// ok to just ignore this irp, it's been pop'd off the queue
// and will be completed in the cancel routine.
//
// no need to complete it
//
}
else
{
IoGetCurrentIrpStackLocation(
pAppPool->pDemandStartIrp
)->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
pAppPool->pDemandStartIrp->IoStatus.Status = STATUS_CANCELLED;
pAppPool->pDemandStartIrp->IoStatus.Information = 0;
UlCompleteRequest(pAppPool->pDemandStartIrp, g_UlPriorityBoost);
}
pAppPool->pDemandStartIrp = NULL;
pAppPool->pDemandStartProcess = NULL;
}
while (IsListEmpty(&pProcess->NewIrpHead) == FALSE)
{
PLIST_ENTRY pEntry;
PIRP pIrp;
//
// Pop it off the list.
//
pEntry = RemoveHeadList(&pProcess->NewIrpHead);
pEntry->Blink = pEntry->Flink = NULL;
pIrp = CONTAINING_RECORD(pEntry, IRP, Tail.Overlay.ListEntry);
ASSERT(IS_VALID_IRP(pIrp));
//
// pop the cancel routine
//
if (IoSetCancelRoutine(pIrp, NULL) == NULL)
{
//
// IoCancelIrp pop'd it first
//
// ok to just ignore this irp, it's been pop'd off the queue
// and will be completed in the cancel routine.
//
// keep looping
//
pIrp = NULL;
}
else
{
PUL_APP_POOL_OBJECT pProcessAppPool;
//
// cancel it. even if pIrp->Cancel == TRUE we are supposed to
// complete it, our cancel routine will never run.
//
pProcessAppPool = (PUL_APP_POOL_OBJECT)(
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer
);
ASSERT(pProcessAppPool == pAppPool);
DEREFERENCE_APP_POOL(pProcessAppPool);
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
UlCompleteRequest(pIrp, g_UlPriorityBoost);
pIrp = NULL;
}
}
//
// cancel I/O and move requests to local list
//
InitializeListHead(&PendingRequestHead);
UlAcquireInStackQueuedSpinLock(&pAppPool->QueueSpinLock, &LockHandle);
while (pRequest = UlpDequeueRequest(&pProcess->PendingRequestQueue))
{
//
// move the entry to local list so we can close its
// connection outside the app pool lock
//
InsertTailList(&PendingRequestHead, &pRequest->AppPool.AppPoolEntry);
}
UlReleaseInStackQueuedSpinLock(&pAppPool->QueueSpinLock, &LockHandle);
//
// tank any queued new requests
//
UlpUnbindQueuedRequests(pProcess);
//
// adjust number of active processes
//
if (!pProcess->Controller)
{
pAppPool->NumberActiveProcesses--;
}
UlReleaseResource(&pAppPool->pResource->Resource);
//
// close connections associated with the requests
//
while ( !IsListEmpty(&PendingRequestHead) )
{
PLIST_ENTRY pEntry;
PUL_INTERNAL_REQUEST pRequest;
NTSTATUS Status;
pEntry = RemoveHeadList(&PendingRequestHead);
pEntry->Flink = pEntry->Blink = NULL;
pRequest = CONTAINING_RECORD(
pEntry,
UL_INTERNAL_REQUEST,
AppPool.AppPoolEntry
);
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
UlTrace(ROUTING, (
"ul!UlDetachProcessFromAppPool(%p, %S): tanking pending req=%p\n",
pProcess,
pAppPool->pName,
pRequest
));
//
// cancel any pending io
//
UlAcquireResourceExclusive(&(pRequest->pHttpConn->Resource), TRUE);
UlCancelRequestIo(pRequest);
UlReleaseResource(&(pRequest->pHttpConn->Resource));
//
// abort the connection this request is associated with
//
Status = UlCloseConnection(
pRequest->pHttpConn->pConnection,
TRUE,
NULL,
NULL
);
CHECK_STATUS(Status);
//
// drop our list's reference
//
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
}
ASSERT( IsListEmpty(&PendingRequestHead) );
//
// Cancel any remaining WaitForDisconnect IRPs
//
UlAcquireResourceExclusive( &g_pUlNonpagedData->DisconnectResource, TRUE );
UlNotifyAllEntries(
UlpNotifyCompleteWaitForDisconnect,
&pProcess->WaitForDisconnectHead,
&CancelStatus
);
UlReleaseResource( &g_pUlNonpagedData->DisconnectResource );
//
// Dereference
//
DEREFERENCE_APP_POOL(pAppPool);
//
// Kill any cache entries related to this process
//
UlFlushCacheByProcess(pProcess);
return STATUS_SUCCESS;
}
#if REFERENCE_DEBUG
/***************************************************************************++
Routine Description:
increments the refcount.
Arguments:
pAppPool - the object to increment.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
VOID
UlReferenceAppPool(
IN PUL_APP_POOL_OBJECT pAppPool
REFERENCE_DEBUG_FORMAL_PARAMS
)
{
LONG refCount;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
refCount = InterlockedIncrement( &pAppPool->RefCount );
WRITE_REF_TRACE_LOG(
g_pAppPoolTraceLog,
REF_ACTION_REFERENCE_APP_POOL,
refCount,
pAppPool,
pFileName,
LineNumber
);
UlTrace(
REFCOUNT,
("ul!UlReferenceAppPool ap=%p refcount=%d\n",
pAppPool,
refCount)
);
} // UlReferenceAppPool
/***************************************************************************++
Routine Description:
decrements the refcount. if it hits 0, destruct's the apool, cancelling
all i/o and dumping all queued requests.
Arguments:
pAppPool - the object to decrement.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
VOID
UlDereferenceAppPool(
IN PUL_APP_POOL_OBJECT pAppPool
REFERENCE_DEBUG_FORMAL_PARAMS
)
{
LONG refCount;
NTSTATUS Status;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
refCount = InterlockedDecrement( &pAppPool->RefCount );
//
// Tracing.
//
WRITE_REF_TRACE_LOG(
g_pAppPoolTraceLog,
REF_ACTION_DEREFERENCE_APP_POOL,
refCount,
pAppPool,
pFileName,
LineNumber
);
UlTrace(
REFCOUNT,
("ul!UlDereferenceAppPool ap=%p refcount=%d\n",
pAppPool,
refCount)
);
//
// Clean up if necessary.
//
if (refCount == 0)
{
DELETE_APP_POOL(pAppPool);
}
} // UlDereferenceAppPool
#endif
/***************************************************************************++
Routine Description:
decrements the refcount. if it hits 0, destruct's the apool, cancelling
all i/o and dumping all queued requests.
Arguments:
pAppPool - the object to decrement.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
VOID
UlDeleteAppPool(
IN PUL_APP_POOL_OBJECT pAppPool
REFERENCE_DEBUG_FORMAL_PARAMS
)
{
PUL_INTERNAL_REQUEST pRequest;
UlAcquireResourceExclusive(&g_pUlNonpagedData->AppPoolResource, TRUE);
RemoveEntryList(&pAppPool->ListEntry);
pAppPool->ListEntry.Flink = pAppPool->ListEntry.Blink = NULL;
UlReleaseResource(&g_pUlNonpagedData->AppPoolResource);
ASSERT( UlDbgResourceUnownedExclusive( &pAppPool->pResource->Resource ) );
//
// there better not be any process objects hanging around
//
ASSERT(IsListEmpty(&pAppPool->ProcessListHead));
//
// there should not be any transient bindings around
//
ASSERT(IsListEmpty(&pAppPool->TransientHead.ListHead));
//
// tank any pending reqeusts
//
//
// CODEWORK, BUGBUG: need to close the connection ?
//
while (pRequest = UlpDequeueRequest(&pAppPool->NewRequestQueue))
{
//
// mark it as unlinked and undo references
//
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
}
//
// Cleanup any security descriptor on the object.
//
UlDeassignSecurity( &pAppPool->pSecurityDescriptor );
//
// delete the resource
//
DEREFERENCE_RESOURCE( pAppPool->pResource );
pAppPool->pResource = NULL;
// CODEWORK: is this code right?
UL_FREE_POOL_WITH_SIG(pAppPool, UL_APP_POOL_OBJECT_POOL_TAG);
} // UlDeleteAppPool
/***************************************************************************++
Routine Description:
Queries the app-pool queue length. If the supplied output buffer is NULL
the required length is returned in the optional length argument.
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlQueryAppPoolInformation(
IN PUL_APP_POOL_PROCESS pProcess,
IN HTTP_APP_POOL_INFORMATION_CLASS InformationClass,
OUT PVOID pAppPoolInformation,
IN ULONG Length,
OUT PULONG pReturnLength OPTIONAL
)
{
NTSTATUS Status = STATUS_SUCCESS;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
//
// This shouldn't happen, but just in case
//
if (!IS_VALID_AP_PROCESS(pProcess))
{
return STATUS_INVALID_PARAMETER;
}
//
// Do the action
//
switch (InformationClass)
{
case HttpAppPoolDemandStartInformation:
case HttpAppPoolDemandStartFlagInformation:
Status = STATUS_NOT_IMPLEMENTED;
break;
case HttpAppPoolQueueLengthInformation:
if (pAppPoolInformation == NULL)
{
//
// Return the size needed
//
*pReturnLength = sizeof(LONG);
}
//
// check the size of the buffer
//
else if (Length >= sizeof(LONG))
{
//
// Get the request queue length
//
*((PLONG)pAppPoolInformation) = UlpGetAppPoolQueueLength(pProcess);
*pReturnLength = sizeof(LONG);
}
else
{
Status = STATUS_BUFFER_TOO_SMALL;
}
break;
case HttpAppPoolStateInformation:
if (pAppPoolInformation == NULL)
{
//
// Return the size needed
//
*pReturnLength = sizeof(HTTP_ENABLED_STATE);
}
else if (Length < sizeof(HTTP_ENABLED_STATE))
{
Status = STATUS_BUFFER_TOO_SMALL;
}
else
{
PHTTP_ENABLED_STATE pCurrentState =
((PHTTP_ENABLED_STATE) pAppPoolInformation);
PUL_APP_POOL_OBJECT pAppPool = pProcess->pAppPool;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
*pCurrentState = pAppPool->Enabled;
*pReturnLength = sizeof(HTTP_ENABLED_STATE);
}
break;
default:
// should have been caught in UlQueryAppPoolInformationIoctl
ASSERT(FALSE);
Status = STATUS_INVALID_PARAMETER;
break;
}
return Status;
} // UlQueryAppPoolInformation
/***************************************************************************++
Routine Description:
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlSetAppPoolInformation(
IN PUL_APP_POOL_PROCESS pProcess,
IN HTTP_APP_POOL_INFORMATION_CLASS InformationClass,
IN PVOID pAppPoolInformation,
IN ULONG Length
)
{
NTSTATUS Status = STATUS_SUCCESS;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
//
// check parameters
//
// this shouldn't happen, but just in case
if (!IS_VALID_AP_PROCESS(pProcess)) {
return STATUS_INVALID_PARAMETER;
}
if (!pAppPoolInformation) {
return STATUS_INVALID_PARAMETER;
}
//
// Do the action
//
switch (InformationClass)
{
case HttpAppPoolDemandStartInformation:
case HttpAppPoolDemandStartFlagInformation:
Status = STATUS_NOT_IMPLEMENTED;
break;
case HttpAppPoolQueueLengthInformation:
//
// check the size of the buffer
//
if (Length >= sizeof(LONG))
{
PLONG pQueueLength = (PLONG) pAppPoolInformation;
//
// Set the max incoming request queue length
//
Status = UlpSetAppPoolQueueLength(pProcess, * pQueueLength);
} else {
Status = STATUS_INVALID_PARAMETER;
}
break;
case HttpAppPoolStateInformation:
if (Length < sizeof(HTTP_ENABLED_STATE))
{
Status = STATUS_INVALID_PARAMETER;
}
else
{
HTTP_ENABLED_STATE NewState =
*((PHTTP_ENABLED_STATE) pAppPoolInformation);
if ((NewState != HttpEnabledStateActive)
&& (NewState != HttpEnabledStateInactive))
{
Status = STATUS_INVALID_PARAMETER;
}
else
{
UlpSetAppPoolState(pProcess, NewState);
}
}
break;
default:
// should have been caught in UlSetAppPoolInformationIoctl
ASSERT(FALSE);
Status = STATUS_INVALID_PARAMETER;
break;
}
return Status;
} // UlSetAppPoolInformation
/***************************************************************************++
Routine Description:
Marks an app pool as active or inactive. If setting to inactive,
will return immediately 503 on all requests queued to app pool.
Arguments:
pProcess - the app pool process object with which the irp is associated.
Enabled - mark app pool as active or inactive
Return Value:
NTSTATUS
--***************************************************************************/
NTSTATUS
UlpSetAppPoolState(
IN PUL_APP_POOL_PROCESS pProcess,
IN HTTP_ENABLED_STATE Enabled
)
{
NTSTATUS Status = STATUS_SUCCESS;
PUL_APP_POOL_OBJECT pAppPool;
LIST_ENTRY RequestQueueHead;
KLOCK_QUEUE_HANDLE LockHandle;
ASSERT(IS_VALID_AP_PROCESS(pProcess));
pAppPool = pProcess->pAppPool;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
UlTrace(ROUTING, (
"http!UlpSetAppPoolState(AppPool=%p, %lu).\n",
pAppPool, (ULONG) Enabled
));
InitializeListHead(&RequestQueueHead);
UlAcquireResourceExclusive(&pAppPool->pResource->Resource, TRUE);
pAppPool->Enabled = Enabled;
if (Enabled == HttpEnabledStateInactive)
{
// Make a copy of the app pool's new request queue
if (IsListEmpty(&pAppPool->NewRequestQueue.RequestHead))
{
ASSERT(pAppPool->NewRequestQueue.RequestCount == 0);
}
else
{
RequestQueueHead.Flink
= pAppPool->NewRequestQueue.RequestHead.Flink;
RequestQueueHead.Blink
= pAppPool->NewRequestQueue.RequestHead.Blink;
RequestQueueHead.Blink->Flink = &RequestQueueHead;
RequestQueueHead.Flink->Blink = &RequestQueueHead;
ASSERT(pAppPool->NewRequestQueue.RequestCount > 0);
// Now zap the app pool's new request queue
InitializeListHead(&pAppPool->NewRequestQueue.RequestHead);
pAppPool->NewRequestQueue.RequestCount = 0;
}
}
UlReleaseResource(&pAppPool->pResource->Resource);
// CODEWORK: need to check Enabled flag elsewhere?
// Destroy the list outside of the app pool lock
if (Enabled == HttpEnabledStateInactive)
{
ULONG cRequests = 0;
while (! IsListEmpty(&RequestQueueHead))
{
PUL_INTERNAL_REQUEST pRequest
= CONTAINING_RECORD(
RequestQueueHead.Flink,
UL_INTERNAL_REQUEST,
AppPool.AppPoolEntry
);
PUL_HTTP_CONNECTION pHttpConn = pRequest->pHttpConn;
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConn));
UlAcquireInStackQueuedSpinLock(
&pAppPool->QueueSpinLock,
&LockHandle
);
RemoveEntryList(&pRequest->AppPool.AppPoolEntry);
pRequest->AppPool.AppPoolEntry.Flink =
pRequest->AppPool.AppPoolEntry.Blink = NULL;
pRequest->AppPool.QueueState = QueueUnlinkedState;
pRequest->ErrorCode = UlErrorUnavailable; // 503
UlReleaseInStackQueuedSpinLock(
&pAppPool->QueueSpinLock,
&LockHandle
);
UlAcquireResourceExclusive(&pHttpConn->Resource, TRUE);
UlSendErrorResponse(pHttpConn);
UlReleaseResource(&pHttpConn->Resource);
// CODEWORK: Need to call UlCancelRequestIo()
// or UlCloseConnection()?
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
++cRequests;
}
UlTrace(ROUTING, (
"%lu unhandled requests 503'd from AppPool %p.\n",
cRequests, pAppPool
));
}
ASSERT(IsListEmpty(&RequestQueueHead));
return Status;
} // UlpSetAppPoolState
/***************************************************************************++
Routine Description:
associates an irp with the apool that will be completed prior to any
requests being queued.
Arguments:
pProcess - the process object that is queueing this irp
pIrp - the irp to associate.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlWaitForDemandStart(
IN PUL_APP_POOL_PROCESS pProcess,
IN PIRP pIrp
)
{
NTSTATUS Status;
PIO_STACK_LOCATION pIrpSp;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
ASSERT(IS_VALID_AP_OBJECT(pProcess->pAppPool));
ASSERT(pIrp != NULL);
UlAcquireResourceExclusive(&pProcess->pAppPool->pResource->Resource, TRUE);
//
// Make sure we're not cleaning up the process
//
if (pProcess->InCleanup) {
Status = STATUS_INVALID_HANDLE;
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
goto end;
}
//
// already got one?
//
if (pProcess->pAppPool->pDemandStartIrp != NULL)
{
Status = STATUS_OBJECT_NAME_COLLISION;
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
goto end;
}
//
// anything waiting in the queue?
//
if (UlpIsRequestQueueEmpty(pProcess))
{
//
// nope, pend the irp
//
IoMarkIrpPending(pIrp);
//
// give the irp a pointer to the app pool
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pProcess->pAppPool;
//
// the cancel routine better not see an irp if it runs immediately
//
ASSERT(pProcess->pAppPool->pDemandStartIrp == NULL);
IoSetCancelRoutine(pIrp, &UlpCancelDemandStart);
//
// cancelled?
//
if (pIrp->Cancel)
{
//
// darn it, need to make sure the irp get's completed
//
if (IoSetCancelRoutine( pIrp, NULL ) != NULL)
{
//
// we are in charge of completion, IoCancelIrp didn't
// see our cancel routine (and won't). ioctl wrapper
// will complete it
//
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
pIrp->IoStatus.Information = 0;
UlUnmarkIrpPending( pIrp );
Status = STATUS_CANCELLED;
goto end;
}
//
// our cancel routine will run and complete the irp,
// don't touch it
//
//
// STATUS_PENDING will cause the ioctl wrapper to
// not complete (or touch in any way) the irp
//
Status = STATUS_PENDING;
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
goto end;
}
//
// now we are safe to queue it
//
pProcess->pAppPool->pDemandStartIrp = pIrp;
pProcess->pAppPool->pDemandStartProcess = PsGetCurrentProcess();
Status = STATUS_PENDING;
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
goto end;
}
else
{
//
// something's in the queue, instant demand start
//
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
IoMarkIrpPending(pIrp);
pIrp->IoStatus.Status = STATUS_SUCCESS;
UlCompleteRequest( pIrp, g_UlPriorityBoost );
Status = STATUS_PENDING;
goto end;
}
end:
return Status;
} // UlWaitForDemandStart
/***************************************************************************++
Routine Description:
receives a new http request into pIrp.
Arguments:
RequestId - NULL for new requests, non-NULL for a specific request,
which must be on the special queue.
Flags - ignored
pProcess - the process that wants the request
pIrp - the irp to receive the request
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlReceiveHttpRequest(
IN HTTP_REQUEST_ID RequestId,
IN ULONG Flags,
IN PUL_APP_POOL_PROCESS pProcess,
IN PIRP pIrp
)
{
NTSTATUS Status;
PUL_INTERNAL_REQUEST pRequest = NULL;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
ASSERT(IS_VALID_AP_OBJECT(pProcess->pAppPool));
ASSERT(pIrp != NULL);
UlAcquireResourceShared(&pProcess->pAppPool->pResource->Resource, TRUE);
//
// Make sure we're not cleaning up the process
//
if (pProcess->InCleanup) {
Status = STATUS_INVALID_HANDLE;
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
goto end;
}
//
// Is this for a new request?
//
if (HTTP_IS_NULL_ID(&RequestId))
{
//
// Do we have a queue'd new request?
//
pRequest = UlpDequeueNewRequest(pProcess);
if (pRequest == NULL)
{
PIO_STACK_LOCATION pIrpSp;
//
// Nope, queue the irp up.
//
IoMarkIrpPending(pIrp);
//
// give the irp a pointer to the app pool
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pProcess->pAppPool;
REFERENCE_APP_POOL(pProcess->pAppPool);
//
// set to these to null just in case the cancel routine runs
//
pIrp->Tail.Overlay.ListEntry.Flink = NULL;
pIrp->Tail.Overlay.ListEntry.Blink = NULL;
IoSetCancelRoutine(pIrp, &UlpCancelHttpReceive);
//
// cancelled?
//
if (pIrp->Cancel)
{
//
// darn it, need to make sure the irp get's completed
//
if (IoSetCancelRoutine( pIrp, NULL ) != NULL)
{
//
// we are in charge of completion, IoCancelIrp didn't
// see our cancel routine (and won't). ioctl wrapper
// will complete it
//
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
DEREFERENCE_APP_POOL(pProcess->pAppPool);
pIrp->IoStatus.Information = 0;
UlUnmarkIrpPending( pIrp );
Status = STATUS_CANCELLED;
goto end;
}
//
// our cancel routine will run and complete the irp,
// don't touch it
//
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
//
// STATUS_PENDING will cause the ioctl wrapper to
// not complete (or touch in any way) the irp
//
Status = STATUS_PENDING;
goto end;
}
//
// now we are safe to queue it
//
ExInterlockedInsertTailList(
&pProcess->NewIrpHead,
&pIrp->Tail.Overlay.ListEntry,
KSPIN_LOCK_FROM_UL_SPIN_LOCK(&pProcess->NewIrpSpinLock)
);
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
Status = STATUS_PENDING;
goto end;
}
else // if (pRequest == NULL)
{
//
// Have a queue'd request, serve it up!
//
// UlpDequeueNewRequest gives ourselves a short-lived reference
//
//
// all done with the app pool
//
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
//
// Copy it to the irp, the routine will take ownership
// of pRequest if it is not able to copy it to the irp.
//
// it will also complete the irp so don't touch it later.
//
IoMarkIrpPending(pIrp);
UlpCopyRequestToIrp(pRequest, pIrp);
pIrp = NULL;
//
// let go our short-lived reference
//
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
pRequest = NULL;
Status = STATUS_PENDING;
goto end;
}
}
else // if (HTTP_IS_NULL_ID(&RequestId))
{
//
// need to grab the specific request
//
//
// Get the object ptr from id
//
pRequest = UlGetRequestFromId(RequestId);
if (UL_IS_VALID_INTERNAL_REQUEST(pRequest) == FALSE)
{
Status = STATUS_INVALID_PARAMETER;
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
goto end;
}
//
// Is this request really queue'd on this process waiting ?
//
if ((pRequest->AppPool.QueueState != QueueCopiedState) ||
(pRequest->AppPool.pProcess != pProcess))
{
Status = STATUS_INVALID_PARAMETER;
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
goto end;
}
UlTrace(ROUTING, (
"ul!UlReceiveHttpRequest(ID = %I64x, pProcess = %p)\n"
" pAppPool = %p (%S)\n"
" Found pRequest = %p on PendingRequest queue\n",
RequestId,
pProcess,
pProcess->pAppPool,
pProcess->pAppPool->pName,
pRequest
));
//
// let go the lock
//
UlReleaseResource(&pProcess->pAppPool->pResource->Resource);
//
// Copy it to the irp, the routine will take ownership
// of pRequest if it is not able to copy it to the irp
//
IoMarkIrpPending(pIrp);
UlpCopyRequestToIrp(pRequest, pIrp);
//
// let go our reference
//
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
pRequest = NULL;
Status = STATUS_PENDING;
goto end;
}
end:
if (pRequest != NULL)
{
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
pRequest = NULL;
}
//
// At this point if Status != PENDING, the ioctl wrapper will
// complete pIrp
//
return Status;
}
/***************************************************************************++
Routine Description:
called by the http engine to deliver a request to an apool.
this attemps to find a free irp from any process attached to the apool
and copies the request to that irp.
otherwise it queues the request, without taking a refcount on it. the
request will remove itself from this queue if the connection is dropped.
Arguments:
pRequest - the request to deliver
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlDeliverRequestToProcess(
IN PUL_APP_POOL_OBJECT pAppPool,
IN PUL_INTERNAL_REQUEST pRequest
)
{
NTSTATUS Status;
PIRP pDemandStartIrp;
PIRP pIrp = NULL;
PUL_APP_POOL_PROCESS pProcess = NULL;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
ASSERT(IS_VALID_URL_CONFIG_GROUP_INFO(&pRequest->ConfigInfo));
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
//
// Has the app pool been disabled?
//
if (pAppPool->Enabled == HttpEnabledStateInactive)
{
pRequest->ErrorCode = UlErrorUnavailable; // 503
return STATUS_PORT_DISCONNECTED;
}
Status = STATUS_SUCCESS;
//
// grab the lock!
//
UlAcquireResourceShared(&pAppPool->pResource->Resource, TRUE);
UlTrace(ROUTING, (
"ul!UlDeliverRequestToProcess(pRequest = %p)\n"
" verb + path -> %d %S\n"
" pAppPool = %p (%S)\n",
pRequest,
pRequest->Verb,
pRequest->CookedUrl.pUrl,
pAppPool,
pAppPool->pName
));
TRACE_TIME(
pRequest->ConnectionId,
pRequest->RequestId,
TIME_ACTION_ROUTE_REQUEST
);
//
// hook up request references
//
UL_REFERENCE_INTERNAL_REQUEST(pRequest);
if (pAppPool->NumberActiveProcesses <= 1)
{
//
// bypass process binding if we have only one active process
//
pProcess = NULL;
pIrp = UlpPopNewIrp(pAppPool, &pProcess);
}
else
{
//
// check for a process binding
//
pProcess = UlQueryProcessBinding(pRequest->pHttpConn, pAppPool);
if (UlpIsProcessInAppPool(pProcess, pAppPool)) {
//
// we're bound to a valid process.
// Try to get a free irp from that process
//
pIrp = UlpPopIrpFromProcess(pProcess);
} else {
//
// we are unbound or bound to a process that went away.
// Try and get an free irp from any process.
//
pProcess = NULL;
pIrp = UlpPopNewIrp(pAppPool, &pProcess);
//
// establish a binding if we got something
//
if (pIrp != NULL) {
ASSERT( IS_VALID_AP_PROCESS( pProcess ) );
Status = UlBindConnectionToProcess(
pRequest->pHttpConn,
pAppPool,
pProcess
);
//
// Is there anything special we should do on
// failure? I don't think it should be fatal..
//
Status = STATUS_SUCCESS;
}
}
}
//
// If we have an IRP, complete it. Otherwise queue the request.
//
if (pIrp != NULL)
{
ASSERT( pIrp->MdlAddress != NULL );
ASSERT( pProcess->InCleanup == 0 );
//
// attach the request to this process, this grabs the lock
// we are about to let go of. this allows us to drop the
// connection if the process dies in the middle of request processing.
//
UlpQueuePendingRequest(pProcess, pRequest);
//
// we are all done and about to complete the irp, free the lock
//
UlReleaseResource(&pAppPool->pResource->Resource);
//
// Copy it to the irp, the routine will take ownership
// of pRequest if it is not able to copy it to the irp
//
// it will also complete the irp, don't touch it later
//
UlpCopyRequestToIrp(pRequest, pIrp);
}
else
{
ASSERT(pIrp == NULL);
//
// Either didn't find an IRP or there's stuff on the pending request
// list, so queue this pending request.
//
Status = UlpQueueUnboundRequest(pAppPool, pRequest);
if (!NT_SUCCESS(Status)) {
//
// doh! we couldn't queue it, so let go of the request
//
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
UlReleaseResource(&pAppPool->pResource->Resource);
return Status;
}
//
// complete the demand start
//
pDemandStartIrp = pAppPool->pDemandStartIrp;
if (pDemandStartIrp != NULL)
{
pDemandStartIrp = (PIRP) InterlockedCompareExchangePointer(
(PVOID *) &pAppPool->pDemandStartIrp,
NULL,
pDemandStartIrp
);
}
if (pDemandStartIrp != NULL)
{
//
// pop the cancel routine
//
if (IoSetCancelRoutine(pDemandStartIrp, NULL) == NULL)
{
//
// IoCancelIrp pop'd it first
//
// ok to just ignore this irp, it's been pop'd off the queue
// and will be completed in the cancel routine.
//
// no need to complete it
//
}
else if (pDemandStartIrp->Cancel)
{
//
// we pop'd it first. but the irp is being cancelled
// and our cancel routine will never run.
//
IoGetCurrentIrpStackLocation(
pDemandStartIrp
)->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
pDemandStartIrp->IoStatus.Status = STATUS_CANCELLED;
pDemandStartIrp->IoStatus.Information = 0;
pIrp = pDemandStartIrp;
}
else
{
//
// free to use the irp
//
IoGetCurrentIrpStackLocation(
pDemandStartIrp
)->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
pDemandStartIrp->IoStatus.Status = STATUS_SUCCESS;
pDemandStartIrp->IoStatus.Information = 0;
pIrp = pDemandStartIrp;
}
pAppPool->pDemandStartProcess = NULL;
}
//
// now we finished queue'ing the request, free the lock
//
UlReleaseResource(&pAppPool->pResource->Resource);
//
// complete any demand start irp (after releasing the resource)
//
if (pIrp != NULL)
{
UlCompleteRequest(pIrp, g_UlPriorityBoost);
pIrp = NULL;
}
}
return Status;
}
/***************************************************************************++
Routine Description:
Removes a request from any app pool lists.
Arguments:
pRequest - the request to be unlinked
--***************************************************************************/
VOID
UlUnlinkRequestFromProcess(
IN PUL_APP_POOL_OBJECT pAppPool,
IN PUL_INTERNAL_REQUEST pRequest
)
{
KLOCK_QUEUE_HANDLE LockHandle;
//
// Sanity check
//
PAGED_CODE();
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
UlAcquireInStackQueuedSpinLock(&pAppPool->QueueSpinLock, &LockHandle);
//
// remove from whatever queue we're on
//
switch (pRequest->AppPool.QueueState)
{
case QueueDeliveredState:
//
// we're on the apool object new request queue
//
UlpRemoveRequest(&pAppPool->NewRequestQueue, pRequest);
pRequest->AppPool.QueueState = QueueUnlinkedState;
//
// clean up the references
//
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
break;
case QueueCopiedState:
//
// we're on the apool process pending queue
//
ASSERT(IS_VALID_AP_PROCESS(pRequest->AppPool.pProcess));
ASSERT(pRequest->AppPool.pProcess->pAppPool == pAppPool);
UlpRemoveRequest(
&pRequest->AppPool.pProcess->PendingRequestQueue,
pRequest
);
pRequest->AppPool.QueueState = QueueUnlinkedState;
//
// clean up the references
//
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
break;
case QueueUnroutedState:
case QueueUnlinkedState:
//
// It's not on our lists, so we don't do anything
//
break;
default:
//
// this shouldn't happen
//
ASSERT(FALSE);
break;
}
UlReleaseInStackQueuedSpinLock(&pAppPool->QueueSpinLock, &LockHandle);
} // UlUnlinkRequestFromProcess
/***************************************************************************++
Routine Description:
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlInitializeAP(
VOID
)
{
NTSTATUS Status = STATUS_SUCCESS;
ASSERT(!g_InitAPCalled);
if (!g_InitAPCalled)
{
InitializeListHead(&g_AppPoolListHead);
Status = UlInitializeResource(
&g_pUlNonpagedData->AppPoolResource,
"AppPoolResource",
0,
UL_APP_POOL_RESOURCE_TAG
);
ASSERT(NT_SUCCESS(Status)); // the call always returns success
if (NT_SUCCESS(Status)) {
Status = UlInitializeResource(
&g_pUlNonpagedData->DisconnectResource,
"DisconnectResource",
0,
UL_DISCONNECT_RESOURCE_TAG
);
if (NT_SUCCESS(Status)) {
//
// finished, remember that we're initialized
//
g_InitAPCalled = TRUE;
} else {
UlDeleteResource(&g_pUlNonpagedData->AppPoolResource);
}
}
}
return Status;
}
/***************************************************************************++
Routine Description:
Arguments:
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
VOID
UlTerminateAP(
VOID
)
{
NTSTATUS Status;
if (g_InitAPCalled)
{
Status = UlDeleteResource(&g_pUlNonpagedData->AppPoolResource);
ASSERT(NT_SUCCESS(Status));
Status = UlDeleteResource(&g_pUlNonpagedData->DisconnectResource);
ASSERT(NT_SUCCESS(Status));
g_InitAPCalled = FALSE;
}
}
/***************************************************************************++
Routine Description:
Allocates and initializes a UL_APP_POOL_PROCESS object
Arguments:
None.
Return Values:
NULL on failure, process object on success
--***************************************************************************/
PUL_APP_POOL_PROCESS
UlCreateAppPoolProcess(
PUL_APP_POOL_OBJECT pObject
)
{
PUL_APP_POOL_PROCESS pProcess;
//
// Sanity check
//
PAGED_CODE();
pProcess = UL_ALLOCATE_STRUCT(
NonPagedPool,
UL_APP_POOL_PROCESS,
UL_APP_POOL_PROCESS_POOL_TAG
);
if (pProcess) {
NTSTATUS Status;
RtlZeroMemory(pProcess, sizeof(UL_APP_POOL_PROCESS));
pProcess->Signature = UL_APP_POOL_PROCESS_POOL_TAG;
pProcess->pAppPool = pObject;
InitializeListHead(&pProcess->NewIrpHead);
Status = UlpInitRequestQueue(
&pProcess->PendingRequestQueue, // the queue
-1 // unlimited size
);
ASSERT(NT_SUCCESS(Status));
UlInitializeSpinLock(&pProcess->NewIrpSpinLock, "NewIrpSpinLock");
//
// remember current process (for debugging)
//
pProcess->pProcess = PsGetCurrentProcess();
//
// Init list of WaitForDisconnect IRPs
//
UlInitializeNotifyHead(
&pProcess->WaitForDisconnectHead,
NULL
);
}
return pProcess;
}
/***************************************************************************++
Routine Description:
Destroys a UL_APP_POOL_PROCESS object
Arguments:
pProcess - object to destory
--***************************************************************************/
VOID
UlFreeAppPoolProcess(
PUL_APP_POOL_PROCESS pProcess
)
{
//
// Sanity check
//
PAGED_CODE();
ASSERT( IS_VALID_AP_PROCESS(pProcess) );
ASSERT( pProcess->InCleanup );
//
// free the pool
//
UL_FREE_POOL_WITH_SIG(pProcess, UL_APP_POOL_PROCESS_POOL_TAG);
}
/***************************************************************************++
Routine Description:
cancels the pending user mode irp which was to receive demand start
notification. this routine ALWAYS results in the irp being completed.
note: we queue off to cancel in order to process the cancellation at lower
irql.
Arguments:
pDeviceObject - the device object
pIrp - the irp to cancel
--***************************************************************************/
VOID
UlpCancelDemandStart(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
{
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
ASSERT(pIrp != NULL);
//
// release the cancel spinlock. This means the cancel routine
// must be the one completing the irp (to avoid the race of
// completion + reuse prior to the cancel routine running).
//
IoReleaseCancelSpinLock(pIrp->CancelIrql);
//
// queue the cancel to a worker to ensure passive irql.
//
UL_CALL_PASSIVE(
UL_WORK_ITEM_FROM_IRP( pIrp ),
&UlpCancelDemandStartWorker
);
}
/***************************************************************************++
Routine Description:
Actually performs the cancel for the irp.
Arguments:
pWorkItem - the work item to process.
--***************************************************************************/
VOID
UlpCancelDemandStartWorker(
IN PUL_WORK_ITEM pWorkItem
)
{
PIRP pIrp;
PUL_APP_POOL_OBJECT pAppPool;
//
// Sanity check.
//
PAGED_CODE();
//
// grab the irp off the work item
//
pIrp = UL_WORK_ITEM_TO_IRP( pWorkItem );
ASSERT(IS_VALID_IRP(pIrp));
//
// grab the app pool off the irp
//
pAppPool = (PUL_APP_POOL_OBJECT)(
IoGetCurrentIrpStackLocation(pIrp)->Parameters.DeviceIoControl.Type3InputBuffer
);
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
//
// grab the lock protecting the queue'd irp
//
UlAcquireResourceExclusive(&pAppPool->pResource->Resource, TRUE);
//
// does it need to be dequeue'd ?
//
if (pAppPool->pDemandStartIrp != NULL)
{
//
// remove it
//
pAppPool->pDemandStartIrp = NULL;
pAppPool->pDemandStartProcess = NULL;
}
//
// let the lock go
//
UlReleaseResource(&pAppPool->pResource->Resource);
//
// let our reference go
//
IoGetCurrentIrpStackLocation(pIrp)->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
//
// complete the irp
//
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
UlCompleteRequest( pIrp, g_UlPriorityBoost );
}
/***************************************************************************++
Routine Description:
cancels the pending user mode irp which was to receive the http request.
this routine ALWAYS results in the irp being completed.
note: we queue off to cancel in order to process the cancellation at lower
irql.
Arguments:
pDeviceObject - the device object
pIrp - the irp to cancel
--***************************************************************************/
VOID
UlpCancelHttpReceive(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
{
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
ASSERT(pIrp != NULL);
//
// release the cancel spinlock. This means the cancel routine
// must be the one completing the irp (to avoid the race of
// completion + reuse prior to the cancel routine running).
//
IoReleaseCancelSpinLock(pIrp->CancelIrql);
//
// queue the cancel to a worker to ensure passive irql.
//
UL_CALL_PASSIVE(
UL_WORK_ITEM_FROM_IRP( pIrp ),
&UlpCancelHttpReceiveWorker
);
}
/***************************************************************************++
Routine Description:
Actually performs the cancel for the irp.
Arguments:
pWorkItem - the work item to process.
--***************************************************************************/
VOID
UlpCancelHttpReceiveWorker(
IN PUL_WORK_ITEM pWorkItem
)
{
PUL_APP_POOL_OBJECT pAppPool;
PIRP pIrp;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(pWorkItem != NULL);
//
// grab the irp off the work item
//
pIrp = UL_WORK_ITEM_TO_IRP( pWorkItem );
ASSERT(IS_VALID_IRP(pIrp));
//
// grab the app pool off the irp
//
pAppPool = (PUL_APP_POOL_OBJECT)(
IoGetCurrentIrpStackLocation(pIrp)->Parameters.DeviceIoControl.Type3InputBuffer
);
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
//
// grab the lock protecting the queue
//
UlAcquireResourceExclusive(&pAppPool->pResource->Resource, TRUE);
//
// does it need to be de-queue'd ?
//
if (pIrp->Tail.Overlay.ListEntry.Flink != NULL)
{
//
// remove it
//
RemoveEntryList(&pIrp->Tail.Overlay.ListEntry);
pIrp->Tail.Overlay.ListEntry.Flink = NULL;
pIrp->Tail.Overlay.ListEntry.Blink = NULL;
}
//
// let the lock go
//
UlReleaseResource(&pAppPool->pResource->Resource);
//
// let our reference go
//
IoGetCurrentIrpStackLocation(pIrp)->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
DEREFERENCE_APP_POOL(pAppPool);
//
// complete the irp
//
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
UlCompleteRequest( pIrp, g_UlPriorityBoost );
} // UlpCancelHttpReceive
/******************************************************************************
Routine Description:
Copy an HTTP request to a buffer.
Arguments:
pRequest - Pointer to this request.
pBuffer - Pointer to buffer where we'll copy.
BufferLength - Length of pBuffer.
pEntityBody - Pointer to entity body of request.
EntityBodyLength - Length of entity body.
******************************************************************************/
NTSTATUS
UlpCopyRequestToBuffer(
PUL_INTERNAL_REQUEST pRequest,
PUCHAR pKernelBuffer,
PVOID pUserBuffer,
ULONG BufferLength,
PUCHAR pEntityBody,
ULONG EntityBodyLength
)
{
PHTTP_REQUEST pHttpRequest;
PHTTP_UNKNOWN_HEADER pUserCurrentUnknownHeader;
PUCHAR pCurrentBufferPtr;
ULONG i;
ULONG BytesCopied;
ULONG HeaderCount = 0;
PHTTP_NETWORK_ADDRESS_IPV4 pLocalAddress;
PHTTP_NETWORK_ADDRESS_IPV4 pRemoteAddress;
PHTTP_TRANSPORT_ADDRESS pAddress;
PHTTP_COOKED_URL pCookedUrl;
PLIST_ENTRY pListEntry;
NTSTATUS Status;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
ASSERT(pKernelBuffer != NULL);
ASSERT(pUserBuffer != NULL);
ASSERT(BufferLength > sizeof(HTTP_REQUEST));
Status = STATUS_SUCCESS;
//
// Set up our pointers to the HTTP_REQUESTS structure, the
// header arrays we're going to fill in, and the pointer to
// where we're going to start filling them in.
//
// CODEWORK: Make this transport independent.
//
pHttpRequest = (PHTTP_REQUEST)pKernelBuffer;
pLocalAddress = (PHTTP_NETWORK_ADDRESS_IPV4)( pHttpRequest + 1 );
pRemoteAddress = pLocalAddress + 1;
pUserCurrentUnknownHeader = (PHTTP_UNKNOWN_HEADER)( pRemoteAddress + 1 );
pCurrentBufferPtr = (PUCHAR)(pUserCurrentUnknownHeader +
pRequest->UnknownHeaderCount);
//
// Now fill in the HTTP request structure.
//
ASSERT(!HTTP_IS_NULL_ID(&pRequest->ConnectionId));
ASSERT(!HTTP_IS_NULL_ID(&pRequest->RequestIdCopy));
pHttpRequest->ConnectionId = pRequest->ConnectionId;
pHttpRequest->RequestId = pRequest->RequestIdCopy;
pHttpRequest->UrlContext = pRequest->ConfigInfo.UrlContext;
pHttpRequest->Version = pRequest->Version;
pHttpRequest->Verb = pRequest->Verb;
pHttpRequest->Reason = pRequest->Reason;
pHttpRequest->BytesReceived = pRequest->BytesReceived;
pAddress = &pHttpRequest->Address;
pAddress->RemoteAddressLength = sizeof(HTTP_NETWORK_ADDRESS_IPV4);
pAddress->RemoteAddressType = HTTP_NETWORK_ADDRESS_TYPE_IPV4;
pAddress->pRemoteAddress = FIXUP_PTR(
PVOID,
pUserBuffer,
pKernelBuffer,
pRemoteAddress,
BufferLength
);
pRemoteAddress->IpAddress = pRequest->pHttpConn->pConnection->RemoteAddress;
pRemoteAddress->Port = pRequest->pHttpConn->pConnection->RemotePort;
pAddress->LocalAddressLength = sizeof(HTTP_NETWORK_ADDRESS_IPV4);
pAddress->LocalAddressType = HTTP_NETWORK_ADDRESS_TYPE_IPV4;
pAddress->pLocalAddress = FIXUP_PTR(
PVOID,
pUserBuffer,
pKernelBuffer,
pLocalAddress,
BufferLength
);
pLocalAddress->IpAddress = pRequest->pHttpConn->pConnection->LocalAddress;
pLocalAddress->Port = pRequest->pHttpConn->pConnection->LocalPort;
//
// and now the cooked url sections
//
//
// Unicode strings must be at 2-byte boundaries. All previous data
// are structures, so the assertion should be true.
//
ASSERT(((ULONG_PTR) pCurrentBufferPtr % sizeof(WCHAR)) == 0);
//
// make sure they are valid
//
ASSERT(pRequest->CookedUrl.pUrl != NULL);
ASSERT(pRequest->CookedUrl.pHost != NULL);
ASSERT(pRequest->CookedUrl.pAbsPath != NULL);
//
// do the full url
//
ASSERT(pRequest->CookedUrl.Length <= 0xffff);
pCookedUrl = &pHttpRequest->CookedUrl;
pCookedUrl->FullUrlLength = (USHORT)(pRequest->CookedUrl.Length);
pCookedUrl->pFullUrl = FIXUP_PTR(
PWSTR,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
//
// and the host
//
pCookedUrl->HostLength = DIFF(pRequest->CookedUrl.pAbsPath - pRequest->CookedUrl.pHost)
* sizeof(WCHAR);
pCookedUrl->pHost = pCookedUrl->pFullUrl +
DIFF(pRequest->CookedUrl.pHost - pRequest->CookedUrl.pUrl);
//
// is there a query string?
//
if (pRequest->CookedUrl.pQueryString != NULL)
{
pCookedUrl->AbsPathLength = DIFF(pRequest->CookedUrl.pQueryString -
pRequest->CookedUrl.pAbsPath) * sizeof(WCHAR);
pCookedUrl->pAbsPath = pCookedUrl->pHost +
(pCookedUrl->HostLength / sizeof(WCHAR));
pCookedUrl->QueryStringLength = (USHORT)(pRequest->CookedUrl.Length) - (
DIFF(
pRequest->CookedUrl.pQueryString -
pRequest->CookedUrl.pUrl
) * sizeof(WCHAR)
);
pCookedUrl->pQueryString = pCookedUrl->pAbsPath +
(pCookedUrl->AbsPathLength / sizeof(WCHAR));
}
else
{
pCookedUrl->AbsPathLength = (USHORT)(pRequest->CookedUrl.Length) - (
DIFF(
pRequest->CookedUrl.pAbsPath -
pRequest->CookedUrl.pUrl
) * sizeof(WCHAR)
);
pCookedUrl->pAbsPath = pCookedUrl->pHost +
(pCookedUrl->HostLength / sizeof(WCHAR));
pCookedUrl->QueryStringLength = 0;
pCookedUrl->pQueryString = NULL;
}
//
// copy the full url
//
RtlCopyMemory(
pCurrentBufferPtr,
pRequest->CookedUrl.pUrl,
pRequest->CookedUrl.Length
);
pCurrentBufferPtr += pRequest->CookedUrl.Length;
//
// terminate it
//
((PWSTR)pCurrentBufferPtr)[0] = UNICODE_NULL;
pCurrentBufferPtr += sizeof(WCHAR);
//
// any raw verb?
//
if (pRequest->Verb == HttpVerbUnknown)
{
//
// Need to copy in the raw verb for the client.
//
ASSERT(pRequest->RawVerbLength <= 0x7fff);
pHttpRequest->UnknownVerbLength = (USHORT)(pRequest->RawVerbLength * sizeof(CHAR));
pHttpRequest->pUnknownVerb = FIXUP_PTR(
PSTR,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
RtlCopyMemory(
pCurrentBufferPtr,
pRequest->pRawVerb,
pRequest->RawVerbLength
);
BytesCopied = pRequest->RawVerbLength * sizeof(CHAR);
pCurrentBufferPtr += BytesCopied;
//
// terminate it
//
((PSTR)pCurrentBufferPtr)[0] = ANSI_NULL;
pCurrentBufferPtr += sizeof(CHAR);
}
else
{
pHttpRequest->UnknownVerbLength = 0;
pHttpRequest->pUnknownVerb = NULL;
}
//
// copy the raw url
//
ASSERT(pRequest->RawUrl.Length <= 0x7fff);
pHttpRequest->RawUrlLength = (USHORT)(pRequest->RawUrl.Length * sizeof(CHAR));
pHttpRequest->pRawUrl = FIXUP_PTR(
PSTR,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
RtlCopyMemory(
pCurrentBufferPtr,
pRequest->RawUrl.pUrl,
pRequest->RawUrl.Length
);
BytesCopied = pRequest->RawUrl.Length * sizeof(CHAR);
pCurrentBufferPtr += BytesCopied;
//
// terminate it
//
((PSTR)pCurrentBufferPtr)[0] = ANSI_NULL;
pCurrentBufferPtr += sizeof(CHAR);
//
// no entity body, CODEWORK.
//
if (pRequest->ContentLength > 0 || pRequest->Chunked == 1)
{
pHttpRequest->MoreEntityBodyExists = 1;
}
else
{
pHttpRequest->MoreEntityBodyExists = 0;
}
pHttpRequest->EntityChunkCount = 0;
pHttpRequest->pEntityChunks = NULL;
//
// Copy in the known headers.
//
// Loop through the known header array in the HTTP connection,
// and copy any that we have.
//
RtlZeroMemory(
pHttpRequest->Headers.pKnownHeaders,
HttpHeaderRequestMaximum * sizeof(HTTP_KNOWN_HEADER)
);
for (i = 0; i < HttpHeaderRequestMaximum; i++)
{
HTTP_HEADER_ID HeaderId = (HTTP_HEADER_ID)pRequest->HeaderIndex[i];
if (HeaderId == HttpHeaderRequestMaximum)
{
break;
}
//
// Have a header here we need to copy in.
//
ASSERT(pRequest->HeaderValid[HeaderId]);
ASSERT(pRequest->Headers[HeaderId].HeaderLength <= 0x7fff);
//
// ok for HeaderLength to be 0 . we will give usermode a pointer
// pointing to a NULL string. RawValueLength will be 0.
//
pHttpRequest->Headers.pKnownHeaders[HeaderId].RawValueLength =
(USHORT)(pRequest->Headers[HeaderId].HeaderLength * sizeof(CHAR));
pHttpRequest->Headers.pKnownHeaders[HeaderId].pRawValue =
FIXUP_PTR(
PSTR,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
RtlCopyMemory(
pCurrentBufferPtr,
pRequest->Headers[HeaderId].pHeader,
pRequest->Headers[HeaderId].HeaderLength
);
BytesCopied = pRequest->Headers[HeaderId].HeaderLength * sizeof(CHAR);
pCurrentBufferPtr += BytesCopied;
//
// terminate it
//
((PSTR)pCurrentBufferPtr)[0] = ANSI_NULL;
pCurrentBufferPtr += sizeof(CHAR);
}
//
// Now loop through the unknown headers, and copy them in.
//
pHttpRequest->Headers.UnknownHeaderCount = pRequest->UnknownHeaderCount;
if (pRequest->UnknownHeaderCount == 0)
{
pHttpRequest->Headers.pUnknownHeaders = NULL;
}
else
{
pHttpRequest->Headers.pUnknownHeaders =
FIXUP_PTR(
PHTTP_UNKNOWN_HEADER,
pUserBuffer,
pKernelBuffer,
pUserCurrentUnknownHeader,
BufferLength
);
}
pListEntry = pRequest->UnknownHeaderList.Flink;
while (pListEntry != &pRequest->UnknownHeaderList)
{
PUL_HTTP_UNKNOWN_HEADER pUnknownHeader;
pUnknownHeader = CONTAINING_RECORD(
pListEntry,
UL_HTTP_UNKNOWN_HEADER,
List
);
pListEntry = pListEntry->Flink;
HeaderCount++;
ASSERT(HeaderCount <= pRequest->UnknownHeaderCount);
//
// First copy in the header name.
//
pUserCurrentUnknownHeader->NameLength =
(USHORT)pUnknownHeader->HeaderNameLength * sizeof(CHAR);
pUserCurrentUnknownHeader->pName =
FIXUP_PTR(
PSTR,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
RtlCopyMemory(
pCurrentBufferPtr,
pUnknownHeader->pHeaderName,
pUnknownHeader->HeaderNameLength
);
BytesCopied = pUnknownHeader->HeaderNameLength * sizeof(CHAR);
pCurrentBufferPtr += BytesCopied;
//
// terminate it
//
((PSTR)pCurrentBufferPtr)[0] = ANSI_NULL;
pCurrentBufferPtr += sizeof(CHAR);
//
// Now copy in the header value.
//
ASSERT(pUnknownHeader->HeaderValue.HeaderLength <= 0x7fff);
if (pUnknownHeader->HeaderValue.HeaderLength == 0)
{
pUserCurrentUnknownHeader->RawValueLength = 0;
pUserCurrentUnknownHeader->pRawValue = NULL;
}
else
{
pUserCurrentUnknownHeader->RawValueLength =
(USHORT)(pUnknownHeader->HeaderValue.HeaderLength * sizeof(CHAR));
pUserCurrentUnknownHeader->pRawValue =
FIXUP_PTR(
PSTR,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
RtlCopyMemory(
pCurrentBufferPtr,
pUnknownHeader->HeaderValue.pHeader,
pUnknownHeader->HeaderValue.HeaderLength
);
BytesCopied = pUnknownHeader->HeaderValue.HeaderLength * sizeof(CHAR);
pCurrentBufferPtr += BytesCopied;
//
// terminate it
//
((PSTR)pCurrentBufferPtr)[0] = ANSI_NULL;
pCurrentBufferPtr += sizeof(CHAR);
}
//
// skip to the next header
//
pUserCurrentUnknownHeader++;
}
//
// Copy raw connection ID.
//
pHttpRequest->RawConnectionId = pRequest->RawConnectionId;
//
// Copy in SSL information.
//
if (pRequest->pHttpConn->SecureConnection == FALSE)
{
pHttpRequest->pSslInfo = NULL;
}
else
{
pCurrentBufferPtr = (PUCHAR)ALIGN_UP_POINTER(pCurrentBufferPtr, PVOID);
Status = UlGetSslInfo(
&pRequest->pHttpConn->pConnection->FilterInfo,
BufferLength - DIFF(pCurrentBufferPtr - pKernelBuffer),
FIXUP_PTR(
PUCHAR,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
),
pCurrentBufferPtr,
&BytesCopied
);
if (NT_SUCCESS(Status) && BytesCopied)
{
pHttpRequest->pSslInfo = FIXUP_PTR(
PHTTP_SSL_INFO,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
pCurrentBufferPtr += BytesCopied;
}
else
{
pHttpRequest->pSslInfo = NULL;
}
}
//
// Make sure we didn't use too much
//
ASSERT(DIFF(pCurrentBufferPtr - pKernelBuffer) <= BufferLength);
TRACE_TIME(
pRequest->ConnectionId,
pRequest->RequestId,
TIME_ACTION_COPY_REQUEST
);
return Status;
} // UlpCopyRequestToBuffer
/******************************************************************************
Routine Description:
Find a pending IRP to deliver a request to. This routine must
be called with the lock on the apool held.
Arguments:
pAppPool - the apool to search for the irp
ppProcess - the process that we got the irp from
Return Value:
A pointer to an IRP if we've found one, or NULL if we didn't.
******************************************************************************/
PIRP
UlpPopNewIrp(
IN PUL_APP_POOL_OBJECT pAppPool,
OUT PUL_APP_POOL_PROCESS * ppProcess
)
{
PUL_APP_POOL_PROCESS pProcess;
PIRP pIrp = NULL;
PLIST_ENTRY pEntry;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
ASSERT(ppProcess != NULL);
//
// Start looking for a process with a free IRP. We tend to always go to
// the first one to try and prevent process thrashing.
//
pEntry = pAppPool->ProcessListHead.Flink;
while (pEntry != &(pAppPool->ProcessListHead))
{
pProcess = CONTAINING_RECORD(
pEntry,
UL_APP_POOL_PROCESS,
ListEntry
);
ASSERT(IS_VALID_AP_PROCESS(pProcess));
//
// get an IRP from this process
//
pIrp = UlpPopIrpFromProcess(pProcess);
//
// did we find one ?
//
if (pIrp != NULL)
{
*ppProcess = pProcess;
break;
}
//
// keep looking - move on to the next process entry
//
pEntry = pProcess->ListEntry.Flink;
}
return pIrp;
}
/***************************************************************************++
Routine Description:
Pulls an IRP off the given processes queue if there is one.
Arguments:
pProcess - a pointer to the process to search
Return Value:
A pointer to an IRP if we've found one, or NULL if we didn't.
--***************************************************************************/
PIRP
UlpPopIrpFromProcess(
IN PUL_APP_POOL_PROCESS pProcess
)
{
PUL_APP_POOL_OBJECT pProcessAppPool;
PLIST_ENTRY pEntry;
PIRP pIrp = NULL;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
pEntry = ExInterlockedRemoveHeadList(
&pProcess->NewIrpHead,
KSPIN_LOCK_FROM_UL_SPIN_LOCK(&pProcess->NewIrpSpinLock)
);
if (pEntry != NULL)
{
//
// Found a free irp !
//
pEntry->Blink = pEntry->Flink = NULL;
pIrp = CONTAINING_RECORD(pEntry, IRP, Tail.Overlay.ListEntry);
//
// pop the cancel routine
//
if (IoSetCancelRoutine(pIrp, NULL) == NULL)
{
//
// IoCancelIrp pop'd it first
//
// ok to just ignore this irp, it's been pop'd off the queue
// and will be completed in the cancel routine.
//
// keep looking for a irp to use
//
pIrp = NULL;
}
else if (pIrp->Cancel)
{
//
// we pop'd it first. but the irp is being cancelled
// and our cancel routine will never run. lets be
// nice and complete the irp now (vs. using it
// then completing it - which would also be legal).
//
pProcessAppPool = (PUL_APP_POOL_OBJECT)(
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer
);
ASSERT(pProcessAppPool == pProcess->pAppPool);
DEREFERENCE_APP_POOL(pProcessAppPool);
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
UlCompleteRequest(pIrp, g_UlPriorityBoost);
pIrp = NULL;
}
else
{
//
// we are free to use this irp !
//
pProcessAppPool = (PUL_APP_POOL_OBJECT)(
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer
);
ASSERT(pProcessAppPool == pProcess->pAppPool);
DEREFERENCE_APP_POOL(pProcessAppPool);
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
}
}
return pIrp;
}
/***************************************************************************++
Routine Description:
Loops through an app pool's list of processes, looking for the specified
process.
Arguments:
pProcess - the process to search for
pAppPool - the app pool to search
Return Values:
TRUE if the process was found, FALSE otherwise
--***************************************************************************/
BOOLEAN
UlpIsProcessInAppPool(
IN PUL_APP_POOL_PROCESS pProcess,
IN PUL_APP_POOL_OBJECT pAppPool
)
{
BOOLEAN Found = FALSE;
PLIST_ENTRY pEntry;
PUL_APP_POOL_PROCESS pCurrentProc;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
//
// only look if process isn't NULL.
//
if (pProcess != NULL) {
//
// Start looking for the process.
//
pEntry = pAppPool->ProcessListHead.Flink;
while (pEntry != &(pAppPool->ProcessListHead))
{
pCurrentProc = CONTAINING_RECORD(
pEntry,
UL_APP_POOL_PROCESS,
ListEntry
);
ASSERT(IS_VALID_AP_PROCESS(pCurrentProc));
//
// did we find it?
//
if (pCurrentProc == pProcess) {
Found = TRUE;
break;
}
//
// keep looking - move on to the next process entry
//
pEntry = pCurrentProc->ListEntry.Flink;
}
if (!Found) {
//
// process must have gone away.
//
UlTrace(ROUTING, (
"ul!UlpIsProcessInAppPool(\n"
" pProcess = %p\n"
" pAppPool = %p (%S) )\n"
" returning FALSE\n",
pProcess,
pAppPool,
pAppPool->pName
));
}
}
return Found;
}
/***************************************************************************++
Routine Description:
Adds a request to the unbound queue. These requests can be routed to
any process in the app pool.
Arguments:
pAppPool - the pool which is getting the request
pRequest - the request to queue
--***************************************************************************/
NTSTATUS
UlpQueueUnboundRequest(
IN PUL_APP_POOL_OBJECT pAppPool,
IN PUL_INTERNAL_REQUEST pRequest
)
{
NTSTATUS Status;
KLOCK_QUEUE_HANDLE LockHandle;
//
// Sanity check
//
PAGED_CODE();
ASSERT( IS_VALID_AP_OBJECT(pAppPool) );
ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
UlAcquireInStackQueuedSpinLock(&pAppPool->QueueSpinLock, &LockHandle);
//
// add it to the queue
//
Status = UlpQueueRequest(&pAppPool->NewRequestQueue, pRequest);
//
// if it's in, change the queue state
//
if (NT_SUCCESS(Status)) {
pRequest->AppPool.QueueState = QueueDeliveredState;
} else {
//
// the queue is too full, return an error to the client
//
UlTrace(ROUTING, (
"ul!UlpQueueUnboundRequest(pAppPool = %p, pRequest = %p)\n"
" Rejecting request. AppPool Queue is full (%d items)\n",
pAppPool,
pRequest,
UlpQueryQueueLength(&pAppPool->NewRequestQueue)
));
pRequest->ErrorCode = UlErrorUnavailable; // 503
}
UlReleaseInStackQueuedSpinLock(&pAppPool->QueueSpinLock, &LockHandle);
return Status;
} // UlpQueueUnboundRequest
/***************************************************************************++
Routine Description:
Searches request queues for a request available to the specified process.
If a request is found, it is removed from the queue and returned.
Arguments:
pProcess - the process that will get the request
Return Values:
Pointer to an HTTP_REQUEST if one is found.
NULL otherwise.
--***************************************************************************/
PUL_INTERNAL_REQUEST
UlpDequeueNewRequest(
IN PUL_APP_POOL_PROCESS pProcess
)
{
PLIST_ENTRY pEntry;
PUL_INTERNAL_REQUEST pRequest = NULL;
PUL_APP_POOL_OBJECT pAppPool;
KLOCK_QUEUE_HANDLE LockHandle;
//
// Sanity check
//
PAGED_CODE();
ASSERT( IS_VALID_AP_PROCESS(pProcess) );
pAppPool = pProcess->pAppPool;
ASSERT( IS_VALID_AP_OBJECT(pAppPool) );
UlAcquireInStackQueuedSpinLock(&pAppPool->QueueSpinLock, &LockHandle);
//
// find a usable request
//
pEntry = pAppPool->NewRequestQueue.RequestHead.Flink;
while (pEntry != &pAppPool->NewRequestQueue.RequestHead) {
PUL_APP_POOL_PROCESS pProcBinding;
pRequest = CONTAINING_RECORD(
pEntry,
UL_INTERNAL_REQUEST,
AppPool.AppPoolEntry
);
ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
if (pAppPool->NumberActiveProcesses <= 1) {
//
// done if there is only one active process
//
break;
}
//
// check the binding
//
pProcBinding = UlQueryProcessBinding(
pRequest->pHttpConn,
pAppPool
);
if (pProcBinding == pProcess) {
//
// found a request bound to the correct process
//
break;
} else if (pProcBinding == NULL) {
//
// found an unbound request
//
//
// bind unbound request to this process
// Note: we're ignoring the return val
// of UlBindConnectionToProcess because
// it's probably not a fatal error..
//
UlBindConnectionToProcess(
pRequest->pHttpConn,
pAppPool,
pProcess
);
break;
}
//
// try the next one
//
pEntry = pEntry->Flink;
}
//
// if we found something, remove it from the NewRequestQueue
// and pend it onto the PendingRequestQuueue
//
if (pRequest)
{
UlpRemoveRequest(&pAppPool->NewRequestQueue, pRequest);
//
// attach the request to this process. this allows us to drop the
// connection if the process dies in the middle of request
// processing
//
pRequest->AppPool.pProcess = pProcess;
pRequest->AppPool.QueueState = QueueCopiedState;
//
// add a reference to the request so as to allow unlink from
// process to happen once we let go of the lock
//
UL_REFERENCE_INTERNAL_REQUEST(pRequest);
UlpQueueRequest(
&pProcess->PendingRequestQueue,
pRequest
);
}
UlReleaseInStackQueuedSpinLock(&pAppPool->QueueSpinLock, &LockHandle);
return pRequest;
} // UlpDequeueNewRequest
/***************************************************************************++
Routine Description:
Takes all the queued requests bound to the given process and makes them
available to all processes.
Arguments:
pProcess - the process whose requests are to be redistributed
--***************************************************************************/
VOID
UlpUnbindQueuedRequests(
IN PUL_APP_POOL_PROCESS pProcess
)
{
PLIST_ENTRY pEntry;
PUL_INTERNAL_REQUEST pRequest = NULL;
PUL_APP_POOL_OBJECT pAppPool;
KLOCK_QUEUE_HANDLE LockHandle;
//
// Sanity check
//
PAGED_CODE();
ASSERT( IS_VALID_AP_PROCESS(pProcess) );
pAppPool = pProcess->pAppPool;
ASSERT( IS_VALID_AP_OBJECT(pAppPool) );
UlAcquireInStackQueuedSpinLock(&pAppPool->QueueSpinLock, &LockHandle);
//
// find a bound request
//
pEntry = pAppPool->NewRequestQueue.RequestHead.Flink;
while (pEntry != &pAppPool->NewRequestQueue.RequestHead) {
PUL_APP_POOL_PROCESS pProcBinding;
pRequest = CONTAINING_RECORD(
pEntry,
UL_INTERNAL_REQUEST,
AppPool.AppPoolEntry
);
ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
//
// remember the next one
//
pEntry = pEntry->Flink;
//
// check the binding
//
if (pAppPool->NumberActiveProcesses <= 1) {
pProcBinding = pProcess;
} else {
pProcBinding = UlQueryProcessBinding(
pRequest->pHttpConn,
pAppPool
);
}
if (pProcBinding == pProcess) {
//
// remove from the list
//
UlpRemoveRequest(&pAppPool->NewRequestQueue, pRequest);
//
// mark it as unrouted
//
pRequest->AppPool.QueueState = QueueUnroutedState;
UlTrace(ROUTING, (
"STICKY KILL cid %I64x to proc %p\n",
pRequest->ConnectionId,
pProcess
));
//
// kill the binding
//
UlBindConnectionToProcess(
pRequest->pHttpConn,
pProcess->pAppPool,
NULL
);
//
// there may be an IRP for this newly unbound
// request, so redeliver the request outside
// the locks we're holding.
//
UL_QUEUE_WORK_ITEM(
&pRequest->WorkItem,
&UlpRedeliverRequestWorker
);
}
}
UlReleaseInStackQueuedSpinLock(&pAppPool->QueueSpinLock, &LockHandle);
}
/***************************************************************************++
Routine Description:
Delivers the given request to an App Pool. UlpUnbindQueuedRequests
uses this routine to call into UlDeliverRequestToProcess outside
of any locks.
Arguments:
pWorkItem - embedded in the request to deliver
--***************************************************************************/
VOID
UlpRedeliverRequestWorker(
IN PUL_WORK_ITEM pWorkItem
)
{
NTSTATUS Status;
PUL_INTERNAL_REQUEST pRequest;
pRequest = CONTAINING_RECORD(
pWorkItem,
UL_INTERNAL_REQUEST,
WorkItem
);
//
// Sanity check
//
PAGED_CODE();
ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
ASSERT( IS_VALID_URL_CONFIG_GROUP_INFO(&pRequest->ConfigInfo) );
ASSERT( pRequest->ConfigInfo.pAppPool );
Status = UlDeliverRequestToProcess(
pRequest->ConfigInfo.pAppPool,
pRequest
);
//
// remove the extra reference added in UlpUnbindQueuedRequests
//
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
CHECK_STATUS(Status);
}
/***************************************************************************++
Routine Description:
Checks the request queue to see if there are any requests available
to the specified process.
Arguments:
pProcess - the process doing the check
Return Values:
TRUE if there are no requests available, FALSE if there are requests
--***************************************************************************/
BOOLEAN
UlpIsRequestQueueEmpty(
IN PUL_APP_POOL_PROCESS pProcess
)
{
PUL_APP_POOL_OBJECT pAppPool;
//
// Sanity check
//
PAGED_CODE();
ASSERT( IS_VALID_AP_PROCESS(pProcess) );
pAppPool = pProcess->pAppPool;
ASSERT( IS_VALID_AP_OBJECT(pAppPool) );
return (UlpQueryQueueLength(&pAppPool->NewRequestQueue) == 0);
}
/***************************************************************************++
Routine Description:
Changes the maximum length of the incoming request queue on the app pool.
Arguments:
pProcess - App pool process object
QueueLength - the new max length of the queue
--***************************************************************************/
NTSTATUS
UlpSetAppPoolQueueLength(
IN PUL_APP_POOL_PROCESS pProcess,
IN LONG QueueLength
)
{
PUL_APP_POOL_OBJECT pAppPool;
PLONG pQueueLength;
NTSTATUS Status;
pAppPool = pProcess->pAppPool;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
//
// set the new value
//
UlAcquireResourceExclusive(&pAppPool->pResource->Resource, TRUE);
Status = UlpSetMaxQueueLength(&pAppPool->NewRequestQueue, QueueLength);
UlTrace(ROUTING, (
"ul!UlpSetAppPoolQueueLength(pProcess = %p, QueueLength = %d)\n"
" pAppPool = %p (%ws), Status = 0x%08x\n",
pProcess,
QueueLength,
pAppPool,
pAppPool->pName,
Status
));
UlReleaseResource(&pAppPool->pResource->Resource);
RETURN(Status);
}
/***************************************************************************++
Routine Description:
Gets the maximum length of the incoming request queue on the app pool.
Arguments:
pProcess - App pool process object
return value - max length of the queue
--***************************************************************************/
LONG
UlpGetAppPoolQueueLength(
IN PUL_APP_POOL_PROCESS pProcess
)
{
PUL_APP_POOL_OBJECT pAppPool;
NTSTATUS Status;
LONG QueueLength;
pAppPool = pProcess->pAppPool;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
//
// Get the max length
//
UlAcquireResourceExclusive(&pAppPool->pResource->Resource, TRUE);
QueueLength = pAppPool->NewRequestQueue.MaxRequests;
UlReleaseResource(&pAppPool->pResource->Resource);
return QueueLength;
}
/******************************************************************************
Routine Description:
this copies a request into a free irp.
if the request is too large, it queues to request onto the process and
completes the irp, so that the process can come back later with a larger
buffer.
Arguments:
pRequest - the request to copy
pProcess - the process that owns pIrp
pIrp - the irp to copy pRequest to
Return Value:
VOID - it always works.
******************************************************************************/
VOID
UlpCopyRequestToIrp(
PUL_INTERNAL_REQUEST pRequest,
PIRP pIrp
)
{
NTSTATUS Status;
PIO_STACK_LOCATION pIrpSp;
ULONG BytesNeeded;
PUCHAR pKernelBuffer;
PVOID pUserBuffer;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
ASSERT(pIrp != NULL);
__try
{
//
// Make sure this is big enough to handle the request, and
// if so copy it in.
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
//
// Calculate the size needed for the request, we'll need it below.
//
Status = UlpComputeRequestBytesNeeded(pRequest, &BytesNeeded);
if (!NT_SUCCESS(Status))
{
goto complete;
}
//
// Make sure we've got enough space to handle the whole request.
//
if (BytesNeeded <= pIrpSp->Parameters.DeviceIoControl.OutputBufferLength)
{
//
// get the addresses for the buffer
//
pKernelBuffer = (PUCHAR) MmGetSystemAddressForMdlSafe(
pIrp->MdlAddress,
NormalPagePriority
);
if (pKernelBuffer == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto complete;
}
//
// Make sure we are properly aligned.
//
if (((ULONG_PTR) pKernelBuffer) & (TYPE_ALIGNMENT(HTTP_REQUEST) - 1))
{
UlTrace(ROUTING, (
"UlpCopyRequestToIrp: pKernelBuffer = %p, Alignment = %p\n"
" ((ULONG_PTR) pKernelBuffer) & (TYPE_ALIGNMENT(HTTP_REQUEST) - 1) = %p\n",
pKernelBuffer,
TYPE_ALIGNMENT(HTTP_REQUEST),
((ULONG_PTR) pKernelBuffer) & (TYPE_ALIGNMENT(HTTP_REQUEST) - 1)
));
Status = STATUS_DATATYPE_MISALIGNMENT_ERROR;
goto complete;
}
pUserBuffer = MmGetMdlVirtualAddress( pIrp->MdlAddress );
ASSERT( pUserBuffer != NULL );
//
// This request will fit in this buffer, so copy it.
//
Status = UlpCopyRequestToBuffer(
pRequest,
pKernelBuffer,
pUserBuffer,
pIrpSp->Parameters.DeviceIoControl.OutputBufferLength,
NULL,
0
);
if (NT_SUCCESS(Status))
{
pIrp->IoStatus.Information = BytesNeeded;
}
else
{
goto complete;
}
}
else
{
//
// the user buffer is too small
//
Status = STATUS_BUFFER_OVERFLOW;
//
// is it big enough to hold the basic structure?
//
if (pIrpSp->Parameters.DeviceIoControl.OutputBufferLength >=
sizeof(HTTP_REQUEST))
{
PHTTP_REQUEST pUserRequest;
pUserRequest = (PHTTP_REQUEST)MmGetSystemAddressForMdlSafe(
pIrp->MdlAddress,
NormalPagePriority
);
if (pUserRequest == NULL)
{
Status = STATUS_INSUFFICIENT_RESOURCES;
goto complete;
}
//
// Copy the request ID into the output buffer. Copy it from the
// private copy that request holds. Original opaque id may get
// nulled if connection cleanup happens before we get here.
//
ASSERT(!HTTP_IS_NULL_ID(&pRequest->RequestIdCopy));
pUserRequest->RequestId = pRequest->RequestIdCopy;
pUserRequest->ConnectionId = pRequest->ConnectionId;
//
// and tell how much we actually need
//
pIrp->IoStatus.Information = BytesNeeded;
}
else
{
//
// Very bad, we can never get here as we check the length in ioctl.cxx
//
ASSERT(FALSE);
pIrp->IoStatus.Information = 0;
}
}
}
__except( UL_EXCEPTION_FILTER() )
{
Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
}
//
// complete the irp
//
complete:
UlTrace(ROUTING, (
"ul!UlpCopyRequestToIrp(\n"
" pRequest = %p,\n"
" pIrp = %p) Completing Irp\n"
" pAppPool = %p (%S)\n"
" pRequest->ConnectionId = %I64x\n"
" pIrpSp->Parameters.DeviceIoControl.OutputBufferLength = %d\n"
" pIrp->IoStatus.Status = 0x%x\n"
" pIrp->IoStatus.Information = %d\n",
pRequest,
pIrp,
pRequest->ConfigInfo.pAppPool,
pRequest->ConfigInfo.pAppPool->pName,
pRequest->ConnectionId,
pIrpSp->Parameters.DeviceIoControl.OutputBufferLength,
Status,
pIrp->IoStatus.Information
));
pIrp->IoStatus.Status = Status;
UlCompleteRequest(pIrp, g_UlPriorityBoost);
//
// success. we completed the irp
//
} // UlpCopyRequestToIrp
/******************************************************************************
Routine Description:
this will return the apool object reference by this handle, bumping the
refcount on the apool.
this is called by UlSetConfigGroupInformation when user mode wants to
associate an app pool to the config group by handle.
the config group keeps a pointer to the apool.
Arguments:
AppPool - the handle of the apool
ppAppPool - returns the apool object the handle represented.
Return Value:
NTSTATUS
******************************************************************************/
NTSTATUS
UlGetPoolFromHandle(
IN HANDLE AppPool,
OUT PUL_APP_POOL_OBJECT * ppAppPool
)
{
NTSTATUS Status;
PFILE_OBJECT pFileObject = NULL;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(ppAppPool != NULL);
Status = ObReferenceObjectByHandle(
AppPool,
0, // DesiredAccess
*IoFileObjectType, // ObjectType
KernelMode, // AccessMode
(void**)&pFileObject, // Object
NULL // HandleInformation
);
if (NT_SUCCESS(Status) == FALSE)
{
goto end;
}
if (IS_APP_POOL(pFileObject) == FALSE ||
IS_VALID_AP_PROCESS(GET_APP_POOL_PROCESS(pFileObject)) == FALSE)
{
Status = STATUS_INVALID_HANDLE;
goto end;
}
*ppAppPool = GET_APP_POOL_PROCESS(pFileObject)->pAppPool;
ASSERT(IS_VALID_AP_OBJECT(*ppAppPool));
REFERENCE_APP_POOL(*ppAppPool);
end:
if (pFileObject != NULL)
{
ObDereferenceObject( pFileObject );
}
return Status;
}
/******************************************************************************
Routine Description:
this routine is called to associate a HTTP_REQUEST with an apool
process.
this is basically always done (used to be for 2 [now 3] reasons):
1) the process called ReceiveEntityBody and pended an IRP to the
request. if the process detaches from the apool (CloseHandle,
ExitProcess) UlDetachProcessFromAppPool will walk the request queue
and cancel all i/o.
2) the request did not fit into a waiting irp, so the request is queued
for a larger irp to come down and fetch it.
3) the response has not been fully sent for the request. the request
is linked with the process so that the connection can be aborted
if the process aborts.
Arguments:
pProcess - the process to associate the request with.
pRequest - the request.
Return Value:
VOID - it always works.
******************************************************************************/
VOID
UlpQueuePendingRequest(
IN PUL_APP_POOL_PROCESS pProcess,
IN PUL_INTERNAL_REQUEST pRequest
)
{
NTSTATUS Status;
KLOCK_QUEUE_HANDLE LockHandle;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
UlTrace(ROUTING, (
"ul!UlpQueuePendingRequest(pRequest = %p, pProcess = %p)\n"
" pAppPool = %p (%S)\n",
pRequest,
pProcess,
pProcess->pAppPool,
pProcess->pAppPool->pName
));
UlAcquireInStackQueuedSpinLock(
&pProcess->pAppPool->QueueSpinLock,
&LockHandle
);
//
// save a pointer to the process in the object so we can confirm
// that it's on our list.
//
pRequest->AppPool.pProcess = pProcess;
pRequest->AppPool.QueueState = QueueCopiedState;
//
// put it on the list
//
ASSERT(pRequest->AppPool.AppPoolEntry.Flink == NULL);
Status = UlpQueueRequest(
&pProcess->PendingRequestQueue,
pRequest
);
// queue is unlimited
ASSERT(NT_SUCCESS(Status));
UlReleaseInStackQueuedSpinLock(
&pProcess->pAppPool->QueueSpinLock,
&LockHandle
);
} // UlpQueuePendingRequest
//
// functions to manipulate a UL_REQUEST_QUEUE
//
/***************************************************************************++
Routine Description:
Initializes a UL_REQUEST_QUEUE object.
Arguments:
pQueue - The queue object to initialize
MaxRequests - The maximum allowed length of the queue
--***************************************************************************/
NTSTATUS
UlpInitRequestQueue(
PUL_REQUEST_QUEUE pQueue,
LONG MaxRequests
)
{
ASSERT(pQueue);
if ((MaxRequests < 0) && (MaxRequests != -1)) {
return STATUS_INVALID_PARAMETER;
}
pQueue->RequestCount = 0;
pQueue->MaxRequests = MaxRequests;
InitializeListHead(&pQueue->RequestHead);
return STATUS_SUCCESS;
}
/***************************************************************************++
Routine Description:
Changes the maximum length of a queue.
Arguments:
pQueue - The queue object to change
MaxRequests - The maximum allowed length of the queue
--***************************************************************************/
NTSTATUS
UlpSetMaxQueueLength(
PUL_REQUEST_QUEUE pQueue,
LONG MaxRequests
)
{
ASSERT(pQueue);
if ((MaxRequests < 0) && (MaxRequests != -1)) {
return STATUS_INVALID_PARAMETER;
}
pQueue->MaxRequests = MaxRequests;
return STATUS_SUCCESS;
}
/***************************************************************************++
Routine Description:
Queries the current length of a queue.
Arguments:
pQueue - The queue object to query
Return values:
LONG - the current length of the queue
--***************************************************************************/
LONG
UlpQueryQueueLength(
PUL_REQUEST_QUEUE pQueue
)
{
ASSERT(pQueue);
return pQueue->RequestCount;
}
/***************************************************************************++
Routine Description:
Adds a request to the tail of the queue.
Arguments:
pQueue - The queue object
pRequest - The request to be added
--***************************************************************************/
NTSTATUS
UlpQueueRequest(
PUL_REQUEST_QUEUE pQueue,
PUL_INTERNAL_REQUEST pRequest
)
{
ASSERT(pQueue);
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
if ((pQueue->RequestCount < pQueue->MaxRequests) ||
(pQueue->MaxRequests == -1))
{
//
// add to the end of the queue
//
InsertTailList(&pQueue->RequestHead, &pRequest->AppPool.AppPoolEntry);
pQueue->RequestCount++;
ASSERT(pQueue->RequestCount >= 1);
return STATUS_SUCCESS;
} else {
//
// the queue is too full, return an error to the client
//
return STATUS_DEVICE_BUSY;
}
}
/***************************************************************************++
Routine Description:
Removes a particular request from the queue.
Arguments:
pQueue - The queue object
pRequest - The request to be removed
--***************************************************************************/
VOID
UlpRemoveRequest(
PUL_REQUEST_QUEUE pQueue,
PUL_INTERNAL_REQUEST pRequest
)
{
ASSERT(pQueue);
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
ASSERT(NULL != pRequest->AppPool.AppPoolEntry.Flink);
RemoveEntryList(&pRequest->AppPool.AppPoolEntry);
pRequest->AppPool.AppPoolEntry.Flink =
pRequest->AppPool.AppPoolEntry.Blink = NULL;
pQueue->RequestCount--;
ASSERT(pQueue->RequestCount >= 0);
}
/***************************************************************************++
Routine Description:
Removes a request from the head of a queue if there is one.
App Pool lock must be held.
Arguments:
pQueue - The queue object
Return values:
Pointer to the request or NULL if the queue is empty.
--***************************************************************************/
PUL_INTERNAL_REQUEST
UlpDequeueRequest(
PUL_REQUEST_QUEUE pQueue
)
{
PLIST_ENTRY pEntry;
PUL_INTERNAL_REQUEST pRequest = NULL;
ASSERT(pQueue);
if (pQueue->RequestCount)
{
pEntry = RemoveHeadList(&pQueue->RequestHead);
ASSERT(pEntry != NULL);
pEntry->Flink = pEntry->Blink = NULL;
pQueue->RequestCount--;
pRequest = CONTAINING_RECORD(
pEntry,
UL_INTERNAL_REQUEST,
AppPool.AppPoolEntry
);
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
pRequest->AppPool.QueueState = QueueUnlinkedState;
}
else
ASSERT(IsListEmpty(&pQueue->RequestHead));
return pRequest;
}
/***************************************************************************++
Routine Description:
A little utility function to get the app pool pointer out of the
opaque app pool process object.
Arguments:
pProcess - the process object
--***************************************************************************/
PUL_APP_POOL_OBJECT
UlAppPoolObjectFromProcess(
PUL_APP_POOL_PROCESS pProcess
)
{
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
ASSERT(IS_VALID_AP_OBJECT(pProcess->pAppPool));
return pProcess->pAppPool;
}
/***************************************************************************++
Routine Description:
Adds a config group to the list of transient registrations associated
with this app pool.
Arguments:
pConfigGroup - the transient group
pAppPool - the app pool with the registration
--***************************************************************************/
VOID
UlLinkConfigGroupToAppPool(
IN PUL_CONFIG_GROUP_OBJECT pConfigGroup,
IN PUL_APP_POOL_OBJECT pAppPool
)
{
//
// Sanity check
//
PAGED_CODE();
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
ASSERT(pConfigGroup->AppPoolFlags.Present);
ASSERT(pConfigGroup->pAppPool == pAppPool);
UlAddNotifyEntry(
&pAppPool->TransientHead,
&pConfigGroup->HandleEntry
);
}
/***************************************************************************++
Routine Description:
Determines if the specified connection has been disconnected. If so,
the IRP is completed immediately, otherwise the IRP is pended.
Arguments:
pProcess - the app pool process object with which the irp is associated.
pHttpConn - Supplies the connection to wait for.
N.B. Since this connection was retrieved via a opaque ID, it has
an outstanding reference for this request on the assumption the
IRP will pend. If this routine does not pend the IRP, the reference
must be removed.
pIrp - Supplies the IRP to either complete or pend.
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlWaitForDisconnect(
IN PUL_APP_POOL_PROCESS pProcess,
IN PUL_HTTP_CONNECTION pHttpConn,
IN PIRP pIrp
)
{
PDRIVER_CANCEL pCancelRoutine;
NTSTATUS status;
PIO_STACK_LOCATION pIrpSp;
PUL_DISCONNECT_OBJECT pDisconnectObj;
//
// Allocate an object to associate the IRP with the connection
// and the app pool
//
pDisconnectObj = UlpCreateDisconnectObject(pIrp);
if (!pDisconnectObj) {
UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn);
return STATUS_NO_MEMORY;
}
//
// Acquire the lock protecting the disconnect data and determine
// if we should queue the IRP or complete it immediately.
//
UlAcquireResourceExclusive( &g_pUlNonpagedData->DisconnectResource, TRUE );
if (pHttpConn->DisconnectFlag)
{
//
// Connection already disconnected, complete the IRP immediately.
//
UlReleaseResource( &g_pUlNonpagedData->DisconnectResource );
UL_DEREFERENCE_HTTP_CONNECTION(pHttpConn);
UlpFreeDisconnectObject(pDisconnectObj);
IoMarkIrpPending(pIrp);
pIrp->IoStatus.Status = STATUS_SUCCESS;
UlCompleteRequest( pIrp, g_UlPriorityBoost );
return STATUS_PENDING;
}
//
// Save a pointer to the disconnect object in the IRP so we
// can find it inside our cancel routine.
//
pIrpSp = IoGetCurrentIrpStackLocation( pIrp );
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pDisconnectObj;
//
// Make the IRP cancellable.
//
IoMarkIrpPending( pIrp );
IoSetCancelRoutine( pIrp, &UlpCancelWaitForDisconnect );
if (pIrp->Cancel)
{
//
// The IRP has either already been cancelled IRP is in the
// process of being cancelled.
//
pCancelRoutine = IoSetCancelRoutine( pIrp, NULL );
if (pCancelRoutine == NULL)
{
//
// The previous cancel routine was already NULL, meaning that
// it has either already run or will run Real Soon Now, so
// we can just ignore it. Returning STATUS_PENDING causes
// the IOCTL wrapper to not attempt to complete the IRP.
//
status = STATUS_PENDING;
goto dont_queue;
}
else
{
//
// We have to cancel it ourselves, so we'll just complete
// the IRP immediately with STATUS_CANCELLED.
//
status = STATUS_CANCELLED;
goto dont_queue;
}
}
//
// The IRP has not been cancelled yet. Queue it on the connection
// and return with the connection still referenced. The reference
// is removed when the IRP is dequeued & completed or cancelled.
//
// Also queue it on the app pool process in case the pool handle
// gets closed before the connection does.
//
UlAddNotifyEntry(
&pHttpConn->WaitForDisconnectHead,
&pDisconnectObj->ConnectionEntry
);
UlAddNotifyEntry(
&pProcess->WaitForDisconnectHead,
&pDisconnectObj->ProcessEntry
);
UlReleaseResource( &g_pUlNonpagedData->DisconnectResource );
//
// now that we're on the lists we don't need the reference to
// the HTTP_CONNECTION.
//
UL_DEREFERENCE_HTTP_CONNECTION( pHttpConn );
return STATUS_PENDING;
dont_queue:
UL_DEREFERENCE_HTTP_CONNECTION( pHttpConn );
UlUnmarkIrpPending( pIrp );
UlReleaseResource( &g_pUlNonpagedData->DisconnectResource );
UlpFreeDisconnectObject(pDisconnectObj);
return status;
} // UlWaitForDisconnect
/***************************************************************************++
Routine Description:
Cancels the pending "wait for disconnect" IRP.
Arguments:
pDeviceObject - Supplies the device object for the request.
pIrp - Supplies the IRP to cancel.
--***************************************************************************/
VOID
UlpCancelWaitForDisconnect(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
{
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
ASSERT(pIrp != NULL);
//
// release the cancel spinlock. This means the cancel routine
// must be the one completing the irp (to avoid the race of
// completion + reuse prior to the cancel routine running).
//
IoReleaseCancelSpinLock(pIrp->CancelIrql);
//
// queue the cancel to a worker to ensure passive irql.
//
UL_CALL_PASSIVE(
UL_WORK_ITEM_FROM_IRP( pIrp ),
&UlpCancelWaitForDisconnectWorker
);
}
/***************************************************************************++
Routine Description:
Actually performs the cancel for the irp.
Arguments:
pWorkItem - the work item to process.
--***************************************************************************/
VOID
UlpCancelWaitForDisconnectWorker(
IN PUL_WORK_ITEM pWorkItem
)
{
PIRP pIrp;
PIO_STACK_LOCATION pIrpSp;
PUL_DISCONNECT_OBJECT pDisconnectObj;
//
// Sanity check.
//
PAGED_CODE();
//
// grab the irp off the work item
//
pIrp = UL_WORK_ITEM_TO_IRP( pWorkItem );
ASSERT(IS_VALID_IRP(pIrp));
//
// grab the disconnect object off the irp
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pDisconnectObj = (PUL_DISCONNECT_OBJECT)(
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer
);
ASSERT(IS_VALID_DISCONNECT_OBJECT(pDisconnectObj));
//
// Acquire the lock protecting the disconnect data, and remove the
// IRP if necessary.
//
UlAcquireResourceExclusive( &g_pUlNonpagedData->DisconnectResource, TRUE );
UlRemoveNotifyEntry(&pDisconnectObj->ConnectionEntry);
UlRemoveNotifyEntry(&pDisconnectObj->ProcessEntry);
UlReleaseResource( &g_pUlNonpagedData->DisconnectResource );
//
// Free the disconnect object and complete the IRP.
//
UlpFreeDisconnectObject(pDisconnectObj);
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
UlCompleteRequest( pIrp, g_UlPriorityBoost );
} // UlpCancelWaitForDisconnectWorker
/***************************************************************************++
Routine Description:
Completes all WaitForDisconnect IRPs attached to an http connection
has been disconnected.
Arguments:
pHttpConnection - the connection that's disconnected
--***************************************************************************/
VOID
UlCompleteAllWaitForDisconnect(
IN PUL_HTTP_CONNECTION pHttpConnection
)
{
PLIST_ENTRY pListEntry;
PIRP pIrp;
NTSTATUS Success = STATUS_SUCCESS;
//
// Sanity check
//
PAGED_CODE();
ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConnection));
UlAcquireResourceExclusive( &g_pUlNonpagedData->DisconnectResource, TRUE);
//
// prevent new IRPs from getting attached to the connection,
// by setting the DisconnectFlag.
//
ASSERT(!pHttpConnection->DisconnectFlag);
pHttpConnection->DisconnectFlag = TRUE;
//
// Complete any pending "wait for disconnect" IRPs.
//
UlNotifyAllEntries(
&UlpNotifyCompleteWaitForDisconnect,
&pHttpConnection->WaitForDisconnectHead,
&Success
);
UlReleaseResource( &g_pUlNonpagedData->DisconnectResource );
}
/***************************************************************************++
Routine Description:
Removes a UL_DISCONNECT_OBJECT from its lists and completes the IRP.
Arguments:
pEntry - the notify list entry
pHost - the UL_DISCONNECT_OBJECT
pv - pointer to an NTSTATUS to be returned
--***************************************************************************/
BOOLEAN
UlpNotifyCompleteWaitForDisconnect(
IN PUL_NOTIFY_ENTRY pEntry,
IN PVOID pHost,
IN PVOID pv
)
{
PUL_DISCONNECT_OBJECT pDisconnectObj;
PIRP pIrp;
PDRIVER_CANCEL pCancelRoutine;
PNTSTATUS pStatus;
//
// Sanity check
//
PAGED_CODE();
ASSERT(pEntry);
ASSERT(pHost);
ASSERT(pv);
pStatus = (PNTSTATUS) pv;
pDisconnectObj = (PUL_DISCONNECT_OBJECT) pHost;
ASSERT(IS_VALID_DISCONNECT_OBJECT(pDisconnectObj));
//
// remove object from lists
//
UlRemoveNotifyEntry(&pDisconnectObj->ConnectionEntry);
UlRemoveNotifyEntry(&pDisconnectObj->ProcessEntry);
//
// complete the IRP
//
pIrp = pDisconnectObj->pIrp;
//
// We'll be completing the IRP real soon, so make it
// non-cancellable.
//
pCancelRoutine = IoSetCancelRoutine( pIrp, NULL );
if (pCancelRoutine == NULL)
{
//
// The cancel routine is already NULL, meaning that the
// cancel routine will run Real Soon Now, so we can
// just drop this IRP on the floor.
//
}
else
{
//
// Complete the IRP, then free the disconnect object
//
pIrp->IoStatus.Status = *pStatus;
pIrp->IoStatus.Information = 0;
UlCompleteRequest( pIrp, g_UlPriorityBoost );
UlpFreeDisconnectObject(pDisconnectObj);
}
return TRUE;
}
/***************************************************************************++
Routine Description:
Allocates and initializes a disconnect object.
Arguments:
pIrp - a UlWaitForDisconnect IRP
--***************************************************************************/
PUL_DISCONNECT_OBJECT
UlpCreateDisconnectObject(
IN PIRP pIrp
)
{
PUL_DISCONNECT_OBJECT pObject;
pObject = UL_ALLOCATE_STRUCT(
PagedPool,
UL_DISCONNECT_OBJECT,
UL_DISCONNECT_OBJECT_POOL_TAG
);
if (pObject) {
pObject->Signature = UL_DISCONNECT_OBJECT_POOL_TAG;
pObject->pIrp = pIrp;
UlInitializeNotifyEntry(&pObject->ProcessEntry, pObject);
UlInitializeNotifyEntry(&pObject->ConnectionEntry, pObject);
}
return pObject;
}
/***************************************************************************++
Routine Description:
Gets rid of a disconnect object
Arguments:
pObject - the disconnect object to free
--***************************************************************************/
VOID
UlpFreeDisconnectObject(
IN PUL_DISCONNECT_OBJECT pObject
)
{
UL_FREE_POOL_WITH_SIG(pObject, UL_DISCONNECT_OBJECT_POOL_TAG);
}