Dopo questa breve introduzione siamo pronti per scrivere il nostro primo modulo
Apache. Un modulo Apache è una porzione di codice che esegue una serie di azioni
in corrispondenza di una o più fasi della Request Loop. Ad ogni fase
corrisponde un handler che può essere ridefinito tramite la creazione di
moduli personalizzati. La creazione di un modulo Apache in mod_perl si
compone essenzialmente di due macro fasi:
- Scrittura del modulo Perl che implementi l'handler
- Installazione dell'handler tramite l'aggiunta di una direttiva
Perl*Handler nel file di configurazione di Apache
In mod_perl la definizione di un handler è la seguente:
sub handler {
my $r = shift;
# codice specifico ...
return STATUS_CODE;
}
La funzione handler() riceve come parametro una reference ad un oggetto
request $r di Apache. Questo oggetto è una versione Object-Oriented
dell'equivalente struttura C request_rec e contiene una serie di informazioni
(proprietà e metodi) concernenti la transazione corrente. La funzione
handler() deve infine restituire un codice di uscita (STATUS_CODE) per
indicare ad Apache l'esito della procedura. Tutti i possibili status code sono
associati a delle costanti simboliche definite nel modulo Apache::Constants
(l'equivalente di httpd.h in C) che quindi dovrà essere incluso in ogni
modulo.
Esempio 1. Apache/FirstModule.pm, il nostro primo modulo mod_perl
package Apache::FirstModule;
use strict;
use Apache::Constants qw(:common);
use vars qw($counter);
sub handler {
my $r = shift;
my $remote_ip = $r->connection->remote_ip;
$counter = 0 if ! defined $counter;
$counter++;
my $str = <<EOM;
<HTML>
<HEAD>
<TITLE>Hello nice '$remote_ip' guy!</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<H1>Hello nice '$remote_ip' guy!</H1>
Questo è il mio primo modulo Apache mod_perl!<BR>
Il modulo è stato eseguito $counter volte dal child con pid=$$.
</BODY>
</HTML>
EOM
$r->content_type('text/html');
$r->send_http_header;
$r->print($str);
return OK;
}
1;
__END__
Figura 3. Output del modulo Apache/FirstModule.pm

Analizziamo brevemente il listato dell'Esempio 1. Nella prima riga definiamo il
namespace del nostro modulo. Premesso che possiamo dare al nostro modulo un
qualsiasi namespace, la scelta del namespace Apache:: per i nostri moduli è
una convenzione ad indicare il loro stretto legame col server Apache.
Successivamente invochiamo il pragma use strict per forzare l'interprete
Perl, in fase di compilazione e non di esecuzione, a generare un errore ogni
qualvolta vengono incontrate variabili (o più in generale oggetti) globali che
non siano stati esplicitamente dichiarati con our, use vars, resi locali
con my() o che non siano 'fully-qualified'. Abbiamo infatti visto nella
precedente puntata come la persistenza (non voluta) delle variabili tra
successive invocazioni dello stesso modulo sia una delle cause più comuni di
malfunzionamento. Con:
use Apache::Constants qw(:common);
importiamo gli STATUS_CODE di uscita. In particolare col tag :common
importiamo solo un piccolo subset di simboli e funzioni necessari per la
stragrande maggioranza dei moduli che scriveremo. Quindi dichiariamo $counter
come variabile globale tramite il pragma use vars. uesto ci servirà per
contare le invocazioni del modulo durante le successive richieste.
Con la dichiarazione della funzione handler() inizia il modulo vero e
proprio, dove viene cioè definita la parte di codice che Apache eseguirà ad ogni
invocazione dell'handler installato in httpd.conf con la direttiva:
PerlHandler Apache::FirstModule
Sull'installazione del modulo torneremo approfonditamente più avanti, per ora
accontentiamoci di sapere che il nome handler() è una convenzione in quanto
mod_perl per default cercherà di eseguire quella subroutine nel nostro file
.pm. Definita la variabile locale $r come la reference ad un oggetto
request che Apache passa sempre come parametro alla funzione handler(),
subito invochiamo uno dei metodi che abbiamo a disposizione:
my $remote_ip = $r->connection->remote_ip;
per catturare l'IP remoto del client. L'oggetto $r è di fondamentale
importanza in quanto fornisce gran parte dell'interfaccia Perl alla API Apache.
Nella Appendice B sono descritti i principali metodi e proprietà. Nel nostro
esempio, $r->connection è un metodo dell'oggetto request che restituisce a
sua volta un oggetto della classe Apache::Connection, l'equivalente Perl
della struttura C conn_req della API Apache e fornisce una serie di proprietà
e metodi sulla connessione con il client che ha effettuato la richiesta.
Definita poi per la prima volta la variabile $counter, viene inzializzata la
variabile $str per bufferizzare l'output. Il blocco di istruzioni successive:
$r->content_type('text/html');
$r->send_http_header;
$r->print($str);
richiama i metodi dell'oggetto request per impostare e inviare il corretto
header HTTP e per inviare al client la risposta. Infine, poiché un handler deve
sempre restituire uno status code, utilizziamo la costante simbolica OK
(importata da Apache::Constants) per indicare l'esito positivo della funzione
ad Apache che ora può passare la richiesta alla fase successiva della Request
Loop (oppure al successivo modulo che abbia registrato un altro handler in
quella stessa fase).
Rimane ora da installare questo handler in Apache. Una volta decisa la posizione
nel file system, salviamo innanzitutto il modulo, ad esempio in
/home/web/Apache/FisrtModule.pm. Editiamo infine il file di configurazione di
Apache httpd.conf aggiungendo le direttive:
<IfModule mod_perl.c>
<Perl>
use lib '/home/web';
</Perl>
PerlModule Apache::FirstModule
<Location /linuxandc-1>
SetHandler perl-script
PerlHandler Apache::FirstModule
</Location>
</IfModule>
Tutto il blocco è all'interno della direttiva <IfModule mod_perl.c> e
verrà processato se e solo se Apache è stato avviato con il modulo mod_perl.
Utilizziamo poi un blocco <Perl> per scrivere codice Perl
direttamente in httpd.conf: ciò è necessario per aggiungere la directory
/home/web tra i path presenti nell'array @INC: in tal modo Apache saprà
dove cercare i nostri moduli. Successivamente includeremo questa ed altre
istruzioni Perl non direttamente in httpd.conf ma in uno script Perl vero e
proprio che lanceremo all'avvio del server tramite la direttiva PerlRequire
nome_file. Con la direttiva PerlModule precarichiamo il nostro modulo
allo startup del server. Con il blocco seguente <Location> definiamo
la URI virtuale /linuxandc-1. SetHandler definisce perl-script il
content handler per la location definita all'interno di questo blocco e di fatto
ne attiva il mod_perl (cioè l'interprete Perl). L'installazione vera e
propria del nostro modulo viene fatta infine con:
PerlHandler Apache::FirstModule
in cui definiamo il nostro modulo Apache/FirstModule.pm come responsabile
della Response Phase (generazione dei contenuti) della Request Loop. Apache va
quindi riavviato per rendere effettivo l'handler appena installato a cui
corrisponde l'URL http://localhost/linuxandc-1. Ad ogni richiesta a questa URL
Apache cercherà di eseguire la subroutine handler() definita in
Apache/FirstModule.pm. Come accennato precedentemente il nome handler() è
una convenzione. Nulla ci vieta infatti di definire un'altra funzione
my_sub() e di installare l'handler dichiarandola esplicitamente:
PerlHandler Apache::FirstModule::my_sub
Ora che abbiamo preso confidenza passiamo ad un altro esempio che mostra come
effettuare alcuni dei task più comuni come la gestione dei parametri di una form
utilizzando i metodi nativi della API Perl.
Esempio 2. Apache/RequestTest.pm, il nostro secondo modulo
package Apache::RequestTest;
use strict;
use Apache::Constants qw(:common);
use Apache::Request ();
use Data::Dumper;
use vars qw($counter);
sub handler {
my $r = shift;
my $remote_ip = $r->connection->remote_ip;
my $form = new Apache::Request($r);
my %form_values;
foreach ( qw/name desk hacker/ ) {
$form_values{$_} = $form->param($_);
}
foreach ( qw/distro opensrc/ ) {
$form_values{$_} = [$form->param($_)];
}
$counter = 0 if ! defined $counter;
$counter++;
# Get options from httpd.conf
my $config_file = $r->dir_config('MyConfigFile');
my %options = map { $_ => 1 } $r->dir_config->get('MyOption');
my $str = <<EOM;
<HTML>
<HEAD>
<TITLE>Hello '$remote_ip' guy!</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<CENTER>
<H2>Hello nice '$remote_ip' guy!</H2>
<H3>Apache RequestTest.pm module</H3>
</CENTER>
Questo è il mio secondo modulo Apache mod_perl! Il modulo è stato eseguito
$counter volte dal child con pid=$$.
<FORM METHOD="POST">
<TABLE BORDER>
<TR>
<TD>Nome:</TD>
<TD><INPUT TYPE="text" NAME="name"></TD>
<TD ALIGN="center">
<INPUT TYPE="radio" NAME="desk" VALUE="kde">KDE
<INPUT TYPE="radio" NAME="desk" VALUE="gnome">Gnome
</TD>
</TR>
<TR>
<TD>Pref distro:</TD>
<TD>
<SELECT NAME="distro" MULTIPLE SIZE="5">
<OPTION>Slackware
<OPTION>Red Hat
<OPTION>Debian
<OPTION>Suse
<OPTION>LinuxMandrake
</SELECT>
</TD>
<TD>
<INPUT TYPE="checkbox" NAME="opensrc" VALUE="apache">Apache
<INPUT TYPE="checkbox" NAME="opensrc" VALUE="wu-ftpd">wu-ftpd<BR>
<INPUT TYPE="checkbox" NAME="opensrc" VALUE="squid">squid<BR>
<INPUT TYPE="checkbox" NAME="opensrc" VALUE="samba">samba<BR>
</TD>
</TR>
<TR>
<TD COLSPAN="3" ALIGN="center">
<INPUT TYPE="checkbox" NAME="hacker">Hacker</TD>
</TR>
<TR>
<TD COLSPAN="3" ALIGN="center"><INPUT TYPE="submit" VALUE="Invia!"></TD>
</TR>
</TABLE>
</FORM>
EOM
$str .= "<TABLE BORDER><TR><TD>Configuration dump<PRE>MyConfigFile = $config_file\n" .
Dumper(\%options) . "</PRE></TD>";
$str .= "<TD><PRE>" . Dumper(\%form_values) . "</PRE></TD></TR></TABLE>";
$str .= "</BODY></HTML>";
$r->content_type('text/html');
$r->send_http_header;
$r->print($str);
return OK;
}
1;
__END__
Figura 4. Output del modulo Apache/RequestTest.pm

Definito il namespace del nostro modulo, vediamo subito che utilizziamo la
classe Apache::Request. Questa classe non è nella distribuzione standard
mod_perl e, anche se è stata realizzata dallo stesso autore del mod_perl,
deve essere scaricata compilata ed installata separatamente
http://www.cpan.org/modules/by_module/Apache/libapreq-x.y.tar.gz:
%> tar -zxvf libapreq-x.y.tar.gz
%> cd libapreq-x.yy
%> perl Makefile.PL
%> make
%> make test
%> make install
Apache::Request è una classe realizzata sullo stesso modello (metodi e
proprietà) della pluridecorata classe CGI e rappresenta l'interfaccia Perl
alle librerie C libapreq. Nello stesso pacchetto sono poi presenti le classi
Apache::Cookie e Apache::Upload per la gestione dei cookies e dell'input
di tipo multipart/form-data nelle form (ad esempio l'upload di file via HTTP).
Il vantaggio di usare tale classe è che è assai più veloce e comporta un
occupazione di memoria (per child) di soli 50 KB contro i circa 1.5 MB della
classe CGI! Beninteso, il modulo CGI.pm offre molte altre funzionalità
rispetto ad Apache::Request, ma se il solo utilizzo che ne facciamo è quello
per il recupero dei parametri delle form e per la gestione dei cookies allora è
conveniente usare i metodi offerti da Apache::Request. Un oggetto
Apache::Request viene inizializzato con:
my $form = new Apache::Request($r);
Successivamente viene invocato il metodo param() per recuperare i parametri
della form. Si noti come si utilizzi l'hash array %form_values e si usino,
come valori dei parametri multivalore (select e checkbox multiple ad esempio),
riferimenti ad array anonimi. Con le istruzioni:
my $config_file = $r->dir_config('MyConfigFile');
my %options = map { $_ => 1 } $r->dir_config->get('MyOption');
utilizziamo il metodo get() dell'oggetto request $r->dir_config per
recuperare i parametri passati al modulo da httpd.conf con le direttive
PerlSetVar. L'utilizzo del modulo Data::Dumper ci viene in nostro aiuto
per scopi di debug ed esegue il dump (in svariati formati, qui è utilizzata la
sintassi Perl) degli oggetti passati come riferimento: nel nostro caso l'hash
array contiene le coppie chiave/valori dei parametri della form. Questo modulo,
una volta salvato su file system come /home/web/Apache/RequestTest.pm, va
installato in httpd.conf, all'interno del blocco <IfModule
mod_perl.c> precedentemente definito:
PerlModule Apache::RequestTest
<Location /linuxandc-2>
SetHandler perl-script
PerlHandler Apache::RequestTest
PerlSetVar MyConfigFile /home/web/app.conf
PerlSetVar MyOption Option_1
PerlAddVar MyOption Option_2
</Location>
Apache va quindi riavviato per rendere effettivo l'handler appena installato a
cui corrisponderà l'URL http://localhost/linuxandc-2.
|