mod_perl]
Farid Hajji: Perl - Einführung, Anwendungen, Referenz
2., aktualisierte und erweiterte Auflage
Addison-Wesley Longman, ISBN 3-8273-1535-2
mod_perlApache::PerlRunApache::RegistryApache::Registry-Programmemod_perl-Handlernmod_perl-Handlers (mod_perl-API)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:
httpd-Prozesse des Webservers i.d.R. mehr als nur eine einzige Anforderung bearbeiten, können die Verbindungen zu einem Datenbankserver von mehreren Anforderungen genutzt werden. Somit entfällt die Zeit, die zum Einloggen beim Datenbankserver notwendig wäre, da existierende Verbindungshandles weiterhin genutzt werden können.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/.
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:
mod_perl aus http://perl.apache.org/dist/.mod_perl im selben Verzeichnis aus:
farid@sun-1:~> tar -zxf apache_1.3.6.tar.gz farid@sun-1:~> tar -zxf mod_perl-1.21.tar.gz farid@sun-1:~> cd mod_perl-1.21/ 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] farid@sun-1:~/mod_perl-1.21> make farid@sun-1:~/mod_perl-1.21> make test root@sun-1:~farid/mod_perl-1.21> make install
mod_perl zusammen übersetzt. Das ist ja notwendig, denn schlißlig soll ja der Perl-Interpreter mit dem Apache zusammengelinkt werden! Anschließend werden die mod_perl-Module in das Perl-Installationsverzeichnis installiert.
httpd installiert werden. Dies geschieht auf die übliche Art und Weise. Wir haben folgendes Setup gewählt:
root@sun-1:/> mkdir /usr/local/apache root@sun-1:/> mkdir /usr/local/apache/bin root@sun-1:/> mv ~farid/apache_1.3.6/src/httpd /usr/local/apache/bin root@sun-1:/> mv ~farid/apache_1.3.6/conf /usr/local/apache root@sun-1:/> mv ~farid/apache_1.3.6/cgi-bin /usr/local/apache root@sun-1:/> mv ~farid/apache_1.3.6/icons /usr/local/apache root@sun-1:/> mv ~farid/apache_1.3.6/htdocs /usr/local/apache root@sun-1:/> mkdir -p /var/local/apache/logs farid@sun-1:~/apache_1.3.6/> cd src/support farid@sun-1:~/apache_1.3.6/src/support> make root@sun-1:/> mv ~farid/apache_1.3.6/src/support/* /usr/local/apache/bin root@sun-1:/> rm /usr/local/apache/bin/*.o root@sun-1:/> mkdir /usr/local/apache/man root@sun-1:/> mkdir /usr/local/apache/man/man1 root@sun-1:/> mkdir /usr/local/apache/man/man8 root@sun-1:/> cd /usr/local/apache root@sun-1:/usr/local/apache> mv bin/*.8 man/man8/ root@sun-1:/usr/local/apache> mv bin/*.1 man/man1/
PIDFILE=/var/local/apache/logs/httpd.pid HTTPD='/usr/local/apache/bin/httpd' LYNX="lynx -dump" STATUSURL="http://localhost/server-status"
root@sun-1:/usr/local/apache> cp bin/apachectl /etc/init.d/apachectl
www:x:1003:102:WWW Pseudo User:/usr/local/apache:/bin/false
# httpd.conf -- Apache HTTP server configuration file
ServerType standalone
Port 80
User nobody
Group nobody
ServerAdmin farid@sun-1.meta.net
ServerName sun-1.meta.net
ServerRoot /usr/local/apache
LockFile /var/local/apache/logs/httpd.lock
PidFile /var/local/apache/logs/httpd.pid
ErrorLog /var/local/apache/logs/error_log
TransferLog /var/local/apache/logs/access_log
ScoreBoardFile /var/local/apache/logs/httpd.scoreboard
ResourceConfig /dev/null
AccessConfig /dev/null
# Hier befinden sie die normalen statischen Dokumenten
DocumentRoot /usr/local/apache/htdocs
<Directory /usr/local/apache/htdocs>
Options Indexes FollowSymLinks
AllowOverride None
</Directory>
# Hier sind unsere normale CGI-Skripte
ScriptAlias /cgi-bin/ /usr/local/apache/cgi-bin/
<Directory /usr/local/apache/cgi-bin>
AllowOverride None
Options None
</Directory>
# Die mod_perl-Konfiguration kommt in eine eigene Datei
<IfModule mod_perl.c>
Include conf/perl.conf
</IfModule>
mod_perl-basierten Modulen wollen wir in einem eigenen Verzeichnis unterbringen. Wir erzeugen zunächst diese Verzeichnisse:
root@sun-1:/> mkdir -p ~www/lib/perl/Apache
@INC-Suchpfad ist (siehe Kapitel 14).httpd-Prozesses ausgeführt werden sollen. Eine der Initialisierungen wird die Erweiterung des @INC-Suchpfades sein. Häufig benötigte Perl-Module können ebenfalls an dieser Stelle bereits mit use eingebunden werden. Der Inhalt von ~www/conf/startup.pl lautet:
#!/usr/local/bin/perl
# startup.pl -- mod_perl Apache Startup Datei.
# Wir teilen mod_perl mit, wo es benutzerdefinierte Module finden kann.
# Dies muessen wir in einem BEGIN Block tun!
# Beachten Sie, dass die Module in ~www/lib/perl sein werden.
BEGIN {
use Apache ();
use lib Apache->server_root_relative('lib/perl');
}
# Diese Module werden haeufig benoetigt (Std. bei mod_perl).
use Apache::Registry ();
use Apache::Constants ();
# Das CGI.pm-Modul werden wir mit Sicherheit brauchen.
use CGI qw(-compile :all);
use CGI::Carp ();
# Hier noch weitere nuetzliche Module.
use Apache::DBI ();
use LWP ();
use DB_File ();
# Kommentierung entfernen zwecks Debugging.
# print STDERR "startup.pl initializeed successfully\n";
1; # Wie jedes Modul, erfolgreicher Rueckgabecode
httpd-Prozesses ausgeführt, weil am Anfang von ~www/conf/perl.conf folgendes steht:
PerlRequire conf/startup.pl PerlFreshRestart On
root@sun-1:/> /etc/init.d/apachectl start /etc/init.d/apachectl start: httpd started
mod_perl-Skripten ist die ständige Beobachtung der Fehlerlogs. Dies können Sie beispielsweise in einem eigenen xterm-Fenster tun, indem Sie dort folgendes Kommando eingeben:
farid@sun-1:~> tail -f /var/local/apache/logs/error_log
[Wed Nov 3 10:19:21 1999] [notice] Apache/1.3.6 (Unix) mod_perl/1.21
configured -- resuming normal operations
Apache::PerlRunDie 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.
Apache::RegistryEin 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.
Apache::Registry-ProgrammeCGI-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:
-w und use strict; am Anfang Ihrer Skripten:
#!/usr/local/bin/perl -w # Hier der Titel des Programms und weitere Kommentare use strict;
use strict; und Warnungen des -w-Flags genauer untersuchen und Ihren Code so umstellen, daß diese verschwinden. Das mag am Anfang ziemlich mühselig sein. Dennoch lohnt sich eine solche Mühe, da Sie somit ein besseres Programm erzeugt haben. Da -w Checks zur Laufzeit ausführt, sollten Sie möglicherweise später im Produktionsbetrieb das -w-Flag wieder entfernen. Tun Sie dies aber erst, nachdem Sie sämtliche Warnungen eliminiert haben!
close().
# Schlecht: Hier waechst @somelist bei jedem Aufruf immer mehr!
# (@somelist ist nicht unbedingt undef hier!)
foreach $i ( 1 .. 5 ) {
push(@somelist, $query->param("mycomment_$i");
}
# Besser: Hier wird @somelist nur so gross wie erforderlich
@somelist = (); # oder besser: undef @somelist;
foreach $i ( 1 .. 5) {
push(@somelist, $query->param("mycomment_$i");
}
# Noch besser: Hier wird der Speicherplatz wieder freigegeben
{
my @somelist = (); # Leere liste
push(@somelist, $query->param("mycomment_$i");
do_something_with(@somelist);
# Bei Verlassen des Blocks, wird @somelist zerstoert
# und der Speicherplatz wieder freigegeben.
}
$sth) mit finish() schließen. Ob Sie auch die Datenbankhandles ($dbh) mittels disconnect() vom Datenbankserver trennen sollen, bleibt Ihnen überlassen. Sie können nämlich von der Persistenzeigenschaft des Perl-Interpreters profitieren, um sich wieder schneller beim Datenbankserver einzuloggen:
# Beim Datenbankserver einloggen, falls nicht schon geschenen: $dbh ||= DBI->connect($DSN, $USER, $AUTH); # Explizites disconnect() vermeiden, wenn dieses Skript # haeufiger aufgerufen werden sollte: # $dbh->disconnect();
Apache::DBI beim Serverstart einbinden. Dazu tragen Sie in ~www/conf/startup.pl ein:
# Hier noch weitere nuetzliche Module. use Apache::DBI ();
DBI::connect() und DBI::disconnect() so, daß Sie ganz normal eine Verbindung aufbauen und trennen können. Im Hintergrund wird dafür gesorgt, daß nach Möglichkeit von der Persistenz Gebrauch gemacht wird. Der Code ist dann wieder wie gewohnt:
# Wenn Apache::DBI in startup.pl eingetragen ist: use DBI; $dbh = DBI->connect($DSN, $USER, $AUTH); $dbh->disconnect(); # NOP bei Apache::DBI
-w) und use strict; folgende Warnung im Fehlerlog erscheint:
[Sun Nov 7 10:11:12 1999] counter3.pl:
Variable "$query" will not stay shared
at /usr/local/apache/perl-registry/counter3.pl
line 76.
Apache::Registry cacht den Code eines Perl-Skriptes, indem es diesen innerhalb einer Funktion handler() wie folgt einbettet:
package Zusammengesetzter_Name_aus_URL_und_Skriptname;
sub handler {
# Hier der gesamte Inhalt der Datei, die den Code enthaelt.
}
1;
my $lexglobal = param("myvalue");
# Etwas spaeter im Programm:
myfunction();
# Diese Funktion verwendet das globale $lexglobal:
sub myfunction {
$lexglobal = "new value";
}
# Hier der Rest der Datei...
Apache::Registry erzeugt daraus den Code:
package Zusammengesetzter_Name_aus_URL_und_Skriptname;
sub handler {
my $lexglobal = param("myvalue");
# Etwas spaeter im Programm:
myfunction();
# Diese Funktion verwendet das globale $lexglobal:
sub myfunction {
$lexglobal = "new value";
}
# Hier der Rest der Datei...
}
1;
use strict; akzeptiert werden, sollten Sie diese globalen Variablen mit use vars deklarieren:
#!/usr/local/bin/perl -w
use strict;
use vars qw($realglobal); # Echte globale Variable
$realglobal = param("myname");
# Etwas spaeter im Programm:
myfunction();
# Diese Funktion verwendet die echte globale Variable $realglobal:
sub myfunction {
$realglobal = "new value";
}
# Hier der Rest der Datei...
my $lexglobal = param("myname");
# etwas spaeter im Programm
myfunction($lexglobal); # explizit uebergeben!
# Diese Funktion verwendet nur ihre Parameter:
sub myfunction {
my $lexglobalcopy = shift; # das ist eine Kopie von $lexglobal
$lexglobalcopy = "new value"; # Okay, kein Problem!
}
# Hier der Rest der Datei...
require() oder use() in Ihrem Apache::Registry-Skript:
#!/usr/local/bin/perl -w # Der Apache::Registry-kompatible Wrapper use strict; require "das_wesentliche_aus_der_anwendung"; anwendung_starten();
anwendung_starten() enthält hier den (ausführbaren) Code des Hauptprogramms. Der überwiegende Rest der Anwendung ist in einer eigenen Datei untergebracht, die mit require() (bzw. use()) eingelesen wird.Apache::Registry cacht nicht nur den kompilierten Code sondern auch den Zeitpunkt, an dem die Datei, die diesen Code enthält, zuletzt verändert wurde. Nun kann diese Datei verändert werden und Apache::Registry merkt das beim nächsten Aufruf und lädt diese Datei dann neu. Es ist also nicht notwendig, den Webserver neu zu starten, sobald ein unter der Kontrolle von Apache::Registry stehendes Skript verändert wird.require() oder use() eine weitere Datei lädt, ist das für Apache::Registry nicht sichtbar. Wenn Sie jetzt eine durch require() oder use() eingelesene Datei verändern, merkt das Apache::Registry nicht. Dies kann zu schwer aufzufindenden Fehlern führen.Apache::Registry anweisen, nicht nur das Modifikationsdatum der auszuführenden Datei zu cachen und zu überprüfen, sondern auch gleich dasjenige Modifikationsdatum aller Dateien zu überwachen, die in den durch %INC enthaltenen Pfaden (siehe Kapitel 14) im Auge zu behalten. Dies erreichen Sie dadurch, daß Sie das Modul Apache::StatINC als PerlInitHandler in ~www/conf/perl.conf installieren:
Alias /perl-registry/ /usr/local/apache/perl-registry/
<Location /perl-registry>
SetHandler perl-script
PerlHandler Apache::Registry
PerlInitHandler Apache::StatINC
PerlSendHeader On
Options +ExecCGI
</Location>
Apache::StatINC Änderungen von @INC (sei es direkt oder durch use lib;), die Ihre Skripte selbst vornehmen, nicht bemerkt. Das liegt daran, daß Apache::StatINC außerhalb von Skripten ausgeführt wird. Wenn Sie also eigene Suchpfade hinzufügen wollen, müssen Sie dies auf einer globalen Ebene tun, z.B. in ~www/conf/startup.pl:
# In ~www/conf/startup.pl den Suchpfad @INC fuer alle erweitern!
BEGIN {
use Apache ();
use lib Apache->server_root_relative('lib/perl');
}
mod_perl-HandlernIm 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; }"
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!
PerlChildInitHandlerhttpd-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.PerlPostReadRequestHandlerPerlPostReadRequestHandler-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.PerlTransHandlerDocumentRoot-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
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
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.
PerlHeaderParserHandlerPerlHeaderParserHandler-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...)PerlAccessHandlerPerlAccessHandler-Direktive darf überall stehen.PerlAuthenHandlerPerlAccessHandler-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.PerlAuthzHandlerPerlAuthenHandler 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.PerlTypeHandlerPerlTypeHandler 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.PerlFixupHandlerPerlHandlerPerlHandler-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.PerlLogHandlerPerlCleanupHandlermod_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.PerlChildExitHandlerhttpd-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.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:
package befindet (siehe Kapitel 14). Daher beginnt jeder Handler mit einer package-Anweisung und endet mit einer wahren Anweisung (1;).mod_perl zum passenden Zeitpunkt aufgerufen. Diese Subroutine hat defaultmäßig den Namen handler(). Im obigen Beispiel ist handler() die einzige Subroutine.handler() von der Apache-API aufgerufen wird, muß er mit einem Hook verbunden werden. Dies geschieht in den Serverkonfigurationsdateien, je nach Hook in der globalen Sektion oder virtuellen-Server Sektion, oder auch in den verzeichnisabhängigen <Directory>-, <Location>- und <Files>-Sektionen bzw. in .htaccess-Dateien. Dies wurde bereits weiter oben gezeigt. Hier nochmal ein Beispiel zur Erinnerung:
# In ~www/conf/perl.conf: # Rufe fuer alle /myhandler/... URLs die Funktion handler() # des Apache::Hypothetical Moduls auf, und zwar fuer die # Content-Hanlder Phase. <Location /myhandler> SetHandler perl-script PerlHandler Apache::Hypothetical; </Location> # Rufe fuer alle *.xhtml-Dateien die Funktion special() # des Apache::Special-Moduls auf # waehrend der Content-Handler Phase. <FilesMatch "\.xhtml$"> SetHandler perl-script PerlModule Apache::Special PerlHandler Apache::Special::special </FilesMatch> # Installiere einen URL-Transformer: PerlTransHandler Apache::SpellURL
handler() zu beziehen.
mod_perl diese Handler nacheinander auf. Somit können mehrere Handler an der Gestaltung der Antwort mitwirken. Wichtig ist jedoch jetzt der Rückgabecode des Handlers. Die folgenden symbolischen Konstanten stammen aus use Apache::Constants qw(:common). Sie haben folgende Bedeutung:
OKmod_perl unterschiedlich auf diesen Rückgabecode:
PerlTransHandler, Zugriffskontrollen, PerlTypeHandler und PerlHandler bedeutet OK, daß die Phase erfolgreich abgeschlossen ist. In diesem Fall wird direkt zur nächsten Phase übergegangen. Es macht in diesen Fällen wenig Sinn, weitere Handler zu installieren, da diese Phasen von einem einzigen Handler erfolgreich bearbeitet werden können. Es ist aber möglich, mittels stacked Handlers diese Einschränkung aufzuheben.OK, daß der aktuelle Handler erfolgreich war und daß nun mod_perl den nächsten evtl. installierten oder registrierten Handler desselben Hooks aufrufen soll. Ist kein solcher Handler mehr vorhanden, ist die Bearbeitung dieser Phase beendet und die nächste Phase wird betreten.OK zurück, wenn der Handler seine Arbeit normal beendet hat und nichts dagegen spricht, daß evtl. weitere registrierte Handler derselben Phase nun an die Reihe sind.
DONEOK, signalisiert der DONE Rückgabecode mod_perl, daß der Handler zwar mit seiner Arbeit erfolgreich war, aber die aktuelle Phase als abgeschlossen zu betrachten. Eventuell weitere registrierte Handler dieser Phase dürfen nicht mehr aufgerufen werden. Sie liefern diesen Rückgabecode zurück, wenn Sie im aktuellen Handler alles getan haben, was die Phase erfordert und sicher sind, daß nachfolgende Handler nicht mehr sinnvoll eingreifen könnten.DECLINEDDECLINED tun. In diesem Fall ruft mod_perl einfach den nächsten installierten bzw. registrierten Handler dieser Phase auf, wenn einer vorhanden ist, bzw. geht sonst zur nächsten Phase über.NOT_FOUNDNOT_FOUND liefern. In diesem Fall wird die Verarbeitung aller nachfolgenden Phasen (bis auf die PerlLogHandler- und PerlCleanupHandler-Phasen) abgebrochen und der Browser erhält den entsprechenden HTTP-Antwortcode. Eine sinnvolle Anwendung des NOT_FOUND-Rückgabecodes wäre z.B. ein Dokumentenmanagementsystem, bei dem die Dokumente aus einer Datenbank statt aus dem Filesystem extrahiert werden. Wenn ein passendes Dokument in der Datenbank nicht gefunden wird, könnten Sie NOT_FOUND zurückliefern.FORBIDDENPerlAccessHandler zurück, wenn der Browser des Users definitif keinen Zugriff auf die geforderte URL erhalten soll. In diesem Fall wird der Browser dem User dies durch eine entsprechende Fehlermeldung anzeigen. Diesen Code werden Sie typischerweise in einem PerlAccessHandler senden, wenn der userunabhängige Teil schon zum Ausschluß führt. In einem PerlAuthenHandler oder PerlAuthzHandler würden Sie hingegen bei falscher Username/Paßwort-Kombination bzw. fehlender Autorisation den Code AUTH_REQUIRED senden, was dem Browser veranlaßt eine solche Kombination (erneut?) beim User zu erfragen und die Anforderung mit diesen Daten zu wiederholen. Bei FORBIDDEN zeigt der Browser des Users eine Fehlermeldung an. Der User erhält keine Gelegenheit, es mit einer Username/Paßwort-Kombination erneut zu probieren!AUTH_REQUIREDPerlAuthenHandler heraus, daß ein User für den Zugriff auf eine URL authentifiziert werden muß, liefert er den Code AUTH_REQUIRED zurück. In diesem Fall wird dem Browser einen entsprechenden HTTP-Antwortcode auf dessen Anforderung hin gesendet. Der Browser versteht diesen Code dann als Aufforderung, eine Username/Paßwort-Kombination dem Server zu senden. Diese Kombination kann der Browser entweder aus seinem Cache holen und für den Benutzer unsichtbar in eine zweite Anforderung an den Server senden, oder aber diese Kombination beim User anfragen (typischerweise in einem Popup-Fenster) und diese anschließend automatisch in einer zweiten Anforderung zum Server schicken. Diesen Code liefert ein PerlAuthenHandler zurück, wenn eine Username/Paßwort-Kombination erforderlich ist, aber fehlt, oder wenn die Username/Paßwort-Kombination zwar vorhanden ist, aber nicht korrekt war. In beiden Fällen erhät der User die Gelegenheit, eine solche Kombination einzugeben und der Browser versucht es mit einer zweiten Anforderung erneut. Ein PerlAuthzHandler sollte diesen Code senden, wenn ein bereits authentifizierter User für den Zugriff auf eine bestimmte URL ein zusätzliches Paßwort senden soll, bzw. wenn der authentifizierte User auf die Ressource nicht zugreifen darf (z.B. aufgrund seiner Gruppenzugehörigkeit).SERVER_ERRORSERVER_ERROR zurückliefern. In diesem Fall wird dem Browser der HTTP-Antwortcode 500 Server Error gesendet, was zu der allgemein bekannten und gefürchteten Server Error Fehlerseite führt. Sie sollten diesen Rückgabecode, wenn überhaupt, nur sehr sparsam einsetzen. Meist gibt es bessere Rückgabecodes für einen Handler.handler() weiß warum er aufgerufen wurde, übergibt ihm mod_perl als erstes Argument ein Anforderungsobjekt, im folgenden stets $r genannt. Die Methoden dieses Objekts sind vollständig in man Apache dokumentiert. Wir werden im folgenden einige dieser Methoden einsetzen, sowohl um herauszufinden, wozu der Handler aufgerufen wurde, als auch um bestimmte Aktionen durchzufuehren.$r = Apache->request([$r]);Apache::Registry-CGI-Skripten ist es jedoch möglich, ebenfalls an das Anforderungsobjekt durch diesen Aufruf zu gelangen, wenn Sie es benötigen.$r->as_string()$r->main(), $r->prev(), $r->next(), $r->last()$r->is_main(), $r->is_initial_req()$r->main() aber etwas effizienter. $r->is_initial_req() ist wahr, wenn es sich bei der aktuellen Anforderung um die primäre Anforderung handelt. $r->is_initial_req() ist falsch, falls es sich bei dieser Anforderung um eine sekundäre Anforderung oder um ein internes Redirect handelt.PerlTransHandler-Phase. Somit werden z.B. auch die Zugriffskontrollphasen fuer die neue URI durchgeführt. Im Gegensatz zu einer normalen Anforderung, bricht jedoch die Verarbeitung im Falle eines Subrequests kurz vor der Content-Handler-Phase, PerlHandler, ab. Das liegt daran, daß für den Inhalt der Antwort schließlig ein Handler der Hauptanforderung zuständig sein sollte. Zusätlich zu den Subrequest-Methoden der vorigen Sektion, stehen noch folgende Methoden zur Verfügung:
$r->lookup_uri($uri)$r->lookup_file($filename)$r->run()Apache::SubRequest-Objekts zur Verügung.
$r->method( [$meth] ), $r->method_number( [$num] )method() liefert die gewünschte Methode als String GET, HEAD, POST, PUT etc. zurück. Handler können darauf entsprechend reagieren. Ein Beispiel hierfür finden Sie in PutHandler.pm. Wird ein Parameter $meth übergeben, wird die Methode geändert. Das ist für interne Redirects bzw. Subrequests sinnvoll. Das gilt auch für weitere optionale Parameter weiter unten. Die low-level Funktion method_number() erwartet statt Strings numerische Konstanten M_GET, M_POST etc., die in Apache::Constants deklariert sind. Dies ist vorallem zu Debugging-Zwecken sinnvoll.$r->bytes_sent()PerlLogHanlder-Phase verwenden.$r->the_request()$r->proxyreq()PerlTransHandler-Phase benutzt werden um solche Proxyanforderungen z.B. weiterzuleiten.$r->header_only()HEAD ist.$r->protocol()HTTP/1.0 oder HTTP/1.1.$r->uri( [$uri] )PerlTransHandler-Phase als Eingangsparameter verwendet. Mit dem optionale Argument kann ein Handler die URI auch modifizieren.$r->filename( [$filename] )PerlTransHandler werden Sie üblicherweise diesen Pfadnamen setzen, indem Sie den optionalen Parameter $filename übergeben. Ein sinnvoller Wert kann nur nach der PerlTransHandler-Phase erwartet werden.$r->path_info( [$path_info] )path_info() liefert die zusätzliche PATH_INFO, die nach dem Dateiname in einer URI steht. Natürlich ist erst nach der PerlTransHandler-Phase ein sinnvoller Wert dort zu erwarten! Sie können in der PerlTransHandler-Phase selbst PATH_INFO identifizieren und mit dem optionale Parameter $path_info setzen.$query = $r->args(), %query = $r->args()args()-Methode liefert die Daten eines eventuell vorhandenen Query-Strings zurück. Wird args() im skalaren Kontext aufgerufen, wird der gesamte QUERY_STRING zurückgegeben. Wenn aber args() im Listenkontext aufgerufen wurde, wird der geparste Query-String als einen Hash von Feldname => Wert-Paaren zurückgeliefert. Sie können sich daher das manuelle Parsen des QUERY_STRINGs ersparen.%hash = $r->headers_in()headers_in() liefert einen Hash von HTTP-Headern, die uns der Client gesendet hat. Die Schlüssel dieses Hashes sind wie gewohnt die Namen der HTTP-Headern. Im skalaren Kontext wird eine Apache::Table-Hashreferenz zurückgeliefert.$r->header_in( $header_name, [$value] )headers_in() liefert die Methode header_in() den zum HTTP-Header namens $header_name zugehörige Wert als String zurück. Beispielsweise: $ct = $r->header_in("Content-type");. Es ist auch möglich, mit Hilfe des optionalen Parameters $value ein HTTP-Header zu verändern oder neu zu definieren, damit nachfolgende Handler darauf reagieren.$r->content()content()-Methode diesen Inhalt auslesen. Im skalaren Kontext liefert content() den ganzen ungeparsten String.Im Listenkontext parst content() die Daten und liefert einen Hash von Schlüssel/Wert-Paaren zurück. Diese Funktion darf nur einmal aufgerufen werden, da die Daten direkt vom Client gelesen werden!$r->read( $buf, $bytes_to_read )content() liest die read()-Methode $bytes_to_read Bytes vom Client und speichert diese in $buf, wobei die Daten ganz beliebig sein können (es wird nichts geparst und auch kein application/x-www-form-urlencoded Encoding vorausgesetzt). Intern wird vorher ein Timeout mit hard_timeout() gesetzt.$r->get_remote_host()HostNameLookups ist in der Konfigurationsdatei auf off gesetzt. In diesem Fall wird die Adresse des Clienthosts in Dotted-Quad Notation zurückgeliefert. Wird die DNS gefordert und ist der Hostname nicht DNS-auflösbar, wird undef zurückgeliefert.$r->get_remote_logname()IdentityCheck nicht eingeschaltet, wird undef zurückgegeben. Achtung! Nur die allerwenigsten-Rechner im Netz unterstützen das Identifikationsprotokoll nach RFC-1413. Daher wird diese Funktion so gut wie nie eingesetzt!Apache::Connection-Objekts $c des Anforderungsobjekts $r:
$c = $r->connection()connection() des Anforderungsobjekts $r liefert ein Apache::Connection-Objekt $c, das die nachfolgenden Methoden bereitstellt. Dieses Objekt repräsentiert die aktuelle Verbindung zu einem Client.$c->remote_host()HostNameLookups eingeschaltet, liefert $c->remote_host() den DNS-Namen des Clients als String zurück. Die DNS-Anfrage wird für spätere Aufruf derselben Methode gecacht, was zu einer Verringerung des DNS-Verkehrs beiträgt. Kann der DNS-Name des Clients nicht ermittelt werden und ist HostNameLookups eingeschaltet, wird der leere String als Ergebnis zurückgeliefert. Ist hingegen HostNameLookups ausgeschaltet, wird die IP-Adresse des Clients in Dotted Quad Notation als String zurückgeliefert.$c->remote_ip()remote_ip() liefert die IP-Adresse des Clients in Dotted Quad Notation als String zurück.$c->local_addr()SOCKADDR_IN-Struktur, wie sie von der Funktion pack_sockaddr_in() des Socket-Moduls erzeugt wird, wird durch diese Methode erzeugt.$c->remote_addr()SOCKADDR_IN-Struktur, wie sie von der Funktion pack_sockaddr_in() des Socket-Moduls erzeugt wird, liefert diese Methode.$c->remote_logname()$r->get_remote_logname() des Anforderungsobjekts $r.$c->user()user() enthä den Namen eines erfolgreich authentifizierten Users. Dies entspricht den Inhalt der Umgebungsvariablen REMOTE_USER in der CGI-Welt.$c->auth_type()user() einen Usernamen, dann wurde dieser User mit Hilfe des durch auth_type() bezeichneten Authentifikationsschemas (z.B. Basic) authentifiziert.$c->aborted()aborted() einen wahren Wert zurück. Das kann beispielsweise dann der Fall sein, wenn der User den Stop-Button seines Browsers betätigt hat oder wenn seine Verbindung aus einem anderen Grunde zu unseren Server aufgehoben wurde. In diesem Fall ist es z.B. nicht mehr erforderlich, eine Berechnung fortzuführen, oder eine Antwort zu generieren. Diese würde ohnehin nicht mehr zum Client gelangen!In Vorbereitung.
In Vorbereitung.
In Vorbereitung.
In Vorbereitung.
In Vorbereitung.
In Vorbereitung.
In Vorbereitung.
In Vorbereitung.
In Vorbereitung.
In Vorbereitung.
[Prev] [Up] [Next]
[Alte Quelle]
| Last modified: $Date: 2006/05/18 12:55:47 $ FH. Search :: Sitemap :: Disclaimer :: Copyright :: Privacy |
|