Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By MICKEY WILLIAMS and David Bennett

ISAPI Support in MFC

Visual C++ 6.0 also provides support for ISAPI extension applications and filters using MFCs that encapsulate the ISAPI you saw earlier in this chapter. The classes provided by MFC to work with ISAPI include the following:

In addition, MFC defines a set of macros that enables you to define a parse map to map requests to your ISA to specific functions in your implementation. Visual C++ also provides an Application Wizard for creating ISAs and ISAPI filters that use the MFC ISAPI classes.

Creating ISAPI DLLs with AppWizard

Visual C++ 6.0 provides an Application Wizard you can use to generate the skeleton of an ISAPI filter or ISA. In fact, you can use it to create both a filter and an ISA in the same DLL project. To use the ISAPI AppWizard, choose File, New and choose the ISAPI Extension Wizard from the Projects page. You also need to select a name and location for your new project.

The first step in the ISAPI Extension Wizard enables you to choose whether you want to create a filter object or a server extension object (ISA). This dialog box also enables you to choose names for your classes and descriptions for your filter or ISA, as well as to select whether to use MFC as a DLL or as a statically linked library.

If you are only creating a server extension, you can click Finish to see a short summary of the project that the wizard will create for you. Just click OK in this dialog box, and the wizard creates the skeleton of a server extension for you.

If you are creating a filter object, clicking Next presents a second dialog box that enables you to set up the priority for your filter and the types of connections and specific notifications it is interested in handling. These options correspond directly to the flags that are returned in the GetFilterVersion() function that you saw earlier.

After you create the framework for your DLL with the ISAPI Extension Wizard, you are ready to start adapting the MFC classes that it creates to suit the needs of your specific implementation. In the following sections, you will look at each of the classes that MFC provides to help in working with ISAPI.

CHttpServer

An MFC ISA will have one—and only one—object derived from CHttpServer. This class encapsulates both the GetExtensionVersion() and HttpExtensionProc() entry points that you saw earlier. In addition, it provides several other methods that can simplify the processing of client requests, and the CHttpServer class enables you to create CHtmlStream objects that can be used for building responses.

Your ISA should create one instance of your class derived from CHttpServer. You can specify a delimiter character used to separate command parameters in requests for your ISA, although the default (&) generally should be used.

CHttpServer::GetExtensionVersion()

The CHttpServer class provides a member function that is identical to the GetExtensionVersion() function you saw earlier, which reports version information back to the server when the ISA is first loaded. You also can use this function to perform the initialization of your ISA.

CHttpServer::HttpExtensionProc()

The CHttpServer class also implements a member function that is exported as the HttpExtensionProc() you saw earlier. This function is called once for each client request that is received. The default implementation of this function will create a new CHttpServerContext object for the request, parse the request, call the CHttpServer::InitInstance() member function, and call CHttpServer::CallFunction() to use a parse map to route the client request to a function in your ISA. It also will use the CHttpServer::OnParseError() function to generate HTML responses to handle various errors that may occur.

InitInstance()

The InitInstance() member of CHttpServer is called by the default implementation of CHttpServer::HttpExtensionProc() each time a new request is received. You can override this function in your class derived from CHttpServer to perform any initialization for each request. Any initialization that should be performed only once, when the DLL is first loaded, should not appear in InitInstance() but should be placed somewhere else—in the GetExtensionVersion() member function, for example.

CallFunction()

The CallFunction() member of CHttpServer is called by the default implementation of CHttpServer::HttpExtensionProc() to map the command in the client request URL to a specific function in your ISA. The default implementation of this function uses a parse map (similar to message maps) to choose the appropriate function to call, based on command information from the ECB structure. If you want to perform your own custom mapping of commands to implementation functions, you can override this function—in which case, you will not need to implement a parse map.

Parse Maps

MFC provides a set of macros for defining a parse map in your ISA. The parse map is a structure, similar to MFC message maps, that is used by the CallFunction() method to map a client request to a function that will handle the request. You should define one—and only one—parse map in each of your MFC ISA DLLs.

A parse map begins with the BEGIN_PARSE_MAP macro, which takes parameters for the class defining the map and its base class (usually CHttpServer), and ends with the END_PARSE_MAP macro, which takes the name of the class defining the map.

Between the begin and end macros, you can add entries for specific commands with the ON_PARSE_COMMAND macro, which takes parameters for the name of a member function, the class name that the member function belongs to, and a list of constants representing the parameters to be passed to the handler function. The following are the constants used to represent the available argument types:

To illustrate how this works, let's take a look at an example parse map, which will map the command myFunc1 to CMyIsa::myFunc1() and myFunc2 to CMyIsa::myFunc2():

BEGIN_PARSE_MAP(CMyIsa, CHttpServer)
    ON_PARSE_COMMAND(myFunc1, CMyIsa, ITS_I2 ITS_PSTR)
    ON_PARSE_COMMAND_PARAMS("param1 param2=default")
    ON_PARSE_COMMAND(myFunc2, CMyIsa, ITS_I4)
    ON_PARSE_COMMAND_PARAMS("myIndex=123")
    DEFAULT_PARSE_COMMAND(myFunc1, CMyIsa)
END_PARSE_MAP(CMyIsa)

