Calling HSE_REQ_EXEC_URL and HSE_REQ_EXEC_UNICODE_URL

The HSE_REQ_EXEC_URL and HSE_REQ_EXEC_UNICODE_URL support functions asynchronously process a child request, and enable an ISAPI extension to rewrite a client URL request in order to call another ISAPI extension or redirect to a Web file.

This topic provides some extended information about using these support functions.

Comparison

The HSE_REQ_EXEC_URL and HSE_REQ_EXEC_UNICODE_URL support functions are functionally identical except in the following ways.

HSE_REQ_EXEC_URL

HSE_REQ_EXEC_UNICODE_URL

Only executes URLs formatted in the codepage of the IIS server. It does not support international applications, and is slower than the Unicode version.

  • Only executes URLs formatted in Unicode characters. It fully supports retrieving any Unicode resource on a file system like NTFS.

Allows only ANSI characters to be specified for child AUTH_USER, LOGON_USER, or REMOTE_USER IIS Server Variables in the pszCustomUserName parameter.

  • Allows Unicode characters to be specified for child AUTH_USER, LOGON_USER, or REMOTE_USER IIS Server Variables in the pszCustomUserName parameter.

Overview

The HSE_REQ_EXEC_URL and HSE_REQ_EXEC_UNICODE_URL support functions allow the calling ISAPI extension (the parent) to modify the entire incoming request and pass the modifications to the child application (another ISAPI or a Web page). The ISAPI call is asynchronous, so you must call HSE_REQ_IO_COMPLETION to set the asynchronous completion function.

Modifications of the incoming request can include the impersonation token and selected facets of the child application's response.

The HSE_REQ_EXEC_URL and HSE_REQ_EXEC_UNICODE_URL support functions can not redirect URLs across application pools or across directories that are configured to use different authentication schemes. They also can not filter the child application's response, but they can suppress child headers sent by IIS.

Pre-fetching Request Data

IIS handles request data in the following way

  1. A client makes a request, posting 128K of data.

  2. IIS pre-fetches the first 48K of the request and makes it available to the lpbData parameter of the EXTENSION_CONTROL_BLOCK Structure structure of the ISAPI extension.

  3. The parent ISAPI extension calls the child application with the following values:

    HSE_EXEC_URL_ENTITY_INFO->cbAvailable = 1
    HSE_EXEC_URL_ENTITY_INFO->lpbData = "A"
    
  4. The child application sees the following data, with 32 + 1K pending:

    pecb->lpbData = "A" + (48K-1)
    pecb->cbAvailable = 48K
    pecb->cbTotalBytes = 80K+1
    

HSE_EXEC_URL_INFO and HSE_EXEC_UNICODE_URL_INFO

typedef struct _HSE_EXEC_URL_INFO  {
    LPSTR pszUrl;                       // URL to execute
    LPSTR pszMethod;                    // Method
    LPSTR pszChildHeaders;              // Request headers for child
    LPHSE_EXEC_URL_USER_INFO pUserInfo; // User for new request
    LPHSE_EXEC_URL_ENTITY_INFO pEntity; // Entity body for new request
    DWORD dwExecUrlFlags;               // Flags
} HSE_EXEC_URL_INFO, * LPHSE_EXEC_URL_INFO;

The HSE_EXEC_URL_INFO Structure and HSE_EXEC_UNICODE_URL_INFO Structure structures, used with the HSE_REQ_EXEC_URL and HSE_REQ_EXEC_UNICODE_URL support functions, define the outline of a new request that the calling ISAPI (parent) gives the child application. From the perspective of the child, being invoked by the HSE_REQ_EXEC_URL and HSE_REQ_EXEC_UNICODE_URL support functions is no different than being invoked by a direct client request; server variables are changed accordingly, pending data in the rest of the request remains to be read, and keep-alive status is preserved (except certain exceptional corner cases).

Note

Setting pszUrl to null in the *_URL_INFO structures and calling the support functions will cause an ISAPI extension to enter an infinite loop unless there is code to detect recursion.

The pszChildHeaders parameter is tested for proper <header>:<value> format; any illegal headers are ignored. No trailing double CRLF CRLF is necessary because the format test would ignore the second CRLF as an illegal header.

For all parameters except dwExecUrlFlags, a value of null tells the *_URL_INFO structure to pass to the child application whatever value the parent received. Any other value is passed to the child application in the related portion of the request.

The dwExecUrlFlags parameter can contain the following possible values.

  • HSE_EXEC_URL_NO_HEADERS - If present, the parent ISAPI suppresses any headers that are sent normally by IIS from the child application. Consider the case where the parent ISAPI wants to put a header or footer banner around the content the child generates.

  • HSE_EXEC_URL_IGNORE_CURRENT_INTERCEPTOR - If present, the parent ISAPI is not invoked anymore for the duration of the processing of the child application. The parent ISAPI only runs once for the child application, even if the child request recurses back to the same parent URL, or the child encounters other global interceptor ISAPIs that utilize the parent ISAPI.

  • HSE_EXEC_URL_IGNORE_VALIDATION_AND_RANGE - If present, the parent ISAPI automatically consumes and does not pass onto the child several HTTP caching-related headers, such as If-Match, If-Modified-Since, If-None-Match, If-Range, If-Unmodified-Since, and Range.

  • HSE_EXEC_URL_DISABLE_CUSTOM_ERROR - If present, any HTTP custom error entity-body sent by the child is consumed by the parent. The headers are still returned, so the parent ISAPI can detect and return its own custom error message.

  • HSE_REQ_GET_EXEC_URL_STATUS - This allows the parent ISAPI to retrieve some basic status information from the child's processing. This is only useful in the child's asynchronous callback function, and has no meaning if you call it right after HSE_REQ_EXEC_URL or HSE_REQ_EXEC_UNICODE_URL.

