ISAPI Extensions with Delphi (similar procedure in C-Builder)
original source : www.icss.net
for VC examples see also  :  www.genusa.com
 

Introduction

Certainly by now, most people in the software development business have had to at least contemplate developing web-based applications. With the rise of the Internet and the advent of corporate and enterprise based intranets, web development and the extension of web servers is becoming more and popular -- and more an more necessary. The ability to develop web-based applications is quickly becoming a high-demand skill. Fortunately for Delphi developers, they won't be left behind; rather, they will be out in front, as Delphi 3.0 makes it very easy to build powerful, fully-scalable web applications. 

By encapsulating the HTTP protocol -- the protocol used to transmit HTML pages and other data across the Internet -- and the Application Programming Interfaces (API) of the most popular web servers, Delphi provides the programmer with the tools to build such applications using the full power and rapid application development capabilities of Delphi. 

The Web

Background

History

The use of and access to the web has grown phenomenally over the past two years. And along with that, the sophistication of the web has increased as well. With its origins at the NCSA, it's first users did little more than post static HTML pages for others to access. This afforded only limited access to information as there was no interaction between the user and the data, and the user could only access data specifically made available by the page author. Soon, however, the web allowed for direct downloading of files and data via the browser, and information became a bit more accessible. The addition of Common Gateway Interface (CGI) scripting allowed web authors to interact with users to gather information and provide tailored content. Recently, this has been taken one step further by web servers that provide their own Application Programming Interface, greatly enhancing the ability of Web authors to provide dynamic Web pages and data to end users. 

Changes

In addition to technological changes, the character of the Web and the Internet changed. Originally the bastion of government and academia, the Web and the Internet used to be esoteric, fast, and 'free'. A few years ago, however, many commercial enterprises began to see the benefits and opportunities created by the Internet. New browsers, tools, and protocols, in addition to the proliferation of personal computers were leading many users to the World-wide Web, and business saw this as an opportunity to reach customers. There was an initial culture clash as the Internet community, highly attuned to the non-commercialism of the web were angered by what they saw as the sullying of the purity of the Internet. 

Though such feelings ran deep, the economic potential of the Web was too overwhelming. Whereas just a few short years ago there was almost no corporate presence on the Web, now virtually every major corporations, and even many mid-sized and small businesses have a Web presence. Television commercials feature web addresses, and Web-based companies became the darling of Wall Street. While jokes about ways to make money abound and concerns over security of conducting transactions over the Internet persist, it seems inevitable that online commerce will be a large part of the future of many businesses. 

Intranets

Selling products over the Web was certainly seductive, but it soon became clear that Web technology had more to offer than just slick marketing to external customers. The same tools that were making dynamic web authoring possible could be used to build internal networks -- intranets. For many years, coordinating the efforts of distant workgroups and sharing large volumes of corporate data and information was a difficulty for many organizations. A good medium for presenting and delivering the information remained elusive. Getting that information to many people across many departments and locations proved costly and difficult.

The Web has provided an answer to this dilemma for many organizations. By putting data and information online and making it accessible only to internal users, companies have been rapidly realizing great advantages and benefits with Web technology. The current technology allows for advanced data access and queries using scalable, multi-tiered architectures. Data that was formerly locked away in mainframes and corporate databases was made available to sales representatives at customer sites and hotel rooms around the world. Users could access documents, corporate policies, up to the minute sales figures or inventory information, all from a single browser using any platform or operating system. In addition, many organizations have been seeing large returns on their investments, and intranets lower costs and make them more competitive and efficient. Intranets may even change the way computers are bought, as strong servers and powerful browsers are allowing 'thin-client' network computers to become viable. 

Web Development

All of this, of course, has made the development of web applications a key factor in the success of many organizations and corporations. The market for tools to develop such applications is immature but growing rapidly. Early applications were done mainly on UNIX servers using scripting tools such as PERL. The methods for implementing such applications were somewhat crude and unwieldy. The rise of Java as a web programming language has changed much of that. In addition, the appearance of Windows-based Web servers have given Windows programmers an opportunity to leverage their skills in the web development world. No matter what the language or tool used, however, all Web-based applications have to have knowledge of and implement the basic tools of the World-wide Web. 

The HTTP Protocol

The basic technology that make the Web possible is deceptively simple. The two agents in the process, the web client and the web server must establish a communications link and pass information between themselves. The client requests information and the server provides it. Of course, the client and the server have to agree on how to communicate and what the form of the information that they share will take. They do this across the web with nothing more than a ASCII byte stream. The client sends a text request and gets a text answer back. The web client knows little about what takes place on the server, and vice-versa, allowing for cross-platform communication, normally via the TCP/IP protocol. 
 
 

The standard method of communicating used on the Web is the Hyper-text Transfer Protocol (HTTP). A protocol is simply an agreement about a way of doing business, and HTTP is a protocol designed to pass information from the client to the server in the form of a request, and from the server to the client in the form of a response. It does so by formatting information as a byte stream of ASCII characters and sending it between the two agents. The protocol itself is quite simple, yet is both flexible and powerful. When used in concert with the Hyper-text Markup Language, it can quickly and easily provide web pages to a browser. 

An HTTP request might look like this: 
 
 

 GET /mysite/webapp.dll/dataquery?name=CharlieTuna&company=Borland
HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/3.0b4Gold (WinNT; I)
Host: www.mysite.com:1024
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*
 

HTTP is stateless, which means that the server has no knowledge of the state of the client and the communication between the server and the client ends once the request has been satisfied. This makes creating database applications using HTTP somewhat problematical, as many database applications rely on the client having access to a live dataset. State information can be stored through the use of 'cookies', or pieces of information that are stored on the client as a result of the HTTP response. 

As you might imagine, there is a wealth of information about the HTTP protocol on the web. Look to this bookmark at the end of the paper for a collection of interesting sites. 

ISAPI, NSAPI and CGI

There are two main ways to provide dynamic, interactive web sites: the Common Gateway Interface (CGI) and Web server extension libraries. CGI was the first to be implemented and is probably the most commonly used server extension currently used. It is implemented by the server. A request comes in for CGI servers, usually with parameters that pass data to be used for customizing the Web page. The server would then launch the requested application as a separate process, passing the parameters by creating a file with their information in it. The application would run, processing the passed data, and write out another file with the HTML code to be returned to the client. The server would pass this page back to the client, and the CGI application would shut down. 

