Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1461 lines
36 KiB

/*++
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