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.
1899 lines
42 KiB
1899 lines
42 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name :
|
|
workerrequest.cxx
|
|
|
|
Abstract:
|
|
UL_NATIVE_REQUEST implementation
|
|
|
|
Author:
|
|
Murali R. Krishnan ( MuraliK ) 23-Oct-1998
|
|
Lei Jin ( leijin ) 13-Apr-1999 (Porting)
|
|
|
|
Environment:
|
|
Win32 - User Mode
|
|
|
|
Project:
|
|
ULATQ.DLL
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
//
|
|
// User supplied completion routines
|
|
//
|
|
|
|
extern PFN_ULATQ_NEW_REQUEST g_pfnNewRequest;
|
|
extern PFN_ULATQ_IO_COMPLETION g_pfnIoCompletion;
|
|
|
|
//
|
|
// UL_NATIVE_REQUEST statics
|
|
//
|
|
|
|
ULONG UL_NATIVE_REQUEST::sm_cRequestsServed;
|
|
ULONG UL_NATIVE_REQUEST::sm_cRestart;
|
|
LONG UL_NATIVE_REQUEST::sm_RestartMsgSent;
|
|
LIST_ENTRY UL_NATIVE_REQUEST::sm_RequestListHead;
|
|
CRITICAL_SECTION UL_NATIVE_REQUEST::sm_csRequestList;
|
|
DWORD UL_NATIVE_REQUEST::sm_cRequests;
|
|
PTRACE_LOG UL_NATIVE_REQUEST::sm_pTraceLog;
|
|
DWORD UL_NATIVE_REQUEST::sm_cRequestsPending;
|
|
DWORD UL_NATIVE_REQUEST::sm_cDesiredPendingRequests;
|
|
BOOL UL_NATIVE_REQUEST::sm_fAddingRequests;
|
|
ALLOC_CACHE_HANDLER * UL_NATIVE_REQUEST::sm_pachNativeRequests;
|
|
DWORD UL_NATIVE_REQUEST::sm_cMaxFreeRequests = DEFAULT_MAX_FREE_REQUESTS;
|
|
DWORD UL_NATIVE_REQUEST::sm_cFreeRequests;
|
|
#ifdef _WIN64
|
|
CSpinLock UL_NATIVE_REQUEST::sm_FreeListLock;
|
|
SINGLE_LIST_ENTRY UL_NATIVE_REQUEST::sm_FreeList;
|
|
#else
|
|
SLIST_HEADER UL_NATIVE_REQUEST::sm_FreeList;
|
|
#endif
|
|
HANDLE UL_NATIVE_REQUEST::sm_hPendingRequestsTimer = NULL;
|
|
|
|
|
|
UL_NATIVE_REQUEST::UL_NATIVE_REQUEST(
|
|
VOID
|
|
) : _pbBuffer( _achBuffer ),
|
|
_cbBuffer( sizeof( _achBuffer ) ),
|
|
_pClientCertInfo( NULL ),
|
|
_cbAllocateMemoryOffset( 0 ),
|
|
_cRefs( 1 )
|
|
{
|
|
InitializeListHead( &_ListEntry );
|
|
|
|
_FreeListEntry.Next = NULL;
|
|
|
|
AddToRequestList();
|
|
|
|
Reset();
|
|
|
|
_dwSignature = UL_NATIVE_REQUEST_SIGNATURE;
|
|
}
|
|
|
|
UL_NATIVE_REQUEST::~UL_NATIVE_REQUEST(
|
|
VOID
|
|
)
|
|
{
|
|
DBG_ASSERT( CheckSignature() );
|
|
_dwSignature = UL_NATIVE_REQUEST_SIGNATURE_FREE;
|
|
|
|
if ( !IsListEmpty( &_ListEntry ) )
|
|
{
|
|
RemoveFromRequestList();
|
|
}
|
|
|
|
DBG_ASSERT( _cRefs == 0 );
|
|
|
|
Cleanup();
|
|
}
|
|
|
|
VOID
|
|
UL_NATIVE_REQUEST::Cleanup(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Private helper method called by destructor and Reset()
|
|
It assures that resources are cleaned up properly
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if ( _pbBuffer != _achBuffer )
|
|
{
|
|
LocalFree( _pbBuffer );
|
|
}
|
|
_pbBuffer = _achBuffer;
|
|
_cbBuffer = sizeof( _achBuffer );
|
|
|
|
//
|
|
// http.sys returns token in HTTP_SSL_CLIENT_CERT_INFO structure
|
|
// UL_NATIVE_REQUEST must take care of closing the token to avoid leak
|
|
//
|
|
|
|
if ( _pClientCertInfo != NULL &&
|
|
_pClientCertInfo->Token != NULL )
|
|
{
|
|
CloseHandle( _pClientCertInfo->Token );
|
|
_pClientCertInfo->Token = NULL;
|
|
}
|
|
|
|
//
|
|
// Start allocating from the beginning of the buffer
|
|
//
|
|
|
|
_cbAllocateMemoryOffset = 0;
|
|
}
|
|
|
|
|
|
VOID
|
|
UL_NATIVE_REQUEST::Reset(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reset a UL_NATIVE_REQUEST for use in the request state machine
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
Cleanup();
|
|
|
|
_ExecState = NREQ_STATE_START;
|
|
_pvContext = NULL;
|
|
_cbAsyncIOData = 0;
|
|
_dwAsyncIOError = NO_ERROR;
|
|
_dwClientCertFlags = 0;
|
|
|
|
ZeroMemory( &_Overlapped, sizeof(_Overlapped) );
|
|
}
|
|
|
|
VOID *
|
|
UL_NATIVE_REQUEST::AllocateMemory(
|
|
DWORD cbSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate some memory for use by W3DT.DLL user
|
|
in our private buffer
|
|
|
|
Arguments:
|
|
|
|
cbSize - Size to allocate
|
|
|
|
Return Value:
|
|
|
|
Pointer to memory (NULL if failed)
|
|
|
|
--*/
|
|
{
|
|
DWORD cbSizeNeeded = 0;
|
|
VOID * pvRet;
|
|
ULONG_PTR ulPointer;
|
|
|
|
cbSizeNeeded = _cbAllocateMemoryOffset + cbSize + sizeof( ULONG_PTR );
|
|
|
|
if ( cbSizeNeeded <= sizeof( _abAllocateMemory ) )
|
|
{
|
|
//
|
|
// Allocate inline
|
|
//
|
|
|
|
ulPointer = (ULONG_PTR) _abAllocateMemory + _cbAllocateMemoryOffset;
|
|
ulPointer = ( ulPointer + 7 ) & ~7;
|
|
pvRet = (VOID*) ulPointer;
|
|
|
|
_cbAllocateMemoryOffset += cbSize + sizeof( ULONG_PTR );
|
|
}
|
|
else
|
|
{
|
|
pvRet = NULL;
|
|
}
|
|
|
|
return pvRet;
|
|
}
|
|
|
|
VOID
|
|
UL_NATIVE_REQUEST::DoWork(
|
|
DWORD cbData,
|
|
DWORD dwError,
|
|
LPOVERLAPPED lpo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The driver of the state machine. It is called off async completions and
|
|
initially (with lpo=NULL) to kick off the state machine.
|
|
|
|
Arguments:
|
|
|
|
cbData - Bytes completed
|
|
dwError - Win32 Error of completion
|
|
lpo - Pointer to OVERLAPPED passed in async call
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NREQ_STATUS Status = NSTATUS_NEXT;
|
|
BOOL fError = FALSE;
|
|
|
|
ReferenceWorkerRequest();
|
|
|
|
if ( lpo != NULL )
|
|
{
|
|
//
|
|
// This is an async completion. Dereference the request to match
|
|
// the reference before posting async operation
|
|
//
|
|
|
|
DereferenceWorkerRequest();
|
|
|
|
//
|
|
// Save away the completion data for state handler use and debugging
|
|
// purposes
|
|
//
|
|
|
|
_cbAsyncIOData = cbData;
|
|
_dwAsyncIOError = dwError;
|
|
|
|
ZeroMemory( &_Overlapped, sizeof( _Overlapped ) );
|
|
}
|
|
|
|
//
|
|
// Keep on executing the state machine until a state handler returns
|
|
// pending. If so, the IO completion will continue the machine
|
|
//
|
|
// We also break out on error (typical case will be shutdown)
|
|
//
|
|
|
|
while ( !fError && Status == NSTATUS_NEXT )
|
|
{
|
|
switch ( _ExecState )
|
|
{
|
|
case NREQ_STATE_START:
|
|
Status = DoStateStart();
|
|
break;
|
|
|
|
case NREQ_STATE_READ:
|
|
Status = DoStateRead();
|
|
break;
|
|
|
|
case NREQ_STATE_PROCESS:
|
|
Status = DoStateProcess();
|
|
break;
|
|
|
|
case NREQ_STATE_ERROR:
|
|
fError = TRUE;
|
|
DereferenceWorkerRequest();
|
|
break;
|
|
|
|
case NREQ_STATE_CLIENT_CERT:
|
|
Status = DoStateClientCertificate();
|
|
break;
|
|
|
|
default:
|
|
fError = TRUE;
|
|
DBG_ASSERT( FALSE );
|
|
break;
|
|
}
|
|
}
|
|
|
|
DereferenceWorkerRequest();
|
|
}
|
|
|
|
NREQ_STATUS
|
|
UL_NATIVE_REQUEST::DoStateStart(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The NREQ_START state. This state does the initial read for a new HTTP
|
|
request (it passes a NULL request ID). Continuing the read process (
|
|
for example if the buffer is too small) happens in the NREQ_READ state.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NSTATUS_PENDING if async IO pending, else NSTATUS_NEXT
|
|
|
|
--*/
|
|
{
|
|
ULONG rc;
|
|
HTTP_REQUEST_ID RequestId;
|
|
HANDLE hAsync;
|
|
|
|
//
|
|
// This is the initial read, therefore we don't know the request ID
|
|
//
|
|
|
|
HTTP_SET_NULL_ID( &RequestId );
|
|
|
|
//
|
|
// If we are in shutdown, then just bail with error
|
|
//
|
|
|
|
if ( g_pwpContext->IsInShutdown() )
|
|
{
|
|
_ExecState = NREQ_STATE_ERROR;
|
|
return NSTATUS_NEXT;
|
|
}
|
|
|
|
//
|
|
// Have we served enough requests?
|
|
//
|
|
|
|
if ( sm_cRestart &&
|
|
sm_cRequestsServed >= sm_cRestart )
|
|
{
|
|
//
|
|
// Indicate to WAS such and the error out
|
|
//
|
|
|
|
if ( NeedToSendRestartMsg() )
|
|
{
|
|
g_pwpContext->SendMsgToAdminProcess( IPM_WP_RESTART_COUNT_REACHED );
|
|
}
|
|
|
|
_ExecState = NREQ_STATE_ERROR;
|
|
return NSTATUS_NEXT;
|
|
}
|
|
|
|
//
|
|
// Make an async call to HttpReceiveHttpRequest to get the next request
|
|
//
|
|
|
|
_ExecState = NREQ_STATE_READ;
|
|
|
|
InterlockedIncrement( (LPLONG) &sm_cRequestsPending );
|
|
|
|
ReferenceWorkerRequest();
|
|
|
|
hAsync = g_pwpContext->GetAndLockAsyncHandle();
|
|
|
|
if ( hAsync != NULL )
|
|
{
|
|
rc = HttpReceiveHttpRequest( hAsync,
|
|
RequestId,
|
|
HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY,
|
|
(HTTP_REQUEST *) _pbBuffer,
|
|
_cbBuffer,
|
|
NULL,
|
|
&_Overlapped );
|
|
}
|
|
else
|
|
{
|
|
rc = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DBG_REQUIRE(SUCCEEDED(g_pwpContext->UnlockAsyncHandle()));
|
|
|
|
if ( rc == NO_ERROR )
|
|
{
|
|
rc = ERROR_IO_PENDING;
|
|
}
|
|
|
|
if ( rc != ERROR_IO_PENDING )
|
|
{
|
|
DBG_ASSERT( rc != NO_ERROR );
|
|
|
|
InterlockedDecrement( (LPLONG) &sm_cRequestsPending );
|
|
|
|
_ExecState = NREQ_STATE_ERROR;
|
|
|
|
DereferenceWorkerRequest();
|
|
|
|
return NSTATUS_NEXT;
|
|
}
|
|
else
|
|
{
|
|
return NSTATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
|
|
NREQ_STATUS
|
|
UL_NATIVE_REQUEST::DoStateRead(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The NREQ_READ state. This state is responsible for producing a complete
|
|
UL_HTTP_REQUEST for consumption by the NREQ_PROCESS state. Note that
|
|
this may require another UlReceiveHttpRequest() if the initial was not
|
|
supplied a large enough buffer.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NSTATUS_PENDING if async IO pending, else NSTATUS_NEXT
|
|
|
|
--*/
|
|
{
|
|
HTTP_REQUEST_ID RequestId;
|
|
ULONG cbRequired;
|
|
DWORD cCurrentRequestsPending;
|
|
HANDLE hAsync;
|
|
|
|
|
|
//
|
|
// The initial read is complete. If the error is ERROR_MORE_DATA, then
|
|
// our buffer was not large enough. Resize and try again
|
|
//
|
|
|
|
if ( _dwAsyncIOError == ERROR_MORE_DATA )
|
|
{
|
|
//
|
|
// Remember the request ID to retry the the UlReceiveHttpRequest
|
|
//
|
|
RequestId = QueryRequestId();
|
|
DBG_ASSERT( RequestId != HTTP_NULL_ID );
|
|
|
|
//
|
|
// We need to allocate a larger buffer :(
|
|
//
|
|
|
|
if ( _pbBuffer != _achBuffer )
|
|
{
|
|
//
|
|
// We shouldn't be getting ERROR_MORE_DATA if we already
|
|
// resized due to an earlier ERROR_MORE_DATA
|
|
//
|
|
|
|
DBG_ASSERT( FALSE );
|
|
LocalFree( _pbBuffer );
|
|
_pbBuffer = NULL;
|
|
}
|
|
|
|
//
|
|
// The completed bytes tells us the required size of our input
|
|
// buffer
|
|
//
|
|
|
|
_cbBuffer = _cbAsyncIOData;
|
|
_pbBuffer = (UCHAR*) LocalAlloc( LPTR, _cbBuffer );
|
|
if ( _pbBuffer == NULL )
|
|
{
|
|
_ExecState = NREQ_STATE_ERROR;
|
|
return NSTATUS_NEXT;
|
|
}
|
|
|
|
//
|
|
// Read the HTTP request again (better to do it sychronously, since
|
|
// it is all ready now)
|
|
//
|
|
|
|
hAsync = g_pwpContext->GetAndLockAsyncHandle();
|
|
|
|
if ( hAsync != NULL )
|
|
{
|
|
_dwAsyncIOError = HttpReceiveHttpRequest(
|
|
hAsync,
|
|
RequestId,
|
|
HTTP_RECEIVE_REQUEST_FLAG_COPY_BODY,
|
|
(HTTP_REQUEST *)_pbBuffer,
|
|
_cbBuffer,
|
|
&cbRequired,
|
|
NULL );
|
|
}
|
|
else
|
|
{
|
|
_dwAsyncIOError = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DBG_REQUIRE(SUCCEEDED(g_pwpContext->UnlockAsyncHandle()));
|
|
|
|
//
|
|
// No reason this should fail again with ERROR_MORE_DATA
|
|
//
|
|
DBG_ASSERT( _dwAsyncIOError != ERROR_MORE_DATA );
|
|
}
|
|
|
|
if ( _dwAsyncIOError == NO_ERROR )
|
|
{
|
|
//
|
|
// We're done. The read was successful and we have a full
|
|
// UL_HTTP_REQUEST. We can now pass off to the NREQ_PROCESS state
|
|
//
|
|
|
|
cCurrentRequestsPending = InterlockedDecrement( (LPLONG)
|
|
&sm_cRequestsPending );
|
|
|
|
if ( cCurrentRequestsPending < sm_cDesiredPendingRequests / 2 )
|
|
{
|
|
AddPendingRequests( sm_cDesiredPendingRequests - cCurrentRequestsPending );
|
|
}
|
|
|
|
_ExecState = NREQ_STATE_PROCESS;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Some other error. Try again
|
|
//
|
|
|
|
InterlockedDecrement( (LPLONG) &sm_cRequestsPending );
|
|
|
|
Reset();
|
|
}
|
|
|
|
return NSTATUS_NEXT;
|
|
}
|
|
|
|
NREQ_STATUS
|
|
UL_NATIVE_REQUEST::DoStateProcess(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The NREQ_PROCESS state. This state actually calls consumer of the
|
|
NewRequests and IoCompletions. This state calls the set routines in
|
|
W3CORE.DLL
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NSTATUS_PENDING if async IO pending, else NSTATUS_NEXT
|
|
|
|
--*/
|
|
{
|
|
WP_IDLE_TIMER * pTimer;
|
|
|
|
//
|
|
// Reset idle tick since we're not idle if we're processing
|
|
//
|
|
|
|
pTimer = g_pwpContext->QueryIdleTimer();
|
|
if ( pTimer != NULL )
|
|
{
|
|
pTimer->ResetCurrentIdleTick();
|
|
}
|
|
|
|
ReferenceWorkerRequest();
|
|
|
|
if ( _pvContext == NULL )
|
|
{
|
|
//
|
|
// Extra reference here. The consumer must call UlAtqResetContext()
|
|
// to finally cleanup. This complication is brought on by
|
|
// scenarios where non-IO-completions are required to finish a request
|
|
// (example: async ISAPI)
|
|
//
|
|
|
|
ReferenceWorkerRequest();
|
|
|
|
DBG_ASSERT( g_pfnNewRequest != NULL );
|
|
|
|
g_pfnNewRequest( this );
|
|
}
|
|
else
|
|
{
|
|
DBG_ASSERT( g_pfnIoCompletion != NULL );
|
|
|
|
g_pfnIoCompletion( _pvContext,
|
|
_cbAsyncIOData,
|
|
_dwAsyncIOError,
|
|
&_Overlapped );
|
|
}
|
|
|
|
DereferenceWorkerRequest();
|
|
|
|
return NSTATUS_PENDING;
|
|
}
|
|
|
|
NREQ_STATUS
|
|
UL_NATIVE_REQUEST::DoStateClientCertificate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Handle a completion for receiving a client certificate
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NSTATUS_PENDING if async IO pending, else NSTATUS_NEXT
|
|
|
|
--*/
|
|
{
|
|
ULONG Status;
|
|
HTTP_REQUEST * pRequest;
|
|
HANDLE hAsync;
|
|
|
|
DBG_ASSERT( _ExecState == NREQ_STATE_CLIENT_CERT );
|
|
|
|
//
|
|
// Is our buffer too small. If so retry the request synchronously with
|
|
// a bigger buffer
|
|
// Note: STATUS_BUFFER_OVERFLOW is translated to WIN32 Error - ERROR_MORE_DATA
|
|
|
|
if ( _dwAsyncIOError == ERROR_MORE_DATA )
|
|
{
|
|
//
|
|
// If buffer is not big enough, HTTP.sys will return only HTTP_SSL_CLIENT_CERT_INFO
|
|
// structure that contains the size of the client certificate
|
|
// The following assert is to assure that HTTP.SYS returns back that
|
|
// HTTP_SSL_CLIENT_CERT_INFO structure and nothing more
|
|
|
|
DBG_ASSERT( _cbAsyncIOData == sizeof( HTTP_SSL_CLIENT_CERT_INFO ) );
|
|
|
|
//
|
|
// We need to allocate enough memory to contain HTTP_SSL_CLIENT_CERT_INFO
|
|
// and certificate blob
|
|
//
|
|
DWORD dwRequiredSize = _pClientCertInfo->CertEncodedSize +
|
|
sizeof( HTTP_SSL_CLIENT_CERT_INFO );
|
|
|
|
DBG_ASSERT( dwRequiredSize > _buffClientCertInfo.QuerySize() );
|
|
|
|
if ( !_buffClientCertInfo.Resize( dwRequiredSize ) )
|
|
{
|
|
//
|
|
// Funnel the fatal error to the complete
|
|
//
|
|
|
|
_dwAsyncIOError = GetLastError();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Retry the request for client cert synchronously
|
|
//
|
|
_pClientCertInfo = reinterpret_cast<HTTP_SSL_CLIENT_CERT_INFO *>( _buffClientCertInfo.QueryPtr() );
|
|
|
|
hAsync = g_pwpContext->GetAndLockAsyncHandle();
|
|
|
|
if ( hAsync != NULL )
|
|
{
|
|
|
|
Status = HttpReceiveClientCertificate( hAsync,
|
|
QueryConnectionId(),
|
|
_dwClientCertFlags,
|
|
_pClientCertInfo,
|
|
_buffClientCertInfo.QuerySize(),
|
|
NULL,
|
|
NULL );
|
|
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DBG_REQUIRE(SUCCEEDED(g_pwpContext->UnlockAsyncHandle()));
|
|
|
|
if ( Status != NO_ERROR )
|
|
{
|
|
DBG_ASSERT( _dwAsyncIOError != ERROR_MORE_DATA );
|
|
}
|
|
|
|
_dwAsyncIOError = Status;
|
|
_cbAsyncIOData = dwRequiredSize;
|
|
|
|
}
|
|
}
|
|
|
|
if ( _dwAsyncIOError == NO_ERROR )
|
|
{
|
|
pRequest = QueryHttpRequest();
|
|
|
|
DBG_ASSERT( pRequest->pSslInfo != NULL );
|
|
DBG_ASSERT( pRequest->pSslInfo->pClientCertInfo == NULL );
|
|
|
|
pRequest->pSslInfo->pClientCertInfo = _pClientCertInfo;
|
|
}
|
|
|
|
//
|
|
// Regardless of what happened, we are no longer processing a client cert
|
|
//
|
|
|
|
_ExecState = NREQ_STATE_PROCESS;
|
|
|
|
DBG_ASSERT( g_pfnIoCompletion != NULL );
|
|
|
|
g_pfnIoCompletion( _pvContext,
|
|
_cbAsyncIOData,
|
|
_dwAsyncIOError,
|
|
&_Overlapped );
|
|
|
|
return NSTATUS_PENDING;
|
|
}
|
|
|
|
VOID
|
|
UL_NATIVE_REQUEST::ResetContext(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The implementation of UlAtqResetContext which the consumer of W3DT.DLL
|
|
calls to cleanup the context.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DereferenceWorkerRequest();
|
|
}
|
|
|
|
VOID
|
|
UL_NATIVE_REQUEST::ReferenceWorkerRequest(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Increment the reference count on the worker request
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
LONG cRefs = InterlockedIncrement( &_cRefs );
|
|
|
|
//
|
|
// Log the reference ( sm_pTraceLog!=NULL if DBG=1)
|
|
//
|
|
|
|
if ( sm_pTraceLog != NULL )
|
|
{
|
|
WriteRefTraceLog( sm_pTraceLog,
|
|
cRefs,
|
|
this );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UL_NATIVE_REQUEST::DereferenceWorkerRequest(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dereference Request. This routine will optionally reset the context
|
|
for use for reading the next HTTP request. Putting the reset code in
|
|
one place (here) handles all the cases where we would want to reset.
|
|
However, there are a few places where we definitely don't want to reset
|
|
(in the case of error where the context will be going away)
|
|
|
|
In either case, if the ref count goes to 0, we can delete the object.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
LONG cRefs = InterlockedDecrement( &_cRefs );
|
|
|
|
if ( sm_pTraceLog != NULL )
|
|
{
|
|
WriteRefTraceLog( sm_pTraceLog,
|
|
cRefs,
|
|
this );
|
|
}
|
|
|
|
if ( cRefs == 0 )
|
|
{
|
|
//
|
|
// If 0, we can definitely cleanup, regardless. This is the error
|
|
// case which is used to cleanup contexts on shutdown (on shutdown,
|
|
// the apppool handle is closed which will error out all pending
|
|
// UlReceiveHttpRequests
|
|
//
|
|
|
|
delete this;
|
|
}
|
|
else if ( cRefs == 1 && _ExecState != NREQ_STATE_ERROR )
|
|
{
|
|
//
|
|
// Reset the state machine. Now we can increment our served count
|
|
//
|
|
|
|
InterlockedIncrement( (PLONG) &sm_cRequestsServed );
|
|
|
|
//
|
|
// If we have too many outstanding requests, then don't pend
|
|
// another receive. Keep the request around ready to go
|
|
//
|
|
|
|
if ( sm_cRequestsPending > sm_cDesiredPendingRequests * 2 )
|
|
{
|
|
FreeWorkerRequest( this );
|
|
}
|
|
else
|
|
{
|
|
Reset();
|
|
|
|
//
|
|
// Re-kickoff the state machine
|
|
//
|
|
|
|
DoWork( 0, 0, NULL );
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
UL_NATIVE_REQUEST::RemoveFromRequestList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Remove this UL_NATIVE_REQUEST from the static list of requests. Main
|
|
purpose of the list is for debugging.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
EnterCriticalSection( &sm_csRequestList );
|
|
|
|
RemoveEntryList( &_ListEntry );
|
|
sm_cRequests--;
|
|
|
|
LeaveCriticalSection( &sm_csRequestList );
|
|
}
|
|
|
|
VOID
|
|
UL_NATIVE_REQUEST::AddToRequestList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add this request to the static list of requests. The main purpose of the
|
|
list is for debugging.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
EnterCriticalSection( &sm_csRequestList );
|
|
|
|
sm_cRequests++;
|
|
InsertTailList( &sm_RequestListHead, &_ListEntry );
|
|
|
|
LeaveCriticalSection( &sm_csRequestList );
|
|
}
|
|
|
|
HRESULT
|
|
UL_NATIVE_REQUEST::SendResponse(
|
|
BOOL fAsync,
|
|
DWORD dwFlags,
|
|
HTTP_RESPONSE * pResponse,
|
|
HTTP_CACHE_POLICY * pCachePolicy,
|
|
DWORD *pcbSent,
|
|
HTTP_LOG_FIELDS_DATA *pUlLogData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send an HTTP response thru UL.
|
|
|
|
Arguments:
|
|
|
|
fAsync - TRUE if send is async
|
|
dwFlags - UlSendHttpResponse flags
|
|
pResponse - Pointer to UL_HTTP_RESPONSE
|
|
pCachePolicy - Cache policy
|
|
pcbSent - Receives number of bytes send
|
|
pULLogData - Logging information
|
|
|
|
Return Value:
|
|
|
|
HRESULT (if pending, the return is NO_ERROR)
|
|
|
|
--*/
|
|
{
|
|
ULONG Status = NO_ERROR;
|
|
HRESULT hr = NO_ERROR;
|
|
HANDLE hAsync;
|
|
|
|
if ( pcbSent == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
if ( fAsync )
|
|
{
|
|
ReferenceWorkerRequest();
|
|
}
|
|
|
|
hAsync = g_pwpContext->GetAndLockAsyncHandle();
|
|
|
|
if ( hAsync != NULL )
|
|
{
|
|
Status = HttpSendHttpResponse( hAsync,
|
|
QueryRequestId(),
|
|
dwFlags,
|
|
pResponse,
|
|
pCachePolicy,
|
|
fAsync ? NULL : pcbSent,
|
|
NULL,
|
|
0,
|
|
fAsync ? &(_Overlapped) : NULL ,
|
|
pUlLogData );
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DBG_REQUIRE(SUCCEEDED(g_pwpContext->UnlockAsyncHandle()));
|
|
|
|
//
|
|
// If the response is pending, then we return a successful error code.
|
|
// This frees the caller from the ERROR_IO_PENDING checks in common
|
|
// case
|
|
//
|
|
|
|
if ( fAsync )
|
|
{
|
|
if ( Status == NO_ERROR )
|
|
{
|
|
Status = ERROR_IO_PENDING;
|
|
}
|
|
|
|
DBG_ASSERT( Status != NO_ERROR );
|
|
|
|
if ( Status != ERROR_IO_PENDING )
|
|
{
|
|
_Overlapped.Internal = Status;
|
|
|
|
DereferenceWorkerRequest();
|
|
hr = HRESULT_FROM_WIN32( Status );
|
|
}
|
|
}
|
|
else if ( Status != NO_ERROR )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( Status );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
UL_NATIVE_REQUEST::SendEntity(
|
|
BOOL fAsync,
|
|
DWORD dwFlags,
|
|
USHORT cChunks,
|
|
HTTP_DATA_CHUNK * pChunks,
|
|
DWORD *pcbSent,
|
|
HTTP_LOG_FIELDS_DATA *pUlLogData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Send an HTTP entity thru UL.
|
|
|
|
Arguments:
|
|
|
|
fAsync - TRUE if send is async
|
|
dwFlags - UlSendHttpResponse flags
|
|
cChunks - Number of chunks in response
|
|
pChunks - Pointer to array of UL_DATA_CHUNKs
|
|
pcbSent - Receives number of bytes sent
|
|
pUlLogData - Log information
|
|
|
|
Return Value:
|
|
|
|
HRESULT (if pending, the return is NO_ERROR)
|
|
|
|
--*/
|
|
{
|
|
ULONG Status = NO_ERROR;
|
|
HRESULT hr = NO_ERROR;
|
|
HANDLE hAsync;
|
|
|
|
if ( pcbSent == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
if ( fAsync )
|
|
{
|
|
ReferenceWorkerRequest();
|
|
}
|
|
|
|
hAsync = g_pwpContext->GetAndLockAsyncHandle();
|
|
|
|
if ( hAsync != NULL )
|
|
{
|
|
Status = HttpSendResponseEntityBody( hAsync,
|
|
QueryRequestId(),
|
|
dwFlags,
|
|
cChunks,
|
|
pChunks,
|
|
fAsync ? NULL : pcbSent,
|
|
NULL,
|
|
0,
|
|
fAsync ? &(_Overlapped) : NULL,
|
|
pUlLogData );
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DBG_REQUIRE(SUCCEEDED(g_pwpContext->UnlockAsyncHandle()));
|
|
|
|
//
|
|
// If the send is pending, then we return a successful error code.
|
|
// This frees the caller from the ERROR_IO_PENDING checks in common
|
|
// case
|
|
//
|
|
|
|
if ( fAsync )
|
|
{
|
|
if ( Status == NO_ERROR )
|
|
{
|
|
Status = ERROR_IO_PENDING;
|
|
}
|
|
|
|
DBG_ASSERT( Status != NO_ERROR );
|
|
|
|
if ( Status != ERROR_IO_PENDING )
|
|
{
|
|
_Overlapped.Internal = Status;
|
|
|
|
DereferenceWorkerRequest();
|
|
hr = HRESULT_FROM_WIN32( Status );
|
|
}
|
|
}
|
|
else if ( Status != NO_ERROR )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( Status );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
UL_NATIVE_REQUEST::ReceiveEntity(
|
|
BOOL fAsync,
|
|
DWORD dwFlags,
|
|
VOID * pBuffer,
|
|
DWORD cbBuffer,
|
|
DWORD * pBytesReceived
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Receive HTTP entity thru UL.
|
|
|
|
Arguments:
|
|
|
|
fAsync - TRUE if receive is async
|
|
dwFlags - UlSendHttpResponse flags
|
|
pBuffer - A buffer to receive the data
|
|
cbBuffer - The size of the receive buffer
|
|
pBytesReceived - Upon return, the number of bytes
|
|
copied to the buffer
|
|
|
|
Return Value:
|
|
|
|
HRESULT (if pending, the return is NO_ERROR)
|
|
|
|
--*/
|
|
{
|
|
ULONG Status = NO_ERROR;
|
|
HRESULT hr = NO_ERROR;
|
|
HANDLE hAsync;
|
|
|
|
if ( fAsync )
|
|
{
|
|
ReferenceWorkerRequest();
|
|
}
|
|
|
|
hAsync = g_pwpContext->GetAndLockAsyncHandle();
|
|
|
|
if ( hAsync != NULL )
|
|
{
|
|
Status = HttpReceiveRequestEntityBody( hAsync,
|
|
QueryRequestId(),
|
|
dwFlags,
|
|
pBuffer,
|
|
cbBuffer,
|
|
fAsync ? NULL : pBytesReceived,
|
|
fAsync ? &(_Overlapped) : NULL );
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DBG_REQUIRE(SUCCEEDED(g_pwpContext->UnlockAsyncHandle()));
|
|
|
|
//
|
|
// If the receive is pending, then we return a successful error code.
|
|
// This frees the caller from the ERROR_IO_PENDING checks in common
|
|
// case
|
|
//
|
|
|
|
if ( fAsync )
|
|
{
|
|
if ( Status == NO_ERROR )
|
|
{
|
|
Status = ERROR_IO_PENDING;
|
|
}
|
|
|
|
DBG_ASSERT( Status != NO_ERROR );
|
|
|
|
if ( Status != ERROR_IO_PENDING )
|
|
{
|
|
_Overlapped.Internal = Status;
|
|
|
|
DereferenceWorkerRequest();
|
|
hr = HRESULT_FROM_WIN32( Status );
|
|
}
|
|
}
|
|
else if ( Status != NO_ERROR )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( Status );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
UL_NATIVE_REQUEST::ReceiveClientCertificate(
|
|
BOOL fAsync,
|
|
BOOL fDoCertMap,
|
|
HTTP_SSL_CLIENT_CERT_INFO **ppClientCertInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Receive a client certificate
|
|
|
|
Arguments:
|
|
|
|
fAsync - TRUE if receive should be async
|
|
fDoCertMap - TRUE if we should map client certificate to token
|
|
ppClientCertInfo - Set to point to client cert info on success
|
|
|
|
Return Value:
|
|
|
|
HRESULT (if pending, the return is NO_ERROR)
|
|
|
|
--*/
|
|
{
|
|
ULONG Status;
|
|
HTTP_REQUEST * pHttpRequest;
|
|
HRESULT hr = NO_ERROR;
|
|
HANDLE hAsync;
|
|
|
|
if ( ppClientCertInfo == NULL )
|
|
{
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
*ppClientCertInfo = NULL;
|
|
|
|
//
|
|
// If this request is not SSL enabled, then getting the client cert is
|
|
// a no-go
|
|
//
|
|
|
|
pHttpRequest = QueryHttpRequest();
|
|
DBG_ASSERT( pHttpRequest != NULL );
|
|
|
|
if ( pHttpRequest->pSslInfo == NULL )
|
|
{
|
|
return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED );
|
|
}
|
|
|
|
//
|
|
// Do we already have a cert associated with this request?
|
|
//
|
|
|
|
DBG_ASSERT( pHttpRequest->pSslInfo != NULL );
|
|
|
|
if ( pHttpRequest->pSslInfo->pClientCertInfo != NULL )
|
|
{
|
|
if ( fAsync )
|
|
{
|
|
//
|
|
// BUGBUG: Probably should support this case. And if I do, then
|
|
// need to fake a completion!
|
|
//
|
|
|
|
DBG_ASSERT( FALSE );
|
|
return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
*ppClientCertInfo = pHttpRequest->pSslInfo->pClientCertInfo;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// OK. We'll have to ask UL to renegotiate. We must be processing
|
|
// a request
|
|
//
|
|
|
|
DBG_ASSERT( _ExecState == NREQ_STATE_PROCESS );
|
|
|
|
if ( !_buffClientCertInfo.Resize( INITIAL_CERT_INFO_SIZE ) )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
ZeroMemory( _buffClientCertInfo.QueryPtr(), INITIAL_CERT_INFO_SIZE );
|
|
|
|
_pClientCertInfo = reinterpret_cast<HTTP_SSL_CLIENT_CERT_INFO *>( _buffClientCertInfo.QueryPtr() );
|
|
|
|
//
|
|
// Are we cert mapping?
|
|
//
|
|
|
|
if ( fDoCertMap )
|
|
{
|
|
_dwClientCertFlags = HTTP_RECEIVE_CLIENT_CERT_FLAG_MAP;
|
|
}
|
|
else
|
|
{
|
|
_dwClientCertFlags = 0;
|
|
}
|
|
|
|
//
|
|
// If we're doing this async, then manage state such that
|
|
// DoStateClientCert gets the completion
|
|
//
|
|
|
|
if ( fAsync )
|
|
{
|
|
ReferenceWorkerRequest();
|
|
_ExecState = NREQ_STATE_CLIENT_CERT;
|
|
}
|
|
|
|
hAsync = g_pwpContext->GetAndLockAsyncHandle();
|
|
|
|
if ( hAsync != NULL )
|
|
{
|
|
Status = HttpReceiveClientCertificate( hAsync,
|
|
QueryConnectionId(),
|
|
_dwClientCertFlags,
|
|
_pClientCertInfo,
|
|
_buffClientCertInfo.QuerySize(),
|
|
NULL,
|
|
fAsync ? &_Overlapped : NULL );
|
|
}
|
|
else
|
|
{
|
|
Status = ERROR_INVALID_HANDLE;
|
|
}
|
|
|
|
DBG_REQUIRE(SUCCEEDED(g_pwpContext->UnlockAsyncHandle()));
|
|
|
|
if ( fAsync )
|
|
{
|
|
if ( Status == NO_ERROR )
|
|
{
|
|
Status = ERROR_IO_PENDING;
|
|
}
|
|
|
|
DBG_ASSERT( Status != NO_ERROR );
|
|
|
|
if ( Status != ERROR_IO_PENDING )
|
|
{
|
|
_Overlapped.Internal = Status;
|
|
|
|
DereferenceWorkerRequest();
|
|
hr = HRESULT_FROM_WIN32( Status );
|
|
_ExecState = NREQ_STATE_PROCESS;
|
|
}
|
|
}
|
|
else if ( Status != NO_ERROR )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( Status );
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
UL_NATIVE_REQUEST::Initialize(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Static initialization of UL_NATIVE_REQUESTs
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
ALLOC_CACHE_CONFIGURATION acConfig;
|
|
HKEY hKey;
|
|
BOOL fRet;
|
|
HRESULT hr;
|
|
|
|
//
|
|
// Setup allocation lookaside
|
|
//
|
|
|
|
acConfig.nConcurrency = 1;
|
|
acConfig.nThreshold = 100;
|
|
acConfig.cbSize = sizeof( UL_NATIVE_REQUEST );
|
|
|
|
DBG_ASSERT( sm_pachNativeRequests == NULL );
|
|
|
|
sm_pachNativeRequests = new ALLOC_CACHE_HANDLER( "UL_NATIVE_REQUEST",
|
|
&acConfig );
|
|
if ( sm_pachNativeRequests == NULL )
|
|
{
|
|
return HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
|
|
fRet = InitializeCriticalSectionAndSpinCount( &sm_csRequestList,
|
|
UL_NATIVE_REQUEST_CS_SPINS );
|
|
if ( !fRet )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
delete sm_pachNativeRequests;
|
|
sm_pachNativeRequests = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
InitializeListHead( &sm_RequestListHead );
|
|
|
|
#ifdef _WIN64
|
|
sm_FreeList.Next = NULL;
|
|
#else
|
|
InitializeSListHead( &sm_FreeList );
|
|
#endif
|
|
|
|
sm_cRequestsServed = 0;
|
|
sm_cRestart = 0;
|
|
sm_RestartMsgSent = 0;
|
|
sm_cRequestsPending = 0;
|
|
|
|
//
|
|
// If the number of pending HttpReceiveHttpRequest is configured, use
|
|
// that rather than the default value
|
|
//
|
|
if (RegOpenKeyEx( HKEY_LOCAL_MACHINE,
|
|
REGISTRY_KEY_W3SVC_PERFORMANCE_KEY_W,
|
|
0,
|
|
KEY_READ,
|
|
&hKey ) == ERROR_SUCCESS)
|
|
{
|
|
DWORD cbBuffer = sizeof(DWORD);
|
|
DWORD dwType;
|
|
|
|
if (RegQueryValueEx( hKey,
|
|
L"ReceiveRequestsPending",
|
|
NULL,
|
|
&dwType,
|
|
(LPBYTE)&sm_cDesiredPendingRequests,
|
|
&cbBuffer ) != NO_ERROR ||
|
|
dwType != REG_DWORD)
|
|
{
|
|
sm_cDesiredPendingRequests = DESIRED_PENDING_REQUESTS;
|
|
}
|
|
|
|
RegCloseKey( hKey );
|
|
}
|
|
else
|
|
{
|
|
sm_cDesiredPendingRequests = DESIRED_PENDING_REQUESTS;
|
|
}
|
|
|
|
sm_fAddingRequests = FALSE;
|
|
|
|
#if DBG
|
|
sm_pTraceLog = CreateRefTraceLog( 2000, 0 );
|
|
#endif
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//static
|
|
VOID
|
|
UL_NATIVE_REQUEST::StopListening(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Shutdown the apppool in preparation for shutdown
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
HANDLE hAsync;
|
|
|
|
hAsync = g_pwpContext->GetAndLockAsyncHandle();
|
|
|
|
if ( hAsync != NULL )
|
|
{
|
|
//
|
|
// This will cause us to cancel all pending HttpReceiveHttpRequest calls
|
|
// and not get any more but other operations will still go on
|
|
//
|
|
HttpShutdownAppPool( hAsync );
|
|
}
|
|
|
|
DBG_REQUIRE(SUCCEEDED(g_pwpContext->UnlockAsyncHandle()));
|
|
|
|
}
|
|
|
|
//static
|
|
VOID
|
|
UL_NATIVE_REQUEST::Terminate(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Static termination
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
if ( sm_pTraceLog != NULL )
|
|
{
|
|
DestroyRefTraceLog( sm_pTraceLog );
|
|
sm_pTraceLog = NULL;
|
|
}
|
|
|
|
DeleteCriticalSection( &sm_csRequestList );
|
|
|
|
if ( sm_pachNativeRequests != NULL )
|
|
{
|
|
delete sm_pachNativeRequests;
|
|
sm_pachNativeRequests = NULL;
|
|
}
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
UL_NATIVE_REQUEST::AddPendingRequests(
|
|
DWORD cRequests
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Pools calls to UlReceiveHttpRequest by creating new UL_NATIVE_REQUESTs
|
|
and kicking off their state machines
|
|
|
|
Arguments:
|
|
|
|
cRequests - Number of requests to read
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
UL_NATIVE_REQUEST * pRequest;
|
|
|
|
if ( sm_fAddingRequests )
|
|
{
|
|
return NO_ERROR;
|
|
}
|
|
|
|
if ( InterlockedCompareExchange( (LPLONG) &sm_fAddingRequests,
|
|
TRUE,
|
|
FALSE ) == FALSE )
|
|
{
|
|
for ( DWORD i = 0; i < cRequests; i++ )
|
|
{
|
|
pRequest = AllocateWorkerRequest();
|
|
if ( pRequest == NULL )
|
|
{
|
|
return HRESULT_FROM_WIN32( ERROR_NOT_ENOUGH_MEMORY );
|
|
}
|
|
|
|
pRequest->DoWork( 0, 0, NULL );
|
|
}
|
|
|
|
sm_fAddingRequests = FALSE;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//static
|
|
HRESULT
|
|
UL_NATIVE_REQUEST::ReleaseAllWorkerRequests(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Wait for all outstanding UL_NATIVE_REQUESTs to drain
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
{
|
|
UL_NATIVE_REQUEST * pRequest;
|
|
|
|
//
|
|
// Now, wait for all requests to be destructed
|
|
//
|
|
|
|
while ( sm_cRequests )
|
|
{
|
|
//
|
|
// Clear out any of our free list items. Since this list
|
|
// may be appended to right now, keep on cleaning it
|
|
//
|
|
|
|
for ( ; ; )
|
|
{
|
|
pRequest = PopFreeList();
|
|
if ( pRequest == NULL )
|
|
{
|
|
break;
|
|
}
|
|
|
|
DBG_ASSERT( pRequest != NULL );
|
|
DBG_ASSERT( pRequest->CheckSignature() );
|
|
|
|
//
|
|
// Free it up
|
|
//
|
|
|
|
pRequest->DereferenceWorkerRequest();
|
|
}
|
|
|
|
Sleep( 1000 );
|
|
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"UL_NATIVE_REQUEST::ReleaseAllWorkerRequests waiting for %d requests to drain.\n",
|
|
sm_cRequests ));
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
//static
|
|
VOID
|
|
WINAPI
|
|
UL_NATIVE_REQUEST::PendingRequestsMonitorHandler(
|
|
PVOID,
|
|
BOOLEAN
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if pending requests number is 0.
|
|
If it is 0 then try to add 1 pending request
|
|
to allow this worker process to start handling
|
|
http requests again
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
if ( sm_cRequestsPending == 0 )
|
|
{
|
|
DBGPRINTF((DBG_CONTEXT,
|
|
"Worker process has run out of pending requests. Trying to add one\n"
|
|
));
|
|
|
|
//
|
|
// If pending requests number is 0 then
|
|
// it means there must have been errors on
|
|
// HttpReceiveHttpRequest() call that
|
|
// caused worker process to run out of pending requests
|
|
// It is time to intervene by adding one
|
|
// pending request. That should jump start
|
|
// the process of handling the requests again
|
|
//
|
|
// Note: We don't check the error because there
|
|
// is nothing we can do about the error. If
|
|
// we failed then the next time timer fires and
|
|
// this handler gets called we will simply retry
|
|
|
|
AddPendingRequests( 1 );
|
|
}
|
|
}
|
|
|
|
|
|
//static
|
|
HRESULT
|
|
UL_NATIVE_REQUEST::StartPendingRequestsMonitor(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Start timer to enable monitoring of the pending requests
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
//
|
|
// create timer to wake up every 5 seconds
|
|
//
|
|
//
|
|
if ( !CreateTimerQueueTimer( &sm_hPendingRequestsTimer,
|
|
NULL,
|
|
UL_NATIVE_REQUEST::PendingRequestsMonitorHandler,
|
|
NULL,
|
|
5000,
|
|
5000,
|
|
WT_EXECUTELONGFUNCTION ) )
|
|
{
|
|
hr = HRESULT_FROM_WIN32( GetLastError() );
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
//static
|
|
VOID
|
|
UL_NATIVE_REQUEST::StopPendingRequestsMonitor(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stop timer for monitoring of the pending requests
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
HRESULT
|
|
|
|
--*/
|
|
|
|
{
|
|
if ( sm_hPendingRequestsTimer != NULL )
|
|
{
|
|
if ( !DeleteTimerQueueTimer(
|
|
NULL,
|
|
sm_hPendingRequestsTimer,
|
|
INVALID_HANDLE_VALUE /* wait for callbacks to complete */
|
|
) )
|
|
{
|
|
DBGPRINTF(( DBG_CONTEXT,
|
|
"Failed to delete Ping timer (%d)\n",
|
|
GetLastError() ));
|
|
}
|
|
sm_hPendingRequestsTimer = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//static
|
|
VOID
|
|
UL_NATIVE_REQUEST::FreeWorkerRequest(
|
|
UL_NATIVE_REQUEST * pWorkerRequest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free worker request
|
|
|
|
Arguments:
|
|
|
|
pNativeRequest - Worker request to retrieve
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
DBG_ASSERT( pWorkerRequest != NULL );
|
|
|
|
if ( sm_cFreeRequests > sm_cMaxFreeRequests ||
|
|
g_pwpContext->IsInShutdown() )
|
|
{
|
|
//
|
|
// Don't keep too many around (especially in shutdown ;-)).
|
|
// Just free this guy by dereferencing one more time
|
|
//
|
|
|
|
pWorkerRequest->DereferenceWorkerRequest();
|
|
}
|
|
else
|
|
{
|
|
pWorkerRequest->Reset();
|
|
|
|
PushFreeList( pWorkerRequest );
|
|
|
|
sm_cFreeRequests++;
|
|
}
|
|
}
|
|
|
|
//static
|
|
UL_NATIVE_REQUEST *
|
|
UL_NATIVE_REQUEST::AllocateWorkerRequest(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocate a new request
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Pointer to new request or NULL if error
|
|
|
|
--*/
|
|
{
|
|
UL_NATIVE_REQUEST * pRequest;
|
|
|
|
if ( sm_cFreeRequests )
|
|
{
|
|
pRequest = PopFreeList();
|
|
if ( pRequest != NULL )
|
|
{
|
|
DBG_ASSERT( pRequest->CheckSignature() );
|
|
|
|
sm_cFreeRequests--;
|
|
|
|
return pRequest;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we got to here, we have to allocate a new request
|
|
//
|
|
|
|
pRequest = new UL_NATIVE_REQUEST;
|
|
|
|
return pRequest;
|
|
}
|