Enter ATL Server
MFC includes several classes to help you write ISAPI DLLs. These include CHttpServer, CHttpServerContext, and CHtmlStream. In addition, you can see that the creators of some real-world sites actually use MFC to write ISAPI DLLs for their sites.There's a good amount of documentation on writing ISAPI DLLs using MFC, including earlier editions of this book. Suffice it to say that writing ISAPI DLLs by hand is a C++ process; as a result, it's prone to C++ foibles such as memory leaks and null pointers. The MFC classes make the process somewhat easier, and ATL Server makes the process easier still.The modern way to write ISAPI DLLs is to use ATL Server. ATL Server is not solely for Web-based user interface development—it's also useful for developing Web services (programmable Web sites). Let's start by taking a look at the ATL Server architecture.
ATL vs. ATL Server
In Chapter 25, we looked at ATL as a COM development tool—a set of class libraries that hide the complexities of COM from the developer. In this chapter, we're looking at ATL Server, which actually has very little to do with COM. The ATL-based COM support is generally independent of ATL Server's support for ISAPI.
Where Does ATL Server Fit In?
There are many ways to handle an HTTP request on the Windows platform. They run the gamut from writing ISAPI DLLs by hand to writing ASP code, writing PERL scripts, and writing ASP.NET code. Each method has its advantages and disadvantages. For example, when you write ISAPI DLLs by hand, you have complete control over how to handle each request. However, you have little leverage over the response to the client. (That is, you have to generate every little part of the response programmatically.) In addition, writing ISAPI DLLs by hand means writing boilerplate code. On the other end of the spectrum is ASP, in which each page is usually a mixture of HTML, some script code, and perhaps some COM objects.ATL Server tries to sit between the two. Developing Web-based content and user interfaces usually involves managing HTML tags (or perhaps some XML in the case of Web services) and developing logic to drive dynamic content. The executable part of an ATL Server application lives within some C++ classes. In this way, ATL Server provides performance similar to when you code C++ ISAPI DLLs from scratch, and it also includes some of the HTML management features of higher-level development tools such as ASP.NET. The ATL Server classes encapsulate the request, the response, cookies, forms, and the execution context. As a result, handling requests using ATL Server is much easier than when you use the raw ECB.
In addition to a fundamental architecture for managing ISAPI executable code and HTML, ATL Server includes other useful features such as a performance cache and a thread pool.
The ATL Server Architecture
At the end of the day, your job as a Web developer (and who isn't one these days, at least in some capacity?) is to respond to HTTP requests and shove some content out to the client that's connected to your server. You've already seen how to write a server using sockets and the MFC socket classes. Microsoft has already done the dirty work of managing port 80 by providing a service that will watch the port for you: IIS. As an application developer, you need to make IIS do something meaningful—generate well-formed HTML for the client. That's ATL Server's job.At a high level, ATL Server applications are divided into roughly two parts: a collection of DLLs (both ISAPI extensions and application extension DLLs) and HTML generation templates called Server Response Files (SRF files for short). ATL Server's architecture clearly separates the application presentation from the application logic. The basic content of your page (the presentation) is laid out in SRF files, and then ATL Server uses the application DLLs to replace well-marked HTML code (HTML with embedded code that ATL Server understands) in the HTML text within the SRF files. An SRF file includes HTML content interspersed with well-defined substitution tags. The SRF file is fed to the ISAPI extensions and ATL Server DLLs (the application logic), which make substitutions for the tags.From the C++ point of view, an ATL Server project comprises several DLLs: a single ISAPI extension DLL and one or more ATL Server application DLLs. The ISAPI extension DLL caches the loaded ATL Server DLLs and parsed SRF files. The ISAPI DLL also contains a thread pool for responding to client requests. The ATL Server application DLL has the smarts to parse the SRF files and replace SRF substitution tags with HTML.The ISAPI DLL is responsible for hooking up to IIS, and the application DLLs contain code to handle requests. In ATL Server, classes for handling requests derive from CRequestHandlerT and contain whatever methods you write for replacing SRF substitution tags with HTML. In many ways, ATL Server is much like the application wizards we looked at in Chapter 4.ATL Server handler classes contain a pair of dictionaries to associate request handler classes with request handler DLLs and to associate replacement methods with SRF tags. In addition to the replacement dictionaries, CRequestHandlerT contains methods and member variables for accessing standard Web application elements such as form variables, cookies, request streams, and response streams. Figure 30-7 shows how ATL Server processes a client. It shows several application DLLs and a single ISAPI extension DLL.

Figure 30-7: The ATL Server architecture.
Here's the path that a request follows through an ATL Server application:
The client requests an SRF file via HTTP (for example, http://www.consolidatedmessenger.com/default.srf).
IIS picks up the request and maps the extension to an ISAPI DLL. In the case of an ATL Server application, this is the ISAPI DLL for the application (which implements HttpExtensionProc).
The ISAPI DLL contains a thread pool and two caches: a DLL cache and a stencil cache. The DLL cache contains loaded ATL Server request handler DLLs, and the stencil cache contains loaded and token-parsed SRF files. The ISAPI DLL passes the request to a thread pool. (The caches will be used shortly.)
One of the worker threads from the pool handles the queued request by opening the SRF file to determine which application DLL should receive the request. (We'll look at the format of an SFR file in a moment.)
The worker thread expects to find a line in the SRF file indicating the application DLL to load. If the DLL isn't already loaded, the worker thread will load the DLL. The application will then pass the request to the default request handler class.
The SRF file is parsed into tokens (if necessary) and rendered into HTML. Each time the application encounters a token representing a substitution tag, it calls the corresponding replacement method in a handler class that resides in one of the application DLLs. The replacement method generates the output to the browser dynamically.
IIS sends the entire response to the client.
Now let's take a look at what those SRF files look like.
SRF Files
For the most part, SRF files are composed of HTML. The bulk of a response is usually the HTML contained in an SRF file. The SRF file also includes simple flow control tags such as if and while. SRF syntax also supports making method calls into C++ classes, and DLL function mappings.The SRF tags we're about to look at actually make method calls into your C++ classes. Naturally, the SRF file contains a list of the application DLLs it uses. These appear as handler tags within the SRF file.As an example, here's a simple SRF file that prints a greeting:
{{handler MyFirstApplication.dll/Default}}
<html>
<head>
</head>
<body>
{{HelloHandler}}<br>
</body>
</html>
Notice that the first line of the file names the application DLL (MyFirstApplication.dll) in which the handler map lives. (The handler map relates tags to specific C++ code that handle the tags.) The application DLL is used by the ISAPI extension to process the request. This line specifies the handler class and the DLL it lives in.The next line includes double curly braces, which identify server-side tags to interpret or replace. The tag, HelloHandler, names the request handler method to invoke. The output from the request handler named within the tag is injected into the HTML buffer. Here's where the separation between the user interface and the code happens. Web designers can modify the HTML surrounding the handler tag without touching the C++ code.
Multiple Application DLLs
If you think about it, having a one-to-one mapping between application DLLs and SRF files would not work very well in a dynamic environment. A single SRF file can be processed in any number of ways, depending on the capabilities of the server. To support this, ATL Server allows an SRF file to be served by more than one application DLL and by more than one handler. The SRF file will include one default DLL, and the other DLLs will be named. Named DLLs are given IDs within the request comment block. The SRF file uses the IDs within the replacement tags to specify which hander and which method to call.For example, the following code snippet shows an SRF file with two handlers:
{{handler HandlerOne.dll/Default}}
{{id=AlternateHandler handler=HandlerTwo.dll/OtherHandler}}
<html>
<body>
{{MainHandlerMethod}}<br>
{{AlternateHandler.AlternateMethod}}
</body>
</html>
This code specifies the default request handler class living in the HandlerOne.dll file and an alternate handler class named OtherHandler living within a second DLL named HandlerTwo.dll.A tag can include SRF keywords (for example, flow control keywords such as if and endif), which we'll look at in a bit. If the string within the tag is not an SRF keyword, it is passed to a handler DLL for replacement. Look carefully at the example. Replacement tags devoid of IDs are managed by the default handler. Tags with identifiers are handled by the specified DLL. HandlerOne.dll interprets the MainHandlerMethod tag by mapping it to the default request handler class, and it calls the replacement method associated with the MainHandlerMethod tag. HandlerTwo.dll interprets the AlternateHandler.AlternateMethod tag by mapping it to the request handler class named AlternateHandler, and it calls the replacement method associated with the AlternateMethod tag.
Tag Handlers
Tag handlers are member functions of a handler class that lives within an application DLL. The handler classes derive from CRequestHandlerT, as shown in the next code snippet. The class includes a replacement method map. Notice that the snippet also includes a handler map (at the DLL-level scope) that maps the class to a replacement string.
class CMainHandler : public CRequestHandlerT<CMainHandler>
{
public:
DWORD ValidateAndExchange();
DWORD OnMainHandlerMethod();
BEGIN_REPLACEMENT_METHOD_MAP(CMainHandler)
REPLACEMENT_METHOD_ENTRY("MainHandlerMethod",
OnMainHandlerMethod)
END_REPLACEMENT_METHOD_MAP()
};
BEGIN_HANDLER_MAP()
HANDLER_ENTRY("Default", CMainHandler)
// Other handlers within this DLL are mapped here.
END_HANDLER_MAP()
Each tag replacement method looks something like this:
HTTP_CODE OnMainHandlerMethod()
{
CWriteStreamHelper os(m_pStream);
os << "This text was generated by the application DLL." << endl;
return HTTP_SUCCESS;
}
The CWriteStreamHelper class encapsulates the output buffer that will eventually be sent to the browser. If you're an MFC developer, you're probably familiar with the << streaming syntax. The code simply inserts "This text was generated by the application DLL" into the stream of text going back to the browser.
Control Flow
SRF files include replacement tags like those we've just seen. They also include keywords for managing control flow.The if, else, and endif keywords support branching. For example, here's how to use branching within an SRF file:
{{handler MyFirstApplication.dll/Default}}
<html>
<head>
</head>
<body>
{{if IsUserRegistered}}
{{HelloHandler}}<br>
</body>
</html>
When the tag including if IsUserRegistered is encountered, execution flow ends up within a replacement method in MyFirstApplication.dll (presumably called OnIsUserRegistered). This method returns either HTTP_SUCCESS or HTTP_S_FALSE. For example, the replacement method for IsUserRegistered might look something like this:
// Member of the default handler class
HTTP_CODE OnIsUserRegistered()
{
if (LookupUserInDatabase())
return HTTP_SUCCESS;
else
return HTTP_S_FALSE;
}
This method simply controls the execution flow based on the state of the database.The while and endwhile keywords control looping. The while keyword uses a replacement method's return value as the conditional. For example:
{{handler MyFirstApplication.dll/Default}}
<html>
<head>
</head>
<body>
{{while CustomersInDatabase}}
{{ShowNextCustomerHandler}}<br>
{{endwhile}}
</body>
</html>
Include Files
Finally, SRF files can include other files (SRF and HTML files)—as sort of a form of reuse. The include keyword brings in another file. The include mechanism uses a URL to specify the path to the file. A great use for include files is for managing standard user interface elements such as headers and footers. For example, here's how you might use the include statement in an SRF file:
{{handler MyFirstApplication.dll/Default}}
{{include menu.srf}}
<html>
<head>
</head>
<body>
{{while CustomersInDatabase}}
{{ShowNextCustomerHandler}}<br>
{{endwhile}}
</body>
</html>
Notice that ATL Server is mostly declarative. There's only HTML and replacement tags. There are no script blocks, no calls to CreateObject, and generally no executable code in the page. It's all in the code behind the page.