mirror of https://github.com/tongzx/nt5src
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.
2286 lines
54 KiB
2286 lines
54 KiB
/*++
|
|
|
|
Copyright (c) 1998-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
httpconn.cxx
|
|
|
|
Abstract:
|
|
|
|
This module implements the HTTP_CONNECTION object.
|
|
|
|
Author:
|
|
|
|
Keith Moore (keithmo) 08-Jul-1998
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
#include "httpconnp.h"
|
|
|
|
|
|
//
|
|
// Private globals.
|
|
//
|
|
|
|
//
|
|
// Global connection Limits stuff
|
|
//
|
|
|
|
ULONG g_MaxConnections = HTTP_LIMIT_INFINITE;
|
|
ULONG g_CurrentConnections = 0;
|
|
BOOLEAN g_InitGlobalConnections = FALSE;
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#endif // ALLOC_PRAGMA
|
|
|
|
#if 0
|
|
NOT PAGEABLE -- UlBindConnectionToProcess
|
|
NOT PAGEABLE -- UlQueryProcessBinding
|
|
NOT PAGEABLE -- UlpCreateProcBinding
|
|
NOT PAGEABLE -- UlpFreeProcBinding
|
|
NOT PAGEABLE -- UlpFindProcBinding
|
|
|
|
NOT PAGEABLE -- UlCreateHttpConnection
|
|
NOT PAGEABLE -- UlReferenceHttpConnection
|
|
NOT PAGEABLE -- UlDereferenceHttpConnection
|
|
|
|
NOT PAGEABLE -- UlReferenceHttpRequest
|
|
NOT PAGEABLE -- UlDereferenceHttpRequest
|
|
NOT PAGEABLE -- UlpCreateHttpRequest
|
|
NOT PAGEABLE -- UlpFreeHttpRequest
|
|
#endif
|
|
|
|
//
|
|
// Public functions.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Creates a new HTTP_CONNECTION object.
|
|
|
|
Arguments:
|
|
|
|
ppHttpConnection - Receives a pointer to the new HTTP_CONNECTION
|
|
object if successful.
|
|
|
|
pConnection - Supplies a pointer to the UL_CONNECTION to associate
|
|
with the newly created HTTP_CONNECTION.
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlCreateHttpConnection(
|
|
OUT PUL_HTTP_CONNECTION *ppHttpConnection,
|
|
IN PUL_CONNECTION pConnection
|
|
)
|
|
{
|
|
PUL_HTTP_CONNECTION pHttpConnection;
|
|
NTSTATUS Status;
|
|
|
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|
|
|
pHttpConnection = &pConnection->HttpConnection;
|
|
|
|
if (pHttpConnection != NULL)
|
|
{
|
|
pHttpConnection->Signature = UL_HTTP_CONNECTION_POOL_TAG;
|
|
pHttpConnection->RefCount = 1;
|
|
pHttpConnection->pConnection = pConnection;
|
|
REFERENCE_CONNECTION( pConnection );
|
|
pHttpConnection->SecureConnection = pConnection->FilterInfo.SecureConnection;
|
|
|
|
HTTP_SET_NULL_ID(&pHttpConnection->ConnectionId);
|
|
|
|
pHttpConnection->SendBufferedBytes = 0;
|
|
pHttpConnection->NextRecvNumber = 0;
|
|
pHttpConnection->NextBufferNumber = 0;
|
|
pHttpConnection->NextBufferToParse = 0;
|
|
pHttpConnection->pRequest = NULL;
|
|
pHttpConnection->pCurrentBuffer = NULL;
|
|
pHttpConnection->pConnectionCountEntry = NULL;
|
|
pHttpConnection->pPrevSiteCounters = NULL;
|
|
|
|
//
|
|
// Init connection timeout info block; implicitly starts
|
|
// ConnectionIdle timer.
|
|
//
|
|
|
|
UlInitializeConnectionTimerInfo( &pHttpConnection->TimeoutInfo );
|
|
|
|
//
|
|
// Initialize the disconnect management fields.
|
|
//
|
|
|
|
pHttpConnection->NeedMoreData = 0;
|
|
pHttpConnection->UlconnDestroyed = 0;
|
|
pHttpConnection->WaitingForResponse = 0;
|
|
pHttpConnection->DisconnectFlag = FALSE;
|
|
|
|
UlInitializeNotifyHead(
|
|
&pHttpConnection->WaitForDisconnectHead,
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// Init our buffer list
|
|
//
|
|
|
|
InitializeListHead(&(pHttpConnection->BufferHead));
|
|
|
|
//
|
|
// Init QoS parameters on the connection
|
|
//
|
|
|
|
pHttpConnection->BandwidthThrottlingEnabled = 0;
|
|
pHttpConnection->pFlow = NULL;
|
|
pHttpConnection->pFilter = NULL;
|
|
|
|
//
|
|
// init app pool process binding list
|
|
//
|
|
|
|
InitializeListHead(&(pHttpConnection->BindingHead));
|
|
|
|
UlInitializeSpinLock(
|
|
&pHttpConnection->BindingSpinLock,
|
|
"BindingSpinLock"
|
|
);
|
|
|
|
Status = UlInitializeResource(
|
|
&(pHttpConnection->Resource),
|
|
"UL_HTTP_CONNECTION[%p].Resource",
|
|
pHttpConnection,
|
|
UL_HTTP_CONNECTION_RESOURCE_TAG
|
|
);
|
|
|
|
if (NT_SUCCESS(Status) == FALSE)
|
|
{
|
|
RETURN(Status);
|
|
}
|
|
|
|
//
|
|
// Initialize BufferingInfo structure.
|
|
//
|
|
|
|
UlInitializeSpinLock(
|
|
&pHttpConnection->BufferingInfo.BufferingSpinLock,
|
|
"BufferingSpinLock"
|
|
);
|
|
|
|
pHttpConnection->BufferingInfo.BytesBuffered = 0;
|
|
pHttpConnection->BufferingInfo.TransportBytesNotTaken = 0;
|
|
pHttpConnection->BufferingInfo.DrainAfterDisconnect = FALSE;
|
|
pHttpConnection->BufferingInfo.ReadIrpPending = FALSE;
|
|
|
|
pHttpConnection->pRequestIdContext = NULL;
|
|
|
|
UlInitializeSpinLock(
|
|
&pHttpConnection->RequestIdSpinLock,
|
|
"RequestIdSpinLock"
|
|
);
|
|
|
|
UlTrace(
|
|
REFCOUNT, (
|
|
"ul!UlCreateHttpConnection conn=%p refcount=%d\n",
|
|
pHttpConnection,
|
|
pHttpConnection->RefCount)
|
|
);
|
|
|
|
*ppHttpConnection = pHttpConnection;
|
|
RETURN(STATUS_SUCCESS);
|
|
}
|
|
|
|
RETURN(STATUS_INSUFFICIENT_RESOURCES);
|
|
|
|
} // UlCreateHttpConnection
|
|
|
|
NTSTATUS
|
|
UlCreateHttpConnectionId(
|
|
IN PUL_HTTP_CONNECTION pHttpConnection
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Create an opaque id for the connection
|
|
//
|
|
|
|
Status = UlAllocateOpaqueId(
|
|
&pHttpConnection->ConnectionId, // pOpaqueId
|
|
UlOpaqueIdTypeHttpConnection, // OpaqueIdType
|
|
pHttpConnection // pContext
|
|
);
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
UL_REFERENCE_HTTP_CONNECTION(pHttpConnection);
|
|
|
|
TRACE_TIME(
|
|
pHttpConnection->ConnectionId,
|
|
0,
|
|
TIME_ACTION_CREATE_CONNECTION
|
|
);
|
|
}
|
|
|
|
RETURN(Status);
|
|
|
|
} // UlCreateHttpConnectionId
|
|
|
|
VOID
|
|
UlConnectionDestroyedWorker(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
)
|
|
{
|
|
PUL_HTTP_CONNECTION pHttpConnection;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
pHttpConnection = CONTAINING_RECORD(
|
|
pWorkItem,
|
|
UL_HTTP_CONNECTION,
|
|
WorkItem
|
|
);
|
|
|
|
ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConnection));
|
|
|
|
UlTrace(HTTP_IO, (
|
|
"ul!UlConnectionDestroyedWorker: httpconn=%p\n",
|
|
pHttpConnection
|
|
));
|
|
|
|
|
|
UlAcquireResourceExclusive( &pHttpConnection->Resource, TRUE );
|
|
//
|
|
// kill the request if there is one
|
|
//
|
|
|
|
if (pHttpConnection->pRequest) {
|
|
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pHttpConnection->pRequest));
|
|
|
|
UlUnlinkHttpRequest(pHttpConnection->pRequest);
|
|
pHttpConnection->pRequest = NULL;
|
|
}
|
|
|
|
//
|
|
// make sure no one adds a new request now that we're done
|
|
//
|
|
pHttpConnection->UlconnDestroyed = 1;
|
|
|
|
//
|
|
// Decrement the global connection limit. Its safe to decrement for
|
|
// global case because http object doesn't even allocated for global
|
|
// rejection which happens as tcp refusal early when acepting the
|
|
// connection request.
|
|
//
|
|
|
|
UlDecrementConnections( &g_CurrentConnections );
|
|
|
|
//
|
|
// Decrement the site connections and let our ref go away. If the
|
|
// pConnectionCountEntry is not null, we have been accepted.
|
|
//
|
|
|
|
if (pHttpConnection->pConnectionCountEntry)
|
|
{
|
|
UlDecrementConnections(
|
|
&pHttpConnection->pConnectionCountEntry->CurConnections );
|
|
|
|
DEREFERENCE_CONNECTION_COUNT_ENTRY(pHttpConnection->pConnectionCountEntry);
|
|
pHttpConnection->pConnectionCountEntry = NULL;
|
|
}
|
|
|
|
//
|
|
// and now remove the connection's id
|
|
//
|
|
|
|
if (HTTP_IS_NULL_ID(&pHttpConnection->ConnectionId) == FALSE)
|
|
{
|
|
UlFreeOpaqueId(
|
|
pHttpConnection->ConnectionId,
|
|
UlOpaqueIdTypeHttpConnection
|
|
);
|
|
|
|
HTTP_SET_NULL_ID(&pHttpConnection->ConnectionId);
|
|
|
|
UL_DEREFERENCE_HTTP_CONNECTION(pHttpConnection);
|
|
}
|
|
|
|
|
|
UlReleaseResource( &pHttpConnection->Resource );
|
|
|
|
//
|
|
// Complete all WaitForDisconnect IRPs
|
|
//
|
|
UlCompleteAllWaitForDisconnect(pHttpConnection);
|
|
|
|
//
|
|
// and remove the ULTDI reference from the HTTP_CONNECTION
|
|
//
|
|
|
|
UL_DEREFERENCE_HTTP_CONNECTION(pHttpConnection);
|
|
|
|
} // UlConnectionDestroyedWorker
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Tries to establish a binding between a connection and an app pool
|
|
process. You can query these bindings with UlQueryProcessBinding.
|
|
|
|
Arguments:
|
|
|
|
pHttpConnection - the connection to bind
|
|
pAppPool - the app pool that owns the process (key for lookups)
|
|
pProcess - the process to bind to
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlBindConnectionToProcess(
|
|
IN PUL_HTTP_CONNECTION pHttpConnection,
|
|
IN PUL_APP_POOL_OBJECT pAppPool,
|
|
IN PUL_APP_POOL_PROCESS pProcess
|
|
)
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PUL_APOOL_PROC_BINDING pBinding = NULL;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Sanity check
|
|
//
|
|
ASSERT( UL_IS_VALID_HTTP_CONNECTION( pHttpConnection ) );
|
|
ASSERT( pAppPool->NumberActiveProcesses > 1 || pProcess == NULL );
|
|
|
|
UlAcquireSpinLock(&pHttpConnection->BindingSpinLock, &OldIrql);
|
|
|
|
//
|
|
// see if there's already a binding object
|
|
//
|
|
pBinding = UlpFindProcBinding(pHttpConnection, pAppPool);
|
|
if (pBinding) {
|
|
if (pProcess) {
|
|
//
|
|
// modify the binding
|
|
//
|
|
pBinding->pProcess = pProcess;
|
|
} else {
|
|
//
|
|
// we're supposed to kill this binding
|
|
//
|
|
RemoveEntryList(&pBinding->BindingEntry);
|
|
UlpFreeProcBinding(pBinding);
|
|
}
|
|
} else {
|
|
if (pProcess) {
|
|
//
|
|
// create a new entry
|
|
//
|
|
pBinding = UlpCreateProcBinding(pAppPool, pProcess);
|
|
|
|
if (pBinding) {
|
|
InsertHeadList(
|
|
&pHttpConnection->BindingHead,
|
|
&pBinding->BindingEntry
|
|
);
|
|
} else {
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
|
|
UlReleaseSpinLock(&pHttpConnection->BindingSpinLock, OldIrql);
|
|
|
|
UlTrace(ROUTING, (
|
|
"ul!UlBindConnectionToProcess(\n"
|
|
" pHttpConnection = %p (%I64x)\n"
|
|
" pAppPool = %p\n"
|
|
" pProcess = %p )\n"
|
|
" Status = 0x%x\n",
|
|
pHttpConnection,
|
|
pHttpConnection->ConnectionId,
|
|
pAppPool,
|
|
pProcess,
|
|
Status
|
|
));
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Removes an HTTP request from all lists and cleans up it's opaque id.
|
|
|
|
Arguments:
|
|
|
|
pRequest - the request to be unlinked
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlCleanupHttpConnection(
|
|
IN PUL_HTTP_CONNECTION pHttpConnection
|
|
)
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
|
|
ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pHttpConnection->pRequest ) );
|
|
ASSERT( pHttpConnection->WaitingForResponse );
|
|
|
|
UlUnlinkHttpRequest( pHttpConnection->pRequest );
|
|
|
|
pHttpConnection->pRequest = NULL;
|
|
pHttpConnection->WaitingForResponse = 0;
|
|
|
|
UlTrace( PARSER, (
|
|
"***1 pConnection %p->WaitingForResponse = 0\n",
|
|
pHttpConnection
|
|
));
|
|
|
|
//
|
|
// Free the old request's buffers
|
|
//
|
|
|
|
ASSERT( UL_IS_VALID_REQUEST_BUFFER( pHttpConnection->pCurrentBuffer ) );
|
|
|
|
pEntry = pHttpConnection->BufferHead.Flink;
|
|
while (pEntry != &pHttpConnection->BufferHead)
|
|
{
|
|
PUL_REQUEST_BUFFER pBuffer;
|
|
|
|
//
|
|
// Get the object
|
|
//
|
|
|
|
pBuffer = CONTAINING_RECORD(
|
|
pEntry,
|
|
UL_REQUEST_BUFFER,
|
|
ListEntry
|
|
);
|
|
|
|
//
|
|
// did we reach the end?
|
|
//
|
|
|
|
if (pBuffer == pHttpConnection->pCurrentBuffer) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// remember the next one
|
|
//
|
|
|
|
pEntry = pEntry->Flink;
|
|
|
|
//
|
|
// unlink it
|
|
//
|
|
|
|
UlFreeRequestBuffer(pBuffer);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
UlReferenceHttpConnection(
|
|
IN PVOID pObject
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
LONG refCount;
|
|
|
|
PUL_HTTP_CONNECTION pHttpConnection = (PUL_HTTP_CONNECTION) pObject;
|
|
|
|
refCount = InterlockedIncrement( &pHttpConnection->RefCount );
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pHttpConnectionTraceLog,
|
|
pHttpConnection->pTraceLog,
|
|
REF_ACTION_REFERENCE_HTTP_CONNECTION,
|
|
refCount,
|
|
pHttpConnection,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
UlTrace(
|
|
REFCOUNT, (
|
|
"ul!UlReferenceHttpConnection conn=%p refcount=%d\n",
|
|
pHttpConnection,
|
|
refCount)
|
|
);
|
|
|
|
} // UlReferenceHttpConnection
|
|
|
|
|
|
VOID
|
|
UlDereferenceHttpConnection(
|
|
IN PUL_HTTP_CONNECTION pHttpConnection
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
LONG refCount;
|
|
|
|
ASSERT( pHttpConnection );
|
|
ASSERT( pHttpConnection->RefCount > 0 );
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pHttpConnectionTraceLog,
|
|
pHttpConnection->pTraceLog,
|
|
REF_ACTION_DEREFERENCE_HTTP_CONNECTION,
|
|
pHttpConnection->RefCount - 1,
|
|
pHttpConnection,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
refCount = InterlockedDecrement( &pHttpConnection->RefCount );
|
|
|
|
UlTrace(
|
|
REFCOUNT, (
|
|
"ul!UlDereferenceHttpConnection conn=%p refcount=%d\n",
|
|
pHttpConnection,
|
|
refCount)
|
|
);
|
|
|
|
if (refCount == 0)
|
|
{
|
|
//
|
|
// now it's time to free the connection, we need to do
|
|
// this at lower'd irql, let's check it out
|
|
//
|
|
|
|
UL_CALL_PASSIVE(
|
|
&(pHttpConnection->WorkItem),
|
|
&UlDeleteHttpConnection
|
|
);
|
|
}
|
|
|
|
} // UlDereferenceHttpConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees all resources associated with the specified HTTP_CONNECTION.
|
|
|
|
Arguments:
|
|
|
|
pWorkItem - Supplies the HTTP_CONNECTION to free.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDeleteHttpConnection(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
)
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY pEntry;
|
|
PUL_HTTP_CONNECTION pHttpConnection;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
pHttpConnection = CONTAINING_RECORD(
|
|
pWorkItem,
|
|
UL_HTTP_CONNECTION,
|
|
WorkItem
|
|
);
|
|
|
|
ASSERT( pHttpConnection != NULL &&
|
|
pHttpConnection->Signature == UL_HTTP_CONNECTION_POOL_TAG );
|
|
ASSERT(HTTP_IS_NULL_ID(&pHttpConnection->ConnectionId));
|
|
ASSERT(pHttpConnection->SendBufferedBytes == 0);
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pHttpConnectionTraceLog,
|
|
pHttpConnection->pTraceLog,
|
|
REF_ACTION_DESTROY_HTTP_CONNECTION,
|
|
0,
|
|
pHttpConnection,
|
|
__FILE__,
|
|
__LINE__
|
|
);
|
|
|
|
//
|
|
// The request is gone by now
|
|
//
|
|
|
|
ASSERT(pHttpConnection->pRequest == NULL);
|
|
|
|
//
|
|
// remove from Timeout Timer Wheel(c)
|
|
//
|
|
|
|
UlTimeoutRemoveTimerWheelEntry(
|
|
&pHttpConnection->TimeoutInfo
|
|
);
|
|
|
|
//
|
|
// delete the buffer list
|
|
//
|
|
|
|
pEntry = pHttpConnection->BufferHead.Flink;
|
|
while (pEntry != &pHttpConnection->BufferHead)
|
|
{
|
|
PUL_REQUEST_BUFFER pBuffer;
|
|
|
|
//
|
|
// Get the object
|
|
//
|
|
|
|
pBuffer = CONTAINING_RECORD(
|
|
pEntry,
|
|
UL_REQUEST_BUFFER,
|
|
ListEntry
|
|
);
|
|
|
|
//
|
|
// remember the next one
|
|
//
|
|
|
|
pEntry = pEntry->Flink;
|
|
|
|
//
|
|
// unlink it
|
|
//
|
|
|
|
UlFreeRequestBuffer(pBuffer);
|
|
}
|
|
|
|
ASSERT(IsListEmpty(&pHttpConnection->BufferHead));
|
|
|
|
//
|
|
// get rid of any bindings we're keeping
|
|
//
|
|
while (!IsListEmpty(&pHttpConnection->BindingHead))
|
|
{
|
|
PUL_APOOL_PROC_BINDING pBinding;
|
|
|
|
//
|
|
// Get the object
|
|
//
|
|
pEntry = RemoveHeadList(&pHttpConnection->BindingHead);
|
|
|
|
pBinding = CONTAINING_RECORD(
|
|
pEntry,
|
|
UL_APOOL_PROC_BINDING,
|
|
BindingEntry
|
|
);
|
|
|
|
ASSERT( IS_VALID_PROC_BINDING(pBinding) );
|
|
|
|
//
|
|
// free it
|
|
//
|
|
|
|
UlpFreeProcBinding(pBinding);
|
|
}
|
|
|
|
//
|
|
// get rid of our resource
|
|
//
|
|
Status = UlDeleteResource(&(pHttpConnection->Resource));
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
//
|
|
// Attempt to remove any QoS filter on this connection,
|
|
// if BWT is enabled.
|
|
//
|
|
|
|
if (pHttpConnection->BandwidthThrottlingEnabled)
|
|
{
|
|
UlTcDeleteFilter(pHttpConnection);
|
|
}
|
|
|
|
//
|
|
// Remove final (previous) site counter entry reference
|
|
// (matches reference in UlSendCachedResponse/UlDeliverHttpRequest)
|
|
//
|
|
if (pHttpConnection->pPrevSiteCounters)
|
|
{
|
|
UlDecSiteCounter(
|
|
pHttpConnection->pPrevSiteCounters,
|
|
HttpSiteCounterCurrentConns
|
|
);
|
|
|
|
DEREFERENCE_SITE_COUNTER_ENTRY(pHttpConnection->pPrevSiteCounters);
|
|
pHttpConnection->pPrevSiteCounters = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// free the memory
|
|
//
|
|
|
|
pHttpConnection->Signature = MAKE_FREE_SIGNATURE(
|
|
UL_HTTP_CONNECTION_POOL_TAG
|
|
);
|
|
|
|
//
|
|
// let go the UL_CONNECTION
|
|
//
|
|
|
|
DEREFERENCE_CONNECTION( pHttpConnection->pConnection );
|
|
|
|
} // UlDeleteHttpConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS - Completion status.
|
|
|
|
--***************************************************************************/
|
|
NTSTATUS
|
|
UlpCreateHttpRequest(
|
|
IN PUL_HTTP_CONNECTION pHttpConnection,
|
|
OUT PUL_INTERNAL_REQUEST *ppRequest
|
|
)
|
|
{
|
|
PUL_INTERNAL_REQUEST pRequest = NULL;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
pRequest = UlPplAllocateInternalRequest();
|
|
|
|
if (pRequest == NULL)
|
|
{
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
ASSERT( pRequest->Signature == MAKE_FREE_TAG(UL_INTERNAL_REQUEST_POOL_TAG) );
|
|
|
|
pRequest->Signature = UL_INTERNAL_REQUEST_POOL_TAG;
|
|
pRequest->RefCount = 1;
|
|
|
|
//
|
|
// the verb comes first
|
|
//
|
|
|
|
pRequest->ParseState = ParseVerbState;
|
|
|
|
//
|
|
// keep a reference to the connection.
|
|
//
|
|
|
|
UL_REFERENCE_HTTP_CONNECTION( pHttpConnection );
|
|
|
|
pRequest->pHttpConn = pHttpConnection;
|
|
pRequest->ConnectionId = pHttpConnection->ConnectionId;
|
|
pRequest->Secure = pHttpConnection->SecureConnection;
|
|
|
|
//
|
|
// init our LIST_ENTRY's
|
|
//
|
|
|
|
InitializeListHead( &pRequest->UnknownHeaderList );
|
|
InitializeListHead( &pRequest->IrpHead );
|
|
|
|
//
|
|
// We start with no header buffers appended nor Irps pending.
|
|
//
|
|
|
|
pRequest->HeadersAppended = FALSE;
|
|
pRequest->IrpsPending = FALSE;
|
|
|
|
//
|
|
// Reset the index to our default unknown header table to 0 as well as
|
|
// the unknown header count.
|
|
//
|
|
|
|
pRequest->NextUnknownHeaderIndex = 0;
|
|
pRequest->UnknownHeaderCount = 0;
|
|
|
|
//
|
|
// Create an opaque id for the request.
|
|
//
|
|
// We also make a copy of the id in case we need
|
|
// to see what the id was after the real one
|
|
// is freed and set to null.
|
|
//
|
|
|
|
HTTP_SET_NULL_ID(&pRequest->RequestId);
|
|
|
|
pRequest->RequestIdCopy = pRequest->RequestId;
|
|
|
|
//
|
|
// Grab the raw connection id off the UL_CONNECTION.
|
|
//
|
|
|
|
pRequest->RawConnectionId = pHttpConnection->pConnection->FilterInfo.ConnectionId;
|
|
|
|
//
|
|
// Initialize the header index table.
|
|
//
|
|
|
|
pRequest->HeaderIndex[0] = HttpHeaderRequestMaximum;
|
|
|
|
//
|
|
// Initialize the referenced buffers arrary.
|
|
//
|
|
|
|
pRequest->AllocRefBuffers = 1;
|
|
pRequest->UsedRefBuffers = 0;
|
|
pRequest->pRefBuffers = pRequest->pInlineRefBuffers;
|
|
|
|
//
|
|
// Initialize the UL_URL_CONFIG_GROUP_INFO.
|
|
//
|
|
|
|
UlpInitializeUrlInfo(&pRequest->ConfigInfo);
|
|
|
|
//
|
|
// Zero the remaining fields.
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
(PUCHAR)pRequest + FIELD_OFFSET(UL_INTERNAL_REQUEST, HeaderValid),
|
|
sizeof(*pRequest) - FIELD_OFFSET(UL_INTERNAL_REQUEST, HeaderValid)
|
|
);
|
|
|
|
CREATE_REF_TRACE_LOG( pRequest->pTraceLog, 32 - REF_TRACE_OVERHEAD, 0 );
|
|
|
|
//
|
|
// return it
|
|
//
|
|
|
|
*ppRequest = pRequest;
|
|
|
|
UlTrace(REFCOUNT, (
|
|
"ul!UlpCreateHttpRequest req=%p refcount=%d\n",
|
|
pRequest,
|
|
pRequest->RefCount
|
|
));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // UlpCreateHttpRequest
|
|
|
|
|
|
VOID
|
|
UlReferenceHttpRequest(
|
|
IN PVOID pObject
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
LONG refCount;
|
|
|
|
PUL_INTERNAL_REQUEST pRequest = (PUL_INTERNAL_REQUEST) pObject;
|
|
|
|
refCount = InterlockedIncrement( &pRequest->RefCount );
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pHttpRequestTraceLog,
|
|
pRequest->pTraceLog,
|
|
REF_ACTION_REFERENCE_HTTP_REQUEST,
|
|
refCount,
|
|
pRequest,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
UlTrace(
|
|
REFCOUNT, (
|
|
"ul!UlReferenceHttpRequest req=%p refcount=%d\n",
|
|
pRequest,
|
|
refCount)
|
|
);
|
|
|
|
} // UlReferenceHttpRequest
|
|
|
|
VOID
|
|
UlDereferenceHttpRequest(
|
|
IN PUL_INTERNAL_REQUEST pRequest
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
LONG refCount;
|
|
|
|
WRITE_REF_TRACE_LOG2(
|
|
g_pHttpRequestTraceLog,
|
|
pRequest->pTraceLog,
|
|
REF_ACTION_DEREFERENCE_HTTP_REQUEST,
|
|
pRequest->RefCount - 1,
|
|
pRequest,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
refCount = InterlockedDecrement( &pRequest->RefCount );
|
|
|
|
UlTrace(
|
|
REFCOUNT, (
|
|
"ul!UlDereferenceHttpRequest req=%p refcount=%d\n",
|
|
pRequest,
|
|
refCount)
|
|
);
|
|
|
|
if (refCount == 0)
|
|
{
|
|
UL_CALL_PASSIVE(
|
|
&pRequest->WorkItem,
|
|
&UlpFreeHttpRequest
|
|
);
|
|
}
|
|
|
|
} // UlDereferenceHttpRequest
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Cancels all pending http io.
|
|
|
|
Arguments:
|
|
|
|
pRequest - Supplies the HTTP_REQUEST.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlCancelRequestIo(
|
|
IN PUL_INTERNAL_REQUEST pRequest
|
|
)
|
|
{
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(UL_IS_VALID_INTERNAL_REQUEST(pRequest));
|
|
|
|
//
|
|
// Mark the request as InCleanup, so additional IRPs
|
|
// on the request will not be queued.
|
|
//
|
|
pRequest->InCleanup = 1;
|
|
|
|
//
|
|
// tank all pending io on this request
|
|
//
|
|
|
|
while (IsListEmpty(&pRequest->IrpHead) == FALSE)
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
PIRP pIrp;
|
|
|
|
//
|
|
// Pop it off the list.
|
|
//
|
|
|
|
pEntry = RemoveHeadList(&pRequest->IrpHead);
|
|
pEntry->Blink = pEntry->Flink = NULL;
|
|
|
|
pIrp = CONTAINING_RECORD(pEntry, IRP, Tail.Overlay.ListEntry);
|
|
ASSERT(IS_VALID_IRP(pIrp));
|
|
|
|
//
|
|
// pop the cancel routine
|
|
//
|
|
|
|
if (IoSetCancelRoutine(pIrp, NULL) == NULL)
|
|
{
|
|
//
|
|
// IoCancelIrp pop'd it first
|
|
//
|
|
// ok to just ignore this irp, it's been pop'd off the queue
|
|
// and will be completed in the cancel routine.
|
|
//
|
|
// keep looping
|
|
//
|
|
|
|
pIrp = NULL;
|
|
}
|
|
else
|
|
{
|
|
PUL_INTERNAL_REQUEST pIrpRequest;
|
|
|
|
//
|
|
// cancel it. even if pIrp->Cancel == TRUE we are supposed to
|
|
// complete it, our cancel routine will never run.
|
|
//
|
|
|
|
pIrpRequest = (PUL_INTERNAL_REQUEST)(
|
|
IoGetCurrentIrpStackLocation(pIrp)->
|
|
Parameters.DeviceIoControl.Type3InputBuffer
|
|
);
|
|
|
|
ASSERT(pIrpRequest == pRequest);
|
|
|
|
UL_DEREFERENCE_INTERNAL_REQUEST(pIrpRequest);
|
|
|
|
IoGetCurrentIrpStackLocation(pIrp)->
|
|
Parameters.DeviceIoControl.Type3InputBuffer = NULL;
|
|
|
|
pIrp->IoStatus.Status = STATUS_CANCELLED;
|
|
pIrp->IoStatus.Information = 0;
|
|
|
|
UlCompleteRequest(pIrp, g_UlPriorityBoost);
|
|
pIrp = NULL;
|
|
}
|
|
|
|
} // while (IsListEmpty(&pRequest->IrpHead) == FALSE)
|
|
|
|
} // UlCancelRequestIo
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Frees all resources associated with the specified UL_INTERNAL_REQUEST.
|
|
|
|
Arguments:
|
|
|
|
pRequest - Supplies the UL_INTERNAL_REQUEST to free.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpFreeHttpRequest(
|
|
IN PUL_WORK_ITEM pWorkItem
|
|
)
|
|
{
|
|
PUL_INTERNAL_REQUEST pRequest;
|
|
PLIST_ENTRY pEntry;
|
|
ULONG i;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
pRequest = CONTAINING_RECORD(
|
|
pWorkItem,
|
|
UL_INTERNAL_REQUEST,
|
|
WorkItem
|
|
);
|
|
|
|
//
|
|
// our opaque id should already be free'd (UlDeleteOpaqueIds)
|
|
//
|
|
|
|
ASSERT(HTTP_IS_NULL_ID(&pRequest->RequestId));
|
|
|
|
//
|
|
// free any known header buffers allocated
|
|
//
|
|
|
|
if (pRequest->HeadersAppended)
|
|
{
|
|
for (i = 0; i < HttpHeaderRequestMaximum; i++)
|
|
{
|
|
HTTP_HEADER_ID HeaderId = (HTTP_HEADER_ID)pRequest->HeaderIndex[i];
|
|
|
|
if (HeaderId == HttpHeaderRequestMaximum)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ASSERT( pRequest->HeaderValid[HeaderId] );
|
|
|
|
if (pRequest->Headers[HeaderId].OurBuffer == 1)
|
|
{
|
|
UL_FREE_POOL(
|
|
pRequest->Headers[HeaderId].pHeader,
|
|
HEADER_VALUE_POOL_TAG
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// and any unknown header buffers allocated
|
|
//
|
|
|
|
while (IsListEmpty(&pRequest->UnknownHeaderList) == FALSE)
|
|
{
|
|
PUL_HTTP_UNKNOWN_HEADER pUnknownHeader;
|
|
|
|
pEntry = RemoveHeadList(&pRequest->UnknownHeaderList);
|
|
|
|
pUnknownHeader = CONTAINING_RECORD(
|
|
pEntry,
|
|
UL_HTTP_UNKNOWN_HEADER,
|
|
List
|
|
);
|
|
|
|
if (pUnknownHeader->HeaderValue.OurBuffer == 1)
|
|
{
|
|
UL_FREE_POOL(
|
|
pUnknownHeader->HeaderValue.pHeader,
|
|
HEADER_VALUE_POOL_TAG
|
|
);
|
|
}
|
|
|
|
//
|
|
// Free the header structure
|
|
//
|
|
|
|
if (pUnknownHeader->HeaderValue.ExternalAllocated == 1)
|
|
{
|
|
UL_FREE_POOL(
|
|
pUnknownHeader,
|
|
UL_HTTP_UNKNOWN_HEADER_POOL_TAG
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// there better not be any pending io, it would have been cancelled either
|
|
// in UlDeleteHttpConnection or in UlDetachProcessFromAppPool .
|
|
//
|
|
|
|
ASSERT(IsListEmpty(&pRequest->IrpHead));
|
|
|
|
//
|
|
// dereferenc request buffers.
|
|
//
|
|
|
|
for (i = 0; i < pRequest->UsedRefBuffers; i++)
|
|
{
|
|
ASSERT( UL_IS_VALID_REQUEST_BUFFER(pRequest->pRefBuffers[i]) );
|
|
UL_DEREFERENCE_REQUEST_BUFFER(pRequest->pRefBuffers[i]);
|
|
}
|
|
|
|
if (pRequest->AllocRefBuffers > 1)
|
|
{
|
|
UL_FREE_POOL(
|
|
pRequest->pRefBuffers,
|
|
UL_REF_REQUEST_BUFFER_POOL_TAG
|
|
);
|
|
}
|
|
|
|
//
|
|
// free any url that was allocated
|
|
//
|
|
|
|
if (pRequest->CookedUrl.pUrl != NULL)
|
|
{
|
|
if (pRequest->CookedUrl.pUrl != pRequest->pUrlBuffer)
|
|
{
|
|
UL_FREE_POOL(pRequest->CookedUrl.pUrl, URL_POOL_TAG);
|
|
}
|
|
}
|
|
|
|
//
|
|
// free any config group info
|
|
//
|
|
|
|
ASSERT( IS_VALID_URL_CONFIG_GROUP_INFO(&pRequest->ConfigInfo) );
|
|
ASSERT( pRequest->pHttpConn );
|
|
|
|
//
|
|
// Perf counters
|
|
// NOTE: Assumes cache & non-cache paths both go through here
|
|
// NOTE: If connection is refused the pConnectionCountEntry will be NULL
|
|
//
|
|
if (pRequest->ConfigInfo.pSiteCounters &&
|
|
pRequest->pHttpConn->pConnectionCountEntry)
|
|
{
|
|
PUL_SITE_COUNTER_ENTRY pCtr = pRequest->ConfigInfo.pSiteCounters;
|
|
|
|
UlAddSiteCounter64(
|
|
pCtr,
|
|
HttpSiteCounterBytesSent,
|
|
pRequest->BytesSent
|
|
);
|
|
|
|
UlAddSiteCounter64(
|
|
pCtr,
|
|
HttpSiteCounterBytesReceived,
|
|
pRequest->BytesReceived
|
|
);
|
|
|
|
UlAddSiteCounter64(
|
|
pCtr,
|
|
HttpSiteCounterBytesTransfered,
|
|
(pRequest->BytesSent + pRequest->BytesReceived)
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Release all the references from the UL_URL_CONFIG_GROUP_INFO.
|
|
//
|
|
UlpConfigGroupInfoRelease(&pRequest->ConfigInfo);
|
|
|
|
//
|
|
// release our reference to the connection
|
|
//
|
|
UL_DEREFERENCE_HTTP_CONNECTION( pRequest->pHttpConn );
|
|
|
|
//
|
|
// Free the object buffer
|
|
//
|
|
ASSERT(pRequest->Signature == UL_INTERNAL_REQUEST_POOL_TAG);
|
|
pRequest->Signature = MAKE_FREE_TAG(UL_INTERNAL_REQUEST_POOL_TAG);
|
|
|
|
DESTROY_REF_TRACE_LOG( pRequest->pTraceLog );
|
|
|
|
UlPplFreeInternalRequest( pRequest );
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates and initializes a new UL_REQUEST_BUFFER.
|
|
|
|
Arguments:
|
|
|
|
BufferSize - size of the new buffer in bytes
|
|
|
|
--***************************************************************************/
|
|
PUL_REQUEST_BUFFER
|
|
UlCreateRequestBuffer(
|
|
ULONG BufferSize,
|
|
ULONG BufferNumber
|
|
)
|
|
{
|
|
PUL_REQUEST_BUFFER pBuffer;
|
|
|
|
BufferSize = MAX(BufferSize, DEFAULT_MAX_REQUEST_BUFFER_SIZE);
|
|
|
|
if (BufferSize > DEFAULT_MAX_REQUEST_BUFFER_SIZE)
|
|
{
|
|
BufferSize = (ULONG) MAX(
|
|
BufferSize,
|
|
PAGE_SIZE - ALIGN_UP(sizeof(UL_REQUEST_BUFFER),PVOID)
|
|
);
|
|
|
|
pBuffer = UL_ALLOCATE_STRUCT_WITH_SPACE(
|
|
NonPagedPool,
|
|
UL_REQUEST_BUFFER,
|
|
BufferSize,
|
|
UL_REQUEST_BUFFER_POOL_TAG
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pBuffer = UlPplAllocateRequestBuffer();
|
|
}
|
|
|
|
if (pBuffer == NULL)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
(PCHAR)pBuffer + sizeof(SINGLE_LIST_ENTRY),
|
|
sizeof(UL_REQUEST_BUFFER) - sizeof(SINGLE_LIST_ENTRY)
|
|
);
|
|
|
|
pBuffer->Signature = UL_REQUEST_BUFFER_POOL_TAG;
|
|
|
|
UlTrace(HTTP_IO, (
|
|
"ul!UlCreateRequestBuffer buff=%p num=%d size=%d\n",
|
|
pBuffer,
|
|
BufferNumber,
|
|
BufferSize
|
|
));
|
|
|
|
pBuffer->RefCount = 1;
|
|
pBuffer->AllocBytes = BufferSize;
|
|
pBuffer->BufferNumber = BufferNumber;
|
|
|
|
return pBuffer;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Removes a request buffer from the buffer list and destroys it.
|
|
|
|
Arguments:
|
|
|
|
pBuffer - the buffer to be deleted
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlFreeRequestBuffer(
|
|
PUL_REQUEST_BUFFER pBuffer
|
|
)
|
|
{
|
|
ASSERT( UL_IS_VALID_REQUEST_BUFFER( pBuffer ) );
|
|
|
|
UlTrace(HTTP_IO, (
|
|
"ul!UlFreeRequestBuffer(pBuffer = %p, Num = %d)\n",
|
|
pBuffer,
|
|
pBuffer->BufferNumber
|
|
));
|
|
|
|
if (pBuffer->ListEntry.Flink != NULL) {
|
|
RemoveEntryList(&pBuffer->ListEntry);
|
|
pBuffer->ListEntry.Blink = pBuffer->ListEntry.Flink = NULL;
|
|
}
|
|
|
|
UL_DEREFERENCE_REQUEST_BUFFER(pBuffer);
|
|
}
|
|
|
|
|
|
//
|
|
// Private functions.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves a binding set with UlBindConnectionToProcess.
|
|
|
|
Arguments:
|
|
|
|
pHttpConnection - the connection to query
|
|
pAppPool - the key to use for the lookup
|
|
|
|
Return Values:
|
|
|
|
A pointer to the bound process if one was found. NULL otherwise.
|
|
|
|
--***************************************************************************/
|
|
PUL_APP_POOL_PROCESS
|
|
UlQueryProcessBinding(
|
|
IN PUL_HTTP_CONNECTION pHttpConnection,
|
|
IN PUL_APP_POOL_OBJECT pAppPool
|
|
)
|
|
{
|
|
PUL_APOOL_PROC_BINDING pBinding;
|
|
PUL_APP_POOL_PROCESS pProcess = NULL;
|
|
KIRQL OldIrql;
|
|
|
|
//
|
|
// Sanity check
|
|
//
|
|
ASSERT( UL_IS_VALID_HTTP_CONNECTION( pHttpConnection ) );
|
|
|
|
UlAcquireSpinLock(&pHttpConnection->BindingSpinLock, &OldIrql);
|
|
|
|
pBinding = UlpFindProcBinding(pHttpConnection, pAppPool);
|
|
|
|
if (pBinding) {
|
|
pProcess = pBinding->pProcess;
|
|
}
|
|
|
|
UlReleaseSpinLock(&pHttpConnection->BindingSpinLock, OldIrql);
|
|
|
|
return pProcess;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates and builds a UL_APOOL_PROC_BINDING object.
|
|
|
|
Arguments:
|
|
|
|
pAppPool - the lookup key
|
|
pProcess - the binding
|
|
|
|
Return Values:
|
|
|
|
a pointer to the allocated object, or NULL on failure
|
|
|
|
--***************************************************************************/
|
|
PUL_APOOL_PROC_BINDING
|
|
UlpCreateProcBinding(
|
|
IN PUL_APP_POOL_OBJECT pAppPool,
|
|
IN PUL_APP_POOL_PROCESS pProcess
|
|
)
|
|
{
|
|
PUL_APOOL_PROC_BINDING pBinding;
|
|
|
|
ASSERT( pAppPool->NumberActiveProcesses > 1 );
|
|
|
|
//
|
|
// CODEWORK: lookaside
|
|
//
|
|
|
|
pBinding = UL_ALLOCATE_STRUCT(
|
|
NonPagedPool,
|
|
UL_APOOL_PROC_BINDING,
|
|
UL_APOOL_PROC_BINDING_POOL_TAG
|
|
);
|
|
|
|
if (pBinding) {
|
|
pBinding->Signature = UL_APOOL_PROC_BINDING_POOL_TAG;
|
|
pBinding->pAppPool = pAppPool;
|
|
pBinding->pProcess = pProcess;
|
|
|
|
UlTrace(ROUTING, (
|
|
"ul!UlpCreateProcBinding(\n"
|
|
" pAppPool = %p\n"
|
|
" pProcess = %p )\n"
|
|
" pBinding = %p\n",
|
|
pAppPool,
|
|
pProcess,
|
|
pBinding
|
|
));
|
|
}
|
|
|
|
return pBinding;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Gets rid of a proc binding
|
|
|
|
Arguments:
|
|
|
|
pBinding - the binding to get rid of
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlpFreeProcBinding(
|
|
IN PUL_APOOL_PROC_BINDING pBinding
|
|
)
|
|
{
|
|
UL_FREE_POOL_WITH_SIG(pBinding, UL_APOOL_PROC_BINDING_POOL_TAG);
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Searches a connection's list of bindings for one that has the right
|
|
app pool key
|
|
|
|
Arguments:
|
|
|
|
pHttpConnection - the connection to search
|
|
pAppPool - the key
|
|
|
|
Return Values:
|
|
|
|
The binding if found or NULL if not found.
|
|
|
|
--***************************************************************************/
|
|
PUL_APOOL_PROC_BINDING
|
|
UlpFindProcBinding(
|
|
IN PUL_HTTP_CONNECTION pHttpConnection,
|
|
IN PUL_APP_POOL_OBJECT pAppPool
|
|
)
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
PUL_APOOL_PROC_BINDING pBinding = NULL;
|
|
|
|
//
|
|
// Sanity check
|
|
//
|
|
ASSERT( UL_IS_VALID_HTTP_CONNECTION(pHttpConnection) );
|
|
|
|
pEntry = pHttpConnection->BindingHead.Flink;
|
|
while (pEntry != &pHttpConnection->BindingHead) {
|
|
|
|
pBinding = CONTAINING_RECORD(
|
|
pEntry,
|
|
UL_APOOL_PROC_BINDING,
|
|
BindingEntry
|
|
);
|
|
|
|
ASSERT( IS_VALID_PROC_BINDING(pBinding) );
|
|
|
|
if (pBinding->pAppPool == pAppPool) {
|
|
//
|
|
// got it!
|
|
//
|
|
break;
|
|
}
|
|
|
|
pEntry = pEntry->Flink;
|
|
}
|
|
|
|
return pBinding;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Removes an HTTP request from all lists and cleans up it's opaque id.
|
|
|
|
Arguments:
|
|
|
|
pRequest - the request to be unlinked
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlUnlinkHttpRequest(
|
|
IN PUL_INTERNAL_REQUEST pRequest
|
|
)
|
|
{
|
|
//
|
|
// if we got far enough to deliver the request then
|
|
// unlink it from the app pool. this needs to happen here because the
|
|
// queue'd IRPs keep references to the UL_INTERNAL_REQUEST objects.
|
|
// this kicks their references off and allows them to delete.
|
|
//
|
|
if (pRequest->ConfigInfo.pAppPool)
|
|
{
|
|
UlUnlinkRequestFromProcess(pRequest->ConfigInfo.pAppPool, pRequest);
|
|
}
|
|
|
|
//
|
|
// cancel i/o
|
|
//
|
|
if (pRequest->IrpsPending)
|
|
{
|
|
UlCancelRequestIo(pRequest);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Mark the request as InCleanup, so additional IRPs
|
|
// on the request will not be queued.
|
|
//
|
|
// Normally UlCancelRequestIo sets this flag, but we
|
|
// have to do it here since we're optimizing that
|
|
// call away.
|
|
//
|
|
pRequest->InCleanup = 1;
|
|
}
|
|
|
|
//
|
|
// delete its opaque id
|
|
//
|
|
|
|
if (HTTP_IS_NULL_ID(&pRequest->RequestId) == FALSE)
|
|
{
|
|
UlFreeRequestId(pRequest);
|
|
|
|
HTTP_SET_NULL_ID(&pRequest->RequestId);
|
|
|
|
//
|
|
// it is still referenced by this connection
|
|
//
|
|
|
|
ASSERT(pRequest->RefCount > 1);
|
|
|
|
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
|
|
}
|
|
|
|
//
|
|
// deref it
|
|
//
|
|
|
|
UL_DEREFERENCE_INTERNAL_REQUEST(pRequest);
|
|
}
|
|
|
|
|
|
//
|
|
// Following code has been implemented for Global & Site specific connection
|
|
// limits feature. If enforced, incoming connections get refused when they
|
|
// exceed the existing limit. Control channel & config group (re)sets this
|
|
// values whereas httprcv and sendresponse updates the limits for incoming
|
|
// requests & connections. A seperate connection_count_entry structure keeps
|
|
// track of the limits per site. Global limits are tracked by the global
|
|
// variables g_MaxConnections & g_CurrentConnections same API has been used
|
|
// for both purposes.
|
|
//
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a connection count entry which will hold the site specific
|
|
connection count info. Get called by cgroup when Config group is
|
|
attempting to allocate a connection count entry.
|
|
|
|
Arguments:
|
|
|
|
pConfigGroup - To receive the newly allocated count entry
|
|
MaxConnections - The maximum allowed connections
|
|
|
|
--***************************************************************************/
|
|
|
|
NTSTATUS
|
|
UlCreateConnectionCountEntry(
|
|
IN OUT PUL_CONFIG_GROUP_OBJECT pConfigGroup,
|
|
IN ULONG MaxConnections
|
|
)
|
|
{
|
|
PUL_CONNECTION_COUNT_ENTRY pEntry;
|
|
|
|
// Sanity check.
|
|
|
|
PAGED_CODE();
|
|
ASSERT(IS_VALID_CONFIG_GROUP(pConfigGroup));
|
|
|
|
// Alloc new struct from Paged Pool
|
|
|
|
pEntry = UL_ALLOCATE_STRUCT(
|
|
PagedPool,
|
|
UL_CONNECTION_COUNT_ENTRY,
|
|
UL_CONNECTION_COUNT_ENTRY_POOL_TAG
|
|
);
|
|
if (!pEntry)
|
|
{
|
|
UlTrace(LIMITS,
|
|
("Http!UlCreateConnectionCountEntry: OutOfMemory pConfigGroup %p\n",
|
|
pConfigGroup
|
|
));
|
|
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
pEntry->Signature = UL_CONNECTION_COUNT_ENTRY_POOL_TAG;
|
|
pEntry->RefCount = 1;
|
|
pEntry->MaxConnections = MaxConnections;
|
|
pEntry->CurConnections = 0;
|
|
|
|
// Update cgroup pointer
|
|
|
|
ASSERT( pConfigGroup->pConnectionCountEntry == NULL );
|
|
pConfigGroup->pConnectionCountEntry = pEntry;
|
|
|
|
UlTrace(LIMITS,
|
|
("Http!UlCreateConnectionCountEntry: pNewEntry %p, pConfigGroup %p, Max %d\n",
|
|
pEntry,
|
|
pConfigGroup,
|
|
MaxConnections
|
|
));
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // UlCreateConnectionCountEntry
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
increments the refcount for ConnectionCountEntry.
|
|
|
|
Arguments:
|
|
|
|
pEntry - the object to increment.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlReferenceConnectionCountEntry(
|
|
IN PUL_CONNECTION_COUNT_ENTRY pEntry
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
LONG refCount;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( IS_VALID_CONNECTION_COUNT_ENTRY(pEntry) );
|
|
|
|
refCount = InterlockedIncrement( &pEntry->RefCount );
|
|
|
|
//
|
|
// Tracing.
|
|
//
|
|
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pConnectionCountTraceLog,
|
|
REF_ACTION_REFERENCE_CONNECTION_COUNT_ENTRY,
|
|
refCount,
|
|
pEntry,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
UlTrace(
|
|
REFCOUNT,
|
|
("Http!UlReferenceConnectionCountEntry pEntry=%p refcount=%d\n",
|
|
pEntry,
|
|
refCount
|
|
));
|
|
|
|
} // UlReferenceConnectionCountEntry
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
decrements the refcount. if it hits 0, destruct's ConnectionCountEntry
|
|
|
|
Arguments:
|
|
|
|
pEntry - the object to decrement.
|
|
|
|
--***************************************************************************/
|
|
VOID
|
|
UlDereferenceConnectionCountEntry(
|
|
IN PUL_CONNECTION_COUNT_ENTRY pEntry
|
|
REFERENCE_DEBUG_FORMAL_PARAMS
|
|
)
|
|
{
|
|
LONG refCount;
|
|
NTSTATUS Status;
|
|
|
|
//
|
|
// Sanity check.
|
|
//
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT( IS_VALID_CONNECTION_COUNT_ENTRY(pEntry) );
|
|
|
|
refCount = InterlockedDecrement( &pEntry->RefCount );
|
|
|
|
//
|
|
// Tracing.
|
|
//
|
|
WRITE_REF_TRACE_LOG(
|
|
g_pConnectionCountTraceLog,
|
|
REF_ACTION_DEREFERENCE_CONNECTION_COUNT_ENTRY,
|
|
refCount,
|
|
pEntry,
|
|
pFileName,
|
|
LineNumber
|
|
);
|
|
|
|
UlTrace(
|
|
REFCOUNT,
|
|
("Http!UlDereferenceConnectionCountEntry pEntry=%p refcount=%d\n",
|
|
pEntry,
|
|
refCount
|
|
));
|
|
|
|
//
|
|
// Cleanup the memory and do few checks !
|
|
//
|
|
|
|
if ( refCount == 0 )
|
|
{
|
|
// Make sure no connection on the site
|
|
ASSERT( 0 == pEntry->CurConnections );
|
|
|
|
UlTrace(
|
|
LIMITS,
|
|
("Http!UlDereferenceConnectionCountEntry: Removing pEntry=%p\n",
|
|
pEntry
|
|
));
|
|
|
|
// Release memory
|
|
UL_FREE_POOL_WITH_SIG(pEntry,UL_CONNECTION_COUNT_ENTRY_POOL_TAG);
|
|
}
|
|
|
|
} // UlDereferenceConnectionCountEntry
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This
|
|
function set the maximum limit. Maximum allowed number of connections
|
|
at any time by the active control channel.
|
|
|
|
Arguments:
|
|
|
|
MaxConnections - Maximum allowed number of connections
|
|
|
|
Return Value:
|
|
|
|
Old Max Connection count
|
|
|
|
--***************************************************************************/
|
|
|
|
ULONG
|
|
UlSetMaxConnections(
|
|
IN OUT PULONG pCurMaxConnection,
|
|
IN ULONG NewMaxConnection
|
|
)
|
|
{
|
|
ULONG OldMaxConnection;
|
|
|
|
UlTrace(LIMITS,
|
|
("Http!UlSetMaxConnections pCurMaxConnection=%p NewMaxConnection=%d\n",
|
|
pCurMaxConnection,
|
|
NewMaxConnection
|
|
));
|
|
|
|
//
|
|
// By setting this we are not forcing the existing connections to
|
|
// termination but this number will be effective for all newcoming
|
|
// connections, as soon as the atomic operation completed.
|
|
//
|
|
|
|
OldMaxConnection = (ULONG) InterlockedExchange((LONG *) pCurMaxConnection,
|
|
(LONG) NewMaxConnection
|
|
);
|
|
return OldMaxConnection;
|
|
|
|
} // UlSetMaxConnections
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Control channel uses this function to set or reset the global connection
|
|
limits.
|
|
|
|
--***************************************************************************/
|
|
|
|
VOID
|
|
UlSetGlobalConnectionLimit(
|
|
IN ULONG Limit
|
|
)
|
|
{
|
|
UlSetMaxConnections( &g_MaxConnections, Limit );
|
|
UlTrace(LIMITS,("Http!UlSetGlobalConnectionLimit: To %d\n", Limit));
|
|
|
|
} // ULSetGlobalConnectionLimit
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Control channel uses this function to initialize the global connection
|
|
limits. Assuming the existince of one and only one active control channel
|
|
this globals get set only once during init. But could be set again later
|
|
because of a reconfig.
|
|
|
|
--***************************************************************************/
|
|
|
|
NTSTATUS
|
|
UlInitGlobalConnectionLimits(
|
|
VOID
|
|
)
|
|
{
|
|
ASSERT(!g_InitGlobalConnections);
|
|
|
|
if (!g_InitGlobalConnections)
|
|
{
|
|
g_MaxConnections = HTTP_LIMIT_INFINITE;
|
|
g_CurrentConnections = 0;
|
|
|
|
UlTrace(LIMITS,
|
|
("Http!UlInitGlobalConnectionLimits: Init g_MaxConnections %d,"
|
|
"g_CurrentConnections %d\n",
|
|
g_MaxConnections,
|
|
g_CurrentConnections
|
|
));
|
|
|
|
g_InitGlobalConnections = TRUE;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
} // UlInitGlobalConnectionLimits
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Wrapper around the Accept Connection for global connections
|
|
|
|
--***************************************************************************/
|
|
BOOLEAN
|
|
UlAcceptGlobalConnection(
|
|
VOID
|
|
)
|
|
{
|
|
return UlAcceptConnection( &g_MaxConnections, &g_CurrentConnections );
|
|
|
|
} // UlAcceptGlobalConnection
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
This function checks if we are allowed to accept the incoming connection
|
|
based on the number enforced by the control channel.
|
|
|
|
Return value:
|
|
|
|
Decision for the newcoming connection either ACCEPT or REJECT as boolean
|
|
|
|
--***************************************************************************/
|
|
|
|
BOOLEAN
|
|
UlAcceptConnection(
|
|
IN PULONG pMaxConnection,
|
|
IN OUT PULONG pCurConnection
|
|
)
|
|
{
|
|
ULONG LocalCur;
|
|
ULONG LocalCurPlusOne;
|
|
ULONG LocalMax;
|
|
|
|
do
|
|
{
|
|
//
|
|
// Capture the Max & Cur. Do the comparison. If in the limit
|
|
// attempt to increment the connection count by ensuring nobody
|
|
// else did it before us.
|
|
//
|
|
|
|
LocalMax = *((volatile ULONG *) pMaxConnection);
|
|
LocalCur = *((volatile ULONG *) pCurConnection);
|
|
|
|
//
|
|
// Its greater than or equal because Max may get updated to
|
|
// a smaller number on-the-fly and we end up having current
|
|
// connections greater than the maximum allowed.
|
|
// NOTE: HTTP_LIMIT_INFINITE has been picked as (ULONG)-1 so
|
|
// following comparison won't reject for the infinite case.
|
|
//
|
|
|
|
if ( LocalCur >= LocalMax )
|
|
{
|
|
//
|
|
// We are already at the limit refuse it.
|
|
//
|
|
|
|
UlTrace(LIMITS,
|
|
("Http!UlAcceptConnection REFUSE pCurConnection=%p[%d]"
|
|
"pMaxConnection=%p[%d]\n",
|
|
pCurConnection,LocalCur,
|
|
pMaxConnection,LocalMax
|
|
));
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Either the limit was infinite or conn count was not exceeding
|
|
// the limit. Lets attempt to increment the count and accept the
|
|
// connection in the while statement.
|
|
//
|
|
|
|
LocalCurPlusOne = LocalCur + 1;
|
|
|
|
}
|
|
while ( LocalCur != (ULONG) InterlockedCompareExchange(
|
|
(LONG *) pCurConnection,
|
|
(LONG) LocalCurPlusOne,
|
|
(LONG) LocalCur
|
|
) );
|
|
|
|
//
|
|
// Successfully incremented the counter. Let it go with success.
|
|
//
|
|
|
|
UlTrace(LIMITS,
|
|
("Http!UlAcceptConnection ACCEPT pCurConnection=%p[%d]"
|
|
"pMaxConnection=%p[%d]\n",
|
|
pCurConnection,LocalCur,
|
|
pMaxConnection,LocalMax
|
|
));
|
|
|
|
return TRUE;
|
|
|
|
} // UlAcceptConnection
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Everytime a disconnection happens we will decrement the count here.
|
|
|
|
Return Value:
|
|
|
|
The newly decremented value
|
|
|
|
--***************************************************************************/
|
|
|
|
LONG
|
|
UlDecrementConnections(
|
|
IN OUT PULONG pCurConnection
|
|
)
|
|
{
|
|
LONG NewConnectionCount;
|
|
|
|
NewConnectionCount = InterlockedDecrement( (LONG *) pCurConnection );
|
|
|
|
ASSERT( NewConnectionCount >= 0 );
|
|
|
|
return NewConnectionCount;
|
|
|
|
} // UlDecrementConnections
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
For cache & non-cache hits this function get called. Connection resource
|
|
has assumed to be acquired at this time. The function decide to accept or
|
|
reject the request by looking at the corresponding count entries.
|
|
|
|
Arguments:
|
|
|
|
pConnection - For getting the previous site's connection count entry
|
|
pConfigInfo - Holds a pointer to newly received request's site's
|
|
connection count entry.
|
|
Return Value:
|
|
|
|
Shows reject or accept
|
|
|
|
--***************************************************************************/
|
|
|
|
BOOLEAN
|
|
UlCheckSiteConnectionLimit(
|
|
IN OUT PUL_HTTP_CONNECTION pConnection,
|
|
IN OUT PUL_URL_CONFIG_GROUP_INFO pConfigInfo
|
|
)
|
|
{
|
|
BOOLEAN Accept;
|
|
PUL_CONNECTION_COUNT_ENTRY pConEntry;
|
|
PUL_CONNECTION_COUNT_ENTRY pCIEntry;
|
|
|
|
if (pConfigInfo->pMaxConnections == NULL || pConfigInfo->pConnectionCountEntry == NULL)
|
|
{
|
|
//
|
|
// No connection count entry for the new request perhaps WAS never
|
|
// set this before otherwise its a problem.
|
|
//
|
|
|
|
UlTrace(LIMITS,
|
|
("Http!UlCheckSiteConnectionLimit: NO LIMIT pConnection=%p pConfigInfo=%p\n",
|
|
pConnection,
|
|
pConfigInfo
|
|
));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
ASSERT(IS_VALID_CONNECTION_COUNT_ENTRY(pConfigInfo->pConnectionCountEntry));
|
|
|
|
pCIEntry = pConfigInfo->pConnectionCountEntry;
|
|
pConEntry = pConnection->pConnectionCountEntry;
|
|
Accept = FALSE;
|
|
|
|
//
|
|
// Make a check on the connection limit of the site. Refuse the request
|
|
// if the limit is exceded.
|
|
//
|
|
if (pConEntry)
|
|
{
|
|
ASSERT(IS_VALID_CONNECTION_COUNT_ENTRY(pConEntry));
|
|
|
|
//
|
|
// For consecutive requests we decrement the previous site's connection count
|
|
// before proceeding to the decision on the newcoming request,if the two sides
|
|
// are not same.That means we assume this connection on site x until (if ever)
|
|
// a request changes this to site y. Naturally until the first request arrives
|
|
// and successfully parsed, the connection does not count to any specific site
|
|
//
|
|
|
|
if (pConEntry != pCIEntry)
|
|
{
|
|
UlDecrementConnections(&pConEntry->CurConnections);
|
|
DEREFERENCE_CONNECTION_COUNT_ENTRY(pConEntry);
|
|
|
|
//
|
|
// We do not increment the connection here yet, since the AcceptConnection
|
|
// will decide and do that.
|
|
//
|
|
|
|
REFERENCE_CONNECTION_COUNT_ENTRY(pCIEntry);
|
|
pConnection->pConnectionCountEntry = pCIEntry;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// There was an old entry, that means this connection already got through.
|
|
// And the entry hasn't been changed with this new request.
|
|
// No need to check again, our design is not forcing existing connections
|
|
// to disconnect.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
REFERENCE_CONNECTION_COUNT_ENTRY(pCIEntry);
|
|
pConnection->pConnectionCountEntry = pCIEntry;
|
|
}
|
|
|
|
Accept = UlAcceptConnection(
|
|
&pConnection->pConnectionCountEntry->MaxConnections,
|
|
&pConnection->pConnectionCountEntry->CurConnections
|
|
);
|
|
|
|
if (Accept == FALSE)
|
|
{
|
|
// We are going to refuse. Let our ref & refcount go away
|
|
// on the connection etry to prevent the possible incorrect
|
|
// decrement in the UlConnectionDestroyedWorker. If refused
|
|
// current connection hasn't been incremented in the accept
|
|
// connection. Perf counters also depends on the fact that
|
|
// pConnectionCountEntry will be Null when con got refused
|
|
|
|
DEREFERENCE_CONNECTION_COUNT_ENTRY(pConnection->pConnectionCountEntry);
|
|
pConnection->pConnectionCountEntry = NULL;
|
|
}
|
|
|
|
return Accept;
|
|
|
|
} // UlCheckSiteConnectionLimit
|
|
|
|
|
|
#ifdef REUSE_CONNECTION_OPAQUE_ID
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Allocate a request opaque ID.
|
|
|
|
Return Value:
|
|
|
|
NT_SUCCESS
|
|
|
|
--***************************************************************************/
|
|
|
|
NTSTATUS
|
|
UlAllocateRequestId(
|
|
IN PUL_INTERNAL_REQUEST pRequest
|
|
)
|
|
{
|
|
PUL_HTTP_CONNECTION pConnection;
|
|
KIRQL OldIrql;
|
|
|
|
PAGED_CODE();
|
|
|
|
pConnection = pRequest->pHttpConn;
|
|
|
|
UlAcquireSpinLock(&pConnection->RequestIdSpinLock, &OldIrql);
|
|
pConnection->pRequestIdContext = pRequest;
|
|
UlReleaseSpinLock(&pConnection->RequestIdSpinLock, OldIrql);
|
|
|
|
pRequest->RequestId = pConnection->ConnectionId;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Free a request opaque ID.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--***************************************************************************/
|
|
|
|
VOID
|
|
UlFreeRequestId(
|
|
IN PUL_INTERNAL_REQUEST pRequest
|
|
)
|
|
{
|
|
PUL_HTTP_CONNECTION pConnection;
|
|
KIRQL OldIrql;
|
|
|
|
PAGED_CODE();
|
|
|
|
pConnection = pRequest->pHttpConn;
|
|
|
|
UlAcquireSpinLock(&pConnection->RequestIdSpinLock, &OldIrql);
|
|
pConnection->pRequestIdContext = NULL;
|
|
UlReleaseSpinLock(&pConnection->RequestIdSpinLock, OldIrql);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/***************************************************************************++
|
|
|
|
Routine Description:
|
|
|
|
Get a request from the connection opaque ID.
|
|
|
|
Return Value:
|
|
|
|
PUL_INTERNAL_REQUEST
|
|
|
|
--***************************************************************************/
|
|
|
|
PUL_INTERNAL_REQUEST
|
|
UlGetRequestFromId(
|
|
IN HTTP_REQUEST_ID RequestId
|
|
)
|
|
{
|
|
PUL_HTTP_CONNECTION pConnection;
|
|
PUL_INTERNAL_REQUEST pRequest;
|
|
KIRQL OldIrql;
|
|
|
|
PAGED_CODE();
|
|
|
|
pConnection = UlGetConnectionFromId(RequestId);
|
|
|
|
if (pConnection != NULL)
|
|
{
|
|
UlAcquireSpinLock(&pConnection->RequestIdSpinLock, &OldIrql);
|
|
|
|
pRequest = pConnection->pRequestIdContext;
|
|
|
|
if (pRequest != NULL)
|
|
{
|
|
UL_REFERENCE_INTERNAL_REQUEST(pRequest);
|
|
}
|
|
|
|
UlReleaseSpinLock(&pConnection->RequestIdSpinLock, OldIrql);
|
|
|
|
UL_DEREFERENCE_HTTP_CONNECTION(pConnection);
|
|
|
|
return pRequest;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|