Athena

Apache Shared Modules in Delphi

Brian Long (www.blong.com)

If you find this article useful then please consider making a donation. It will be appreciated however big or small it might be and will encourage Brian to continue researching and writing about interesting subjects in the future.


Introduction

Back in Issue 69 (May, 2001) of The Delphi Magazine in Apache Shared Modules, I looked at the Apache Web server running on Linux and how Kylix could be used to write CGI applications and also Apache shared modules or DSOs (the main thrust of the article). That was shortly after Kylix had been released, and discussed how to overcome a variety of problems, including the fact that the standard Apache binary distribution is not suitable for using shared modules.

This update looks at what issues need to borne in mind when using Delphi 6 to create Web applications for Apache running on Windows. Fortunately, things are a bit simpler here than they are in Linux (a welcome relief). This article should be considered an update to the original article rather than standalone, so if you are unfamiliar with Apache and DSOs (or shared modules) I recommend you have a read of it. Just ignore any references to Linux issues along the way, particularly about recompiling Apache (there is no need to do this with the Windows version of Apache).

Your first question might be to ask why we should bother looking at Apache anyway. After all, Apache on Windows is certainly not as well established as Apache on Linux, or IIS on Windows. Well, you should consider that whilst Windows 2000 Professional ships with a copy of IIS, it only has a 10 user limit. If you want your Web server to work well you will need to upgrade to Windows 2000 Server and get the appropriate additional licensing.

More things in Apache's favour include the fact that it is completely free and has its full source code freely available for download. Additionally, it is straightforward to configure through one textual configuration file and has updates made to its source code tree far more frequently than IIS. On top of this, the main source code base is evidently well established, given the mass acceptance of Apache on Internet servers (primarily Unix systems), and the documentation states that it can even load up ISAPI DLLs.

There is also the fact that the basic logic you place in a WebBroker or WebSnap application is much the same no matter what Web server protocol you are targeting, so you can easily target Apache Web server from your existing Web server applications by creating a new project file set up appropriately. A minimal amount of work can result in another Web server platform that you can say that you target.

Additionally, it is just at home on Windows 95, 98 or Me as it is under Windows NT or 2000, so can be used for non-sensitive Web servers on a company Intranet. If you need to test Apache out (because you are thinking of setting up a Linux Web server, for example), trying it out under Windows can be much more comfortable if you are new to Linux. Get the hang of what's required of Apache in Windows, then that is one problem out of the way when you set up your Linux box.

Anyway, the bottom line is that it provides another option on top of Microsoft's IIS or PWS, or Netscape server, or CGI applications, so it can be useful to know how to take advantage of it.

Choosing Apache 1 or Apache 2

Apache 2 was released after Delphi 6 and introduces some important issues. The Apache 2 module format differs from the Apache 1 module format and as a consequence, Delphi 6 can only work with Apache 1.x.

To build shared modules for Apache 2 requires Delphi 7, which introduces additional support for Apache 2.x. The key difference in an Apache 2.x shared module, when compared with an Apache 1.x shared module is the project file uses clause. Shared modules targeting Apache 1.x use the ApacheApp.pas unit, whereas Apache 2.x projects use ApacheTwoApp.pas.

Note: Delphi 7 has only been certified for use with Apache up to version 2.0.39 as that was the most current version available when it was sent to manufacturing. Apache 2.0.40 introduces more changes that break the Delphi support and requiring modifications to the support units. However, all is not lost as unofficial patch information supplied by a Borland engineer is available online that allows Apache 2.0.40 and higher to be used with shared modules written in Delphi.

Delphi 7 Apache support
Figure 1: Delphi 7 Apache support

Kylix 3 was released just before Delphi 7 (indeed Delphi 7 includes a copy of the Delphi language version of Kylix 3 in the box), but Apache 2 support did not make it into that product. This may happen with an update pack, but in the meantime you could always try "borrowing" the pertinent Apache 2 source files from your copy of Delphi 7 and using those instead of the Apache 1 files in your shared module project. The Apache source files are located in $(DELPHI)\Source\Internet, where $(DELPHI) is the main Delphi installation directory.

The units used in Apache 1 applications are:

The files used in Apache 2 applications are:

So in theory you could copy the Apache 2 support files across to your project directory and modify the project uses clause to use ApacheTwoApp instead of ApacheApp.

Note: that this suggestion is speculative - I haven't tested it.

Installing Apache

