NAME

ServerSupportFunction - general purpose callback for ISAPI handlers.


SYNOPSIS

 /* cb is either an extension control block 
    or a filter context; see isapi(7) */
 HCONN connid = (extension) ? cb->ConnID : cb;
 DWORD request; /* at least 18 possible values, which
                   control the  meanings of ... */
 LPVOID buffer;
 LPDWORD size, type;

 if ( cb->ServerSupportFunction( connid, request, 
                                 buffer, size, type )) {
        /* Success: use results */
 } else {
        /* handle error */
 }


DESCRIPTION

An ISAPI-handled request, when passed down to a handler library, is described by a `control block' or `filter context' data structure (the pointer to which is above illustratively given as variable `cb') which provides various routines by which the handler can communicate with the client and server. One of these is the ServerSupportFunction, which provides a variety of support functionality which the handler may need. See isapi(7) or the appropriate Http*Proc(3) for details.

ServerSupportFunction is a generic routine providing a wide range of services. Some of these are available for filters, some for extensions and some for both.

connid

First argument identifies the connection being serviced; it is obtained from the same control block or filter context as provided ServerSupportFunction; for an extension it is the ConnID field of the control block; for a filter, it is the filter context itself.

request

Second argument (request) is a DWORD specifying what to do; this dictates the meaning of the remaining three arguments (buffer, size and type). These last are all pointers (and may be ignored for some requests); the DWORD pointed to by size generally describes the size of the array indicated by buffer, the DWORD pointed to by type generally provides ancillary data; and buffer is generally the main channel of information. However the details of their meanings and roles depend on the request code.


REQUEST VALUES

The following request values are recognized. Those whose names begin HSE_ are only available to extensions, those beginning SF_ are only available to filters; where a request value is valid for both types, both names are listed.

HSE_REQ_SEND_RESPONSE_HEADER SF_REQ_SEND_RESPONSE_HEADER

This sends an HTTP header (without its trailing blank line); it should only be called once during any given invocation of an Http*Proc. Its buffer parameter, if non-NULL, is interpreted as a string giving the HTTP status line, e.g. ``200 OK''. The meaning of the other two parameters varies between filters and extensions.

For an extension, using HSE_REQ_SEND_RESPONSE_HEADER, size is interpreted as a pointer to an integer giving the number of bytes of text pointed to by type, which is interpreted as a string containing further headers.

For a filter, using SF_REQ_SEND_RESPONSE_HEADER, size is interpreted as a string providing further headers; type is ignored.

HSE_APPEND_LOG_PARAMETER

Emits logging information.

HSE_REQ_ASYNC_READ_CLIENT

Sets up for asynchronous reading from the client. Requires last argument, type, to be HSE_IO_ASYNC.

HSE_REQ_DONE_WITH_SESSION

Ignores size and type; buffer may optionally point to a DWORD (i.e. 32-bit int) which contains an HSE_STATUS code, notably the value HSE_STATUS_SUCCESS_WITH_KEEP_CONN.

This explicitly requests that the connection be closed.

HSE_REQ_IS_KEEP_CONN

Enquires whether the connection has keep-alive enabled. Pass the address of a BOOL variable as buffer; it will be set to TRUE or FALSE as appropriate. Note, later request processing may alter the state of the keep-alive status of this connection, so this callback is not very useful, but is included for compatibility with IIS.

HSE_REQ_MAP_URL_TO_PATH

Third parameter, buffer, should point to the start of a character array; fourth parameter, size, should point to a DWORD. In buffer, store an URL-namespace path (terminated with a '\\0'), in size store the size of the buffer (this may usefully be greater than the length of the URL path); then call ServerSupportFunction. (Ignores last parameter, type - but see HSE_REQ_MAP_URL_TO_PATH_EX, below.) On successful return, buffer shall contain the local file-system's path corresponding to the given URL; this path's length will be stored in size.

This callback is currently unimplemented.

HSE_REQ_SEND_URL_REDIRECT_RESP

Point buffer at a '\\0'-terminated string specifying an URL, point size at a DWORD containing the length of this string (type is ignored). The client will be sent an HTTP 302 response (URL Redirect) redirecting it to the given URL. A handler using this should tidy up any resources allocated to the request and return control to the ISAPI runner, since the client will be redirected, so will not see any further response from the handler.

HSE_REQ_SEND_URL

Point buffer at a '\\0'-terminated string giving an URL, point size at a DWORD containing the length of this string (type is ignored). The client will receive the response it would have received if the client had requested this URL (but it will appear to have come from the URL the client requested). A handler using this should tidy up and return as for a redirect.

