Apache 2.2.x modules with FreePascal (Linux)

Finally I have a couple of minutes free to write about Apache 2.2.x Modules in Linux using FreePascal. The methodology is pretty much the same as my previous article about Win32 modules.

The code

Open your favorite editor and type this code, then save it as "mod_helloworld.pp":


{*******************************************
* Test library of the Apache Pascal Headers
********************************************}
library mod_helloworld;

{*******************************************
* The mode must be objfpc on this unit because
* the unix code uses some extensions
* introduced on Free Pascal
********************************************}
{$ifdef fpc}
{$mode objfpc}{$H+}
{$endif}

{$IFDEF WIN32}
{$DEFINE WINDOWS}
{$ENDIF}

{$define Apache2_0}

uses SysUtils, httpd {$ifndef Apache1_3}, apr{$endif};

var
test_module: module; {$ifdef Unix} public name 'test_module'; {$endif}
default_module_ptr: Pmodule;

const
MODULE_NAME = 'mod_helloworld.so';
HANDLER = 'helloworld-handler';

{****************************************************
* Free Pascal only supports exporting
* variables on Windows
*****************************************************}
{$ifdef WINDOWS}
exports
test_module name 'test_module';
{$endif}

{****************************************************
* Handles apache requests
*****************************************************}
function DefaultHandler(r: Prequest_rec): Integer; cdecl;
var
RequestedHandler: string;
begin
RequestedHandler := r^.handler;

{ We decline to handle a request if hello-handler is not the value of r->handler }
if not SameText(RequestedHandler, 'testapache-handler') then
begin
Result := DECLINED;
Exit;
end;

{ The following line just prints a message to the errorlog }
ap_log_error(MODULE_NAME, 54, APLOG_NOERRNO or APLOG_NOTICE,
{$ifndef Apache1_3}0,{$endif} r^.server,
'mod_hello: %s', [PChar('Before content is output')]);

{ We set the content type before doing anything else }
{$ifdef Apache1_3}
r^.content_type := 'text/html';
// ap_send_http_header(r);
{$else}
ap_set_content_type(r, 'text/html');
{$endif}

{ If the request is for a header only, and not a request for
the whole content, then return OK now. We don't have to do
anything else. }
if (r^.header_only <> 0) then
begin
Result := OK;
Exit;
end;

{ Now we just print the contents of the document using the
ap_rputs and ap_rprintf functions. More information about
the use of these can be found in http_protocol.inc }
ap_rputs('<html>' + LineEnding, r);
ap_rputs('<head>' + LineEnding, r);
ap_rputs('<title>Hello There</title>' + LineEnding, r);
ap_rputs('</head>' + LineEnding, r);
ap_rputs('<body bgcolor="#FFFFFF">' + LineEnding ,r);
ap_rputs('<h1>Hello world</h1>' + LineEnding, r);
ap_rputs('This is the first Apache Module working with the new binding from Free Pascal' + LineEnding, r);
// ap_rprintf(r, '<br />A sample line generated by ap_rprintf<br />' + LineEnding, []);
ap_rputs('</body></html>' + LineEnding, r);

{ We can either return OK or DECLINED at this point. If we return
* OK, then no other modules will attempt to process this request }
Result := OK;
end;

{***************************************************
* Registers the hooks
****************************************************}
{$ifdef apache1_3}

procedure hw_init(s: PServer_rec; p: PPool); cdecl;
begin
end;

var
hw_handlers: array[0..0] of handler_rec =
(
(content_type: 'hw_app'; handler: @DefaultHandler)
);

{$else}

procedure RegisterHooks(p: Papr_pool_t); cdecl;
begin
ap_hook_handler(@DefaultHandler, nil, nil, APR_HOOK_MIDDLE);
end;

{$endif}

{***************************************************
* Library initialization code
****************************************************}
begin
default_module_ptr := @test_module;
FillChar(default_module_ptr^, SizeOf(default_module_ptr^), 0);

{$ifdef apache1_3}
STANDARD_MODULE_STUFF(test_module);

with test_module do
begin
name := MODULE_NAME;
init := @hw_init;
handlers := hw_handlers;
end;
{$else}
STANDARD20_MODULE_STUFF(test_module);

with test_module do
begin
name := MODULE_NAME;
register_hooks := @RegisterHooks;
end;
{$endif}
end.


Then stop Apache 2 service.

To compile the module I had to use this on my Debian 5 machine using FPC 2.2.4:


fpc -WR -Xs -XX -Fu/usr/lib/fpc/2.2.4/units/i386-linux/httpd22 mod_helloworld.pp


Pay attention I'm explicitly using the httpd22 path for loading the http.pp related stuff. Without this, the compiler could load the http unit for another version of Apache.

Note: The -WR parameter tells the compiler to add relocatable code to the dll. Without this, you cannot load two dlls compiled with FPC with the same executable.

After compiling the program you will get the file libmod_helloworld, now you have to rename the file to mod_helloworld.so and copy to your Apache/modules directory, usually in /usr/lib/apache2/modules/mod_helloworld.so.

In my Debian 5, Apache2 config files are in /etc/apache2/mods-available. I created the files helloworld.load and helloworld.conf files containing this:

helloworld.load

LoadModule test_module /usr/lib/apache2/modules/mod_helloworld.so


helloworld.conf

<Location /hello>
SetHandler helloworld-handler
</Location>


After this, do an "sudo apache2ctl restart" and go to http://<yourhost>/hello. It should open a page saying "Hello World".

The last article of this series will be the same module for FreeBSD. I'd try to create one, bug got stuck in an issue...at the moment FPC can't create FreeBSD shared libraries, so I'll try to use a workaround to at least, test a basic module.