mod_perl]
www.farid-hajji.net banner

Farid Hajji

Perl: Einführung, Anwendungen, Referenz (2/e) [Support-Site]

Farid Hajji: Perl - Einführung, Anwendungen, Referenz
2., aktualisierte und erweiterte Auflage
Addison-Wesley Longman, ISBN 3-8273-1535-2

Einführung in mod_perl

Inhalt dieser Seite
Motivation

Es war aus Platzgründen nicht mehr möglich, im Buch ausführlich auf mod_perl einzugehen. Dies holen wir auf dieser Seite nun nach.

Ein wesentlicher Nachteil von CGI ist der Overhead, der mit dem Starten eines externen Prozesses verbunden ist. Jeder Aufruf eines Perl-basieren CGI-Skriptes geht also einher mit einem zeitaufwendigen fork(), gefolgt vom Laden des Perl-Interpreters, welcher wiederrum die Datei mit dem Perl-Code lesen muß. Der Aufruf von CGI-Skripten ist also mit einer gewissen Latenz verbunden.

Ein weiterer Nachteil von CGI tritt zutage im Zusammenhang mit Datenbanken. Wenn ein CGI-Skript eine Information aus einer DBI-Datenbank benötigt, muß es sich erst beim Datenbankserver anmelden und kann erst dann seine SQL-Befehle dorthin abschicken. Leider ist die Anmeldung an einem Datenbanksystem eine zeitaufwendige Angelegenheit. Dies vergrößert die Latenz solcher CGI-Skripten noch mehr.

Ein Ausweg aus diesem Dilemma wurde mit mod_perl gefunden. Hier wurden der Apache-Webserver und der Perl-Interpreter zu einem einzigen Programm verschmolzen. Diese Technik wurde ja auch in Kapitel 16 vorgeführt. Mit Hilfe von mod_perl ist es nun möglich, den so erweiterten Apache-Webserver zur direkten Ausführung von Perl-Code zu bewegen.

Mit mod_perl werden nun Perl-basierte CGI-Skripten nicht mehr von einem externen Prozeß ausgeführt, sondern direkt von einem der httpd-Prozesse des Webservers. Dies hat mehrere Vorteile:

Diese Seite zeigt, wie mod_perl am besten eingesetzt werden kann. Zu mod_perl gibt es eine hervorragende Referenz, die Sie unbedingt benötigen, wenn Sie sich ernsthaft mit der Thematik befassen wollen:

Lincoln Stein and Doug MacEachern:
Writing Apache Modules with Perl and C.
O'Reilly Nutshell-Book
ISBN 1-56592-567-X, © 1999-2003

Dieses O'Reilly-Buch hat auch eine Homepage unter http://www.modperl.com/.

Installation und Konfiguration

Die Installation von mod_perl und seine Konfiguration wurden bereits im Buch vorgestellt. Für alle, die das Buch nicht haben, wird hier nochmal das Wesentliche zusammengefaßt:

Schnellere CGI-Ausführung mit Apache::PerlRun

Die einfachste Verwendung von mod_perl ist der Einsatz von bereits existierenden CGI-Skripten unter Apache::PerlRun. Versuchen wir es einfach!

root@sun-1:/> mkdir ~www/perl-run

Nun tragen wir in ~www/conf/perl.conf folgendes ein:

Alias /perl-run/ /usr/local/apache/perl-run/
<Location /perl-run>
    SetHandler  perl-script  
    PerlHandler Apache::PerlRun
    Options     +ExecCGI
</Location>

Nun sollten wir den Webserver neu starten:

root@sun-1:/usr/local/apache/conf> /etc/init.d/apachectl restart
/etc/init.d/apachectl restart: httpd restarted

Kopieren wir nun ein CGI-Skript in ~www/perl-run und probieren wir es aus:

root@sun-1:/usr/local/apache/conf> cp ~www/cgi-bin/cgi-printenv.pl ~www/perl-run

Nun rufen wir in unserem Browser folgende URL auf:

http://sun-1.meta.net/perl-run/cgi-printenv.pl

Am Anfang dauert der Aufruf des CGI-Skriptes genauso lange wie unter cgi-bin. Drücken Sie nun den Reload-Button Ihres Browsers einige Male. Sie werden dann sehen, daß nach einigen Versuchen das Skript deutlich schneller startet. Der Geschwindigkeitsunterschied ist deutlich sichtbar. Hier wurden die unveränderten CGI-Skripten direkt vom in den httpd Prozessen eingebauten Perl-Interpreter ausgeführt. Der mit dem Starten eines weiteren Prozesses und dem Aufruf von perl verbundenen Overhead entfällt.

