Enrico Sorcinelli
Apache/mod_perl: un application server open source

In questa puntata vedremo come mod_perl offra molto più d'un modo per accelerare gli script Perl CGI: tramite un'interfaccia ad oggetti, mod_perl ci fornisce un accesso completo alla API di Apache permettendo la realizzazione di estensioni del server (cioè moduli Apache) in Perl piuttosto che in C.
Parte II

Data di pubblicazione: Linux & C., anno 4, numero 23 (Aprile 2002).

©2003 Linux & C. Tutti i diritti riservati. E' vietata la copia e la riproduzione o ridistribuzione dell'articolo senza l'espresso permesso scritto dell'autore e dell'editore.

© Perl Mongers Italia. Tutti i diritti riservati.


Introduzione

Nella precedente puntata abbiamo visto come l'accoppiata Apache/mod_perl renda l'esecuzione degli script Perl CGI estremamente veloce ottenendo prestazioni nettamente superiori (a volte di due ordini di grandezza). Infatti sotto mod_perl, l'interpretate del Perl fa parte già del processo Web server e non vi è necessità di avviarlo o arrestarlo per ogni richiesta di esecuzione di uno script CGI. E, poiché uno script mod_perl è realmente un modulo che estende la funzionalità del server Web Apache, non c'è bisogno di ricompilare il codice del Perl ad ogni richiesta poiché rimane residente in memoria pronto per servire la successiva richiesta.


Oltre il CGI

In questa puntata vedremo come mod_perl offra molto più d'un modo non doloroso per accelerare gli script Perl CGI: tramite un'interfaccia ad oggetti il mod_perl ci fornisce un accesso completo alla API di Apache permettendo la realizzazione di estensioni del server (cioè moduli Apache) in Perl piuttosto che in C, ottenendo prestazioni comparabili. La necessità di realizzare estensioni del server non è una semplice riscrittura di script CGI magari perfettamente funzionanti, ma può essere una necessità tutte le volte che dobbiamo affrontare le limitazioni intrinseche del protocollo CGI come ad esempio:

  • Il remapping delle URL
  • Procedure custom per il controllo di accesso e autenticazione
  • Gestione dei MIME type dei documenti
  • Log degli accessi in un RDBMS


L'API di Apache

Come accennato all'inizio, mod_perl è realmente un'interfaccia Perl alla potente e complessa API del server Apache. In generale, La API (acronimo di Application Programming Interface) non è altro che un'insieme di funzioni, o più in generale veri e propri componenti software organizzati in librerie, che sono incorporati all'interno del sistema e che possono essere usati per creare applicazioni.

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 httpd, per non parlare delle problematiche legate alla gestione diretta della memoria) e in genere si segue questa strada per la scrittura di piccoli moduli frequentemente eseguiti e per cui si necessita di alte prestazioni. Usare mod_perl significa invece che tutte queste funzioni (Apache API) possono essere accedute ed estese usando il Perl e non il C.


Apache Life Cycle

Prima di addentrarci nella programmazione mod_perl vera e propria è necessario spendere due parole sulla architettura di Apache. L'intero ciclo di vita, che andiamo a descrivere, è mostrato in Figura 1:

Figura 1. Ciclo di vita del server Apache

Ciclo di vita del server Apache

  • Server startup e configurazione
  • Apache è basato su un modello multiprocesso (anche se dalla versione 2 Apache include il supporto al multithreading): all'avvio del server viene creato un processo padre che fungerà da supervisore; in tale fase vengono inizializzate le risorse globali e viene caricata la configurazione da httpd.conf (che può anche contenere direttive implementate da moduli esterni)

  • Inizializzazione Moduli
  • in tale fase vengono inizializzati i moduli

  • Fork e inizializzazione processi figli
  • il processo padre provvede a forkare un certo numero di processi figli (child). In tale fase è prevista la possibilità di una ulteriore re-inizializzazione dei moduli per ogni processo figlio

  • Request Loop
  • ogni child entra in loop e si mette in ascolto per rispondere alle richieste vere e proprie

  • Uscita processi figli
  • a seconda della configurazione, dopo un certo numero di richieste, il processo figlio viene terminato ed eventualmente rimpiazzato da un altro child.

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

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. mod_perl consente di aggiungere una logica a determinate o a tutte le fasi della Request Loop (e non solo) di Apache e di fatto rappresenta un ambiente estremamente robusto che permette di costruire applicazioni avanzate ad alto rendimento. La descrizione di tutte le direttive Perl che installano gli handler corrispondenti alle fasi del ciclo di vita di Apache e della Request Loop è riportata in Appendice A.


Hello World

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:

  1. Scrittura del modulo Perl che implementi l'handler
  2. 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

 Apache/FirstModule.pm output

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

 Apache/RequestTest.pm output

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.