HSE_EXEC_URL_USER_INFO and HSE_EXEC_UNICODE_URL_USER_INFO

typedef struct _HSE_EXEC_URL_USER_INFO  {
    HANDLE hImpersonationToken;
    LPSTR pszCustomUserName;
    LPSTR pszCustomAuthType;
} HSE_EXEC_URL_USER_INFO, * LPHSE_EXEC_URL_USER_INFO;

The pUserInfo parameter of the HSE_EXEC_URL_INFO Structure and HSE_EXEC_UNICODE_URL_INFO Structure structures allows the parent ISAPI to change the user authentication token used by the child application. As a best practice, obtain the user token and set it in the hImpersonationToken parameter of the *_URL_USER_INFO structures.

The pszCustomUserName parameter of the *_URL_USER_INFO structures allows you to assign a new logged on user identity for the child application. If the child application viewed the AUTH_USER, LOGON_USER, or REMOTE_USER IIS Server Variables, the variables would refer to the new identity. However, the hImpersonationToken parameter determines actual resource access.

The pszCustomAuthType parameter of the *_URL_USER_INFO structures allows you to specify an arbitrary authentication type ("Auth-Type" header) for the child application. If the child application viewed the AUTH_TYPE, IIS Server Variables, the variable would refer to the new authentication name.

The pszCustomUserName and pszCustomAuthType parameters of the HSE_EXEC_URL_USER_INFO Structure structure must not be null.

HSE_EXEC_URL_ENTITY_INFO

typedef struct _HSE_EXEC_URL_ENTITY_INFO  {
    DWORD cbAvailable;
    LPVOID lpbData;
} HSE_EXEC_URL_ENTITY_INFO, * LPHSE_EXEC_URL_ENTITY_INFO;

The pEntity parameter of the HSE_EXEC_URL_INFO Structure and HSE_EXEC_UNICODE_URL_INFO Structure structures allows the parent ISAPI to examine, modify, and consume the incoming request entity body before passing control to the child application. If there is pending data, IIS calculates the appropriate value for the cbAvailable parameter of the HSE_EXEC_URL_ENTITY_INFO Structure structure for the child based on the cbAvailable parameter of the parent ISAPI.

The lpbData parameter of the HSE_EXEC_URL_ENTITY_INFO Structure structure must not be null if the cbAvailable parameter is not 0.

HSE_EXEC_URL_STATUS

typedef struct _HSE_EXEC_URL_STATUS  {
    USHORT uHttpStatusCode;
    USHORT uHttpSubStatus;
    DWORD dwWin32Error;
} HSE_EXEC_URL_STATUS, * LPHSE_EXEC_URL_STATUS;

The HSE_EXEC_URL_STATUS Structure structure is passed to the HSE_REQ_GET_EXEC_URL_STATUS support function to retrieve any status information the child request might have set. The HTTP sub-status code can be retrieved, which is useful to determine when IIS returns an HTTP 404.2 or an HTTP 401.3 error as the result of the execution of a child request.

Example

Description

The following example uses the C++ programming language to show you how to create an ISAPI extension that passes control through to a child application.

Code

HSE_EXEC_UNICODE_URL_INFO* pHseExecUrlInfo = new HSE_EXEC_UNICODE_URL_INFO;
ZeroMemory(pHseExecUrlInfo, sizeof(HSE_EXEC_UNICODE_URL_INFO);

pECB->ServerSupportFunction( pECB->ConnID,
                             HSE_REQ_IO_COMPLETION,
                             ExecuteUrlCompletionCallback,
                             NULL,
                             (LPDWORD)pHseExecUrlInfo );

// Include other functionality here

if ( pECB->ServerSupportFunction( pECB->ConnID,
                                  HSE_REQ_EXEC_UNICODE_URL,
                                  pHseExecUrlInfo,
                                  NULL,
                                  NULL ) )
{
    return HSE_STATUS_PENDING;
}
else
{
    return HSE_STATUS_ERROR;
}


VOID
WINAPI
ExecuteUrlCompletionCallback(
    LPEXTENSION_CONTROL_BLOCK lpECB,
    PVOID pContext,
    DWORD cbIO,
    DWORD dwError
)
{
    HSE_EXEC_URL_STATUS hseExecUrlStatus;
    HSE_EXEC_UNICODE_URL_INFO* pHseExecUrlInfo = (HSE_EXEC_UNICODE_URL_INFO*)pContext;

    //
    // Set HTTP Status code on ISAPI so that logging works properly
    // cbIO and dwError should always be 0
    //
    if ( !lpECB->ServerSupportFunction( lpECB->ConnID,
                                        HSE_REQ_GET_EXEC_URL_STATUS,
                                        &hseExecUrlStatus,
                                        NULL,
                                        NULL ) )
    {
        //
        // Was not able to fetch the HTTP Status codes.  Won't log any ISAPI failures for this
        //
    }
    else
    {
        //
        // Must set lpECB->dwHttpStatusCode or else logging is incorrectly always 200
        // Should propagate hseExecUrlStatus.dwWin32Error if I don't do anything about it
        //
        lpECB->dwHttpStatusCode = hseExecUrlStatus.uHttpStatusCode;
        SetLastError( hseExecUrlStatus.dwWin32Error );
    }

    lpECB->ServerSupportFunction ( lpECB->ConnID,
                                   HSE_REQ_DONE_WITH_SESSION,
                                   HSE_STATUS_SUCCESS,
                                   NULL,
                                   NULL );

    delete pHseExecUrlInfo;
}