|
|
/*++
Copyright (c) 2000-2001 Microsoft Corporation
Module Name:
fastio.cxx
Abstract:
This module implements the fast I/O logic of HTTP.SYS.
Author:
Chun Ye (chunye) 09-Dec-2000
Revision History:
--*/
#include "precomp.h"
//
// Private globals.
//
#ifdef ALLOC_PRAGMA
#pragma alloc_text( PAGE, UlFastIoDeviceControl )
#pragma alloc_text( PAGE, UlSendHttpResponseFastIo )
#pragma alloc_text( PAGE, UlReceiveHttpRequestFastIo )
#pragma alloc_text( PAGE, UlpFastSendHttpResponse )
#pragma alloc_text( PAGE, UlpFastReceiveHttpRequest )
#pragma alloc_text( PAGE, UlpFastCopyHttpRequest )
#endif // ALLOC_PRAGMA
#if 0
NOT PAGEABLE -- UlpRestartFastSendHttpResponse NOT PAGEABLE -- UlpFastSendCompleteWorker #endif
FAST_IO_DISPATCH UlFastIoDispatch = { sizeof(FAST_IO_DISPATCH), // SizeOfFastIoDispatch
NULL, // FastIoCheckIfPossible
NULL, // FastIoRead
NULL, // FastIoWrite
NULL, // FastIoQueryBasicInfo
NULL, // FastIoQueryStandardInfo
NULL, // FastIoLock
NULL, // FastIoUnlockSingle
NULL, // FastIoUnlockAll
NULL, // FastIoUnlockAllByKey
UlFastIoDeviceControl // FastIoDeviceControl
};
//
// Public functions.
//
BOOLEAN UlFastIoDeviceControl( IN PFILE_OBJECT pFileObject, IN BOOLEAN Wait, IN PVOID pInputBuffer OPTIONAL, IN ULONG InputBufferLength, OUT PVOID pOutputBuffer OPTIONAL, IN ULONG OutputBufferLength, IN ULONG IoControlCode, OUT PIO_STATUS_BLOCK pIoStatus, IN PDEVICE_OBJECT pDeviceObject ) { if (IoControlCode == IOCTL_HTTP_SEND_HTTP_RESPONSE || IoControlCode == IOCTL_HTTP_SEND_ENTITY_BODY) { return UlSendHttpResponseFastIo( pFileObject, Wait, pInputBuffer, InputBufferLength, pOutputBuffer, OutputBufferLength, pIoStatus, pDeviceObject, IoControlCode == IOCTL_HTTP_SEND_ENTITY_BODY? TRUE: FALSE ); } else if (IoControlCode == IOCTL_HTTP_RECEIVE_HTTP_REQUEST) { return UlReceiveHttpRequestFastIo( pFileObject, Wait, pInputBuffer, InputBufferLength, pOutputBuffer, OutputBufferLength, pIoStatus, pDeviceObject ); } else { return FALSE; } } // UlFastIoDeviceControl
BOOLEAN UlSendHttpResponseFastIo( IN PFILE_OBJECT pFileObject, IN BOOLEAN Wait, IN PVOID pInputBuffer, IN ULONG InputBufferLength, OUT PVOID pOutputBuffer, IN ULONG OutputBufferLength, OUT PIO_STATUS_BLOCK pIoStatus, IN PDEVICE_OBJECT pDeviceObject, IN BOOLEAN RawResponse ) { NTSTATUS Status; PHTTP_SEND_HTTP_RESPONSE_INFO pSendInfo; PUL_INTERNAL_REQUEST pRequest = NULL; LONG BufferLength = 0; ULONG BytesSent;
//
// Sanity check.
//
PAGED_CODE();
//
// Initialize IoStatus in the failure case.
//
pIoStatus->Information = 0;
//
// Ensure this is really an app pool, not a control channel.
//
if (IS_APP_POOL(pFileObject) == FALSE) { //
// Not an app pool.
//
Status = STATUS_INVALID_DEVICE_REQUEST; goto end; }
//
// Ensure the input buffer is large enough.
//
if (pInputBuffer == NULL || InputBufferLength < sizeof(*pSendInfo)) { //
// Input buffer too small.
//
Status = STATUS_BUFFER_TOO_SMALL; goto end; }
pSendInfo = (PHTTP_SEND_HTTP_RESPONSE_INFO)pInputBuffer;
//
// Check if fast path can be taken for this response.
//
__try { ProbeTestForRead( pSendInfo, sizeof(*pSendInfo), sizeof(PVOID) );
//
// Fast path is only enabled if the response can be buffered.
//
if (RawResponse == FALSE && pSendInfo->CachePolicy.Policy != HttpCachePolicyNocache && g_UriCacheConfig.EnableCache) { //
// Take the slow path if we need to build a cache entry.
//
Status = STATUS_NOT_IMPLEMENTED; } else if (pSendInfo->EntityChunkCount == 0) { //
// Fast path should have no problem handling zero chunk responses.
// Validate the response type.
//
if (RawResponse) { Status = STATUS_INVALID_PARAMETER; } else { Status = STATUS_SUCCESS; } } else { PHTTP_DATA_CHUNK pEntityChunks = pSendInfo->pEntityChunks; ULONG i;
//
// Make sure all chunks are from memory and their total size
// is <= g_UlMaxCopyThreshold. Take the slow path if this is
// not the case.
//
Status = STATUS_SUCCESS;
for (i = 0; i < pSendInfo->EntityChunkCount; i++) { ProbeTestForRead( pEntityChunks, sizeof(HTTP_DATA_CHUNK), sizeof(PVOID) );
if (pEntityChunks->DataChunkType != HttpDataChunkFromMemory) { Status = STATUS_NOT_SUPPORTED; break; }
BufferLength += pEntityChunks->FromMemory.BufferLength;
if (BufferLength > (LONG)g_UlMaxCopyThreshold) { Status = STATUS_NOT_SUPPORTED; break; }
pEntityChunks++; } } } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() ); }
if (NT_SUCCESS(Status) == FALSE) { // BUGBUG: should close connection
goto end; }
//
// SendHttpResponse *must* take a PHTTP_RESPONSE. This will
// protect us from those whackos that attempt to build their own
// raw response headers. This is ok for SendEntityBody. The
// two cases are differentiated by the RawResponse flag.
//
if (RawResponse == FALSE && pSendInfo->pHttpResponse == NULL) { Status = STATUS_INVALID_PARAMETER; goto end; }
//
// Now get the request from the request ID. This gives us a reference
// to the request.
//
pRequest = UlGetRequestFromId( pSendInfo->RequestId );
if (pRequest == NULL) { Status = STATUS_INVALID_PARAMETER; goto end; }
ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) ); ASSERT( UL_IS_VALID_HTTP_CONNECTION( pRequest->pHttpConn ) );
//
// Check if we have exceeded maximum SendBufferedBytes limit.
//
if ((BufferLength + pRequest->pHttpConn->SendBufferedBytes) > (LONG)g_UlMaxSendBufferedBytes) { Status = STATUS_ALLOTTED_SPACE_EXCEEDED; goto end; }
//
// Check if a response has already been sent on this request. We can test
// this without acquiring the request resource, since the flag is only
// set (never reset).
//
if (RawResponse == FALSE) { //
// Make sure only one response header goes back.
//
if (1 == InterlockedCompareExchange( (PLONG)&pRequest->SentResponse, 1, 0 )) { Status = STATUS_INVALID_DEVICE_STATE; goto end; } } else if (pRequest->SentResponse == 0) { //
// Ensure a response has already been sent. If the application is
// sending entity without first having sent a response header, check
// the HTTP_SEND_RESPONSE_FLAG_RAW_HEADER flag.
//
if ((pSendInfo->Flags & HTTP_SEND_RESPONSE_FLAG_RAW_HEADER)) { //
// Make sure only one response header goes back.
//
if (1 == InterlockedCompareExchange( (PLONG)&pRequest->SentResponse, 1, 0 )) { Status = STATUS_INVALID_DEVICE_STATE; goto end; } } else { Status = STATUS_INVALID_DEVICE_STATE; goto end; } }
//
// Also ensure that all previous calls to SendHttpResponse
// and SendEntityBody had the MORE_DATA flag set.
//
if ((pSendInfo->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) { //
// Set if we have sent the last response, but bail out if the flag
// is already set since there can be only one last response.
//
if (1 == InterlockedCompareExchange( (PLONG)&pRequest->SentLast, 1, 0 )) { Status = STATUS_INVALID_DEVICE_STATE; goto end; } } else if (pRequest->SentLast == 1) { Status = STATUS_INVALID_DEVICE_STATE; goto end; }
//
// OK, we have the connection. Now capture the incoming HTTP_RESPONSE
// structure, map it to our internal format and send the response.
//
Status = UlpFastSendHttpResponse( pSendInfo->pHttpResponse, pSendInfo->pLogData, pSendInfo->pEntityChunks, pSendInfo->EntityChunkCount, pSendInfo->Flags, pRequest, NULL, &BytesSent );
if (NT_SUCCESS(Status)) { //
// Record the number of bytes we have sent successfully.
//
pIoStatus->Information = BytesSent; }
end:
if (pRequest != NULL) { UL_DEREFERENCE_INTERNAL_REQUEST( pRequest ); }
//
// Complete the fast I/O.
//
if (NT_SUCCESS(Status)) { //
// If we took the fast path, always return success even if completion
// routine returns failure later.
//
pIoStatus->Status = STATUS_SUCCESS; return TRUE; } else { pIoStatus->Status = Status; return FALSE; } } // UlSendHttpResponseFastIo
BOOLEAN UlReceiveHttpRequestFastIo( IN PFILE_OBJECT pFileObject, IN BOOLEAN Wait, IN PVOID pInputBuffer, IN ULONG InputBufferLength, OUT PVOID pOutputBuffer, IN ULONG OutputBufferLength, OUT PIO_STATUS_BLOCK pIoStatus, IN PDEVICE_OBJECT pDeviceObject ) { NTSTATUS Status; PHTTP_RECEIVE_REQUEST_INFO pRequestInfo; ULONG BytesRead;
//
// Sanity check.
//
PAGED_CODE();
//
// Ensure this is really an app pool, not a control channel.
//
if (IS_APP_POOL(pFileObject) || NULL != pInputBuffer) { if (NULL != pOutputBuffer && OutputBufferLength >= sizeof(HTTP_REQUEST) && InputBufferLength == sizeof(HTTP_RECEIVE_REQUEST_INFO)) { pRequestInfo = (PHTTP_RECEIVE_REQUEST_INFO) pInputBuffer;
Status = STATUS_SUCCESS;
__try { ProbeTestForRead( pRequestInfo, sizeof(*pRequestInfo), sizeof(PVOID) ); } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() ); }
if (NT_SUCCESS(Status)) { Status = UlpFastReceiveHttpRequest( pRequestInfo->RequestId, GET_APP_POOL_PROCESS(pFileObject), pOutputBuffer, OutputBufferLength, &BytesRead ); } } else { Status = STATUS_BUFFER_TOO_SMALL; } } else { Status = STATUS_INVALID_DEVICE_REQUEST; }
//
// Complete the fast I/O.
//
if (NT_SUCCESS(Status)) { pIoStatus->Status = STATUS_SUCCESS; pIoStatus->Information = BytesRead; return TRUE; } else { pIoStatus->Status = Status; pIoStatus->Information = 0; return FALSE; } } // UlReceiveHttpRequestFastIo
//
// Private functions.
//
NTSTATUS UlpFastSendHttpResponse( IN PHTTP_RESPONSE pUserResponse OPTIONAL, IN PHTTP_LOG_FIELDS_DATA pUserLogData OPTIONAL, IN PHTTP_DATA_CHUNK pUserEntityDataChunk, IN ULONG ChunkCount, IN ULONG Flags, IN PUL_INTERNAL_REQUEST pRequest, IN PIRP pUserIrp OPTIONAL, OUT PULONG BytesSent ) { NTSTATUS Status; PUCHAR pResponseBuffer; ULONG ResponseBufferLength; ULONG HeaderLength; ULONG FixedHeaderLength; ULONG VarHeaderLength; ULONG ContentLength = 0; ULONG TotalResponseSize; ULONG ContentLengthStringLength; CHAR ContentLengthString[MAX_ULONGLONG_STR]; PUL_FULL_TRACKER pTracker = NULL; PUL_HTTP_CONNECTION pHttpConnection = NULL; PUL_CONNECTION pConnection; CCHAR SendIrpStackSize; BOOLEAN Disconnect; UL_CONN_HDR ConnHeader; LARGE_INTEGER TimeSent; BOOLEAN LastResponse; PMDL pSendMdl; ULONG i; KIRQL OldIrql;
//
// Sanity check.
//
PAGED_CODE();
__try { //
// Setup locals so we know how to cleanup on exit.
//
Status = UlProbeHttpHeaders( pUserResponse );
if (NT_SUCCESS(Status) == FALSE) { // BUGBUG: 389145: should close the connection
goto end; }
for (i = 0; i < ChunkCount; i++) { ContentLength += pUserEntityDataChunk[i].FromMemory.BufferLength; }
ASSERT( pUserIrp != NULL || ContentLength <= g_UlMaxCopyThreshold ); ASSERT( pUserIrp == NULL || ContentLength <= MAX_BYTES_BUFFERED );
//
// Allocate a fast tracker to send the response.
//
pConnection = pRequest->pHttpConn->pConnection; SendIrpStackSize = pConnection->ConnectionObject.pDeviceObject->StackSize; LastResponse = (0 == (Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA));
if (LastResponse && SendIrpStackSize <= DEFAULT_MAX_IRP_STACK_SIZE) { pTracker = pRequest->pTracker; } else { pTracker = UlpAllocateFastTracker( 0, SendIrpStackSize ); }
if (pTracker == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto end; }
//
// Try to generate the fixed header within the default fixed header
// buffer first. If this fails, take the normal path.
//
pResponseBuffer = pTracker->pAuxiliaryBuffer;
if (pUserIrp != NULL) { ResponseBufferLength = g_UlMaxFixedHeaderSize + g_UlMaxCopyThreshold; } else { ResponseBufferLength = g_UlMaxFixedHeaderSize + g_UlMaxCopyThreshold - ContentLength; }
if (pUserResponse != NULL) { Status = UlGenerateFixedHeaders( pRequest->Version, pUserResponse, ResponseBufferLength, pResponseBuffer, &FixedHeaderLength );
if (!NT_SUCCESS(Status)) { // Either the buffer was too small or an exception was thrown.
if (Status != STATUS_INSUFFICIENT_RESOURCES) goto end;
if (pTracker->IsFromRequest == FALSE) { if (pTracker->IsFromLookaside) { pTracker->Signature = MAKE_FREE_TAG(UL_FULL_TRACKER_POOL_TAG); UlPplFreeFullTracker( pTracker ); } else { UL_FREE_POOL_WITH_SIG( pTracker, UL_FULL_TRACKER_POOL_TAG ); } }
//
// Calculate the fixed header size.
//
Status = UlComputeFixedHeaderSize( pRequest->Version, pUserResponse, &HeaderLength );
if (!NT_SUCCESS(Status)) goto end;
ASSERT( HeaderLength > ResponseBufferLength );
pTracker = UlpAllocateFastTracker( HeaderLength, SendIrpStackSize );
if (pTracker == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; goto end; }
pResponseBuffer = pTracker->pAuxiliaryBuffer;
Status = UlGenerateFixedHeaders( pRequest->Version, pUserResponse, HeaderLength, pResponseBuffer, &FixedHeaderLength );
if (!NT_SUCCESS(Status)) goto end;
ASSERT( HeaderLength == FixedHeaderLength ); }
pResponseBuffer += FixedHeaderLength; } else { FixedHeaderLength = 0; }
//
// Take a reference of HTTP connection for the tracker.
//
pHttpConnection = pRequest->pHttpConn;
ASSERT(UL_IS_VALID_HTTP_CONNECTION(pHttpConnection));
UL_REFERENCE_HTTP_CONNECTION( pHttpConnection );
//
// Take a reference of the request too because logging needs it.
//
UL_REFERENCE_INTERNAL_REQUEST( pRequest );
//
// Initialization.
//
pTracker->Signature = UL_FULL_TRACKER_POOL_TAG; pTracker->pRequest = pRequest; pTracker->pHttpConnection = pHttpConnection; pTracker->Flags = Flags; pTracker->pLogData = NULL; pTracker->pUserIrp = pUserIrp; pTracker->SendBufferedBytes = 0;
//
// Capture the log data.
//
if (NULL != pUserLogData && LastResponse && pRequest->ConfigInfo.pLoggingConfig) { Status = UlProbeLogData( pUserLogData );
if (NT_SUCCESS(Status) == FALSE) { goto end; }
pTracker->pLogData = &pRequest->LogData;
Status = UlAllocateLogDataBuffer( pTracker->pLogData, pRequest, pRequest->ConfigInfo.pLoggingConfig ); ASSERT( NT_SUCCESS( Status ) );
Status = UlCaptureLogFields( pUserLogData, pRequest->Version, pTracker->pLogData );
if (NT_SUCCESS(Status) == FALSE) { goto end; } }
//
// Generate the variable header.
//
if (FixedHeaderLength > 0) { //
// Should we close the connection?
//
Disconnect = FALSE;
if ((Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT) != 0) { //
// Caller is forcing a disconnect.
//
Disconnect = TRUE; } else if (LastResponse) { //
// No more data is coming, should we disconnect?
//
if (UlCheckDisconnectInfo(pRequest)) { Disconnect = TRUE; pTracker->Flags |= HTTP_SEND_RESPONSE_FLAG_DISCONNECT; } }
//
// Choose the connection header to send back.
//
ConnHeader = UlChooseConnectionHeader( pRequest->Version, Disconnect );
//
// Decide if we need to generate a content-length header.
//
if (IS_LENGTH_SPECIFIED(pUserResponse->Headers.pKnownHeaders) == FALSE && IS_CHUNK_SPECIFIED(pUserResponse->Headers.pKnownHeaders) == FALSE && UlNeedToGenerateContentLength( pRequest->Verb, pUserResponse->StatusCode, pTracker->Flags )) { //
// Autogenerate a content-length header. We can use our own fast
// generator here because we know the content-length is <= 64k.
//
ContentLengthStringLength = UlpFastGenerateContentLength( ContentLengthString, ContentLength ); } else { ContentLengthString[0] = ANSI_NULL; ContentLengthStringLength = 0; }
//
// Now generate the variable header.
//
UlGenerateVariableHeaders( ConnHeader, (PUCHAR)ContentLengthString, ContentLengthStringLength, pResponseBuffer, &VarHeaderLength, &TimeSent );
ASSERT( VarHeaderLength <= g_UlMaxVariableHeaderSize );
pResponseBuffer += VarHeaderLength; } else { VarHeaderLength = 0; }
pTracker->pMdlAuxiliary->ByteCount = FixedHeaderLength + VarHeaderLength;
//
// If this routine is called from the fast I/O path, copy the content
// to the auxilary buffer inside the tracker and set up the send MDL.
// Otherwise, we need to MmProbeAndLock the user buffer into another
// separate MDL.
//
if (pUserIrp == NULL) { PUCHAR pAuxiliaryBuffer = pResponseBuffer;
Status = STATUS_SUCCESS;
__try { for (i = 0; i < ChunkCount; i++) { if (pUserEntityDataChunk[i].FromMemory.BufferLength == 0 || pUserEntityDataChunk[i].FromMemory.pBuffer == NULL) { ExRaiseStatus( STATUS_INVALID_PARAMETER ); }
ProbeTestForRead( pUserEntityDataChunk[i].FromMemory.pBuffer, pUserEntityDataChunk[i].FromMemory.BufferLength, sizeof(CHAR) );
RtlCopyMemory( pAuxiliaryBuffer, pUserEntityDataChunk[i].FromMemory.pBuffer, pUserEntityDataChunk[i].FromMemory.BufferLength );
pAuxiliaryBuffer += pUserEntityDataChunk[i].FromMemory.BufferLength; } } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() ); }
if (NT_SUCCESS(Status) == FALSE) { goto end; }
pTracker->pMdlAuxiliary->ByteCount += ContentLength; pTracker->pMdlAuxiliary->Next = NULL; } else { ASSERT( ChunkCount == 1 );
Status = STATUS_SUCCESS;
__try { if (pUserEntityDataChunk->FromMemory.BufferLength == 0 || pUserEntityDataChunk->FromMemory.pBuffer == NULL) { ExRaiseStatus( STATUS_INVALID_PARAMETER ); }
ProbeTestForRead( pUserEntityDataChunk->FromMemory.pBuffer, pUserEntityDataChunk->FromMemory.BufferLength, sizeof(CHAR) );
Status = UlpInitializeAndLockMdl( pTracker->pMdlUserBuffer, pUserEntityDataChunk->FromMemory.pBuffer, ContentLength, IoReadAccess ); } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() ); }
if (NT_SUCCESS(Status) == FALSE) { goto end; }
pTracker->pMdlAuxiliary->Next = pTracker->pMdlUserBuffer; }
TotalResponseSize = FixedHeaderLength + VarHeaderLength + ContentLength;
//
// Fail the zero length response which can be generated in the case
// where the request is 0.9 and there is no body attached to the
// response. We have to take this code path to possibly force a
// disconnect here because the slow path may not go far enough to the
// disconnect logic since we may have flags such as SentLast or
// SentResponse set.
//
if (TotalResponseSize == 0) { if (IS_DISCONNECT_TIME(pTracker)) { UlCloseConnection( pTracker->pHttpConnection->pConnection, FALSE, NULL, NULL ); }
Status = STATUS_INVALID_PARAMETER; goto end; }
//
// Add to MinKBSec watch list, since we now know TotalResponseSize.
//
UlSetMinKBSecTimer( &pHttpConnection->TimeoutInfo, TotalResponseSize );
//
// Mark the IRP as pending before the send as we are guaranteed to
// return pending from this point on.
//
if (pUserIrp != NULL) { IoMarkIrpPending( pUserIrp ); }
//
// Skip the zero length MDL if created one.
//
pSendMdl = pTracker->pMdlAuxiliary;
if (pSendMdl->ByteCount == 0) { pSendMdl = pSendMdl->Next; }
ASSERT( pSendMdl != NULL ); ASSERT( pSendMdl->ByteCount != 0 );
//
// Adjust SendBufferedBytes for the fast I/O path only.
//
if (pUserIrp == NULL) { pTracker->SendBufferedBytes = ContentLength;
InterlockedExchangeAdd( &pHttpConnection->SendBufferedBytes, ContentLength ); }
//
// Send the response. Notice the logic to disconnect the connection is
// different from sending back a disconnect header.
//
Status = UlSendData( pHttpConnection->pConnection, pSendMdl, TotalResponseSize, &UlpRestartFastSendHttpResponse, pTracker, pTracker->pSendIrp, &pTracker->IrpContext, IS_DISCONNECT_TIME(pTracker) );
ASSERT( Status == STATUS_PENDING );
if (BytesSent != NULL) { *BytesSent = TotalResponseSize; }
return STATUS_PENDING; } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() ); }
end: //
// Cleanup.
//
if (pTracker) { UlpFreeFastTracker( pTracker ); //
// Let the references go.
//
if (pHttpConnection != NULL) { UL_DEREFERENCE_HTTP_CONNECTION( pHttpConnection ); UL_DEREFERENCE_INTERNAL_REQUEST( pRequest ); } }
return Status; } // UlpFastSendHttpResponse
VOID UlpRestartFastSendHttpResponse( IN PVOID pCompletionContext, IN NTSTATUS Status, IN ULONG_PTR Information ) { PUL_FULL_TRACKER pTracker; PIRP pIrp;
pTracker = (PUL_FULL_TRACKER)pCompletionContext;
ASSERT( IS_VALID_FULL_TRACKER( pTracker ) );
//
// Set status and bytes transferred fields as returned.
//
pTracker->IoStatus.Status = Status; pTracker->IoStatus.Information = Information;
if (NT_SUCCESS(Status) == FALSE && IS_DISCONNECT_TIME(pTracker) == FALSE) { //
// Disconnect if there was an error and we didn't disconnect already.
//
UlCloseConnection( pTracker->pHttpConnection->pConnection, TRUE, NULL, NULL ); }
//
// Complete the orignal user send IRP if set.
//
pIrp = pTracker->pUserIrp;
if (pIrp != NULL) { //
// Don't forget to unlock the user buffer.
//
ASSERT( pTracker->pMdlAuxiliary->Next != NULL ); ASSERT( pTracker->pMdlAuxiliary->Next == pTracker->pMdlUserBuffer );
MmUnlockPages( pTracker->pMdlUserBuffer );
pIrp->IoStatus.Status = Status; pIrp->IoStatus.Information = Information;
UlCompleteRequest( pIrp, g_UlPriorityBoost ); } else { //
// Adjust SendBufferedBytes.
//
InterlockedExchangeAdd( &pTracker->pHttpConnection->SendBufferedBytes, - pTracker->SendBufferedBytes ); }
UL_QUEUE_WORK_ITEM( &pTracker->WorkItem, &UlpFastSendCompleteWorker ); } // UlpRestartFastSendHttpResponse
VOID UlpFastSendCompleteWorker( IN PUL_WORK_ITEM pWorkItem ) { PUL_FULL_TRACKER pTracker; PUL_HTTP_CONNECTION pHttpConnection; PUL_INTERNAL_REQUEST pRequest; NTSTATUS Status; KIRQL OldIrql;
//
// Sanity check.
//
PAGED_CODE();
pTracker = CONTAINING_RECORD( pWorkItem, UL_FULL_TRACKER, WorkItem );
ASSERT( IS_VALID_FULL_TRACKER( pTracker ) );
Status = pTracker->IoStatus.Status;
pHttpConnection = pTracker->pHttpConnection; ASSERT( UL_IS_VALID_HTTP_CONNECTION( pHttpConnection ) );
pRequest = pTracker->pRequest; ASSERT( UL_IS_VALID_INTERNAL_REQUEST( pRequest ) );
//
// Update the BytesSent counter in the request.
//
UlInterlockedAdd64( (PLONGLONG)&pRequest->BytesSent, pTracker->IoStatus.Information );
if ((pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0) { //
// Stop the MinKBSec timer and start Idle timer
//
UlLockTimeoutInfo( &pHttpConnection->TimeoutInfo, &OldIrql );
UlResetConnectionTimer( &pHttpConnection->TimeoutInfo, TimerMinKBSec );
UlSetConnectionTimer( &pHttpConnection->TimeoutInfo, TimerConnectionIdle );
UlUnlockTimeoutInfo( &pHttpConnection->TimeoutInfo, OldIrql );
UlEvaluateTimerState( &pHttpConnection->TimeoutInfo ); }
//
// If this is the last response for this request and there was a log
// data passed down by the user then now its time to log.
//
if (NULL != pTracker->pLogData) { UlLogHttpHit( pTracker->pLogData ); }
//
// Kick the parser on the connection and release our hold.
//
if (Status == STATUS_SUCCESS && (pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_MORE_DATA) == 0 && (pTracker->Flags & HTTP_SEND_RESPONSE_FLAG_DISCONNECT) == 0) { UlResumeParsing( pHttpConnection ); }
//
// Cleanup.
//
UlpFreeFastTracker( pTracker );
UL_DEREFERENCE_HTTP_CONNECTION( pHttpConnection ); UL_DEREFERENCE_INTERNAL_REQUEST( pRequest ); } // UlpFastSendCompleteWorker
NTSTATUS UlpFastReceiveHttpRequest( IN HTTP_REQUEST_ID RequestId, IN PUL_APP_POOL_PROCESS pProcess, IN PVOID pOutputBuffer, IN ULONG OutputBufferLength, OUT PULONG pBytesRead ) { NTSTATUS Status; PUL_INTERNAL_REQUEST pRequest = NULL;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( IS_VALID_AP_PROCESS(pProcess) ); ASSERT( IS_VALID_AP_OBJECT(pProcess->pAppPool) ); ASSERT( pOutputBuffer != NULL);
UlAcquireResourceShared( &pProcess->pAppPool->pResource->Resource, TRUE );
//
// Make sure we're not cleaning up the process.
//
if (!pProcess->InCleanup) { //
// Obtain the request based on the request ID. This can be from the
// NewRequestQueue of the AppPool if the ID is NULL, or directly
// from the matching opaque ID entry.
//
if (HTTP_IS_NULL_ID(&RequestId)) { pRequest = UlpDequeueNewRequest( pProcess ); } else { pRequest = UlGetRequestFromId( RequestId );
if (NULL != pRequest) { ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) );
//
// Weed out the request in bad state.
//
if (pRequest->AppPool.QueueState != QueueCopiedState || pRequest->AppPool.pProcess != pProcess) { UL_DEREFERENCE_INTERNAL_REQUEST( pRequest ); pRequest = NULL; } } } }
//
// Let go the lock since we have taken a short-lived reference of
// the request in the success case.
//
UlReleaseResource( &pProcess->pAppPool->pResource->Resource );
//
// Return immediately if no request is found and let the slow path
// handle this.
//
if (NULL == pRequest) { return STATUS_INVALID_PARAMETER; }
//
// Copy it to the output buffer.
//
Status = UlpFastCopyHttpRequest( pRequest, pOutputBuffer, OutputBufferLength, pBytesRead );
//
// Let go our reference.
//
UL_DEREFERENCE_INTERNAL_REQUEST( pRequest );
return Status; } // UlpFastReceiveHttpRequest
NTSTATUS UlpFastCopyHttpRequest( IN PUL_INTERNAL_REQUEST pRequest, IN PVOID pOutputBuffer, IN ULONG OutputBufferLength, OUT PULONG pBytesRead ) { NTSTATUS Status; ULONG BytesNeeded;
//
// Sanity check.
//
PAGED_CODE();
ASSERT( UL_IS_VALID_INTERNAL_REQUEST(pRequest) ); ASSERT( pOutputBuffer != NULL);
//
// Make sure this is big enough to handle the request, and
// if so copy it in.
//
Status = UlpComputeRequestBytesNeeded( pRequest, &BytesNeeded );
if (NT_SUCCESS(Status)) { //
// Make sure we've got enough space to handle the whole request.
//
if (BytesNeeded <= OutputBufferLength) { //
// This request will fit in this buffer, so copy it.
//
__try { ProbeForWrite( pOutputBuffer, OutputBufferLength, TYPE_ALIGNMENT(HTTP_REQUEST) );
Status = UlpCopyRequestToBuffer( pRequest, (PUCHAR) pOutputBuffer, pOutputBuffer, OutputBufferLength, NULL, 0 ); } __except( UL_EXCEPTION_FILTER() ) { Status = UL_CONVERT_EXCEPTION_CODE( GetExceptionCode() ); }
if (NT_SUCCESS(Status)) { *pBytesRead = BytesNeeded; } } else { //
// Let the slow path handle this.
//
Status = STATUS_BUFFER_OVERFLOW; } }
return Status; } // UlpFastCopyHttpRequest
|