/*++

Copyright (c) 1998-2001 Microsoft Corporation

Module Name:

    apool.h

Abstract:

    The public definition of app pool interfaces.

Author:

    Paul McDaniel (paulmcd)       28-Jan-1999


Revision History:

--*/


#ifndef _APOOL_H_
#define _APOOL_H_

#ifdef __cplusplus
extern "C" {
#endif


//
// Kernel mode mappings to the user mode set defined in ulapi.h
//

//
// Forwarders.
//

typedef struct _UL_INTERNAL_REQUEST *PUL_INTERNAL_REQUEST;
typedef struct _UL_HTTP_CONNECTION *PUL_HTTP_CONNECTION;
typedef struct _UL_CONFIG_GROUP_OBJECT *PUL_CONFIG_GROUP_OBJECT;


//
// this structure contains a queue of HTTP_REQUEST objects
// CODEWORK: investigate using an NBQUEUE instead
//

typedef struct _UL_REQUEST_QUEUE
{
    LONG        RequestCount;
    LONG        MaxRequests;
    LIST_ENTRY  RequestHead;

} UL_REQUEST_QUEUE, *PUL_REQUEST_QUEUE;


//
// This structure represents an internal app pool object
//

#define IS_VALID_AP_OBJECT(pObject) \
    (((pObject) != NULL) && ((pObject)->Signature == UL_APP_POOL_OBJECT_POOL_TAG) && ((pObject)->RefCount > 0))

typedef struct _UL_APP_POOL_OBJECT
{
    //
    // NonPagedPool
    //

    //
    // lock that protects NewRequestQueue, PendingRequestQueue
    // for each attached process and queue state of the request
    //
    // ensure it on cache-line and use InStackQueuedSpinLock for
    // better performance
    //
    UL_SPIN_LOCK            QueueSpinLock;

    //
    // UL_APP_POOL_OBJECT_POOL_TAG
    //
    ULONG                   Signature;

    //
    // Ref count for this app pool
    //
    LONG                    RefCount;

    //
    // links all apool objects, anchored by g_AppPoolListHead
    //
    LIST_ENTRY              ListEntry;

    //
    // Locks lists on the app pool & process objects, is refcounted and
    // given to the HTTP_REQUEST object to synchronize access to process
    // objects when connections drop and the request(s) need to be released
    //
    PUL_NONPAGED_RESOURCE   pResource;

    //
    // A apool wide new request list (when no irps are available)
    //
    UL_REQUEST_QUEUE        NewRequestQueue;

    //
    // the demand start irp (OPTIONAL)
    //
    PIRP                    pDemandStartIrp;
    PEPROCESS               pDemandStartProcess;

    //
    // the list of processes bound to this app pool
    //
    LIST_ENTRY              ProcessListHead;

    PSECURITY_DESCRIPTOR    pSecurityDescriptor;

    //
    // List of transient config groups
    //
    UL_NOTIFY_HEAD          TransientHead;

    //
    // the length of pName
    //
    ULONG                   NameLength;

    //
    // number of active processes in the AppPool, used to decide if binding
    // is necessary
    //
    ULONG                   NumberActiveProcesses;

    //
    // Only route requests to this AppPool if it's marked active
    //
    HTTP_ENABLED_STATE      Enabled;

    //
    // the apool's name
    //
    WCHAR                   pName[0];

} UL_APP_POOL_OBJECT, *PUL_APP_POOL_OBJECT;


//
// The structure representing a process bound to an app pool.
//

#define IS_VALID_AP_PROCESS(pObject) \
    (((pObject) != NULL) && ((pObject)->Signature == UL_APP_POOL_PROCESS_POOL_TAG))

typedef struct _UL_APP_POOL_PROCESS
{
    //
    // NonPagedPool
    //

    //
    // UL_APP_POOL_PROCESS_POOL_TAG
    //
    ULONG                   Signature;

    //
    // set if we are in cleanup. You must check this flag before attaching
    // any IRPs to the process.
    //
    ULONG                   InCleanup : 1;

    //
    // set if process is attached with the HTTP_OPTION_CONTROLLER option
    //
    ULONG                   Controller : 1;

    //
    // used to link into the apool object
    //
    LIST_ENTRY              ListEntry;

    //
    // points to the app pool this process belongs
    //
    PUL_APP_POOL_OBJECT     pAppPool;

    //
    // a list of pending IRP(s) waiting to receive new requests
    //
    LIST_ENTRY              NewIrpHead;

    //
    // lock that protects the above list
    //
    UL_SPIN_LOCK            NewIrpSpinLock;

    //
    // links requests that would not fit in a irp buffer and need to wait for
    // the larger buffer
    //
    // and
    //
    // requests that this process is working on and need
    // i/o cancellation if the process detaches from the apool
    //
    UL_REQUEST_QUEUE        PendingRequestQueue;

    //
    // Pointer to the actual process (for debugging)
    //
    PEPROCESS               pProcess;

    //
    // List of pending "wait for disconnect" IRPs.
    //
    UL_NOTIFY_HEAD          WaitForDisconnectHead;

} UL_APP_POOL_PROCESS, *PUL_APP_POOL_PROCESS;


// IRQL == PASSIVE_LEVEL
//
NTSTATUS
UlAttachProcessToAppPool(
    IN  PWCHAR                          pName OPTIONAL,
    IN  ULONG                           NameLength,
    IN  BOOLEAN                         Create,
    IN  PACCESS_STATE                   pAccessState,
    IN  ACCESS_MASK                     DesiredAccess,
    IN  KPROCESSOR_MODE                 RequestorMode,
    OUT PUL_APP_POOL_PROCESS *          ppProcess
    );

// IRQL == PASSIVE_LEVEL
//
NTSTATUS
UlDetachProcessFromAppPool(
    IN  PUL_APP_POOL_PROCESS            pProcess
    );

// IRQL == PASSIVE_LEVEL
//
#if REFERENCE_DEBUG
VOID
UlReferenceAppPool(
    IN  PUL_APP_POOL_OBJECT             pAppPool
    REFERENCE_DEBUG_FORMAL_PARAMS
    );
#else
__inline
VOID
FASTCALL
UlReferenceAppPool(
    IN  PUL_APP_POOL_OBJECT             pAppPool
    )
{
    InterlockedIncrement(&pAppPool->RefCount);
}
#endif

#define REFERENCE_APP_POOL( papp )                                          \
    UlReferenceAppPool(                                                     \
        (papp)                                                              \
        REFERENCE_DEBUG_ACTUAL_PARAMS                                       \
        )

// IRQL == PASSIVE_LEVEL
//
VOID
UlDeleteAppPool(
    IN  PUL_APP_POOL_OBJECT             pAppPool
    REFERENCE_DEBUG_FORMAL_PARAMS
    );

#define DELETE_APP_POOL( papp )                                             \
    UlDeleteAppPool(                                                        \
        (papp)                                                              \
        REFERENCE_DEBUG_ACTUAL_PARAMS                                       \
        )

#if REFERENCE_DEBUG
VOID
UlDereferenceAppPool(
    IN  PUL_APP_POOL_OBJECT             pAppPool
    REFERENCE_DEBUG_FORMAL_PARAMS
    );
#else
__inline
VOID
FASTCALL
UlDereferenceAppPool(
    IN  PUL_APP_POOL_OBJECT             pAppPool
    )
{
    if (InterlockedDecrement(&pAppPool->RefCount) == 0)
    {
        UlDeleteAppPool(pAppPool);
    }
}
#endif

#define DEREFERENCE_APP_POOL( papp )                                        \
    UlDereferenceAppPool(                                                   \
        (papp)                                                              \
        REFERENCE_DEBUG_ACTUAL_PARAMS                                       \
        )

// IRQL == PASSIVE_LEVEL
//
NTSTATUS
UlQueryAppPoolInformation(
    IN  PUL_APP_POOL_PROCESS            pProcess,
    IN  HTTP_APP_POOL_INFORMATION_CLASS InformationClass,
    OUT PVOID                           pAppPoolInformation,
    IN  ULONG                           Length,
    OUT PULONG                          pReturnLength OPTIONAL
    );

// IRQL == PASSIVE_LEVEL
//
NTSTATUS
UlSetAppPoolInformation(
    IN  PUL_APP_POOL_PROCESS            pProcess,
    IN  HTTP_APP_POOL_INFORMATION_CLASS InformationClass,
    IN  PVOID                           pAppPoolInformation,
    IN  ULONG                           Length
    );

// IRQL == PASSIVE_LEVEL
//
NTSTATUS
UlWaitForDemandStart(
    IN  PUL_APP_POOL_PROCESS            pProcess,
    IN  PIRP                            pIrp
    );


// IRQL == PASSIVE_LEVEL
//
NTSTATUS
UlReceiveHttpRequest(
    IN  HTTP_REQUEST_ID                 RequestId,
    IN  ULONG                           Flags,
    IN  PUL_APP_POOL_PROCESS            pProcess,
    IN  PIRP                            pIrp
    );


// IRQL == PASSIVE_LEVEL
//
NTSTATUS
UlDeliverRequestToProcess(
    IN PUL_APP_POOL_OBJECT pAppPool,
    IN PUL_INTERNAL_REQUEST pRequest
    );

VOID
UlUnlinkRequestFromProcess(
    IN PUL_APP_POOL_OBJECT pAppPool,
    IN PUL_INTERNAL_REQUEST pRequest
    );

// IRQL == PASSIVE_LEVEL
//
NTSTATUS
UlGetPoolFromHandle(
    IN HANDLE                           hAppPool,
    OUT PUL_APP_POOL_OBJECT *           ppAppPool
    );


NTSTATUS
UlInitializeAP(
    VOID
    );

VOID
UlTerminateAP(
    VOID
    );

PUL_APP_POOL_PROCESS
UlCreateAppPoolProcess(
    PUL_APP_POOL_OBJECT pObject
    );

VOID
UlFreeAppPoolProcess(
    PUL_APP_POOL_PROCESS pProcess
    );

PUL_APP_POOL_OBJECT
UlAppPoolObjectFromProcess(
    PUL_APP_POOL_PROCESS pProcess
    );

VOID
UlLinkConfigGroupToAppPool(
    IN PUL_CONFIG_GROUP_OBJECT pConfigGroup,
    IN PUL_APP_POOL_OBJECT pAppPool
    );

NTSTATUS
UlWaitForDisconnect(
    IN PUL_APP_POOL_PROCESS pProcess,
    IN PUL_HTTP_CONNECTION  pHttpConn,
    IN PIRP pIrp
    );

VOID
UlCompleteAllWaitForDisconnect(
    IN PUL_HTTP_CONNECTION pHttpConnection
    );

NTSTATUS
UlpCopyRequestToBuffer(
    IN PUL_INTERNAL_REQUEST pRequest,
    IN PUCHAR pKernelBuffer,
    IN PVOID pUserBuffer,
    IN ULONG BufferLength,
    IN PUCHAR pEntityBody,
    IN ULONG EntityBodyLength
    );

PUL_INTERNAL_REQUEST
UlpDequeueNewRequest(
    IN PUL_APP_POOL_PROCESS pProcess
    );

__inline
NTSTATUS
FASTCALL
UlpComputeRequestBytesNeeded(
    IN PUL_INTERNAL_REQUEST pRequest,
    IN PULONG pBytesNeeded
    )
{
    NTSTATUS Status;
    ULONG SslInfoSize;

    //
    // Calculate the size needed for the request, we'll need it below.
    //

    *pBytesNeeded =
        sizeof(HTTP_REQUEST) +
        pRequest->TotalRequestSize +
        (pRequest->UnknownHeaderCount * sizeof(HTTP_UNKNOWN_HEADER));

    //
    // Include additional space for the local and remote addresses.
    //
    // CODEWORK: Make this transport independent.
    //

    *pBytesNeeded += sizeof(HTTP_NETWORK_ADDRESS_IPV4) * 2;

    //
    // Include space for any SSL information.
    //

    if (pRequest->pHttpConn->SecureConnection)
    {
        Status = UlGetSslInfo(
                        &pRequest->pHttpConn->pConnection->FilterInfo,
                        0,                      // BufferSize
                        NULL,                   // pUserBuffer
                        NULL,                   // pBuffer
                        &SslInfoSize            // pBytesNeeded
                        );

        if (NT_SUCCESS(Status))
        {
            //
            // Struct must be aligned; add some slop space
            //

            *pBytesNeeded = ALIGN_UP(*pBytesNeeded, PVOID);
            *pBytesNeeded += SslInfoSize;
        }
        else
        {
            return Status;
        }
    }

    return STATUS_SUCCESS;
}

#ifdef __cplusplus
}; // extern "C"
#endif

#endif // _APOOL_H_