Unteranfragen und Interne Weiterleitungen
Hier geht es um diese Zeilen aus Apache2::ClickPath:
unless( $r->is_initial_req ) {
# sub-request oder internal redirect
my $pr = $r->main # sub-request
|| $r->prev; # internal redirect
$r->subprocess_env( SESSION=>$pr->subprocess_env('SESSION') );
$r->pnotes( newsession=>$pr->pnotes( 'newsession' ) );
return Apache::DECLINED
}
In der gedruckten Version des Workshops konnte aus Platzgründen darauf nicht eingegangen werden.
Im ersten Teil erwähnte ich vom Apache selbst erzeugte virtuelle Anfragen, die den HTTP-Anfrage-Zyklus teilweise durchlaufen. Bei genauerer Betrachtung ergeben sich 2 Arten solcher Anfragen, interne Weiterleitungen (internal redirect) und Unteranfragen (sub-request). Im Weiteren werde ich die englischen Bezeichnungen benutzen.
Sub-Requests werden eingesetzt, wenn ein Handler in einer frühen Phase Informationen braucht, die erst in einer späteren Phase bereitgestellt werden. Man kann damit Fragen vom Typ: Was wäre, wenn ...? stellen. Dabei wird ein neues Anfrage-Objekt geschaffen, das dann alle Phasen des HTTP-Anfrage Zyklus bis kurz vor die Response Phase durchläuft. Interne Redirects durchlaufen alle Phasen einschließlich der Response Phase.
Zunächst ein paar Beispiele: Sub-Requests entstehen z.B. durch Verwendung
der DirectoryIndex Anweisung. Hier können mehrere Index-Dokumente
angegeben werden. Mod_dir erzeugt für jedes einen Sub-Request mit der
Frage: Ist der Status-Code 200 (OK), wenn dieses Dokument angefordert
würde? Wird die Frage mit ja beantwortet, dann liefert es das Dokument
mittels internem Redirect aus.
Etwas seltener sind sicher RewriteCond Anweisungen mit
%{LA-U:variable} als Teststring. Damit kann man z.B.
REMOTE_USER
in einer RewriteRule schon in der Translation-Phase benutzen, obwohl dieser
Wert erst in der Authentication-Phase ermittelt wird. Hier wird auch ein
Sub-Request benutzt.
Ein interner Redirect entsteht z.B., wenn ein CGI-Script nur eine
Location Header-Zeile ausgibt:
#!/bin/bash
echo Location: $QUERY_STRING
echo
Aufgerufen als /cgi-bin/redir.sh?/pfad/nach/irgendwo.html erzeugt
das Script einen internen Redirect nach /pfad/nach/irgendwo.html.
Eine andere Möglichkeit interne Redirects zu erzeugen, bietet die
ErrorDocument Anweisung.
Mod_perl (und Apache) bieten mehrere Funktionen, um mit internen Redirects
und Sub-Requests zu arbeiten. Mit lookup_uri und
lookup_file
werden Sub-Requests erzeugt und mit internal_redirect interne
Redirects.
Wichtiger in unserem Fall, ist zu erfahren, ob unser Translation Handler
für eine solche virtuelle Anfrage aufgerufen wurde, oder ob die Anfrage
direkt vom Browser kam. Diese Frage beantwortet is_initial_req.
Liefert sie true, so kommt die Anfrage vom Browser und der
Translation Handler erzeugt die Session wie bisher. Liefert sie aber
false, so müssen wir die Session von der vorhergehenden initialen
oder virtuellen Anfrage übernehmen. Auf diese Weise vererbt sich die
Session in alle virtuellen Anfragen. Die wichtigen Funktionen hierfür sind
main und prev. main liefert im Fall
eines Sub-Requests
die zugehörige Hauptanfrage, die nicht unbedingt die initiale sein
muss. prev gibt im Fall einer internen Weiterleitung die
vorhergehende Anfrage zurück.
Apache2::ReqChain
Der interessierte Leser kann mit folgendem Modul als Translation- oder Fixup-Handler selbst experimentieren und das Zusammenspiel von internen Redirects und Sub-Requests herausfinden.
package Apache2::ReqChain;
use 5.008;
use strict;
use Apache2::RequestRec ();
use Apache2::RequestUtil ();
use Apache2::Const -compile => qw(DECLINED);
our $VERSION='0.01';
our $seq_num;
sub handler {
my $r=shift; # das Anfrage-Objekt
$seq_num=1 if( $r->is_initial_req );
my $n=$r->pnotes( 'req#' );
unless( defined $n ) {
$n=$seq_num++;
$r->pnotes( 'req#'=>$n );
}
warn "\n>>>> $n\n";
warn " r uri: ".$r->uri."\n";
unless( $r->is_initial_req ) {
my $pr;
if( $pr=$r->main ) { # sub-request
warn " sub-request von\n";
warn " main uri: ".$pr->uri."\n";
warn " main initial: ".$pr->is_initial_req, "\n";
warn " main #: ".$pr->pnotes( 'req#' ), "\n";
warn " main main #: ".$pr->main->pnotes( 'req#' ), "\n"
if( $pr->main );
warn " main prev #: ".$pr->prev->pnotes( 'req#' ), "\n"
if( $pr->prev );
}
if( $pr=$r->prev ) { # internal redirect
warn " internal redirect von\n";
warn " prev uri: ".$pr->uri."\n";
warn " prev initial: ".$pr->is_initial_req, "\n";
warn " prev #: ".$pr->pnotes( 'req#' ), "\n";
warn " prev main #: ".$pr->main->pnotes( 'req#' ), "\n"
if( $pr->main );
warn " prev prev #: ".$pr->prev->pnotes( 'req#' ), "\n"
if( $pr->prev );
}
}
warn "<<<< $n\n";
return Apache2::Const::DECLINED
}
1;
Die globale Variable $seq_num spielt hier die zentrale Rolle. Sie
wird mit 1 initialisiert, wenn die Anfrage vom Browser kommt. Wird eine
Nummer benötigt, wird der aktuelle Wert genommen und $seq_num
inkrementiert. Jedesmal wenn der Handler aufgerufen wird, prüft er, ob der
aktuellen Anfrage schon eine Nummer zugeordnet ist. Wenn nicht, weist er die
nächste zu und speichert sie als Pnote. Damit erhält jede Anfrage
eine Nummer mit der wir sie im error_log wieder finden. Der Output
nach einem Aufruf von /-TESTSESSION/cgi-bin/redir.sh?/dir/ könnte
dann z.B. so aussehen (einige Kommentare wurden noch eingefügt):
# Die initiale Anfrage hat die Nummer 1.
>>>> 1
r uri: /-TESTSESSION/cgi-bin/redir.sh
<<<< 1
# Der Location-Header erzeugt einen internen Redirect.
>>>> 2
r uri: /dir/
internal redirect von
prev uri: /cgi-bin/redir.sh
prev initial: 1
prev #: 1
<<<< 2
# Eine DirectoryIndex-Anweisung lässt mod_dir zuerst nach index.shtml dann
nach index.html suchen.
>>>> 3
r uri: /dir/index.shtml
sub-request von
main uri: /dir/
main initial: 0
main #: 2
main prev #: 1
<<<< 3
>>>> 4
r uri: /dir/index.html
sub-request von
main uri: /dir/
main initial: 0
main #: 2
main prev #: 1
<<<< 4
Weiter mit einigen Aspekten zu mod_include