Inefficient

Though effective, CGI is not efficient. The CGI application has to launch, execute, and shut itself down for every request that the server receives. To pass information back and forth, between itself and the server, it has to read and write files. The application is run as a completely separate and independent process. This means that if there are ten requests, then ten instances of the CGI application are run, possibly all at the same time. This is not efficient, eats up resources, and can lead to slow response times to client requests. 

DLL Extensions 

Realizing these limitations, the two main server companies, Netscape and Microsoft, along with some of the smaller Web server builders came up with a better solution. Instead of launching a completely separate process, the Web server could take advantage of Windows' ability to load dynamic link libraries (DLLs) and extend the Web server through an API and a DLL extension. This way, dynamic web pages would be created all in the same address space, a single instance of the extension could be loaded, and parameters and responses could be passed back and forth directly rather than through the cumbersome process of file writing. 

Different Approaches

 The two major Web server providers, Microsoft and Netscape, took very different approaches to writing the APIs for their products. Microsoft took the simple route, providing only three necessary API calls, one of which does all the real work. They called their API the Internet Server API, or ISAPI. It is implemented in their Web server, Internet Information Server. They took the view that the HTTP process is very simple, and that all a server extension had to do was take a request, process it, and provide HTML in return; hence, the very simple API. They assume that the DLL itself will do the majority of the work and process the passed data. You can find out more information about how ISAPI is implemented, along with code to directly access it via Delphi at http://members.aol.com/charliecal/internet.htm

On the other hand, Netscape's server API, NSAPI, has over 300 routines, allowing you much finer control over the server itself than ISAPI provides. NSAPI is implemented in various Netscape server products. It is much more granular, providing far greater services, and complexity, to the server extension programmer. (See this section below for information on how to get your Netscape server to run ISAPI DLL's) 

The Delphi Way to the Internet

RAD on the Internet

 As it does with most of Windows programming, Delphi brings rapid application development to the building of Web server extensions. Because Delphi provides you with complete control over your application, Delphi's designers decided to implement the Microsoft approach to Web server extensions. However, in doing so, they used the power of object-oriented programming to allow the same DLL to be used by both IIS and NSAPI based servers. They made the paradigm simple -- make a request and get a response, but allowed you to use that simple paradigm to access both server types. As you would expect, the programmer is spared the details of the APIs themselves, and provided with a clean, object-oriented interface to the server. 

TWebRequest/TWebResponse

The heart of Delphi's Web Server extension implementation is the TWebRequest and TWebResponse objects. These objects encapsulate the HTTP protocol specification, making it easy to create an HTTP request and get information from the resulting HTTP response. The declarations for these objects can be found in the HTTPApp unit that is part of Delphi 3.0. TWebRequest consists mainly of a series of string properties that provide access to the data within the HTTP request. The class also does a lot of parsing work for you by providing TStrings access to the parameter values, such as query parameters, that are passed by the request. This allows you to assign the values to a TStringList , for example. All of the string properties that correspond to HTTP header fields are read only. They are set by the internal workings of the TWebRequest class, and cannot be changed. The key properties are the ContentFields and QueryFields properties, which can contain lists of data passed to the server as parameters. 

The TWebResponse object is similar to the TWebRequest class; however, rather that providing you with information, it is up to you to provide the class with information to be sent back to the server. It provides a list of properties that correspond to the HTTP response data fields. TWebResponse provides a number of functions that allow you to respond directly to the request by diverting it, sending cookie information, or a stream of data. At a minimum, you need to at least provide some text back to the Content property. Normally, this will be the HTML that will define the page to be displayed. 

TWebModule/TWebDispatcher

 If you select File|New|Web Server Application and then select the ISAPI/NSAPI option, Delphi will open up a new library project with an empty TWebModule as the DLL's main form. 

The module will be empty, but because it is a descendant of TDataModule, you can place database components, etc., in it just as you would a regular TDataModule . These components can then be used to generate the HTML for your response. The TWebModule has built into it a TWebDispatcher component, which catches all the HTTP requests destined for the server extension, wraps up the request, and passes it on to the proper Action

    Note: You can create a Web Server application with one of your existing Datamodules. If you open the DataModule, you can drop a TWebDispatcher component on it. The TWebDispatcher component on the Internet page of the Component Palette will add all of the functionality encapsulated in the TWebModule. So if you have all of your business rules wrapped up in an existing TDataModule , making those rules available to your Web applications is as easy as point and click 
Looking at the Object Inspector with the TWebModule selected, you will see the Actions property, which is the hub of activity for a Delphi based Web server application. By defining Actions, and using OnAction event attached to each action, you can provide dynamic HTML pages in response to the client request. Double clicking on the edit box next to the Actions property or pressing the ellipse button will bring up the Actions property editor. You can then provide PathInfo for the action, which will define how the server application will be called. 

If you select the Events page in the Object Inspector, and double click the OnAction event, you will get an event handler that looks like this: 
 
 