HSE_REQ_TRANSMIT_FILE

Provides a very high-performance mechanism for sending a file (or a portion of a file) to the client, with an optional header or footer. HSE_REQ_TRANSMIT_FILE will send the file directly from the webserver's internal cache, and is the recommended way of sending content back to the client for scenarios requiring high-performance.

The buffer variable should point to a HSE_TF_INFO object. size and type parameters are ignored. See the EXAMPLES section below for sample usage.

HSE_TF_INFO.pHead and HSE_TF_INFO.pTail, if provided, should point to memory that is valid for the lifetime of the connection, e.g. it should either be static, or point into memory returned from the AllocMem() call. It should not point to temporarily allocated memory, such as off the stack, since this memory is used to asynchonrously write the data to the client in the background after the ISAPI extension has completed, the web server does not take a copy of it for performance reasons.

SF_REQ_ADD_HEADERS_ON_DENIAL

Registers some headers for the web server to deliver if processing of the request leads to access denial. These headers will be ignored if the request is responded to successfully.

SF_REQ_DISABLE_NOTIFICATIONS

Suppresses some notifications.

SF_REQ_GET_CONNID

Returns the connection ID associated with the request being processed. This is the ConnID field of the EXTENSION_CONTROL_BLOCK an extension would see: it is of use (for example) in enabling filters and extensions to coordinate shared data.

SF_REQ_NORMALISE_URL

Requests normalization of a URL.

SF_REQ_SET_NEXT_READ_SIZE

Sets the block-size in which buffered data is to be read: only of use to filters which return SF_STATUS_REQ_READ_NEXT while handling one of the raw data notifications.

SF_REQ_SET_PROXY_INFO

Indicates that the request came from a proxy.


SPECIFICATION EXTENTIONS

The following request values are extensions added by the Zeus Web Server.

HSE_REQ_GET_FILE_DESCRIPTOR SF_REQ_GET_FILE_DESCRIPTOR

Allows in-process ISAPI extensions or filters to get hold of the raw file-descriptor that the web server is using to talk to the client.

buffer should point to a memory location containing a DWORD. size should contain the size of the buffer, which should be at least a DWORD in size. Upon successful return buffer will be filled in with the file-descriptor.

HSE_REQ_ALLOC_MEM

Allows an ISAPI extension to use the per-request memory pool facility provided by the web server. Allocations from this memory pool are very fast, and any memory allocated is actually freed upon completion of the request, making memory management much easier and memory leaks much less likely.

On entry, size should point to a DWORD containing the amount of memory to allocate. On successful return, buffer will point the allocated memory block.

HSE_REQ_TRANSMIT_FILEV

An extension of the functionality for HSE_REQ_TRANSMIT_FILE. Allows the ISAPI extension (in-process only), to provide multiple chunks to write back to the client, similar to the writev() system call. Each chunk has a flag associated with it which provides the web server with information about the 'lifetime' of the chunk. Each chunk can live in one of three states:

1. from the file denoted by hFile

2. from memory that will be stable/persistent for at least the lifetime of the transaction. The web server need not take a copy of it.

3. from memory that is not stable/persistent for at least the lifetime of the transaction. The web server will take a copy of the data presented to it in this call, and use that copy to send asynchronously to the client.

On entry, buffer should point to a HSE_TFV_INFO structure.

Currently, at most one chunk can be provided by hFile.

HSE_REQ_GET_TIMEPTR

Available to in-process extensions only. Allows the ISAPI extension to retrieve the address of a memory location that is updated with the current time once-per-second. This allows the ISAPI extension to get hold of the current time without having to use expensive OS calls.

On entry, buffer should point to an LPDWORD, and size should point to a DWORD containing the size of the memory pointed to be buffer, which should be >= sizeof( LPDWORD ). Upon successful return, buffer will now point to a memory location which will always contain the current time.

SF_REQ_SEND_RAW_MIME

Available to in-process filters only. Allows the ISAPI filter to specify which mime-type/class it is interested in dealing with when using the SF_NOTIFY_SEND_RAW_DATA hook. Generally, most ISAPI filters using SF_NOTIFY_SEND_RAW_DATA are only interested in rewriting a particular kind of response (e.g. those of the mime-type text/html), and the overhead of using SF_NOTIFY_SEND_RAW_DATA is quite high. By using this support function, the filter can tell the web server to only call it to rewrite output of certain mime-types, which can provide a huge performance increase.

Furthermore, setting the mime-type it is interested in to an unused/invalid mime-type, (e.g. /), allows the ISAPI filter to notify the web server it is not interested in this rewriting this connection at all.

