Hallo Bernd, On 03/31/2013 02:52 PM, Lentes, Bernd wrote:
jetzt habe ich das mit der substitution soweit verstanden. Jetzt noch mal ein paar Fragen zum matching.
vorab folgendes. Alles außerhalb von <directory> oder .htaccess (oder auch <location> oder <files>), also alles, was entweder in keinem Container oder direkt unterhalb eines <VirtualHost> Containers steht nennt sich server-context bzw. vhost-context. Innerhalb <directory> oder .htaccess ist directory-context. Innerhalb <location> heißt auch directory-context, unterscheidet sich aber in Details. Hier muss in der Doku die Balance gewahrt werden zwischen Genauigkeit und Verwirrung der Leser. In den meisten Fällen passt die Trennung zwischen server-context und directory-context recht gut. Der große Unterschied zwischen server-context und directory-context ist, dass die Konfiguration für den Servercontext beim Start des Apache berechnet werden kann. Für alle Container kann die Konfiguration des Containers auch vorausberechnet werden, nicht aber für .htaccess Files. Zur Laufzeit wird dann die Konfiguration eines konkreten Requests berechnet, indem praktisch ein Klon der Servercontextkonfiguration genommen und mit allen passenden Directorycontextkonfigurationen ergänzt wird. Das passiert in der "Map to Storage" Phase. Eine RewriteRule im Directorycontext ist eigentlich ein komplett anderes Ding als eine RewriteRule im Servercontext. Sie tun etwas Ähnliches, haben aber eigentlich nicht viel miteinander zu tun. Sie werden zu unterschiedlichen Zeitpunkten während der Abarbeitung des Requests ausgewertet.
Ich beziehe mich auf folgenden Abschnitt der Doku: "What is matched? In VirtualHost context, The Pattern will initially be matched against the part of the URL after the hostname and port, and before the query string (e.g. "/app1/index.html").
Das stimmt so nicht. Verglichen wird immer mit r->filename. Eine RewriteRule im Servercontext (oder VHost) wird in der "URI Translation Phase" abgearbeitet. Normalerweise ist zu diesem Zeitpunkt r->filename leer. Wenn dem so ist, initialisiert mod_rewrite r->filename mit r->uri vor der Verarbeitung der ersten RewriteRule. Mod_rewrite enthält (ziemlich am Ende) folgende Funktion. Ich habe den Kram entfernt, die für die aktuelle Diskussion unwichtig sind. static void register_hooks(apr_pool_t *p) { ... ap_hook_handler(handler_redirect, NULL, NULL, APR_HOOK_MIDDLE); ... ap_hook_fixups(hook_fixup, aszPre, NULL, APR_HOOK_FIRST); ap_hook_fixups(hook_mimetype, NULL, NULL, APR_HOOK_LAST); ap_hook_translate_name(hook_uri2file, NULL, NULL, APR_HOOK_FIRST); } Zunächst ist die letzte Zeile interessant. Sie deklariert die Funktion, die in der URI Translation Phase ausgeführt werden soll. Der letzte Parameter gibt an, dass sie als eine der ersten Funktionen in dieser Phase ausgeführt werden will. Mod_alias enthält hier diese ähnliche Zeile: ap_hook_translate_name(translate_alias_redir,NULL,aszSucc,APR_HOOK_MIDDLE); Damit wird bekanntgegeben, dass die Funktion translate_alias_redir in der URI Translation Phase aufgerufen werden soll. APR_HOOK_MIDDLE besagt, dass es keine Präferenzen gibt, wann das geschehen soll. Modperl benutzt auch einen Handler für die URI Translation Phase. Dieser wird aber mit APR_HOOK_REALLY_FIRST als letztem Parameter installiert. Er soll also auf jedem Fall vor mod_rewrite aufgerufen werden. Wenn Du also alle 3 Module benutzt, wird in der URI Translation Phase zuerst der modperl Handler, dann der von mod_rewrite und als letzter der von mod_alias aufgerufen. Das stimmt nicht ganz, denn jeder Handler kann durch seinen Return-Code angeben, ob die Phase beendet ist oder ob der nächste Handler auch noch aufgerufen werden soll. Jetzt könnte ein modperl Handler in der Translation Phase r->filename setzen und mit dem Return Code OK anzeigen, dass die Phase beendet ist. Dann würden die Handler von mod_rewrite und mod_alias nicht mehr aufgerufen werden. Er könnte aber auch r->filename setzen und DECLINED zurück geben. Damit sagt er, dass die nächsten Handler auch noch aufgerufen werden sollen, solange bis entweder keine Handler für die Phase mehr da sind, oder bis einer OK liefert. Der mod_rewrite Handler liefert DECLINED, wenn z.B. "RewriteEngine Off" angegeben ist. Er liefert auch DECLINED, wenn keine passenden RewriteRules gefunden wurden, oder wenn das PT Flag angegeben wurde. Nur in dem Fall hat mod_alias eine Chance einzugreifen. Findet mod_rewrite eine passende Rule ohne PT Flag gibt der Handler OK zurück und beendet damit die URI Translation Phase. Setzt nun in unserem hypothetischen Setup der modperl Handler r->filename und gibt DECLINED zurück, wird der mod_rewrite Handler aufgerufen. Er stellt fest, dass r->filename bereits gesetzt ist und wendet die erste Rule darauf an. Der entsprechende Code sieht so aus: if (r->filename == NULL) { r->filename = apr_pstrdup(r->pool, r->uri); rewritelog((r, 2, NULL, "init rewrite engine with requested uri %s", r->filename)); } else { rewritelog((r, 2, NULL, "init rewrite engine with passed filename " "%s. Original uri = %s", r->filename, r->uri)); } /* * now apply the rules ... */ rulestatus = apply_rewrite_list(r, conf->rewriterules, NULL); Die zweite Zeile kopiert r->uri nach r->filename. r->uri wurde in einer früheren Phase aus dem Request gebildet. Wenn Du folgende Frage stellst: GET /bla/blub?fritz=12 dann ist /bla/blub die URI als r->uri verfügbar. "fritz=12" steht in r->args. Das ist der Grund, warum Du in einer RewriteRule keinen Zugriff auf den Querystring hast. Dafür brauchst Du RewriteCond. conf->rewriterules enthält nur die Rules im Servercontext. Die Rules aus <directory> Containern kann er an der Stelle noch nicht kennen.
In Directory and htaccess context, the Pattern will initially be matched against the filesystem path, after removing the prefix that lead the server to the current RewriteRule (e.g. "app1/index.html" or "index.html" depending on where the directives are defined)."
Also ganz genau ist das auch nicht. Und das komplette Bild ist recht
komplex. Der Apache kennt in diesem Zusammenhang folgende 2 Felder,
r->filename und r->path_info. Diese werden normalerweise von
default-Handler der "Map to Storage" Phase gesetzt. Das muss aber nicht
sein. Solche Module wie modperl können die "Map to Storage" Phase
komplett übernehmen und tun, was sie wollen. Sie können die Felder auch
später ändern.
Ich beschreibe jetzt den Normalfall. Angenommen vor der "Map to Storage"
Phase ist r->filename /mein/existierender/Pfad und dieser Pfad existiert
im Filesystem wirklich, egal ob Directory oder plain File. Dann ist nach
der "Map to Storage" Phase r->filename immer noch /mein/existierender/Pfad.
Jetzt nehmen wir an, vor Map to Storage ist r->filename
/eine/directory/nicht-existierender-Teil, wobei /eine/directory/ als
Verzeichnis existiert, nicht-existierender-Teil aber nicht. Auch hier
ändert sich r->filename nicht. Es bleibt bei
/eine/directory/nicht-existierender-Teil.
Nun nehmen wir an, vor Map to Storage ist r->filename
/eine/directory/nicht-existierend/bla/bla. Nach Map to Storage enthält
r->filename nur noch /eine/directory/nicht-existierend. r->path_info
wurde zu /bla/bla.
Noch ein anderer Fall, vor Map to Storage ist r->filename
/eine/directory/existing-plain-file/bla/bla. Danach wird r->filename zu
/eine/directory/existing-plain-file und r->path_info zu /bla/bla.
Ich glaube, ich habe jetzt alle Fälle.
Gewöhnlich ändern sich r->filename und r->path_info dann nicht nochmal.
D.h. sie kommen so in der Fixup Phase an. Wie oben angedeutet ist das
aber kein Gesetz.
Der Fixup-Handler von mod_rewrite führt nun nach ein wenig
Initialisierung folgende Funktion aus:
rulestatus = apply_rewrite_list(r, dconf->rewriterules,
dconf->directory);
"dconf" steht dabei für Directory Configuration. "dconf->rewriterules"
enthält die Rules aus einem <Directory> Container oder einem .htaccess
File. In dconf->directory hat sich mod_rewrite das Verzeichnis gemerkt,
das zu dem Container bzw. dem .htaccess File gehört.
Bei der Verarbeitung JEDER Rule wird nun Folgendes zur Initialisierung
ausgeführt. (Ich habe wieder unwichtige Teile gelöscht.)
ctx->uri = r->filename;
if (ctx->perdir) {
apr_size_t dirlen = strlen(ctx->perdir);
...
/* Since we want to match against the (so called) full URL, we have
* to re-add the PATH_INFO postfix
*/
if (r->path_info && *r->path_info) {
rewritelog((r, 3, ctx->perdir,
"add path info postfix: %s -> %s%s",
ctx->uri, ctx->uri, r->path_info));
ctx->uri = apr_pstrcat(r->pool, ctx->uri, r->path_info, NULL);
}
/* Additionally we strip the physical path from the url to match
* it independent from the underlaying filesystem.
*/
if (!is_proxyreq && strlen(ctx->uri) >= dirlen &&
!strncmp(ctx->uri, ctx->perdir, dirlen)) {
rewritelog((r, 3, ctx->perdir, "strip per-dir prefix: %s -> %s",
ctx->uri, ctx->uri + dirlen));
ctx->uri = ctx->uri + dirlen;
}
}
/* Try to match the URI against the RewriteRule pattern
* and exit immediately if it didn't apply.
*/
rewritelog((r, 3, ctx->perdir, "applying pattern '%s' to uri '%s'",
p->pattern, ctx->uri));
rc = !ap_regexec(p->regexp, ctx->uri, AP_MAX_REG_MATCH, regmatch, 0);
Zunächst sehen wir, dass auf r->filename gearbeitet wird. Dieser Wert
wird in ctx->uri geschrieben und in der letzten Zeile verwendet, um zu
ermitteln, ob die Rule passt. "ctx->perdir" enthält hier den Wert aus
"dconf->directory", d.h. "/dir" aus "
Was bedeutet " In Directory and htaccess context, ... against the filesystem path, after removing the prefix that lead the server to the current RewriteRule" ?
Siehe oben.
Welches Prefix wird genau entfernt ? Ist da der Pfad gemeint, der im
steht ?
Im Prinzip ja, aber siehe oben.
Was ist, wenn mehrere Rewrite Rules hintereinander folgen ? Erste Rule wird gematcht, ggf. substituiert, dann nächste Rule gematcht (gegen die Substitution der ersten Rule), ggf. substituiert ... ?
Wenn eine passende Regel gefunden wurde, enthält der Folgecode u.a. folgendes: /* Add the previously stripped per-directory location prefix, unless * (1) it's an absolute URL path and * (2) it's a full qualified URL */ if ( ctx->perdir && !is_proxyreq && *r->filename != '/' && !is_absolute_uri(r->filename)) { rewritelog((r, 3, ctx->perdir, "add per-dir prefix: %s -> %s%s", r->filename, ctx->perdir, r->filename)); r->filename = apr_pstrcat(r->pool, ctx->perdir, r->filename, NULL); } D.h. r->filename wird für die nächste Rule gesetzt. Wenn das Ergebnis der Substitution nicht mit einem Slash beginnt und auch keine absolute URI (scheme://...) ist, wird ctx->perdir vorangestellt.
Folgendes Szenario: ein vhost mit RewriteRule im vhost container. Im diesem vhost ein directory container für DocumentRoot, ebenfalls mit einer Rewrite Rule. Es kommt ein request für den vhost, der im directory container landet. Welche RewriteRule wird zuerst abgearbeitet ?
Die Frage solltest Du inzwischen selbst beantworten können.
" On the first RewriteRule it is applied to the (%-decoded) URL-path of the request; subsequent patterns ...": gilt das/ immer/ für die erste RewriteRule, das gegen die URL gematcht wird ? Unabhängig vom context ?
Die Frage solltest Du inzwischen fast selbst beantworten können. Hier die bisher fehlende Information: Auch der Fixup Handler von mod_rewrite enthält vor dem Aufruf von apply_rewrite_list folgenden Code: if (r->filename == NULL) { r->filename = apr_pstrdup(r->pool, r->uri); rewritelog((r, 2, "init rewrite engine with requested uri %s", r->filename)); } Normalerweise kommt es nicht vor, dass in der Fixup Phase r->filename nicht gesetzt ist. Wenn aber doch, wird es mit r->uri initialisiert. So, noch eine solche Mail werde ich nicht schreiben. Wenn Du es genauer wissen willst, nimm die ultimative Doku, den Source Code, und lies. Das ist gar nicht so schwer, wie man vermuten würde. Torsten -- Um die Liste abzubestellen, schicken Sie eine Mail an: opensuse-de+unsubscribe@opensuse.org Um den Listen Administrator zu erreichen, schicken Sie eine Mail an: opensuse-de+owner@opensuse.org