Haben Sie auch den Fehlerlog /var/local/apache/logs/error_log im oben genannten xterm im Auge behalten? Sie werden hier einige Fehler sehen, die früher nicht aufgetaucht sind. Beispielsweise entsteht bei der URL

http://sun-1.meta.net/cgi-bin/cgi-printenv.pl

keine Fehlermeldung, während bei

http://sun-1.meta.net/perl-run/cgi-printenv.pl

folgende Fehlermeldung bei jedem Aufruf getriggert wird:

[Wed Nov  3 11:44:35 1999] cgi-printenv.pl: Use of uninitialized value
                       at /usr/local/lib/perl5/5.00553/CGI.pm line 456.

Bei weiteren Skripten können noch weitere Fehler gemeldet werden. Bei counter3.pl wird ein ganze Reihe von Fehlermeldungen angezeigt:

[Wed Nov  3 11:49:38 1999] counter3.pl: Constant subroutine DB_DSN redefined
                        at /usr/local/lib/perl5/5.00553/constant.pm line 175.
[Wed Nov  3 11:49:38 1999] counter3.pl: Use of uninitialized value
                        at /usr/local/lib/perl5/5.00553/constant.pm line 175.
[Wed Nov  3 11:49:38 1999] counter3.pl: Constant subroutine DB_USER redefined
                        at /usr/local/lib/perl5/5.00553/constant.pm line 175.
[Wed Nov  3 11:49:38 1999] counter3.pl: Constant subroutine DB_PASS redefined
                        at /usr/local/lib/perl5/5.00553/constant.pm line 175.
[Wed Nov  3 11:49:38 1999] counter3.pl: Constant subroutine DB_TABLE redefined
                        at /usr/local/lib/perl5/5.00553/constant.pm line 175.
[Wed Nov  3 11:49:38 1999] counter3.pl: Subroutine output_as_gif redefined
                        at /usr/local/apache/perl-run/counter3.pl line 60.
[Wed Nov  3 11:49:38 1999] counter3.pl: Subroutine output_statistics redefined
                        at /usr/local/apache/perl-run/counter3.pl line 85.
[Wed Nov  3 11:49:38 1999] counter3.pl: Subroutine error redefined
                        at /usr/local/apache/perl-run/counter3.pl line 131.
[Wed Nov  3 11:49:38 1999] counter3.pl: Subroutine which_domain redefined
                        at /usr/local/apache/perl-run/counter3.pl line 141.
[Wed Nov  3 11:49:38 1999] counter3.pl: Subroutine which_day redefined
                        at /usr/local/apache/perl-run/counter3.pl line 153.
[Wed Nov  3 11:49:38 1999] counter3.pl: Subroutine which_week redefined
                        at /usr/local/apache/perl-run/counter3.pl line 159.
[Wed Nov  3 11:49:38 1999] counter3.pl: Subroutine which_month redefined
                        at /usr/local/apache/perl-run/counter3.pl line 164.
[Wed Nov  3 11:49:38 1999] counter3.pl: Subroutine which_browser redefined
                        at /usr/local/apache/perl-run/counter3.pl line 171.
[Wed Nov  3 11:49:38 1999] counter3.pl: Use of uninitialized value
                        at /usr/local/lib/perl5/5.00553/CGI.pm line 456.

Dennoch wird das Skript richtig ausgeführt. Das zeigt uns schon deutlich, daß wir unsere Programmiergewohnheiten etwas anpassen müssen, wenn wir saubere CGI-Skripten programmieren wollen.

In diesem Spezialfall, können wir das Problem dadurch vermeiden, daß wir den httpd Prozeß beenden, sobald das Skript ausgeführt wurde. Dazu setzen wir in ~www/conf/perl.conf noch die Umgebungsvariable PerlRunOnce auf On:

Alias /perl-run/ /usr/local/apache/perl-run/
<Location /perl-run>
    PerlSetVar  PerlRunOnce On
    SetHandler  perl-script
    PerlHandler Apache::PerlRun
    Options     +ExecCGI
</Location>

Wenn wir nun den Server neu starten, dürfte das Problem nicht mehr angezeigt werden:

root@sun-1:/usr/local/apache/conf> /etc/init.d/apachectl restart
/etc/init.d/apachectl restart: httpd restarted

Bei counter3.pl wird nur noch folgende Fehlermeldung angezeigt:

[Wed Nov  3 11:58:12 1999] counter3.pl: Use of uninitialized value
                   at /usr/local/lib/perl5/5.00553/CGI.pm line 456.

Daß der Fehler bei den URLs

http://sun-1.meta.net/perl-run/counter3.pl/mycounter1?getstats=1
http://sun-1.meta.net/perl-run/counter3.pl/mycounter1?getstats=2

nicht erscheint, deutet auf einen Fehler in CGI.pm hin. Vielleicht ist der Fehler bei einer neueren Version von CGI.pm behoben worden (hier eingesetzte Version: 2.53).