Handler di autenticazione

I moduli precedenti illustravano un determinato tipo di handler, il content handler corrispondente alla Response Phase. Tuttavia mod_perl mette a disposizione gli handler relativi a tutte le fasi di vita di Apache (e non solo) consentendo quindi di riscrivere il comportamento di Apache per ognuna di queste fasi in maniera semplice e veloce. A titolo di esempio scriviamo ora un modulo che ridefinisce il comportamento di Apache per verificare l'autenticazione di un utente che richieda un documento in un'area protetta (con Basic Authentication).

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 handler() viene chiamato il metodo $r->get_basic_auth_pw per recuperare la password inviata dall'utente. Si noti come si esca dal modulo nel caso che il primo parametro restituito dal metodo non sia OK passando poi ad Apache tale status code. Nelle successive istruzione viene fatto il check sul corretto login eseguito dall'utente. In caso di errore (password non corretta, utente inesistente, ecc) viene generata una entry nell'error log del server tramite l'utilizzo del metodo $r->log_error. La chiamata del metodo:

   $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 AUTH_REQUIRED per forzare la riautenticazione dell'utente. L'installazione di questo handler viene fatta utilizzando la direttiva PerlAuthenHandler. Salvato il modulo su file system come /home/web/Apache/MyAuthen.pm, installiamolo per autenticare ad esempio gli accessi al modulo Apache::RequestTest definito precedentemente:

   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.


Connessioni permanenti

Una della caratteristiche (nel bene e nel male) della programmazione sotto mod_perl è la persistenza degli oggetti in ogni Apache child. Questo può essere felicemente utilizzato per rendere permanenti le connessioni ad un RDBMS. Esiste un modulo, Apache::DBI, che provvede a rendere persistenti le connessioni al DB in maniera completamente trasparente al programmatore. Apache::DBI intercetta le chiamate di connessione al DB, sovrascrivendo i metodi connect() e disconnect() del modulo DBI, e restituisce un oggetto connessione se già istanziato, altrimenti ne crea uno nuovo.

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 Apache::DBI tramite il comando

   use Apache::DBI;

Alternativamente possiamo precaricare Apache::DBI allo startup di Apache e definire le policies di inizializzazione delle connessioni per ogni child. Avevamo accennato in precedenza alla possibilità di lanciare uno script Perl all'avvio del server. Ciò può essere fatto utilizzando la direttiva PerlRequire path_file che consente di eseguire lo script indicato in path_file. Questa tecnica può risultare utile per svolgere tutta una serie di elaborazioni utilizzando il linguaggio Perl e per precaricare all'avvio del server tutti i moduli (librerie) di terze parti che utilizzeremo nei nostri moduli Apache. Nel caso di Apache::DBI è necessario che venga caricato prima del modulo DBI e/o di qualsiasi altro modulo che ne faccia uso altrimenti non avremo connessioni permanenti!

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 <IfModule mod_perl.c> precedentemente definito:

   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 Apache::DBI->connect_on_init creiamo, ad ogni fork di Apache, un handle per la connessione permanente al DB che verrà utilizzato per tutta la vita del child.

Figura 5. Informazioni di debug di Apache::DBI nell'error_log

Apache::DBI on error_log


Conclusione

L'elevata estendibilità, scalabilità e supporto della Comunità Perl rende mod_perl uno degli ambienti di sviluppo per web application più potenti disponibili attualmente. Questo tuttavia è soltanto una piccola parte dei benefici che si hanno utilizzando mod_perl: un server mod_perl-enabled può infatti avvalersi dei numerosi moduli che sono stati scritti per l'ambiente Apache/mod_perl senza considerare che fra l'ampia lista dei moduli disponibili ci sono parecchi sistemi ad alto livello che uniscono gruppi di moduli più piccoli in più grandi ambienti integrati, veri e propri framework di sviluppo. In definitiva mod_perl trae massimo vantaggio da tutto quello che il server Apache può offrire lasciando a disposizione tutta la flessibilità e la potenza del linguaggio Perl.

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/mod_perl opensource dispondibili.


Appendice A

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).

PerlChildInitHandler
Installa l'handler che viene chiamato ad ogni fork di un child Apache (fase Fork e inizializzazione processi figli dell'Apache life cycle)

PerlPostReadRequestHandler
Questa à la prima delle direttive che installano gli handler relativi alle fasi della Request Loop. Apache esegue questo handler (post-read-request) immediatamente dopo aver ricevuto la richiesta ed aver fatto il parsing dei campi dell'header HTTP

PerlTransHandler
Installa l'handler relativo alla fase URI translation. Apache esegue questo handler per mappare le richieste dalla forma URI in un nome di un file

PerlHeaderParserHandler
Installa l'handler relativo alla fase Header parsing.

