Friday, July 03, 2009

 

Apache 2.2.x modules with FreePascal (Win32)


Continuing with this series of articles, I will start with the FreePascal version using plain command line FreePascal 2.2.4 for Win32, and later I'll try to create a module for Apache 2.2.x on Linux.

Before copying-pasting this example, I recommend you to read this Wiki to get a detailed knowledge of the problem.

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, and compile the file with this command:


fpc -WR -XX -Xs mod_helloworld.pp


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 mod_helloworld.dll, now you have to rename the file to mod_helloworld.so and copy to your Apache/modules directory, usually in C:\Program files\Apache Software Foundation\Apache2.2\modules.

Now add this to your httpd.conf file in C:\Program files\Apache Software Foundation\Apache2.2\conf:


LoadModule test_module modules/mod_helloworld.so
<Location /hello>
SetHandler helloworld-handler
</Location>


I don't have a Linux box here to test this (shhh, I'm at work), I'll try to compile it this weekend at home in Linux and FreeBSD.
Comments:
Hello Leonardo, that's really great example. I managed to compile it, but i had to change the if statement in line 42 and basically remove the "not" from it and that's it, done! Keep up the good work, i'm still waiting the Linux version, i hope you can make it soon :)
 
sorry a small correction in the line 42: instead of "if not SameText(RequestedHandler, 'testapache-handler')" it should be "if not SameText(RequestedHandler, HANDLER )"
 
it was very interesting to read.
 
Post a Comment



<< Home

This page is powered by Blogger. Isn't yours?