Natürlich gehen bei PerlRunOnce On die meisten Vorteile von Apache::PerlRun verloren. Dennoch ist es immer noch eine Methode, schnell und bequem quick and dirty CGI-Skripte schneller ausführen zu lassen.

Cachen von CGI-Skripten mit Apache::Registry

Ein Nachteil bei Apache::PerlRun war, daß Perl-Skripten bei jedem neuen Aufruf erneut von einer Datei geladen und vom in den httpd-Prozessen eingebauten Perl-Interpreter neu kompiliert werden. Besser wäre es, einmal in Bytecode kompilierte Skripten zu cachen und bei Bedarf nur noch auszuführen. Dies würde bei jedem nachfolgenden Aufruf den Übersetzungsschritt einsparen und somit einen weitere Geschwindigkeitsvorteil einbringen.

Das Modul Apache::Registry ist genau dafür zuständig. Es wird so ähnlich wie Apache::PerlRun konfiguriert und verwendet:

root@sun-1:/usr/local/apache/conf> mkdir ~www/perl-registry

In ~www/conf/perl.conf fügen wir folgenden Zeilen ein:

Alias /perl-registry/ /usr/local/apache/perl-registry/
<Location /perl-registry>
    SetHandler     perl-script
    PerlHandler    Apache::Registry
    PerlSendHeader On
    Options        +ExecCGI
</Location>

Nun starten wir den Server neu:

root@sun-1:/usr/local/apache/conf> /etc/init.d/apachectl restart
/etc/init.d/apachectl restart: httpd restarted

Wenn wir nun wieder unser counter3.pl (oder jedes andere CGI-Skript) nach ~www/perl-registry kopieren und nun unter folgender URLs aufrufen:

http://sun-1.meta.net/perl-registry/counter3.pl/mycounter1
http://sun-1.meta.net/perl-registry/counter3.pl/mycounter1?getstats=1
http://sun-1.meta.net/perl-registry/counter3.pl/mycounter1?getstats=2

werden wir sehen, daß der Zählerstand deutlich schneller aktualisiert wird. Wir erhalten im Fehlerlog zwar noch einige Warnungen, aber das Programm scheint richtig zu funktionieren. Der Geschwindigkeitsvorteil ist deutlich zu erkennen! Wir werden noch im folgenden versuchen, die Fehlermeldungen zu beheben und zum anderen sehen, ob der Zähler nicht noch schneller gemacht werden kann.

Saubere Apache::Registry-Programme

CGI-Skripten, die früher problemlos funktionierten, erzeugen häufig Warnungen oder gar Fehlermeldungen, wenn sie unter Apache::PerlRun oder Apache::Registry ausgeführt werden. Wieso eigentlich?

Der wesentliche Grund ist, daß im Gegensatz zur reinen CGI-Umgebung die Skripten von einem persistenten Perl-Interpreter ausgeführt werden. Was bedeutet das? Bei einem normalen CGI-Programmaufruf wird ein eigener Prozeß gestartet, der wiederrum den Perl-Interpreter perl aufruft, welcher dann das CGI-Skript ausführt. Jeder einzelne Aufruf ist unabhängig von anderen Aufrufen. Sie können z.B. davon ausgehen, daß Variablen am Anfang des Skriptes uninitialisiert sind.

Ganz anders sieht es bei Apache::Registry aus! Hier werden die "CGI-Skripten" von den httpd-Prozessen des Webservers direkt ausgeführt. Genauer gesagt, sie werden von einem Perl-Interpreter ausgeführt, der in den httpd-Prozessen des Webservers "lebt". Dies hat wichtige Konsequenzen: Da die Perl-Interpreter nicht nur für die Dauer eines einzelnen CGI-Aufrufs existieren, sind sie persistent. Dadurch können Sie, um unser obiges Beispiel zu nehmen, nicht mehr davon ausgehen, daß Variablen am Anfang des CGI-Skriptes immer undefiniert sind. Das mag zwar beim ersten Aufruf eines solchen Skriptes stimmen (auf jedem der httpd-Prozessen), nicht jedoch bei nachfolgenden Aufrufen. Viele CGI-Skripten, die von einer solchen Voraussetzung ausgingen, können nun nicht mehr unter Apache::Registry richtig laufen.

Wenn Sie Apache::Registry einsetzen, sollten Sie sauber programmieren. Das bedeutet, daß Sie Ihre Programmiergewohnheiten etwas umstellen sollten. Die Manual-Seite man mod_perl_traps zeigt die wichtigsten Fallen auf, die bei der sorglosen CGI-Programmierung keine Rolle spielen, sehr wohl aber unter Apache::Registry.