procedure TWebModule1.WebModule1Actions0Action(Sender: TObject; 
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin

end

Inside this event handler is where all the work of creating the HTML takes place. Within this event handler, you can call any Delphi code at all -- database code, Windows API calls, create and destroy objects, etc. As we'll see below, Delphi provides a number of components to help create and format HTML code, including basic HTML page layout, the customizing of existing pages for dynamic output, and table creation based on datasets and queries. In the OnAction event handler, notice the two most important parameters, Request and Response. The Request parameter is a TWebRequest object that holds all the information about the HTTP request that initiated the OnAction event. The Response parameter is a TWebResponseobject waiting to be filled out and returned. The Sender parameter is the TWebModule that owns the Action. The Handled property is a boolean var parameter that indicates whether or not this OnAction event handler actually decided to provide a response. This default is to True, but set it to False if you decide not to handle this request and pass it on to the default event handler instead. 

In order to produce a page in response to the passed request, you must at the very least provide a string back to the Response.Content property. This is a string value that will be passed back to the client and displayed by the browser. It is up to you to provide the all the HTML code needed within this property. Delphi has a number of components to help out in building and managing the HTML for you . 

In addition, however, you can respond in a number of different ways. You can redirect the request to an entirely different page with the Response.SendRedirect method. You can place a cookie on a user's machine with the SetCookieField method, or you can directly send a stream of data with the SendStream method. You can use theWWWAuthenticatemethod to get a user name and password before passing on further data. Most often, however, you will be using the Content property to send HTML 

TPageProducer

 Much of the time, you will be producing dynamic pages that are similar, but customized to specific inputs. Delphi provides the TPageProducer component that makes it easy to create a template HTML page and substitute key pieces of information within that template. You may have a standard response to an order submission and want to customize it with the users name, or you may have a page that is always the same except for the data in a table. TPageProducer allows you to define your own unique tags, which are then replaced with the custom HTML code, information, or text of your choosing. 

TPageProducer components are normally placed on the server extension's TWebModule , although they certainly can be created at runtime. HTML code can be entered into the TPageProducer two ways, either as a string via the HTMLDoc property, or as the filename of an existing HTML document in the HTMLFile property. 

The TPageProducer has one event, OnHTMLTag, and it occurs whenever the TPageProducer.Content method is called. The Content method parses the HTML code assigned via the HTMLDoc or HTMLFile properties and fires the OnHTMLTag event handler once for each user-defined tag (text with the '<' and '>' pair) that is found. The event is declared as follows: 

procedure TWebModule1.PageProducer1HTMLTag(Sender: TObject; Tag:
TTag; const TagString: String ; TagParams: TStrings; var ReplaceText:
String);
begin

end;

Within the OnHTMLTag event, the TagString parameter will contain the name of the tag, and the TagParams parameter will be a TStrings descendant holding all the parameters passed with the tag, each parameter in a single TStrings.Items item. You can use all of this information to create a value for the var parameter ReplaceText. The value returned in ReplaceText will be used to replace the entire tag itself within the HTML of TPageProducer. This enables you to create customized web pages based on existing files or HTML created on the fly by your code. 

The Tag parameter is of type TTag. Delphi defines a number of user-defined tag values for you. Allowing you to break out various special tags for special purposes and will pass one of the following values: 

    tgCustom

    A user defined or unidentified tag

    tgLink

    This tag should be converted to a link (<A>..</A>)

    tgImage

    This tag should be converted to an Image tag (<IMG SRC=...>)

    tgTable

    This tag should be replaced with an HTML table (<TABLE>..</TABLE>)

    tgImageMap

    This tag should be replaced with an Image Map. An Image Map defines links based on hot zones within an image.

    tgObject

    This tag should be replaced with code that calls an ActiveX control

    tgEmbed

    This tag should be converted to a tag that refers to a Netscape -compliant add-in DLL

    A user-defined tag will normally take on the following format: 

    <#UserTag Param1=SomeValue Param2=SomeValue>

     For example, a tag that should be converted into a link might look like this: 

    <#LINK LinkName=CompanyName>

    The resulting OnHTMLTag event might react to the tgTableTag value and then use the CompanyName parameter to look up the actual link based on an entry in a database table. 
     
     

    TDataSetTableProducer/TQueryTableProducer

    The TDataSetTableProducer and the TQueryTableProducer components enable you to generate HTML tables from data accessed through Delphi's database components. Both components descend from the TDSTableProducer class and perform almost identically. Indeed, their function at design time will be identical as long as the TQuery attached to TQueryTableProducer contains a valid SELECT call in its SQL property. These table generators are of course limited by the capabilities of the HTML specification, but they can nevertheless provide total control over the appearance of an HTML table. 

    At design time, the TDSTableProducer allows you complete control over its appearance. Attach a dataset to the component with the DataSet property. You can set its caption's text and alignment with the Caption and CaptionAlignment properties, respectively. The Header and Footer properties allow you to add HTML code before and after the table. At runtime you might use other content producers to create HTML code to put around the table in order to embed the table in a complete web page. The RowAttributes property allows you to set the properties of the tables rows. 

    TQueryableProducer's Component Editor

    TDSTableProducers have a very powerful Component Editor. Double-clicking on the component itself brings it up and gives you a large amount of control over the appearance of the table. You can completely customize the appearance of the table by adding columns and changing those columns appearance. It even will display your settings in an HTML viewer as you make them. If you set the controls dataset to Active, you can see the table with live data at design time. You set general table properties with the controls in the upper left of the Editor. For example, in order to make a border appear, set the Border property to a positive number. You can select which columns from the dataset to include in the table. In addition, you can set any of the HTML tags that are available to HTML tables in the Component Editor. 

    Use the Add button to add a THTMLTableColumn item to the Component Editor. Selecting it will then allow you to edit its properties in the Object Inspector. You can assign it to a specific column from the dataset in the FieldName property and then modify its appearance. The Title property will determine appearance of the header for the selected column. You can set the other properties to determine the properties for the column from the Object Inspector. 

      Note: Column property settings take precedence over Row property settings, and properties set at run time take precedence over those set at design time. 
    At runtime, using the TDSTableProducer is very straight forward. In order to cause it to produce an HTML table, refer to its Content property, which will return a string with the HTML code for the table in it. You can add content around the table by adding HTML code to its Headerand Footer properties. You can use the OnCreateContent event to control whether the table should be produced. If there are reasons why the table cannot be displayed, then this event can set the continue property to False, and the Table will not be generated. The OnFormatCell event allows you to customize how each cell is formatted. You can use the CellData string parameter to check the value of the data, and set the cell's appearance as desired. For example, if a numeric value were below a certain level, you could highlight that cell with a red background. The CellRow and CellColumn parameters will allow you to format specific cells, rows or columns if desired. 

    Finally, almost all of the classes that are properties of TDSTableProducer have a Custom property. This property can be used to add custom parameters to any of the given entities in the table. For instance, Internet Explorer 3.01 from Microsoft adds some extensions to the HTML table such as BorderColor. Setting the Custom property of the TableAttributes property to 'BORDERCOLOR="RED"' would add that parameter to the <TABLE> tag, and display a red border to the table if it were viewed in Internet Explorer. If the HTML specification expands to include new cell formatting properties, they could be added here as well. 

    Examples

     The following section show a number of examples which illustrate the techniques described above. Each example contains the pertinent selections of code from the accompanying source and examples. Included with each example is an accompanying HTML page that illustrates how to call the resulting Web server extension. The following table contains an index to the project names and links to the explanation of each example. 

    Topic

    Code Directory

    Project Name

    Dynamic HTML Creation

    /

    sample1.dpr

    HTML from an Existing File

    /sample2

    sample2.dpr

    HTTP Request Example

    /sample3

    sample3.dpr

    Simple Table Creation

    /sample4

    sample4.dpr

    Guest Book Application

    /sample5

    sample5.dpr

    Web Advertising Example

    /sample6

    sample6.dpr

    Cookie Example

    /sample7

    sample7.dpr

    Basic Web Page Creation

    These examples illustrate basic principles of Web Server application creation. They use the TWebModule and it's Actions property to respond to an HTTP request, and the TPageProducer component to customize HTML text to provide a web page for the response to that request. 

    Dynamic HTML Creation 

    The TPageProducer can be used to very easily create a simple dynamic HTML page. The code below demonstrates how this might be done. The WebModule's OnAction event simply builds a very straight forward web page inside of an instance of TStringList, and passes that to the TPageProducer. HTMLDoc property. The page includes a user-defined tag to replace <#BDCCity> with 'Nashville' in the OnHTMLTag event of the TPageProducer component. The result is a very simple page that has the correct city placed within it. 

    The Action that occurs for the below code has a PathInfo property of /dynamic. It gets called when a link like the following is called in an HTML page: 

    <A HREF="sample1.dll/dynamic">Click Here!</A>

     This is the code from unit1.pas of the first sample that does all of the work: 

    procedure TWebModule1.WebModule1Actions0Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled: 
         Boolean);
    var
       Page: TStringList;
    begin
       Page := TStringList.Create;
       try
         with Page do
         begin
              Add('<HTML>');
              Add('<HEAD>');
              Add('<TITLE>Web Server Extensions Sample One</TITLE>');
              Add('</HEAD>');
              Add('<BODY>');
              Add('<B>This page was created on the fly by Delphi 
                            3.0</B><P>');
              Add('<HR>');
              Add('Are we having fun here in <#BDCCity>?');
              Add('</BODY>');
              Add('</HTML>');
         end;
              PageProducer1.HTMLDoc := Page;          Response.Content := PageProducer1.Content;
       finally
         Page.Free;
       end;
       Handled := True;
    end;
     

    procedure TWebModule1.PageProducer1HTMLTag(Sender: TObject; Tag:
    TTag; const TagString: String; TagParams: TStrings; var ReplaceText:
    String);
    begin
         case Tag of
             tgCustom: if CompareStr(TagString, 'BDCCity') = 0 then 
                   ReplaceText := 'Nashville';
         end;
    end;

     This technique would not likely be used for large pages, but might be used for small, simple pages. However, it illustrates the basics of responding to an HTTP request and using the TPageProducer components to generate customized HTML. 

    This example also shows the purpose of the Action.Default property. If this property is set to True, the action becomes the default action for the Web server application. Only one action within the TWebModule can be the default, and setting this property to True sets all others to False. This Action will be fired if no other action is able to handle the request. The OnAction event might return a page with an explanation and an error message. The code for the default action of this application looks like this: 

    procedure TWebModule1.WebModule1Actions1Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled:
    Boolean);
    var
       Page: TStringList;
    begin
       Page := TStringList.Create;
       try
         with Page do
         begin
              Add('<HTML>');
              Add('<HEAD>');
              Add('<TITLE>Web Server Extensions Default Page</TITLE>');
              Add('</HEAD>');
              Add('<BODY>');
              Add('This page is the default page and appears when a bad 
                       link is called.</B><P>');
              Add('</BODY>');
              Add('</HTML>');
         end;
              PageProducer1.HTMLDoc := Page;
              Response.Content := PageProducer1.Content;
       finally
         Page.Free;
       end;
       Handled := True;
    end;

    So if a bad link is called, i.e. one that doesn't exist, this OnAction event will be called. The default Action will also be called if all existing OnAction events in the application fail to provide a response and set their Handled parameters to False

    HTML From an Existing File

    This next example is very similar to the first, but instead of creating the HTML on the fly, the TPageProducer uses an existing HTM file to establish the basis for the response. The TPageProducer.HTMLFile property is set to the specified file, and then the OnHTMLTagevent occurs to replace a user-defined tag with the full path of the module name of the application. 

    In order to assure that the file can be found on any system that might run the app, a call is made to the Windows API function GetModuleFileName , which fills the specified buffer with the path to the calling module. This call is made within the little known SetString procedure which makes a string out of a PChar variable. The result is used to set the TPageProducer.HTMLFile property: 

    procedure TWebModule1.WebModule1Create(Sender: TObject);
    var
      Path: array[0..MAX_PATH - 1] of Char;
      PathStr: string; 
    begin
      SetString(PathStr, Path, GetModuleFileName(HInstance, Path, 
           SizeOf(Path)));
      PageProducer1.HTMLFile := ExtractFilePath(PathStr) + 'appname.htm';
    end;

    Once the HTMLFile property has been set, the code is almost identical to the previous example: 

    procedure TWebModule1.WebModule1Actions0Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled:
    Boolean);
    begin
         {HTMLFile property was set in OnCreate of the datamodule}
         Response.Content := PageProducer1.Content;
         Handled := True;
    end;
     

    procedure TWebModule1.PageProducer1HTMLTag(Sender: TObject; Tag:
    TTag;
      const TagString: String; TagParams: TStrings; var ReplaceText:
    String);
    var  Path: array[0..MAX_PATH - 1] of Char;
      PathStr: string;
    begin
         case Tag of
           tgCustom:
             begin
               if CompareStr(TagString, 'AppName') = 0 then
               begin
                    SetString(PathStr, Path, GetModuleFileName(HInstance, 
                                    Path, SizeOf(Path)));
                    ReplaceText := PathStr;
               end;
             end;
         end;
    end;

    HTTP Request Example

     This next example takes a look at a typical HTTP request by displaying in a web page all the properties of the THTTPRequest class. It is a straightforward coding example and doesn't display any new techniques. The benefit is in running the app and looking at the results in the web browser. This example will display all of the properties of the TISAPIHTTPRequest that was passed when the page itself was requested. Many of the fields are left blank with a simple request like this one, but the structure of an HTTP request can be seen. The code used to create the HTTP listing follows: 

    procedure TWebModule1.WebModule1Actions0Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled:
    Boolean);
    var
       Page: TStringList;
    begin
       Page := TStringList.Create;
       try
         with Page do
         begin
              Add('<HTML>');
              Add('<HEAD>');
              Add('<TITLE>Web Server Extensions THTTPRequest 
                            Demo</TITLE>');
              Add('</HEAD>');
              Add('<BODY>');

              Add('<H3><FONT="RED">This page displays the properties of 
                     the HTTP request that asked for it.</FONT></H3>');
              Add('<P>');

              Add('Method = ' + Request.Method + '<BR>');
              Add('ProtocolVersion = ' + Request.ProtocolVersion + 
                         '<BR>');
              Add('URL = ' + Request.URL + '<BR>');
              Add('Query = ' + Request.Query + '<BR>');
              Add('PathInfo = ' + Request.PathInfo + '<BR>');          Add('PathTranslated = ' + Request.PathTranslated + '<BR>');
              Add('Authorization = ' + Request.Authorization + '<BR>');
              Add('CacheControl = ' + Request.CacheControl + '<BR>');
              Add('Cookie = ' + Request.Cookie + '<BR>');
              Add('Date = ' + FormatDateTime ('mmm dd, yyyy hh:mm', 
                          Request.Date) + '<BR>');
              Add('Accept = ' + Request.Accept + '<BR>');
              Add('From = ' + Request.From + '<BR>');
              Add('Host = ' + Request.Host + '<BR>');
              Add('IfModifiedSince = ' + FormatDateTime ('mmm dd, yyyy 
                          hh:mm', Request.IfModifiedSince) + '<BR>');
              Add('Referer = ' + Request.Referer + '<BR>');
              Add('UserAgent = ' + Request.UserAgent + '<BR>');
              Add('ContentEncoding = ' + Request.ContentEncoding + 
                          '<BR>');
              Add('ContentType = ' + Request.ContentType + '<BR>');
              Add('ContentLength = ' + IntToStr(Request.ContentLength) + 
                          '<BR>');
              Add('ContentVersion = ' + Request.ContentVersion + '<BR>');
              Add('Connection = ' + Request.Connection + '<BR>');
              Add('DerivedFrom = ' + Request.DerivedFrom + '<BR>'); 
              Add('Expires = ' + FormatDateTime ('mmm dd, yyyy hh:mm', 
                          Request.Expires) + '<BR>');
              Add('Title = ' + Request.Title + '<BR>');
              Add('RemoteAddr = ' + Request.RemoteAddr + '<BR>');
              Add('RemoteHost = ' + Request.RemoteHost + '<BR>');
              Add('ScriptName = ' + Request.ScriptName + '<BR>');
              Add('ServerPort = ' + IntToStr(Request.ServerPort) + 
                           '<BR>');

              Add('</BODY>');
              Add('</HTML>');
         end;
           PageProducer1.HTMLDoc := Page;
           Response.Content := PageProducer1.Content;
       finally
         Page.Free;
       end;
      Handled := True;
    end;

    Database Access and Table Creation

    These examples provide a basic look at the TTableDataSetProducer and TQueryTableProducer components and how they can be used to create customized HTML-based tables for a web page. The examples in this section are rather straightforward. The examples in the next section build on the principles show here by combining database techniques with tables and HTML output to bring the full power of Delphi to bear on building dynamic web sites. 

    One thing to note when using database tools inside a Web server application -- there always needs to be a TSession component on the TWebModule if you are going to be using any of the database components from the component palette. Each request to the application from the web server launches a separate thread within the application instance in order to run each request. If you set the AutoSessionName property of the TSession component to True, each session will be given a unique name, ensuring that each thread has its own database session to run in. This way, there won't be any conflict or errors when multiple clients request the same action. 

    Simple Table Creation

     The creation of an HTML table using the TDataSetTableProducer component is surprisingly easy. All it requires is a TTable component in addition to the TDatasetTableProducer component itself. By setting the Dataset property to the TTable, and by setting the TTable.DatabaseName and TTable.TableName properties, you can easily hook the TTable into the TDataSetTableProducer and use it to produce an HTML table. The code below illustrates all that it takes to make the table appear: 

    procedure TWebModule1.WebModule1Actions0Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled:
    Boolean);
    begin
         Response.Content := DataSetTableProducer1.Content;
         Handled := True;
    end;

    Guest Book Application

    One of the more popular features on web surfer's personal web pages is a Guest Book. Folks seem to like leaving messages on other people's homepages, and to read what users have to say about their own pages. A guestbook is really a simple database, and hence a perfect example of building an ISAPI web extension using Delphi's database tools. 

    A basic guestbook application consists of two things -- an HTML form to gather information and an HTML page to display what the guestbook has to say. Delphi's tools don't provide a way to build an HTML form, but there are numerous tools out there that can build an HTML-based form for your guestbook. (Actually, this example borrows the guestbook form found in the Intrabuilder demonstration programs. It is included with the source code for this paper. Intrabuilder is a pretty good tool for building such forms. You can build a form in Intrabuilder, display it in a web browser and then steal the resulting HTML.) 

    To use this guestbook application, you will need a BDE alias named GBData which points to the directory holding the database files included with this demo. 

    In setting up the form to use your ISAPI extension, you need to set the action parameter to the application DLL. In this example, the guestbook entry page has the following line of HTML code to provide an action for the "Submit" button: 

    <form method="post" action="sample5.dll/form" >

    This causes the form's data to be passed to the /form action of the SAMPLE5.DLL application, along with the form's data. 

    After a user displays and fills out the guestbook form, the first thing that your application must do is gather up the relevant data. Delphi does this automatically for you, making the content passed back from the form into an easily accessible property -- TWebRequest.ContentFields. This parameter is passed to the OnAction event and accessible via the Request parameter. 

    This code below takes the data in the Request.ContentFields parameter, assigns it to a TParamsList class instance and then assigns those values to a database file. 

    Note: The TParamsList class is part of the WebUtils unit included with the paper's code. It is a class that automatically parses out parameters from a TStrings descendent and allows you to index them by the parameter's name. For instance, TWebResponse gathers all the cookies passed in an HTTP response and places them in the CookieFields property, which is a TStrings descendant. The Cookies are in the form CookieName= CookieValue. TParamsList takes these values, parses them, and indexes them by the parameter name. Thus, the preceding parameter could be accessed with MyParams['CookieName'], which would return CookieValue.

    The OnAction event handler below then goes on the create a simple HTML page thanking the user by name for submitting an entry into the guestbook. 

    procedure TWebModule1.WebModule1Actions0Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled:
    Boolean);
    var
       MyPage: TStringList;
       ParamsList: TParamsList;
    begin
         begin
              ParamsList := TParamsList.Create;
              try try
                ParamsList.AddParameters(Request.ContentFields);
                GBTable.Open;
                GBTable.Append;
                GBTable.FieldByName('Name').Value :=
    ParamsList['fullnameText'];
                GBTable.FieldByName('EMail').Value :=
    ParamsList['emailText'];
                GBTable.FieldByName('WhereFrom').Value :=
    ParamsList['wherefromText'];
                GBTable.FieldByName('Comments').Value :=
    ParamsList['commentsTextArea'];
                GBTable.FieldByName('FirstTime').Value :=
    (CompareStr(ParamsList['firstVisitCheck'], 'on') = 0);
                GBTable.FieldByName('DateTime').Value := Now;
                GBTable.Post;
              except
                   Response.Content := 'An Error occurred in processing
    your data.';               Handled := True;
              end;
              finally
                ParamsList.Free;
                GBTable.Close;
              end;
         end;

      MyPage := TStringList.Create;
      ParamsList := TParamsList.Create;
         try
           with MyPage do
           begin
             Add('<HTML>');
             Add('<HEAD><TITLE>Guest Book Demo Page</TITLE></HEAD>');
             Add('<BODY>');
             Add('<H2>Delphi 3.0 Guest Book Demo</H2><HR>');

             ParamsList.AddParameters(Request.ContentFields);
             Add('<H3>Hello <FONT COLOR="RED">'+
    ParamsList['fullnameText'] +'</FONT> from
    '+ParamsList['wherefromText']+'!</H3><P>');
             Add('Thanks for visiting my homepage and making an entry
    into my Guestbook.<P>');
             Add('If we need to e-mail you, we will use this address --
    <B>'+ParamsList['emailText']+'</B>');
             Add('<HR></BODY>');
             Add('</HTML>');
           end;
         PageProducer1.HtmlDoc := MyPage;
         finally
           MyPage.Free;
           ParamsList.Free;
         end;

         Response.Content := PageProducer1.Content;
         Handled := True;
    end;

     The next task would be to allow the user to view entries in a guest book. This is done in a rather straightforward manner. A separate OnAction event reads through the data in the database, creating HTML code for each entry. The procedure below, for example, creates a single entry in the guestbook. 

    procedure TWebModule1.DoGuestBookEntries(Page: TStrings; Comments:
    String; Name: String; eMail: String;
                                               WhereFrom: String; When: 
    TDateTime; FirstTime: Boolean);
    begin
          Page.Add('<B>' + Comments +'</B><BR>');
          Page.Add(Name + '  (<A HREF="mailto:' + eMail + '">'+ email
    +'</A>)<BR>');
          Page.Add(WhereFrom + '<BR>');
          Page.Add(FormatDateTime('dd mmmm yyyy hh:mm:ss', When));
          if FirstTime then
          begin
            Page.Add('<FONT COLOR="RED">(First Time)</FONT><BR>');
          end;
          Page.Add('<HR>');
    end; 

    This OnAction handler then loops through all of the database entries, calling DoGuestBookEntries for each entry found, returning the resuting HTML code as the response. 

    procedure TWebModule1.WebModule1Actions1Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled:
    Boolean);
    var
      Page: TStringList;
    begin
      Page := TStringList.Create;
      try
             Page.Add('<HTML>');
             Page.Add('<HEAD><TITLE>Guest Book Entries</TITLE></HEAD>');
             Page.Add('<BODY>');
             Page.Add('<H2>Delphi 3.0 Internet Tools Guest Book
    Demo</H2><HR>');
             Page.Add('<B><FONT COLOR="BLUE">These are the entries to my
    Guest Book:</FONT></B><HR>');

               try
                 GBTable.Open;
                 while not GBTable.EOF do
                 begin
                   DoGuestBookEntries(Page,

    GBTable.FieldByName('Comments').AsString,

    GBTable.FieldByName('Name').AsString,

    GBTable.FieldByName('email').AsString,

    GBTable.FieldByName('WhereFrom').AsString,

    GBTable.FieldByName('DateTime').AsDateTime,

    GBTable.FieldByName('FirstTime').AsBoolean
                                      );
                   GBTable.Next;
                 end;
               finally
                 GBTable.Close;
               end;
             Page.Add('<HR></BODY>');
             Page.Add('</HTML>');
        PageProducer2.HtmlDoc := Page;
      finally
        Page.Free;
      end;
      Response.Content := PageProducer2.Content;
      Handled := True;
    end;
     

    Special Techniques

    Web Advertising Example (Redirection)

     Most commercial sites have some form of web advertising. This usually consists of an eye-catching graphic at the top of a page that, when clicked on, links to the advertiser's page. You might notice, too, that the same page will often have different advertisers on it, and that the links often do not link directly to the advertiser's site, but pass through a site on the advertising entity's site before going on. Web pages that have advertising often like to gather information about which ads are selected, which graphics were chosen, and how many 'hits' a particular advertiser is receiving. Such data might be used to charge advertisers based on 'click-through' rates. 

    ISAPI web server extensions make such data-gathering and web site selection easy, and Delphi's powerful database tools make storing such data a breeze. The example below illustrates a rather straight-forward and simple approach to this problem. It uses a database of companies to randomly select a company's advertising banner whenever the page is opened. It retrieves the banner, displays it in a dynamic web page, and makes it a link to another OnAction event within the DLL. The link will pass the address of the company, the image that was selected, and a reference to the company in a database that tracks the total number of times a particular company is selected. 

    This OnHTMLTag procedure replaces the <#AdGoesHere> tag inside an existing HTML file with an image that is a link to the proper company. The link itseld contains parameters to tell the called DLL which company and which graphic was selected by the user. The SpacesToPluses function is in the WebUtils unit. It converts any spaces inside parameters to plus signs ('+'). HTTP does not allow spaces to be passed in its parameters, so plus signs are generally place where spaces are. 

    procedure TWebModule1.PageProducer1HTMLTag(Sender: TObject; Tag:
    TTag;
      const TagString: String; TagParams: TStrings; var ReplaceText:
    String);
    var
       Param: String;
       Total: Integer;
    begin
         case Tag of
           tgCustom: begin
                        if CompareStr(TagString, 'AdGoesHere') = 0 then
                        begin
                             Total := GetRandomCompany;
                             with AdInfoTable do
                             begin                           Open; 
                               First;
                               MoveBy(Total);
                               try
                                 Param := '<#Ad ';
                                 Param := Param + 'Company=' +
    SpacesToPluses(FieldByName('CompanyName').AsString);
                                 Param := Param + ' Link=' + 
    FieldByName('Link').AsString; 
                                 Param := Param + ' Image=' +
    FieldByName('GIFFile').AsString;
                                 Param := Param + ' ID=' + 
    FieldByName('CompID').AsString;
                                 Param := Param + '>'; 
                               finally
                                 Close;
                               end;
                               ReplaceText := Param;
                             end;
                        end;
                     end;
           tgImage: ;
         end;
    end;

    procedure TWebModule1.PageProducer2HTMLTag(Sender: TObject; Tag:
    TTag;
      const TagString: String; TagParams: TStrings; var ReplaceText:
    String);
    var
      Params: TParamsList;
      Link: String;
    begin
      Params := TParamsList.Create;
      try
         Params.AddParameters(TagParams);
         case Tag of
           tgCustom: begin
                       if CompareStr(TagString, 'Ad') = 0 then
                       begin
                         Link := MakeTransferImageLink(Params['Link'],
    Params['Image'], Params['Company'], Params['ID']);
                       end;
                     end;
         end;
         ReplaceText := Link;
      finally
        Params.Free;
      end;
    end;

    function TWebModule1.MakeTransferImageLink(strLink, strImage,
    strCompany, strID: string): string;
    begin
      Result := '<A HREF='+ MakeHTMLParam('transfer?' + strLink + '&' +
    strID) + '>';
      Result := Result + '<IMG SRC=' + MakeHTMLParam(strImage) + ' ';
      Result := Result + 'ALT=' + AddQuotes(strCompany) + '></A>';
    end;

    The following OnAction handler takes the parameters passed by the image and adds entries to a database to keep track of how many hits each company is getting and which graphic was selected. (An advertiser might have more that one graphic being displayed and might be interested in which ones were getting more attention.) All of the real work gets done in the final line of this procedure, where the link that was passed by the image is called in ResponseSendDirect. ResponseSendDirect causes the web browser to look to another URL for HTML to display. 

    procedure TWebModule1.WebModule1WebActionItem2Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled:
    Boolean);
    var
       Hits: integer;
    begin
      with AdInfoTable do
      begin
         Open;
         try
           SetKey;
           FieldByName('CompID').AsString := Request.QueryFields[1];
           GotoKey;
           Hits := FieldByName('HitCount').AsInteger;
           Edit;
           FieldByName('HitCount').AsInteger := Hits + 1;
           Post;
         finally
           Close;
         end;
      end;

      with HitTable do
      begin
           Open;
           try
              Insert;
              FieldByName('DateTime').AsDateTime := Now;
              FieldByName('Source').AsString := Request.RemoteAddr;
              FieldByName('CompID').AsString := Request.QueryFields[1];
              Post;
           finally
              Close;
           end;
      end;
      Response.SendRedirect(Request.QueryFields[0]);
    end;

    Setting Cookies

    The HTTP protocol is a powerful protocol, but one of its weaknesses is that it is stateless. This means that once an HTTP conversation has been completed, neither the client nor the server have any memory at all that the conversation even took place, much less what it was about. This can present a number of problems for applications that run across the web, as the server is not able to remember important things like passwords, data, record positions, etc., that have been sent to the client. Database applications are particularly affected as they often rely on the client knowing which record is the current record back on the server. 

    The HTTP protocol provides a basic method for writing information on the client's machine to allow the server to get information about the client from previous HTTP exchanges. Called by the curious name 'cookie', they allow the server to write state information into a file on the clients hard drive and to recall that information at a subsequent HTTP request. This greatly increases a server's capabilities with respect to dynamic web pages. 

    Cookies are no more than a text value in the form of CookieName=CookieValue. A cookie should not include semi-colons, commas, or white space. The user can refuse to accept cookies, so no application should ever assume that a cookie will be present. Cookies are becoming more and more prevalent as web sites get more and more sophisticated. If you are a Netscape user, you might be surprised by what you find in your COOKIES.TXT file. Internet Explorer users might peek into the \WINDOWS\COOKIES folder. If you want to track cookies as they are set on your machine, both of these browsers allow you to approve individual cookie settings within their security preference settings. 

    Managing cookies in Delphi is, pardon the pun, a piece of cake. The THTTPRequest and THTTPResponseclasses encapsulate the handling of cookies quite cleanly, allowing you to easily control how cookie values are set on a clients machine, and to read what cookies have been previously set. 

    The work of setting a cookie is all done in the TWebResponse.SetCookieField method. Here you can pass a TStrings descendent full of cookie values, along with the restrictions limitations placed on the cookies. 

    The SetCookieField method is declared as follows in the HTTPAPP unit: 

    procedure SetCookieField(Values: TStrings; const ADomain, APath:
    string; AExpires: TDateTime; ASecure: Boolean);

    The Values parameter is a TStrings descendent (you will probably use a TStringList ), that holds the actual string values of the cookies. You can pass multiple cookies in the Valuesparameter. 

    The ADomain parameter allows you to define in which domain the given cookies are relevant. If no domain value is passed, then the cookie will be passed to every server to which a client makes a request. Normally, a Web application will set its own domain here so that only the pertinent cookies are returned. The client will examine the existing cookie values and return those cookies that match the given criteria. 

    For example, if you pass widgets.com in the ADomain parameter, all future requests to a server in the widgets.com domain will pass along the cookie value set with that domain value. The cookie value won't be passed to other domains. If the client requests big.widgets.com or small.widgets.com the cookie will be passed. Only hosts within the domain can set cookie values for that domain, avoiding all sorts of potential for mischief. 

    The APath parameter allows you to set a subset of URL's within the domain where the cookie is valid. The APath parameter is a subset of the ADomain parameter. If the server domain matches the ADomain parameter, then the APath parameter is checked against the current path information of the requested domain. If the APath parameter matches the pathname information in the client request, then the cookie is considered valid. 

    For example, following the above example, of APath contained the value '/nuts', then the cookie would be valid for a request to widgets.com/nuts and any further paths such as widgets.com/nuts/andbolts. 

    The AExpires parameter determines how long a cookie should remain valid. You can pass any TDateTime value in this parameter. If you want a cookie to be valid for ten days, pass Now + 10 as a value. If you want to delete a cookie, pass a date value that is in the past, and that will invalidate the cookie. Note that a cookie may become invalid and not be passed, but that does not necessarily mean that the data is actually removed from the client's machine. 

    The final parameter, ASecure, is a boolean value that determines whether the cookie can be passed over non-secure channels. A value of True means that the cookie can only be passed over the HTTP-Secure protocol or a Secure Socket Layer network. For normal use, this parameter should be set to False. 

    You web server application receives cookies sent by the client in the TWebRequest.CookieFields property. This parameter is a TStrings descendent that hold the values in an indexed array. The strings are the complete cookie value in param=value form. They can be accessed like any other TStrings value. The cookies are also passed as a single string in the TWebRequest.Cookie property, but normally you wouldn't want to manipulate them here. You can assign the cookies directly to an existing TStrings object with the TWebRequest.ExtractCookieFieldsmethod. 

    A simple example can illustrate the ease with which Delphi deals with cookies. First, add the WebUtils unit to your uses clause (The WebUtils unit is included with the code for this paper). Then, create a new Web server application and give it two Actions, one named SetCookie and the other GetCookie. Set the code in the OnAction event for SetCookie to the following code: 

    procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled:
    Boolean); 
    var
      List: TStringList;
    begin
      List := TStringList.Create;
      try
         List.Add('LastVisit=' + FormatDateTime('mm/dd/yyyy hh:mm:ss',
    Now));
         Response.SetCookieField(List, '', '', Now + 10, False);
         Response.Content := 'Cookie set -- ' + Response.Cookies[0].Name;
      finally
        List.Free;
      end;
      Handled := True;
    end;

    and make the OnAction code for GetCookie look like this: 

    procedure TWebModule1.WebModule1WebActionItem2Action(Sender: TObject;
      Request: TWebRequest; Response: TWebResponse; var Handled:
    Boolean);
    var
      Params: TParamsList;
    begin
         Params := TParamsList.Create;
         try
           Params.AddParameters(Request.CookieFields);
           Response.Content := 'You last set the cookie on ' +
    Params['LastVisit'];
         finally 
           Params.Free;
         end;
    end;

    Set up a web page that calls the following URLs: 

    http://<your server>/project1.dll/SetCookie

    and 

    http://<your server>/project1.dll/GetCookie

     Then, set the cookie by calling for the first URL from a web page in the same directory as the DLL. This will set a cookie on the client machine that lasts for ten days and contains the date and time that the request was made in a cookie called LastVisit. If you have your web browser set to accept cookies, it should ask you to confirm the writing of the cookie. Then call the GetCookie action to read the cookie, and you should see the date and time that you last called the SetCookie action. 

    Cookies can contain any information that can be stored in a string. Cookies can be as big as 4K bytes, and a client can store as many as 300 cookies. Any individual server or domain is limited to twenty cookies. Cookies are powerful, but as you can see, you should try to limit their use. They certainly cannot be used to store large amounts of data on a client's machine.

    Further Information and Resources

     If you are going to do any HTML writing at all in Delphi, you have to download Kevin Weeks' THTMLWriter (http://www.informant.com/libs/delphi/1x/DI9605kw.zip). It's a beautiful and elegant component that makes writing HTML an absolute breeze. It was featured in an article (http://www.informant.com/libs/delphi/samples/DI0596KW.PDF) in the May 1996 issue of the Delphi Informant (http://www.informant.com/delphi/di_index.HTM) magazine and well, well worth the download. (You'll need an Adobe Acrobat viewer to look at the article. You can get the Acrobat reader at http://www.adobe.com

    The HTTP protocol is an evolving standard. The links below can provide some insight into its current and future definition: 

    http://www.w3.org/pub/WWW/Protocols/HTTP/AsImplemented.html

    http://www.w3.org/pub/WWW/Protocols/HTTP/HTTP2.html 

    http://www.ics.uci.edu/pub/ietf/http/rfc1945.html

    http://www.ics.uci.edu/pub/ietf/http/

    http://www.w3.org/pub/WWW/Protocols/HTTP-NG/http-ng-status.html

    This is a very good site for Delphi ISAPI developers. Consider adding some code and examples for others to share -- http://delphi.snapjax.com/isapi/

    This page from Yahoo has links to pages with information about Cookies -- http://www.yahoo.com/Computers_and_Internet/Internet/World_Wide_Web/HTTP/Protocol_Specification/Persis tent_Cookies/

    Setting up Netscape Servers to run ISAPI DLL's

    Prior to using Delphi Web Server Applications on Netscape servers the following configuration changes are necessary: 

    Place the file ISAPITER.DLL file located in the Delphi 3.0 \BIN directory into the C:\Netscape\server\nsapi\examples directory (your path may differ). 

    Make the following modifications to the server configuration files located in C:\Netscape\server\httpd-<servername>\config directory: 

    Make the following changes in obj.conf: 

    Insert Line (modify the server path information accordingly): 

    Init funcs="handle-isapi,check-isapi,log-isapi" fn="load-modules" shlib="c:/netscape/server/nsapi/examples/ISAPIter.dll"

    * AFTER the line: 

    Init fn=load-types mime-types=mime.types

     In the <Object name=default> section inset the line: 

    NameTrans from="/scripts" fn="pfx2dir" dir="C:/Netscape/Server/docs/scripts" name="isapi"

    before the line 

    NameTrans fn=document-root root="C:/Netscape/Server/docs"

    Add the following new section to the end of the obj.conf file: 

    ------ Cut here ------- 

    <Object name="isapi">

    PathCheck fn="check-isapi"

    ObjectType fn="force-type" type="magnus-internal/isapi"

    Service fn="handle-isapi"

     </Object>

    ------ Cut here ------- 

    In mime.types add the following line as the last line in the file: 

    type=magnus-internal/isapi exts=dll

    Once this is completed, you need to create a server directory alias that point to a directory where the DLL's are to reside. You need to set the Style for that directory to ISAPI. Note that the server won't serve an HTML page from that directory, expecting that every file int he directory is an ISAPI file. You'll have to adjust your HTML accordingly. 

    Debugging Netscape Server Extensions with Delphi

    To debug a Web Server application using Netscape Fast Track web server, set the application's run parameters as follows: 

    Host Application: c:\Netscape\server\bin\httpd\httpd.exe

    Run Parameters: c:\Netscape\server\httpd-<servername>\config

     This starts the Netscape server and indicates to the server where the configuration files are located. 

This site was last updated on:  Thursday, November 11, 1999