Before writing any Apache shared modules, you need to get hold of Apache and install it. You can get a ZIP file containing the source code from http://www.apache.org/dist/httpd/binaries/win32/, but that requires you to have Visual C++ to compile it. Instead, I recommend you get the precompiled version without source code from httpd.apache.org/dist/httpd/binaries/win32. I downloaded version 1.3.20, apache_1.3.20-win32-no_src-r2.msi, which was a Windows Installer file that can be used to install Apache. Clearly this means you need the Windows Installer on your machine, but if you haven't you can download it from the Microsoft Web site.

The last time I checked there were two main versions of Apache available, one is a version 1.x (Apache 1.3.28) and one is a version 2.x (Apache 2.0.47). As discussed earlier, if you are using Delphi 6 you must download the 1.x version as support for 2.x only arrives in Delphi 7.

Before installing, you should terminate any other Web servers you have running (I had IIS running on Windows 2000), as they will typically all fight for the same default TCP port (port 80). I suspended IIS through the Internet Information Services Control Panel applet (in Control Panel's Administrative Tools folder).

When you run the file, the installation wizard will start:

Apache installation wizard
Figure 2: Installing Apache for Windows

When it gets around to asking you about your Web server information, you will be best placed to answer the questions. Since I want to access my Web server by specifying its machine name (Cube), that's what I tell the installation program:

Apache installation settings
Figure 3: Specifying my Web server settings

This will mean I can browse to http://Cube from anywhere on my network and gain access to Cube's Web server.

Notice in Figure 3 that the installation asks if you wish Apache to run as a service. On Windows NT/2000, this will be a good choice for a production system, as it keeps Apache running from boot-up to shutdown, but is not recommended on Windows 9x, as it is still at an experimental stage on these platforms.

On a development system (and on Windows 9x), you might find it easier to choose the other option, which installs it for manual invocation in a console window. This option makes it straightforward to debug your Apache shared modules and is the recommended option when installing on Windows 9x.

Controlling Apache

When installation is done, about 4Mb of disk space will have been consumed (even though the installation program suggests it will take about 15Mb) and Apache should be ready for use. Depending whether you chose to install Apache as a service or not, your Apache Start menu folder will be organised differently. Figure 4 shows the menu structure for the service installation.

Apache Start menu group
Figure 4: The Apache service Start Menu Group

If you chose the other option (manually starting Apache in Figure 3), the Control Apache Server menu will not be present, but you will have one extra menu item under Apache httpd Server, which says Start Apache in Console.

If you installed Apache as a service, it will be running each time you start Windows. You can stop Apache with the Stop menu item and start it with the Start item. The Restart item will attempt to stop Apache if it is running, and then start it again. This is a convenient way of getting Apache to re-read its configuration file, after you have made changes to it.

If Apache is installed for manual invocation, the Start Apache in Console item will start the Apache process in a console window (in Apache 1.3.4 and earlier, this option was called Apache Server). This process can be terminated by pressing Ctrl+C in the console window (in version 1.3.13 and later) or by clicking the console window's cross button (in version 1.3.15 and later).

For both installation types, you can also terminate Apache by passing it appropriate command-line switches (it might be convenient to add a new shortcut with this command line to the Apache Start menu folder, if you installed it for manual invocation):

C:\Program Files\Apache Group\Apache\Apache.exe -k shutdown

When Apache is running, you can invoke your Web server's home page, with http://localhost, and you should get the default Apache home page:

Apache home page
Figure 5: The default Apache homepage

Writing a Shared Module

To create a shared module in Delphi 6, choose File | New | Other..., then choose Web Server Application. In the Web Server Application wizard, choose Apache Shared Module (DLL) and press OK. This creates a DLL-based WebBroker application, where we need to focus on the project file (choose Project | View Source).

Note: Delphi 7 and later will give you a choice of Apache versions to target, which affects the Apache support unit used in the project source file.

The default content of the project file looks like:


library Project1;

uses
  WebBroker,
  ApacheApp, //or ApacheTwoApp
  Unit1 in 'Unit1.pas' {WebModule1: TWebModule};

{$R *.res}

exports
  apache_module name 'Project1_module';

begin
  Application.Initialize;
  Application.CreateForm(TWebModule1, WebModule1);
  Application.Run;
end.

In this case, I saved the project as ApacheTest.dpr, which will generate a binary file called ApacheTest.dll. To keep things consistent with the modules supplied with Apache, I added the compiler directives below to the project file to change the output name to mod_ApacheTest.so instead (so is the file extension of DLL equivalents on Linux, called shared objects).


{$E so}             //change binary file extension from .dll to .so
{$LIBPREFIX 'mod_'} //prefix binary file name with mod_

Note: you can also use the Application page of the project options dialog to change the extension and prefix. The other change here was to update the default exported module record name from Project1_module, resulting in:


library ApacheTest;

uses
  WebBroker,
  ApacheApp, //or ApacheTwoApp
  ApacheTestU in 'ApacheTestU.pas' {WebModule1: TWebModule};

{$R *.res}

{$E so}
{$LIBPREFIX 'mod_'}

exports
  apache_module name 'ApacheTest_module';

begin
  Application.Initialize;
  Application.CreateForm(TWebModule1, WebModule1);
  Application.Run;
end.

At this point, the application can have functionality added to it, as in any other WebBroker project. In my sample shared module I have a Web action set up on the Web module (double-click the Web module or use the property editor for the Actions property) that has no PathInfo, but sends a simple piece of HTML back in itsOnAction event handler.


procedure TWebModule1.WebModule1WebActionItem1Action(Sender: TObject;
  Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
  Response.Content := '<H1>Hello World</H1>'
end;

This should suffice to prove whether we can get a DSO working or not.

Using The Shared Module

The next step is to compile the project into the correct directory, and tell Apache to use it. In the project options dialog, the Directories/Conditionals page specifies where the compiled executable will be placed (the Output directory: option). Set this to wherever Apache stores its shared modules, which, on a default setup, is C:\Program Files\Apache Group\Apache\modules. Now compile the project (Ctrl+F9) and the shared module will take its place alongside all the default ones.

The final step is to modify the configuration file. One of the menu items in the Configure Apache Server group will bring up the configuration file in Notepad. Scroll down to the commented section entitled Dynamic Shared Object (DSO) Support. In this section, you will see a number of commented LoadModule directives to which we need to add another. We also need to add a Location directive to identify which URLs will invoke the shared module. For example, you may wish all requested URLs that start with /delphi to be handled by your shared module.

This is the generic template for what needs to go in the config file:


LoadModule module_record_name library_name
<Location /URL>
  SetHandler content_type
</Location>

The bits you customise are:

If we plug these bits into the framework above, we get this:


LoadModule ApacheTest_module modules/mod_ApacheTest.so
<Location /delphi>
  SetHandler mod_apachetest-handler
</Location>

Add these LoadModule and Location directives to the configuration file and then save it.

Note: just before restarting Apache you should be aware of the ClearModuleList directive. If this is present in the configuration file you should ensure it occurs before your LoadModule directives, as it unloads both statically linked modules and also dynamically loaded modules (shared modules).

Now you can restart Apache (as described earlier) and you will be able to browse to http://localhost/delphi. The Web server application will respond accordingly with the simple HTML string:

An Apache shared module
Figure 6: A shared module in action

Note: since the URL portion of the Location directive is case-sensitive, browsing to http://localhost/Delphi will give an error 404, page not found error.

Debugging Shared Modules

In order to debug a shared module, you must ensure Apache is not running. If Apache is installed as a service, stop its execution with the appropriate menu item. If Apache was installed for manual invocation, make sure it is not running in any console windows.

Since your shared module is loaded by Apache when it starts, you must tell Delphi about this host application. In the Run | Parameters... dialog, enter the full path to Apache in the Host Application field:

Setting Delphi parameters to debug shared modules
Figure 7: Telling Delphi how to debug your shared module

Now go to the debugger options dialog (Tools | Debugger Options...) and enable the Debug spawned processes checkbox.

Now you can set breakpoints in your shared module code as you would in any normal application (although to be honest, I could only get this operational under Windows 2000; my Windows 98 tests proved unsuccessful). When you press the Run button (or F9), Delphi launches Apache, which then loads up the shared module allowing it to be debugged.

When you are finished debugging, remember to terminate Apache in the correct manner, as described earlier. In other words, do not simply reset the debugged process (Ctrl+F2) as Apache may not shut down properly. Either use the Start menu item (if available) or the corresponding command-line or, if Apache is running in a console window, press Ctrl+C in that window.

About Brian Long

Brian Long used to work at Borland UK, performing a number of duties including Technical Support on all the programming tools. Since leaving in 1995, Brian has spent the intervening years as a trainer, trouble-shooter and mentor focusing on the use of the C#, Delphi and C++ languages, and of the Win32 and .NET platforms. In his spare time Brian actively researches and employs strategies for the convenient identification, isolation and removal of malware.

If you need training in these areas or need solutions to problems you have with them, please get in touch or visit Brian's Web site.

Brian authored a Borland Pascal problem-solving book in 1994 and occasionally acts as a Technical Editor for Wiley (previously Sybex); he was the Technical Editor for Mastering Delphi 7 and Mastering Delphi 2005 and also contributed a chapter to Delphi for .NET Developer Guide. Brian is a regular columnist in The Delphi Magazine and has had numerous articles published in Developer's Review, Computing, Delphi Developer's Journal and EXE Magazine. He was nominated for the Spirit of Delphi 2000 award.