Die folgenden Heuristiken sollten Sie bei jedem CGI-Skript anwenden, das Sie unter Apache::Registry ausführen wollen:

Installation von nativen mod_perl-Handlern

Im Gegensatz zu gewönlichen CGI-Programmen, oder CGI-Programmen, die durch Apache::PerlRun bzw. Apache::Registry beschleunigt wurden, sind Sie mit echten mod_perl-Programmen in der Lage, an jeder möglichen Stelle des sog. Anforderungszyklus des Apache-Webserver einzugreifen (siehe unten). Dies geschieht üblicherweise in einem Perl*Handler, den Sie selbst vorgeben.

Bei der Installation von mod_perl mit der Option EVERYTHING=1 haben wir auf Seite 1026 gesehen, daß diverse Handler (auch Hooks genannt) aktiviert wurden:

farid@sun-1:~/mod_perl-1.21> perl Makefile.PL EVERYTHING=1
Configure mod_perl with ../apache_1.3.6/src ? [y] 
Shall I build httpd in ../apache_1.3.6/src for you? [y] 

PerlDispatchHandler.........enabled   PerlChildInitHandler........enabled
PerlChildExitHandler........enabled   PerlPostReadRequestHandler..enabled
PerlTransHandler............enabled   PerlHeaderParserHandler.....enabled
PerlAccessHandler...........enabled   PerlAuthenHandler...........enabled
PerlAuthzHandler............enabled   PerlTypeHandler.............enabled
PerlFixupHandler............enabled   PerlHandler.................enabled
PerlLogHandler..............enabled   PerlInitHandler.............enabled
PerlCleanupHandler..........enabled   PerlStackedHandlers.........enabled
PerlMethodHandlers..........enabled   PerlDirectiveHandlers.......enabled
PerlTableApi................enabled   PerlLogApi..................enabled
PerlUriApi..................enabled   PerlUtilApi.................enabled
PerlFileApi.................enabled   PerlConnectionApi...........enabled
PerlServerApi...............enabled   PerlSections................enabled
PerlSSI.....................enabled

Diese Hooks werden bei jedem Aufruf durch den Apache-Webserver, via mod_perl an verschiedenen Stellen des Anforderungszyklus aufgerufen. Sie können nun für einige oder sogar jedem dieser Hooks einen oder mehrere Handler installieren. Diese werden dann an der entsprechenden Stelle durch mod_perl aufgerufen.

Wir werden uns diese Hooks weiter unten genauer anschauen.

Wie werden nun diese Hooks verwendet? Das ist ganz einfach. Zunächst schreiben Sie oder borgen sich einen Handler aus dem CPAN. Dieser Handler wird in den meisten Fällen ein .pm-Modul im Namensraum Apache:: sein. Diese Datei kopieren Sie nun in ~www/lib/perl/Apache und sorgen dafür, daß sie durch den httpd-User (meist nobody) gelesen werden kann.

Wie sieht ein solcher handler aus? Auch das ist einfach: Der hypothetische Handler Apache::Hypothetical ist eine Datei, die in ~www/lib/perl/Apache/Hypothetical.pm liegt. Diese hat die grundsätzliche Form:

# ~www/lib/perl/Apache/Hypothetical.pm: 
package Apache::Hypothetical;
use strict;

use Apache::Constants qw(:common);

sub handler {
    my $r = shift;         # Das Anforderungsobjekt
    # Hier etwas mit $r tun, dann Rueckgabecode zurueckgeben.
    return IRGEND_EIN_STATUS_CODE;
}

1;

Ein solcher Handler ist entweder für bestimmte Dateien oder URLs zuständig, oder für den ganzen Server. In ersten Fall sollten die nachfolgenden Anweisungen innerhalb einer <Location> stehen, im zweiten Fall auf der globalen Ebene. Einzelheiten zum Aufbau eines solchen nativen Handlers finden Sie weiter unten.

Um nun diesen Handler an einen Hook, sagen wir mal Perl*Handler zu binden, tragen wir folgende Anweisung in ~www/conf/perl.conf ein:

Perl*Handler    Apache::Hypothetical

Hier steht Perl*Handler für eines der weiter unten angegebenen Hooks.

Sobald der Anforderungszyklus des Apache-Webservers die entsprechende Stelle erreicht hat, wird mod_perl aufgerufen. mod_perl führt nun die handler()-Funktion des jeweiligen Moduls aus. Natürlich wird das Modul bei Bedarf erst geladen und intern kompiliert, falls dies bisher für den entsprechenden httpd-Prozeß noch nicht geschehen ist.

Es ist möglich, mehrere Handler für einen bestimmten Hook zu installieren. Diese werden dann der Reihe nach aufgerufen, es sei denn, eines der Handler hat die Verarbeitung explizit beendet. Mehrere Handler installieren Sie wie folgt:

