Introduction
Apache::Registry
Variantes de Apache::Registry
Comment utiliser un module tout fait ? Comment écrire le sien ?
Modules divers, ponctuellement utiles
Modules utiles lors du débugguage
Web applications frameworks
Services Web
Modules que je n'ai pas réussi à classer ailleurs
Divers
Divers
Si vous devez concevoir un site Web, vous pouvez faire l'un des choix suivants.
Un site web statique, soit créé à la main (mais il risque d'être hétérogène), soit à l'aide d'outils adéquats, comme WML (Website Modelling Language).
http://thewml.org/
Un site web dynamique, utilisant des modules comme CGI ou CGI::Application, basé sur des CGI ou (ce qui revient en fait au même, sauf que c'est plus rapide et que ça ne s'appelle plus CGI), Apache::Registry.
Un site Web dynamique, construit autour d'un module mod_perl que vous aurez écrit, ou d'un module qui fera exactement ce que vous voulez (slashcode, Apache::MP3, etc.)
http://slashcode.com/
Un site Web dynamique construit à l'aide de HTML::Mason, HTML::Template ou AxKit.
C'est le module qui permet de lancer des scripts Perl à partir d'un serveur web Apache, comme si c'étaient des CGI. Mais en fait, ce ne sont pas des CGI : ils ne sont compilés qu'une seule fois, ils restent en mémoire, et leurs variables globales sont réellement globales, elles sont préservées entre deux exécutions. (Attention, toutefois : comme il y a plusieurs serveurs web (Apache se forke), il y aura une copie différente du programme dans chaque serveur.)
Pour que tous les fichiers d'un répertoire soient gérés par Apache::Registry, on peut mettre dans le httpd.conf
PerlModule Apache::Registry <Location /perl> SetHandler perl-script PerlHandler Apache::Registry Options ExecCGI </Location>
Pour que tous les fichiers ayant l'extension .perl soient gérés par Apache::Registry, on peut mettre dans httpd.conf
<Location /perl/*.pl> SetHandler perl-script PerlHandler Apache::Registry Options -Indexes ExecCGI PerlSendHeader On </Location>
Les fichiers *.perl ainsi gérés par Apache::Registry peuvent être des programmes Perl tout à fait normaux.
#! perl -wT use strict; use Apache::Run; print "Content-type: text/html\n\n"; print "Hi There!";
Mais on n'est pas obligé de mettre les premières lignes (car perl est déjà lancé et le module Apache::Run est déjà chargé).
print "Content-type: text/html\n\n"; print "Hi There!";
ou encore
my $r = Apache->request; $r->content_type("text/html"); $r->send_http_header; $r->print("Hi There!");
ou encore, en utilisant le module CGI (qui sert d'une part à récupérer les arguments envoyés par POST ou GET, d'autre part, pour les gens qui n'aiment pas le HTML, à remplacer le HTML par des appels de fonctions -- ça n'est pas parce qu'on utilise ce module qu'on fait du CGI).
#!/usr/local/bin/perl -Tw use strict; use CGI qw(:all); $|++; print header; print start_html('Essai'), h1('Essai'), hr, p('Ceci est un essai.'), end_html;
Le module Apache::Registry ne convient pas à tous les scripts : les plus mal écrits ne supporteront pas (entre autres) que les variables globales soient préservées entre deux exécutions. Le module Apache::PerlRun est à mi-chemin entre Apache::Registry et mod_cgi : les fichiers sont compilés à chaque fois (donc c'est plus lent qu'Apache::Registry), mais par contre, les modules qu'ils utilisent ne le sont qu'une seule fois (c'est donc plus rapide que mod_cgi).
Pour que tous les fichiers d'un répertoire soient traités de cette manière, on peut mettre les lignes suivantes dans les httpd.conf.
PerlModule Apache::PerlRun <Location /cgi-perl> SetHandler perl-script PerlHandler Apache::PerlRun Options +ExecCGI PerlSendHeader On </Location>
C'est une variante de Apache::Registry.
Apache::RegistryNG -- Apache::Registry New Generation Apache::RegistryNG is the same as Apache::Registry, aside from using filenames instead of URIs for namespaces. It also uses an Object Oriented interface.
Une autre variante de Apache::Registry.
Apache::RegistryBB -- Apache::Registry Bare Bones It works just like Apache::Registry, but does not test the x bit (-x file test for executable mode), only compiles the file once (no stat() call is made per request), skips the OPT_EXECCGI checks and does not chdir() into the script parent directory. It uses the Object Oriented interface.
Normalement, les scripts utilisant Apache::Registry sont compilés la première fois qu'il sont demandés. Ce module permet de les compiler dès le lancement du serveur.
Dans le fichier de configuration d'Apache, on rajoute un bloc <location>, qui va charger le module en question. Généralement, un site Web ne contiendra qu'un seul tel bloc : il n'y en aura plusieurs que s'il y a des choses de nature très différente. Par exemple, ma page web sur http://www.math.jussieu.fr/~zoonek/ contient des documents HTML statiques (i.e., qui n'interagissent pas avec l'utilisateur), construits automatiquement à partir de fichiers texte, et parfois accompagnés de fichiers annexes (fichiers Perl ou PostScript). Si on voulait en faire un site dynamique (par exemple, si je voulais laisser les visiteurs commenter mes écrits, ou si je voulais ajouter un moteur de recherche), on utiliserait un seul module, qui se chargerait de rajouter une barre de navigation et de convertir les fichiers texte en HTML.
Le bloc <location> permet aussi de configurer le module (si je reprends l'exemple de mon site Web plutôt statique, on pourrait y mettre de nom des rubriques et des répertoires qui doivent figurer sur le site, ou les couleurs à utiliser).
Ce module permet de gérer un album photo. Son bloc <location> ressemble à ceci.
<Location /albums> SetHandler perl-script PerlHandler Apache::Album PerlSetVar AlbumDir /albums_loc PerlSetVar BodyArgs BGCOLOR=white PerlSetVar Footer "<EM>Optional Footer Here</EM>" </Location>
Le module ressemble à ceci (je simplifie) -- c'est un bon exemple pour comprendre comment en écrire un soi-même.
package Apache::Album; use strict; use vars qw($VERSION); use Apache::Constants qw/:common REDIRECT/; use Apache::Request; use Apache::URI (); $VERSION = '0.95'; sub handler { my $r = Apache::Request->new(shift); # Lecture des paramètres venant de httpd.conf my %settings; $settings{'AlbumDir'} = $r->dir_config('AlbumDir') || "/albums_loc"; ... # Lecture des arguments my %params = (); %params = $r->method eq 'POST' ? $r->content : $r->args; # Utilisation des arguments if (defined $params{'AlbumName'}) { my $directory = $params{AlbumName}; $directory =~ s,[^\w\d()],,g; ... } ... }
C'est à peu près tout. On rappelle qu'il ne faut JAMAIS faire confiance aux données qui viennent de l'utilisateur : ici, par exemple, on vérifie que le nom de répertoire ne contient que des lettres ou des parenthèses. Il conviendrait même de se mettre en « taint mode », de manière à arrêter l'exécution du programme si jamais il fait quelque chose de dangereux avec des données venant de l'utilisateur qui n'ont pas été vérifiées.
Voir Apache::TaintRequest (un peu plus loin)
Un autre album photo (qui utilise imlib2 à grands coups de use Inline 'C'...).
Un autre album photo
Interface à une archive CVS
Pour manipuler automatiquement des images, par exemple avec des URL de la forme
http://localhost/images/whatever.gif/Annotate?font=Arial&x=5&gravity=west&text=Hello+world+!
Pour proposer des MP3 à télécharger ou à écouter en streaming.
Le module Apache::MP3::Skin permet de configurer l'apparence du site.
http://www.apachemp3.com
Transforme la documentation des modules installés en HTML.
Transforme des fichiers texte en HTML
Transforme les fichiers HTML avant de les servir au client, en remplaçant <!--header-->, <!--start--> ou <!--footer--> par le contenu d'un fichier.
Ajoute le début et la fin des fichiers HTML.
Ajoute le haut, le bas et le bord des fichiers HTML:
On qualifie de Weblog des sites dans lesquels des gens (les modérateurs) postent des messages (généralement, des informations) et dans lesquels les lecteurs peuvent répondre.
http://freshmeat.net/ http://linuxfr.org/
L'un des logiciels sous-jacents à ces sites est Slashcode.
http://slashcode.com/
C'est probablement un bon exercice que de lire le code d'un Weblog minimaliste :
http://www.raelity.org/apps/blosxom/
Ce module permet d'une part de récupérer les différents arguments d'un script (qu'il soit lancé par GET ou POST), d'autre part, pour les programmeurs qui n'aiment pas le HTML, de remplacer les <H1>, <TABLE> et autres <A> par des appels de fonction h1(...), table(...), a(...).
Quand on conçoit un site, on a généralement plusieurs types de page : la page principale, une page contenant un formulaire pour ajouter des commentaires, une page contenant un formulaire pour effectuer une recherche, une page contenant les réponses de cette recherche, etc. Généralement, on programme cela avec plein de ifs.
if( $mode eq "main" ){ ... } elsif( $mode eq "add_comment" ){ ... } elsif( $mode eq "search_form" ){ ... } elsif( $mode eq "search_results" ){ ... } else { ... }
Le module CGI::Application oblige à programmer plus proprement, en utilisant des « run modes » : à chaque type de page différent correspond une fonction particulière.
Ce module n'apporte rien de plus, il se contente de nous inciter à programmer lisiblement.
Ce module permet de stocker des informations lors d'une session, i.e., lors de plusieures requètes provenant d'une même personne. On manipule simplement une table de hachage, liée à un fichier (à l'aide du module Storage) où à une base de données (via le module DBI).
Après avoir récupéré ou créé le numéro de la session dans le cookie (oui, il faut faire ça soi-même -- ne pas oublier de renvoyer le cookie au client), on se contente d'un
my %session; tie %session, 'Apache::Session::DBI', $session_id, {DataSource => 'dbi:mysql:sessions', UserName => $db_user, Password => $db_pass };
et on peut ensuite stocker ou récupérer le contenu de cette table de hachage.
Ce module permet d'utiliser une connection persistente à une base de données. Quand un script a besoin d'accéder à une base de données, il doit tout d'abord se connecter, ce qui peut prendre un certain temps. Si le même script est appelé très souvent, ces connection et déconnections successivent constituent une perte de temps (et une charge inutile pour la base de données) : ce module permet de réutiliser une connection préalablement ouverte, si elle est toujours active. (Note : le serveur Apache se forke, et on pourra avoir une connection dans chaque processis fils).
Ce module permet de donner automatiquement les chaines de caractères sales (tainted, en anglais) à escape_html avant de les renvoyer au client, pour éviter le CSS (Cross Site Scripting).
use Apache::TaintRequest (); sub handler { my $r = shift; $r = Apache::TaintRequest->new($r); my $querystring = $r->query_string(); $r->print($querystring); # html is escaped... ... }
Ce module permet d'exiger que l'utilisateur s'autentifie avant d'accéder à certaines pages. S'il n'est pas autentifié, il est automatiquement redirigé vers une page de login.
Le module ne s'utilise pas directement : on écrit un module qui hérite de ce module, dans lequel on définit deux méthodes, une première pour vérifier login et mot de passe et créer une clef de session, une seconde pour vérifier l'autenticité de la clef de session.
Je viens de dire que si on voulait gérer un site en écrivant des modules, il fallait modifier le fichier de configuration d'Apache à chaque ajout de module. En fait, le module Apache::Dispatch rend cela superflu : quand on lui donne un URI, il va essayer de trouver le module correspondant.
Ce module contient les constantes dont on a besoin pour écrire des handlers, par exemple, les codes de retour OK ou DECLINED.
use Apache::Constants qw(:common);
Remplacement (plus rapide) de IO::File. Sa fonction la plus utile doit être tmpfile.
Remplacement (plus rapide) de URI::URL. On peut l'utiliser pour décomposer un URI,
my $string = "http://www.foo.com/path/file.html?query+string" my $uri = Apache::URI->parse($r, $string);
mais l'URI courant a déjà été décomposé et se trouve dans
my $uri = $r->parsed_uri;
On peut récupérer les différentes parties de l'URI.
my $scheme = $uri->scheme; my $hostinfo = $uri->hostinfo; my $user = $uri->user; my $password = $uri->password; my $hostname = $uri->hostname; my $port = $uri->port; my $path = $uri->path; my $path = $uri->rpath; my $query = $uri->query; my $fragment = $uri->fragment;
On peut aussi reconstruire cet URI :
$string = $uri->unparse;
Ce module définit des fonctions déjà présentes dans d'autres modules, mais plus rapides (car écrites en C et pas en Perl).
La fonction Apache::Util::escape_html est l'équivalent de HTML::Entities::encode et transforme les < ou & en > ou &.
La fonction Apache::Util::escape_uri est l'équivalent de URI::Escape::uri_escape et transforme les caractères qui ne doivent pas se trouver dans un URL en choses du genre %20 (où 20 est le code ASCII (hexadécimal) du caractère dont on ne veut pas, par exemple, un espace).
Il y a aussi des fonctions unescape_uri, unescape_uri_info (idem, mais elle transforme aussi les + en espaces, comme d'habitude dans les arguments passés par la méthode GET), parsedate, ht_time, validate_password, size_string.
Ce module permet (par exemple) d'afficher dans les logs quand un client a interrompu la connection.
PerlFixupHandler Apache::SIG LogFormat "%h %l %u %t \"%r\" %s %b %{SIGPIPE}e"
Ce module affiche l'état du serveur (environement, modules chargés, handlers actifs, tables de symboles (voir aussi Apache::Symdump), etc.)
<Location /perl-status> SetHandler perl-script PerlHandler Apache::Status Order deny,allow Deny from all Allow from .your-domain.com </Location>
Dans le même ordre d'idée (mais ça n'est plus Perl) :
<Location /server-status> SetHandler server-status Order deny,allow Deny from all Allow from .your-domain.com </Location>
Ce module utilise Devel::Symdump et permet de sauvegarder la table des symboles dans un fichier, pour comparaison ultérieure, avec la commande diff.
Ce module permet de limiter la mémoire ou le temps de calcul d'un processus fils httpd (si on constate, sans parvenir à comprendre pourquoi, qu'il y a une fuite de mémoire ou que ça boucle).
PerlSetEnv PERL_RLIMIT_DEFAULTS On PerlModule Apache::Resource
La page de manuel de setrlimit nous donne la liste des paramètres modifiables (ça dépend du système).
RLIMIT_CPU /* CPU time in seconds */ RLIMIT_FSIZE /* Maximum filesize */ RLIMIT_DATA /* max data size */ RLIMIT_STACK /* max stack size */ RLIMIT_CORE /* max core file size */ RLIMIT_RSS /* max resident set size */ RLIMIT_NPROC /* max number of processes */ RLIMIT_NOFILE /* max number of open files */ RLIMIT_MEMLOCK /* max locked-in-memory address space*/
Voir aussi Apache::SizeLimit.
Module, appelé depuis le fichier de configuration
# sartup.pl: use Apache::SizeLimit; # sizes are in KB $Apache::SizeLimit::MAX_PROCESS_SIZE = 10000; # 10MB $Apache::SizeLimit::MIN_SHARE_SIZE = 1000; # 1MB $Apache::SizeLimit::MAX_UNSHARED_SIZE = 12000; # 12MB # httpd.conf: PerlFixupHandler Apache::SizeLimit
ou depuis un script CGI
use Apache::SizeLimit; &Apache::SizeLimit::setmax(10000); # Max size in KB &Apache::SizeLimit::setmin(1000); # Min share in KB &Apache::SizeLimit::setmax_unshared(12000); # Max unshared size in KB
qui tue les processus trop gros.
On peut utiliser du code Perl dans le fichier de configuration d'Apache (httpd.conf), dans des blocs <Perl>. À des fins de débugguage, on peut vouloir vérifier quelles sont les valeurs effectivement calculées par ces morceaux de code : les méthodes dump et store envoient ces valeurs sur la sortie standard ou dans un fichier.
Ce module permet de débugguer, quand on écrit des modules (qui vont servir de handlers) plutôt que des scripts isolés.
This module monitors hanging Apache/mod_perl processes. You define the time in seconds after which the process is to be counted as hanging or run away.
Remplacement de mod_status
# Configuration in httpd.conf <Location /sys-monitor> SetHandler perl-script PerlHandler Apache::VMonitor </Location> # startup file or <Perl> section: use Apache::VMonitor(); $Apache::VMonitor::Config{BLINKING} = 0; # Blinking is evil $Apache::VMonitor::Config{REFRESH} = 0; $Apache::VMonitor::Config{VERBOSE} = 0; $Apache::VMonitor::Config{SYSTEM} = 1; $Apache::VMonitor::Config{APACHE} = 1; $Apache::VMonitor::Config{PROCS} = 1; $Apache::VMonitor::Config{MOUNT} = 1; $Apache::VMonitor::Config{FS_USAGE} = 1; $Apache::VMonitor::Config{NETLOAD} = 1; @Apache::VMonitor::NETDEVS = qw(lo eth0); $Apache::VMonitor::PROC_REGEX = join "\|", qw(httpd mysql squid);
This module allows you to kill off Apache processes if they grow too large or if they share too little of their memory. You can choose to set up the process size limiter to check the process size on every request:
Normalement, tout ce qui est envoyé sur STDERR est perdu : ce module l'envoie dans un fichier.
PerlModule Apache::LogSTDERR HookStderr logs/stderr_log
Voir aussi CGI::Carp.
Ce module note les modules les plus utilisés. Il peut aussi regarder quels ont été les modules les plus utilisés récemment et les charger automatiquement.
Hooks for the interactive Perl debugger
Hooks for Devel::DProf
Hooks for Devel::SmallProf
Pour tester la montée en charge d'un serveur Apache/mod_perl.
On écrit d'une part des fichiers HTML, qui vont servir de moules, d'autre part du code en Perl, qui va servir) les remplir.
Voici par exemple une réécriture en Perl de
http://crookshanks.free.fr/
Voici le fichier principal, en Perl.
#! perl -w use strict; # use Carp (); # $SIG{__WARN__} = \&Carp::cluck; # $SIG{__DIE__} = \&Carp::confess; # $|++; use Date::Calc qw/Today_and_Now/; use HTML::Template; use CGI; my $query = new CGI; ############################################################ # Accès à la base de données use DBI; my $dbh = DBI->connect("dbi:Pg:dbname=zoonek", 'zoonek', '', { RaiseError => 1, AutoCommit => 1 }) or action_error("Cannot connect to db: $DBI::errstr"); # Schéma de la base my $table_name = "dvdhk"; sub tmpl_values { return [ map {{ VALUE => $_ }} @_ ] } my @schema = ( { field => 'titre', name => 'Titre du livre', type => 'TEXT', validate => \&validate_require, }, { field => 'auteur', name => 'Auteur(s)', type => 'TEXT' }, { field => 'editeur', name => 'Editeur', type => 'TEXTAREA' }, { field => 'genre', name => 'Genre', type => 'SELECT', values => tmpl_values(qw/roman theatre philosophie/), }, { field => 'date', name => 'Date', type => 'HIDDEN', value => sprintf("%s/%s/%s %s:%s:%s", Today_and_Now()), }, ); my @fields = map { $_->{field} } @schema; my $title_name = $fields[0]; my $field_names = join(', ', @fields); my $schema_tmpl = [ map { { NAME => $_->{field}, VALUE => $_->{value}, $_->{type} => 1, VALUES => $_->{values} || [], } } @schema ]; use Data::Dumper; print STDERR Dumper($schema_tmpl); ############################################################ # Propriétés que doivent vérifier les champs que l'on rajoute dans la base sub validate_any { } sub validate_require { print STDERR "Checking field $_[0] => '$_[1]'\n"; action_error("Le champ \"$_[0]\" est obligatoire") unless defined $_[1] and $_[1] !~ m/^\s*$/sm ; } # Requètes SQL sub get_five_last { my $sth = $dbh->prepare("SELECT $field_names FROM $table_name ORDER BY date DESC LIMIT 55"); $sth->execute(); return sql_to_tmpl($sth); } sub get_title_list { my $sth = $dbh->prepare("SELECT DISTINCT $title_name FROM $table_name ORDER BY $title_name"); $sth->execute(); my $result = []; while( my $row = $sth->fetchrow_arrayref ) { push @$result, { TITLE => $row->[0] }; } return $result; # Should look like this: # return [ { TITLE => 'foo' }, { TITLE => 'bar' }, { TITLE => 'baz' } ] ; } sub get_search_results { my $search = shift; my $sql = "SELECT $field_names FROM $table_name WHERE $title_name LIKE ? ORDER BY date DESC"; my $sth = $dbh->prepare($sql); $sth->execute( '%' . $search . '%' ); return sql_to_tmpl($sth); } sub add_entry { my $sth = $dbh->prepare("INSERT INTO $table_name ($field_names) VALUES (". join(',', map {"?"} @fields) .")"); $sth->execute(@_); } ############################################################ sub sql_to_tmpl { my $sth = shift; my $result = []; while( my $row = $sth->fetchrow_arrayref ) { my @h; my $i=0; foreach my $col (@schema) { push @h, { NAME => $col->{field}, VALUE => $row->[$i] }; $i++; } push @$result, { FICHE => \@h }; } return $result; # Should look like this: return [ { FICHE => [ { NAME => "titre", VALUE => "qjsdf" }, { NAME => "éditeur", VALUE => "ajhaf" }, { NAME => "auteur", VALUE => "lkfdf" }, ] }, { FICHE => [ { NAME => "titre", VALUE => "qjsdf" }, { NAME => "éditeur", VALUE => "ajhaf" }, { NAME => "auteur", VALUE => "lkfdf" }, ] }, { FICHE => [ { NAME => "titre", VALUE => "qjsdf" }, { NAME => "éditeur", VALUE => "ajhaf" }, { NAME => "auteur", VALUE => "lkfdf" }, ] }, ]; } # La liste des actions possibles et les fonctions correspondantes my %actions; my @actions = qw/derniers titres recherche formulaire envoi/; my $default_action = $actions[0]; { no strict; %actions = map { $_ => \&{"action_$_"} } @actions; } # Aiguillage selon la valeur du paramètre "action" foreach my $p ($query->param()){ print STDERR "param $p => ". $query->param($p) ."\n"; } my $act = $query->param('action') || $default_action; if( exists $actions{$act} ){ &{ $actions{$act} }(); } else { action_error("No such action: $act"); } # On vérifie que la page a bien été envoyée par POST et pas par GET. sub require_POST { action_error("POST-only page") unless $query->request_method eq "POST"; } # Une fonction pour afficher les pages sub print_template { print "Content-Type: text/html\n\n"; my $name = shift; my $template = HTML::Template->new(filename => $name); while(@_){ my $a = shift; my $b = shift; $template->param($a, $b); } print $template->output; } # La définition des différentes actions sub action_derniers { print_template('derniers.tmpl', FICHES => get_five_last()); } sub action_titres { print_template('titres.tmpl', LIST => get_title_list()); } sub action_recherche { my $search = $_[0] || $query->param("search"); my $res = get_search_results($search); print_template('recherche.tmpl', NUMBER_RESULTS => (scalar @$res), SEARCH => $search, FICHES => $res, ); } sub action_formulaire { print_template('formulaire.tmpl', SCHEMA => $schema_tmpl); } sub action_envoi { require_POST(); foreach my $e (@schema) { my ($v, $f) = ($e->{validate}, $e->{field}); print STDERR "about to check $f => '". $query->param($f) ."'\n"; &{ $v }( $f, $query->param($f) ) if $v; } add_entry(map { $query->param($_->{field}) } @schema); action_recherche($query->param($title_name)); } sub action_error { print_template('error.tmpl', MESSAGE => $_[0]); exit; }
Le fichier bas.tmpl :
(date) </body> </html>
Le fichier blabla.tmpl :
<p>Bla bla</p> <p><a href="dvdhk.pl?action=formulaire">Ajouter une nouvelle fiche</a></p> <p> <form method="GET" action="dvdhk.pl"> <input type="submit" value="Rechercher un titre"> <input type="text" name="search" size="60"> <input type="hidden" name="action" value="recherche"> </form> </p>
Le fichier css.tmpl :
<style type="text/css"> <!-- body, table.plain td, table.plain th { background: white; color: black; } a:link { color: #00f; background: transparent; } a:visited { color: #800080; background: transparent; } a:active { color: green; background: #FFD700; } th { font-weight: normal; text-align: left; vertical-align: top } table { background: white; color: black; empty-cells: show; } table td, table th { background: #DDDDFF; color: black; } --> </style>
Le fichier derniers.tmpl :
<TMPL_INCLUDE NAME="haut.tmpl"> <TMPL_INCLUDE NAME="blabla.tmpl"> <p>Voici les cinq dernières fiches :</p> <TMPL_INCLUDE NAME="fiches.tmpl"> <TMPL_INCLUDE NAME="bas.tmpl">
Le fichier error.tmpl :
<html> <head> <title>Erreur</title> <TMPL_INCLUDE NAME="css.tmpl"> </head> <body> <p> <TMPL_VAR NAME=MESSAGE> </p> </body> </html>
Le fichier fiches.tmpl :
<TMPL_LOOP NAME=FICHES> <table> <TMPL_LOOP NAME=FICHE> <tr> <th><TMPL_VAR NAME=NAME></th> <td><TMPL_VAR NAME=VALUE></td> </tr> </TMPL_LOOP> </table> </TMPL_LOOP>
Le fichier formulaire.tmpl :
<TMPL_INCLUDE NAME="haut.tmpl"> <form method="POST" action="dvdhk.pl"> <table class="plain"> <TMPL_LOOP NAME=SCHEMA> <tr> <td><TMPL_VAR NAME=NAME></td> <td> <TMPL_IF NAME=HIDDEN> <input type="hidden" name="<TMPL_VAR NAME>" value="<TMPL_VAR NAME=VALUE>" size=50> </TMPL_IF> <TMPL_IF NAME=TEXT> <input type="text" name="<TMPL_VAR NAME>" value="<TMPL_VAR NAME=VALUE>" size=50> </TMPL_IF> <TMPL_IF NAME=TEXTAREA> <textarea name="<TMPL_VAR NAME=NAME>" rows=1 cols=50> <TMPL_VAR NAME=VALUE> </textarea> </TMPL_IF> <TMPL_IF SELECT> <select name="<TMPL_VAR NAME>"> <TMPL_LOOP VALUES> <option value="<TMPL_VAR VALUE>"><TMPL_VAR VALUE></option> </TMPL_LOOP> </select> </TMPL_IF> </td> </tr> </TMPL_LOOP> <tr><td colspan=2><input type="submit" value="Ajouter la fiche"></td></tr> </table> <input type="hidden" name="action" value="envoi"> </form> <TMPL_INCLUDE NAME="bas.tmpl">
Le fichier haut.tmpl :
<html> <head> <title>DVDHK</title> <TMPL_INCLUDE NAME="css.tmpl"> </head> <body> <h1>DVDHK</h1>
Le fichier recherche.tmpl :
<TMPL_INCLUDE NAME="haut.tmpl"> <TMPL_INCLUDE NAME="blabla.tmpl"> <p>Il y a <TMPL_VAR NAME="NUMBER_RESULTS"> fiche(s) contenant `<TMPL_VAR NAME="SEARCH">' :</p> <TMPL_INCLUDE NAME="fiches.tmpl"> <TMPL_INCLUDE NAME="bas.tmpl">
Le fichier titres.tmpl :
<TMPL_INCLUDE NAME="haut.tmpl"> <TMPL_INCLUDE NAME="blabla.tmpl"> <p>Voici la liste des fiches :</p> <p> <TMPL_LOOP NAME=LIST> <TMPL_VAR NAME=TITLE> <br> </TMPL_LOOP> </p> <TMPL_INCLUDE NAME="bas.tmpl">
Seul le fichier formulaire.tmpl est peu lisible. Il faudrait le rendre plus lisible en ajoutant quelques lignes en Perl (dans le fichier principal). Voir aussi la FAQ 11 dans le manuel.
Ce module utilise le modèle de conception MVCC.
PageKit follows a Model/View/Content/Controller design pattern, which is an adaption of the Model/View/Controller pattern used in many other web frameworks, including Java's Webmacro and Struts.
Le contenu du site est dans des fichiers XML, qui seront convertis en HTML, WML ou PDF à l'aide de XSLT.
Le modèle (ou la « business logic »), ce sont les actions à faire lors des différentes requêtes, par exemple, ajouter un utilisateur, vérifier l'autentification d'un utilisateur, effectuer une recherche, vérifier le contenu des différents champs d'un formulaire, etc.
La vue est un ensemble de morceaux de HTML (début d'un tableau, etc.), dans lequel on peut utiliser des variables (exactement comme avec HTML::Template -- d'ailleurs, c'est HTML::Template). Il peut aussi s'agir de fichiers XSLT. Il peut y avoir plusieures vues : HTML avec plein d'images clignotantes pour empécher le client de lire le site, HTML imprimable, WML, PDF, etc.
http://pagekit.org/ http://take23.org/articles/2001/01/04/pagekit.xml/1
Le principe est le même que Zope : les pages sont constituées de composants qui peuvent être des morceaux de HTML ou des fonctions. À la différence de Zope, il n'y a pas de distinction très nette entre le code Perl et le HTML.
http://axkit.org/ http://www.perl.com/pub/a/2002/03/12/axkit.html http://www.perl.com/pub/a/2002/04/16/axkit.html http://www.perl.com/pub/a/2002/07/02/axkit.html http://www.perl.com/pub/a/2002/09/24/axkit.html
Permet de mettre du code Perl au milieu du HTML (exactement comme PHP, avec les mêmes défauts : le mélange du fond et de la forme).
À peu près comme Apache::ASP.
Comme EmbedPerl.
SOAP est l'un des protocoles sous-jacent aux services Web. Je rappelle que les services Web, c'est exactement comme les formulaires sur certains sites Web (recherche d'un livre sur Amazon, recherche d'un mot sur http://www.dict.org/ ), sauf que les services Web ne sont pas utilisés par des être humains, mais par d'autres machines. Ainsi, Google est aussi un service Web (il donnent même un exemple en Perl dans leur documentation, mais il faut bien chercher : alors que les exemples en Java ou C# occuppent des dizaines de fichiers, le code Perl (qui pourtant fait la même chose) est réduit à quatre malheureuses lignes perdues au milieu du fichier README...).
#! perl -w use strict; use SOAP::Lite service => 'http://services.xmethods.net/soap/urn:xmethods-delayed-quotes.wsdl', print getQuote('MSFT'), "\n";
Si on insiste, ça peut tenir en une seule ligne...
perl "-MSOAP::Lite service=>'file:./quote.wsdl'" -le "print getQuote('MSFT')"
Cela oblige à interpréter le fichier WSDL à chaque fois. On peut lui demander de le lire une première fois pour écrire un module qui contiendra tout ce qu'il faut. Par la suite, on se contentera de charger ce module.
perl stubmaker.pl http://www.xmethods.net/sd/StockQuoteService.wsdl #! perl -w use strict; use StockQuoteService ':all'; print getQuote('MSFT'), "\n";
Voir aussi
mod_soap. http://www.soaplite.com/ SOAP::Transport::*
(si on fait un serveur, il faut écrire le fichier WSDL à la main...)
Permet d'empiler les handlers (ils sont exécutés de droite à gauche).
<Files *.html> SetHandler perl-script PerlHandler Apache::OutputChain Apache::GzipChain Apache::PassFile </Files> <Files *.html> SetHandler perl-script PerlHandler Apache::OutputChain Apache::EmbperlChain Apache::SSIChain Apache::PassHtml </Files> Alias /foo /home/httpd/perl/foo <Location /foo> SetHandler "perl-script" Options +ExecCGI PerlHandler Apache::OutputChain Apache::GzipChain Apache::Registry </Location>
Voir plutôt Apache::Filter.
(Contrairement à Apache::OutputChain, les modules sont exécutés dans l'ordre dans lequel ils apparaissent.)
PerlModule Apache::Filter <Files ~ "*\.html"> SetHandler perl-script PerlSetVar Filter On PerlHandler Apache::Gzip </Files> PerlModule Apache::Filter Alias /home/http/perl /perl <Location /perl> SetHandler perl-script PerlSetVar Filter On PerlHandler Apache::RegistryFilter Apache::Gzip </Location> PerlModule Apache::Filter <Files ~ "*\.blah"> SetHandler perl-script PerlSetVar Filter On PerlHandler Filter1 Filter2 Apache::Gzip </Files>
Ce module permet de mélanger mod_perl et mod_include, i.e., d'inclure une page (ou n'importe quoi d'autre généré par le serveur) dans un script. (jamais utilisé)
#! perl -Tw use Apache::Include (); print "Content-type: text/html\n\n"; print "before include\n"; Apache::Include->virtual("/perl/env.pl"); print "after include\n";
Normalement, les modules ne sont chargés et compilés qu'une seule fois. Ce module demande qu'ils soient rechargés si jamais ils ont changés sur le disque.
# Dans httpd.conf PerlInitHandler Apache::StatINC
Cookies
Allow Easy, Consistent Access to Cookie and Form Data Across Each Request Phase
Permet d'avoir des modules de même nom à des endroits différents, par exemple une version de développement et une version de production (il y aura toujours une seule version du module en mémoire, et à chaque fois on regardera si c'est la bonne, sinon, on l'efface et on chargte la bonne).
PerlModule Apache::PerlVINC <Location /status-dev/perl> SetHandler perl-script PerlHandler Apache::Status PerlINC /home/httpd/dev/lib PerlFixupHandler Apache::PerlVINC PerlVersion Apache/Status.pm </Location> <Location /status/perl> SetHandler perl-script PerlHandler Apache::Status PerlINC /home/httpd/prod/lib PerlFixupHandler Apache::PerlVINC PerlVersion Apache/Status.pm </Location>
Quand on lance un processus (par exemple, avec la commande system) on s'attendrait à ce que la sortie standard soit envoyée au client : ce n'est pas le cas. Ce module rétablit le comportement attendu.
use Apache::SubProcess qw(system exec); ...
Authentification à l'aide de /etc/passwd (ou, plus précisément, getpwnam).
Comme Apache::AuthenPasswd, mais avec /etc/group.
Remplacement de mod_autoindex et mod_dir (quand il n'y a pas de fichier index.html).
Une « gateway », c'est comme un proxy, mais le client croit qu'il accède au site originel. On peut par exemple proposer un accès à CPAN en utilisant plusieurs miroirs de CPAN.
Pour informer les visiteurs que le site sera arrété pour maintenance dans quelques minutes.
block request based upon "Referer" header
Permet d'envoyer différentes choses au client selon la vitesse de sa connection, par exemple, des images (ou des sons) moins grosses.
Exemple de PerlLogHandler
mod_usertrack emulation
Benchmarking tool for Apache modules. See also some othe benchmarking tools:
ApacheBenchmark (ab) httperf http_load crashme (see perl.com)
J'aurais dû évoquer les modules suivants.
Data::FormValidator http://forum.swarthmore.edu/~ken/modules/Apache-AuthCookie/
J'avais initialement prévu d'évoquer aussi les modules suivants...
Apache::Embperl - Embed Perl code in HTML documents Apache::SSI - Implement server-side includes in Perl Apache::DBI - Maintain persistent DBI connections Apache::GzipChain - Compress output on the fly Apache::TransLDAP - Translate URIs via LDAP lookups Apache::ASP - Implement "Active Server Pages" Apache::AuthenDBI - Authenticate against a database via DBI Apache::PHLogin - Authenticate against a PH database Apache::SessionX Devel::Leak Devel::Symdump GTop (requires libgtop) Regexp MP3::Info Ogg::Vorbis XML::RSS IPC::Shareable CGI::Carp CGI::Application Apache::Session Data::FormValidator Apache::MP3 AxKit::XSP::Util Apache module that uses XML processing pipelines OpenInteract OpenInteract is an apache / mod_perl based application server that implements database connectivity and security through the SPOPS object framework. SPOPS (Simple Perl Object Persistence with Security) is an object-oriented application framework that provides object persistency and security services to your applications. XML::Parser::Expat (included with XML::Parser) MIME::Base64 URI (for HTTP/SMTP transport and autodispatch) HTTP::Daemon (for daemon server implementation, included with libwww-perl) Apache (for mod_perl server implementation, included with mod_perl) Net::POP3 and MIME::Parser (for POP3 server implementation) MIME::Lite (for SMTP client implementation) IO::File (for FTP client and IO server) Net::FTP (for FTP client) MQSeries (for MQSeries transport) Net::Jabber (for Jabber transport) FCGI (for FastCGI server implementation) IO::Socket::SSL (SSL support for TCP transport) MIME::Parser (for MIME attachment support) Compress::Zlib (for compression support) LWP::UserAgent (included with libwww-perl) HTTP::Request (included with libwww-perl) HTTP::Headers (included with libwww-perl) HTTP::Status (included with libwww-perl) Crypt::SSLeay (for HTTPS/SSL transfer) Wombat ?
Vincent Zoonekynd
<zoonek@math.jussieu.fr>
latest modification on jeu nov 14 08:44:32 CET 2002