PerlAccessHandler
Installa l'handler relativo alla fase Access control. Apache esegue questo handler per controllare gli accessi sulla base dell'IP del client o del suo hostname.

PerlAuthenHandler
Installa l'handler relativo alla fase di Authentication. Apache esegue questo handler nel caso che la risorsa richiesta sia protetta da password. Con tale handler è possibile riscrivere la fase di autenticazione utilizzando ad esempio un database relazionale per memorizzare le informazioni degli utenti.

PerlAuthzHandler
Installa l'handler relativo alla fase di Authorization. Apache esegue questo handler nel caso che la risorsa richiesta sia protetta. Con tale handler possono essere ridefinite le policies di accesso a determinate risorse sulla base dei privilegi dell'utente

PerlTypeHandler
Installa l'handler relativo alla fase di Type checker. In questa fase viene assegnato il tipo MIME alla risorsa richiesta

PerlFixupHandler
Installa l'handler relativo alla fase di Fixups. In questa fase è possibile eseguire azioni dell'ultimo minuto prima che venga generata la risposta come ad esempio il settaggio di determinate variabili di ambiente

PerlHandler
Installa l'handler relativo alla fase di Response. Apache esegue questo handler (storicamente noto col nome di content handler) per la generazione dei contenuti. È quindi il più importante di tutta la Request Loop ed è utilizzato dalla maggioranza dei moduli mod_perl

PerlLogHandler
Installa l'handler relativo alla fase di Log

PerlCleanupHandler
Installa l'handler (l'ultimo della Request Loop) relativo alla fase di Cleanup. In questa fase la transazione HTTP è terminata ed è possibile eseguire azioni di 'pulizia' come il rilascio di risorse (chiusura connessioni ad un DB) o rimozione di file temporanei.

PerlChildExitHandler
Installa l'handler che Apache chiama appena prima che un processo child viene terminato.


Appendice B

Principali proprietà e metodi dell'oggetto $r (request_rec). Ecco un 'essenziale' elenco dei principali metodi e proprietà dell'oggetto Apache request $r:

$r = Apache->request()
Restituisce un oggetto Apache request
$r->header_in($header)
Restituisce e/o imposta il valore del campo $header dell'header HTTP
$r->content_type($type)
Imposta il MIME type (text/html, image/png, ecc) del documento inviato al browser.
$r->send_http_header()
Imposta il corretto header HTTP di risposta al client. Questo metodo va invocato prima di inviare dati al client
$r->print()
Rappresenta il metodo principale per inviare i dati al client ed è l'equivalente della funzione Perl print()
$r->printf()
L'equivalente della funzione built-in Perl printf()
$r->rflush()
Esegue il flush del buffer usato da Apache ed invia i dati al client. Con l'autoflush impostato ($|=1) questo metodo viene invocato dopo ogni $r->print()
$r->dir_config($name)
Restituisce il valore della variabile $name impostata in httdp.conf (o .htaccess) con la direttiva PerlSetVar
$r->document_root()
Restituisce il valore della document root impostata in httpd.conf con CDocumentRoot>
$r->log_error($message)
Scrive il messaggio di errore $message nell'error log del server
$r->warn($message)
Scrive il messaggio di errore $message nell'error log del server (la direttiva LogLevel deve essere impostata almeno a warn)
$r->get_basic_auth_pw()
Se la risorsa richiesta è protetta da Basic authentication, restuisce un array di due valori di cui il primo è il codice di ritorno ed il secondo è la password inviata dal client
$r->server
Restituisce un riferimento ad un oggetto Apache::Server fornendo l'interfaccia Perl alla struttura C della API Apache server_rec

$c = $r->connection
Restituisce un riferimento ad un oggetto Apache::Connection fornendo l'interfaccia Perl alla struttura C della API Apache conn_rec
$c->remote_host()
Restituisce l'hostname (se disponibile) del client
$c->remote_ip()
Restituisce l'indirizzo IP del client


Convenzioni usate in questo articolo

Le seguenti convenzioni tipografiche sono stato utilizzate in questo articolo:

Corsivo

Utilizzato nelle URL, nei percorsi di file, nelle pagine di manuale, nei titoli di libri e/o articoli. Anche nuovi termini cui si intende dare particolare rilievo sono formattati in corsivo.

Larghezza costante

Utilizzato negli esempi di codice e file di configurazione, nel codice che appare all'interno del testo e negli esempi che illustrano cosa digitare a linea di comando.

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 #! (shebang):

   #!/usr/bin/perl

Tutti gli esempi che illustrano procedure a linea di comando usano %> per indicare il generico prompt della shell:

   %> perl -e 'print "Hello world\n"'
      Hello word

Occasionalmente viene utilizzato #> per indicare espressamente la shell dell'utente root:

   #> perl -e 'print "Hello world\n"'
      Hello word