Perl*Handler     Apache::Hypothetical Apache::Test1 Apache::Test2

Hier werden erst Apache::Hypothetical::handler(), dann Apache::Test1::handler() gefolgt von Apache::Test2::handler() durch mod_perl beim Perl*Handler-Hook aufgerufen.

Alternativ dazu können die Handler auch auf aufeinananderfolgenden Zeilen aufgelistet werden. Beide Techniken können Sie auch mischen:

Perl*Handler     Apache::Hypothetical Apache::Test1 Apache::Test2
Perl*Handler     Apache::Test3

Es ist praktisch, den Eintrittspunkt der Handler in eine Subroutine namens handler() unterzubringen, da mod_perl nach einer Subroutine dieses Namens sucht. Es ist aber auch möglich, wenn auch nicht empfohlen, einen abweichenden Namen zu wählen. Diesen sollten Sie dann in der Perl*Handler-Direktive angeben. Angenommen, Sie entscheiden sich aus irgend einem Grund gegen den Subroutinennamen handler() und wählen stattdessen den Namen doit(). In diesem Fall lautet die entsprechende Perl*Handler-Direktive:

PerlModule       Apache::Hypothetical
Perl*Handler     Apache::Hypothetical::doit

Die erste Zeile ist notwendig, da mod_perl sonst das Modul nicht automatisch laden würde. Dieses Problem hatten wir nicht bei der Wahl des Namens handler()!

Es ist auch möglich, anonyme Subroutinen (siehe Kapitel 13) anzugeben:

Perl*Handler     "sub { my $r = shift; tue_etwas_mit_$r; return RUECKGABECODE; }"
Die Hooks des Anforderungszyklus

Sobald eine Anforderung von einem Browser beim Apache-Webserver eintrifft, tritt der sogenannte Anforderungszyklus in Aktion. Dieser besteht darin, daß die Beantwortung der Browseranforderung in mehrere Phasen zerlegt wird, die alle hintereinander ablaufen. Für jede Phase stellt nun die Apache-API bzw. mod_perl Hooks bereit. An diesen Hooks können nun diverse Handler gebunden werden, welche dann vom Webserver aufgerufen werden. Schauen wir uns die wichtigsten dieser Hooks in der Reihenfolge ihres Aufrufs im Anforderungszyklus etwas genauer an!

PerlChildInitHandler
Sobald eine Anforderung beim Webserver eintrifft, wird sie vom Haupt-httpd-Prozeß an einem seiner Kindprozesse delegiert. Falls dafür ein neuer Kindprozeß gefork()t werden muß wird im neu erzeugten Prozeß zunächst die PerlChildInitHandler-Phase ausgeführt. An dieser Stelle kann ein Handler beispielsweise den neu erzeugten httpd-Prozeß bei einem Datenbankserver einloggen, da es sich hierbei um eine kostspielige Operation handelt. Nachfolgende Anforderungen an diesen Prozeß könnten dann von der bereits bestehenden Verbindung zum Datenbankserver profitieren (Siehe Modul Apache::DBILogin). Natürlich sind ebenfalls andere Anwendungen für diese Phase denkbar. Diese Anweisung hat globalen Charakter und kann nur auf der globalen Ebene der Konfigurationsdatei ~www/conf/httpd.conf bzw. in einer Virtuellen-Server-Sektion stehen, nicht jedoch in <Directory>-, <Location>- oder <Files>-Sektionen und auch nicht in .htaccess-Dateien.
PerlPostReadRequestHandler
Die Anforderung des Browsers besteht aus einigen HTTP-Headern, eventuell gefolgt von irgendwelchen Daten. Der Apache-Webserver parst nun diese HTTP-Header. Anschließend ruft er die PerlPostReadRequestHandler-Phase auf. An dieser Stelle wurde die angeforderte URI noch nicht zu einem physikalischen Pfadnamen konvertiert. Handler dieser Phase können z.Zt. zwar am dem eigentlichen Parsen der HTTP-Header nichts beitragen, aber sie können sehr wohl allgemeine Tätigkeiten durchführen, die am Anfang einer jeden Transaktion stattfinden sollen. Da jedoch die URL noch nicht bearbeitet wurde, können hier nur Tätigkeiten durchgeführt werden, die von der URL noch nicht abhängen. Eine typische Anwendung könnte beispielsweise einen Timer starten, um so die Dauer von Transaktionen zu messen. Der Timer könnte dann am Ende des Anforderungszyklus wieder gestoppt werden. Die Einschränkungen hinsichtlich der Stelle, an der die PerlPostReadRequestHandler-Direktive in der Konfiguratonsdatei stehen darf sind identisch mit denen der vorhergehenden PerlChildInitHandler-Phase, und zwar aus demselben Grund.
PerlTransHandler
Als nächstes muß die URL zu einem physikalischen Pfad erweitert werden. So etwas kennen Sie ja schon. Eine URL von /a/path/to/file wird beispielsweise zur URL /usr/local/apache/htdocs/a/path/to/file transformiert, wenn die DocumentRoot-Directive in ~www/conf/httpd.conf auf /usr/local/apache/htdocs verweist. Es gibt jedoch auch die ScriptAlias-, <Location>- und weitere Direktiven, die eine alternative Verarbeitung erfordern. Es sind auch raffiniertere URL-Transformationen denkbar, wie z.B. die Konvertierung von Großbuchstaben in Kleinbuchstaben für alle User, die glauben, keinen Unterschied zwischen beiden machen zu müssen, oder auch die automatische Korrektur kleiner Typos in einer URL. Denkbar wäre auch, PATH_INFO mitten in der URL unterzubringen und dann später extrahieren, z.B.:
/shopping-cart/3432987434321/sc.pl?action=add&item=7434
Hier würde 3432987434321 eine bestimmte Session-ID repräsentieren. Ein weiteres Beispiel ist Bereitstellung mehrerer Dokumentbäume in verschiedenen Sprachen. Aufgrund der Spracheangabe in den HTTP-Headern kann nun die URL so verändert werden, daß sie zu der richtigen Sprache verweist. Dasselbe gilt auch für den Browsertyp. Sie können verschiedenartige Dokumente bereitstellen, die für verschiedene Browsertypen optimiert wurden. Beispiel:
# Die folgende URL:
/a/path/to/file.html

