| |||||
| © Perl Mongers Italia. Tutti i diritti riservati. | |||||
| |||||
Nella precedente puntata abbiamo visto come l'accoppiata Apache/ | |||||
| |||||
In questa puntata vedremo come
| |||||
| |||||
Come accennato all'inizio, Apache ha una propria vasta e potente API, scritta in C, che permette di
accedere a tutte le sue funzioni interne e può essere usata dallo sviluppatore
per estendere le funzionalità o modificare il comportamento del server Web
stesso linkando un nuovo modulo direttamente all'eseguibile. Tuttavia lo
sviluppo di un modulo Apache in C può risultare estremamente più complesso
(dalla gestione dei sorgenti alla scrittura del Makefile, alla ricompilazione
del binario | |||||
| |||||
Prima di addentrarci nella programmazione Figura 1. Ciclo di vita del server Apache
Nonostante sia possibile intervenire in ognuna di queste fasi sicuramente la più importante e quella sulla quale concentreremo la nostra attenzione, è la Request Loop (Figura 2). Figura 2. Apache Request Loop
La Request Loop è la fase in cui viene processata la richiesta HTTP ed è a
sua volta composta da più fasi ad ognuna delle quali è associato un handler in
corrispondenza del quale viene eseguita una porzione di codice, in genere un
modulo Apache. | |||||
| |||||
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
In
sub handler {
my $r = shift;
# codice specifico ...
return STATUS_CODE;
}
La funzione Esempio 1. Apache/FirstModule.pm, il nostro primo modulo
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 use Apache::Constants qw(:common); importiamo gli Con la dichiarazione della funzione PerlHandler Apache::FirstModule Sull'installazione del modulo torneremo approfonditamente più avanti, per ora
accontentiamoci di sapere che il nome my $remote_ip = $r->connection->remote_ip; per catturare l'IP remoto del client. L'oggetto
$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 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 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 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 %> tar -zxvf libapreq-x.y.tar.gz %> cd libapreq-x.yy %> perl Makefile.PL %> make %> make test %> make install
my $form = new Apache::Request($r); Successivamente viene invocato il metodo
my $config_file = $r->dir_config('MyConfigFile');
my %options = map { $_ => 1 } $r->dir_config->get('MyOption');
utilizziamo il metodo
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. | |||||
| |||||
I moduli precedenti illustravano un determinato tipo di handler, il content
handler corrispondente alla Response Phase. Tuttavia Esempio 3. Apache/MyAuthen.pm, il modulo per l'autenticazione
package Apache::MyAuthen;
use strict; use Apache::Constants qw(:common);
sub handler {
my $r = shift;
my ($res, $sent_pwd) = $r->get_basic_auth_pw;
return $res if $res != OK;
my $user = $r->connection->user;
my $passwd = 'pippo'; # prende la password da file, da DB, ecc.
unless ($passwd eq $sent_pwd) {
$r->log_error("MyAuthen: access to " . $r->uri .
" failed for " . $r->get_remote_host .
", reason: password mismatch sent '$sent_pwd' for user '$user'");
$r->note_basic_auth_failure; return AUTH_REQUIRED;
}
return OK;
}
1;
__END__
Definito il namespace del nostro modulo ed importati gli status code comuni,
vediamo che dopo la definizione della subroutine $r->note_basic_auth_failure; fa sì che venga aggiunto il campo WWW-Authenticate nell'header HTTP inviato
al client contenente tipo e realm dell'autenticazione e venga passato ad Apache
lo status code
PerlModule Apache::RequestTest
PerlModule Apache::MyAuthen
<Location /linuxandc-2>
SetHandler perl-script
PerlHandler Apache::RequestTest
AuthName "My Protected Area"
AuthType Basic
require valid-user
PerlAuthenHandler Apache::MyAuthen
PerlSetVar MyConfigFile /home/myapp/app.conf
PerlSetVar MyOption Option_1
PerlAddVar MyOption Option_2
</Location>
Riavviamo Apache e proviamo nuovamente a lanciare col browser l'URL http://localhost/linuxandc-2: questa volta comparirà la dialog box con la richiesta di username e password per accedere alla risorsa. | |||||
| |||||
Una della caratteristiche (nel bene e nel male) della programmazione sotto
Il nostro codice relativo alle connessioni e disconnessioni rimane quindi
invariato, l'unica modifica è quella di includere all'inizio di ogni script o
modulo, il modulo use Apache::DBI; Alternativamente possiamo precaricare Esempio 4. startup.pl lanciato all'avvio del server Apache
#!/usr/local/bin/perl -w
#
# startup.pl - File caricato all'avvio del server Apache tramite la
# direttiva: PerlRequire /path/to/startup.pl
# Author: Enrico Sorcinelli <e.sorcinelli (at) oltrelinux.com>
#
# Test to make sure we are in a sane environment.
$ENV{GATEWAY_INTERFACE} =~ /^CGI-Perl/ or die "GATEWAY_INTERFACE not Perl!";
# Definizione corretto path @INC dei nostri moduli mod_perl
use lib '/home/web';
# Moduli mod_perl per l'esecuzione dei CGI tradizionali sotto mod_perl
#use Apache::Registry;
#use Apache::Status;
# Moduli Apache::DBI per connessioni permanenti al DB (a livello di child)
use Apache::DBI;
$Apache::DBI::DEBUG = 2;
Apache::DBI->connect_on_init("dbi:mysql:test", "test", "");
#Apache::DBI->setPingTimeOut("dbi:driver:database", $timeout);
# Tutti i moduli da precaricare vanno qui
use strict;
use DBI;
Salviamo il file su file system (/home/web/startup.pl) e aggiungiamo la
direttiva all'inizio del blocco PerlRequire /home/web/startup.pl Nell'esempio attiviamo il livello di debug 2 (Figura 5) in modo da scrivere
nell'error log del server alcune utili informazioni circa le connessioni
inizializzate. Inoltre tramite il metodo Figura 5. Informazioni di debug di
| |||||
| |||||
L'elevata estendibilità, scalabilità e supporto della Comunità Perl rende
Nella prossima puntata analizzeremo in dettaglio quali sono i requisiti di un
framework per lo sviluppo di una web application e metteremo sul banco di prova
i più diffusi ambienti Apache/ | |||||
| |||||
Direttive Perl per l'installazione degli handlers. Questa sezione illustra brevemente le direttive Perl disponibili per installare gli handler relativi a tutto il ciclo di vita Apache (e quindi anche alle fasi della Request Loop).
| |||||
| |||||
Principali proprietà e metodi dell'oggetto
| |||||
| |||||
Le seguenti convenzioni tipografiche sono stato utilizzate in questo articolo: Corsivo
Negli articoli ci sono molti esempi di codice Perl: alcuni sono solamente pezzi
di codice, altri invece programmi completi che possono essere individuati poiché
iniziano tutti con la linea #!/usr/bin/perl Tutti gli esempi che illustrano procedure a linea di comando usano %> perl -e 'print "Hello world\n"'
Hello word
Occasionalmente viene utilizzato #> perl -e 'print "Hello world\n"'
Hello word
| |||||