In diesem Abschnitt werden wir uns mit der CGI-Schnittstelle beschäftigen. Wir werden dabei:
CGI (=Common Gateway Interface) ist eine Schnittstelle die es erlaubt, über das http-Protokoll, auf einem WWW-Server Programme auszuführen und sich das Ergebnis im Browser anzusehen. Eine typische Anwendung ist die Verarbeitung von Formularangaben aus html-Dokumenten mit denen WWW-Seiten zu Oberflächen von interaktiven Anwendungen für Warenbestellung oder Suchmaschinen werden.
Derzeit liegt die Definition der CGI-Schittstelle in der Version 1.1 vor. Historisch gesehen ist die CGI-Schnittstelle die älteste Schnittstelle zwischen externen Programmen und WWW-Server, mittlerweile stehen aber auch eine Reihe von Alternativen zur Verfügung.
Alternativen zu CGI umfassen zum einen die proprietäre Schnittstellen von kommerziellen Serverherstellern (Der Terminus "Server" ist in diesem Zusammenhang natürlich recht weitgefasst...). Zum Netscape-Server gehört die NSAPI-Schnittstelle, zum Microsoft IIS die ISAPI-Schnittstelle.
Eine weitere Klasse von Webserver/Anwendungsschnittstelle sind solche Schnittstellen die frei (und|oder) kostenlos zur Verfügung stehen, aber eine bestimmte Programmiersprache der Anwendung voraussetzen. Beispiele hierfür sind PHP oder die Java basierte Servlet-Schnittstelle.
Die CGI-Schnittstelle unterscheidet sich von derartigen Lösungen vor allem dadurch, dass sie lediglich festlegt wie Server und Anwendung miteinander kommunizieren, aber keine bestimmte Programmiersprache vorschreibt. Dies, und die Tatsache, daß es sich bei CGI um einen freien, kostenlosen, produktübergreifenden Standard handelt, ist eine Voraussetzung dafür dass CGI (z.B in Verbindung mit dem freien Webserver Apache) nach wie vor einen erheblichen Marktanteil und Entwicklungspotential aufweist, was sich in Entwicklungen wie "fastCGI" und "mod_perl" manifestiert, die des Gespann PERL-CGI-apache auch in der Performance konkurrenzfähig erhalten.
Eine CGI-Umgebung besteht aus einem HTTP-Server, der die Ausführung der CGI-Scripts anstößt, sowie den CGI-Programmen bzw Scripts. In den meisten Fällen werden nur solche Scripts als ausführbare CGI-Scripts erkannt, die in einem bestimmten Verzeichnis liegen. Oft erhält dieses Verzeichnis den Namen cgi-bin oder cgi-local. Bei einem Provider, der eine CGI-Schnittstelle eingerichtet hat, findet man das CGI-Verzeichnis i.A. bereits vor, und die Scripte brauchen nur noch per FTP in dieses Verzeichnis hochgeladen zu werden. Oft lassen sich Scripte auch nach dem Hochladen in das CGIDir eines Unix-basierten Webservers immer noch nicht ausführen. Der Grund sind fehlende Ausführungsrechte. Es empfiehlt sich dann die Dateirechte des Scriptes mit dem FTP-Kommando chmod auf 755 zu setzen. (Viele grafische FTP-Clients implementieren das chmod Kommando nicht. Solche Clients sind leider unbrauchbar wenn man sich nicht auf dem Server einloggen kann um das Chmod Kommando per Hand auszuführen.) Seltener findet man auch Server bei denen nur solche Scripts ausgeführt werden, die eine bestimmte Dateiendung haben.
Wie aber erfolgt nun die Datenübergabe vom Server zum CGI-Programm? Da der CGI-Standard keine Annahmen über die verwendetete Programmiersprache und das Serverbetriebssystem machen kann, bleiben nur noch die "Standardschnittstellen" Eingabeparameter, Environment und Standardeingabe/Standardausgabe für den Datenaustausch zwischen Server und CGI-Programm übrig. Da die Anzahl der Eingabeparameter auf den meisten Betriebssystemen beschränkt ist, bleiben am Ende nur Environment und Standardeingabe als Kommunikationskanal übrig. Der CGI-Jargon bezeichnet die Kommunikation über das Environment als GET-Request, die Kommunkation über STDIN als POST-Request.
Bevor man sich an einem CGI Script erfreuen kann, muss der Webserver (in unserem Fall der LINUX de-facto-Standard Apache) auch so konfiguriert werden, dass er das Script auch ausführt:
Zuallererst muss ein Apache mit CGI-Unterstützung installiert sein.
Die Kompilierung eines eigenen Apache mit CGI-Unterstützung ist
"straightforward". Man läd sich
die Quellen von
apache.org , und folgt
dann dem bekannten .configure; make; make install/ Schema.
Der Kompilationsvorgang an sich ist zwar interessant, aber bei der Benutzung
einer der "großen" Distributionen
(Debian, SUSE, Redhat) nicht notwendig,
weil hier die vorkonfigurierten Apache-Server CGI unterstützen.
Um CGI-Scripts ausführen zu können
Der Apache Server parst beim Start eine Hauptkonfigurationsdatei, und verzweigt, je nach den Direktiven in dieser Hauptkonfigurationsdatei in weitere optionale Konfigurationsdateien. Da der Apache sich auf sehr vielen exotischen Plattformen (z.B: Windows 9x) einsetzen lässt existiert eine GNU-typische Vielfalt, was die Namen und Lage dieser Konfigurationsdateien im Verzeichnisbaum betrifft:
Häufig werden drei Konfigurationsdateien mit unscharf abgegrenzter Zuständigkeit verwendet:
Es sei noch einmal betont das die Zuständigkeiten der einzelnen Dateien nur auf wackeliger Konvention beruhen. Jede Direktive kann in jeder Konfigurationsdatei liegen, und man kann Konfigurationsdateien mit selbst ausgedachten Phantasienamen verwenden. Die Konfigurationsdatei, die als erste geparst wird kann dem Server beim Start mitgeteilt oder aber fest in das binary einkompiliert werden.
Standardmäßig befindet ist die Hauptkonfigurationsdatei bei der SUSE Distribution die Datei "/etc/httpd/httpd.conf", "/etc/httpd/access.conf" und "/etc/httpd/srm.conf" sind leere Dummies. Bei der Redhat Distribution kommen alle drei Konfigurationsdateien zum Einsatz, sie befinden im Directory "/etc/httpd/conf/". Oft wird dem httpd-Prozess der Name der Hauptkonfigurationsdatei beim start mit der "-f" Option mitgeteilt. Anhand der Ausgabe des ps-Befehls:
ps -ax | grep httpd
z.B bei der SUSE-Distribution:
698 ? S 0:00 /usr/sbin/httpd -f /etc/httpd/httpd.conf -D PERL
kann man erkennen, dass die Hauptkonfigurationsdatei "httpd.conf" heißt, und sich im Verzeichnis "/etc/httpd" befindet.
Wie auch immer sich die Konfigurationsdatei nennen mag, die für die CGI-Ausfürung wichtigen Direktiven ändern sich dadurch erfreulicherweise nicht.
Wie jeder anständige Webserver ist der Apache modular aufgebaut. Eventuelle zusätzlich zu wenigen Grundfunktionen benötigte Funktionalität wird in "Shared Objects Libraries" ausgelagert, und nur bei Bedarf dynamisch geladen.
Die Ausführung von CGI-Scripts ist eine solche Zusatzfunktion. Um diese Zusatzfunktion nutzen zu können muss der Apache das CGI Module mod_cgi laden. Dies wird mit der "LoadModule" Directive angestoßen. Die Zeile:
LoadModule cgi_module /usr/lib/apache/mod_cgi.so
weist den Apatschen an das Modul "cgi_module",
respektive die entsprechende so-Datei /usr/lib/apache/mod_cgi.so
zu laden. Eine weitere Direktive ist die "AddModule" Direktive:
AddModule mod_cgi.c
Die Reihenfolge der AddModule Direktiven beeinflußt die Reihenfolge in der die einzelnen Module geladen werden. Die Reihenfolge ist wichtig, wenn ein Apache-Modul auf einem anderen Apache Modul aufbaut. Das CGI-Modul baut seinerseits auf zwei anderen standardmäßig vorhandenen Modulen auf: Dem Modul mod_env, das dafür sorgt dass der Aache auf Environment-Variablen des Betriebssystems zugreifen kann, sowie dem Modul mod_alias, das gebraucht wird um bestimmte Dateien als CGI-Script erkennbar zu machen.
Die Tatsache, dass der Apache mit cgi-unterstützung kompiliert wurde, und dass entsprechende Modul geladen ist reicht noch nicht aus, um CGI-Scripts auch tatsächlich zu starten. Weil CGI-Scripts von außen angestoßen werden können sind sie eine beliebte Möglichkeit Unfug mit einem Server zu treiben.
Mit einem schlecht oder bösartig geschriebenen PERL oder Shellscript lassen sich beliebige Aktionen auf der Servermaschine ausführen, und zwar unter der BenutzerID des Servers. Aus diesem Grund sollte der Apache niemals mit root-Rechten laufen. Die User ID des Apache lässt sich mit der Direktive "User" in den Konfigurationsdateien einstellen, z.B:
User wwwrun
Aber eben niemals:
User root
Aber auch ohne root-Rechte bleiben noch genug Möglichkeiten um die Prozessorlast hochzutreiben, den Memoryverbrauch durch wilde Rekursion exponentiell zu steigern, oder Massenmailings über den unschuldigen Webserver zu verschicken. Die vielfältigen Mißbrauchsmöglichkeiten sind auch dafür verantwortlich, dass kommerzielle Webspace-Anbieter sich die Möglichkeit eigene CGIs zu installieren vergleichsweise teuer bezahlen lassen.
Will man als Webmaster die installierten CGI-Scripts unter Kontrolle behalten, dann sollte man die Ausführung von CGIs auf eines oder wenige Directories beschränken. Dazu dient die "ScriptAlias" Direktive. Der Eintrag:
ScriptAlias /cgi-bin/ "/usr/local/httpd/cgi-bin/"
Deklariert das Verzeichnis /usr/local/httpd/cgi-bin/ als ein Script Directory, und bindet es
an den Alias "/cgi-bin/". Das bedeutet, das ausführbare Dateien in diesem Directory tatsächlich
ausgeführt werden. Die auf dem Server meinserver.com in
"/usr/local/http/cgi-bin" abgelegten Scripts können dann unter der URL
http://meinserver.com/cgi/bin/>dateiname<
Angestoßen werden.
Außer der Deklaration des Directories als Script Alias sollten auch noch gewisse Zugriffsmodalitäten explizit gesetzt werden. Dazu dient eine Directory-Direktive:
<Directory "/usr/local/httpd/cgi-bin">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from localhost penny.avci.priv *.innominate-training.de
Deny from all
</Directory>
Diese Zeilen habe die folgende Bedeutung:
"Order allow deny"
Wird zuerst auf Erlaubnis, dann auf Verbot geprüft.
Etwas flexibler als der ScriptAlias Mechanismus ist der CGI-Handler Mechanismus. Dabei werden in bestimmten Teilen des WWW-Baumes alle Dateien deren Dateinamen eine bestimmte Endung hat zu ausführbaren CGI-Scripts erklärt. Dies geschieht in zwei Schritten, die wir hier mit der allgemein für diesen Zweck üblichen Dateiendung ".cgi" demonstrieren:
AddHandler cgi-script .cgi
ExecCGI aktiviert sein.
Im folgenden Beispiel wird dies für den ganzen Teilbaum unterhalb von
"/usr/local/httpd/cgi-bin2" der CGI-Handler Mechanismus aktiviert, und
der Dateibaum als /cgi-bin2/ in den www-Baum eingehängt:
Alias /cgi-bin2/ "/usr/local/httpd/cgi-bin2/"
#
<Directory "/usr/local/httpd/cgi-bin2">
AllowOverride None
Options ExecCGI
Order allow,deny
Allow from all
</Directory>
Sehr mutige oder wahnsinnige Webmaster setzen ExecCGI für die Wurzel des WWW-Baumes. Andererseits lässt sich die ExecCGI Methode aber auch mit den Zugriffsmechanismen des Authentisierungsmodules mod_auth kombinieren, und erlaubt dann eine besonders ausgeklügelte Zugriffskontrolle über den Mechanismus der ".htaccess" Dateien.
Nach diesen Vorarbeiten ist die Zeit endlich reif für ein HelloWorld.cgi. Dafür legen wir zunächst einen neuen Benutzer "cgidev" (CGI-Develloper) mit Home-Verzeichnis "/home/cgidev" an. Die neu zu erstellenden CGI-Scripts sollen direkt in dessen Homedirectory erstellt und ausprobiert werden. Zur Abwechslung wählen wir nicht die ScriptAlias sondern die CGI-Handler Methode. Die Folgenden Schritte sind dazu notwendig:
chmod -R 755 /home/cgidev
lesbar und ausführbar gemacht werden.
AddHandler cgi-script .cgi
ein Handler-Eintrag in der "httpd.conf" angelegt werden."/home/cgidev"
unter dem Alias "cgi-bin2" in den WWW-Baum eingehängt,
und mit der Option ExecCGI die Ausführung der "*.cgi"
Dateien angeschaltet.
Da die ungetesten Scripts nicht von außen aufgerufen werden sollen
beschränken wir den Zugriff auf "localhost".
Die Folgenden Einträge in der "http.conf" sind dazu notwendig:
Alias /cgi-bin2/ "/home/cgidev/"
#
#
<Directory "/home/cgidev/">
AllowOverride None
Options ExecCGI
Order deny,allow
Allow from localhost
Deny from all
</Directory>
killall -HUP httpd
bringt den Server dazu seine Konfigurationsdateien neu einzulesen.
Jetzt kann es losgehen. Mit dem unserem Lieblingseditor legen wir eine Datei namens "helloworld.txt.cgi" mit folgendem Inhalt an:
#!/usr/bin/perl
print "Content-type: text/plain \n\n";
print "hello world !"
Um uns das Ergebnis anschließend mit dem Lieblingbrowser unter der URL
http://localhost/cgi-bin2/helloworld.txt.cgi
anzusehen....
Das textorientierte Hello World zeigt schon wie die Daten aus dem CGI-Script ihren Weg zum Browser des Surfers finden: Die ersten beiden Zeilen (der "Content-Header") werden abgetrennt und dienen zur Klassifikation der Daten. Der restliche Output wird einfach "roh" über die Socket-Verbindung zum Client geschickt.
Die erste Zeile Content-type: ....bezeichnet den MIME-Type der
übergebenen Daten. In diesem Beispiel simpler Text (text/plain).
Will man statt einer schlichtem Text eine HTML-Formatiertes Hello World
in geschmackvollem lila auf grün, dann geht das mit HTML-Output der als
MIME-Type "text/html" deklariert wird:
#!/usr/bin/perl
print "Content-type: text/html \n\n";
print "<header> <title> Hello World </title></header>";
print "<html>";
print "<body bgcolor=\"green\" text=\"magenta\">";
print "<h1>hello world !</h1>";
print "</body>";
print "</html>";
Die ersten Versuche ein eigenes CGI-Script zu auszuführen sind oft mit einigem Frust verbunden. Die übelste Fehlerquelle ist dabei der Content-Header. Der Content Header muss auch in Groß und Keinschreibung dem Muster:
Content-type: <contentype >
entsprechen. Die Zeile mit dem Content-type darf keine weiteren Zeichen enthalten,
und muss von einer Leerzeile gefolgt werden.
Eine Abweichung von diesem Muster rächt sich schnell mit der Fehlermeldung "Error 500",
über einen "Internal Server Error".
Im Fehlerfall sendet der Apache Fehlermeldungen an eine Logdatei, das ErrorLog.
Die Datei die standardmäßig die Fehlermeldungen aufnimmt wird in der httpd.conf mit
der ErrorLog Direktive konfiguriert. Beim vorkonfigurierten Webserver der SUSE-Distribution
ist das die Datei "/var/log/httpd/error.log", der entsprechende Eintrag in der httpd.conf
ist die Zeile:
ErrorLog /var/log/httpd/error_log
Beim Auftreten eines fehlerhaften Content-headers wird wird ein Eintrag der Form
[Sun Oct 29 11:05:48 2000] [error] [client 127.0.0.1] malformed header from script. Bad header=hello world !: /home/cgidev/helloworld.txt.cgi
in die Logdatei geschrieben. Andere Apache-Version beschwerden sich in diesem Fall auch gerne über ein:
"premature end of script-header"
Andere Fehler entstehen z.B. dadurch, dass der Code eines Scriptes Syntaxfehler enthält, wie hier ein fehlendes Semicolon in der 4ten Zeile
#!/usr/bin/perl -w
print "Content-type: text/html \n\n";
print "hello world "
print "tschuess !";
Weil dieses CGI-Script perl im Warnungsaktiven Modus aufruft, findet sich auch die vertraute Fehlermeldung des PERL Runtime Environments im ErrorLog wieder:
syntax error at /home/cgidev/helloworld.txt.cgi line 4, near "print"
Execution of /home/cgidev/helloworld.txt.cgi aborted due to compilation errors.
[Sun Oct 29 11:58:19 2000] [error] [client 127.0.0.1] Premature end of script headers:
/home/cgidev/helloworld.txt.cgi
Weil es für CGI-Scripts keine Standardfehlerausgabe gibt, spielt das Fehlerlogging für das Debugging von CGI-Scripts dieselbe Rolle wie stderr bei "normalen" Scripts.
Neben dem globalen Errorlog gibt es noch die Möglichkeit die Meldungen die bei der Ausführung
von CGI-Scripts generiert werden in eine eigene Logdatei (ScriptLog) umzuleiten.
Dies ist vorteilhaft, weil das Scriptlog von nicht-CGI-relevanten Fehlermeldungen entlastet wird,
und man das ScriptLog in sein eigenes Arbeitsverzeichnis umleiten kann. Allerdings muss man darauf achten,
dass der Webserver Schreibrechte auf das ScriptLog hat. Mit den folgenden Eintrag in der httpd.conf
wird das Script-Logging in die Datei "/home/cgidev/scriptlog" umgeleitet:
ScriptLog "/home/cgidev/scriptlog"
Das ScriptLog basierte Logging ist nur fürs Debugging gedacht. Das mod_CGI Manual warnt vor dem Einsatz des ScriptLog im Produktionsbetrieb, da das erweiterte Logging noch nicht hinreichend auf Sicherheitsprobleme getestet sei.
Die beiden gerade vorgestellten HelloWorld Scripts ermöglichen keinerlei Interaktion mit dem Anwender am anderen Ende des Internets, eine simple Text-- oder HTML-Datei die die Phrase "Hello World" enthält würde dasselbe leisten. Interessant wird CGI erst durch die Möglichkeit auf Eingaben des Benutzers zu reagieren. Die einfachste und verbreitetste Möglichkeit Benutzerdaten einzugeben sind HTML-Formulare. HTML unterstützt fast das ganze Bestiarium der "Widgets" moderner grafischer Benutzeroberflächen:
Wir werden im Folgenden nur Textfelder als Beispiele benutzen, eine umfassende Einführung in HTML, inklusive der Erstellung von HTML-Formularen enthält das frei downloadbare Dokument "selfhtml" http://www.teamone.de/selfhtml von Stefan Münz.
Um die Interaktion zwischen CGI-Script und Benutzereingabe zu demonstrieren, erstellen wir eine HTML-Seite, die zwei weitgehend identische Beispielformulare mit jeweils zwei Textfeldern enthält:
<html>
<head>
<title>Formular-Demo</title>
</head>
<body>
<h1> GET-Formular </h1>
<form action="http://localhost/cgi-bin2/formdemo.cgi" method=get>
<input name="einzeiligesTextfeld" size="20"> </input><br>
< textarea name="mehrzeiligesTextfeld" cols="20" rows="5">
Vordefinierter Text
</textarea>
<br>
<input type=submit value="abschicken">
</form>
<h1> Post-Formular </h1>
<form action="http://localhost/cgi-bin2/formdemo.cgi" method=post>
<input name="einzeiligesTextfeld" size="20"> </input><br>
<textarea name="mehrzeiligesTextfeld" cols="20" rows="5">
Vordefinierter Text
</textarea>
<br>
<input type=submit value="abschicken">
</form>
</body>
</html>
Beide Formulare enthalten dieselben Eingabeelemente:
<input name="einzeiligesTextfeld" size="20"> </input>
vordefinierter Text":
<textarea name="mehrzeiligesTextfeld" cols="20" rows="5">
Vordefinierter Text
</textarea>
<input type=submit value="abschicken">
Get und Post Formula unterscheiden sich nur durch die Deklaration der Request Methode
im einleitenden < form .... > Tag:
<form action="http://localhost/cgi-bin2/formdemo.cgi" method=get>
für das GET--, und
<form action="http://localhost/cgi-bin2/formdemo.cgi" method=post>
für das Form-Formular.
Um in allen Einzelheiten verfolgen zu können,
wie die Einträge in den Textfeldern an den Browser übermittelt werden,
installieren wir das Script formdemo.cgi nach /home/cgidev/.
Dieses Script listet einfach nur das Environment und (soweit vorhanden) die Parameter beim Aufruf des
Scriptes:
!/usr/bin/perl
read(STDIN, $Data, $ENV{'CONTENT_LENGTH'});
print "Content-type: text/plain\n\n";
print "\n\n E N V I R O N M E N T \:\n\n";
foreach ( keys %ENV){
print( "$_ \n" );
print( "$ENV{$_} \n" );
print "\n";
}
print "\n\n Eingabe über stdin: \n\n";
print "\n $Data";
print (\n);
Stoßen wir formdemo.cgi mit dem GET-Formular an, dann erhalten wir nur eine Liste des Environments, aber (wie erwartet) keine Daten auf der Standardeingabe. Von den vielen Environment-Variablen besprechen wir zunächst nur einen, den "QUERY_STRING". Die Scriptausgabe für den QUERY_STRING lautet:
QUERY_STRING
einzeiligesTextfeld=+wert+Nr+1&mehrzeiligesTextfeld=++%0D%0A+wqwert+Nr+1++
Wenn man sich das URL-Fenster des Browser ansieht, so sieht man wie der QUERY_STRING
seinen Weg zum Server macht: Der QUERY_STRING wird hinter ein Fragezeichen an den URL
des CGI-Scriptes angehängt:
http://localhost/cgi-bin2/formdemo.cgi?einzeiligesTextfeld=&mehrzeiligesTextfeld=++%0D%0A++++++Vordefinierter+Text%0D%0A+++++
Der QUERY STRING enthält die Eingabedaten in einer Name=Value Form.
Er besteht aus einzelnen, durch Ampersands ("&")
voneinander getrennte Teilstrings, die in kodierter Form den Namen und den Wert der verschiedenen
Eingabeelemente enthalten. Innerhalb dieser Teilstrings werden die Namen der einzelnen
Formularelemente von den enthaltenen Daten durch ein Gleichheitszeichen "=" getrennt.
Sowohl Namen als auch Werte der einzelnen Eingebelemente liegen nicht in "platten" Text
vor, sondern werden gemäß der RFC 2046 (MIME-Types) als MIME-Type
application/x-www-form-urlencoded.
codiert
Diese Codierung geschieht nach zwei relativ einfachen Regeln:
Beim POST-REQUEST sind die Daten nicht im QUERY_STRING sondern im eingefrorenen Eingabestrom enthalten, und nach denselben Regeln wie der Querystring codiert:
einzeiligesTextfeld=&mehrzeiligesTextfeld=++%0D%0A++++++Vordefinierter+Text%0D%0A+++++
Außerdem existiert noch die Variable "CONTENT-LENGTH", die benutzt wird um die Datenmenge in der Standardeingabe festzustellen (hier 86 Byte):
CONTENT_LENGTH
86
Beim POST-Request werden diese Daten nicht in der URL sondern separat an den Serverübertragen. Hier wurde der POST-Request mit dem Diagnosetool netcat "eingefangen":
POST /cgi-bin2/formdemo.cgi HTTP/1.0
Connection: Keep-Alive
User-Agent: Mozilla/4.74 [de] (X11; U; Linux 2.2.16 i586)
Host: localhost
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */*
Accept-Encoding: gzip
Accept-Language: en
Accept-Charset: iso-8859-1,*,utf-8
Content-type: application/x-www-form-urlencoded
Content-length: 92
einzeiligesTextfeld=blabla&mehrzeiligesTextfeld=++%0D%0A++++++Vordefinierter+Tex
t%0D%0A+++++
Hier noch eine Übersicht über die wichtigsten Variablen in der Ausgabe von "formdemo.cgi":
Mozilla/4.74 [de] (X11; U; Linux 2.2.16 i586)
und hier ein lynx auf derselben Maschine:
Lynx/2.8.3dev.9 libwww-FM/2.14/
imagegif, image/x-xbitmap, image/jpeg, image/pjpeg, image/png, */* /
/cgi-bin2/formdemo.cgi", nicht zu verwechseln
mit dem Dateinamen!
gzip. Etwas featuristischer gibt sich lynx:
gzip, compress
home/cgidev/formdemo.cgi/.
Man beachte den Unterschied zum Scriptname !;
penny.avci.com
iso-8859-1,*,utf-8
Nur in Ausnahmefällen wird ein Problem das man als Programmierer zu lösen hat völlig neu sein. Meistens ist das Problem schon von hunderten anderer Programmierern angegangen worden. Viele diese Programmierer sind (schon im Interesse der eigenen Qualitätssicherung) bereit ihre Lösungen frei zur Verfügung zu stellen. Typisches Beispiel für ein solches "Allerweltsproblem" ist das Parsen von CGI-Requests.
Für alles was mit PERL zu tun hat gibt es ein "Zentralarchiv, das CPAN"
CPAN steht für "Comprehensive PERL Archive Network". CPAN ist ein zentrales Archiv für PERL Software, das unter dem URL : http://perl.com/CPAN zu finden ist und von hunderten von sites gespiegelt wird (jedes Uni-Rechenzentrum mit etwas Selbstachtung spiegelt den CPAN).
Unter dem URL http://perl.com/CPAN/ (man beachte den "/" am Ende) befindet sich der CPAN-Autoforwarder der jeden Client zu einem Mirrorsite in seiner Nähe weiterleitet.
Der CPAN ist logisch strukturiert in die Abteilungen:
Die Navigation ist derartig intuitiv dass es hier nichts weiter zu erklären gibt, lediglich mit der Modul-Sektion (http://perl.com/CPAN/modules) werden wir uns noch etwas ausführlicher beschäftigen:
Die Wichtigste Informationsquelle ist die "lange Modulliste" (300 KByte) (http://perl.com/CPAN/modules/00modlist.long.html) von Tim Bunce und Andreas König. Die lange Modulliste enthält die Namen aller PERL-Module und eine einzeilige Kurzbeschreibung. Hat man nur eine vage Vorstellung davon was man braucht, dann kann man die Lange Modulliste nach Stichworten durchsuchen.
Die CPAN-Module liegen als "*.tar.gz" Pakete auf dem CPAN und sind nach Kategorien geordnet.
Zum Beispiel gehören die Archive "CGI.pm-2.43.tar.gz",
"CGI-FastTemplate-1.04.tar.gz",
und viele andere Archive zur Kategorie "CGI".
Das Dokument "all modules"
(http://perl.com/CPAN/modules/01modules.index.html)
liefert die Zuordnung von Archiven zu Kategorien.
Die "all modules" Liste enthält aus Platzggründen "nur" die Zuordnung von Kategorien zu "tar.gz" Archiven. Die Liste all modules (70 KByte) enthält die Liste aller Module innerhalb der "*.tar.gz" Archive.
"CPAN/modules/" enthält noch einige andere Dokumente die sich bei der Suche nach Modulen als nützlich erweisen können:
Last not least gibt es noch die Möglichkeit der Volltextsuche, und zwar :
Im Folgenden beschriben wir die Installation des CGI-Modules von
Lincoln Stein, das wir im folgenden Abschnitt etwas ausführlicher besprechen werden.
Neben der hier besprochenen manuelle Installation gibt es auch noch die Möglichkeit
die CPAN-Suche und denInstallationsvorgang mit dem Modulen
CPAN und CPAN::WAIT völlig zu automatisieren.
Zur manuellen Installation müssen wir uns zuerst das aktuelle Archiv
(hier CGI.pm-2.74.tar.gz ) vom CPAN beschaffen.
Das Archiv wird ausgepackt, z.B mit dem Befehl:
tar -xzvf CGI.pm-2.74.tar.gz
dabei sollte ein Verzeichnis "CGI.pm-2.74" entstanden sein,
in das wir jetzt wechseln:
cd CGI.pm-2.74
Der ls-Befehl liefert einen Überblick über den Verzeichnisinhalt.
Man beachte insbsondere die hervorragende Dokumentation, und die Beispiele im
Unterverzeichnis "examples":
ANNOUNCE Changes Makefile.PL cgi-lib_porting.html pm_to_blib
CGI MANIFEST README cgi_docs.html t
CGI.pm blib examples
Das Modul muss eventuell noch an die lokalen Gegebenheiten angepasst werden. Dazu wird zunächst mit
perl Makefile.PL
ein Makefile erzeugt. (Bei einer Standard SuSE -Distribution scheint es da allerdings nicht viel zu tun zu geben: )
checking if your kit is complete...
Looks good
Writing Makefile for CGI
Jetzt geht es weiter mit dem Anpassen :
make
und testen
make test
der Konfiguration. Schließlich wird CGI.pm mit dem Kommando make installinstalliert.
Die Standardinstallation erfordert Systemverwalterrechte, die wir uns her mit dem
Kommando su -c "...." ad-hoc verschaffen:
su -c " make install";
komplettiert die Installation.
Vielleicht das populärste PERL-Modul überhaupt ist das "CGI" Modul von Lincoln Stein. "CGI" bietet eine komfortable objektorientierte Schnittstelle um CGI-Requests zu parsen, und die entsprechenden Antwortdokumente zu generieren. Aus der Vielfalt der Funktionen können wir hier nur die elementarsten ansprechen. Aus Platzgründen verzichten wir auf die fortgeschrittenen Features wie:
Auch das die Tatsache, dass man mit CGI.pm auch nicht-objektorientiert programmieren kann wird aus ideologischen Gründen verschwiegen.
Zunächst ein einfaches CGI-Script zur Demonstration der Parserfunktionen:
#!/usr/bin/perl
#
use CGI;
#
$query = CGI->new();
#
print "Content-Type : text/plain \n\n";
#
@parnames=$query->param() ;
foreach(@parnames){
print ("$_ : " ,$query->param($_) ,"\n") ;
}
Nach dem Einbinden des CGI-Modules wird zuerst mit
$query=CGI->new();
Ein neues CGI-Objekt $query erzeugt.
Zuerst untersucht das neue Objekt das Environment
um festzustellen, ob es seine Erschaffung einem GET oder einem POST Request
verdankt. Je nach Request-Typ wird anschließend die Standardeingabe
oder der QUERY_STRING eingelesen, in Name-Wert Paare zerlegt, und
dekodiert. Die Name Wert-Paare werden in einem Hash des CGI-Objektes
abgespeichert, und man kann mit der param-Funktion komfortabel
darauf zugreifen. Der Aufruf object->param('name') liefert
den Wert des Parameters mit Namen 'name', der Aufruf der
param() ohne Parameter liefert eine Liste aller Parameter.
Man muss sich bei der Benutzung des CGI-Modules also weder um die
Art des Requests noch um die Dekodierung der Parameter kümmern:
Ein Aufruf von cgidemo1.cgi mit der URL:
http://localhost/cgidemo1.cgi?einzeiligesTextfeld=+Wert+&merzeiligesTextfeld=+anderer+Wert+
liefert genauso eine Liste der Parameter-Wert Paare wie ein Aufruf über die Formulare des vorangegangenen Abschnittes.
Zur Unterstützung von HTML formatiertem Output bietet CGI.pm die Möglichkeit den Content-Header und viele HTML Tags über Objektmethoden anzusprechen. Hier als Beispiel eine Version der Parameterliste, die die Formatierung von Content-Header und HTML-Header dem CGI-Modul überlässt (cgidemo2.cgi):
#!/usr/bin/perl
#
use CGI;
#
$query = CGI->new();
#
print $query->header( {-type=>'text/html'});
print $query->start_html(-title=>'Parameterliste',
-author=>'webmaster@localhost',
-base=>'true',
-target=>'_blank',
-meta=>{'keywords'=>'Parameter Liste Formulare ',
'copyright'=>'copyright 2000 schevolution.com'},
-BGCOLOR=>'blue');
print "<h1> Die Parameterliste </h1>"
print "<table>";
@parnames=$query->param() ;
foreach(@parnames){
print ("<tr> <th>$_ </th> <td>" ,$query->param($_),"</td></tr>\n") ;
}
print "</table>"
print("</body>");
Der gigantische Funtionsumfang des CGI-Modules konnte in diesem Abschnitt
natürlich nur angeschniten werden. Ist CGI.pm erst einmal installiert,
dann kann man sich mit perldoc CGI einen Überblick verschaffen.
Die CPAN-Distribution bringt eine ausführliche HTML-Dokumentation und
viele Beispielscripte mit. Mehr über CGI.pm erfährt man auch
auf der Homepage, bzw der Downloadseite:
Eines der ewigen Probleme in der Anwendungsentwicklung ist die Trennung von Anwendungslogik und Benutzerschnittstelle. Je vollständiger diese Trennung ausfällt, umso einfacher ist es nachher die Software an andere Benutzerkontexte, z.B: verschiedenen Sprachen, Browserspezifische Erweiterungen... anzupassen. Das CPAN-Modul CGI::Fast Templates von Jason More benutzt Templates (Schablonen) um die Trennung von Anwenderschnittstelle und Anwendungslogik durchzuführen. Templates sind Textdateien die statischen Text (typischwerweise HTML-Formatierungsanweisungen) und Platzhalter für Variablen enthalten. Um ein Dokument zu erzeugen füllt die Anwendungslogik die Platzhalter mit Werten, und sendet das so erzeugte Dokument an den Client.
In diesem Abschnitt werden wir CGI-Scripts vorstellen, die keinen HTML-Code mehr enthalten, sondern die Darstellung komplett an Templates delegieren. Bei allem Respekt vor der Zunft der Webdesigner kommen wir nicht umhin den damit verbunden Fortschritt mit einem Zitat aus der POD-Dokumentation von CGI::FastTemplates zu würdigen:
"By putting all of your HTML in separate template files, you can let a graphic or interface designer change the look of your application without having to bug you, or let them muck around in your perl code."
Das Erzeugen eines Dokumentes aus Schablonen erfolgt in 4 Schritten:
Ein Template wird nicht mit
seinem vollen Dateinamen sondern mit einem "logischen"
Namen angesprochen. Die "define"-Methode legt einen Hash an, der
den Dateinamen an den logischen Templatenamen bindet.
Dieser Schritt, der zuerst wie eine unnötige
Komplikation wirkt erweist sich in der Praxis als sehr
praktisch. muss man z.B. ein Dokument
je nach Sprache des Clients in Deutsch oder Englisch darstellen,
dann legt man zwei Templatedateien "error.de.tpl" und "error.en.tpl"
ab. Wird die Anwendungslogik angeworfen, dann wird zuerst.
HTTP_ACCEPT_LANGUAGE ausgewertet, und in Abhängigkeit
vom Ergebnis entweder "error.de.tpl" oder "error.en.tpl"
an den logischen Namen "error" gebunden.
Mit dem Template "error" wird dann die Meldung erzeugt.
Mit assign werden den Variablen in den Templates Werte zugewiesen. In bei der einfachen Version der assign-Methode assign(%hash) bekommt ein assign einen Hash der Form:
assign( var_1 => "wert_1", var_2 => "wert_2",....,var_n=>"wert_n")
als Argument. Performanter ist die zweite Variante: assign($hashref), die eine Hashreferenz als Argument erhält. In vielen Anwendungsfällen (Datenbankabfrage!) liegen die Daten die zum Ausfüllen des Templates gebraucht werden nämlich schon als Hash vor, und man kann die Zeit für das Erstellen einer "tiefen" Kopie des Hashes sparen.
die parse Funktion leistet die Hauptarbeit beim Erstellen eines Dokuments. Die parse-Funktion bekommt als Argument einen Hash. Die Keys in diesem Hash sind die zu expandierenden Variablen, die Values sind die (schon expandierten) Templates, die die Werte der zu expandierenden Variablen enthalten. Es gibt 3 verschiedene Möglichkeiten die Parse-Funktion aufzurufen:
parse(A=>"B");
Dabei werden alle Variablen im Template B expandiert,
und das "ausgefüllte" Template in der Variablen "A" abgelegt.
parse (B=>"C");
parse (A=>"B");
die "compound" Variante vereinfacht die beiden Aufrufe zu
einem einzigen Aufruf:
parse {A=>["B","C"]};
ob das übersichtlicher ist sei mal dahingestellt...
parse(B=>."A");
Bei dieser Variante wird B nicht durch das geparste Resultat
von A überschrieben, sondern A wird an B angehängt.
lässt sich gut für Tabellen oder Listen
mit einer a priori unbekannten Zahl von Zeilen/Elementen verwenden.
"print" schreibt den Inhalt des skalaren Paramters nach standardout. Das Argument kann auch weggelassen werden, dann wird der Inhalt der zuletzt geparsten Variable geschrieben.
Als erstes, einfaches Beispiel ein Script das je nach dem Inhalt der Variablen
"HTTP_ACCEPT_LANG" ein deutsches oder englisches Dokument generiert.
Die Templates sind simple HTML-Dateien, es müssen keine Variablen expandiert
werden. Hier das Template für die deutsche Ausgabe:
<!-- NAME: lang.de.tpl -->
<html>
<head></head>
<body bgcolor="white"> Wir sprechen Deutsch </body>
</html>
<!-- END: lang.de.tpl -->
Dieses Template wird unter "/home/cgidev/templates/lang.de.tpl"
abgelegt. Die englische Variante:
<!-- NAME: lang.en.tpl -->
<html> <head> </head>
<body bgcolor="white"> English spoken </body>
</html>
<!-- END: lang.en.tpl -->
liegt unter "/home/cgidev/templates/lang.de.tpl".
Aufgerufen wird das Template von der Anwendung "lang.cgi", der "assign" Schritt enfällt, weil die Templates trivialerweise keine Variablen enthalten, die man belegen könnte:
#!/usr/bin/perl
CGI::FastTemplate;
# neues FastTemplate Objekt erzeugen
# die Templates liegen in:"/home/cgidev/templates"
my $tpl = new CGI::FastTemplate("/home/cgidev/templates");
#
# die Sprache abfragen..
$lang =$ENV{ 'HTTP_ACCEPT_LANGUAGE' };
#
print "Content-Type: text/html \n\n";
#
# Der Inhalt von $lang entscheidet welche Datei für
# das Template "msg" geladen wird...
if($lang=~ m/\s*de/){
$tpl->define( msg => "lang.de.tpl");
}
#
else{
$tpl->define( msg => "lang.en.tpl");
}
#
# msg wird nach "out" geparsed
$tpl->parse(out=>"msg");
#
# Ausgabe der Seite...
$tpl->print(out);
Zum Abschluß noch ein komplexeres Beispiel, das Templates inneinanderschachtelt und ein Template aus einer Schleife heraus aufruft.
Es soll eine Tabelle des Environments erstellt werden, wobei für jede Variable eine Tabellenzeile der Form
<tr> <th> Name <th> <td> Wert </td> </tr>
angezeigt werden soll. Das zugehörige Template (row.tpl) sieht so aus:
<!-- NAME: row.tpl -->
<tr>
<td>$NAME</td>
<td>$VALUE</td>
</tr>
<!-- END: row.tpl -->
Die Tabellenzeilen werden in einer Schleife expandiert, und an eine Variable
"$ROWS" angehängt, wobei die append-Variante der "parse" Funktion
verwendet wird:
foreach (keys(%ENV))
{
$tpl->assign( NAME => $_,
VALUE => $ENV{"$_"}
);
$tpl->parse(ROWS => ".row");
}
Anschließend wird die Tabelle aufgebaut. Das Template für die Tabelle enthält nur die Tags für Beginn und Ende der Tabelle, der Inhalt der Tabelle werden aus der Variablen "$ROWS" übernommen.
<!-- NAME: table.tpl -->
<table border=3>
$ROWS
</table>
<!-- END: table.tpl -->
Das ganze Dokument entsteht durch Ausfüllen der Variable
"$MAIN" im Template "main.tpl mit dem expandierten Template "table":
<!-- NAME: main.tpl -->
<html>
<head>
</head>
<body bgcolor="white">
$MAIN
</body>
</html>
<!-- END: main.tpl -->
Tabelle und Dokument werden in einem Schritt erzeugt, wobei die "compound" Variante der parse-Funktion zur Anwendung kommt:
$tpl->parse(MAIN => ["table", "main"]);
Anschließend wird das Dokument mit tpl->print(MAIN);
ausgegeben.Hier noch einmal das ganze Script "fasttemplatedemo.cgi":
!/usr/bin/perl
#
use CGI::FastTemplate;
#
my $tpl = new CGI::FastTemplate("/home/cgidev/templates");
#
$tpl->define( main => "main.tpl",
table => "table.tpl",
row => "row.tpl",
);
#
# Titel setzen
#
$tpl->assign(TITLE => "Environment");
#
# Tabelleninhalt erzeugen
#
foreach (keys(%ENV))
{
$tpl->assign( NAME => $_,
VALUE => $ENV{"$_"}
);
$tpl->parse(ROWS => ".row");
}
#
# Tabelle und Dokument in einem Schritt erzeugen
#
$tpl->parse(MAIN => ["table", "main"]);
#
# und ausgeben...
#
print "Content-Type: text/html \n\n";
$tpl->print();
## END ##
Aus didaktischen Gründen wurde in diesem Abschnitt viel Aufwand betrieben, um Ergebnisse zu erzielen die man mit wenig hartverdrahtetem PERL-Code genauso hingekriegt hätte. Jason More meint dazu:
If you're thinking you could have done the same thing in a few lines of plain perl, well yes you probably could. But, how would a graphic designer tweak the resulting HTML? How would you have a designer editing the HTML while you're editing another part of the code? How would you save the output to a file, or pipe it to another application (e.g. sendmail)? How would you make your application multi-lingual? How would you build an application that has options for high graphics, or text- only? FastTemplate really starts to shine when you are building mid to large scale web applications, simply because it begins to separate the application's generic logic from the specific implementation.
Die Verwendung von PERL und CGI überzeugte zunächst eher durch Flexibilität als durch Performance. Das Hauptproblem ist, dass bei jedem Aufruf eines CGIScriptes das Script zuerst ins Memory kompiliert wird bevor es ausgeführt werden kann.
Dieses Verhalten kann sich zu einem ernsten Performanceproblem auswachsen, wenn ein Webserver mit Anfragen bombardiert wird. Die Lösung des Problems liegt auf der Hand: Das Script sollte nur einmal kompiliert, und dann bei jedem Request mit neuen Parametern ausgeführt werden.
Die Durchführung dieses Ansatzes wird durch das Apache-Modul mod_perl realisiert. Dieses Modul stellt dem Apache-Server die Funktionen der PERL-Runtime Library zur Verfügung, und erzielt so gleich einen doppelten Nutzen:
Wir werden uns hier nur unter dem Aspekt der beschleunigten CGI-Ausführung mit mod_perl beschäftigen.
Um mod_perl zu benutzen muss zunächst PERL installiert sein. Dann wird ein Apache mit PERL-Unterstützung kompiliert indem bei der Konfiguration nebst allen anderen Optionen (unter FILE) die Lage des PERL-Runtime Environments im Dateisystem angegeben wird:
./configure ......... --with-perl=FILE...
Die mod_perl Quellen kann man als tarball von der Apache-Perl Homepage http://perl.apache.com herunterladen. Mit der üblichen Dreifaltigkeit:
perl Makefile.PL & make & make install
Wird mod_perl installiert. Dabei werden die runtime library
"usr/lib/apache/libperl.so /"
und auch einige PERL-Module im Namespace Apache::* installiert. Letztere
kapseln den Zugriff auf die Apache API.
Die vorkonfigurierten Apache Server der SUSE und der Redhat-Distribution erlauben die
Nachinstallation von mod_perl ohne Neukompilation (sogar ohne Neustart des Servers ).
Die httpd.conf muss Loadmodule und AddModule Anweisungen für das Laden des PERL-Modules enthalten:
LoadModule perl_module /usr/lib/apache/libperl.so
:
:
AddModule mod_perl.c
:
Mit der Direktive "Perlrequire" wird ein Script
angegeben, das einige Initialisierungsarbeiten beim Start des Servers,
bzw beim Start des mod_perl Apparates durchführt.
Dieses Script muss , ähnlich wie Modulcode, nach der Ausführung
ein "true" zurückliefern. Hier wird das mit mod_perl
ausgelieferte Startscript verwendet:
Perlrequire /usr/include/apache/modules/perl/startup.perl
Als Nächstes wird eine Handlermethode für Perl-Scripts definiert. Das ist die Routine die bei jedem Request aufgerufen wird, und die Kompilation und Ausführung eines Scriptes übernimmt. Zur Definition des Perl-Handlers kann man entweder eine Routine explizit angeben, zum Beispiel:
AddHandler perl-script .pl
SetHandler perl-script
PerlHandler Apache::Registry::handler
Oder aber man gibt nur den Namen eines Modules an:
AddHandler perl-script .pl
SetHandler perl-script
PerlHandler Apache::Registry
Das Modul wird dann nach einer Subroutine mit Namen "handler" durchsucht.
Wenn man mit mod_perl vor allen Dingen die Ausführung schon vorhandener CGI-Scripte
beschleunigen will, dann wirkt es sich störend aus, dass mod_perl
per Voreinstellung eigene Header erzeugt, so dass es zu "doppelten Headern" kommt.
Doppelte Header kann man mit der Direktive
PerlSendHeader On
vermeiden. (Das "On" erklärt sich dadurch, dass das Senden des Headers von mod_perl an den Code deligiert wird.)
Eine andere Eigenwilligkeit, die beim kommerziellen Einsatz erwünscht aber während
der Debug-Phase unerwünscht ist, ist das Verhalten von mod_perl
beim Server-Reload. Die im Memory vorkompilierten Scripts werden nicht gelöscht(!).
sondern bleiben im Memory, so dass sich Konfigurationsänderungen oft nicht auswirken.
Erst das Einfügen der Direktive
PerlFreshRestart On
bewirkt einen kompletten Reload des mod_perl Apparates beim Server-Reload.
Mit der "Files"-Direktive lässt sich auch die Ausführung von PERL-Code auf der Basis der Dateiendung (*.pl) im gesammten WWW-Baum realisieren:
<Files *.pl>
SetHandler perl-script
PerlHandler Apache::Registry
Options ExecCGI
</Files>
Wie kann man jetzt herausfinden ob die Kompilation ins Memory auch wirklich funktioniert? Wenn das Apache-Standardmodul "mod_status" geladen ist, was man an den Zeilen
LoadModule status_module /usr/lib/apache/mod_status.so
AddModule mod_status.c
In der httpd.conf erkennt, dann kann man mit der folgenden "Location" Direktive die Anzeige einer ausführlichen Statusmeldung freischalten.
<Location /perl-status>
SetHandler perl-script
PerlHandler Apache::Status
order deny,allow
deny from all
allow from localhost
</Location>
Die Statusmeldung lässt sich mit dem Browser unter
http://localhost/perl-status aufrufen.
Mit dem Shellscript (testmodperl.sh) kann man den Performancegewinn
auch messen. Das Shellscript nimmt einen URL als Parameter, lädt das entsprechende Dokument
10x herunter (mit lynx im nichtinteraktiven Modus) und zeigt dann die benötigte Zeit in
Sekunden an. Der Aufruf von cgidemo1.cgi mit mod_perl Unterstützung sollte etwa
4-6 mal schneller als der Aufruf ohne mod-perl Unterstützung sein.
#!/bin/sh
A=$( date +%s )
COUNT=0
while expr $COUNT \< 10 > /dev/null
do
lynx --dump $1 > /dev/null
COUNT=$( expr $COUNT \+ 1 )
done
B=$( date +%s )
expr $B - $A
mod_perl Programmiertricks
Die Möglichkeiten von mod_perl sind mit der simplen Beschleunigung von CGI noch nicht ausgereizt. Scripts bei denen man von vorneherein davon ausgehen kann, das sie unter mod_perl laufen sollen können noch in anderer Weise optimiert werden. Wird zum Beispiel aus einem CGI-Script heraus auf eine Datenbank zugegriffen, dann wird bei "normalem" CGI bei jedem Request eine neue Datenbankverbindung aufgebaut werden müssen.Was Zeit, und bei mehreren gleichzeitigen Datenbankverbindungen, vor allem teure Clientlizenzen kostet. Da bei mod_perl ständig ein Perl Prozess läft, ist es bei dieser Variante nur noch einmal notwendig die Datenbankverbindung zu öffnen. Viele der im WWW und in Newsgroups vorhandenen Datenbank-Benchmarks die die Zugriffsgeschwindigkeit von PERL,ASP,JSP,PHP.....usw vergleichen berücksichtigen dies nicht !
Die Migration von CGI nach mod_perl kann aber auch einige Probleme aufwerfen.
Die Online-Dokumentation liefert einen Guten Einstieg in die Problematik. Besonders
empfohlen seien die POD-Pages zur CGI->mod_perl Migration (perldoc cgi_to_mod_perl),
die Dokumentation zu "mod_perl Fallen"
(perldoc mod_perl_traps) und natürlich die eigentliche mod_perl Doku
(perldoc mod_perl).