# wird, abhaengig von der Sprache transformiert zu:
/en/a/path/to/file.html    # Englisch
/de/a/path/to/file.html    # Deutsch
/it/a/path/to/file.html    # Italienisch

# Wir koennen auch zu diversen Browsertypen verzweigen:
/nn/a/path/to/file.html    # Netscape Navigator
/ie/a/path/to/file.html    # Micro$oft Internet Explorer
/lynx/a/path/to/file.html  # lynx
Beide Methoden lassen sich natürlich kombinieren! Die URL-Transformation wird durch Handler der Phase PerlTransHandler durchgeführt. Im Gegensatz zu den Handlern der anderen Hooks, wird hier die Bearbeitung dieser Phase beendet, sobald ein Handler einen nicht DECLINED-Rückgabecode zurückliefert. Diese Anweisung darf wieder nur auf der globalen Ebene der Konfigurationsdateien oder in Virtuellen-Host-Sektionen stehen, nicht jedoch in <Directory>-, <Location>- oder <Files>-Sektionen und auch nicht in .htaccess-Dateien.
PerlHeaderParserHandler
Sobald die URI-Transformationsphase abgeschlossen ist, erhalten wir in der PerlHeaderParserHandler-Phase eine zweite Gelegenheit, die HTTP-Header zu untersuchen. Diesmal steht aber eine bereits bearbeitete URL zur Verfügung, so daß wir nun erstmalig in der Lage sind, abhängig von der gewählten URL, unterschiedlich zu reagieren. Aus diesem Grund kann die PerlHeaderParserHandler-Direktive im Gegensatz zum globalen PerlPostReadRequestHandler-Hook auch in <Directory>-, <Location>- oder <Files>-Sektionen sowie in .htaccess-Dateien stehen. Somit können hier Handler pro Verzeichnis installiert werden, die noch vor alle nachfolgenden Phasen unbedingt zum Zuge kommen sollen. An dieser Stelle könnten beispielsweise Robots anhand des Browser-Identifikationsstrings der Zugang zu einem Teil des URL-Baumes verwehrt werden (diese Operation könnte auch in der nachfolgend erklärten PerlAccessHandler-Phase stehen...)
Zugriffskontrollphasen
Es folgen nun drei Phasen, die feststellen, ob der Zugriff auf eine bestimmte URL erlaubt sein soll, oder nicht.
PerlAccessHandler
Während dieser ersten Zugriffskontrollphase wird anhand allgemeiner Daten, wie z.B. die IP-Adresse des Browsers, der Uhrzeit etc. geprüft, ob eine Anforderung weiter bearbeitet werden darf. Die hier geprüften Kriterien sind unabhängig von der Identität eines bestimmten Anwenders. Eine gute Anwendung für diese Phase wäre z.B. ein Robot-Blocker, der Robots anhand des Browser-Identifikationsstrings erkennt und den Zugriff auf bestimmte Verzeichnisse diesem Robot verwehrt. Eine weitere, äußerst populäre Anwendung benutzt die IP-Adresse des Browsers, um z.B. den Zugriff nur für Browsern aus einer bestimmten Liste von Domains zuzulassen und für andere Domains zu sperren. Somit kann z.B. Browsern aus dem Campus der Zugang zu einem Teilbaum gewährt werden, nicht jedoch Browsern von außerhalb. Umgekehrt kann der Zugriff für Browser bestimmter Domains gesperrt werden, die besonders unangenehm aufgefallen sind. Eine weitere Anwendung sieht z.B. die Sperrung des Zugangs auf bestimmte oder alle Teilbäume vor während allgemeine Wartungsarbeiten (z.B. am Datenbankserver, etwa Offline-Sicherungen?) stattfinden. Die PerlAccessHandler-Direktive darf überall stehen.
PerlAuthenHandler
Nachdem die PerlAccessHandler-Phase den Zugriff aufgrund allgemeiner, userunabhängigen Kriterien zugelassen hat, wird nun vom User eine Username/Paßwort-Kombination erfragt. Die Authentifikation prüft dann diese Kombination anhand einer Tabelle (z.B. einer Textdatei, DBM-Datei oder gar einer DBI-Datenbanktabelle). Stimmt die vom User angegebene Kombination mit einer Kombination in der serverseitigen Tabelle überein, ist der User erfolgreich authentifiziert worden. An dieser Stelle geht der Server davon aus, daß der User derjenige ist, für den er sich ausgibt. Wir haben in Kapitel 19, Sektion 19.4.8 ab Seite 1075 bereits einen solchen Handler kennengelernt (Apache::AuthenDBI, siehe Synopsis und Beispiel cgi-s-roaming.pl), der in einer DBI-Datenbank eingetragene User authentifiziert. Die PerlAuthenHandler-Direktive darf überall stehen.
PerlAuthzHandler
Diese Phase dient der noch feineren Einstellung von Zugriffsrechten: Bereits authentifizierte Benutzer müssen noch lange nicht Zugriff auf den gesamten Dokumentenbaum erhalten! Denkbar ist eine Autorisation, die auf der Gruppenzugehörigkeit eines Users basiert. In diesem Fall ist es notwendig, den bereits durch PerlAuthenHandler authentifizierten User anhand weiterer Kriterien den Zugang zur gewüschten URL zu erlauben oder zu verwehren. Das klassische Beispiel hierfür ist z.B. eine Firma, die aus mehreren Abteilungen besteht. Der Zugriff auf bestimmte Bereiche des Teilbaums sind dann nur Mitgliedern bestimmter befugter Abteilungen gestattet. Beispielsweise haben die Marketing-Leute nichts in den R&D-Unterlagen zu suchen und beide haben in den Personaldaten ebenfalls nichts verloren. Ein weiteres Beispiel ist eine Zeitbeschränkung auf Bürozeiten für normale Angestellte, nicht jedoch für Führungspersonal.
PerlTypeHandler
Der MIME-Typ eines Dokumentes wird üblicherweise aufgrund der Dateinamenserweiterung bestimmt. In einem Dokumentenverwaltungssystem, bei dem die Dokumente nicht aus Dateien, sondern z.B. aus einer Datenbank extrahiert werden, liegt diese Erweiterung oft nicht vor. An dieser Stelle kann der PerlTypeHandler den MIME-Typ anderweitig bestimmen. Dieser Handler kann nun einen vorläufigen MIME-Typ vorgeben, der von nachfolgenden Handlern ausgewertet, ja sogar verändert werden kann. Die Bestimmung des MIME-Typs eines Dokuments ist wichtig, denn sie muß dem Browser mitgeteilt werden, damit dieser das Dokument richtig rendern kann. Im Gegensatz zu den Handlern anderer Hooks, wird diese Phase beendet, sobald der einer der installierten Handler einen MIME-Typ gesetzt hat und OK oder einen Fehlercode zurückliefert.
PerlFixupHandler
Handler dieses Hooks werden aufgerufen, sobald der MIME-Typ bestimmt wurde, aber bevor eine Antwort zum Browser generiert und gesendet wird. An dieser Stelle können Sie noch bestimmte Änderungen vornehmen, z.B. Umbegungsvariablen für eine CGI-ähnliche Umgebung setzen etc.
PerlHandler
Die Content-Handler-Phase, auch PerlHandler-Phase genannt, ist die Stelle, an der eine Antwort generiert und an den Browser gesendet wird. Der Standardhandler hierfür liest beispielsweise eine Datei aus dem physikalischen URL-Pfad und sendet sie dann zum Browser. Das ist, was jeder Webserver standardmäßig tut. Es ist aber auch möglich, eine Antwort on-the-fly zu generieren, so wie es CGI-Programme tun. Sehr interessant ist auch die Möglichkeit, mehrere Handler dieser Phase zu stacken, wobei jeder einzelne Handler ein Teil des Dokumentes erzeugen kann. Das klassische Beispiel ist ein Handler, der automatisch einen Footer erzeugt. Ein weiterer Handler könnte Makros innerhalb von statischen HTML-Dateien expandieren (sogenannte SSI-Direktiven). Ein dritter Handler mag auch komprimierte Dateien zunächst entkomprieren, bevor er sie zum Browser sendet. Schließlig kann ein Handler auch beliebigen Perl-Code ausführen und dessen Output zum Browser schicken; genauso wie der perl-script Standardhandler bei Apache::Registry! Es handelt sich hierbei um die wichtigste Phase des Anforderungszyklus. Sie werden wahrscheinlich die meisten Module für diese Phase programmieren oder einsetzen.
PerlLogHandler
Nachdem die Transaktion mit dem Browser beendet ist (ob erfolgreich oder mit einem Fehler), werden Handler der Logging-Phase nacheinander aufgerufen. Diese Handler werden typischerweise in Logfiles diverse Statistiken aufbewahren, wie z.B. der Zeitpunkt der Transkation, ihre Dauer, die Anzahl der übertragenen Bytes, die IP-Adresse des Browsers, oder sonst etwas tun. Aktionen, die einen Log-Eintrag benötigen, müssen vor oder während dieser Phase stattfinden. Beispielsweise wäre das Gegenstück des oben erwähnte Starten eines Timers zum Messen der Transaktionsdauer spätestens hier anzubringen und zu protokollieren.
PerlCleanupHandler
Sobald eine Transaktion beendet ist, ruft mod_perl die Handler dieses Hooks auf. Das ist die Stelle, an der Aufräumarbeiten durchgeführt werden können; beispielsweise: Shared memory wieder freigeben, temporäre Dateien schließen bzw. löschen, evtl. Datenbanken schließen etc. Neben der PerlCleanupHandler-Direktive können Handler selbst einen Cleanup-Handler mit Hilfe der Funktion register_cleanup() installieren. Diese Handler werden erst nach der Logging-Phase aufgerufen! Sie werden auf jeden Fall aufgerufen, auch wenn ein vorhergehender Handler einen Fehlercode zurückgeliefert hat. Ein Rückgabecode des PerlCleanupHandlers wird nicht berücksichtigt! Diese Anweisung kann überall stehen, auch in .htaccess-Dateien.
PerlChildExitHandler
Kurz bevor ein httpd-Prozeß aus dem Prozeßpool des Webservers beendet wird, ruft mod_perl die Handler dieses Hooks auf. Es handelt sich um die letzten Handler, der ein httpd-Prozeß aufruft, bevor er stirbt. Diese Handler werden daher genau einmal für jeden sich beendenden httpd-Prozeß aufgerufen. Bedenken Sie, daß jeder httpd-Prozeß eine maximale Anzahl von Anforderungen bearbeitet, bevor er beendet wird. Diese Anzahl kann in ~www/conf/httpd.conf durch die Direktive MaxRequestsPerChild verändert werden.
Aufbau eines nativen mod_perl-Handlers (mod_perl-API)