Obviously this must be called before any data is sent to the client, so SF_NOTIFY_PREPROC_HEADERS is a good time.

On entry, buffer should point to a zero-terminated string containing the mime-type, or mime-class that the filter is interested in receiving SF_NOTIFY_SEND_RAW_DATA events for. An example mime-type would be text/html. An example mime-class would be text/, which would signify the filter is interested in any mime-types beginning 'text/'.

SF_REQ_BYTES_WRITTEN

Available to in-process ISAPI filters only, SF_REQ_BYTES_WRITTEN allows a filter to retrieve the number of bytes that have been written to the client, primarily useful on the SF_NOTIFY_LOG notification event.

On entry, buffer should point to a DWORD, and size should point to a DWORD containing the size of buffer, which should be at least sizeof( DWORD ). On successful return, buffer willl contain the number of bytes written to the client, including HTTP headers.

SF_REQ_GET_INHEADER, SF_REQ_GET_OUTHEADER

Available to in-process ISAPI filters only, SF_REQ_GET_INHEADER and SF_REQ_GET_OUTHEADER are primarily useful on the SF_NOTIFY_LOG notification event. They allow the ISAPI filter the query the HTTP headers that were sent back to the client, and the HTTP headers in the client's request.

On entry, buffer should point to a zero-terminated string containing the header line to query, e.g. 'Content-Length'. size should point to a memory location where the result should be placed. type should point to a DWORD which contains the size of the buffer pointed to be size. On successful return, the memory pointed to by size will be filled with a zero-terminated string.


UNSUPPORTED REQUEST VALUES

The following Microsoft-specific request values are supported by IIS but not by ZWS, generally because they relate to functionality needed in Windows but having no natural equivalent under Unix.

HSE_REQ_GET_IMPERSONATION_TOKEN

Third argument, buffer, should be the address of a HANDLE, which will be made to represent a user context, needed if the ISAPI handler is to perform Windows-specific `impersonation' actions.

HSE_REQ_IO_COMPLETION

Registers a completion-callback for asynchronous I/O operations. Requires buffer to be the address of a callback function, of type PFN_HSE_IO_COMPLETION, with its associated Context value as the final argument, type. The latter will be passed as the second argument to the former whenever the former is called.

HSE_REQ_ABORTIVE_CLOSE

Selects Windows' ruder but more efficient TCP socket close option, there is no equivalent under Unix.

SF_REQ_GET_PROPERTY

Access to the contents of an SF_PROPERTY_IIS structure.

HSE_REQ_GET_SSPI_INFO

Third argument, buffer, should be a CtxtHandle; fifth, type, should be a CredHandle. Only available for in-process extensions.

HSE_REQ_GET_CERT_INFO_EX

Buffer should be a pointer to a CERT_CONTEXT_EX structure, within which the embedded buffer of CERT_CONTEXT is allocated; the first certificate in the client's certificate chain will be copied into this.

SF_REQ_SET_CERTIFICATE_INFO

Sets an SSPI security context and impersonation token derived from a client certificate.

HSE_REQ_MAP_URL_TO_PATH_EX

A fuller URL-decoding interface, extending HSE_REQ_MAP_URL_TO_PATH: this requires the last parameter, type, to point to an HSE_URL_MAPEX_INFO structure into which the URL will be decoded.

This callback is currently unimplemented.

HSE_REQ_REFRESH_ISAPI_ACL

Force ISAPI runner to re-read the ACL of the handler: buffer points to a string containing the name of the shared library implementing the handler.

HSE_REQ_SEND_URL_EX

As HSE_REQ_SEND_URL but `with support for specifying the verb to use in the redirect'.

HSE_REQ_SEND_RESPONSE_HEADER_EX

This extends HSE_REQ_SEND_RESPONSE_HEADER by taking a pointer to a HSE_SEND_HEADER_EX_INFO(5) structure as its buffer argument: ignores the other two arguments.


RETURN

Returns TRUE on success; FALSE otherwise.


EXAMPLES

HSE_REQ_TRANSMIT_FILE

For most efficient operation:

  1. Use HSE_IO_SEND_HEADERS to send the headers in the same call as the HSE_REQ_TRANSMIT_FILE call.

  2. If you are providing the entire response to this query, and are not going to be doing subsequent calls to WriteClient(), set the HSE_IO_DISCONNECT_AFTER_SEND flag.

    This flag notifies the webserver that is has enough information to generate the entire response, and when coupled with the HSE_IO_SEND_HEADERS flag, it will cause the webserver to automatically insert a 'Content-Length' header in the response and negotiate a HTTP/1.1 keep-alive response with the client.

