On 03/26/2013 09:38 PM, Lentes, Bernd wrote:
Hi,
aus gegebenem Anlaß muß ich mich mit mod_rewrite beschäftigen. Einfach ist anders ! Bin seit Stunden die Doku auf apache.org am Lesen, komme aber im Moment nicht weiter. Bin jetzt am testen. Habe folgende Konfiguration:
============================= ...
RewriteLog /var/log/apache2/rewrite.log RewriteLogLevel 2 Alias /test /hallo
RewriteEngine On ReWriteOptions Inherit RewriteBase /test RewriteRule $ MouseIDGenes Options FollowSymLinks
AllowOverride None
Order allow,deny Allow from 146.107.x.x
</Directory> </VirtualHost> ==============================
Bei einem request auf /test (http://virtueller_host.domain.de/test)
Ich glaube nicht, dass es ein Request auf /test ist, sondern auf /test/. Du siehst bestimmt, wie sich die URL im Browser von /test in /test/ ändert. Mod_dir ist dafür zuständig bei einem Request, der zu einem Verzeichnis auflöst, einen HTTP Code 301 zu liefern mit einem Location-Header, der auf die ursprüngliche URI mit einem angehängten Slash zeigt. Im Folgenden gehe ich davon aus, dass es /test/ ist.
passiert folgendes:
========================================= 146.107.x.x - - [26/Mar/2013:21:19:26 +0100] [mouseidgenes.helmholtz-muenchen.de/sid#7fedaa020a28][rid#7fedaa095978/initial] (2) [perdir /hallo/] rewrite '' -> 'MouseIDGenes' 146.107.x.x - - [26/Mar/2013:21:19:26 +0100] [mouseidgenes.helmholtz-muenchen.de/sid#7fedaa020a28][rid#7fedaa095978/initial] (2) [perdir /hallo/] trying to replace prefix /hallo/ with /test 146.107.x.x - - [26/Mar/2013:21:19:26 +0100] [mouseidgenes.helmholtz-muenchen.de/sid#7fedaa020a28][rid#7fedaa095978/initial] (1) [perdir /hallo/] internal redirect with /test/MouseIDGenes [INTERNAL REDIRECT] 146.107.x.x - - [26/Mar/2013:21:19:26 +0100] [mouseidgenes.helmholtz-muenchen.de/sid#7fedaa020a28][rid#7fedaa099f78/initial/redir#1] (2) [perdir /hallo/] rewrite 'MouseIDGenes' -> 'MouseIDGenes' 146.107.x.x - - [26/Mar/2013:21:19:26 +0100] [mouseidgenes.helmholtz-muenchen.de/sid#7fedaa020a28][rid#7fedaa099f78/initial/redir#1] (1) [perdir /hallo/] initial URL equal rewritten URL: /hallo/MouseIDGenes [IGNORING REWRITE] ========================================
Ich verstehe das folgendermaßen: Durch den Alias landet der Request erst mal im betr. directory-container (hallo), da ja test ein Alias für hallo ist.
Der Apache bearbeitet Requests in mehreren Phasen. Für Dich hier sind 3 davon interessant, "URI Translation", "Map to Storage" und "Fixup". Module, wie mod_rewrite oder mod_alias, können für diese Phasen Handler-Funktionen registrieren. Beide, mod_rewrite und mod_alias, registrieren Handler für URI Translation und Fixup. Ich gehe mal davon aus, dass Du einen Apache aus der 2.x-er Serie benutzt. Die URI Translation Phase kommt zuerst, im wesentlichen direkt nach dem Einlesen des Requests (Request Zeile und HTTP-Header). An dieser Stelle hat der Apache noch keine <Directory> oder ähnliche Blocks auf den Request angewandt. Von Deiner Konfiguration sieht der Apache bisher also nur "Alias /test /hallo" als relevant für die Anfrage an. Weiterhin ist wichtig zu wissen, dass die Request-Struktur des Apache ein Feld für die URI und ein weiteres für den Filenamen (außerdem auch path_info, falls das relevant ist) enthält. Module können in der URI Translation Phase die URI selbst manipulieren. Außerdem können sie das Filename Feld setzen. Die Alias Anweisung setzt also zunächst den Filenamen zu $DocumentRoot/hallo/. Die URI selbst bleibt unangetastet. Direkt im Anschluss an die URI Translation Phase kommt "Map to Storage". Die Aufgabe dieser Phase ist es zum einen die filename und path_info Felder zu füllen, falls das noch nicht geschehen ist. Zum anderen werden <Directory> und <Files> Blöcke und .htaccess Dateien ausgewertet und der Konfiguration des Requests hinzugefügt. Erst jetzt lernt der Apache also Deine RewriteRule kennen. Bis hierher sieht Dein Request also wie folgt aus: uri: /test/ filename: $DocumentRoot/hallo/ Sofort nach Map to Storage werden nun die <Location> Blocks der Konfiguration hinzugefügt. Dieser Prozess kann von Modulen nicht beeinflusst werden. Danach ist die Konfiguration für den Request komplett. Da Du keinen <Location> Block benutzt, ändert sich an Deiner Anfrage nichts. Nach ein paar weiteren Phasen bzgl. Zugriffsberechtigungen u.ä. kommt die Fixup Phase direkt bevor der Request-Body eingelesen und die Antwort ausgegeben wird. Deine RedirectRule konnte bisher noch nicht angewendet werden, weil sie in der URI Translation Phase noch nicht bekannt war. Das passiert jetzt in Fixup. Aus der Map to Storage Phase hat der Apache sich gemerkt, dass die RewriteRule in einem Container stand, der dem Verzeichnis $DocumentRoot/hallo/ entspricht. Dieser Präfix wird von filename abgeschnitten, übrig bleibt der leere String. Deine Rule passt und aus dem leeren String wird 'MouseIDGenes'. Da Du eine RewriteBase angegeben hast, wird der Filename entsprechend ergänzt und zu /test/MouseIDGenes. Ohne RewriteBase wäre /hallo/MouseIDGenes herausgekommen. D.h. es wird einfach DocumentRoot abgeschnitten. Nun ist /test/MouseIDGenes jedoch kein gültiger Dateiname. Es ist eine URI. Die folgende Phase, in der die Antwort erzeugt wird, könnte mit diesem Dateinamen nichts anfangen. Außerdem könnte das neue Ziel andere Anforderungen bzgl. Zugriffsberechtigungen haben. Eigentlich müsste man also alle Phasen des Requests mit der neuen URI nochmal durchlaufen. Genau für diesen Zweck hat der Apache einen Mechanismus, der sich "Internal Redirect" nennt. Dabei wird eine neue Request Struktur mit der neuen URI erzeugt. Diese durchläuft den kompletten Zyklus beginnend mit der URI Translation Phase erneut. Über ein paar Zwischenstufen leitet mod_rewrite nun solch einen Internal Redirect mit der neuen URI (/test/MouseIDGenes) ein. Nach der URI Translation Phase sieht das dann wie folgt aus: uri: /test/MouseIDGenes filename: $DocumentRoot/hallo/MouseIDGenes Das kommt in der Fixup Phase in mod_rewrite an (die vorletzte Zeile in Deinem Log). Du hast richtig erkannt, dass es sich hier um eine neue Anfrage handelt. Von dieser hat der Client aber nichts mitgekriegt. Das ist einfach die neue Request Struktur für den Internal Redirect. Alle Request Strukturen, die zu einem vom Client initiierten Request gehören, sind über Listen- bzw. Baum-ähnliche Strukturen miteinander verkettet. Mod_rewrite kann also feststellen, dass der aktuelle Request das Resultat eines Internal Redirects ist. Der "/redir#1" Teil aus dem Log wird genau das besagen. Die aktuelle Anfrage ist ein Internal Redirect und ist eine Ebene vom originalen Request entfernt. Nun wendet mod_rewrite wieder Deine Rule an und vergleicht "MouseIDGenes" mit der Regexp "$". Es passt, denn auch der String "MouseIDGenes" hat ein Ende. Aus "MouseIDGenes" wird also "MouseIDGenes" und über RewriteBase wieder /test/MouseIDGenes. Dann merkt mod_rewrite, "ups, hier hat sich ja gar nichts geändert" und lehnt es ab, einen weiteren Internal Redirect zu erzeugen, weil das zu einem Zyklus führen würde. Dass die RewriteRule auch auf "MouseIDGenes" passt, ist ein Fehler. Hier solltest Du vielleicht etwas machen, z.B.: RewriteRule ^$ MouseIDGenes oder RewriteRule ^(?!MouseIDGenes)$ MouseIDGenes Sonst macht er eigentlich, was er sollte. Insgesamt ist mod_rewrite in Containern keine gute Idee. Wenn es komplett in der URI Translation Phase arbeiten kann, ist es viel effektiver, insbesondere wenn Du teure Authentifizierung/Autorisierung benutzt.
In diesem Container gibt es einen RewriteRule, die ausgeführt wird. Anschließend wird die RewriteBase vor die substitution geklemmt. OK ?
Dieser "neue" request (/test/MouseIDGenes) landet durch den alias test wieder im hallo container. /test/ wird entfernt, MouseIDGenes wird zu MouseIDGenes substituiert, was mod_rewrite richtigerweise als Unsinn erkennt. OK ?
Was mir nicht klar ist: wieso wird vor der Substitution in Zeile 4 quasi noch einmal ein neuer request erzeugt ? Wieso gibt sich mod_rewrite nicht mit dem redirect in Zeile 3 zufrieden und hört auf ? Und kann mir bitte jemand helfen, was die Interpretation des mod_rewrite-logs angeht ? Alles was ich beim googeln gefunden habe, war das : https://issues.apache.org/bugzilla/show_bug.cgi?id=49844 Was soll diese "redir#1" in Zeile 4 und 5 bei der Request ID ? Und kann ich die Tatsache, daß die RID in Zeile 4 und 5 eine andere ist als in Zeile 1-3 so interpretieren, daß ein neuer request erzeugt wurde ?
Dankbar für jede Hilfe.
Zu Deinen Fragen vom 25.3. auf users@httpd:
1. Frage:
Mod_rewrite arbeitet eigentlich nur auf dem Filename Feld des Requests.
In der URI Translation Phase kann es sein, dass dieses noch nicht
gesetzt ist (NULL Pointer). In diesem Fall wird es vor dem Start der
Maschine mit r->uri initialisiert. Filename muss dabei nicht einer
existierenden Eintrag im Filesystem entsprechen. Es kann eigentlich ein
beliebiger String sein.
Substitution in einer RewriteRule ist eigentlich immer ein Pfad im
Filesystem (der nicht existieren muss), außer wenn mittels Flags etwas
anderes angegeben ist, PT, P oder R. Im Directory-Context wird das PT
Flag impliziert. Somit ist Substitution eine URI.
Im Server-Context wird mit dem PT Flag das r->uri Feld geändert, sonst
immer r->filename. Im Directory-Context wird ein Internal Redirect mit
der neuen URI erzeugt. D.h. es entsteht ein neuer Request mit der neuen
URI in r->uri.
2. Frage:
Ich denke, die Antwort ist inzwischen klar. In r->filename steht am
Anfang "$DocumentRoot/hallo/bla/bla". Die Directory im Directory-Context
lautet "$DocumentRoot/hallo/" (Inhalt von DocumentRoot plus PATH aus
<Directory PATH> (stimmt nicht ganz. Für DirectoryMatch und .htaccess
wird PATH in Map to Storage ausgerechnet)). Dieser Teil wird entfernt
und es bleibt "bla/bla" übrig. Wichtig: dieser String beginnt nie mit
einem Slash.
3. Frage:
Das automatische entfernen des Präfix im Directory Context findet immer
statt, nicht nur für .htaccess.
Siehe Frage 2.
Das Ziel einer RewriteRule im Directory-Context ist immer eine URI. Wenn
RewriteBase angegeben ist, wird der Wert als Präfix ergänzt, sonst wird
$DocumentRoot aus "$DocumentRoot/hallo/" entfernt (ergibt "/hallo/") und
das als Präfix benutzt.
Diese Ersetzung findet erst statt, wenn alle RewriteRules ausgewertet
wurden.
Purzelt aus den Rules eine absolute URL raus, also etwas, das wie
scheme://... aussieht, erfolgt ein vom Browser sichtbarer Redirekt, also
ein HTTP Code zwischen 300 und 399. Im Directory Context versucht
mod_write auch hier, RewriteBase zu ersetzen, damit auch so etwas
funktioniert (R Flag in Directory Context):