In diesem Abschnitt werden wir die Details eines mod_perl-Handlers etwas genauer untersuchen. Dabei werden wir versuchen, so systematisch wie möglich vorzugehen, damit Sie einen ersten Eindruck bekommen können. Natürlich werden Sie hier keine Referenz finden. Diese finden Sie in wesentlichen in man mod_perl, man Apache und in [78].

Die Grundstruktur eines typischen mod_perl-Handlers haben wir weiter oben gesehen. Hier nochmal zur Erinnerung:

# ~www/lib/perl/Apache/Hypothetical.pm: 
package Apache::Hypothetical;
use strict;

use Apache::Constants qw(:common);

sub handler {
    my $r = shift;         # Das Anforderungsobjekt
    # Hier etwas mit $r tun, dann Rueckgabecode zurueckgeben.
    return IRGEND_EIN_STATUS_CODE;
}

1;

Folgendes sollten Sie nun wissen:

URL-Verarbeitung und Umlenkungen

In Vorbereitung.

Zugangskontrolle

In Vorbereitung.

Authentikation

In Vorbereitung.

Autorisation

In Vorbereitung.

MIME-Typen

In Vorbereitung.

Content-Handler

In Vorbereitung.

Logging und Debugging

In Vorbereitung.

Aufräumphase

In Vorbereitung.

Beliebte Fallstricke

In Vorbereitung.

Nützliche Module

In Vorbereitung.

[Prev] [Up] [Next]

[Alte Quelle]


Last modified: $Date: 2006/05/18 12:55:47 $
FH. Search :: Sitemap :: Disclaimer :: Copyright :: Privacy
FreeBSD Logo