Sample usage:

        /* Sample extension that demonstrates use of
           HSE_REQ_TRANSMIT_FILE */

        #include <string.h>
        #include <stdio.h>

        #include "httpext.h"

        #define Success  0
        #define Failure -1

        static int run( LPEXTENSION_CONTROL_BLOCK );

        // ----------------------------------------------------------

        BOOL WINAPI
        GetExtensionVersion( HSE_VERSION_INFO *pVer ) 
        {
                pVer->dwExtensionVersion = 1;
                strncpy( pVer->lpszExtensionDesc, "Transmitfile test",
                HSE_MAX_EXT_DLL_NAME_LEN );
                return 1;
        }

        // ----------------------------------------------------------

        DWORD WINAPI
        HttpExtensionProc( LPEXTENSION_CONTROL_BLOCK ecb )
        {
                if( run( ecb ) == Failure ) return HSE_STATUS_ERROR;
                return HSE_STATUS_SUCCESS_AND_KEEP_CONN;
        }

        // ----------------------------------------------------------

        static void
        WriteHeader( LPEXTENSION_CONTROL_BLOCK ecb, const char* a )
        {
                DWORD sz = strlen( a );
                ecb->ServerSupportFunction( ecb->ConnID, 
                        HSE_REQ_SEND_RESPONSE_HEADER, 0,
                        &sz, (DWORD*)a );
        }

        // ----------------------------------------------------------

        static void 
        Output( LPEXTENSION_CONTROL_BLOCK ecb, const char* a )
        {
                DWORD l = strlen( a );
                ecb->WriteClient( ecb->ConnID, (void*)a, &l, 0 );
        }

        // ----------------------------------------------------------

        static int
        run( LPEXTENSION_CONTROL_BLOCK ecb )
        {
                static int headerlen       = 0;
                static int trailerlen      = 0;
                static const char* header  = "This is my header\n";
                static const char* trailer = "This is my trailer\n";

                const char* filename = "/etc/passwd";
                HSE_TF_INFO tf;

                // one-time initalisation
                if( headerlen  == 0 ) headerlen  = strlen( header );
                if( trailerlen == 0 ) trailerlen = strlen( trailer );

                tf.pfnHseIO      = 0;  /* these must be 0 for Zeus */
                tf.pContext      = 0;
                tf.hFile         = (HANDLE)filename;
                tf.pszStatusCode = "200 OK\n"
                        "Content-Type: text/plain\nFoo: bar";
                /* write all of file - could specify a portion */
                /* of the file to send here. */
                tf.BytesToWrite  = 0;
                tf.Offset        = 0;
                tf.pHead         = (PVOID)header;
                tf.HeadLength    = headerlen;
                tf.pTail         = (PVOID)trailer;
                tf.TailLength    = trailerlen;

                // Because we are not going to be sending any more
                // data after this, we set
                // HSE_IO_DISCONNECT_AFTER_SEND to let the webserver
                // know, and because we are also generating headers,
                // we can use this information to insert a
                // Content-Length & hence use HTTP KeepAlive for
                // better performance.

                tf.dwFlags = HSE_IO_SEND_HEADERS | 
                        HSE_IO_DISCONNECT_AFTER_SEND |
                        HSE_IO_HANDLE_IS_FILENAME;

                if( ecb->ServerSupportFunction( ecb->ConnID, 
                        HSE_REQ_TRANSMIT_FILE,
                        &tf, 0, 0 ) == TRUE ) return Success;

                // Failed for some reason!
                // Drop back to less efficent code
                WriteHeader( ecb, "Content-Type: text/html" );
                Output( ecb, "Transmitfile failed" );
                return Success;
        }


SEE ALSO

isapi(7), HttpExtensionProc(3), HttpFilterProc(3), EXTENSION_CONTROL_BLOCK(5), HTTP_FILTER_CONTEXT(5), HSE_SEND_HEADER_EX_INFO(5).


FILES

$ZEUSHOME/web/include/httpext.h $ZEUSHOME/web/include/httpfilt.h $ZEUSHOME/web/include/wintypes.h $ZEUSHOME/webadmin/docroot/docs/modules/isapi/*.html


NOTES

Note that the type name for a pointer to a HSE_TF_VEC structure, as passed when requesting HSE_REQ_TRANSMIT_FILEV, has been changed from LPHSE_TV_VEC to LPHSE_TF_VEC, the intended spelling. The old name is deprecated but supported by a #define; extensions using it should replace the old name with the new.


COPYRIGHT

Copyright (C) 2000-2001 Zeus Technology Limited. All rights reserved.