The ON_PARSE_COMMAND_PARAMS macro, used in the example, specifies the names of the parameters for the command in the ON_PARSE_COMMAND immediately preceding it. This macro also is used to assign default values to the parameters; if a default value is not assigned in the parse map, the client must supply a value, or the call will fail. In the example, when the client requests the myFunc1 command, it must specify a parameter named param1. If the client does not specify a param2 parameter, it will default to an empty string.

The example also shows the use for the DEFAULT_PARSE_COMMAND macro, which specifies a handler function (and its class) for cases where the client does not include a command in the request.

Handler Functions

The handler functions specified in the ON_PARSE_COMMAND macros should be members of your CHttpServer-derived class. They also should all return void and take a pointer to a CHttpServerContext object as their first parameter. They also should take additional parameters, as specified in the ON_PARSE_COMMAND macro.

For example, consider the following parse map entry:

ON_PARSE_COMMAND(foo, CMyIsa, ITS_I4 ITS_PSTR)

The prototype for the handler function should look like this:

void CMyIsa::foo(CHttpServerContext* pCtxt, int nParam1, LPTSTR pszParam2);

OnParseError()

The OnParseError() member of CHttpServer is called by MFC to create an HTML response for the client based on a set of error codes. If you want to generate your own custom error messages, you can override this member function.

ConstructStream()

MFC will call the ConstructStream() method of CHttpServer to create a new CHtmlStream object. If you want to modify the default behavior, you can override this function.

StartContent()

You can use this function to insert the <Body> and <HTML> tags into an HTML stream to be returned to the client.

EndContent()

You can use this function to insert the </Body> and </HTML> tags into an HTML stream to be returned to the client.

WriteTitle()

This function will insert the title string returned by GetTitle() (surrounded by <Title> and </Title>) into an HTML string to be returned to the client.

GetTitle()

This function is called by MFC to retrieve a title to add to the HTML page to be returned to the client. You can override this function to supply a title other than the default.

AddHeader()

You can call this function to add additional headers to the HTML stream to be returned to the client.

CHttpServerContext

The default implementation of CHttpServer::HttpExtensionProc() will create a new CHttpServerContext object each time it is called to process a new client request. This class encapsulates the ECB structure that you saw earlier, which is available directly via the m_pECB member. The m_pStream member also provides direct access to the CHtmlStream object that will be returned to the client.

This class implements member functions that correspond directly to the GetServerVariable(), ReadClient(), WriteClient(), and ServerSupportFunction() functions that are passed in the ECB, as you saw earlier.

In addition, CHttpServerContext overloads the insertion operator (<<) to write data to the CHtmlStream object associated with the CHttpServerContext object.

CHtmlStream

The CHtmlStream class provides an abstraction for writing HTML data to a temporary memory file before it is sent back to the client. CHtmlStream objects are created by the default implementation of CHttpServer::ConstructStream(), which is called by the default implementation of CHttpServer::CallFunction() to create a new HTML stream to associate with the CHttpServerContext object for a client request. CallFunction() also calls CHtmlStream::InitStream(), which you may override, to initialize the new stream.

You can retrieve the size of the stream file by calling the GetStreamSize() method or by accessing the m_nStreamSize member directly.

CHtmlStream provides the Write() method to enable you to write data to the stream, as well as an overload of the insertion operator (<<) that performs the same function.

Several other members of CHtmlStream also can be used to more closely control the memory used by the stream. Many of these functions can be overridden in classes you derive from CHtmlStream if you are interested in changing the way CHtmlStream deals with memory.

CHttpFilter

MFC provides the CHttpFilter class to encapsulate the functionality required for implementing an ISAPI filter. It will export GetFilterVersion() and HttpFilterProc() entry points, which call CHttpFilter member functions of the same name. For each MFC ISAPI filter, one—and only one—CHttpFilter object should be created. As you will see in just a bit, multiple CHttpFilterContext objects will be created by the CHttpFilter—one for each notification that is received.

CHttpFilter::GetFilterVersion() corresponds directly to the GetFilterVersion() function that you saw earlier. You should override this function in your class derived from CHttpFilter to specify the priority of your filter and the events it wants to process.

You also may override CHttpFilter::HttpFilterProc() to implement your event handlers, although the default implementation will create a new CHttpFilterContext object for you and call one of the other member functions of CHttpFilter, which you can override to handle each specific event. Here are the member functions that are called, with the events that generate calls to them:

SF_NOTIFY_READ_RAW_DATA OnReadRawData()
SF_NOTIFY_SEND_RAW_DATA OnSendRawData()
SF_NOTIFY_PREPROC_HEADERS OnPreprocHeaders()
SF_NOTIFY_AUTHENTICATION OnAuthentication()
SF_NOTIFY_URL_MAP OnUrlMap()
SF_NOTIFY_LOG OnLog()
SF_NOTIFY_END_OF_NET_SESSION OnEndOfNetSession()

The implementation of these functions is very similar to the handling for the event notifications that you saw earlier.

CHttpFilterContext

A new CHttpFilterContext object is created by MFC whenever a new notification is received. This object provides your filter with information about the notification and also provides mechanisms for your filter to communicate with the server, as well as the client making the request. This class encapsulates the HTTP_FILTER_CONTEXT structure that you saw earlier. You can directly access this structure via the m_pFC member of CHttpFilterContext.

The CHttpFilterContext class provides member functions that correspond directly to the GetServerVariable(), AddResponseHeaders(), WriteClient(), ServerSupportFunction(), and AllocMem() functions that you saw earlier during the discussion of the C version of ISAPI for creating filters.

Share ThisShare This

Informit Network