Leaked source code of windows server 2003
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.
 
 
 
 
 
 

5963 lines
154 KiB

/*++
Copyright (c) 1998-2002 Microsoft Corporation
Module Name:
apool.c
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"
#include "apoolp.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text( INIT, UlInitializeAP )
#pragma alloc_text( PAGE, UlTerminateAP )
#pragma alloc_text( PAGE, UlGetPoolFromHandle )
#pragma alloc_text( PAGE, UlQueryAppPoolInformation )
#pragma alloc_text( PAGE, UlSetAppPoolInformation )
#pragma alloc_text( PAGE, UlCloseAppPoolProcess )
#pragma alloc_text( PAGE, UlCopyRequestToBuffer )
#pragma alloc_text( PAGE, UlCopyRequestToIrp )
#pragma alloc_text( PAGE, UlpCopyEntityBodyToBuffer )
#pragma alloc_text( PAGE, UlpRedeliverRequestWorker )
#endif // ALLOC_PRAGMA
#if 0
#if REFERENCE_DEBUG
NOT PAGEABLE -- UlDereferenceAppPool
NOT PAGEABLE -- UlReferenceAppPool
#endif
NOT PAGEABLE -- UlAttachProcessToAppPool
NOT PAGEABLE -- UlDetachProcessFromAppPool
NOT PAGEABLE -- UlShutdownAppPoolProcess
NOT PAGEABLE -- UlReceiveHttpRequest
NOT PAGEABLE -- UlDeliverRequestToProcess
NOT PAGEABLE -- UlUnlinkRequestFromProcess
NOT PAGEABLE -- UlWaitForDisconnect
NOT PAGEABLE -- UlDequeueNewRequest
NOT PAGEABLE -- UlRequeuePendingRequest
NOT PAGEABLE -- UlpSetAppPoolState
NOT PAGEABLE -- UlpPopIrpFromProcess
NOT PAGEABLE -- UlpQueuePendingRequest
NOT PAGEABLE -- UlpQueueUnboundRequest
NOT PAGEABLE -- UlpUnbindQueuedRequests
NOT PAGEABLE -- UlDeleteAppPool
NOT PAGEABLE -- UlpPopNewIrp
NOT PAGEABLE -- UlpIsProcessInAppPool
NOT PAGEABLE -- UlpQueueRequest
NOT PAGEABLE -- UlpRemoveRequest
NOT PAGEABLE -- UlpDequeueRequest
NOT PAGEABLE -- UlpSetAppPoolControlChannelHelper
NOT PAGEABLE -- UlWaitForDemandStart
NOT PAGEABLE -- UlCompleteAllWaitForDisconnect
NOT PAGEABLE -- UlpCancelDemandStart
NOT PAGEABLE -- UlpCancelHttpReceive
NOT PAGEABLE -- UlpCancelWaitForDisconnect
NOT PAGEABLE -- UlpCancelWaitForDisconnectWorker
NOT PAGEABLE -- UlReferenceAppPoolProcess
NOT PAGEABLE -- UlDereferenceAppPoolProcess
NOT PAGEABLE -- UlpSetAppPoolQueueLength
#endif
//
// Globals
//
LIST_ENTRY g_AppPoolListHead = {NULL, NULL};
BOOLEAN g_InitAPCalled = FALSE;
LONG g_RequestsQueued = 0;
/***************************************************************************++
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
N.B. Since pName comes from IoMgr (tag IoNm),
we can safely reference it without extra playing.
NameLength - the byte count of pName
Create - whether or not a new apool should be created if pName
does not exist
pAccessState - the state of an access-in-progress
DesiredAccess - the desired access mask
RequestorMode - UserMode or KernelMode
ppProcess - returns the newly created PROCESS object
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlAttachProcessToAppPool(
IN PWCHAR pName OPTIONAL,
IN USHORT 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;
KLOCK_QUEUE_HANDLE LockHandle;
BOOLEAN SecurityAssigned = FALSE;
ULONG Index;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(ppProcess != NULL);
Status = STATUS_SUCCESS;
*ppProcess = NULL;
ASSERT(NameLength < UL_MAX_APP_POOL_NAME_SIZE);
//
// WAS-type controller process can only be created not opened.
//
if (!Create && (DesiredAccess & WRITE_OWNER))
{
return STATUS_NOT_SUPPORTED;
}
//
// Make sure the AppPool name if passed in doesn't contain '/' since
// it is used as a deliminator of a fragment name.
//
for (Index = 0; Index < NameLength/sizeof(WCHAR); Index++)
{
if (L'/' == pName[Index])
{
return STATUS_OBJECT_NAME_INVALID;
}
}
//
// Try and find an existing app pool of this name; also potentially
// pre-allocate the memory.
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->AppPoolResource, TRUE);
if (pName != NULL)
{
pEntry = g_AppPoolListHead.Flink;
//
// CODEWORK: use something faster than a linear search.
// This won't scale well to hundreds of app pools.
// On the other hand, this isn't something we'll be doing thousands
// of times a second.
//
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)
{
Status = STATUS_OBJECT_NAME_NOT_FOUND;
goto end;
}
//
// Create it. Allocate the object memory.
//
pObject = UL_ALLOCATE_STRUCT_WITH_SPACE(
NonPagedPool,
UL_APP_POOL_OBJECT,
NameLength + sizeof(WCHAR),
UL_APP_POOL_OBJECT_POOL_TAG
);
if (pObject == NULL)
{
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->State = HttpAppPoolDisabled_ByAdministrator;
pObject->LoadBalancerCapability = HttpLoadBalancerBasicCapability;
pObject->pControlChannel = NULL;
InitializeListHead(&pObject->ProcessListHead);
InitializeListHead(&pObject->NewRequestHead);
pObject->RequestCount = 0;
pObject->MaxRequests = DEFAULT_APP_POOL_QUEUE_MAX;
UlInitializeSpinLock(&pObject->SpinLock, "AppPoolSpinLock");
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;
}
SecurityAssigned = TRUE;
WRITE_APP_POOL_TIME_TRACE_LOG(
pObject,
NULL,
APP_POOL_TIME_ACTION_CREATE_APPOOL
);
UlTrace(REFCOUNT, (
"http!UlAttachProcessToAppPool ap=%p refcount=%d\n",
pObject,
pObject->RefCount
));
}
else // if (pObject != NULL)
{
//
// We found the named AppPool object in the list. Reference it.
//
REFERENCE_APP_POOL(pObject);
//
// 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;
}
REFERENCE_APP_POOL_PROCESS(pProcess);
//
// Put the process in the app pool list.
//
UlAcquireInStackQueuedSpinLock(&pObject->SpinLock, &LockHandle);
if (DesiredAccess & WRITE_OWNER)
{
pProcess->Controller = 1;
}
else
{
pObject->NumberActiveProcesses++;
if (pObject->pControlChannel)
{
InterlockedIncrement(
(PLONG)&pObject->pControlChannel->AppPoolProcessCount
);
}
}
InsertHeadList(&pObject->ProcessListHead, &pProcess->ListEntry);
UlReleaseInStackQueuedSpinLock(&pObject->SpinLock, &LockHandle);
WRITE_APP_POOL_TIME_TRACE_LOG(
pObject,
pProcess,
APP_POOL_TIME_ACTION_CREATE_PROCESS
);
//
// Insert AppPool into the global list if it has been created.
//
if (Create)
{
InsertHeadList(&g_AppPoolListHead, &pObject->ListEntry);
}
//
// Return it.
//
*ppProcess = pProcess;
end:
UlReleaseResource(&g_pUlNonpagedData->AppPoolResource);
if (NT_SUCCESS(Status) == FALSE)
{
if (pObject != NULL)
{
if (SecurityAssigned)
{
UlDeassignSecurity(&pObject->pSecurityDescriptor);
}
DEREFERENCE_APP_POOL(pObject);
}
if (pProcess != NULL)
{
UL_FREE_POOL_WITH_SIG(pProcess, UL_APP_POOL_PROCESS_POOL_TAG);
}
}
return Status;
} // UlAttachProcessToAppPool
/***************************************************************************++
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:
pCleanupIrp - the cleanup irp
pCleanupIrpSp - the current stack location of the cleanup irp
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlDetachProcessFromAppPool(
IN PIRP pCleanupIrp,
IN PIO_STACK_LOCATION pCleanupIrpSp
)
{
LIST_ENTRY PendingRequestHead;
PUL_APP_POOL_OBJECT pAppPool;
NTSTATUS CancelStatus = STATUS_CANCELLED;
PUL_INTERNAL_REQUEST pRequest;
KLOCK_QUEUE_HANDLE LockHandle;
PUL_APP_POOL_PROCESS pProcess;
BOOLEAN ListEmpty;
PLIST_ENTRY pEntry;
//
// Sanity check.
//
PAGED_CODE();
pProcess = GET_APP_POOL_PROCESS(pCleanupIrpSp->FileObject);
ASSERT(IS_VALID_AP_PROCESS(pProcess));
UlTrace(ROUTING, (
"http!UlDetachProcessFromAppPool(%p, %S)\n",
pProcess,
pProcess->pAppPool->pName
));
pAppPool = pProcess->pAppPool;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
WRITE_APP_POOL_TIME_TRACE_LOG(
pAppPool,
pProcess,
APP_POOL_TIME_ACTION_DETACH_PROCESS
);
//
// Mark that this appool process is invalid for further
// ioctls.
//
MARK_INVALID_APP_POOL(pCleanupIrpSp->FileObject);
//
// Shut down I/O on the handle.
//
UlShutdownAppPoolProcess(pProcess);
//
// Do final cleanup for the process.
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->AppPoolResource, TRUE);
//
// Unlink from the App Pool list.
//
UlAcquireInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
RemoveEntryList(&pProcess->ListEntry);
pProcess->ListEntry.Flink = pProcess->ListEntry.Blink = NULL;
//
// Move requests that have been passed up to the process to
// a local list so their connections can be closed.
//
InitializeListHead(&PendingRequestHead);
while (NULL != (pRequest = UlpDequeueRequest(
pAppPool,
&pProcess->PendingRequestHead
)))
{
//
// Move the entry to local list so we can close its
// connection outside the app pool lock.
//
InsertTailList(&PendingRequestHead, &pRequest->AppPool.AppPoolEntry);
}
//
// Adjust number of active processes.
//
if (!pProcess->Controller)
{
pAppPool->NumberActiveProcesses--;
if (pAppPool->pControlChannel)
{
InterlockedDecrement(
(PLONG)&pAppPool->pControlChannel->AppPoolProcessCount
);
}
}
ListEmpty = (BOOLEAN) IsListEmpty(&pAppPool->ProcessListHead);
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
//
// Remove the AppPool from the global list if this is the last process
//
if (ListEmpty)
{
RemoveEntryList(&pAppPool->ListEntry);
pAppPool->ListEntry.Flink = pAppPool->ListEntry.Blink = NULL;
//
// Cleanup any security descriptor on the object.
//
UlDeassignSecurity(&pAppPool->pSecurityDescriptor);
}
UlReleaseResource(&g_pUlNonpagedData->AppPoolResource);
//
// Disable the AppPool to clean up the NewRequestQueue if we are the
// last process on the AppPool.
//
if (ListEmpty)
{
UlpSetAppPoolState(pProcess, HttpAppPoolDisabled_ByAdministrator);
}
//
// Close connections associated with the requests that
// the process was handling.
//
while (!IsListEmpty(&PendingRequestHead))
{
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, (
"http!UlDetachProcessFromAppPool(%p, %S): tanking pending req=%p\n",
pProcess,
pAppPool->pName,
pRequest
));
//
// Cancel any pending I/O related to this request.
//
UlAcquirePushLockExclusive(&pRequest->pHttpConn->PushLock);
UlCancelRequestIo(pRequest);
//
// Try to log an entry to the error log file.
// pHttpConn's request pointer could be null, (unlinked)
// need to pass the pRequest separetely.
//
UlErrorLog( pRequest->pHttpConn,
pRequest,
ERROR_LOG_INFO_FOR_APP_POOL_DETACH,
ERROR_LOG_INFO_FOR_APP_POOL_DETACH_SIZE,
TRUE
);
UlReleasePushLockExclusive(&pRequest->pHttpConn->PushLock);
//
// Abort the connection this request is associated with.
//
(VOID) UlCloseConnection(
pRequest->pHttpConn->pConnection,
TRUE,
NULL,
NULL
);
//
// Drop our list's reference.
//
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
}
ASSERT(IsListEmpty(&PendingRequestHead));
//
// Purge all zombie connections that belong to this process.
//
UlPurgeZombieConnections(
&UlPurgeAppPoolProcess,
(PVOID) pProcess
);
//
// Cancel any remaining WaitForDisconnect IRPs.
//
UlAcquireResourceExclusive(&g_pUlNonpagedData->DisconnectResource, TRUE);
UlNotifyAllEntries(
UlpNotifyCompleteWaitForDisconnect,
&pProcess->WaitForDisconnectHead,
&CancelStatus
);
UlReleaseResource(&g_pUlNonpagedData->DisconnectResource);
//
// Kill any cache entries related to this process.
//
UlFlushCacheByProcess(pProcess);
//
// Mark the original cleanup irp pending and then deref and return.
// When the refcount on pProcess reaches to zero it will complete
// the cleanup irp.
//
IoMarkIrpPending(pCleanupIrp);
pCleanupIrp->IoStatus.Status = STATUS_PENDING;
//
// Tell the process which Irp to complete once it's ready
// to go away.
//
pProcess->pCleanupIrp = pCleanupIrp;
//
// Release our refcount on the pProcess.
//
DEREFERENCE_APP_POOL_PROCESS(pProcess);
return STATUS_PENDING;
} // UlDetachProcessFromAppPool
/***************************************************************************++
Routine Description:
Cleans up outstanding I/O on an app pool process. This function
cancels all calls to HttpReceiveHttpRequest, and routes queued
requests to other worker processes. Outstanding send i/o is not
affected.
Arguments:
pProcess - the process object to shut down
Return Value:
None
--***************************************************************************/
VOID
UlShutdownAppPoolProcess(
IN PUL_APP_POOL_PROCESS pProcess
)
{
PUL_APP_POOL_OBJECT pAppPool;
PUL_APP_POOL_OBJECT pDemandStartAppPool;
KLOCK_QUEUE_HANDLE LockHandle;
LIST_ENTRY RequestList;
PUL_INTERNAL_REQUEST pRequest;
PLIST_ENTRY pEntry;
PIRP pIrp;
PUL_APP_POOL_PROCESS pAppPoolProcess;
//
// Sanity check.
//
ASSERT(IS_VALID_AP_PROCESS(pProcess));
pAppPool = pProcess->pAppPool;
UlAcquireResourceExclusive(&g_pUlNonpagedData->AppPoolResource, TRUE);
UlAcquireInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
if (pProcess->InCleanup)
{
//
// If we've already done this, get out.
//
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
UlReleaseResource(&g_pUlNonpagedData->AppPoolResource);
return;
}
//
// Mark the process as InCleanup so new I/O won't be attached,
// and so we won't try to clean it up again.
//
pProcess->InCleanup = 1;
//
// Cancel demand start IRP.
//
if (pProcess->Controller && pAppPool->pDemandStartIrp != NULL)
{
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
{
pDemandStartAppPool = (PUL_APP_POOL_OBJECT)
IoGetCurrentIrpStackLocation(pAppPool->pDemandStartIrp)->
Parameters.DeviceIoControl.Type3InputBuffer;
ASSERT(pDemandStartAppPool == pAppPool);
DEREFERENCE_APP_POOL(pAppPool);
IoGetCurrentIrpStackLocation(pAppPool->pDemandStartIrp)->
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
pAppPool->pDemandStartIrp->IoStatus.Status = STATUS_CANCELLED;
pAppPool->pDemandStartIrp->IoStatus.Information = 0;
UlCompleteRequest(pAppPool->pDemandStartIrp, IO_NO_INCREMENT);
}
pAppPool->pDemandStartIrp = NULL;
pAppPool->pDemandStartProcess = NULL;
}
//
// Cancel pending HttpReceiveHttpRequest IRPs.
//
while (!IsListEmpty(&pProcess->NewIrpHead))
{
//
// 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
{
//
// Cancel it. Even if pIrp->Cancel == TRUE we are supposed to
// complete it, our cancel routine will never run.
//
pAppPoolProcess = (PUL_APP_POOL_PROCESS)
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer;
ASSERT(pAppPoolProcess == pProcess);
DEREFERENCE_APP_POOL_PROCESS(pAppPoolProcess);
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
pIrp = NULL;
}
}
//
// Move requests that have been passed up to the process to a local list
// so their pending HttpReceiveEntityBody IRPs can be canceled.
//
InitializeListHead(&RequestList);
pEntry = pProcess->PendingRequestHead.Flink;
while (pEntry != &pProcess->PendingRequestHead)
{
pRequest = CONTAINING_RECORD(
pEntry,
UL_INTERNAL_REQUEST,
AppPool.AppPoolEntry
);
pEntry = pEntry->Flink;
//
// Take a short lived reference for the request so we can traverse
// the list outside the AppPool lock.
//
UL_REFERENCE_INTERNAL_REQUEST(pRequest);
InsertTailList(&RequestList, &pRequest->AppPool.ProcessEntry);
}
//
// Unbind requests that haven't been passed up to this process so
// they can be handled by other processes in the app pool.
//
UlpUnbindQueuedRequests(pProcess);
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
UlReleaseResource(&g_pUlNonpagedData->AppPoolResource);
//
// Cancel pending HttpReceiveEntityBody IRPs.
//
while (!IsListEmpty(&RequestList))
{
pEntry = RemoveHeadList(&RequestList);
pEntry->Flink = pEntry->Blink = NULL;
pRequest = CONTAINING_RECORD(
pEntry,
UL_INTERNAL_REQUEST,
AppPool.ProcessEntry
);
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
//
// Cancel any pending I/O related to this request.
//
UlAcquirePushLockExclusive(&pRequest->pHttpConn->PushLock);
UlCancelRequestIo(pRequest);
UlReleasePushLockExclusive(&pRequest->pHttpConn->PushLock);
//
// Drop the extra short lived reference we just added.
//
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
}
} // UlShutdownAppPoolProcess
#if REFERENCE_DEBUG
/***************************************************************************++
Routine Description:
Increments the refcount.
Arguments:
pAppPool - the object to increment.
Return Value:
None
--***************************************************************************/
VOID
UlReferenceAppPool(
IN PUL_APP_POOL_OBJECT pAppPool
REFERENCE_DEBUG_FORMAL_PARAMS
)
{
LONG RefCount;
//
// Sanity check.
//
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
RefCount = InterlockedIncrement(&pAppPool->RefCount);
ASSERT(RefCount > 0);
WRITE_REF_TRACE_LOG(
g_pAppPoolTraceLog,
REF_ACTION_REFERENCE_APP_POOL,
RefCount,
pAppPool,
pFileName,
LineNumber
);
UlTrace(REFCOUNT, (
"http!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:
None
--***************************************************************************/
VOID
UlDereferenceAppPool(
IN PUL_APP_POOL_OBJECT pAppPool
REFERENCE_DEBUG_FORMAL_PARAMS
)
{
LONG RefCount;
//
// Sanity check.
//
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
RefCount = InterlockedDecrement(&pAppPool->RefCount);
ASSERT(RefCount >= 0);
//
// Tracing.
//
WRITE_REF_TRACE_LOG(
g_pAppPoolTraceLog,
REF_ACTION_DEREFERENCE_APP_POOL,
RefCount,
pAppPool,
pFileName,
LineNumber
);
UlTrace(REFCOUNT, (
"http!UlDereferenceAppPool ap=%p refcount=%d\n",
pAppPool,
RefCount
));
//
// Clean up if necessary.
//
if (RefCount == 0)
{
DELETE_APP_POOL(pAppPool);
}
} // UlDereferenceAppPool
/***************************************************************************++
Routine Description:
Increments the refcount on appool process.
Arguments:
pAppPoolProcess - the object to increment
Return Value:
None
--***************************************************************************/
VOID
UlReferenceAppPoolProcess(
IN PUL_APP_POOL_PROCESS pAppPoolProcess
REFERENCE_DEBUG_FORMAL_PARAMS
)
{
LONG RefCount;
//
// Sanity check.
//
ASSERT(IS_VALID_AP_PROCESS(pAppPoolProcess));
RefCount = InterlockedIncrement(&pAppPoolProcess->RefCount);
WRITE_REF_TRACE_LOG(
g_pAppPoolProcessTraceLog,
REF_ACTION_REFERENCE_APP_POOL_PROCESS,
RefCount,
pAppPoolProcess,
pFileName,
LineNumber
);
UlTrace(ROUTING,(
"http!UlReferenceAppPoolProcess app=%p refcount=%d\n",
pAppPoolProcess,
RefCount
));
} // UlReferenceAppPoolProcess
/***************************************************************************++
Routine Description:
Decrements the refcount. If it hits 0, it completes the pending cleanup
irp for the process. But does not free up the process structure itself.
The structure get cleaned up when close on the process handle happens.
FastIo path may call us at dispacth level, luckily pAppPoolProcess is
from nonpaged pool and we queue a work item.
Arguments:
pAppPoolProcess - the object to decrement
Return Value:
None
--***************************************************************************/
VOID
UlDereferenceAppPoolProcess(
IN PUL_APP_POOL_PROCESS pAppPoolProcess
REFERENCE_DEBUG_FORMAL_PARAMS
)
{
LONG RefCount;
//
// Sanity check.
//
ASSERT(IS_VALID_AP_PROCESS(pAppPoolProcess));
RefCount = InterlockedDecrement(&pAppPoolProcess->RefCount);
//
// Tracing.
//
WRITE_REF_TRACE_LOG(
g_pAppPoolProcessTraceLog,
REF_ACTION_DEREFERENCE_APP_POOL_PROCESS,
RefCount,
pAppPoolProcess,
pFileName,
LineNumber
);
UlTrace(ROUTING, (
"http!UlDereferenceAppPoolProcess app=%p refcount=%d\n",
pAppPoolProcess,
RefCount
));
if (RefCount == 0)
{
ASSERT(pAppPoolProcess->pCleanupIrp);
UlpCleanUpAppoolProcess(pAppPoolProcess);
}
} // UlDereferenceAppPoolProcess
#endif // REFERENCE_DEBUG
/***************************************************************************++
Routine Description:
The actual cleanup routine to do the original cleanup Irp completion
once the refcount on the process reaches to zero.
Arguments:
pAppPoolProcess - the appool process
Return Value:
None
--***************************************************************************/
VOID
UlpCleanUpAppoolProcess(
IN PUL_APP_POOL_PROCESS pAppPoolProcess
)
{
PIRP pIrp;
//
// Sanity check.
//
ASSERT(IS_VALID_AP_PROCESS(pAppPoolProcess));
ASSERT(pAppPoolProcess->RefCount == 0);
pIrp = pAppPoolProcess->pCleanupIrp;
ASSERT(pIrp);
WRITE_APP_POOL_TIME_TRACE_LOG(
pAppPoolProcess->pAppPool,
pAppPoolProcess,
APP_POOL_TIME_ACTION_DETACH_PROCESS_COMPLETE
);
pAppPoolProcess->pCleanupIrp = NULL;
UlTrace(ROUTING,(
"http!UlpCleanUpAppoolProcess: pAppPoolProcess %p pIrp %p\n",
pAppPoolProcess,
pIrp
));
pIrp->IoStatus.Status = STATUS_SUCCESS;
UlCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
} // UlpCleanUpAppoolProcess
/***************************************************************************++
Routine Description:
Destructs the apool object.
Arguments:
pAppPool - the object to destruct
Return Value:
None
--***************************************************************************/
VOID
UlDeleteAppPool(
IN PUL_APP_POOL_OBJECT pAppPool
REFERENCE_DEBUG_FORMAL_PARAMS
)
{
#if REFERENCE_DEBUG
UNREFERENCED_PARAMETER(pFileName);
UNREFERENCED_PARAMETER(LineNumber);
#endif
ASSERT(0 == pAppPool->RefCount);
//
// There better not be any process objects hanging around.
//
ASSERT(IsListEmpty(&pAppPool->ProcessListHead));
//
// There better not be any pending requests hanging around.
//
ASSERT(IsListEmpty(&pAppPool->NewRequestHead));
//
// If we're holding a ref on a control channel, release it.
//
if (pAppPool->pControlChannel)
{
DEREFERENCE_CONTROL_CHANNEL(pAppPool->pControlChannel);
}
WRITE_APP_POOL_TIME_TRACE_LOG(
pAppPool,
NULL,
APP_POOL_TIME_ACTION_DESTROY_APPOOL
);
UL_FREE_POOL_WITH_SIG(pAppPool, UL_APP_POOL_OBJECT_POOL_TAG);
} // UlDeleteAppPool
/***************************************************************************++
Routine Description:
Queries the app-pool queue length.
Arguments:
pProcess - the appool process
InformationClass - tells which information we want to query
pAppPoolInformation - pointer to the buffer to return information
Length - length of the buffer to return information
pReturnLength - tells how many bytes we have returned
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
)
{
NTSTATUS Status = STATUS_SUCCESS;
UNREFERENCED_PARAMETER(Length);
//
// Sanity check.
//
PAGED_CODE();
ASSERT(pReturnLength);
ASSERT(IS_VALID_AP_PROCESS(pProcess));
ASSERT(IS_VALID_AP_OBJECT(pProcess->pAppPool));
//
// Do the action.
//
switch (InformationClass)
{
case HttpAppPoolQueueLengthInformation:
*((PULONG) pAppPoolInformation) = pProcess->pAppPool->MaxRequests;
*pReturnLength = sizeof(ULONG);
break;
case HttpAppPoolStateInformation:
*((PHTTP_APP_POOL_ENABLED_STATE) pAppPoolInformation) =
pProcess->pAppPool->State;
*pReturnLength = sizeof(HTTP_APP_POOL_ENABLED_STATE);
break;
case HttpAppPoolLoadBalancerInformation:
*((PHTTP_LOAD_BALANCER_CAPABILITIES) pAppPoolInformation) =
pProcess->pAppPool->LoadBalancerCapability;
*pReturnLength = sizeof(HTTP_LOAD_BALANCER_CAPABILITIES);
break;
default:
//
// Should have been caught in UlQueryAppPoolInformationIoctl.
//
ASSERT(FALSE);
Status = STATUS_INVALID_PARAMETER;
break;
}
return Status;
} // UlQueryAppPoolInformation
/***************************************************************************++
Routine Description:
Sets the app-pool queue length etc.
Arguments:
pProcess - the appool process
InformationClass - tells which information we want to set
pAppPoolInformation - pointer to the buffer for the input information
Length - length of the buffer for the input information
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;
ULONG QueueLength;
HTTP_APP_POOL_ENABLED_STATE State;
HTTP_LOAD_BALANCER_CAPABILITIES Capabilities;
UNREFERENCED_PARAMETER(Length);
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
ASSERT(pAppPoolInformation);
//
// Do the action.
//
switch (InformationClass)
{
case HttpAppPoolQueueLengthInformation:
QueueLength = *((PULONG) pAppPoolInformation);
if (QueueLength > UL_MAX_REQUESTS_QUEUED ||
QueueLength < UL_MIN_REQUESTS_QUEUED)
{
return STATUS_NOT_SUPPORTED;
}
else
{
Status = UlpSetAppPoolQueueLength(pProcess, QueueLength);
}
break;
case HttpAppPoolStateInformation:
State = *((PHTTP_APP_POOL_ENABLED_STATE) pAppPoolInformation);
if (State < HttpAppPoolEnabled ||
State >= HttpAppPoolEnabledMaximum)
{
Status = STATUS_NOT_SUPPORTED;
}
else
{
UlpSetAppPoolState(pProcess, State);
}
break;
case HttpAppPoolLoadBalancerInformation:
Capabilities =
*((PHTTP_LOAD_BALANCER_CAPABILITIES) pAppPoolInformation);
if (Capabilities != HttpLoadBalancerBasicCapability &&
Capabilities != HttpLoadBalancerSophisticatedCapability)
{
Status = STATUS_NOT_SUPPORTED;
}
else
{
UlpSetAppPoolLoadBalancerCapability(pProcess, Capabilities);
}
break;
case HttpAppPoolControlChannelInformation:
{
PHTTP_APP_POOL_CONTROL_CHANNEL pControlChannelInfo;
PUL_CONTROL_CHANNEL pControlChannel;
if (Length < sizeof(HTTP_APP_POOL_CONTROL_CHANNEL))
{
Status = STATUS_INVALID_PARAMETER;
}
else
{
pControlChannelInfo =
(PHTTP_APP_POOL_CONTROL_CHANNEL) pAppPoolInformation;
if (pControlChannelInfo->Flags.Present)
{
Status = UlGetControlChannelFromHandle(
pControlChannelInfo->ControlChannel,
UserMode,
&pControlChannel
);
if (NT_SUCCESS(Status))
{
UlpSetAppPoolControlChannelHelper(
pProcess,
pControlChannel
);
}
}
}
}
break;
default:
//
// Should have been caught in UlSetAppPoolInformationIoctl.
//
ASSERT(FALSE);
Status = STATUS_INVALID_PARAMETER;
break;
}
return Status;
} // UlSetAppPoolInformation
/*++
Routine Description:
Sets the app-pool control channel property. Must be non-pageable
because we need to take the app pool spin lock.
Arguments:
pProcess - the appool process
pControlChannel - the new control channel to set on the app pool
--*/
VOID
UlpSetAppPoolControlChannelHelper(
IN PUL_APP_POOL_PROCESS pProcess,
IN PUL_CONTROL_CHANNEL pControlChannel
)
{
PUL_CONTROL_CHANNEL pOldControlChannel;
PUL_APP_POOL_OBJECT pAppPool;
KLOCK_QUEUE_HANDLE LockHandle;
// NOT_PAGEABLE
pAppPool = pProcess->pAppPool;
UlAcquireInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
//
// Get the old control channle (if any)
//
pOldControlChannel = pAppPool->pControlChannel;
//
// Set new control channel on app pool
//
pProcess->pAppPool->pControlChannel = pControlChannel;
//
// If we already have a control channel on the app pool,
// remove this app pool's count & deref old control channel.
//
if (pOldControlChannel)
{
InterlockedExchangeAdd(
(PLONG)&pOldControlChannel->AppPoolProcessCount,
-((LONG)pProcess->pAppPool->NumberActiveProcesses)
);
DEREFERENCE_CONTROL_CHANNEL(pOldControlChannel);
}
//
// add this AppPool's active process count to control channel.
//
InterlockedExchangeAdd(
(PLONG)&pControlChannel->AppPoolProcessCount,
pProcess->pAppPool->NumberActiveProcesses
);
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
return;
}
/***************************************************************************++
Routine Description:
Convert AppPoolEnabledState to ErrorCode.
Arguments:
State - AppPoolEnabledState
Return Value:
ErrorCode
--***************************************************************************/
UL_HTTP_ERROR
UlpConvertAppPoolEnabledStateToErrorCode(
IN HTTP_APP_POOL_ENABLED_STATE State
)
{
UL_HTTP_ERROR ErrorCode;
ASSERT(State != HttpAppPoolEnabled);
switch (State)
{
case HttpAppPoolDisabled_RapidFailProtection:
ErrorCode = UlErrorRapidFailProtection;
break;
case HttpAppPoolDisabled_AppPoolQueueFull:
ErrorCode = UlErrorAppPoolQueueFull;
break;
case HttpAppPoolDisabled_ByAdministrator:
ErrorCode = UlErrorDisabledByAdmin;
break;
case HttpAppPoolDisabled_JobObjectFired:
ErrorCode = UlErrorJobObjectFired;
break;
case HttpAppPoolEnabled:
default:
ASSERT(!"Invalid HTTP_APP_POOL_ENABLED_STATE");
ErrorCode = UlErrorUnavailable; // generic 503
break;
}
return ErrorCode;
} // UlpConvertAppPoolEnabledStateToErrorCode
/***************************************************************************++
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
State - mark app pool as active or inactive
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpSetAppPoolState(
IN PUL_APP_POOL_PROCESS pProcess,
IN HTTP_APP_POOL_ENABLED_STATE State
)
{
PUL_APP_POOL_OBJECT pAppPool;
KLOCK_QUEUE_HANDLE LockHandle;
PUL_INTERNAL_REQUEST pRequest;
PUL_HTTP_CONNECTION pHttpConn;
ULONG Requests = 0;
UL_HTTP_ERROR ErrorCode = UlErrorUnavailable;
LIST_ENTRY NewRequestHead;
PLIST_ENTRY pEntry;
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) State
));
InitializeListHead(&NewRequestHead);
UlAcquireInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
pAppPool->State = State;
if (State != HttpAppPoolEnabled)
{
ErrorCode = UlpConvertAppPoolEnabledStateToErrorCode(State);
WRITE_APP_POOL_TIME_TRACE_LOG(
pAppPool,
(PVOID) (ULONG_PTR) State,
APP_POOL_TIME_ACTION_MARK_APPOOL_INACTIVE
);
while (NULL != (pRequest = UlpDequeueRequest(
pAppPool,
&pAppPool->NewRequestHead
)))
{
//
// Move the entry to a local list so we can process them
// outside the app pool lock.
//
InsertTailList(&NewRequestHead, &pRequest->AppPool.AppPoolEntry);
}
}
else
{
WRITE_APP_POOL_TIME_TRACE_LOG(
pAppPool,
NULL,
APP_POOL_TIME_ACTION_MARK_APPOOL_ACTIVE
);
}
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
//
// Send 503 to all the requests we have removed from the queue.
//
while (!IsListEmpty(&NewRequestHead))
{
pEntry = RemoveHeadList(&NewRequestHead);
pEntry->Flink = pEntry->Blink = NULL;
pRequest = CONTAINING_RECORD(
pEntry,
UL_INTERNAL_REQUEST,
AppPool.AppPoolEntry
);
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
pHttpConn = pRequest->pHttpConn;
ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConn));
UlAcquirePushLockExclusive(&pHttpConn->PushLock);
if (pHttpConn->UlconnDestroyed)
{
ASSERT(NULL == pHttpConn->pRequest);
}
else
{
UlSetErrorCode(pRequest, ErrorCode, pAppPool);
UlSendErrorResponse(pHttpConn);
}
UlReleasePushLockExclusive(&pHttpConn->PushLock);
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
++Requests;
}
if (State != HttpAppPoolEnabled)
{
UlTrace(ROUTING, (
"%lu unhandled requests 503'd from AppPool %p.\n",
Requests, pAppPool
));
}
return STATUS_SUCCESS;
} // UlpSetAppPoolState
/***************************************************************************++
Routine Description:
Sets the load balancer capabilities of an app pool to Basic
or Sophisticated.
Arguments:
pProcess - the app pool process object with which the irp
is associated.
LoadBalancerCapability - new capability
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpSetAppPoolLoadBalancerCapability(
IN PUL_APP_POOL_PROCESS pProcess,
IN HTTP_LOAD_BALANCER_CAPABILITIES LoadBalancerCapability
)
{
PUL_APP_POOL_OBJECT pAppPool;
KLOCK_QUEUE_HANDLE LockHandle;
ASSERT(IS_VALID_AP_PROCESS(pProcess));
pAppPool = pProcess->pAppPool;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
UlTrace(ROUTING, (
"http!UlpSetAppPoolLoadBalancerCapability(AppPool=%p, %lu).\n",
pAppPool, (ULONG) LoadBalancerCapability
));
UlAcquireInStackQueuedSpinLock(&pProcess->pAppPool->SpinLock, &LockHandle);
pAppPool->LoadBalancerCapability = LoadBalancerCapability;
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
WRITE_APP_POOL_TIME_TRACE_LOG(
pAppPool,
(PVOID) (ULONG_PTR) LoadBalancerCapability,
APP_POOL_TIME_ACTION_LOAD_BAL_CAPABILITY
);
return STATUS_SUCCESS;
} // UlpSetAppPoolLoadBalancerCapability
/***************************************************************************++
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;
KLOCK_QUEUE_HANDLE LockHandle;
PEPROCESS CurrentProcess = PsGetCurrentProcess();
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
ASSERT(IS_VALID_AP_OBJECT(pProcess->pAppPool));
ASSERT(pIrp != NULL);
//
// DemandStart IRPs can only come from controller processes.
//
if (!pProcess->Controller)
{
return STATUS_INVALID_ID_AUTHORITY;
}
UlAcquireInStackQueuedSpinLock(&pProcess->pAppPool->SpinLock, &LockHandle);
//
// Make sure we're not cleaning up the process
//
if (pProcess->InCleanup)
{
Status = STATUS_INVALID_HANDLE;
goto end;
}
//
// Already got one?
//
if (pProcess->pAppPool->pDemandStartIrp != NULL)
{
Status = STATUS_OBJECT_NAME_COLLISION;
goto end;
}
//
// Anything waiting in the queue?
//
if (IsListEmpty(&pProcess->pAppPool->NewRequestHead))
{
//
// Nope, pend the irp.
//
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);
//
// 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.
//
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.
//
//
// 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.
//
pProcess->pAppPool->pDemandStartIrp = pIrp;
pProcess->pAppPool->pDemandStartProcess = CurrentProcess;
Status = STATUS_PENDING;
goto end;
}
else
{
//
// Something's in the queue, instant demand start.
//
IoMarkIrpPending(pIrp);
pIrp->IoStatus.Status = STATUS_SUCCESS;
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
Status = STATUS_PENDING;
goto end;
}
end:
UlReleaseInStackQueuedSpinLock(&pProcess->pAppPool->SpinLock, &LockHandle);
return Status;
} // UlWaitForDemandStart
/***************************************************************************++
Routine Description:
Receives a new http request into pIrp or pend the irp if no request
is available.
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;
KLOCK_QUEUE_HANDLE LockHandle;
PIO_STACK_LOCATION pIrpSp;
UNREFERENCED_PARAMETER(Flags);
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
ASSERT(IS_VALID_AP_OBJECT(pProcess->pAppPool));
ASSERT(pIrp);
ASSERT(pIrp->MdlAddress);
UlAcquireInStackQueuedSpinLock(&pProcess->pAppPool->SpinLock, &LockHandle);
//
// Make sure we're not cleaning up the process.
//
if (pProcess->InCleanup)
{
Status = STATUS_INVALID_HANDLE;
UlReleaseInStackQueuedSpinLock(
&pProcess->pAppPool->SpinLock,
&LockHandle
);
goto end;
}
//
// Is this for a new request?
//
if (HTTP_IS_NULL_ID(&RequestId))
{
//
// Do we have a queue'd new request?
//
Status = UlDequeueNewRequest(pProcess, 0, &pRequest);
if (!NT_SUCCESS(Status) && STATUS_NOT_FOUND != Status)
{
UlReleaseInStackQueuedSpinLock(
&pProcess->pAppPool->SpinLock,
&LockHandle
);
goto end;
}
if (pRequest == NULL)
{
//
// Nope, queue the irp up.
//
IoMarkIrpPending(pIrp);
//
// Give the irp a pointer to the app pool process.
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = pProcess;
REFERENCE_APP_POOL_PROCESS(pProcess);
//
// 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.
//
UlReleaseInStackQueuedSpinLock(
&pProcess->pAppPool->SpinLock,
&LockHandle
);
REFERENCE_APP_POOL_PROCESS(pProcess);
pIrp->IoStatus.Information = 0;
UlUnmarkIrpPending(pIrp);
Status = STATUS_CANCELLED;
goto end;
}
//
// Our cancel routine will run and complete the irp,
// don't touch it.
//
UlReleaseInStackQueuedSpinLock(
&pProcess->pAppPool->SpinLock,
&LockHandle
);
//
// 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.
//
InsertTailList(
&pProcess->NewIrpHead,
&pIrp->Tail.Overlay.ListEntry
);
UlReleaseInStackQueuedSpinLock(
&pProcess->pAppPool->SpinLock,
&LockHandle
);
Status = STATUS_PENDING;
goto end;
}
else // if (pRequest == NULL)
{
//
// Have a queue'd request, serve it up!
//
// UlDequeueNewRequest gives ourselves a short-lived reference.
//
UlReleaseInStackQueuedSpinLock(
&pProcess->pAppPool->SpinLock,
&LockHandle
);
//
// 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);
UlCopyRequestToIrp(pRequest, pIrp, TRUE, FALSE);
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 from id.
//
pRequest = UlGetRequestFromId(RequestId, pProcess);
if (!pRequest)
{
Status = STATUS_CONNECTION_INVALID;
UlReleaseInStackQueuedSpinLock(
&pProcess->pAppPool->SpinLock,
&LockHandle
);
goto end;
}
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
//
// Let go the lock.
//
UlReleaseInStackQueuedSpinLock(
&pProcess->pAppPool->SpinLock,
&LockHandle
);
UlTrace(ROUTING, (
"http!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
));
//
// 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);
UlCopyRequestToIrp(pRequest, pIrp, TRUE, FALSE);
//
// 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 != STATUS_PENDING, the ioctl wrapper will
// complete pIrp.
//
return Status;
} // UlReceiveHttpRequest
/***************************************************************************++
Routine Description:
Called by the http engine to deliver a request to an apool.
This attempts 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:
pAppPool - the AppPool
pRequest - the request to deliver
pIrpToComplete - optionally provides a pointer of the irp to complete
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlDeliverRequestToProcess(
IN PUL_APP_POOL_OBJECT pAppPool,
IN PUL_INTERNAL_REQUEST pRequest,
OUT PIRP * pIrpToComplete OPTIONAL
)
{
NTSTATUS Status;
PUL_APP_POOL_OBJECT pDemandStartAppPool;
PIRP pDemandStartIrp;
PIRP pIrp = NULL;
PUL_APP_POOL_PROCESS pProcess = NULL;
KLOCK_QUEUE_HANDLE LockHandle;
PVOID pUrl;
ULONG UrlLength;
PUL_CONTROL_CHANNEL pControlChannel;
BOOLEAN FailedDemandStart = FALSE;
//
// 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));
ASSERT(!pIrpToComplete || !(*pIrpToComplete));
UlTrace(ROUTING, (
"http!UlDeliverRequestToProcess(pRequest = %p)\n"
" verb + path -> %d %S\n"
" pAppPool = %p (%S)\n",
pRequest,
pRequest->Verb,
pRequest->CookedUrl.pUrl,
pAppPool,
pAppPool->pName
));
//
// Grab the lock!
//
UlAcquireInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
TRACE_TIME(
pRequest->ConnectionId,
pRequest->RequestId,
TIME_ACTION_ROUTE_REQUEST
);
//
// Was the app pool enabled yet?
//
if (pAppPool->State != HttpAppPoolEnabled)
{
UlSetErrorCode(
pRequest,
UlpConvertAppPoolEnabledStateToErrorCode(pAppPool->State),
pAppPool
);
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
return STATUS_PORT_DISCONNECTED;
}
Status = STATUS_SUCCESS;
//
// Complete the demand start if this is the very first request so we can
// do load balancing for web gardens, or if there is no worker process
// being started in which case we have no choice.
//
if (pAppPool->pDemandStartIrp &&
(pRequest->FirstRequest || !pAppPool->NumberActiveProcesses))
{
pControlChannel = pAppPool->pControlChannel;
if (pControlChannel &&
(pControlChannel->AppPoolProcessCount >= pControlChannel->DemandStartThreshold))
{
//
// If we currently exceed our demand start threshold, do
// not complete the demand start Irp AND fail the queuing of the
// request (send back a 503).
//
ASSERT(IS_VALID_CONTROL_CHANNEL(pControlChannel));
FailedDemandStart = TRUE;
}
else
{
// Do the Irp Dance
pDemandStartIrp = pAppPool->pDemandStartIrp;
//
// 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.
//
pDemandStartAppPool = (PUL_APP_POOL_OBJECT)
IoGetCurrentIrpStackLocation(pDemandStartIrp)->
Parameters.DeviceIoControl.Type3InputBuffer;
ASSERT(pDemandStartAppPool == pAppPool);
DEREFERENCE_APP_POOL(pDemandStartAppPool);
IoGetCurrentIrpStackLocation(pDemandStartIrp)->
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
pDemandStartIrp->IoStatus.Status = STATUS_CANCELLED;
pDemandStartIrp->IoStatus.Information = 0;
UlCompleteRequest(pDemandStartIrp, IO_NO_INCREMENT);
}
else
{
//
// Free to use the irp.
//
pDemandStartAppPool = (PUL_APP_POOL_OBJECT)
IoGetCurrentIrpStackLocation(pDemandStartIrp)->
Parameters.DeviceIoControl.Type3InputBuffer;
ASSERT(pDemandStartAppPool == pAppPool);
DEREFERENCE_APP_POOL(pDemandStartAppPool);
IoGetCurrentIrpStackLocation(pDemandStartIrp)->
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
pDemandStartIrp->IoStatus.Status = STATUS_SUCCESS;
pDemandStartIrp->IoStatus.Information = 0;
UlCompleteRequest(pDemandStartIrp, IO_NETWORK_INCREMENT);
}
pAppPool->pDemandStartProcess = NULL;
pAppPool->pDemandStartIrp = NULL;
}
}
//
// 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, pRequest, &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, pRequest);
}
else
{
//
// Remove the binding if we were previously bound to a process.
//
if (pProcess)
{
UlBindConnectionToProcess(
pRequest->pHttpConn,
pAppPool,
NULL
);
}
//
// 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, pRequest, &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 (ETW_LOG_MIN())
{
pUrl = NULL;
UrlLength = 0;
//
// Trace the URL optionally here in case we turned off ParseHook.
//
if (ETW_LOG_URL())
{
pUrl = pRequest->CookedUrl.pUrl;
UrlLength = pRequest->CookedUrl.Length;
}
UlEtwTraceEvent(
&UlTransGuid,
ETW_TYPE_ULDELIVER,
(PVOID) &pRequest,
sizeof(PVOID),
&pRequest->RequestIdCopy,
sizeof(HTTP_REQUEST_ID),
&pRequest->ConfigInfo.SiteId,
sizeof(ULONG),
pRequest->ConfigInfo.pAppPool->pName,
pRequest->ConfigInfo.pAppPool->NameLength,
pUrl,
UrlLength,
NULL,
0
);
}
//
// If we have an IRP, complete it. Otherwise queue the request.
//
if (pIrp != NULL)
{
ASSERT(pIrp->MdlAddress != NULL);
ASSERT(pProcess->InCleanup == 0);
//
// We are all done and about to complete the irp, free the lock.
//
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
UlTrace(ROUTING, (
"http!UlDeliverRequestToProcess(pRequest = %p, pProcess = %p)\n"
" queued pending request. pAppPool = %p (%S)\n",
pRequest,
pProcess,
pProcess->pAppPool,
pProcess->pAppPool->pName
));
//
// 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.
//
if (pIrpToComplete)
{
UlCopyRequestToIrp(pRequest, pIrp, FALSE, TRUE);
*pIrpToComplete = pIrp;
}
else
{
UlCopyRequestToIrp(pRequest, pIrp, TRUE, TRUE);
}
}
else
{
ASSERT(pIrp == NULL);
if (FailedDemandStart)
{
UlTrace(ROUTING, (
"http!UlDeliverRequestToProcess(pRequest = %p, pAppPool = %p)\n"
" Failing request because Demand Start Threshold exceeded.\n",
pRequest,
pAppPool
));
UlSetErrorCode( pRequest, UlErrorUnavailable, pAppPool);
Status = STATUS_PORT_DISCONNECTED;
}
else
{
//
// 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);
}
//
// Now we finished queue'ing the request, free the lock.
//
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
}
return Status;
} // UlDeliverRequestToProcess
/***************************************************************************++
Routine Description:
Removes a request from any app pool lists.
Arguments:
pAppPool - the appool to unlink the request from
pRequest - the request to be unlinked
Return Value:
None
--***************************************************************************/
VOID
UlUnlinkRequestFromProcess(
IN PUL_APP_POOL_OBJECT pAppPool,
IN PUL_INTERNAL_REQUEST pRequest
)
{
KLOCK_QUEUE_HANDLE LockHandle;
BOOLEAN NeedDeref = FALSE;
//
// Sanity check.
//
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
UlAcquireInStackQueuedSpinLock(&pAppPool->SpinLock, &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, pRequest);
pRequest->AppPool.QueueState = QueueUnlinkedState;
NeedDeref = TRUE;
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(pAppPool, pRequest);
pRequest->AppPool.QueueState = QueueUnlinkedState;
NeedDeref = TRUE;
break;
case QueueUnroutedState:
case QueueUnlinkedState:
//
// It's not on our lists, so we don't do anything.
//
break;
default:
//
// This shouldn't happen.
//
ASSERT(!"Invalid app pool queue state");
break;
}
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
//
// Clean up the references.
//
if (NeedDeref)
{
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
}
} // UlUnlinkRequestFromProcess
/***************************************************************************++
Routine Description:
Initializes the AppPool module.
Arguments:
None
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlInitializeAP(
VOID
)
{
NTSTATUS Status = STATUS_SUCCESS;
ASSERT(!g_InitAPCalled);
if (!g_InitAPCalled)
{
InitializeListHead(&g_AppPoolListHead);
g_RequestsQueued = 0;
Status = UlInitializeResource(
&g_pUlNonpagedData->AppPoolResource,
"AppPoolResource",
0,
UL_APP_POOL_RESOURCE_TAG
);
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;
} // UlInitializeAP
/***************************************************************************++
Routine Description:
Terminates the AppPool module.
Arguments:
None
Return Value:
None
--***************************************************************************/
VOID
UlTerminateAP(
VOID
)
{
if (g_InitAPCalled)
{
(VOID) UlDeleteResource(&g_pUlNonpagedData->AppPoolResource);
(VOID) UlDeleteResource(&g_pUlNonpagedData->DisconnectResource);
g_InitAPCalled = FALSE;
}
} // UlTerminateAP
/***************************************************************************++
Routine Description:
Allocates and initializes a UL_APP_POOL_PROCESS object.
Arguments:
None
Return Value:
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)
{
RtlZeroMemory(pProcess, sizeof(UL_APP_POOL_PROCESS));
pProcess->Signature = UL_APP_POOL_PROCESS_POOL_TAG;
pProcess->pAppPool = pObject;
InitializeListHead(&pProcess->NewIrpHead);
InitializeListHead(&pProcess->PendingRequestHead);
//
// Remember the current process (WP).
//
pProcess->pProcess = PsGetCurrentProcess();
//
// Initialize list of WaitForDisconnect IRPs.
//
UlInitializeNotifyHead(&pProcess->WaitForDisconnectHead, NULL);
}
return pProcess;
} // UlCreateAppPoolProcess
/***************************************************************************++
Routine Description:
Destroys a UL_APP_POOL_PROCESS object.
Arguments:
pProcess - object to destory
Return Value:
None
--***************************************************************************/
VOID
UlCloseAppPoolProcess(
PUL_APP_POOL_PROCESS pProcess
)
{
//
// Sanity check.
//
PAGED_CODE();
ASSERT(IS_VALID_AP_PROCESS(pProcess));
ASSERT(pProcess->InCleanup);
ASSERT(IS_VALID_AP_OBJECT(pProcess->pAppPool));
WRITE_APP_POOL_TIME_TRACE_LOG(
pProcess->pAppPool,
pProcess,
APP_POOL_TIME_ACTION_DESTROY_APPOOL_PROCESS
);
//
// Drop the AppPool reference.
//
DEREFERENCE_APP_POOL(pProcess->pAppPool);
//
// Free the pool.
//
UL_FREE_POOL_WITH_SIG(pProcess, UL_APP_POOL_PROCESS_POOL_TAG);
} // UlCloseAppPoolProcess
/***************************************************************************++
Routine Description:
Cancels the pending user mode irp which was to receive demand start
notification. This routine ALWAYS results in the irp being completed.
Arguments:
pDeviceObject - the device object
pIrp - the irp to cancel
Return Value:
None
--***************************************************************************/
VOID
UlpCancelDemandStart(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
{
PUL_APP_POOL_OBJECT pAppPool;
PIO_STACK_LOCATION pIrpSp;
KLOCK_QUEUE_HANDLE LockHandle;
UNREFERENCED_PARAMETER(pDeviceObject);
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);
//
// Grab the app pool off the irp.
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pAppPool = (PUL_APP_POOL_OBJECT)
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
//
// Grab the lock protecting the queue'd irp.
//
UlAcquireInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
//
// Does it need to be dequeue'd?
//
if (pAppPool->pDemandStartIrp != NULL)
{
//
// Remove it.
//
pAppPool->pDemandStartIrp = NULL;
pAppPool->pDemandStartProcess = NULL;
}
//
// Let the lock go.
//
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
//
// Let our reference go.
//
DEREFERENCE_APP_POOL(pAppPool);
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
//
// Complete the irp.
//
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
} // UlpCancelDemandStart
/***************************************************************************++
Routine Description:
Cancels the pending user mode irp which was to receive the http request.
this routine ALWAYS results in the irp being completed.
Arguments:
pDeviceObject - the device object
pIrp - the irp to cancel
Return Value:
None
--***************************************************************************/
VOID
UlpCancelHttpReceive(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
{
PUL_APP_POOL_PROCESS pProcess;
PIO_STACK_LOCATION pIrpSp;
KLOCK_QUEUE_HANDLE LockHandle;
UNREFERENCED_PARAMETER(pDeviceObject);
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);
//
// Grab the app pool off the irp.
//
pIrpSp = IoGetCurrentIrpStackLocation(pIrp);
pProcess = (PUL_APP_POOL_PROCESS)
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer;
ASSERT(IS_VALID_AP_PROCESS(pProcess));
//
// Grab the lock protecting the queue.
//
UlAcquireInStackQueuedSpinLock(&pProcess->pAppPool->SpinLock, &LockHandle);
//
// 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.
//
UlReleaseInStackQueuedSpinLock(&pProcess->pAppPool->SpinLock, &LockHandle);
//
// Let our reference go.
//
pIrpSp->Parameters.DeviceIoControl.Type3InputBuffer = NULL;
DEREFERENCE_APP_POOL_PROCESS(pProcess);
//
// Complete the irp.
//
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
} // 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
Flags - flags for HttpReceiveHttpRequest
LockAcquired - either called from UlDeliverRequestToProcess (TRUE)
or UlReceiveHttpRequest/UlpFastReceiveHttpRequest
pBytesCopied - actual bytes copied
Return Value:
NTSTATUS - Completion status.
******************************************************************************/
NTSTATUS
UlCopyRequestToBuffer(
IN PUL_INTERNAL_REQUEST pRequest,
IN PUCHAR pKernelBuffer,
IN PVOID pUserBuffer,
IN ULONG BufferLength,
IN ULONG Flags,
IN BOOLEAN LockAcquired,
OUT PULONG pBytesCopied
)
{
PHTTP_REQUEST pHttpRequest;
PHTTP_UNKNOWN_HEADER pUserCurrentUnknownHeader;
HTTP_HEADER_ID HeaderId;
PUL_HTTP_UNKNOWN_HEADER pUnknownHeader;
PUCHAR pCurrentBufferPtr;
ULONG Index;
ULONG BytesCopied;
ULONG HeaderCount = 0;
PUCHAR pLocalAddress;
PUCHAR pRemoteAddress;
USHORT AddressType;
USHORT AddressLength;
USHORT AlignedAddressLength;
PHTTP_TRANSPORT_ADDRESS pAddress;
PHTTP_COOKED_URL pCookedUrl;
PHTTP_DATA_CHUNK pDataChunk;
PLIST_ENTRY pListEntry;
PEPROCESS pProcess;
NTSTATUS Status;
PUCHAR pEntityBody;
LONG EntityBodyLength;
PCWSTR pFullUrl;
PCWSTR pHost;
PCWSTR pAbsPath;
USHORT HostLength;
USHORT AbsPathLength;
HANDLE MappedToken = NULL;
//
// 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;
*pBytesCopied = 0;
__try
{
//
// Set up our pointers to the HTTP_REQUEST structure, the
// header arrays we're going to fill in, and the pointer to
// where we're going to start filling them in.
//
pHttpRequest = (PHTTP_REQUEST) pKernelBuffer;
AddressType = pRequest->pHttpConn->pConnection->AddressType;
if (TDI_ADDRESS_TYPE_IP == AddressType)
{
AddressLength = SOCKADDR_ADDRESS_LENGTH_IP;
}
else
if (TDI_ADDRESS_TYPE_IP6 == AddressType)
{
AddressLength = SOCKADDR_ADDRESS_LENGTH_IP6;
}
else
{
ASSERT(!"Invalid AddressType");
AddressLength = 0;
}
//
// We've allocated enough space for two SOCKADDR_IN6s, so use that.
//
AlignedAddressLength =
(USHORT) ALIGN_UP(SOCKADDR_ADDRESS_LENGTH_IP6, PVOID);
pLocalAddress = (PUCHAR) (pHttpRequest + 1);
pRemoteAddress = pLocalAddress + AlignedAddressLength;
pUserCurrentUnknownHeader =
(PHTTP_UNKNOWN_HEADER) (pRemoteAddress + AlignedAddressLength);
ASSERT((((ULONG_PTR) pUserCurrentUnknownHeader)
& (TYPE_ALIGNMENT(PVOID) - 1)) == 0);
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->BytesReceived = pRequest->BytesReceived;
pAddress = &pHttpRequest->Address;
pAddress->pRemoteAddress = FIXUP_PTR(
PVOID,
pUserBuffer,
pKernelBuffer,
pRemoteAddress,
BufferLength
);
CopyTdiAddrToSockAddr(
AddressType,
pRequest->pHttpConn->pConnection->RemoteAddress,
(struct sockaddr *) pRemoteAddress
);
pAddress->pLocalAddress = FIXUP_PTR(
PVOID,
pUserBuffer,
pKernelBuffer,
pLocalAddress,
BufferLength
);
CopyTdiAddrToSockAddr(
AddressType,
pRequest->pHttpConn->pConnection->LocalAddress,
(struct sockaddr *) pLocalAddress
);
//
// 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. Must be careful to put any computed values
// that are subsequently needed on the RHS of expressions into
// local stack variables before putting them into pCookedUrl.
// In other words, we must not commit the cardinal sin of reading
// from pCookedUrl after writing to it, because this is a buffer
// that the user can overwrite at any instant.
//
pCookedUrl = &pHttpRequest->CookedUrl;
pCookedUrl->FullUrlLength = (USHORT)(pRequest->CookedUrl.Length);
pFullUrl = FIXUP_PTR(
PCWSTR,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
pCookedUrl->pFullUrl = pFullUrl;
//
// And the host.
//
HostLength = DIFF_USHORT(
(PUCHAR) pRequest->CookedUrl.pAbsPath -
(PUCHAR) pRequest->CookedUrl.pHost
);
pCookedUrl->HostLength = HostLength;
pHost = pFullUrl +
DIFF_USHORT(pRequest->CookedUrl.pHost - pRequest->CookedUrl.pUrl);
pCookedUrl->pHost = pHost;
//
// Is there a query string?
//
if (pRequest->CookedUrl.pQueryString != NULL)
{
AbsPathLength = DIFF_USHORT(
(PUCHAR) pRequest->CookedUrl.pQueryString -
(PUCHAR) pRequest->CookedUrl.pAbsPath
);
pCookedUrl->AbsPathLength = AbsPathLength;
pAbsPath = pHost + (HostLength / sizeof(WCHAR));
pCookedUrl->pAbsPath = pAbsPath;
pCookedUrl->QueryStringLength =
(USHORT) (pRequest->CookedUrl.Length) -
DIFF_USHORT(
(PUCHAR) pRequest->CookedUrl.pQueryString -
(PUCHAR) pRequest->CookedUrl.pUrl
);
pCookedUrl->pQueryString =
pAbsPath + (AbsPathLength / sizeof(WCHAR));
}
else
{
pCookedUrl->AbsPathLength =
(USHORT) (pRequest->CookedUrl.Length) -
DIFF_USHORT(
(PUCHAR) pRequest->CookedUrl.pAbsPath -
(PUCHAR) pRequest->CookedUrl.pUrl
);
pCookedUrl->pAbsPath = pHost + (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 <= ANSI_STRING_MAX_CHAR_LEN);
pHttpRequest->UnknownVerbLength =
(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 <= ANSI_STRING_MAX_CHAR_LEN);
pHttpRequest->RawUrlLength = (USHORT) pRequest->RawUrl.Length;
pHttpRequest->pRawUrl = FIXUP_PTR(
PSTR,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
RtlCopyMemory(
pCurrentBufferPtr,
pRequest->RawUrl.pUrl,
pRequest->RawUrl.Length
);
BytesCopied = pRequest->RawUrl.Length;
pCurrentBufferPtr += BytesCopied;
//
// Terminate it.
//
((PSTR) pCurrentBufferPtr)[0] = ANSI_NULL;
pCurrentBufferPtr += sizeof(CHAR);
//
// Copy in the known headers.
//
// Loop through the known header array in the HTTP connection,
// and copy any that we have.
//
RtlZeroMemory(
pHttpRequest->Headers.KnownHeaders,
HttpHeaderRequestMaximum * sizeof(HTTP_KNOWN_HEADER)
);
for (Index = 0; Index < HttpHeaderRequestMaximum; Index++)
{
HeaderId = (HTTP_HEADER_ID) pRequest->HeaderIndex[Index];
if (HeaderId == HttpHeaderRequestMaximum)
{
break;
}
//
// Have a header here we need to copy in.
//
ASSERT(pRequest->HeaderValid[HeaderId]);
ASSERT(pRequest->Headers[HeaderId].HeaderLength
<= ANSI_STRING_MAX_CHAR_LEN);
//
// Ok for HeaderLength to be 0, we will give usermode a pointer
// pointing to a NULL string. RawValueLength will be 0.
//
pHttpRequest->Headers.KnownHeaders[HeaderId].RawValueLength =
(USHORT) (pRequest->Headers[HeaderId].HeaderLength * sizeof(CHAR));
pHttpRequest->Headers.KnownHeaders[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)
{
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 =
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);
//
// When a handling a request on a keepalive connection, it's
// possible that we might be running on system process's context.
// Therefore if we are copying over the user credentials we have
// to duplicate the token on target worker process but not on the
// system process again.
//
pProcess = pRequest->AppPool.pProcess->pProcess;
Status = UlGetSslInfo(
&pRequest->pHttpConn->pConnection->FilterInfo,
BufferLength - DIFF(pCurrentBufferPtr - pKernelBuffer),
FIXUP_PTR(
PUCHAR,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
),
pProcess,
pCurrentBufferPtr,
&MappedToken,
&BytesCopied
);
if (NT_SUCCESS(Status) && 0 != BytesCopied)
{
pHttpRequest->pSslInfo = FIXUP_PTR(
PHTTP_SSL_INFO,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
pCurrentBufferPtr += BytesCopied;
}
else
{
pHttpRequest->pSslInfo = NULL;
}
}
//
// Copy entity body.
//
if (pRequest->ContentLength > 0 || pRequest->Chunked == 1)
{
pEntityBody = (PUCHAR)ALIGN_UP_POINTER(pCurrentBufferPtr, PVOID);
EntityBodyLength = BufferLength - DIFF(pEntityBody - pKernelBuffer);
if ((Flags & HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY) &&
EntityBodyLength > 0 &&
EntityBodyLength > sizeof(HTTP_DATA_CHUNK) &&
pRequest->ChunkBytesToRead > 0 &&
pRequest->ChunkBytesRead < pRequest->ChunkBytesParsed)
{
pCurrentBufferPtr = pEntityBody;
//
// We at least have 1 byte space for entity body so copy it.
//
pHttpRequest->EntityChunkCount = 1;
pHttpRequest->pEntityChunks = FIXUP_PTR(
PHTTP_DATA_CHUNK,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
pDataChunk = (PHTTP_DATA_CHUNK)pCurrentBufferPtr;
pCurrentBufferPtr += sizeof(HTTP_DATA_CHUNK);
pDataChunk->DataChunkType = HttpDataChunkFromMemory;
pDataChunk->FromMemory.pBuffer = FIXUP_PTR(
PVOID,
pUserBuffer,
pKernelBuffer,
pCurrentBufferPtr,
BufferLength
);
//
// Need to take the HttpConnection lock if this is called
// from the receive I/O path, either fast or slow. The lock is
// already taken on the delivery path.
//
if (!LockAcquired)
{
UlAcquirePushLockExclusive(&pRequest->pHttpConn->PushLock);
}
BytesCopied = UlpCopyEntityBodyToBuffer(
pRequest,
pCurrentBufferPtr,
EntityBodyLength - sizeof(HTTP_DATA_CHUNK),
&pHttpRequest->Flags
);
if (!LockAcquired)
{
UlReleasePushLockExclusive(&pRequest->pHttpConn->PushLock);
}
if (BytesCopied)
{
pDataChunk->FromMemory.BufferLength = BytesCopied;
pCurrentBufferPtr += BytesCopied;
}
else
{
//
// Be nice and reset EntityChunkCount and pEntityChunks if
// UlpCopyEntityBodyToBuffer doesn't copy anything, usually
// indicating an error has been hit.
//
pHttpRequest->EntityChunkCount = 0;
pHttpRequest->pEntityChunks = NULL;
pHttpRequest->Flags =
HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS;
}
}
else
{
//
// Either the app doesn't ask for entity body or we have nothing
// or can't copy. Let ReceiveEntityBody handle this.
//
pHttpRequest->EntityChunkCount = 0;
pHttpRequest->pEntityChunks = NULL;
pHttpRequest->Flags = HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS;
}
}
else
{
//
// This request doesn't have entity bodies.
//
pHttpRequest->EntityChunkCount = 0;
pHttpRequest->pEntityChunks = NULL;
pHttpRequest->Flags = 0;
}
//
// Make sure we didn't use too much.
//
ASSERT(DIFF(pCurrentBufferPtr - pKernelBuffer) <= BufferLength);
*pBytesCopied = DIFF(pCurrentBufferPtr - pKernelBuffer);
}
__except( UL_EXCEPTION_FILTER() )
{
Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() );
}
TRACE_TIME(
pRequest->ConnectionId,
pRequest->RequestId,
TIME_ACTION_COPY_REQUEST
);
if (!NT_SUCCESS(Status) && MappedToken)
{
//
// The only reason we can fail after getting a MappedToken is because
// the code after that throws an exception, which is only possible
// if UlCopyRequestToBuffer is called from the fast I/O path, which
// guarantees we are the user's context.
//
ASSERT(g_pUlSystemProcess != (PKPROCESS)IoGetCurrentProcess());
ZwClose(MappedToken);
}
return Status;
} // UlCopyRequestToBuffer
/******************************************************************************
Routine Description:
Copy as much as entity body as possible to the buffer provided.
Arguments:
pRequest - the request to copy the entity body from
pBuffer - the buffer to copy the entity body
BufferLength - the length of the buffer for the maximum we can copy
pFlags - tells if there are still more entity bodies
Return Value:
Total number of bytes of entity body being copied
******************************************************************************/
ULONG
UlpCopyEntityBodyToBuffer(
IN PUL_INTERNAL_REQUEST pRequest,
IN PUCHAR pEntityBody,
IN ULONG EntityBodyLength,
OUT PULONG pFlags
)
{
ULONGLONG ChunkBytesRead = pRequest->ChunkBytesRead;
ULONG TotalBytesRead;
NTSTATUS Status;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
ASSERT(NULL != pEntityBody);
ASSERT(EntityBodyLength > 0);
ASSERT(NULL != pFlags);
ASSERT(UlDbgPushLockOwnedExclusive(&pRequest->pHttpConn->PushLock));
UlTrace(ROUTING, (
"http!UlpCopyEntityBodyToBuffer"
" pRequest = %p, pEntityBody = %p, EntityBodyLength = %d\n",
pRequest,
pEntityBody,
EntityBodyLength
));
//
// UlProcessBufferQueue needs to be called in a try/except because
// pEntityBody can be user mode memory address when called from the
// fast receive path.
//
__try
{
UlProcessBufferQueue(pRequest, pEntityBody, EntityBodyLength);
}
__except(UL_EXCEPTION_FILTER())
{
Status = UL_CONVERT_EXCEPTION_CODE(GetExceptionCode());
return 0;
}
if (pRequest->ParseState > ParseEntityBodyState &&
pRequest->ChunkBytesRead == pRequest->ChunkBytesParsed)
{
*pFlags = 0;
}
else
{
*pFlags = HTTP_REQUEST_FLAG_MORE_ENTITY_BODY_EXISTS;
}
TotalBytesRead = (ULONG)(pRequest->ChunkBytesRead - ChunkBytesRead);
ASSERT(TotalBytesRead <= EntityBodyLength);
return TotalBytesRead;
} // UlpCopyEntityBodyToBuffer
/******************************************************************************
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
pRequest - the request that needs to pop 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,
IN PUL_INTERNAL_REQUEST pRequest,
OUT PUL_APP_POOL_PROCESS * ppProcess
)
{
PUL_APP_POOL_PROCESS pProcess;
PIRP pIrp = NULL;
PLIST_ENTRY pEntry;
//
// Sanity check.
//
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
ASSERT(UlDbgSpinLockOwned(&pAppPool->SpinLock));
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, pRequest);
//
// Did we find one?
//
if (pIrp != NULL)
{
//
// Save a pointer to the process.
//
*ppProcess = pProcess;
//
// Move the process to the end of the line
// so that other processes get a chance
// to process requests.
//
RemoveEntryList(pEntry);
InsertTailList(&(pAppPool->ProcessListHead), pEntry);
break;
}
//
// Keep looking - move on to the next process entry.
//
pEntry = pProcess->ListEntry.Flink;
}
return pIrp;
} // UlpPopNewIrp
/***************************************************************************++
Routine Description:
Pulls an IRP off the given processes queue if there is one.
Arguments:
pProcess - a pointer to the process to search
pRequest - the request to pop the irp for
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,
IN PUL_INTERNAL_REQUEST pRequest
)
{
PUL_APP_POOL_PROCESS pAppPoolProcess;
PLIST_ENTRY pEntry;
PIRP pIrp = NULL;
NTSTATUS Status;
//
// Sanity check.
//
ASSERT(UlDbgSpinLockOwned(&pProcess->pAppPool->SpinLock));
ASSERT(IS_VALID_AP_PROCESS(pProcess));
if (!IsListEmpty(&pProcess->NewIrpHead))
{
pEntry = RemoveHeadList(&pProcess->NewIrpHead);
//
// 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).
//
pAppPoolProcess = (PUL_APP_POOL_PROCESS)
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer;
ASSERT(pAppPoolProcess == pProcess);
DEREFERENCE_APP_POOL_PROCESS(pAppPoolProcess);
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
pIrp->IoStatus.Status = STATUS_CANCELLED;
pIrp->IoStatus.Information = 0;
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
pIrp = NULL;
}
else
{
//
// We are free to use this irp!
//
pAppPoolProcess = (PUL_APP_POOL_PROCESS)
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer;
ASSERT(pAppPoolProcess == pProcess);
DEREFERENCE_APP_POOL_PROCESS(pAppPoolProcess);
IoGetCurrentIrpStackLocation(pIrp)->
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
//
// Queue the request to the process's pending queue, and if we
// can't, complete the IRP with error status.
//
Status = UlpQueuePendingRequest(pProcess, pRequest);
if (!NT_SUCCESS(Status))
{
pIrp->IoStatus.Status = Status;
pIrp->IoStatus.Information = 0;
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
pIrp = NULL;
}
}
}
return pIrp;
} // UlpPopIrpFromProcess
/***************************************************************************++
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 Value:
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.
//
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
ASSERT(UlDbgSpinLockOwned(&pAppPool->SpinLock));
//
// 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;
}
}
return Found;
} // UlpIsProcessInAppPool
/***************************************************************************++
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
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpQueueUnboundRequest(
IN PUL_APP_POOL_OBJECT pAppPool,
IN PUL_INTERNAL_REQUEST pRequest
)
{
NTSTATUS Status;
PUL_TIMEOUT_INFO_ENTRY pTimeoutInfo;
//
// Sanity check.
//
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
ASSERT(UlDbgSpinLockOwned(&pAppPool->SpinLock));
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
//
// Add the request to the NewRequestQueue.
//
Status = UlpQueueRequest(pAppPool, &pAppPool->NewRequestHead, pRequest);
//
// If it's in, change the queue state.
//
if (NT_SUCCESS(Status))
{
pRequest->AppPool.QueueState = QueueDeliveredState;
//
// Turn the connection idle timer back on so that it won't stay
// on the queue forever and will also get purged under low
// resource conditions.
//
pTimeoutInfo = &pRequest->pHttpConn->TimeoutInfo;
UlAcquireSpinLockAtDpcLevel(&pTimeoutInfo->Lock);
if (UlIsConnectionTimerOff(pTimeoutInfo, TimerAppPool))
{
UlSetConnectionTimer(pTimeoutInfo, TimerAppPool);
}
UlReleaseSpinLockFromDpcLevel(&pTimeoutInfo->Lock);
UlEvaluateTimerState(pTimeoutInfo);
}
else
{
//
// The queue is too full, return an error to the client.
//
UlTrace(ROUTING, (
"http!UlpQueueUnboundRequest(pAppPool = %p, pRequest = %p)\n"
" Rejecting request. AppPool Queue is full (%d items)\n",
pAppPool,
pRequest,
pAppPool->RequestCount
));
UlSetErrorCode( pRequest, UlErrorAppPoolQueueFull, pAppPool); // 503
}
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
RequestBufferLength - the optional buffer length for the request
pRequest - the request pointer to receive the request
Return Value:
Pointer to an HTTP_REQUEST if one is found or NULL otherwise
--***************************************************************************/
NTSTATUS
UlDequeueNewRequest(
IN PUL_APP_POOL_PROCESS pProcess,
IN ULONG RequestBufferLength,
OUT PUL_INTERNAL_REQUEST * pNewRequest
)
{
PLIST_ENTRY pEntry;
PUL_INTERNAL_REQUEST pRequest = NULL;
PUL_APP_POOL_OBJECT pAppPool;
PUL_TIMEOUT_INFO_ENTRY pTimeoutInfo;
NTSTATUS Status = STATUS_NOT_FOUND;
PUL_APP_POOL_PROCESS pProcBinding;
ULONG BytesNeeded;
//
// Sanity check.
//
ASSERT(UlDbgSpinLockOwned(&pProcess->pAppPool->SpinLock));
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
ASSERT(IS_VALID_AP_PROCESS(pProcess));
*pNewRequest = NULL;
pAppPool = pProcess->pAppPool;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
//
// Find a usable request.
//
pEntry = pAppPool->NewRequestHead.Flink;
while (pEntry != &pAppPool->NewRequestHead)
{
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 value 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)
{
//
// Let us check if this request can fit into a buffer with
// RequestBufferLength. Only check this if requested.
//
if (RequestBufferLength)
{
Status = UlComputeRequestBytesNeeded(pRequest, &BytesNeeded);
if (!NT_SUCCESS(Status))
{
return Status;
}
else
if (BytesNeeded > RequestBufferLength)
{
return STATUS_BUFFER_TOO_SMALL;
}
}
//
// We will return STATUS_SUCCESS from here on.
//
Status = STATUS_SUCCESS;
//
// Remove the request from the AppPool's NewRequestQueue and insert
// it to the process's PendingRequestQueue. There is no change in
// the queue limit.
//
RemoveEntryList(&pRequest->AppPool.AppPoolEntry);
InsertTailList(
&pProcess->PendingRequestHead,
&pRequest->AppPool.AppPoolEntry
);
//
// 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 process to ensure it stays around during
// the send for the memory we lock.
//
REFERENCE_APP_POOL_PROCESS(pProcess);
//
// 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);
//
// Stop the connection idle timer after the request is delivered.
//
pTimeoutInfo = &pRequest->pHttpConn->TimeoutInfo;
UlAcquireSpinLockAtDpcLevel(&pTimeoutInfo->Lock);
UlResetConnectionTimer(pTimeoutInfo, TimerAppPool);
UlReleaseSpinLockFromDpcLevel(&pTimeoutInfo->Lock);
UlEvaluateTimerState(pTimeoutInfo);
}
*pNewRequest = pRequest;
return Status;
} // UlDequeueNewRequest
/***************************************************************************++
Routine Description:
Put the request back from the process's pending request queue to the
AppPool's new request queue.
Arguments:
pProcess - the process that will dequeue the request
pRequest - the request to dequeue
Return Value:
None
--***************************************************************************/
VOID
UlRequeuePendingRequest(
IN PUL_APP_POOL_PROCESS pProcess,
IN PUL_INTERNAL_REQUEST pRequest
)
{
KLOCK_QUEUE_HANDLE LockHandle;
PUL_TIMEOUT_INFO_ENTRY pTimeoutInfo;
UlAcquireInStackQueuedSpinLock(
&pProcess->pAppPool->SpinLock,
&LockHandle
);
if (!pProcess->InCleanup &&
QueueCopiedState == pRequest->AppPool.QueueState)
{
//
// Unset the request's AppPool info.
//
ASSERT( pRequest->AppPool.pProcess == pProcess );
DEREFERENCE_APP_POOL_PROCESS(pProcess);
pRequest->AppPool.pProcess = NULL;
pRequest->AppPool.QueueState = QueueDeliveredState;
//
// Move the request back from the process's pending queue to the
// AppPool's new request queue. This doesn't affect queue count.
//
RemoveEntryList(&pRequest->AppPool.AppPoolEntry);
InsertHeadList(
&pProcess->pAppPool->NewRequestHead,
&pRequest->AppPool.AppPoolEntry
);
//
// Lastly, we have to turn back on the idle timer.
//
pTimeoutInfo = &pRequest->pHttpConn->TimeoutInfo;
UlAcquireSpinLockAtDpcLevel(&pTimeoutInfo->Lock);
if (UlIsConnectionTimerOff(pTimeoutInfo, TimerConnectionIdle))
{
UlSetConnectionTimer(pTimeoutInfo, TimerConnectionIdle);
}
UlReleaseSpinLockFromDpcLevel(&pTimeoutInfo->Lock);
UlEvaluateTimerState(pTimeoutInfo);
}
UlReleaseInStackQueuedSpinLock(
&pProcess->pAppPool->SpinLock,
&LockHandle
);
} // UlRequeuePendingRequest
/***************************************************************************++
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
Return Value:
None
--***************************************************************************/
VOID
UlpUnbindQueuedRequests(
IN PUL_APP_POOL_PROCESS pProcess
)
{
PLIST_ENTRY pEntry;
PUL_INTERNAL_REQUEST pRequest = NULL;
PUL_APP_POOL_OBJECT pAppPool;
PUL_APP_POOL_PROCESS pProcBinding;
//
// Sanity check.
//
ASSERT(IS_VALID_AP_PROCESS(pProcess));
ASSERT(UlDbgSpinLockOwned(&pProcess->pAppPool->SpinLock));
pAppPool = pProcess->pAppPool;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
//
// Find a bound request.
//
pEntry = pAppPool->NewRequestHead.Flink;
while (pEntry != &pAppPool->NewRequestHead)
{
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, 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
);
}
}
} // UlpUnbindQueuedRequests
/***************************************************************************++
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
Return Value:
None
--***************************************************************************/
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,
NULL
);
//
// Remove the extra reference added in UlpUnbindQueuedRequests.
//
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
} // UlpRedeliverRequestWorker
/***************************************************************************++
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
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpSetAppPoolQueueLength(
IN PUL_APP_POOL_PROCESS pProcess,
IN ULONG QueueLength
)
{
PUL_APP_POOL_OBJECT pAppPool;
KLOCK_QUEUE_HANDLE LockHandle;
pAppPool = pProcess->pAppPool;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
//
// Set the new value.
//
UlAcquireInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
pAppPool->MaxRequests = QueueLength;
UlReleaseInStackQueuedSpinLock(&pAppPool->SpinLock, &LockHandle);
UlTrace(ROUTING, (
"http!UlpSetAppPoolQueueLength(pProcess = %p, QueueLength = %ld)\n"
" pAppPool = %p (%ws), Status = 0x%08x\n",
pProcess,
QueueLength,
pAppPool,
pAppPool->pName,
STATUS_SUCCESS
));
return STATUS_SUCCESS;
} // UlpSetAppPoolQueueLength
/******************************************************************************
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:
None
******************************************************************************/
VOID
UlCopyRequestToIrp(
IN PUL_INTERNAL_REQUEST pRequest,
IN PIRP pIrp,
IN BOOLEAN CompleteIrp,
IN BOOLEAN LockAcquired
)
{
NTSTATUS Status;
PIO_STACK_LOCATION pIrpSp = NULL;
ULONG BytesNeeded;
ULONG BytesCopied;
PUCHAR pKernelBuffer;
PVOID pUserBuffer;
PHTTP_RECEIVE_REQUEST_INFO pRequestInfo;
PHTTP_REQUEST pUserRequest;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
ASSERT(pIrp != NULL);
ASSERT(NULL != pIrp->MdlAddress);
//
// 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 = UlComputeRequestBytesNeeded(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.
//
ASSERT(!(((ULONG_PTR) pKernelBuffer) & (TYPE_ALIGNMENT(PVOID) - 1)));
pUserBuffer = MmGetMdlVirtualAddress(pIrp->MdlAddress);
ASSERT(pUserBuffer != NULL);
pRequestInfo =
(PHTTP_RECEIVE_REQUEST_INFO)pIrp->AssociatedIrp.SystemBuffer;
//
// This request will fit in this buffer, so copy it.
//
Status = UlCopyRequestToBuffer(
pRequest,
pKernelBuffer,
pUserBuffer,
pIrpSp->Parameters.DeviceIoControl.OutputBufferLength,
pRequestInfo->Flags,
LockAcquired,
&BytesCopied
);
if (NT_SUCCESS(Status))
{
pIrp->IoStatus.Information = BytesCopied;
}
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))
{
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.c.
//
ASSERT(FALSE);
pIrp->IoStatus.Information = 0;
}
}
complete:
UlTrace(ROUTING, (
"http!UlCopyRequestToIrp(\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 = %Iu\n",
pRequest,
pIrp,
pRequest->ConfigInfo.pAppPool,
pRequest->ConfigInfo.pAppPool->pName,
pRequest->ConnectionId,
pIrpSp ? pIrpSp->Parameters.DeviceIoControl.OutputBufferLength : 0,
Status,
pIrp->IoStatus.Information
));
pIrp->IoStatus.Status = Status;
//
// Complete the irp.
//
if (CompleteIrp)
{
//
// Use IO_NO_INCREMENT to avoid the work thread being rescheduled.
//
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
}
} // UlCopyRequestToIrp
/******************************************************************************
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
AccessMode - KernelMode or UserMode
ppAppPool - returns the apool object the handle represented.
Return Value:
NTSTATUS - Completion status.
******************************************************************************/
NTSTATUS
UlGetPoolFromHandle(
IN HANDLE AppPool,
IN KPROCESSOR_MODE AccessMode,
OUT PUL_APP_POOL_OBJECT * ppAppPool
)
{
NTSTATUS Status;
PFILE_OBJECT pFileObject = NULL;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(ppAppPool != NULL);
Status = ObReferenceObjectByHandle(
AppPool,
FILE_READ_ACCESS, // DesiredAccess
*IoFileObjectType, // ObjectType
AccessMode, // AccessMode
(PVOID *) &pFileObject, // Object
NULL // HandleInformation
);
if (NT_SUCCESS(Status) == FALSE)
{
goto end;
}
if (IS_APP_POOL_FO(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;
} // UlGetPoolFromHandle
/******************************************************************************
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:
NTSTATUS - Completion status.
******************************************************************************/
NTSTATUS
UlpQueuePendingRequest(
IN PUL_APP_POOL_PROCESS pProcess,
IN PUL_INTERNAL_REQUEST pRequest
)
{
NTSTATUS Status;
//
// Sanity check.
//
ASSERT(IS_VALID_AP_PROCESS(pProcess));
ASSERT(UlDbgSpinLockOwned(&pProcess->pAppPool->SpinLock));
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
//
// Put it on the list.
//
ASSERT(pRequest->AppPool.AppPoolEntry.Flink == NULL);
Status = UlpQueueRequest(
pProcess->pAppPool,
&pProcess->PendingRequestHead,
pRequest
);
if (NT_SUCCESS(Status))
{
//
// 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;
//
// Add a reference to the process to ensure it stays around during
// the send for the memory we lock.
//
REFERENCE_APP_POOL_PROCESS(pProcess);
}
return Status;
} // UlpQueuePendingRequest
/***************************************************************************++
Routine Description:
Adds a request to the tail of the queue.
App Pool queue lock must be held.
Arguments:
pAppPool - the AppPool to add
pQueueList - the queue list
pRequest - the request to be added
Return Value:
NTSTATUS - Completion status.
--***************************************************************************/
NTSTATUS
UlpQueueRequest(
IN PUL_APP_POOL_OBJECT pAppPool,
IN PLIST_ENTRY pQueueList,
IN PUL_INTERNAL_REQUEST pRequest
)
{
LONG GlobalRequests;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
ASSERT(UlDbgSpinLockOwned(&pAppPool->SpinLock));
ASSERT(pQueueList);
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
//
// See if we've exceeded the global limit on requests queued and
// the queue's limits.
//
GlobalRequests = InterlockedIncrement(&g_RequestsQueued);
ASSERT(GlobalRequests > 0);
if ((ULONG) GlobalRequests > g_UlMaxRequestsQueued ||
pAppPool->RequestCount >= pAppPool->MaxRequests)
{
InterlockedDecrement(&g_RequestsQueued);
return STATUS_INVALID_DEVICE_STATE;
}
//
// Add to the end of the queue.
//
InsertTailList(pQueueList, &pRequest->AppPool.AppPoolEntry);
pAppPool->RequestCount++;
ASSERT(pAppPool->RequestCount >= 1);
return STATUS_SUCCESS;
} // UlpQueueRequest
/***************************************************************************++
Routine Description:
Removes a particular request from the queue.
App Pool queue lock must be held.
Arguments:
pAppPool - the AppPool to remove the request from
pRequest - the request to be removed
Return Value:
None
--***************************************************************************/
VOID
UlpRemoveRequest(
IN PUL_APP_POOL_OBJECT pAppPool,
IN PUL_INTERNAL_REQUEST pRequest
)
{
LONG GlobalRequests;
ASSERT(UlDbgSpinLockOwned(&pAppPool->SpinLock));
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
ASSERT(NULL != pRequest->AppPool.AppPoolEntry.Flink);
ASSERT(pAppPool->RequestCount > 0);
RemoveEntryList(&pRequest->AppPool.AppPoolEntry);
pRequest->AppPool.AppPoolEntry.Flink = NULL;
pRequest->AppPool.AppPoolEntry.Blink = NULL;
pAppPool->RequestCount--;
GlobalRequests = InterlockedDecrement(&g_RequestsQueued);
ASSERT(GlobalRequests >= 0);
} // UlpRemoveRequest
/***************************************************************************++
Routine Description:
Removes a request from the head of a queue if there is one.
App Pool queue lock must be held.
Arguments:
pAppPool - the AppPool to dequeue the request from
pQueueList - the queue list
Return values:
Pointer to the request or NULL if the queue is empty.
--***************************************************************************/
PUL_INTERNAL_REQUEST
UlpDequeueRequest(
IN PUL_APP_POOL_OBJECT pAppPool,
IN PLIST_ENTRY pQueueList
)
{
PLIST_ENTRY pEntry;
PUL_INTERNAL_REQUEST pRequest = NULL;
LONG GlobalRequests;
ASSERT(IS_VALID_AP_OBJECT(pAppPool));
ASSERT(UlDbgSpinLockOwned(&pAppPool->SpinLock));
ASSERT(pQueueList);
if (!IsListEmpty(pQueueList))
{
pEntry = RemoveHeadList(pQueueList);
pEntry->Flink = pEntry->Blink = NULL;
pAppPool->RequestCount--;
pRequest = CONTAINING_RECORD(
pEntry,
UL_INTERNAL_REQUEST,
AppPool.AppPoolEntry
);
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
pRequest->AppPool.QueueState = QueueUnlinkedState;
GlobalRequests = InterlockedDecrement(&g_RequestsQueued);
ASSERT(GlobalRequests >= 0);
}
return pRequest;
} // UlpDequeueRequest
/***************************************************************************++
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;
//
// Acquire the lock protecting the disconnect data and determine
// if we should queue the IRP or complete it immediately.
//
UlAcquirePushLockExclusive(&pHttpConn->PushLock);
//
// WaitForDisconnect is allowed only for the process that picked up
// the current request.
//
if (!pHttpConn->pRequest ||
pHttpConn->pRequest->AppPool.pProcess != pProcess)
{
UlReleasePushLockExclusive(&pHttpConn->PushLock);
return STATUS_INVALID_ID_AUTHORITY;
}
if (pHttpConn->DisconnectFlag)
{
//
// Connection already disconnected, complete the IRP immediately.
//
UlReleasePushLockExclusive(&pHttpConn->PushLock);
IoMarkIrpPending(pIrp);
pIrp->IoStatus.Status = STATUS_SUCCESS;
UlCompleteRequest(pIrp, IO_NO_INCREMENT);
return STATUS_PENDING;
}
//
// Allocate an object to associate the IRP with the connection
// and the app pool.
//
pDisconnectObj = UlpCreateDisconnectObject(pIrp);
if (!pDisconnectObj)
{
UlReleasePushLockExclusive(&pHttpConn->PushLock);
return STATUS_NO_MEMORY;
}
UlAcquireResourceExclusive(&g_pUlNonpagedData->DisconnectResource, TRUE);
//
// 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 end;
}
else
{
//
// We have to cancel it ourselves, so we'll just complete
// the IRP immediately with STATUS_CANCELLED.
//
Status = STATUS_CANCELLED;
goto end;
}
}
//
// We have queued at least one WaitForDisconnect IRP.
//
pHttpConn->WaitForDisconnectFlag = 1;
//
// 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);
UlReleasePushLockExclusive(&pHttpConn->PushLock);
return STATUS_PENDING;
end:
UlUnmarkIrpPending(pIrp);
UlReleaseResource(&g_pUlNonpagedData->DisconnectResource);
UlReleasePushLockExclusive(&pHttpConn->PushLock);
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
Return Value:
None
--***************************************************************************/
VOID
UlpCancelWaitForDisconnect(
IN PDEVICE_OBJECT pDeviceObject,
IN PIRP pIrp
)
{
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
UNREFERENCED_PARAMETER(pDeviceObject);
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
);
} // UlpCancelWaitForDisconnect
/***************************************************************************++
Routine Description:
Actually performs the cancel for the irp.
Arguments:
pWorkItem - the work item to process
Return Value:
None
--***************************************************************************/
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, IO_NO_INCREMENT);
} // UlpCancelWaitForDisconnectWorker
/***************************************************************************++
Routine Description:
Completes all WaitForDisconnect IRPs attached to an http connection
has been disconnected.
Arguments:
pHttpConnection - the connection that's disconnected
Return Value:
None
--***************************************************************************/
VOID
UlCompleteAllWaitForDisconnect(
IN PUL_HTTP_CONNECTION pHttpConnection
)
{
NTSTATUS Status = STATUS_SUCCESS;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConnection));
UlAcquireResourceExclusive(&g_pUlNonpagedData->DisconnectResource, TRUE);
//
// Complete any pending "wait for disconnect" IRPs.
//
UlNotifyAllEntries(
&UlpNotifyCompleteWaitForDisconnect,
&pHttpConnection->WaitForDisconnectHead,
&Status
);
UlReleaseResource(&g_pUlNonpagedData->DisconnectResource);
} // UlCompleteAllWaitForDisconnect
/***************************************************************************++
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
pStatus - pointer to an NTSTATUS to be returned
Return Value:
None
--***************************************************************************/
BOOLEAN
UlpNotifyCompleteWaitForDisconnect(
IN PUL_NOTIFY_ENTRY pEntry,
IN PVOID pHost,
IN PVOID pStatus
)
{
PUL_DISCONNECT_OBJECT pDisconnectObj;
PIRP pIrp;
PDRIVER_CANCEL pCancelRoutine;
//
// Sanity check.
//
PAGED_CODE();
ASSERT(pEntry);
ASSERT(pHost);
ASSERT(pStatus);
ASSERT(UlDbgResourceOwnedExclusive(&g_pUlNonpagedData->DisconnectResource));
UNREFERENCED_PARAMETER(pEntry);
pDisconnectObj = (PUL_DISCONNECT_OBJECT) pHost;
ASSERT(IS_VALID_DISCONNECT_OBJECT(pDisconnectObj));
//
// Locate and try to 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
{
//
// Remove object from lists.
//
UlRemoveNotifyEntry(&pDisconnectObj->ConnectionEntry);
UlRemoveNotifyEntry(&pDisconnectObj->ProcessEntry);
//
// Complete the IRP, then free the disconnect object.
//
pIrp->IoStatus.Status = *((PNTSTATUS) pStatus);
pIrp->IoStatus.Information = 0;
UlCompleteRequest(pIrp, IO_NETWORK_INCREMENT);
UlpFreeDisconnectObject(pDisconnectObj);
}
return TRUE;
} // UlpNotifyCompleteWaitForDisconnect
/***************************************************************************++
Routine Description:
Allocates and initializes a disconnect object.
Arguments:
pIrp - a UlWaitForDisconnect IRP
Return Value:
PUL_DISCONNECT_OBJECT
--***************************************************************************/
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;
} // UlpCreateDisconnectObject
/***************************************************************************++
Routine Description:
Gets rid of a disconnect object.
Arguments:
pObject - the disconnect object to free
Return Value:
None
--***************************************************************************/
VOID
UlpFreeDisconnectObject(
IN PUL_DISCONNECT_OBJECT pObject
)
{
UL_FREE_POOL_WITH_SIG(pObject, UL_DISCONNECT_OBJECT_POOL_TAG);
} // UlpFreeDisconnectObject